diff --git a/Marlin/Conditionals_LCD.h b/Marlin/Conditionals_LCD.h
index 5a858a68cd..c9d44b1be3 100644
--- a/Marlin/Conditionals_LCD.h
+++ b/Marlin/Conditionals_LCD.h
@@ -28,483 +28,483 @@
#ifndef CONDITIONALS_LCD_H // Get the LCD defines which are needed first
#define CONDITIONALS_LCD_H
- #define LCD_HAS_DIRECTIONAL_BUTTONS (BUTTON_EXISTS(UP) || BUTTON_EXISTS(DWN) || BUTTON_EXISTS(LFT) || BUTTON_EXISTS(RT))
+#define LCD_HAS_DIRECTIONAL_BUTTONS (BUTTON_EXISTS(UP) || BUTTON_EXISTS(DWN) || BUTTON_EXISTS(LFT) || BUTTON_EXISTS(RT))
- #if ENABLED(CARTESIO_UI)
+#if ENABLED(CARTESIO_UI)
- #define DOGLCD
- #define ULTIPANEL
- #define DEFAULT_LCD_CONTRAST 90
- #define LCD_CONTRAST_MIN 60
- #define LCD_CONTRAST_MAX 140
+ #define DOGLCD
+ #define ULTIPANEL
+ #define DEFAULT_LCD_CONTRAST 90
+ #define LCD_CONTRAST_MIN 60
+ #define LCD_CONTRAST_MAX 140
- #elif ENABLED(MAKRPANEL)
+#elif ENABLED(MAKRPANEL)
+ #define U8GLIB_ST7565_64128N
+
+#elif ENABLED(ZONESTAR_LCD)
+
+ #define REPRAPWORLD_KEYPAD
+ #define REPRAPWORLD_KEYPAD_MOVE_STEP 10.0
+ #define ADC_KEYPAD
+ #define ADC_KEY_NUM 8
+ #define ULTIPANEL
+
+ // this helps to implement ADC_KEYPAD menus
+ #define ENCODER_PULSES_PER_STEP 1
+ #define ENCODER_STEPS_PER_MENU_ITEM 1
+ #define ENCODER_FEEDRATE_DEADZONE 2
+ #define REVERSE_MENU_DIRECTION
+
+#elif ENABLED(ANET_FULL_GRAPHICS_LCD)
+
+ #define REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER
+
+#elif ENABLED(BQ_LCD_SMART_CONTROLLER)
+
+ #define REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER
+
+#elif ENABLED(miniVIKI) || ENABLED(VIKI2) || ENABLED(ELB_FULL_GRAPHIC_CONTROLLER)
+
+ #define ULTRA_LCD //general LCD support, also 16x2
+ #define DOGLCD // Support for SPI LCD 128x64 (Controller ST7565R graphic Display Family)
+ #define ULTIMAKERCONTROLLER //as available from the Ultimaker online store.
+
+ #if ENABLED(miniVIKI)
+ #define LCD_CONTRAST_MIN 75
+ #define LCD_CONTRAST_MAX 115
+ #define DEFAULT_LCD_CONTRAST 95
#define U8GLIB_ST7565_64128N
-
- #elif ENABLED(ZONESTAR_LCD)
-
- #define REPRAPWORLD_KEYPAD
- #define REPRAPWORLD_KEYPAD_MOVE_STEP 10.0
- #define ADC_KEYPAD
- #define ADC_KEY_NUM 8
- #define ULTIPANEL
-
- // this helps to implement ADC_KEYPAD menus
- #define ENCODER_PULSES_PER_STEP 1
- #define ENCODER_STEPS_PER_MENU_ITEM 1
- #define ENCODER_FEEDRATE_DEADZONE 2
- #define REVERSE_MENU_DIRECTION
-
- #elif ENABLED(ANET_FULL_GRAPHICS_LCD)
-
- #define REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER
-
- #elif ENABLED(BQ_LCD_SMART_CONTROLLER)
-
- #define REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER
-
- #elif ENABLED(miniVIKI) || ENABLED(VIKI2) || ENABLED(ELB_FULL_GRAPHIC_CONTROLLER)
-
- #define ULTRA_LCD //general LCD support, also 16x2
- #define DOGLCD // Support for SPI LCD 128x64 (Controller ST7565R graphic Display Family)
- #define ULTIMAKERCONTROLLER //as available from the Ultimaker online store.
-
- #if ENABLED(miniVIKI)
- #define LCD_CONTRAST_MIN 75
- #define LCD_CONTRAST_MAX 115
- #define DEFAULT_LCD_CONTRAST 95
- #define U8GLIB_ST7565_64128N
- #elif ENABLED(VIKI2)
- #define LCD_CONTRAST_MIN 0
- #define LCD_CONTRAST_MAX 255
- #define DEFAULT_LCD_CONTRAST 140
- #define U8GLIB_ST7565_64128N
- #elif ENABLED(ELB_FULL_GRAPHIC_CONTROLLER)
- #define LCD_CONTRAST_MIN 90
- #define LCD_CONTRAST_MAX 130
- #define DEFAULT_LCD_CONTRAST 110
- #define U8GLIB_LM6059_AF
- #define SD_DETECT_INVERTED
- #endif
-
- #elif ENABLED(OLED_PANEL_TINYBOY2)
-
- #define U8GLIB_SSD1306
- #define ULTIPANEL
- #define REVERSE_ENCODER_DIRECTION
- #define REVERSE_MENU_DIRECTION
-
- #elif ENABLED(RA_CONTROL_PANEL)
-
- #define LCD_I2C_TYPE_PCA8574
- #define LCD_I2C_ADDRESS 0x27 // I2C Address of the port expander
- #define ULTIPANEL
-
- #elif ENABLED(REPRAPWORLD_GRAPHICAL_LCD)
-
- #define DOGLCD
- #define U8GLIB_ST7920
- #define ULTIPANEL
-
- #elif ENABLED(CR10_STOCKDISPLAY)
-
- #define REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER
- #ifndef ST7920_DELAY_1
- #define ST7920_DELAY_1 DELAY_2_NOP
- #endif
- #ifndef ST7920_DELAY_2
- #define ST7920_DELAY_2 DELAY_2_NOP
- #endif
- #ifndef ST7920_DELAY_3
- #define ST7920_DELAY_3 DELAY_2_NOP
- #endif
-
- #elif ENABLED(MKS_12864OLED)
-
- #define REPRAP_DISCOUNT_SMART_CONTROLLER
- #define U8GLIB_SH1106
-
- #elif ENABLED(MKS_12864OLED_SSD1306)
-
- #define REPRAP_DISCOUNT_SMART_CONTROLLER
- #define U8GLIB_SSD1306
-
- #elif ENABLED(MKS_MINI_12864)
-
- #define MINIPANEL
-
+ #elif ENABLED(VIKI2)
+ #define LCD_CONTRAST_MIN 0
+ #define LCD_CONTRAST_MAX 255
+ #define DEFAULT_LCD_CONTRAST 140
+ #define U8GLIB_ST7565_64128N
+ #elif ENABLED(ELB_FULL_GRAPHIC_CONTROLLER)
+ #define LCD_CONTRAST_MIN 90
+ #define LCD_CONTRAST_MAX 130
+ #define DEFAULT_LCD_CONTRAST 110
+ #define U8GLIB_LM6059_AF
+ #define SD_DETECT_INVERTED
#endif
- #if ENABLED(MAKRPANEL) || ENABLED(MINIPANEL)
- #define DOGLCD
- #define ULTIPANEL
- #define DEFAULT_LCD_CONTRAST 17
+#elif ENABLED(OLED_PANEL_TINYBOY2)
+
+ #define U8GLIB_SSD1306
+ #define ULTIPANEL
+ #define REVERSE_ENCODER_DIRECTION
+ #define REVERSE_MENU_DIRECTION
+
+#elif ENABLED(RA_CONTROL_PANEL)
+
+ #define LCD_I2C_TYPE_PCA8574
+ #define LCD_I2C_ADDRESS 0x27 // I2C Address of the port expander
+ #define ULTIPANEL
+
+#elif ENABLED(REPRAPWORLD_GRAPHICAL_LCD)
+
+ #define DOGLCD
+ #define U8GLIB_ST7920
+ #define ULTIPANEL
+
+#elif ENABLED(CR10_STOCKDISPLAY)
+
+ #define REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER
+ #ifndef ST7920_DELAY_1
+ #define ST7920_DELAY_1 DELAY_NS(125)
+ #endif
+ #ifndef ST7920_DELAY_2
+ #define ST7920_DELAY_2 DELAY_NS(125)
+ #endif
+ #ifndef ST7920_DELAY_3
+ #define ST7920_DELAY_3 DELAY_NS(125)
#endif
- #if ENABLED(ULTI_CONTROLLER)
- #define U8GLIB_SSD1309
- #define REVERSE_ENCODER_DIRECTION
- #define LCD_RESET_PIN LCD_PINS_D6 // This controller need a reset pin
- #define LCD_CONTRAST_MIN 0
- #define LCD_CONTRAST_MAX 254
- #define DEFAULT_LCD_CONTRAST 127
- #define ENCODER_PULSES_PER_STEP 2
- #define ENCODER_STEPS_PER_MENU_ITEM 2
+#elif ENABLED(MKS_12864OLED)
+
+ #define REPRAP_DISCOUNT_SMART_CONTROLLER
+ #define U8GLIB_SH1106
+
+#elif ENABLED(MKS_12864OLED_SSD1306)
+
+ #define REPRAP_DISCOUNT_SMART_CONTROLLER
+ #define U8GLIB_SSD1306
+
+#elif ENABLED(MKS_MINI_12864)
+
+ #define MINIPANEL
+
+#endif
+
+#if ENABLED(MAKRPANEL) || ENABLED(MINIPANEL)
+ #define DOGLCD
+ #define ULTIPANEL
+ #define DEFAULT_LCD_CONTRAST 17
+#endif
+
+#if ENABLED(ULTI_CONTROLLER)
+ #define U8GLIB_SSD1309
+ #define REVERSE_ENCODER_DIRECTION
+ #define LCD_RESET_PIN LCD_PINS_D6 // This controller need a reset pin
+ #define LCD_CONTRAST_MIN 0
+ #define LCD_CONTRAST_MAX 254
+ #define DEFAULT_LCD_CONTRAST 127
+ #define ENCODER_PULSES_PER_STEP 2
+ #define ENCODER_STEPS_PER_MENU_ITEM 2
+#endif
+
+// Generic support for SSD1306 / SSD1309 / SH1106 OLED based LCDs.
+#if ENABLED(U8GLIB_SSD1306) || ENABLED(U8GLIB_SSD1309) || ENABLED(U8GLIB_SH1106)
+ #define ULTRA_LCD //general LCD support, also 16x2
+ #define DOGLCD // Support for I2C LCD 128x64 (Controller SSD1306 / SSD1309 / SH1106 graphic Display Family)
+#endif
+
+#if ENABLED(PANEL_ONE) || ENABLED(U8GLIB_SH1106)
+
+ #define ULTIMAKERCONTROLLER
+
+#elif ENABLED(MAKEBOARD_MINI_2_LINE_DISPLAY_1602)
+
+ #define REPRAP_DISCOUNT_SMART_CONTROLLER
+ #define LCD_WIDTH 16
+ #define LCD_HEIGHT 2
+
+#endif
+
+#if ENABLED(REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER) || ENABLED(LCD_FOR_MELZI) || ENABLED(SILVER_GATE_GLCD_CONTROLLER)
+ #define DOGLCD
+ #define U8GLIB_ST7920
+ #define REPRAP_DISCOUNT_SMART_CONTROLLER
+#endif
+
+#if ENABLED(ULTIMAKERCONTROLLER) \
+ || ENABLED(REPRAP_DISCOUNT_SMART_CONTROLLER) \
+ || ENABLED(G3D_PANEL) \
+ || ENABLED(RIGIDBOT_PANEL) \
+ || ENABLED(ULTI_CONTROLLER)
+ #define ULTIPANEL
+#endif
+
+#if ENABLED(REPRAPWORLD_KEYPAD)
+ #define NEWPANEL
+ #if ENABLED(ULTIPANEL) && !defined(REPRAPWORLD_KEYPAD_MOVE_STEP)
+ #define REPRAPWORLD_KEYPAD_MOVE_STEP 1.0
+ #endif
+#endif
+
+/**
+ * I2C PANELS
+ */
+
+#if ENABLED(LCD_SAINSMART_I2C_1602) || ENABLED(LCD_SAINSMART_I2C_2004)
+
+ #define LCD_I2C_TYPE_PCF8575
+ #define LCD_I2C_ADDRESS 0x27 // I2C Address of the port expander
+ #define ULTRA_LCD
+
+ #if ENABLED(LCD_SAINSMART_I2C_2004)
+ #define LCD_WIDTH 20
+ #define LCD_HEIGHT 4
#endif
- // Generic support for SSD1306 / SSD1309 / SH1106 OLED based LCDs.
- #if ENABLED(U8GLIB_SSD1306) || ENABLED(U8GLIB_SSD1309) || ENABLED(U8GLIB_SH1106)
- #define ULTRA_LCD //general LCD support, also 16x2
- #define DOGLCD // Support for I2C LCD 128x64 (Controller SSD1306 / SSD1309 / SH1106 graphic Display Family)
+#elif ENABLED(LCD_I2C_PANELOLU2)
+
+ // PANELOLU2 LCD with status LEDs, separate encoder and click inputs
+
+ #define LCD_I2C_TYPE_MCP23017
+ #define LCD_I2C_ADDRESS 0x20 // I2C Address of the port expander
+ #define LCD_USE_I2C_BUZZER // Enable buzzer on LCD (optional)
+ #define ULTIPANEL
+
+#elif ENABLED(LCD_I2C_VIKI)
+
+ /**
+ * Panucatt VIKI LCD with status LEDs, integrated click & L/R/U/P buttons, separate encoder inputs
+ *
+ * This uses the LiquidTWI2 library v1.2.3 or later ( https://github.com/lincomatic/LiquidTWI2 )
+ * Make sure the LiquidTWI2 directory is placed in the Arduino or Sketchbook libraries subdirectory.
+ * Note: The pause/stop/resume LCD button pin should be connected to the Arduino
+ * BTN_ENC pin (or set BTN_ENC to -1 if not used)
+ */
+ #define LCD_I2C_TYPE_MCP23017
+ #define LCD_I2C_ADDRESS 0x20 // I2C Address of the port expander
+ #define LCD_USE_I2C_BUZZER // Enable buzzer on LCD (requires LiquidTWI2 v1.2.3 or later)
+ #define ULTIPANEL
+
+ #define ENCODER_FEEDRATE_DEADZONE 4
+
+ #define STD_ENCODER_PULSES_PER_STEP 1
+ #define STD_ENCODER_STEPS_PER_MENU_ITEM 2
+
+#elif ENABLED(G3D_PANEL)
+
+ #define STD_ENCODER_PULSES_PER_STEP 2
+ #define STD_ENCODER_STEPS_PER_MENU_ITEM 1
+
+#elif ENABLED(miniVIKI) || ENABLED(VIKI2) \
+ || ENABLED(ELB_FULL_GRAPHIC_CONTROLLER) \
+ || ENABLED(OLED_PANEL_TINYBOY2) \
+ || ENABLED(BQ_LCD_SMART_CONTROLLER) \
+ || ENABLED(LCD_I2C_PANELOLU2) \
+ || ENABLED(REPRAP_DISCOUNT_SMART_CONTROLLER)
+ #define STD_ENCODER_PULSES_PER_STEP 4
+ #define STD_ENCODER_STEPS_PER_MENU_ITEM 1
+#endif
+
+#ifndef STD_ENCODER_PULSES_PER_STEP
+ #define STD_ENCODER_PULSES_PER_STEP 5
+#endif
+#ifndef STD_ENCODER_STEPS_PER_MENU_ITEM
+ #define STD_ENCODER_STEPS_PER_MENU_ITEM 1
+#endif
+#ifndef ENCODER_PULSES_PER_STEP
+ #define ENCODER_PULSES_PER_STEP STD_ENCODER_PULSES_PER_STEP
+#endif
+#ifndef ENCODER_STEPS_PER_MENU_ITEM
+ #define ENCODER_STEPS_PER_MENU_ITEM STD_ENCODER_STEPS_PER_MENU_ITEM
+#endif
+#ifndef ENCODER_FEEDRATE_DEADZONE
+ #define ENCODER_FEEDRATE_DEADZONE 6
+#endif
+
+// Shift register panels
+// ---------------------
+// 2 wire Non-latching LCD SR from:
+// https://bitbucket.org/fmalpartida/new-liquidcrystal/wiki/schematics#!shiftregister-connection
+
+#if ENABLED(SAV_3DLCD)
+ #define SR_LCD_2W_NL // Non latching 2 wire shift register
+ #define ULTIPANEL
+#endif
+
+#if ENABLED(DOGLCD) // Change number of lines to match the DOG graphic display
+ #ifndef LCD_WIDTH
+ #ifdef LCD_WIDTH_OVERRIDE
+ #define LCD_WIDTH LCD_WIDTH_OVERRIDE
+ #else
+ #define LCD_WIDTH 22
+ #endif
#endif
+ #ifndef LCD_HEIGHT
+ #define LCD_HEIGHT 5
+ #endif
+#endif
- #if ENABLED(PANEL_ONE) || ENABLED(U8GLIB_SH1106)
+#if ENABLED(NO_LCD_MENUS)
+ #undef ULTIPANEL
+#endif
- #define ULTIMAKERCONTROLLER
-
- #elif ENABLED(MAKEBOARD_MINI_2_LINE_DISPLAY_1602)
-
- #define REPRAP_DISCOUNT_SMART_CONTROLLER
+#if ENABLED(ULTIPANEL)
+ #define NEWPANEL // Disable this if you actually have no click-encoder panel
+ #define ULTRA_LCD
+ #ifndef LCD_WIDTH
+ #define LCD_WIDTH 20
+ #endif
+ #ifndef LCD_HEIGHT
+ #define LCD_HEIGHT 4
+ #endif
+#elif ENABLED(ULTRA_LCD) // no panel but just LCD
+ #ifndef LCD_WIDTH
#define LCD_WIDTH 16
+ #endif
+ #ifndef LCD_HEIGHT
#define LCD_HEIGHT 2
-
#endif
+#endif
- #if ENABLED(REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER) || ENABLED(LCD_FOR_MELZI) || ENABLED(SILVER_GATE_GLCD_CONTROLLER)
- #define DOGLCD
- #define U8GLIB_ST7920
- #define REPRAP_DISCOUNT_SMART_CONTROLLER
- #endif
+#if ENABLED(DOGLCD)
+ /* Custom characters defined in font dogm_font_data_Marlin_symbols.h / Marlin_symbols.fon */
+ // \x00 intentionally skipped to avoid problems in strings
+ #define LCD_STR_REFRESH "\x01"
+ #define LCD_STR_FOLDER "\x02"
+ #define LCD_STR_ARROW_RIGHT "\x03"
+ #define LCD_STR_UPLEVEL "\x04"
+ #define LCD_STR_CLOCK "\x05"
+ #define LCD_STR_FEEDRATE "\x06"
+ #define LCD_STR_BEDTEMP "\x07"
+ #define LCD_STR_THERMOMETER "\x08"
+ #define LCD_STR_DEGREE "\x09"
- #if ENABLED(ULTIMAKERCONTROLLER) \
- || ENABLED(REPRAP_DISCOUNT_SMART_CONTROLLER) \
- || ENABLED(G3D_PANEL) \
- || ENABLED(RIGIDBOT_PANEL) \
- || ENABLED(ULTI_CONTROLLER)
- #define ULTIPANEL
- #endif
+ #define LCD_STR_SPECIAL_MAX '\x09'
+ // Maximum here is 0x1F because 0x20 is ' ' (space) and the normal charsets begin.
+ // Better stay below 0x10 because DISPLAY_CHARSET_HD44780_WESTERN begins here.
- #if ENABLED(REPRAPWORLD_KEYPAD)
- #define NEWPANEL
- #if ENABLED(ULTIPANEL) && !defined(REPRAPWORLD_KEYPAD_MOVE_STEP)
- #define REPRAPWORLD_KEYPAD_MOVE_STEP 1.0
+ // Symbol characters
+ #define LCD_STR_FILAM_DIA "\xf8"
+ #define LCD_STR_FILAM_MUL "\xa4"
+#else
+ // Custom characters defined in the first 8 characters of the LCD
+ #define LCD_BEDTEMP_CHAR 0x00 // Print only as a char. This will have 'unexpected' results when used in a string!
+ #define LCD_DEGREE_CHAR 0x01
+ #define LCD_STR_THERMOMETER "\x02" // Still used with string concatenation
+ #define LCD_UPLEVEL_CHAR 0x03
+ #define LCD_STR_REFRESH "\x04"
+ #define LCD_STR_FOLDER "\x05"
+ #define LCD_FEEDRATE_CHAR 0x06
+ #define LCD_CLOCK_CHAR 0x07
+ #define LCD_STR_ARROW_RIGHT ">" /* from the default character set */
+#endif
+
+/**
+ * Default LCD contrast for dogm-like LCD displays
+ */
+#if ENABLED(DOGLCD)
+
+ #define HAS_LCD_CONTRAST ( \
+ ENABLED(MAKRPANEL) \
+ || ENABLED(CARTESIO_UI) \
+ || ENABLED(VIKI2) \
+ || ENABLED(miniVIKI) \
+ || ENABLED(ELB_FULL_GRAPHIC_CONTROLLER) \
+ )
+
+ #if HAS_LCD_CONTRAST
+ #ifndef LCD_CONTRAST_MIN
+ #define LCD_CONTRAST_MIN 0
+ #endif
+ #ifndef LCD_CONTRAST_MAX
+ #define LCD_CONTRAST_MAX 63
+ #endif
+ #ifndef DEFAULT_LCD_CONTRAST
+ #define DEFAULT_LCD_CONTRAST 32
#endif
#endif
+#endif
- /**
- * I2C PANELS
- */
+// Boot screens
+#if DISABLED(ULTRA_LCD)
+ #undef SHOW_BOOTSCREEN
+#elif !defined(BOOTSCREEN_TIMEOUT)
+ #define BOOTSCREEN_TIMEOUT 2500
+#endif
- #if ENABLED(LCD_SAINSMART_I2C_1602) || ENABLED(LCD_SAINSMART_I2C_2004)
+#define HAS_DEBUG_MENU (ENABLED(ULTIPANEL) && ENABLED(LCD_PROGRESS_BAR_TEST))
- #define LCD_I2C_TYPE_PCF8575
- #define LCD_I2C_ADDRESS 0x27 // I2C Address of the port expander
- #define ULTRA_LCD
+// MK2 Multiplexer forces SINGLENOZZLE and kills DISABLE_INACTIVE_EXTRUDER
+#if ENABLED(MK2_MULTIPLEXER)
+ #define SINGLENOZZLE
+ #undef DISABLE_INACTIVE_EXTRUDER
+#endif
- #if ENABLED(LCD_SAINSMART_I2C_2004)
- #define LCD_WIDTH 20
- #define LCD_HEIGHT 4
- #endif
+/**
+ * Extruders have some combination of stepper motors and hotends
+ * so we separate these concepts into the defines:
+ *
+ * EXTRUDERS - Number of Selectable Tools
+ * HOTENDS - Number of hotends, whether connected or separate
+ * E_STEPPERS - Number of actual E stepper motors
+ * E_MANUAL - Number of E steppers for LCD move options
+ *
+ */
+#if ENABLED(SINGLENOZZLE) || ENABLED(MIXING_EXTRUDER) // One hotend, one thermistor, no XY offset
+ #define HOTENDS 1
+ #undef TEMP_SENSOR_1_AS_REDUNDANT
+ #undef HOTEND_OFFSET_X
+ #undef HOTEND_OFFSET_Y
+#else // Two hotends
+ #define HOTENDS EXTRUDERS
+#endif
- #elif ENABLED(LCD_I2C_PANELOLU2)
-
- // PANELOLU2 LCD with status LEDs, separate encoder and click inputs
-
- #define LCD_I2C_TYPE_MCP23017
- #define LCD_I2C_ADDRESS 0x20 // I2C Address of the port expander
- #define LCD_USE_I2C_BUZZER // Enable buzzer on LCD (optional)
- #define ULTIPANEL
-
- #elif ENABLED(LCD_I2C_VIKI)
-
- /**
- * Panucatt VIKI LCD with status LEDs, integrated click & L/R/U/P buttons, separate encoder inputs
- *
- * This uses the LiquidTWI2 library v1.2.3 or later ( https://github.com/lincomatic/LiquidTWI2 )
- * Make sure the LiquidTWI2 directory is placed in the Arduino or Sketchbook libraries subdirectory.
- * Note: The pause/stop/resume LCD button pin should be connected to the Arduino
- * BTN_ENC pin (or set BTN_ENC to -1 if not used)
- */
- #define LCD_I2C_TYPE_MCP23017
- #define LCD_I2C_ADDRESS 0x20 // I2C Address of the port expander
- #define LCD_USE_I2C_BUZZER // Enable buzzer on LCD (requires LiquidTWI2 v1.2.3 or later)
- #define ULTIPANEL
-
- #define ENCODER_FEEDRATE_DEADZONE 4
-
- #define STD_ENCODER_PULSES_PER_STEP 1
- #define STD_ENCODER_STEPS_PER_MENU_ITEM 2
-
- #elif ENABLED(G3D_PANEL)
-
- #define STD_ENCODER_PULSES_PER_STEP 2
- #define STD_ENCODER_STEPS_PER_MENU_ITEM 1
-
- #elif ENABLED(miniVIKI) || ENABLED(VIKI2) \
- || ENABLED(ELB_FULL_GRAPHIC_CONTROLLER) \
- || ENABLED(OLED_PANEL_TINYBOY2) \
- || ENABLED(BQ_LCD_SMART_CONTROLLER) \
- || ENABLED(LCD_I2C_PANELOLU2) \
- || ENABLED(REPRAP_DISCOUNT_SMART_CONTROLLER)
- #define STD_ENCODER_PULSES_PER_STEP 4
- #define STD_ENCODER_STEPS_PER_MENU_ITEM 1
- #endif
-
- #ifndef STD_ENCODER_PULSES_PER_STEP
- #define STD_ENCODER_PULSES_PER_STEP 5
- #endif
- #ifndef STD_ENCODER_STEPS_PER_MENU_ITEM
- #define STD_ENCODER_STEPS_PER_MENU_ITEM 1
- #endif
- #ifndef ENCODER_PULSES_PER_STEP
- #define ENCODER_PULSES_PER_STEP STD_ENCODER_PULSES_PER_STEP
- #endif
- #ifndef ENCODER_STEPS_PER_MENU_ITEM
- #define ENCODER_STEPS_PER_MENU_ITEM STD_ENCODER_STEPS_PER_MENU_ITEM
- #endif
- #ifndef ENCODER_FEEDRATE_DEADZONE
- #define ENCODER_FEEDRATE_DEADZONE 6
- #endif
-
- // Shift register panels
- // ---------------------
- // 2 wire Non-latching LCD SR from:
- // https://bitbucket.org/fmalpartida/new-liquidcrystal/wiki/schematics#!shiftregister-connection
-
- #if ENABLED(SAV_3DLCD)
- #define SR_LCD_2W_NL // Non latching 2 wire shift register
- #define ULTIPANEL
- #endif
-
- #if ENABLED(DOGLCD) // Change number of lines to match the DOG graphic display
- #ifndef LCD_WIDTH
- #ifdef LCD_WIDTH_OVERRIDE
- #define LCD_WIDTH LCD_WIDTH_OVERRIDE
- #else
- #define LCD_WIDTH 22
- #endif
- #endif
- #ifndef LCD_HEIGHT
- #define LCD_HEIGHT 5
- #endif
- #endif
-
- #if ENABLED(ULTIPANEL)
- #define NEWPANEL // Disable this if you actually have no click-encoder panel
- #define ULTRA_LCD
- #ifndef LCD_WIDTH
- #define LCD_WIDTH 20
- #endif
- #ifndef LCD_HEIGHT
- #define LCD_HEIGHT 4
- #endif
- #elif ENABLED(ULTRA_LCD) // no panel but just LCD
- #ifndef LCD_WIDTH
- #define LCD_WIDTH 16
- #endif
- #ifndef LCD_HEIGHT
- #define LCD_HEIGHT 2
- #endif
- #endif
-
- #if ENABLED(DOGLCD)
- /* Custom characters defined in font dogm_font_data_Marlin_symbols.h / Marlin_symbols.fon */
- // \x00 intentionally skipped to avoid problems in strings
- #define LCD_STR_REFRESH "\x01"
- #define LCD_STR_FOLDER "\x02"
- #define LCD_STR_ARROW_RIGHT "\x03"
- #define LCD_STR_UPLEVEL "\x04"
- #define LCD_STR_CLOCK "\x05"
- #define LCD_STR_FEEDRATE "\x06"
- #define LCD_STR_BEDTEMP "\x07"
- #define LCD_STR_THERMOMETER "\x08"
- #define LCD_STR_DEGREE "\x09"
-
- #define LCD_STR_SPECIAL_MAX '\x09'
- // Maximum here is 0x1F because 0x20 is ' ' (space) and the normal charsets begin.
- // Better stay below 0x10 because DISPLAY_CHARSET_HD44780_WESTERN begins here.
-
- // Symbol characters
- #define LCD_STR_FILAM_DIA "\xf8"
- #define LCD_STR_FILAM_MUL "\xa4"
+#if ENABLED(SWITCHING_EXTRUDER) // One stepper for every two EXTRUDERS
+ #if EXTRUDERS > 4
+ #define E_STEPPERS 3
+ #define E_MANUAL 3
+ #elif EXTRUDERS > 2
+ #define E_STEPPERS 2
+ #define E_MANUAL 2
#else
- // Custom characters defined in the first 8 characters of the LCD
- #define LCD_BEDTEMP_CHAR 0x00 // Print only as a char. This will have 'unexpected' results when used in a string!
- #define LCD_DEGREE_CHAR 0x01
- #define LCD_STR_THERMOMETER "\x02" // Still used with string concatenation
- #define LCD_UPLEVEL_CHAR 0x03
- #define LCD_STR_REFRESH "\x04"
- #define LCD_STR_FOLDER "\x05"
- #define LCD_FEEDRATE_CHAR 0x06
- #define LCD_CLOCK_CHAR 0x07
- #define LCD_STR_ARROW_RIGHT ">" /* from the default character set */
+ #define E_STEPPERS 1
#endif
+ #define E_MANUAL EXTRUDERS
+#elif ENABLED(MIXING_EXTRUDER)
+ #define E_STEPPERS MIXING_STEPPERS
+ #define E_MANUAL 1
+#else
+ #define E_STEPPERS EXTRUDERS
+ #define E_MANUAL EXTRUDERS
+#endif
- /**
- * Default LCD contrast for dogm-like LCD displays
- */
- #if ENABLED(DOGLCD)
+#define DO_SWITCH_EXTRUDER (ENABLED(SWITCHING_EXTRUDER) && (DISABLED(SWITCHING_NOZZLE) || SWITCHING_EXTRUDER_SERVO_NR != SWITCHING_NOZZLE_SERVO_NR))
- #define HAS_LCD_CONTRAST ( \
- ENABLED(MAKRPANEL) \
- || ENABLED(CARTESIO_UI) \
- || ENABLED(VIKI2) \
- || ENABLED(miniVIKI) \
- || ENABLED(ELB_FULL_GRAPHIC_CONTROLLER) \
- )
+/**
+ * DISTINCT_E_FACTORS affects how some E factors are accessed
+ */
+#if ENABLED(DISTINCT_E_FACTORS) && E_STEPPERS > 1
+ #define XYZE_N (XYZ + E_STEPPERS)
+ #define E_AXIS_N (E_AXIS + extruder)
+#else
+ #undef DISTINCT_E_FACTORS
+ #define XYZE_N XYZE
+ #define E_AXIS_N E_AXIS
+#endif
- #if HAS_LCD_CONTRAST
- #ifndef LCD_CONTRAST_MIN
- #define LCD_CONTRAST_MIN 0
- #endif
- #ifndef LCD_CONTRAST_MAX
- #define LCD_CONTRAST_MAX 63
- #endif
- #ifndef DEFAULT_LCD_CONTRAST
- #define DEFAULT_LCD_CONTRAST 32
- #endif
- #endif
+/**
+ * The BLTouch Probe emulates a servo probe
+ * and uses "special" angles for its state.
+ */
+#if ENABLED(BLTOUCH)
+ #ifndef Z_PROBE_SERVO_NR
+ #define Z_PROBE_SERVO_NR 0
#endif
-
- #if ENABLED(NO_LCD_MENUS)
- #undef ULTIPANEL
- #undef NEWPANEL
+ #ifndef NUM_SERVOS
+ #define NUM_SERVOS (Z_PROBE_SERVO_NR + 1)
#endif
-
- // Boot screens
- #if DISABLED(ULTRA_LCD)
- #undef SHOW_BOOTSCREEN
- #elif !defined(BOOTSCREEN_TIMEOUT)
- #define BOOTSCREEN_TIMEOUT 2500
+ #undef DEACTIVATE_SERVOS_AFTER_MOVE
+ #if NUM_SERVOS == 1
+ #undef SERVO_DELAY
+ #define SERVO_DELAY { 50 }
#endif
-
- #define HAS_DEBUG_MENU (ENABLED(ULTIPANEL) && ENABLED(LCD_PROGRESS_BAR_TEST))
-
- // MK2 Multiplexer forces SINGLENOZZLE and kills DISABLE_INACTIVE_EXTRUDER
- #if ENABLED(MK2_MULTIPLEXER)
- #define SINGLENOZZLE
- #undef DISABLE_INACTIVE_EXTRUDER
+ #ifndef BLTOUCH_DELAY
+ #define BLTOUCH_DELAY 375
#endif
+ #undef Z_SERVO_ANGLES
+ #define Z_SERVO_ANGLES { BLTOUCH_DEPLOY, BLTOUCH_STOW }
- /**
- * Extruders have some combination of stepper motors and hotends
- * so we separate these concepts into the defines:
- *
- * EXTRUDERS - Number of Selectable Tools
- * HOTENDS - Number of hotends, whether connected or separate
- * E_STEPPERS - Number of actual E stepper motors
- * E_MANUAL - Number of E steppers for LCD move options
- *
- */
- #if ENABLED(SINGLENOZZLE) || ENABLED(MIXING_EXTRUDER) // One hotend, one thermistor, no XY offset
- #define HOTENDS 1
- #undef TEMP_SENSOR_1_AS_REDUNDANT
- #undef HOTEND_OFFSET_X
- #undef HOTEND_OFFSET_Y
- #else // Two hotends
- #define HOTENDS EXTRUDERS
- #if ENABLED(SWITCHING_NOZZLE) && !defined(HOTEND_OFFSET_Z)
- #define HOTEND_OFFSET_Z { 0 }
- #endif
- #endif
+ #define BLTOUCH_DEPLOY 10
+ #define BLTOUCH_STOW 90
+ #define BLTOUCH_SELFTEST 120
+ #define BLTOUCH_RESET 160
+ #define _TEST_BLTOUCH(P) (READ(P##_PIN) != P##_ENDSTOP_INVERTING)
- #if ENABLED(SWITCHING_EXTRUDER) // One stepper for every two EXTRUDERS
- #if EXTRUDERS > 4
- #define E_STEPPERS 3
- #define E_MANUAL 3
- #elif EXTRUDERS > 2
- #define E_STEPPERS 2
- #define E_MANUAL 2
- #else
- #define E_STEPPERS 1
- #endif
- #define E_MANUAL EXTRUDERS
- #elif ENABLED(MIXING_EXTRUDER)
- #define E_STEPPERS MIXING_STEPPERS
- #define E_MANUAL 1
+ // Always disable probe pin inverting for BLTouch
+ #undef Z_MIN_PROBE_ENDSTOP_INVERTING
+ #define Z_MIN_PROBE_ENDSTOP_INVERTING false
+
+ #if ENABLED(Z_MIN_PROBE_USES_Z_MIN_ENDSTOP_PIN)
+ #undef Z_MIN_ENDSTOP_INVERTING
+ #define Z_MIN_ENDSTOP_INVERTING Z_MIN_PROBE_ENDSTOP_INVERTING
+ #define TEST_BLTOUCH() _TEST_BLTOUCH(Z_MIN)
#else
- #define E_STEPPERS EXTRUDERS
- #define E_MANUAL EXTRUDERS
+ #define TEST_BLTOUCH() _TEST_BLTOUCH(Z_MIN_PROBE)
#endif
+#endif
- /**
- * DISTINCT_E_FACTORS affects how some E factors are accessed
- */
- #if ENABLED(DISTINCT_E_FACTORS) && E_STEPPERS > 1
- #define XYZE_N (XYZ + E_STEPPERS)
- #define E_AXIS_N (E_AXIS + extruder)
- #else
- #undef DISTINCT_E_FACTORS
- #define XYZE_N XYZE
- #define E_AXIS_N E_AXIS
- #endif
+/**
+ * Set a flag for a servo probe
+ */
+#define HAS_Z_SERVO_PROBE (defined(Z_PROBE_SERVO_NR) && Z_PROBE_SERVO_NR >= 0)
- /**
- * The BLTouch Probe emulates a servo probe
- * and uses "special" angles for its state.
- */
- #if ENABLED(BLTOUCH)
- #ifndef Z_PROBE_SERVO_NR
- #define Z_PROBE_SERVO_NR 0
- #endif
- #ifndef NUM_SERVOS
- #define NUM_SERVOS (Z_PROBE_SERVO_NR + 1)
- #endif
- #undef DEACTIVATE_SERVOS_AFTER_MOVE
- #if NUM_SERVOS == 1
- #undef SERVO_DELAY
- #define SERVO_DELAY { 50 }
- #endif
- #ifndef BLTOUCH_DELAY
- #define BLTOUCH_DELAY 375
- #endif
- #undef Z_SERVO_ANGLES
- #define Z_SERVO_ANGLES { BLTOUCH_DEPLOY, BLTOUCH_STOW }
+/**
+ * Set flags for enabled probes
+ */
+#define HAS_BED_PROBE (ENABLED(FIX_MOUNTED_PROBE) || ENABLED(Z_PROBE_ALLEN_KEY) || HAS_Z_SERVO_PROBE || ENABLED(Z_PROBE_SLED) || ENABLED(SOLENOID_PROBE))
+#define PROBE_SELECTED (HAS_BED_PROBE || ENABLED(PROBE_MANUALLY))
- #define BLTOUCH_DEPLOY 10
- #define BLTOUCH_STOW 90
- #define BLTOUCH_SELFTEST 120
- #define BLTOUCH_RESET 160
- #define _TEST_BLTOUCH(P) (READ(P##_PIN) != P##_ENDSTOP_INVERTING)
+#if !HAS_BED_PROBE
+ // Clear probe pin settings when no probe is selected
+ #undef Z_MIN_PROBE_USES_Z_MIN_ENDSTOP_PIN
+ #undef Z_MIN_PROBE_ENDSTOP
+#elif ENABLED(Z_PROBE_ALLEN_KEY)
+ // Extra test for Allen Key Probe
+ #define PROBE_IS_TRIGGERED_WHEN_STOWED_TEST
+#endif
- // Always disable probe pin inverting for BLTouch
- #undef Z_MIN_PROBE_ENDSTOP_INVERTING
- #define Z_MIN_PROBE_ENDSTOP_INVERTING false
+#define HOMING_Z_WITH_PROBE (HAS_BED_PROBE && Z_HOME_DIR < 0 && ENABLED(Z_MIN_PROBE_USES_Z_MIN_ENDSTOP_PIN))
- #if ENABLED(Z_MIN_PROBE_USES_Z_MIN_ENDSTOP_PIN)
- #undef Z_MIN_ENDSTOP_INVERTING
- #define Z_MIN_ENDSTOP_INVERTING Z_MIN_PROBE_ENDSTOP_INVERTING
- #define TEST_BLTOUCH() _TEST_BLTOUCH(Z_MIN)
- #else
- #define TEST_BLTOUCH() _TEST_BLTOUCH(Z_MIN_PROBE)
- #endif
- #endif
+#define HAS_SOFTWARE_ENDSTOPS (ENABLED(MIN_SOFTWARE_ENDSTOPS) || ENABLED(MAX_SOFTWARE_ENDSTOPS))
+#define HAS_RESUME_CONTINUE (ENABLED(NEWPANEL) || ENABLED(EMERGENCY_PARSER))
+#define HAS_COLOR_LEDS (ENABLED(BLINKM) || ENABLED(RGB_LED) || ENABLED(RGBW_LED) || ENABLED(PCA9632) || ENABLED(NEOPIXEL_LED))
- /**
- * Set a flag for a servo probe
- */
- #define HAS_Z_SERVO_PROBE (defined(Z_PROBE_SERVO_NR) && Z_PROBE_SERVO_NR >= 0)
-
- /**
- * Set flags for enabled probes
- */
- #define HAS_BED_PROBE (ENABLED(FIX_MOUNTED_PROBE) || ENABLED(Z_PROBE_ALLEN_KEY) || HAS_Z_SERVO_PROBE || ENABLED(Z_PROBE_SLED) || ENABLED(SOLENOID_PROBE))
- #define PROBE_SELECTED (HAS_BED_PROBE || ENABLED(PROBE_MANUALLY))
-
- #if !HAS_BED_PROBE
- // Clear probe pin settings when no probe is selected
- #undef Z_MIN_PROBE_USES_Z_MIN_ENDSTOP_PIN
- #undef Z_MIN_PROBE_ENDSTOP
- #elif ENABLED(Z_PROBE_ALLEN_KEY)
- // Extra test for Allen Key Probe
- #define PROBE_IS_TRIGGERED_WHEN_STOWED_TEST
- #endif
-
- #define HOMING_Z_WITH_PROBE (HAS_BED_PROBE && Z_HOME_DIR < 0 && ENABLED(Z_MIN_PROBE_USES_Z_MIN_ENDSTOP_PIN))
-
- #define HAS_SOFTWARE_ENDSTOPS (ENABLED(MIN_SOFTWARE_ENDSTOPS) || ENABLED(MAX_SOFTWARE_ENDSTOPS))
- #define HAS_RESUME_CONTINUE (ENABLED(NEWPANEL) || ENABLED(EMERGENCY_PARSER))
- #define HAS_COLOR_LEDS (ENABLED(BLINKM) || ENABLED(RGB_LED) || ENABLED(RGBW_LED) || ENABLED(PCA9632) || ENABLED(NEOPIXEL_LED))
+#define USE_MARLINSERIAL !(defined(__AVR__) && defined(USBCON))
#endif // CONDITIONALS_LCD_H
diff --git a/Marlin/Conditionals_post.h b/Marlin/Conditionals_post.h
index 73d5cb775a..2e91e44097 100644
--- a/Marlin/Conditionals_post.h
+++ b/Marlin/Conditionals_post.h
@@ -28,1279 +28,1296 @@
#ifndef CONDITIONALS_POST_H
#define CONDITIONALS_POST_H
- #define IS_SCARA (ENABLED(MORGAN_SCARA) || ENABLED(MAKERARM_SCARA))
- #define IS_KINEMATIC (ENABLED(DELTA) || IS_SCARA)
- #define IS_CARTESIAN !IS_KINEMATIC
+#define IS_SCARA (ENABLED(MORGAN_SCARA) || ENABLED(MAKERARM_SCARA))
+#define IS_KINEMATIC (ENABLED(DELTA) || IS_SCARA)
+#define IS_CARTESIAN !IS_KINEMATIC
- /**
- * Axis lengths and center
- */
- #define X_MAX_LENGTH (X_MAX_POS - (X_MIN_POS))
- #define Y_MAX_LENGTH (Y_MAX_POS - (Y_MIN_POS))
- #define Z_MAX_LENGTH (Z_MAX_POS - (Z_MIN_POS))
+/**
+ * Axis lengths and center
+ */
+#define X_MAX_LENGTH (X_MAX_POS - (X_MIN_POS))
+#define Y_MAX_LENGTH (Y_MAX_POS - (Y_MIN_POS))
+#define Z_MAX_LENGTH (Z_MAX_POS - (Z_MIN_POS))
- // Defined only if the sanity-check is bypassed
- #ifndef X_BED_SIZE
- #define X_BED_SIZE X_MAX_LENGTH
+// Defined only if the sanity-check is bypassed
+#ifndef X_BED_SIZE
+ #define X_BED_SIZE X_MAX_LENGTH
+#endif
+#ifndef Y_BED_SIZE
+ #define Y_BED_SIZE Y_MAX_LENGTH
+#endif
+
+// Require 0,0 bed center for Delta and SCARA
+#if IS_KINEMATIC
+ #define BED_CENTER_AT_0_0
+#endif
+
+// Define center values for future use
+#if ENABLED(BED_CENTER_AT_0_0)
+ #define X_CENTER 0
+ #define Y_CENTER 0
+#else
+ #define X_CENTER ((X_BED_SIZE) / 2)
+ #define Y_CENTER ((Y_BED_SIZE) / 2)
+#endif
+#define Z_CENTER ((Z_MIN_POS + Z_MAX_POS) / 2)
+
+// Get the linear boundaries of the bed
+#define X_MIN_BED (X_CENTER - (X_BED_SIZE) / 2)
+#define X_MAX_BED (X_CENTER + (X_BED_SIZE) / 2)
+#define Y_MIN_BED (Y_CENTER - (Y_BED_SIZE) / 2)
+#define Y_MAX_BED (Y_CENTER + (Y_BED_SIZE) / 2)
+
+/**
+ * CoreXY, CoreXZ, and CoreYZ - and their reverse
+ */
+#define CORE_IS_XY (ENABLED(COREXY) || ENABLED(COREYX))
+#define CORE_IS_XZ (ENABLED(COREXZ) || ENABLED(COREZX))
+#define CORE_IS_YZ (ENABLED(COREYZ) || ENABLED(COREZY))
+#define IS_CORE (CORE_IS_XY || CORE_IS_XZ || CORE_IS_YZ)
+#if IS_CORE
+ #if CORE_IS_XY
+ #define CORE_AXIS_1 A_AXIS
+ #define CORE_AXIS_2 B_AXIS
+ #define NORMAL_AXIS Z_AXIS
+ #elif CORE_IS_XZ
+ #define CORE_AXIS_1 A_AXIS
+ #define NORMAL_AXIS Y_AXIS
+ #define CORE_AXIS_2 C_AXIS
+ #elif CORE_IS_YZ
+ #define NORMAL_AXIS X_AXIS
+ #define CORE_AXIS_1 B_AXIS
+ #define CORE_AXIS_2 C_AXIS
#endif
- #ifndef Y_BED_SIZE
- #define Y_BED_SIZE Y_MAX_LENGTH
- #endif
-
- // Require 0,0 bed center for Delta and SCARA
- #if IS_KINEMATIC
- #define BED_CENTER_AT_0_0
- #endif
-
- // Define center values for future use
- #if ENABLED(BED_CENTER_AT_0_0)
- #define X_CENTER 0
- #define Y_CENTER 0
+ #if ENABLED(COREYX) || ENABLED(COREZX) || ENABLED(COREZY)
+ #define CORESIGN(n) (-(n))
#else
- #define X_CENTER ((X_BED_SIZE) / 2)
- #define Y_CENTER ((Y_BED_SIZE) / 2)
+ #define CORESIGN(n) (n)
#endif
- #define Z_CENTER ((Z_MIN_POS + Z_MAX_POS) / 2)
+#endif
- // Get the linear boundaries of the bed
- #define X_MIN_BED (X_CENTER - (X_BED_SIZE) / 2)
- #define X_MAX_BED (X_CENTER + (X_BED_SIZE) / 2)
- #define Y_MIN_BED (Y_CENTER - (Y_BED_SIZE) / 2)
- #define Y_MAX_BED (Y_CENTER + (Y_BED_SIZE) / 2)
+/**
+ * No adjustable bed on non-cartesians
+ */
+#if IS_KINEMATIC
+ #undef LEVEL_BED_CORNERS
+#endif
- /**
- * CoreXY, CoreXZ, and CoreYZ - and their reverse
- */
- #define CORE_IS_XY (ENABLED(COREXY) || ENABLED(COREYX))
- #define CORE_IS_XZ (ENABLED(COREXZ) || ENABLED(COREZX))
- #define CORE_IS_YZ (ENABLED(COREYZ) || ENABLED(COREZY))
- #define IS_CORE (CORE_IS_XY || CORE_IS_XZ || CORE_IS_YZ)
- #if IS_CORE
- #if CORE_IS_XY
- #define CORE_AXIS_1 A_AXIS
- #define CORE_AXIS_2 B_AXIS
- #define NORMAL_AXIS Z_AXIS
- #elif CORE_IS_XZ
- #define CORE_AXIS_1 A_AXIS
- #define NORMAL_AXIS Y_AXIS
- #define CORE_AXIS_2 C_AXIS
- #elif CORE_IS_YZ
- #define NORMAL_AXIS X_AXIS
- #define CORE_AXIS_1 B_AXIS
- #define CORE_AXIS_2 C_AXIS
- #endif
- #if ENABLED(COREYX) || ENABLED(COREZX) || ENABLED(COREZY)
- #define CORESIGN(n) (-(n))
- #else
- #define CORESIGN(n) (n)
- #endif
- #endif
+/**
+ * SCARA cannot use SLOWDOWN and requires QUICKHOME
+ */
+#if IS_SCARA
+ #undef SLOWDOWN
+ #define QUICK_HOME
+#endif
- /**
- * No adjustable bed on non-cartesians
- */
- #if IS_KINEMATIC
- #undef LEVEL_BED_CORNERS
- #endif
-
- /**
- * SCARA cannot use SLOWDOWN and requires QUICKHOME
- */
- #if IS_SCARA
- #undef SLOWDOWN
- #define QUICK_HOME
- #endif
-
- /**
- * Set the home position based on settings or manual overrides
- */
- #ifdef MANUAL_X_HOME_POS
- #define X_HOME_POS MANUAL_X_HOME_POS
- #elif ENABLED(BED_CENTER_AT_0_0)
- #if ENABLED(DELTA)
- #define X_HOME_POS 0
- #else
- #define X_HOME_POS ((X_BED_SIZE) * (X_HOME_DIR) * 0.5)
- #endif
- #else
- #if ENABLED(DELTA)
- #define X_HOME_POS (X_MIN_POS + (X_BED_SIZE) * 0.5)
- #else
- #define X_HOME_POS (X_HOME_DIR < 0 ? X_MIN_POS : X_MAX_POS)
- #endif
- #endif
-
- #ifdef MANUAL_Y_HOME_POS
- #define Y_HOME_POS MANUAL_Y_HOME_POS
- #elif ENABLED(BED_CENTER_AT_0_0)
- #if ENABLED(DELTA)
- #define Y_HOME_POS 0
- #else
- #define Y_HOME_POS ((Y_BED_SIZE) * (Y_HOME_DIR) * 0.5)
- #endif
- #else
- #if ENABLED(DELTA)
- #define Y_HOME_POS (Y_MIN_POS + (Y_BED_SIZE) * 0.5)
- #else
- #define Y_HOME_POS (Y_HOME_DIR < 0 ? Y_MIN_POS : Y_MAX_POS)
- #endif
- #endif
-
- #ifdef MANUAL_Z_HOME_POS
- #define Z_HOME_POS MANUAL_Z_HOME_POS
- #else
- #define Z_HOME_POS (Z_HOME_DIR < 0 ? Z_MIN_POS : Z_MAX_POS)
- #endif
-
- /**
- * If DELTA_HEIGHT isn't defined use the old setting
- */
- #if ENABLED(DELTA) && !defined(DELTA_HEIGHT)
- #define DELTA_HEIGHT Z_HOME_POS
- #endif
-
- /**
- * Z Sled Probe requires Z_SAFE_HOMING
- */
- #if ENABLED(Z_PROBE_SLED)
- #define Z_SAFE_HOMING
- #endif
-
- /**
- * DELTA should ignore Z_SAFE_HOMING and SLOWDOWN
- */
+/**
+ * Set the home position based on settings or manual overrides
+ */
+#ifdef MANUAL_X_HOME_POS
+ #define X_HOME_POS MANUAL_X_HOME_POS
+#elif ENABLED(BED_CENTER_AT_0_0)
#if ENABLED(DELTA)
- #undef Z_SAFE_HOMING
- #undef SLOWDOWN
- #endif
-
- /**
- * Safe Homing Options
- */
- #if ENABLED(Z_SAFE_HOMING)
- #ifndef Z_SAFE_HOMING_X_POINT
- #define Z_SAFE_HOMING_X_POINT X_CENTER
- #endif
- #ifndef Z_SAFE_HOMING_Y_POINT
- #define Z_SAFE_HOMING_Y_POINT Y_CENTER
- #endif
- #define X_TILT_FULCRUM Z_SAFE_HOMING_X_POINT
- #define Y_TILT_FULCRUM Z_SAFE_HOMING_Y_POINT
+ #define X_HOME_POS 0
#else
- #define X_TILT_FULCRUM X_HOME_POS
- #define Y_TILT_FULCRUM Y_HOME_POS
+ #define X_HOME_POS ((X_BED_SIZE) * (X_HOME_DIR) * 0.5)
#endif
-
- /**
- * Host keep alive
- */
- #ifndef DEFAULT_KEEPALIVE_INTERVAL
- #define DEFAULT_KEEPALIVE_INTERVAL 2
- #endif
-
- /**
- * Provide a MAX_AUTORETRACT for older configs
- */
- #if ENABLED(FWRETRACT) && !defined(MAX_AUTORETRACT)
- #define MAX_AUTORETRACT 99
- #endif
-
- /**
- * MAX_STEP_FREQUENCY differs for TOSHIBA
- */
- #if ENABLED(CONFIG_STEPPERS_TOSHIBA)
- #define MAX_STEP_FREQUENCY 10000 // Max step frequency for Toshiba Stepper Controllers
+#else
+ #if ENABLED(DELTA)
+ #define X_HOME_POS (X_MIN_POS + (X_BED_SIZE) * 0.5)
#else
- #define MAX_STEP_FREQUENCY 40000 // Max step frequency for Ultimaker (5000 pps / half step)
+ #define X_HOME_POS (X_HOME_DIR < 0 ? X_MIN_POS : X_MAX_POS)
#endif
+#endif
- // MS1 MS2 Stepper Driver Microstepping mode table
- #define MICROSTEP1 LOW,LOW
- #if ENABLED(HEROIC_STEPPER_DRIVERS)
- #define MICROSTEP128 LOW,HIGH
+#ifdef MANUAL_Y_HOME_POS
+ #define Y_HOME_POS MANUAL_Y_HOME_POS
+#elif ENABLED(BED_CENTER_AT_0_0)
+ #if ENABLED(DELTA)
+ #define Y_HOME_POS 0
#else
- #define MICROSTEP2 HIGH,LOW
- #define MICROSTEP4 LOW,HIGH
+ #define Y_HOME_POS ((Y_BED_SIZE) * (Y_HOME_DIR) * 0.5)
#endif
- #define MICROSTEP8 HIGH,HIGH
- #define MICROSTEP16 HIGH,HIGH
+#else
+ #if ENABLED(DELTA)
+ #define Y_HOME_POS (Y_MIN_POS + (Y_BED_SIZE) * 0.5)
+ #else
+ #define Y_HOME_POS (Y_HOME_DIR < 0 ? Y_MIN_POS : Y_MAX_POS)
+ #endif
+#endif
- /**
- * Override here because this is set in Configuration_adv.h
- */
- #if ENABLED(ULTIPANEL) && DISABLED(ELB_FULL_GRAPHIC_CONTROLLER)
- #undef SD_DETECT_INVERTED
- #endif
+#ifdef MANUAL_Z_HOME_POS
+ #define Z_HOME_POS MANUAL_Z_HOME_POS
+#else
+ #define Z_HOME_POS (Z_HOME_DIR < 0 ? Z_MIN_POS : Z_MAX_POS)
+#endif
- /**
- * Set defaults for missing (newer) options
- */
- #ifndef DISABLE_INACTIVE_X
- #define DISABLE_INACTIVE_X DISABLE_X
- #endif
- #ifndef DISABLE_INACTIVE_Y
- #define DISABLE_INACTIVE_Y DISABLE_Y
- #endif
- #ifndef DISABLE_INACTIVE_Z
- #define DISABLE_INACTIVE_Z DISABLE_Z
- #endif
- #ifndef DISABLE_INACTIVE_E
- #define DISABLE_INACTIVE_E DISABLE_E
- #endif
+/**
+ * If DELTA_HEIGHT isn't defined use the old setting
+ */
+#if ENABLED(DELTA) && !defined(DELTA_HEIGHT)
+ #define DELTA_HEIGHT Z_HOME_POS
+#endif
- // Power Signal Control Definitions
- // By default use ATX definition
- #ifndef POWER_SUPPLY
- #define POWER_SUPPLY 1
+/**
+ * Z Sled Probe requires Z_SAFE_HOMING
+ */
+#if ENABLED(Z_PROBE_SLED)
+ #define Z_SAFE_HOMING
+#endif
+
+/**
+ * DELTA should ignore Z_SAFE_HOMING and SLOWDOWN
+ */
+#if ENABLED(DELTA)
+ #undef Z_SAFE_HOMING
+ #undef SLOWDOWN
+#endif
+
+/**
+ * Safe Homing Options
+ */
+#if ENABLED(Z_SAFE_HOMING)
+ #ifndef Z_SAFE_HOMING_X_POINT
+ #define Z_SAFE_HOMING_X_POINT X_CENTER
#endif
- #if (POWER_SUPPLY == 1) // 1 = ATX
- #define PS_ON_AWAKE LOW
- #define PS_ON_ASLEEP HIGH
- #elif (POWER_SUPPLY == 2) // 2 = X-Box 360 203W
- #define PS_ON_AWAKE HIGH
- #define PS_ON_ASLEEP LOW
+ #ifndef Z_SAFE_HOMING_Y_POINT
+ #define Z_SAFE_HOMING_Y_POINT Y_CENTER
#endif
- #define HAS_POWER_SWITCH (POWER_SUPPLY > 0 && PIN_EXISTS(PS_ON))
+ #define X_TILT_FULCRUM Z_SAFE_HOMING_X_POINT
+ #define Y_TILT_FULCRUM Z_SAFE_HOMING_Y_POINT
+#else
+ #define X_TILT_FULCRUM X_HOME_POS
+ #define Y_TILT_FULCRUM Y_HOME_POS
+#endif
- /**
- * Temp Sensor defines
- */
- #if TEMP_SENSOR_0 == -4
- #define HEATER_0_USES_AD8495
- #elif TEMP_SENSOR_0 == -3
- #define HEATER_0_USES_MAX6675
- #define MAX6675_IS_MAX31855
- #define MAX6675_TMIN -270
- #define MAX6675_TMAX 1800
- #elif TEMP_SENSOR_0 == -2
- #define HEATER_0_USES_MAX6675
- #define MAX6675_TMIN 0
- #define MAX6675_TMAX 1024
- #elif TEMP_SENSOR_0 == -1
- #define HEATER_0_USES_AD595
- #elif TEMP_SENSOR_0 == 0
- #undef HEATER_0_MINTEMP
- #undef HEATER_0_MAXTEMP
- #elif TEMP_SENSOR_0 > 0
- #define THERMISTORHEATER_0 TEMP_SENSOR_0
- #define HEATER_0_USES_THERMISTOR
+/**
+ * Host keep alive
+ */
+#ifndef DEFAULT_KEEPALIVE_INTERVAL
+ #define DEFAULT_KEEPALIVE_INTERVAL 2
+#endif
+
+/**
+ * Provide a MAX_AUTORETRACT for older configs
+ */
+#if ENABLED(FWRETRACT) && !defined(MAX_AUTORETRACT)
+ #define MAX_AUTORETRACT 99
+#endif
+
+/**
+ * MAX_STEP_FREQUENCY differs for TOSHIBA
+ */
+#if ENABLED(CONFIG_STEPPERS_TOSHIBA)
+ #define MAX_STEP_FREQUENCY 10000 // Max step frequency for Toshiba Stepper Controllers
+#else
+ #define MAX_STEP_FREQUENCY 40000 // Max step frequency for Ultimaker (5000 pps / half step)
+#endif
+
+// MS1 MS2 Stepper Driver Microstepping mode table
+#define MICROSTEP1 LOW,LOW
+#if ENABLED(HEROIC_STEPPER_DRIVERS)
+ #define MICROSTEP128 LOW,HIGH
+#else
+ #define MICROSTEP2 HIGH,LOW
+ #define MICROSTEP4 LOW,HIGH
+#endif
+#define MICROSTEP8 HIGH,HIGH
+#define MICROSTEP16 HIGH,HIGH
+
+/**
+ * Override here because this is set in Configuration_adv.h
+ */
+#if ENABLED(ULTIPANEL) && DISABLED(ELB_FULL_GRAPHIC_CONTROLLER)
+ #undef SD_DETECT_INVERTED
+#endif
+
+/**
+ * Set defaults for missing (newer) options
+ */
+#ifndef DISABLE_INACTIVE_X
+ #define DISABLE_INACTIVE_X DISABLE_X
+#endif
+#ifndef DISABLE_INACTIVE_Y
+ #define DISABLE_INACTIVE_Y DISABLE_Y
+#endif
+#ifndef DISABLE_INACTIVE_Z
+ #define DISABLE_INACTIVE_Z DISABLE_Z
+#endif
+#ifndef DISABLE_INACTIVE_E
+ #define DISABLE_INACTIVE_E DISABLE_E
+#endif
+
+// Power Signal Control Definitions
+// By default use ATX definition
+#ifndef POWER_SUPPLY
+ #define POWER_SUPPLY 1
+#endif
+#if (POWER_SUPPLY == 1) // 1 = ATX
+ #define PS_ON_AWAKE LOW
+ #define PS_ON_ASLEEP HIGH
+#elif (POWER_SUPPLY == 2) // 2 = X-Box 360 203W
+ #define PS_ON_AWAKE HIGH
+ #define PS_ON_ASLEEP LOW
+#endif
+#define HAS_POWER_SWITCH (POWER_SUPPLY > 0 && PIN_EXISTS(PS_ON))
+
+/**
+ * Temp Sensor defines
+ */
+#if TEMP_SENSOR_0 == -4
+ #define HEATER_0_USES_AD8495
+#elif TEMP_SENSOR_0 == -3
+ #define HEATER_0_USES_MAX6675
+ #define MAX6675_IS_MAX31855
+ #define MAX6675_TMIN -270
+ #define MAX6675_TMAX 1800
+#elif TEMP_SENSOR_0 == -2
+ #define HEATER_0_USES_MAX6675
+ #define MAX6675_TMIN 0
+ #define MAX6675_TMAX 1024
+#elif TEMP_SENSOR_0 == -1
+ #define HEATER_0_USES_AD595
+#elif TEMP_SENSOR_0 == 0
+ #undef HEATER_0_MINTEMP
+ #undef HEATER_0_MAXTEMP
+#elif TEMP_SENSOR_0 > 0
+ #define THERMISTORHEATER_0 TEMP_SENSOR_0
+ #define HEATER_0_USES_THERMISTOR
+#endif
+
+#if TEMP_SENSOR_1 == -4
+ #define HEATER_1_USES_AD8495
+#elif TEMP_SENSOR_1 == -3
+ #error "MAX31855 Thermocouples (-3) not supported for TEMP_SENSOR_1."
+#elif TEMP_SENSOR_1 == -2
+ #error "MAX6675 Thermocouples (-2) not supported for TEMP_SENSOR_1."
+#elif TEMP_SENSOR_1 == -1
+ #define HEATER_1_USES_AD595
+#elif TEMP_SENSOR_1 == 0
+ #undef HEATER_1_MINTEMP
+ #undef HEATER_1_MAXTEMP
+#elif TEMP_SENSOR_1 > 0
+ #define THERMISTORHEATER_1 TEMP_SENSOR_1
+ #define HEATER_1_USES_THERMISTOR
+#endif
+
+#if TEMP_SENSOR_2 == -4
+ #define HEATER_2_USES_AD8495
+#elif TEMP_SENSOR_2 == -3
+ #error "MAX31855 Thermocouples (-3) not supported for TEMP_SENSOR_2."
+#elif TEMP_SENSOR_2 == -2
+ #error "MAX6675 Thermocouples (-2) not supported for TEMP_SENSOR_2."
+#elif TEMP_SENSOR_2 == -1
+ #define HEATER_2_USES_AD595
+#elif TEMP_SENSOR_2 == 0
+ #undef HEATER_2_MINTEMP
+ #undef HEATER_2_MAXTEMP
+#elif TEMP_SENSOR_2 > 0
+ #define THERMISTORHEATER_2 TEMP_SENSOR_2
+ #define HEATER_2_USES_THERMISTOR
+#endif
+
+#if TEMP_SENSOR_3 == -4
+ #define HEATER_3_USES_AD8495
+#elif TEMP_SENSOR_3 == -3
+ #error "MAX31855 Thermocouples (-3) not supported for TEMP_SENSOR_3."
+#elif TEMP_SENSOR_3 == -2
+ #error "MAX6675 Thermocouples (-2) not supported for TEMP_SENSOR_3."
+#elif TEMP_SENSOR_3 == -1
+ #define HEATER_3_USES_AD595
+#elif TEMP_SENSOR_3 == 0
+ #undef HEATER_3_MINTEMP
+ #undef HEATER_3_MAXTEMP
+#elif TEMP_SENSOR_3 > 0
+ #define THERMISTORHEATER_3 TEMP_SENSOR_3
+ #define HEATER_3_USES_THERMISTOR
+#endif
+
+#if TEMP_SENSOR_4 == -4
+ #define HEATER_4_USES_AD8495
+#elif TEMP_SENSOR_4 == -3
+ #error "MAX31855 Thermocouples (-3) not supported for TEMP_SENSOR_4."
+#elif TEMP_SENSOR_4 == -2
+ #error "MAX6675 Thermocouples (-2) not supported for TEMP_SENSOR_4."
+#elif TEMP_SENSOR_4 == -1
+ #define HEATER_4_USES_AD595
+#elif TEMP_SENSOR_4 == 0
+ #undef HEATER_4_MINTEMP
+ #undef HEATER_4_MAXTEMP
+#elif TEMP_SENSOR_4 > 0
+ #define THERMISTORHEATER_4 TEMP_SENSOR_4
+ #define HEATER_4_USES_THERMISTOR
+#endif
+
+#if TEMP_SENSOR_BED == -4
+ #define HEATER_BED_USES_AD8495
+#elif TEMP_SENSOR_BED == -3
+ #error "MAX31855 Thermocouples (-3) not supported for TEMP_SENSOR_BED."
+#elif TEMP_SENSOR_BED == -2
+ #error "MAX6675 Thermocouples (-2) not supported for TEMP_SENSOR_BED."
+#elif TEMP_SENSOR_BED == -1
+ #define HEATER_BED_USES_AD595
+#elif TEMP_SENSOR_BED == 0
+ #undef BED_MINTEMP
+ #undef BED_MAXTEMP
+#elif TEMP_SENSOR_BED > 0
+ #define THERMISTORBED TEMP_SENSOR_BED
+ #define HEATER_BED_USES_THERMISTOR
+#endif
+
+#if TEMP_SENSOR_CHAMBER == -4
+ #define HEATER_CHAMBER_USES_AD8495
+#elif TEMP_SENSOR_CHAMBER == -3
+ #error "MAX31855 Thermocouples (-3) not supported for TEMP_SENSOR_CHAMBER."
+#elif TEMP_SENSOR_CHAMBER == -2
+ #error "MAX6675 Thermocouples (-2) not supported for TEMP_SENSOR_CHAMBER."
+#elif TEMP_SENSOR_CHAMBER == -1
+ #define HEATER_CHAMBER_USES_AD595
+#elif TEMP_SENSOR_CHAMBER > 0
+ #define THERMISTORCHAMBER TEMP_SENSOR_CHAMBER
+ #define HEATER_CHAMBER_USES_THERMISTOR
+#endif
+
+#define HOTEND_USES_THERMISTOR (ENABLED(HEATER_0_USES_THERMISTOR) || ENABLED(HEATER_1_USES_THERMISTOR) || ENABLED(HEATER_2_USES_THERMISTOR) || ENABLED(HEATER_3_USES_THERMISTOR) || ENABLED(HEATER_4_USES_THERMISTOR))
+
+/**
+ * Default hotend offsets, if not defined
+ */
+#define HAS_HOTEND_OFFSET_Z (HOTENDS > 1 && (ENABLED(DUAL_X_CARRIAGE) || ENABLED(SWITCHING_NOZZLE) || ENABLED(PARKING_EXTRUDER)))
+#if HOTENDS > 1
+ #ifndef HOTEND_OFFSET_X
+ #define HOTEND_OFFSET_X { 0 } // X offsets for each extruder
#endif
-
- #if TEMP_SENSOR_1 == -4
- #define HEATER_1_USES_AD8495
- #elif TEMP_SENSOR_1 == -3
- #error "MAX31855 Thermocouples not supported for TEMP_SENSOR_1"
- #elif TEMP_SENSOR_1 == -2
- #error "MAX6675 Thermocouples not supported for TEMP_SENSOR_1"
- #elif TEMP_SENSOR_1 == -1
- #define HEATER_1_USES_AD595
- #elif TEMP_SENSOR_1 == 0
- #undef HEATER_1_MINTEMP
- #undef HEATER_1_MAXTEMP
- #elif TEMP_SENSOR_1 > 0
- #define THERMISTORHEATER_1 TEMP_SENSOR_1
- #define HEATER_1_USES_THERMISTOR
+ #ifndef HOTEND_OFFSET_Y
+ #define HOTEND_OFFSET_Y { 0 } // Y offsets for each extruder
#endif
-
- #if TEMP_SENSOR_2 == -4
- #define HEATER_2_USES_AD8495
- #elif TEMP_SENSOR_2 == -3
- #error "MAX31855 Thermocouples not supported for TEMP_SENSOR_2"
- #elif TEMP_SENSOR_2 == -2
- #error "MAX6675 Thermocouples not supported for TEMP_SENSOR_2"
- #elif TEMP_SENSOR_2 == -1
- #define HEATER_2_USES_AD595
- #elif TEMP_SENSOR_2 == 0
- #undef HEATER_2_MINTEMP
- #undef HEATER_2_MAXTEMP
- #elif TEMP_SENSOR_2 > 0
- #define THERMISTORHEATER_2 TEMP_SENSOR_2
- #define HEATER_2_USES_THERMISTOR
+ #if HAS_HOTEND_OFFSET_Z && !defined(HOTEND_OFFSET_Z)
+ #define HOTEND_OFFSET_Z { 0 }
#endif
+#endif
- #if TEMP_SENSOR_3 == -4
- #define HEATER_3_USES_AD8495
- #elif TEMP_SENSOR_3 == -3
- #error "MAX31855 Thermocouples not supported for TEMP_SENSOR_3"
- #elif TEMP_SENSOR_3 == -2
- #error "MAX6675 Thermocouples not supported for TEMP_SENSOR_3"
- #elif TEMP_SENSOR_3 == -1
- #define HEATER_3_USES_AD595
- #elif TEMP_SENSOR_3 == 0
- #undef HEATER_3_MINTEMP
- #undef HEATER_3_MAXTEMP
- #elif TEMP_SENSOR_3 > 0
- #define THERMISTORHEATER_3 TEMP_SENSOR_3
- #define HEATER_3_USES_THERMISTOR
- #endif
+/**
+ * ARRAY_BY_EXTRUDERS based on EXTRUDERS
+ */
+#define ARRAY_BY_EXTRUDERS(...) ARRAY_N(EXTRUDERS, __VA_ARGS__)
+#define ARRAY_BY_EXTRUDERS1(v1) ARRAY_BY_EXTRUDERS(v1, v1, v1, v1, v1, v1)
- #if TEMP_SENSOR_4 == -4
- #define HEATER_4_USES_AD8495
- #elif TEMP_SENSOR_4 == -3
- #error "MAX31855 Thermocouples not supported for TEMP_SENSOR_4"
- #elif TEMP_SENSOR_4 == -2
- #error "MAX6675 Thermocouples not supported for TEMP_SENSOR_4"
- #elif TEMP_SENSOR_4 == -1
- #define HEATER_4_USES_AD595
- #elif TEMP_SENSOR_4 == 0
- #undef HEATER_4_MINTEMP
- #undef HEATER_4_MAXTEMP
- #elif TEMP_SENSOR_4 > 0
- #define THERMISTORHEATER_4 TEMP_SENSOR_4
- #define HEATER_4_USES_THERMISTOR
- #endif
+/**
+ * ARRAY_BY_HOTENDS based on HOTENDS
+ */
+#define ARRAY_BY_HOTENDS(...) ARRAY_N(HOTENDS, __VA_ARGS__)
+#define ARRAY_BY_HOTENDS1(v1) ARRAY_BY_HOTENDS(v1, v1, v1, v1, v1, v1)
- #if TEMP_SENSOR_BED == -4
- #define BED_USES_AD8495
- #elif TEMP_SENSOR_BED == -3
- #error "MAX31855 Thermocouples not supported for TEMP_SENSOR_BED"
- #elif TEMP_SENSOR_BED == -2
- #error "MAX6675 Thermocouples not supported for TEMP_SENSOR_BED"
- #elif TEMP_SENSOR_BED == -1
- #define BED_USES_AD595
- #elif TEMP_SENSOR_BED == 0
- #undef BED_MINTEMP
- #undef BED_MAXTEMP
- #elif TEMP_SENSOR_BED > 0
- #define THERMISTORBED TEMP_SENSOR_BED
- #define BED_USES_THERMISTOR
- #endif
-
- #if TEMP_SENSOR_CHAMBER == -4
- #define CHAMBER_USES_AD8495
- #elif TEMP_SENSOR_CHAMBER == -3
- #error "MAX31855 Thermocouples not supported for TEMP_SENSOR_CHAMBER"
- #elif TEMP_SENSOR_CHAMBER == -2
- #error "MAX6675 Thermocouples not supported for TEMP_SENSOR_CHAMBER"
- #elif TEMP_SENSOR_CHAMBER == -1
- #define CHAMBER_USES_AD595
- #elif TEMP_SENSOR_CHAMBER > 0
- #define THERMISTORCHAMBER TEMP_SENSOR_CHAMBER
- #define CHAMBER_USES_THERMISTOR
- #endif
-
- #define HEATER_USES_AD8495 (ENABLED(HEATER_0_USES_AD8495) || ENABLED(HEATER_1_USES_AD8495) || ENABLED(HEATER_2_USES_AD8495) || ENABLED(HEATER_3_USES_AD8495) || ENABLED(HEATER_4_USES_AD8495))
- #define HEATER_USES_AD595 (ENABLED(HEATER_0_USES_AD595) || ENABLED(HEATER_1_USES_AD595) || ENABLED(HEATER_2_USES_AD595) || ENABLED(HEATER_3_USES_AD595) || ENABLED(HEATER_4_USES_AD595))
-
- /**
- * Default hotend offsets, if not defined
- */
- #if HOTENDS > 1
- #ifndef HOTEND_OFFSET_X
- #define HOTEND_OFFSET_X { 0 } // X offsets for each extruder
- #endif
- #ifndef HOTEND_OFFSET_Y
- #define HOTEND_OFFSET_Y { 0 } // Y offsets for each extruder
- #endif
- #if !defined(HOTEND_OFFSET_Z) && (ENABLED(DUAL_X_CARRIAGE) || ENABLED(SWITCHING_NOZZLE))
- #define HOTEND_OFFSET_Z { 0 }
- #endif
- #endif
-
- /**
- * ARRAY_BY_EXTRUDERS based on EXTRUDERS
- */
- #define ARRAY_BY_EXTRUDERS(...) ARRAY_N(EXTRUDERS, __VA_ARGS__)
- #define ARRAY_BY_EXTRUDERS1(v1) ARRAY_BY_EXTRUDERS(v1, v1, v1, v1, v1, v1)
-
- /**
- * ARRAY_BY_HOTENDS based on HOTENDS
- */
- #define ARRAY_BY_HOTENDS(...) ARRAY_N(HOTENDS, __VA_ARGS__)
- #define ARRAY_BY_HOTENDS1(v1) ARRAY_BY_HOTENDS(v1, v1, v1, v1, v1, v1)
-
- /**
- * X_DUAL_ENDSTOPS endstop reassignment
- */
- #if ENABLED(X_DUAL_ENDSTOPS)
- #if X_HOME_DIR > 0
- #if X2_USE_ENDSTOP == _XMIN_
- #define X2_MAX_ENDSTOP_INVERTING X_MIN_ENDSTOP_INVERTING
- #define X2_MAX_PIN X_MIN_PIN
- #elif X2_USE_ENDSTOP == _XMAX_
- #define X2_MAX_ENDSTOP_INVERTING X_MAX_ENDSTOP_INVERTING
- #define X2_MAX_PIN X_MAX_PIN
- #elif X2_USE_ENDSTOP == _YMIN_
- #define X2_MAX_ENDSTOP_INVERTING Y_MIN_ENDSTOP_INVERTING
- #define X2_MAX_PIN Y_MIN_PIN
- #elif X2_USE_ENDSTOP == _YMAX_
- #define X2_MAX_ENDSTOP_INVERTING Y_MAX_ENDSTOP_INVERTING
- #define X2_MAX_PIN Y_MAX_PIN
- #elif X2_USE_ENDSTOP == _ZMIN_
- #define X2_MAX_ENDSTOP_INVERTING Z_MIN_ENDSTOP_INVERTING
- #define X2_MAX_PIN Z_MIN_PIN
- #elif X2_USE_ENDSTOP == _ZMAX_
- #define X2_MAX_ENDSTOP_INVERTING Z_MAX_ENDSTOP_INVERTING
- #define X2_MAX_PIN Z_MAX_PIN
- #else
- #define X2_MAX_ENDSTOP_INVERTING false
- #endif
- #define X2_MIN_ENDSTOP_INVERTING false
+/**
+ * X_DUAL_ENDSTOPS endstop reassignment
+ */
+#if ENABLED(X_DUAL_ENDSTOPS)
+ #if X_HOME_DIR > 0
+ #if X2_USE_ENDSTOP == _XMIN_
+ #define X2_MAX_ENDSTOP_INVERTING X_MIN_ENDSTOP_INVERTING
+ #define X2_MAX_PIN X_MIN_PIN
+ #elif X2_USE_ENDSTOP == _XMAX_
+ #define X2_MAX_ENDSTOP_INVERTING X_MAX_ENDSTOP_INVERTING
+ #define X2_MAX_PIN X_MAX_PIN
+ #elif X2_USE_ENDSTOP == _YMIN_
+ #define X2_MAX_ENDSTOP_INVERTING Y_MIN_ENDSTOP_INVERTING
+ #define X2_MAX_PIN Y_MIN_PIN
+ #elif X2_USE_ENDSTOP == _YMAX_
+ #define X2_MAX_ENDSTOP_INVERTING Y_MAX_ENDSTOP_INVERTING
+ #define X2_MAX_PIN Y_MAX_PIN
+ #elif X2_USE_ENDSTOP == _ZMIN_
+ #define X2_MAX_ENDSTOP_INVERTING Z_MIN_ENDSTOP_INVERTING
+ #define X2_MAX_PIN Z_MIN_PIN
+ #elif X2_USE_ENDSTOP == _ZMAX_
+ #define X2_MAX_ENDSTOP_INVERTING Z_MAX_ENDSTOP_INVERTING
+ #define X2_MAX_PIN Z_MAX_PIN
#else
- #if X2_USE_ENDSTOP == _XMIN_
- #define X2_MIN_ENDSTOP_INVERTING X_MIN_ENDSTOP_INVERTING
- #define X2_MIN_PIN X_MIN_PIN
- #elif X2_USE_ENDSTOP == _XMAX_
- #define X2_MIN_ENDSTOP_INVERTING X_MAX_ENDSTOP_INVERTING
- #define X2_MIN_PIN X_MAX_PIN
- #elif X2_USE_ENDSTOP == _YMIN_
- #define X2_MIN_ENDSTOP_INVERTING Y_MIN_ENDSTOP_INVERTING
- #define X2_MIN_PIN Y_MIN_PIN
- #elif X2_USE_ENDSTOP == _YMAX_
- #define X2_MIN_ENDSTOP_INVERTING Y_MAX_ENDSTOP_INVERTING
- #define X2_MIN_PIN Y_MAX_PIN
- #elif X2_USE_ENDSTOP == _ZMIN_
- #define X2_MIN_ENDSTOP_INVERTING Z_MIN_ENDSTOP_INVERTING
- #define X2_MIN_PIN Z_MIN_PIN
- #elif X2_USE_ENDSTOP == _ZMAX_
- #define X2_MIN_ENDSTOP_INVERTING Z_MAX_ENDSTOP_INVERTING
- #define X2_MIN_PIN Z_MAX_PIN
- #else
- #define X2_MIN_ENDSTOP_INVERTING false
- #endif
#define X2_MAX_ENDSTOP_INVERTING false
#endif
- #endif
-
- // Is an endstop plug used for the X2 endstop?
- #define IS_X2_ENDSTOP(A,M) (ENABLED(X_DUAL_ENDSTOPS) && X2_USE_ENDSTOP == _##A##M##_)
-
- /**
- * Y_DUAL_ENDSTOPS endstop reassignment
- */
- #if ENABLED(Y_DUAL_ENDSTOPS)
- #if Y_HOME_DIR > 0
- #if Y2_USE_ENDSTOP == _XMIN_
- #define Y2_MAX_ENDSTOP_INVERTING X_MIN_ENDSTOP_INVERTING
- #define Y2_MAX_PIN X_MIN_PIN
- #elif Y2_USE_ENDSTOP == _XMAX_
- #define Y2_MAX_ENDSTOP_INVERTING X_MAX_ENDSTOP_INVERTING
- #define Y2_MAX_PIN X_MAX_PIN
- #elif Y2_USE_ENDSTOP == _YMIN_
- #define Y2_MAX_ENDSTOP_INVERTING Y_MIN_ENDSTOP_INVERTING
- #define Y2_MAX_PIN Y_MIN_PIN
- #elif Y2_USE_ENDSTOP == _YMAX_
- #define Y2_MAX_ENDSTOP_INVERTING Y_MAX_ENDSTOP_INVERTING
- #define Y2_MAX_PIN Y_MAX_PIN
- #elif Y2_USE_ENDSTOP == _ZMIN_
- #define Y2_MAX_ENDSTOP_INVERTING Z_MIN_ENDSTOP_INVERTING
- #define Y2_MAX_PIN Z_MIN_PIN
- #elif Y2_USE_ENDSTOP == _ZMAX_
- #define Y2_MAX_ENDSTOP_INVERTING Z_MAX_ENDSTOP_INVERTING
- #define Y2_MAX_PIN Z_MAX_PIN
- #else
- #define Y2_MAX_ENDSTOP_INVERTING false
- #endif
- #define Y2_MIN_ENDSTOP_INVERTING false
+ #define X2_MIN_ENDSTOP_INVERTING false
+ #else
+ #if X2_USE_ENDSTOP == _XMIN_
+ #define X2_MIN_ENDSTOP_INVERTING X_MIN_ENDSTOP_INVERTING
+ #define X2_MIN_PIN X_MIN_PIN
+ #elif X2_USE_ENDSTOP == _XMAX_
+ #define X2_MIN_ENDSTOP_INVERTING X_MAX_ENDSTOP_INVERTING
+ #define X2_MIN_PIN X_MAX_PIN
+ #elif X2_USE_ENDSTOP == _YMIN_
+ #define X2_MIN_ENDSTOP_INVERTING Y_MIN_ENDSTOP_INVERTING
+ #define X2_MIN_PIN Y_MIN_PIN
+ #elif X2_USE_ENDSTOP == _YMAX_
+ #define X2_MIN_ENDSTOP_INVERTING Y_MAX_ENDSTOP_INVERTING
+ #define X2_MIN_PIN Y_MAX_PIN
+ #elif X2_USE_ENDSTOP == _ZMIN_
+ #define X2_MIN_ENDSTOP_INVERTING Z_MIN_ENDSTOP_INVERTING
+ #define X2_MIN_PIN Z_MIN_PIN
+ #elif X2_USE_ENDSTOP == _ZMAX_
+ #define X2_MIN_ENDSTOP_INVERTING Z_MAX_ENDSTOP_INVERTING
+ #define X2_MIN_PIN Z_MAX_PIN
+ #else
+ #define X2_MIN_ENDSTOP_INVERTING false
+ #endif
+ #define X2_MAX_ENDSTOP_INVERTING false
+ #endif
+#endif
+
+// Is an endstop plug used for the X2 endstop?
+#define IS_X2_ENDSTOP(A,M) (ENABLED(X_DUAL_ENDSTOPS) && X2_USE_ENDSTOP == _##A##M##_)
+
+/**
+ * Y_DUAL_ENDSTOPS endstop reassignment
+ */
+#if ENABLED(Y_DUAL_ENDSTOPS)
+ #if Y_HOME_DIR > 0
+ #if Y2_USE_ENDSTOP == _XMIN_
+ #define Y2_MAX_ENDSTOP_INVERTING X_MIN_ENDSTOP_INVERTING
+ #define Y2_MAX_PIN X_MIN_PIN
+ #elif Y2_USE_ENDSTOP == _XMAX_
+ #define Y2_MAX_ENDSTOP_INVERTING X_MAX_ENDSTOP_INVERTING
+ #define Y2_MAX_PIN X_MAX_PIN
+ #elif Y2_USE_ENDSTOP == _YMIN_
+ #define Y2_MAX_ENDSTOP_INVERTING Y_MIN_ENDSTOP_INVERTING
+ #define Y2_MAX_PIN Y_MIN_PIN
+ #elif Y2_USE_ENDSTOP == _YMAX_
+ #define Y2_MAX_ENDSTOP_INVERTING Y_MAX_ENDSTOP_INVERTING
+ #define Y2_MAX_PIN Y_MAX_PIN
+ #elif Y2_USE_ENDSTOP == _ZMIN_
+ #define Y2_MAX_ENDSTOP_INVERTING Z_MIN_ENDSTOP_INVERTING
+ #define Y2_MAX_PIN Z_MIN_PIN
+ #elif Y2_USE_ENDSTOP == _ZMAX_
+ #define Y2_MAX_ENDSTOP_INVERTING Z_MAX_ENDSTOP_INVERTING
+ #define Y2_MAX_PIN Z_MAX_PIN
#else
- #if Y2_USE_ENDSTOP == _XMIN_
- #define Y2_MIN_ENDSTOP_INVERTING X_MIN_ENDSTOP_INVERTING
- #define Y2_MIN_PIN X_MIN_PIN
- #elif Y2_USE_ENDSTOP == _XMAX_
- #define Y2_MIN_ENDSTOP_INVERTING X_MAX_ENDSTOP_INVERTING
- #define Y2_MIN_PIN X_MAX_PIN
- #elif Y2_USE_ENDSTOP == _YMIN_
- #define Y2_MIN_ENDSTOP_INVERTING Y_MIN_ENDSTOP_INVERTING
- #define Y2_MIN_PIN Y_MIN_PIN
- #elif Y2_USE_ENDSTOP == _YMAX_
- #define Y2_MIN_ENDSTOP_INVERTING Y_MAX_ENDSTOP_INVERTING
- #define Y2_MIN_PIN Y_MAX_PIN
- #elif Y2_USE_ENDSTOP == _ZMIN_
- #define Y2_MIN_ENDSTOP_INVERTING Z_MIN_ENDSTOP_INVERTING
- #define Y2_MIN_PIN Z_MIN_PIN
- #elif Y2_USE_ENDSTOP == _ZMAX_
- #define Y2_MIN_ENDSTOP_INVERTING Z_MAX_ENDSTOP_INVERTING
- #define Y2_MIN_PIN Z_MAX_PIN
- #else
- #define Y2_MIN_ENDSTOP_INVERTING false
- #endif
#define Y2_MAX_ENDSTOP_INVERTING false
#endif
- #endif
-
- // Is an endstop plug used for the Y2 endstop or the bed probe?
- #define IS_Y2_ENDSTOP(A,M) (ENABLED(Y_DUAL_ENDSTOPS) && Y2_USE_ENDSTOP == _##A##M##_)
-
- /**
- * Z_DUAL_ENDSTOPS endstop reassignment
- */
- #if ENABLED(Z_DUAL_ENDSTOPS)
- #if Z_HOME_DIR > 0
- #if Z2_USE_ENDSTOP == _XMIN_
- #define Z2_MAX_ENDSTOP_INVERTING X_MIN_ENDSTOP_INVERTING
- #define Z2_MAX_PIN X_MIN_PIN
- #elif Z2_USE_ENDSTOP == _XMAX_
- #define Z2_MAX_ENDSTOP_INVERTING X_MAX_ENDSTOP_INVERTING
- #define Z2_MAX_PIN X_MAX_PIN
- #elif Z2_USE_ENDSTOP == _YMIN_
- #define Z2_MAX_ENDSTOP_INVERTING Y_MIN_ENDSTOP_INVERTING
- #define Z2_MAX_PIN Y_MIN_PIN
- #elif Z2_USE_ENDSTOP == _YMAX_
- #define Z2_MAX_ENDSTOP_INVERTING Y_MAX_ENDSTOP_INVERTING
- #define Z2_MAX_PIN Y_MAX_PIN
- #elif Z2_USE_ENDSTOP == _ZMIN_
- #define Z2_MAX_ENDSTOP_INVERTING Z_MIN_ENDSTOP_INVERTING
- #define Z2_MAX_PIN Z_MIN_PIN
- #elif Z2_USE_ENDSTOP == _ZMAX_
- #define Z2_MAX_ENDSTOP_INVERTING Z_MAX_ENDSTOP_INVERTING
- #define Z2_MAX_PIN Z_MAX_PIN
- #else
- #define Z2_MAX_ENDSTOP_INVERTING false
- #endif
- #define Z2_MIN_ENDSTOP_INVERTING false
+ #define Y2_MIN_ENDSTOP_INVERTING false
+ #else
+ #if Y2_USE_ENDSTOP == _XMIN_
+ #define Y2_MIN_ENDSTOP_INVERTING X_MIN_ENDSTOP_INVERTING
+ #define Y2_MIN_PIN X_MIN_PIN
+ #elif Y2_USE_ENDSTOP == _XMAX_
+ #define Y2_MIN_ENDSTOP_INVERTING X_MAX_ENDSTOP_INVERTING
+ #define Y2_MIN_PIN X_MAX_PIN
+ #elif Y2_USE_ENDSTOP == _YMIN_
+ #define Y2_MIN_ENDSTOP_INVERTING Y_MIN_ENDSTOP_INVERTING
+ #define Y2_MIN_PIN Y_MIN_PIN
+ #elif Y2_USE_ENDSTOP == _YMAX_
+ #define Y2_MIN_ENDSTOP_INVERTING Y_MAX_ENDSTOP_INVERTING
+ #define Y2_MIN_PIN Y_MAX_PIN
+ #elif Y2_USE_ENDSTOP == _ZMIN_
+ #define Y2_MIN_ENDSTOP_INVERTING Z_MIN_ENDSTOP_INVERTING
+ #define Y2_MIN_PIN Z_MIN_PIN
+ #elif Y2_USE_ENDSTOP == _ZMAX_
+ #define Y2_MIN_ENDSTOP_INVERTING Z_MAX_ENDSTOP_INVERTING
+ #define Y2_MIN_PIN Z_MAX_PIN
+ #else
+ #define Y2_MIN_ENDSTOP_INVERTING false
+ #endif
+ #define Y2_MAX_ENDSTOP_INVERTING false
+ #endif
+#endif
+
+// Is an endstop plug used for the Y2 endstop or the bed probe?
+#define IS_Y2_ENDSTOP(A,M) (ENABLED(Y_DUAL_ENDSTOPS) && Y2_USE_ENDSTOP == _##A##M##_)
+
+/**
+ * Z_DUAL_ENDSTOPS endstop reassignment
+ */
+#if ENABLED(Z_DUAL_ENDSTOPS)
+ #if Z_HOME_DIR > 0
+ #if Z2_USE_ENDSTOP == _XMIN_
+ #define Z2_MAX_ENDSTOP_INVERTING X_MIN_ENDSTOP_INVERTING
+ #define Z2_MAX_PIN X_MIN_PIN
+ #elif Z2_USE_ENDSTOP == _XMAX_
+ #define Z2_MAX_ENDSTOP_INVERTING X_MAX_ENDSTOP_INVERTING
+ #define Z2_MAX_PIN X_MAX_PIN
+ #elif Z2_USE_ENDSTOP == _YMIN_
+ #define Z2_MAX_ENDSTOP_INVERTING Y_MIN_ENDSTOP_INVERTING
+ #define Z2_MAX_PIN Y_MIN_PIN
+ #elif Z2_USE_ENDSTOP == _YMAX_
+ #define Z2_MAX_ENDSTOP_INVERTING Y_MAX_ENDSTOP_INVERTING
+ #define Z2_MAX_PIN Y_MAX_PIN
+ #elif Z2_USE_ENDSTOP == _ZMIN_
+ #define Z2_MAX_ENDSTOP_INVERTING Z_MIN_ENDSTOP_INVERTING
+ #define Z2_MAX_PIN Z_MIN_PIN
+ #elif Z2_USE_ENDSTOP == _ZMAX_
+ #define Z2_MAX_ENDSTOP_INVERTING Z_MAX_ENDSTOP_INVERTING
+ #define Z2_MAX_PIN Z_MAX_PIN
#else
- #if Z2_USE_ENDSTOP == _XMIN_
- #define Z2_MIN_ENDSTOP_INVERTING X_MIN_ENDSTOP_INVERTING
- #define Z2_MIN_PIN X_MIN_PIN
- #elif Z2_USE_ENDSTOP == _XMAX_
- #define Z2_MIN_ENDSTOP_INVERTING X_MAX_ENDSTOP_INVERTING
- #define Z2_MIN_PIN X_MAX_PIN
- #elif Z2_USE_ENDSTOP == _YMIN_
- #define Z2_MIN_ENDSTOP_INVERTING Y_MIN_ENDSTOP_INVERTING
- #define Z2_MIN_PIN Y_MIN_PIN
- #elif Z2_USE_ENDSTOP == _YMAX_
- #define Z2_MIN_ENDSTOP_INVERTING Y_MAX_ENDSTOP_INVERTING
- #define Z2_MIN_PIN Y_MAX_PIN
- #elif Z2_USE_ENDSTOP == _ZMIN_
- #define Z2_MIN_ENDSTOP_INVERTING Z_MIN_ENDSTOP_INVERTING
- #define Z2_MIN_PIN Z_MIN_PIN
- #elif Z2_USE_ENDSTOP == _ZMAX_
- #define Z2_MIN_ENDSTOP_INVERTING Z_MAX_ENDSTOP_INVERTING
- #define Z2_MIN_PIN Z_MAX_PIN
- #else
- #define Z2_MIN_ENDSTOP_INVERTING false
- #endif
#define Z2_MAX_ENDSTOP_INVERTING false
#endif
- #endif
-
- // Is an endstop plug used for the Z2 endstop or the bed probe?
- #define IS_Z2_OR_PROBE(A,M) ( \
- (ENABLED(Z_DUAL_ENDSTOPS) && Z2_USE_ENDSTOP == _##A##M##_) \
- || (ENABLED(Z_MIN_PROBE_ENDSTOP) && Z_MIN_PROBE_PIN == A##_##M##_PIN ) )
-
- /**
- * Set ENDSTOPPULLUPS for active endstop switches
- */
- #if ENABLED(ENDSTOPPULLUPS)
- #if ENABLED(USE_XMAX_PLUG)
- #define ENDSTOPPULLUP_XMAX
- #endif
- #if ENABLED(USE_YMAX_PLUG)
- #define ENDSTOPPULLUP_YMAX
- #endif
- #if ENABLED(USE_ZMAX_PLUG)
- #define ENDSTOPPULLUP_ZMAX
- #endif
- #if ENABLED(USE_XMIN_PLUG)
- #define ENDSTOPPULLUP_XMIN
- #endif
- #if ENABLED(USE_YMIN_PLUG)
- #define ENDSTOPPULLUP_YMIN
- #endif
- #if ENABLED(USE_ZMIN_PLUG)
- #define ENDSTOPPULLUP_ZMIN
- #endif
- #endif
-
- /**
- * Shorthand for pin tests, used wherever needed
- */
-
- // Steppers
- #define HAS_X_ENABLE (PIN_EXISTS(X_ENABLE))
- #define HAS_X_DIR (PIN_EXISTS(X_DIR))
- #define HAS_X_STEP (PIN_EXISTS(X_STEP))
- #define HAS_X_MICROSTEPS (PIN_EXISTS(X_MS1))
-
- #define HAS_X2_ENABLE (PIN_EXISTS(X2_ENABLE))
- #define HAS_X2_DIR (PIN_EXISTS(X2_DIR))
- #define HAS_X2_STEP (PIN_EXISTS(X2_STEP))
- #define HAS_Y_MICROSTEPS (PIN_EXISTS(Y_MS1))
-
- #define HAS_Y_ENABLE (PIN_EXISTS(Y_ENABLE))
- #define HAS_Y_DIR (PIN_EXISTS(Y_DIR))
- #define HAS_Y_STEP (PIN_EXISTS(Y_STEP))
- #define HAS_Z_MICROSTEPS (PIN_EXISTS(Z_MS1))
-
- #define HAS_Y2_ENABLE (PIN_EXISTS(Y2_ENABLE))
- #define HAS_Y2_DIR (PIN_EXISTS(Y2_DIR))
- #define HAS_Y2_STEP (PIN_EXISTS(Y2_STEP))
-
- #define HAS_Z_ENABLE (PIN_EXISTS(Z_ENABLE))
- #define HAS_Z_DIR (PIN_EXISTS(Z_DIR))
- #define HAS_Z_STEP (PIN_EXISTS(Z_STEP))
-
- #define HAS_Z2_ENABLE (PIN_EXISTS(Z2_ENABLE))
- #define HAS_Z2_DIR (PIN_EXISTS(Z2_DIR))
- #define HAS_Z2_STEP (PIN_EXISTS(Z2_STEP))
-
- // Extruder steppers and solenoids
- #define HAS_E0_ENABLE (PIN_EXISTS(E0_ENABLE))
- #define HAS_E0_DIR (PIN_EXISTS(E0_DIR))
- #define HAS_E0_STEP (PIN_EXISTS(E0_STEP))
- #define HAS_E0_MICROSTEPS (PIN_EXISTS(E0_MS1))
- #define HAS_SOLENOID_0 (PIN_EXISTS(SOL0))
-
- #define HAS_E1_ENABLE (PIN_EXISTS(E1_ENABLE))
- #define HAS_E1_DIR (PIN_EXISTS(E1_DIR))
- #define HAS_E1_STEP (PIN_EXISTS(E1_STEP))
- #define HAS_E1_MICROSTEPS (PIN_EXISTS(E1_MS1))
- #define HAS_SOLENOID_1 (PIN_EXISTS(SOL1))
-
- #define HAS_E2_ENABLE (PIN_EXISTS(E2_ENABLE))
- #define HAS_E2_DIR (PIN_EXISTS(E2_DIR))
- #define HAS_E2_STEP (PIN_EXISTS(E2_STEP))
- #define HAS_E2_MICROSTEPS (PIN_EXISTS(E2_MS1))
- #define HAS_SOLENOID_2 (PIN_EXISTS(SOL2))
-
- #define HAS_E3_ENABLE (PIN_EXISTS(E3_ENABLE))
- #define HAS_E3_DIR (PIN_EXISTS(E3_DIR))
- #define HAS_E3_STEP (PIN_EXISTS(E3_STEP))
- #define HAS_E3_MICROSTEPS (PIN_EXISTS(E3_MS1))
- #define HAS_SOLENOID_3 (PIN_EXISTS(SOL3))
-
- #define HAS_E4_ENABLE (PIN_EXISTS(E4_ENABLE))
- #define HAS_E4_DIR (PIN_EXISTS(E4_DIR))
- #define HAS_E4_STEP (PIN_EXISTS(E4_STEP))
- #define HAS_E4_MICROSTEPS (PIN_EXISTS(E4_MS1))
- #define HAS_SOLENOID_4 (PIN_EXISTS(SOL4))
-
- // Trinamic Stepper Drivers
- #define HAS_TRINAMIC (ENABLED(HAVE_TMC2130) || ENABLED(HAVE_TMC2208) || ENABLED(IS_TRAMS))
- #define X_IS_TRINAMIC (ENABLED( X_IS_TMC2130) || ENABLED( X_IS_TMC2208) || ENABLED(IS_TRAMS))
- #define X2_IS_TRINAMIC (ENABLED(X2_IS_TMC2130) || ENABLED(X2_IS_TMC2208))
- #define Y_IS_TRINAMIC (ENABLED( Y_IS_TMC2130) || ENABLED( Y_IS_TMC2208) || ENABLED(IS_TRAMS))
- #define Y2_IS_TRINAMIC (ENABLED(Y2_IS_TMC2130) || ENABLED(Y2_IS_TMC2208))
- #define Z_IS_TRINAMIC (ENABLED( Z_IS_TMC2130) || ENABLED( Z_IS_TMC2208) || ENABLED(IS_TRAMS))
- #define Z2_IS_TRINAMIC (ENABLED(Z2_IS_TMC2130) || ENABLED(Z2_IS_TMC2208))
- #define E0_IS_TRINAMIC (ENABLED(E0_IS_TMC2130) || ENABLED(E0_IS_TMC2208) || ENABLED(IS_TRAMS))
- #define E1_IS_TRINAMIC (ENABLED(E1_IS_TMC2130) || ENABLED(E1_IS_TMC2208))
- #define E2_IS_TRINAMIC (ENABLED(E2_IS_TMC2130) || ENABLED(E2_IS_TMC2208))
- #define E3_IS_TRINAMIC (ENABLED(E3_IS_TMC2130) || ENABLED(E3_IS_TMC2208))
- #define E4_IS_TRINAMIC (ENABLED(E4_IS_TMC2130) || ENABLED(E4_IS_TMC2208))
-
- #if ENABLED(SENSORLESS_HOMING)
- // Disable Z axis sensorless homing if a probe is used to home the Z axis
- #if HOMING_Z_WITH_PROBE
- #undef Z_HOMING_SENSITIVITY
- #endif
- #define X_SENSORLESS (ENABLED(X_IS_TMC2130) && defined(X_HOMING_SENSITIVITY))
- #define Y_SENSORLESS (ENABLED(Y_IS_TMC2130) && defined(Y_HOMING_SENSITIVITY))
- #define Z_SENSORLESS (ENABLED(Z_IS_TMC2130) && defined(Z_HOMING_SENSITIVITY))
- #endif
-
- // Endstops and bed probe
- #define HAS_STOP_TEST(A,M) (PIN_EXISTS(A##_##M) && !IS_X2_ENDSTOP(A,M) && !IS_Y2_ENDSTOP(A,M) && !IS_Z2_OR_PROBE(A,M))
- #define HAS_X_MIN HAS_STOP_TEST(X,MIN)
- #define HAS_X_MAX HAS_STOP_TEST(X,MAX)
- #define HAS_Y_MIN HAS_STOP_TEST(Y,MIN)
- #define HAS_Y_MAX HAS_STOP_TEST(Y,MAX)
- #define HAS_Z_MIN HAS_STOP_TEST(Z,MIN)
- #define HAS_Z_MAX HAS_STOP_TEST(Z,MAX)
- #define HAS_X2_MIN (PIN_EXISTS(X2_MIN))
- #define HAS_X2_MAX (PIN_EXISTS(X2_MAX))
- #define HAS_Y2_MIN (PIN_EXISTS(Y2_MIN))
- #define HAS_Y2_MAX (PIN_EXISTS(Y2_MAX))
- #define HAS_Z2_MIN (PIN_EXISTS(Z2_MIN))
- #define HAS_Z2_MAX (PIN_EXISTS(Z2_MAX))
- #define HAS_Z_MIN_PROBE_PIN (PIN_EXISTS(Z_MIN_PROBE))
-
- // ADC Temp Sensors (Thermistor or Thermocouple with amplifier ADC interface)
- #define HAS_ADC_TEST(P) (PIN_EXISTS(TEMP_##P) && TEMP_SENSOR_##P != 0 && TEMP_SENSOR_##P > -2)
- #define HAS_TEMP_ADC_0 (HAS_ADC_TEST(0) && DISABLED(HEATER_0_USES_MAX6675))
- #define HAS_TEMP_ADC_1 HAS_ADC_TEST(1)
- #define HAS_TEMP_ADC_2 HAS_ADC_TEST(2)
- #define HAS_TEMP_ADC_3 HAS_ADC_TEST(3)
- #define HAS_TEMP_ADC_4 HAS_ADC_TEST(4)
- #define HAS_TEMP_ADC_BED HAS_ADC_TEST(BED)
- #define HAS_TEMP_ADC_CHAMBER HAS_ADC_TEST(CHAMBER)
-
- #define HAS_TEMP_HOTEND (HAS_TEMP_ADC_0 || ENABLED(HEATER_0_USES_MAX6675))
- #define HAS_TEMP_BED HAS_TEMP_ADC_BED
- #define HAS_TEMP_CHAMBER HAS_TEMP_ADC_CHAMBER
-
- // Heaters
- #define HAS_HEATER_0 (PIN_EXISTS(HEATER_0))
- #define HAS_HEATER_1 (PIN_EXISTS(HEATER_1))
- #define HAS_HEATER_2 (PIN_EXISTS(HEATER_2))
- #define HAS_HEATER_3 (PIN_EXISTS(HEATER_3))
- #define HAS_HEATER_4 (PIN_EXISTS(HEATER_4))
- #define HAS_HEATER_BED (PIN_EXISTS(HEATER_BED))
-
- // Shorthand for common combinations
- #define HAS_HEATED_BED (HAS_TEMP_BED && HAS_HEATER_BED)
- #define HAS_TEMP_SENSOR (HAS_TEMP_HOTEND || HAS_HEATED_BED || HAS_TEMP_CHAMBER)
-
- // PID heating
- #if !HAS_HEATED_BED
- #undef PIDTEMPBED
- #endif
- #define HAS_PID_HEATING (ENABLED(PIDTEMP) || ENABLED(PIDTEMPBED))
- #define HAS_PID_FOR_BOTH (ENABLED(PIDTEMP) && ENABLED(PIDTEMPBED))
-
- // Thermal protection
- #define HAS_THERMALLY_PROTECTED_BED (HAS_HEATED_BED && ENABLED(THERMAL_PROTECTION_BED))
- #define WATCH_HOTENDS (ENABLED(THERMAL_PROTECTION_HOTENDS) && WATCH_TEMP_PERIOD > 0)
- #define WATCH_THE_BED (HAS_THERMALLY_PROTECTED_BED && WATCH_BED_TEMP_PERIOD > 0)
-
- // Auto fans
- #define HAS_AUTO_FAN_0 (PIN_EXISTS(E0_AUTO_FAN))
- #define HAS_AUTO_FAN_1 (HOTENDS > 1 && PIN_EXISTS(E1_AUTO_FAN))
- #define HAS_AUTO_FAN_2 (HOTENDS > 2 && PIN_EXISTS(E2_AUTO_FAN))
- #define HAS_AUTO_FAN_3 (HOTENDS > 3 && PIN_EXISTS(E3_AUTO_FAN))
- #define HAS_AUTO_FAN_4 (HOTENDS > 4 && PIN_EXISTS(E4_AUTO_FAN))
- #define HAS_AUTO_CHAMBER_FAN (PIN_EXISTS(CHAMBER_AUTO_FAN))
- #define HAS_AUTO_FAN (HAS_AUTO_FAN_0 || HAS_AUTO_FAN_1 || HAS_AUTO_FAN_2 || HAS_AUTO_FAN_3 || HAS_AUTO_CHAMBER_FAN)
- #define AUTO_1_IS_0 (E1_AUTO_FAN_PIN == E0_AUTO_FAN_PIN)
- #define AUTO_2_IS_0 (E2_AUTO_FAN_PIN == E0_AUTO_FAN_PIN)
- #define AUTO_2_IS_1 (E2_AUTO_FAN_PIN == E1_AUTO_FAN_PIN)
- #define AUTO_3_IS_0 (E3_AUTO_FAN_PIN == E0_AUTO_FAN_PIN)
- #define AUTO_3_IS_1 (E3_AUTO_FAN_PIN == E1_AUTO_FAN_PIN)
- #define AUTO_3_IS_2 (E3_AUTO_FAN_PIN == E2_AUTO_FAN_PIN)
- #define AUTO_4_IS_0 (E4_AUTO_FAN_PIN == E0_AUTO_FAN_PIN)
- #define AUTO_4_IS_1 (E4_AUTO_FAN_PIN == E1_AUTO_FAN_PIN)
- #define AUTO_4_IS_2 (E4_AUTO_FAN_PIN == E2_AUTO_FAN_PIN)
- #define AUTO_4_IS_3 (E4_AUTO_FAN_PIN == E3_AUTO_FAN_PIN)
- #define AUTO_CHAMBER_IS_0 (CHAMBER_AUTO_FAN_PIN == E0_AUTO_FAN_PIN)
- #define AUTO_CHAMBER_IS_1 (CHAMBER_AUTO_FAN_PIN == E1_AUTO_FAN_PIN)
- #define AUTO_CHAMBER_IS_2 (CHAMBER_AUTO_FAN_PIN == E2_AUTO_FAN_PIN)
- #define AUTO_CHAMBER_IS_3 (CHAMBER_AUTO_FAN_PIN == E3_AUTO_FAN_PIN)
- #define AUTO_CHAMBER_IS_4 (CHAMBER_AUTO_FAN_PIN == E4_AUTO_FAN_PIN)
-
- // Other fans
- #define HAS_FAN0 (PIN_EXISTS(FAN))
- #define HAS_FAN1 (PIN_EXISTS(FAN1) && CONTROLLER_FAN_PIN != FAN1_PIN && E0_AUTO_FAN_PIN != FAN1_PIN && E1_AUTO_FAN_PIN != FAN1_PIN && E2_AUTO_FAN_PIN != FAN1_PIN && E3_AUTO_FAN_PIN != FAN1_PIN)
- #define HAS_FAN2 (PIN_EXISTS(FAN2) && CONTROLLER_FAN_PIN != FAN2_PIN && E0_AUTO_FAN_PIN != FAN2_PIN && E1_AUTO_FAN_PIN != FAN2_PIN && E2_AUTO_FAN_PIN != FAN2_PIN && E3_AUTO_FAN_PIN != FAN2_PIN)
- #define HAS_CONTROLLER_FAN (PIN_EXISTS(CONTROLLER_FAN))
-
- // Servos
- #define HAS_SERVO_0 (PIN_EXISTS(SERVO0))
- #define HAS_SERVO_1 (PIN_EXISTS(SERVO1))
- #define HAS_SERVO_2 (PIN_EXISTS(SERVO2))
- #define HAS_SERVO_3 (PIN_EXISTS(SERVO3))
- #define HAS_SERVOS (defined(NUM_SERVOS) && NUM_SERVOS > 0 && (HAS_SERVO_0 || HAS_SERVO_1 || HAS_SERVO_2 || HAS_SERVO_3))
-
- #if HAS_SERVOS && !defined(Z_PROBE_SERVO_NR)
- #define Z_PROBE_SERVO_NR -1
- #endif
-
- // Sensors
- #define HAS_FILAMENT_WIDTH_SENSOR (PIN_EXISTS(FILWIDTH))
-
- // User Interface
- #define HAS_HOME (PIN_EXISTS(HOME))
- #define HAS_KILL (PIN_EXISTS(KILL))
- #define HAS_SUICIDE (PIN_EXISTS(SUICIDE))
- #define HAS_PHOTOGRAPH (PIN_EXISTS(PHOTOGRAPH))
- #define HAS_BUZZER (PIN_EXISTS(BEEPER) || ENABLED(LCD_USE_I2C_BUZZER))
- #define HAS_CASE_LIGHT (PIN_EXISTS(CASE_LIGHT) && ENABLED(CASE_LIGHT_ENABLE))
-
- // Digital control
- #define HAS_MICROSTEPS (HAS_X_MICROSTEPS || HAS_Y_MICROSTEPS || HAS_Z_MICROSTEPS || HAS_E0_MICROSTEPS || HAS_E1_MICROSTEPS || HAS_E2_MICROSTEPS || HAS_E3_MICROSTEPS || HAS_E4_MICROSTEPS)
- #define HAS_STEPPER_RESET (PIN_EXISTS(STEPPER_RESET))
- #define HAS_DIGIPOTSS (PIN_EXISTS(DIGIPOTSS))
- #define HAS_MOTOR_CURRENT_PWM (PIN_EXISTS(MOTOR_CURRENT_PWM_XY) || PIN_EXISTS(MOTOR_CURRENT_PWM_Z) || PIN_EXISTS(MOTOR_CURRENT_PWM_E))
-
- #if !HAS_TEMP_SENSOR
- #undef AUTO_REPORT_TEMPERATURES
- #endif
-
- #define HAS_AUTO_REPORTING (ENABLED(AUTO_REPORT_TEMPERATURES) || ENABLED(AUTO_REPORT_SD_STATUS))
-
- /**
- * This setting is also used by M109 when trying to calculate
- * a ballpark safe margin to prevent wait-forever situation.
- */
- #ifndef EXTRUDE_MINTEMP
- #define EXTRUDE_MINTEMP 170
- #endif
-
- /**
- * Helper Macros for heaters and extruder fan
- */
- #define WRITE_HEATER_0P(v) WRITE(HEATER_0_PIN, v)
- #if HOTENDS > 1 || ENABLED(HEATERS_PARALLEL)
- #define WRITE_HEATER_1(v) WRITE(HEATER_1_PIN, v)
- #if HOTENDS > 2
- #define WRITE_HEATER_2(v) WRITE(HEATER_2_PIN, v)
- #if HOTENDS > 3
- #define WRITE_HEATER_3(v) WRITE(HEATER_3_PIN, v)
- #if HOTENDS > 4
- #define WRITE_HEATER_4(v) WRITE(HEATER_4_PIN, v)
- #endif // HOTENDS > 4
- #endif // HOTENDS > 3
- #endif // HOTENDS > 2
- #endif // HOTENDS > 1
- #if ENABLED(HEATERS_PARALLEL)
- #define WRITE_HEATER_0(v) { WRITE_HEATER_0P(v); WRITE_HEATER_1(v); }
+ #define Z2_MIN_ENDSTOP_INVERTING false
#else
- #define WRITE_HEATER_0(v) WRITE_HEATER_0P(v)
- #endif
-
- /**
- * Heated bed requires settings
- */
- #if HAS_HEATED_BED
- #ifndef MAX_BED_POWER
- #define MAX_BED_POWER 255
+ #if Z2_USE_ENDSTOP == _XMIN_
+ #define Z2_MIN_ENDSTOP_INVERTING X_MIN_ENDSTOP_INVERTING
+ #define Z2_MIN_PIN X_MIN_PIN
+ #elif Z2_USE_ENDSTOP == _XMAX_
+ #define Z2_MIN_ENDSTOP_INVERTING X_MAX_ENDSTOP_INVERTING
+ #define Z2_MIN_PIN X_MAX_PIN
+ #elif Z2_USE_ENDSTOP == _YMIN_
+ #define Z2_MIN_ENDSTOP_INVERTING Y_MIN_ENDSTOP_INVERTING
+ #define Z2_MIN_PIN Y_MIN_PIN
+ #elif Z2_USE_ENDSTOP == _YMAX_
+ #define Z2_MIN_ENDSTOP_INVERTING Y_MAX_ENDSTOP_INVERTING
+ #define Z2_MIN_PIN Y_MAX_PIN
+ #elif Z2_USE_ENDSTOP == _ZMIN_
+ #define Z2_MIN_ENDSTOP_INVERTING Z_MIN_ENDSTOP_INVERTING
+ #define Z2_MIN_PIN Z_MIN_PIN
+ #elif Z2_USE_ENDSTOP == _ZMAX_
+ #define Z2_MIN_ENDSTOP_INVERTING Z_MAX_ENDSTOP_INVERTING
+ #define Z2_MIN_PIN Z_MAX_PIN
+ #else
+ #define Z2_MIN_ENDSTOP_INVERTING false
#endif
- #ifndef HEATER_BED_INVERTING
- #define HEATER_BED_INVERTING false
- #endif
- #define WRITE_HEATER_BED(v) WRITE(HEATER_BED_PIN, (v) ^ HEATER_BED_INVERTING)
+ #define Z2_MAX_ENDSTOP_INVERTING false
#endif
+#endif
- /**
- * Up to 3 PWM fans
- */
- #if HAS_FAN2
- #define FAN_COUNT 3
- #elif HAS_FAN1
- #define FAN_COUNT 2
- #elif HAS_FAN0
- #define FAN_COUNT 1
- #else
- #define FAN_COUNT 0
- #endif
+// Is an endstop plug used for the Z2 endstop or the bed probe?
+#define IS_Z2_OR_PROBE(A,M) ( \
+ (ENABLED(Z_DUAL_ENDSTOPS) && Z2_USE_ENDSTOP == _##A##M##_) \
+ || (ENABLED(Z_MIN_PROBE_ENDSTOP) && Z_MIN_PROBE_PIN == A##_##M##_PIN ) )
- #if HAS_FAN0
- #define WRITE_FAN(v) WRITE(FAN_PIN, v)
- #define WRITE_FAN0(v) WRITE_FAN(v)
+/**
+ * Set ENDSTOPPULLUPS for active endstop switches
+ */
+#if ENABLED(ENDSTOPPULLUPS)
+ #if ENABLED(USE_XMAX_PLUG)
+ #define ENDSTOPPULLUP_XMAX
#endif
- #if HAS_FAN1
- #define WRITE_FAN1(v) WRITE(FAN1_PIN, v)
+ #if ENABLED(USE_YMAX_PLUG)
+ #define ENDSTOPPULLUP_YMAX
#endif
- #if HAS_FAN2
- #define WRITE_FAN2(v) WRITE(FAN2_PIN, v)
+ #if ENABLED(USE_ZMAX_PLUG)
+ #define ENDSTOPPULLUP_ZMAX
#endif
- #define WRITE_FAN_N(n, v) WRITE_FAN##n(v)
+ #if ENABLED(USE_XMIN_PLUG)
+ #define ENDSTOPPULLUP_XMIN
+ #endif
+ #if ENABLED(USE_YMIN_PLUG)
+ #define ENDSTOPPULLUP_YMIN
+ #endif
+ #if ENABLED(USE_ZMIN_PLUG)
+ #define ENDSTOPPULLUP_ZMIN
+ #endif
+#endif
- /**
- * Part Cooling fan multipliexer
- */
- #define HAS_FANMUX PIN_EXISTS(FANMUX0)
+/**
+ * Shorthand for pin tests, used wherever needed
+ */
- /**
- * Bed Probe dependencies
- */
- #if HAS_BED_PROBE
- #if ENABLED(ENDSTOPPULLUPS) && HAS_Z_MIN_PROBE_PIN
- #define ENDSTOPPULLUP_ZMIN_PROBE
+// Steppers
+#define HAS_X_ENABLE (PIN_EXISTS(X_ENABLE))
+#define HAS_X_DIR (PIN_EXISTS(X_DIR))
+#define HAS_X_STEP (PIN_EXISTS(X_STEP))
+#define HAS_X_MICROSTEPS (PIN_EXISTS(X_MS1))
+
+#define HAS_X2_ENABLE (PIN_EXISTS(X2_ENABLE))
+#define HAS_X2_DIR (PIN_EXISTS(X2_DIR))
+#define HAS_X2_STEP (PIN_EXISTS(X2_STEP))
+#define HAS_Y_MICROSTEPS (PIN_EXISTS(Y_MS1))
+
+#define HAS_Y_ENABLE (PIN_EXISTS(Y_ENABLE))
+#define HAS_Y_DIR (PIN_EXISTS(Y_DIR))
+#define HAS_Y_STEP (PIN_EXISTS(Y_STEP))
+#define HAS_Z_MICROSTEPS (PIN_EXISTS(Z_MS1))
+
+#define HAS_Y2_ENABLE (PIN_EXISTS(Y2_ENABLE))
+#define HAS_Y2_DIR (PIN_EXISTS(Y2_DIR))
+#define HAS_Y2_STEP (PIN_EXISTS(Y2_STEP))
+
+#define HAS_Z_ENABLE (PIN_EXISTS(Z_ENABLE))
+#define HAS_Z_DIR (PIN_EXISTS(Z_DIR))
+#define HAS_Z_STEP (PIN_EXISTS(Z_STEP))
+
+#define HAS_Z2_ENABLE (PIN_EXISTS(Z2_ENABLE))
+#define HAS_Z2_DIR (PIN_EXISTS(Z2_DIR))
+#define HAS_Z2_STEP (PIN_EXISTS(Z2_STEP))
+
+// Extruder steppers and solenoids
+#define HAS_E0_ENABLE (PIN_EXISTS(E0_ENABLE))
+#define HAS_E0_DIR (PIN_EXISTS(E0_DIR))
+#define HAS_E0_STEP (PIN_EXISTS(E0_STEP))
+#define HAS_E0_MICROSTEPS (PIN_EXISTS(E0_MS1))
+#define HAS_SOLENOID_0 (PIN_EXISTS(SOL0))
+
+#define HAS_E1_ENABLE (PIN_EXISTS(E1_ENABLE))
+#define HAS_E1_DIR (PIN_EXISTS(E1_DIR))
+#define HAS_E1_STEP (PIN_EXISTS(E1_STEP))
+#define HAS_E1_MICROSTEPS (PIN_EXISTS(E1_MS1))
+#define HAS_SOLENOID_1 (PIN_EXISTS(SOL1))
+
+#define HAS_E2_ENABLE (PIN_EXISTS(E2_ENABLE))
+#define HAS_E2_DIR (PIN_EXISTS(E2_DIR))
+#define HAS_E2_STEP (PIN_EXISTS(E2_STEP))
+#define HAS_E2_MICROSTEPS (PIN_EXISTS(E2_MS1))
+#define HAS_SOLENOID_2 (PIN_EXISTS(SOL2))
+
+#define HAS_E3_ENABLE (PIN_EXISTS(E3_ENABLE))
+#define HAS_E3_DIR (PIN_EXISTS(E3_DIR))
+#define HAS_E3_STEP (PIN_EXISTS(E3_STEP))
+#define HAS_E3_MICROSTEPS (PIN_EXISTS(E3_MS1))
+#define HAS_SOLENOID_3 (PIN_EXISTS(SOL3))
+
+#define HAS_E4_ENABLE (PIN_EXISTS(E4_ENABLE))
+#define HAS_E4_DIR (PIN_EXISTS(E4_DIR))
+#define HAS_E4_STEP (PIN_EXISTS(E4_STEP))
+#define HAS_E4_MICROSTEPS (PIN_EXISTS(E4_MS1))
+#define HAS_SOLENOID_4 (PIN_EXISTS(SOL4))
+
+// Trinamic Stepper Drivers
+#define HAS_TRINAMIC (ENABLED(HAVE_TMC2130) || ENABLED(HAVE_TMC2208) || ENABLED(IS_TRAMS))
+#define X_IS_TRINAMIC (ENABLED( X_IS_TMC2130) || ENABLED( X_IS_TMC2208) || ENABLED(IS_TRAMS))
+#define X2_IS_TRINAMIC (ENABLED(X2_IS_TMC2130) || ENABLED(X2_IS_TMC2208))
+#define Y_IS_TRINAMIC (ENABLED( Y_IS_TMC2130) || ENABLED( Y_IS_TMC2208) || ENABLED(IS_TRAMS))
+#define Y2_IS_TRINAMIC (ENABLED(Y2_IS_TMC2130) || ENABLED(Y2_IS_TMC2208))
+#define Z_IS_TRINAMIC (ENABLED( Z_IS_TMC2130) || ENABLED( Z_IS_TMC2208) || ENABLED(IS_TRAMS))
+#define Z2_IS_TRINAMIC (ENABLED(Z2_IS_TMC2130) || ENABLED(Z2_IS_TMC2208))
+#define E0_IS_TRINAMIC (ENABLED(E0_IS_TMC2130) || ENABLED(E0_IS_TMC2208) || ENABLED(IS_TRAMS))
+#define E1_IS_TRINAMIC (ENABLED(E1_IS_TMC2130) || ENABLED(E1_IS_TMC2208))
+#define E2_IS_TRINAMIC (ENABLED(E2_IS_TMC2130) || ENABLED(E2_IS_TMC2208))
+#define E3_IS_TRINAMIC (ENABLED(E3_IS_TMC2130) || ENABLED(E3_IS_TMC2208))
+#define E4_IS_TRINAMIC (ENABLED(E4_IS_TMC2130) || ENABLED(E4_IS_TMC2208))
+
+#if ENABLED(SENSORLESS_HOMING)
+ // Disable Z axis sensorless homing if a probe is used to home the Z axis
+ #if HOMING_Z_WITH_PROBE
+ #undef Z_HOMING_SENSITIVITY
+ #endif
+ #define X_SENSORLESS (ENABLED(X_IS_TMC2130) && defined(X_HOMING_SENSITIVITY))
+ #define Y_SENSORLESS (ENABLED(Y_IS_TMC2130) && defined(Y_HOMING_SENSITIVITY))
+ #define Z_SENSORLESS (ENABLED(Z_IS_TMC2130) && defined(Z_HOMING_SENSITIVITY))
+#endif
+
+// Endstops and bed probe
+#define HAS_STOP_TEST(A,M) (PIN_EXISTS(A##_##M) && !IS_X2_ENDSTOP(A,M) && !IS_Y2_ENDSTOP(A,M) && !IS_Z2_OR_PROBE(A,M))
+#define HAS_X_MIN HAS_STOP_TEST(X,MIN)
+#define HAS_X_MAX HAS_STOP_TEST(X,MAX)
+#define HAS_Y_MIN HAS_STOP_TEST(Y,MIN)
+#define HAS_Y_MAX HAS_STOP_TEST(Y,MAX)
+#define HAS_Z_MIN HAS_STOP_TEST(Z,MIN)
+#define HAS_Z_MAX HAS_STOP_TEST(Z,MAX)
+#define HAS_X2_MIN (PIN_EXISTS(X2_MIN))
+#define HAS_X2_MAX (PIN_EXISTS(X2_MAX))
+#define HAS_Y2_MIN (PIN_EXISTS(Y2_MIN))
+#define HAS_Y2_MAX (PIN_EXISTS(Y2_MAX))
+#define HAS_Z2_MIN (PIN_EXISTS(Z2_MIN))
+#define HAS_Z2_MAX (PIN_EXISTS(Z2_MAX))
+#define HAS_Z_MIN_PROBE_PIN (PIN_EXISTS(Z_MIN_PROBE))
+
+// ADC Temp Sensors (Thermistor or Thermocouple with amplifier ADC interface)
+#define HAS_ADC_TEST(P) (PIN_EXISTS(TEMP_##P) && TEMP_SENSOR_##P != 0 && DISABLED(HEATER_##P##_USES_MAX6675))
+#define HAS_TEMP_ADC_0 HAS_ADC_TEST(0)
+#define HAS_TEMP_ADC_1 HAS_ADC_TEST(1)
+#define HAS_TEMP_ADC_2 HAS_ADC_TEST(2)
+#define HAS_TEMP_ADC_3 HAS_ADC_TEST(3)
+#define HAS_TEMP_ADC_4 HAS_ADC_TEST(4)
+#define HAS_TEMP_ADC_BED HAS_ADC_TEST(BED)
+#define HAS_TEMP_ADC_CHAMBER HAS_ADC_TEST(CHAMBER)
+
+#define HAS_TEMP_HOTEND (HAS_TEMP_ADC_0 || ENABLED(HEATER_0_USES_MAX6675))
+#define HAS_TEMP_BED HAS_TEMP_ADC_BED
+#define HAS_TEMP_CHAMBER HAS_TEMP_ADC_CHAMBER
+
+// Heaters
+#define HAS_HEATER_0 (PIN_EXISTS(HEATER_0))
+#define HAS_HEATER_1 (PIN_EXISTS(HEATER_1))
+#define HAS_HEATER_2 (PIN_EXISTS(HEATER_2))
+#define HAS_HEATER_3 (PIN_EXISTS(HEATER_3))
+#define HAS_HEATER_4 (PIN_EXISTS(HEATER_4))
+#define HAS_HEATER_BED (PIN_EXISTS(HEATER_BED))
+
+// Shorthand for common combinations
+#define HAS_HEATED_BED (HAS_TEMP_BED && HAS_HEATER_BED)
+#define HAS_TEMP_SENSOR (HAS_TEMP_HOTEND || HAS_HEATED_BED || HAS_TEMP_CHAMBER)
+
+// PID heating
+#if !HAS_HEATED_BED
+ #undef PIDTEMPBED
+#endif
+#define HAS_PID_HEATING (ENABLED(PIDTEMP) || ENABLED(PIDTEMPBED))
+#define HAS_PID_FOR_BOTH (ENABLED(PIDTEMP) && ENABLED(PIDTEMPBED))
+
+// Thermal protection
+#define HAS_THERMALLY_PROTECTED_BED (HAS_HEATED_BED && ENABLED(THERMAL_PROTECTION_BED))
+#define WATCH_HOTENDS (ENABLED(THERMAL_PROTECTION_HOTENDS) && WATCH_TEMP_PERIOD > 0)
+#define WATCH_THE_BED (HAS_THERMALLY_PROTECTED_BED && WATCH_BED_TEMP_PERIOD > 0)
+
+// Auto fans
+#define HAS_AUTO_FAN_0 (PIN_EXISTS(E0_AUTO_FAN))
+#define HAS_AUTO_FAN_1 (HOTENDS > 1 && PIN_EXISTS(E1_AUTO_FAN))
+#define HAS_AUTO_FAN_2 (HOTENDS > 2 && PIN_EXISTS(E2_AUTO_FAN))
+#define HAS_AUTO_FAN_3 (HOTENDS > 3 && PIN_EXISTS(E3_AUTO_FAN))
+#define HAS_AUTO_FAN_4 (HOTENDS > 4 && PIN_EXISTS(E4_AUTO_FAN))
+#define HAS_AUTO_CHAMBER_FAN (PIN_EXISTS(CHAMBER_AUTO_FAN))
+#define HAS_AUTO_FAN (HAS_AUTO_FAN_0 || HAS_AUTO_FAN_1 || HAS_AUTO_FAN_2 || HAS_AUTO_FAN_3 || HAS_AUTO_CHAMBER_FAN)
+#define AUTO_1_IS_0 (E1_AUTO_FAN_PIN == E0_AUTO_FAN_PIN)
+#define AUTO_2_IS_0 (E2_AUTO_FAN_PIN == E0_AUTO_FAN_PIN)
+#define AUTO_2_IS_1 (E2_AUTO_FAN_PIN == E1_AUTO_FAN_PIN)
+#define AUTO_3_IS_0 (E3_AUTO_FAN_PIN == E0_AUTO_FAN_PIN)
+#define AUTO_3_IS_1 (E3_AUTO_FAN_PIN == E1_AUTO_FAN_PIN)
+#define AUTO_3_IS_2 (E3_AUTO_FAN_PIN == E2_AUTO_FAN_PIN)
+#define AUTO_4_IS_0 (E4_AUTO_FAN_PIN == E0_AUTO_FAN_PIN)
+#define AUTO_4_IS_1 (E4_AUTO_FAN_PIN == E1_AUTO_FAN_PIN)
+#define AUTO_4_IS_2 (E4_AUTO_FAN_PIN == E2_AUTO_FAN_PIN)
+#define AUTO_4_IS_3 (E4_AUTO_FAN_PIN == E3_AUTO_FAN_PIN)
+#define AUTO_CHAMBER_IS_0 (CHAMBER_AUTO_FAN_PIN == E0_AUTO_FAN_PIN)
+#define AUTO_CHAMBER_IS_1 (CHAMBER_AUTO_FAN_PIN == E1_AUTO_FAN_PIN)
+#define AUTO_CHAMBER_IS_2 (CHAMBER_AUTO_FAN_PIN == E2_AUTO_FAN_PIN)
+#define AUTO_CHAMBER_IS_3 (CHAMBER_AUTO_FAN_PIN == E3_AUTO_FAN_PIN)
+#define AUTO_CHAMBER_IS_4 (CHAMBER_AUTO_FAN_PIN == E4_AUTO_FAN_PIN)
+
+// Other fans
+#define HAS_FAN0 (PIN_EXISTS(FAN))
+#define HAS_FAN1 (PIN_EXISTS(FAN1) && CONTROLLER_FAN_PIN != FAN1_PIN && E0_AUTO_FAN_PIN != FAN1_PIN && E1_AUTO_FAN_PIN != FAN1_PIN && E2_AUTO_FAN_PIN != FAN1_PIN && E3_AUTO_FAN_PIN != FAN1_PIN)
+#define HAS_FAN2 (PIN_EXISTS(FAN2) && CONTROLLER_FAN_PIN != FAN2_PIN && E0_AUTO_FAN_PIN != FAN2_PIN && E1_AUTO_FAN_PIN != FAN2_PIN && E2_AUTO_FAN_PIN != FAN2_PIN && E3_AUTO_FAN_PIN != FAN2_PIN)
+#define HAS_CONTROLLER_FAN (PIN_EXISTS(CONTROLLER_FAN))
+
+// Servos
+#define HAS_SERVO_0 (PIN_EXISTS(SERVO0))
+#define HAS_SERVO_1 (PIN_EXISTS(SERVO1))
+#define HAS_SERVO_2 (PIN_EXISTS(SERVO2))
+#define HAS_SERVO_3 (PIN_EXISTS(SERVO3))
+#define HAS_SERVOS (defined(NUM_SERVOS) && NUM_SERVOS > 0 && (HAS_SERVO_0 || HAS_SERVO_1 || HAS_SERVO_2 || HAS_SERVO_3))
+
+#if HAS_SERVOS && !defined(Z_PROBE_SERVO_NR)
+ #define Z_PROBE_SERVO_NR -1
+#endif
+
+// Sensors
+#define HAS_FILAMENT_WIDTH_SENSOR (PIN_EXISTS(FILWIDTH))
+
+// User Interface
+#define HAS_HOME (PIN_EXISTS(HOME))
+#define HAS_KILL (PIN_EXISTS(KILL))
+#define HAS_SUICIDE (PIN_EXISTS(SUICIDE))
+#define HAS_PHOTOGRAPH (PIN_EXISTS(PHOTOGRAPH))
+#define HAS_BUZZER (PIN_EXISTS(BEEPER) || ENABLED(LCD_USE_I2C_BUZZER))
+#define HAS_CASE_LIGHT (PIN_EXISTS(CASE_LIGHT) && ENABLED(CASE_LIGHT_ENABLE))
+
+// Digital control
+#define HAS_MICROSTEPS (HAS_X_MICROSTEPS || HAS_Y_MICROSTEPS || HAS_Z_MICROSTEPS || HAS_E0_MICROSTEPS || HAS_E1_MICROSTEPS || HAS_E2_MICROSTEPS || HAS_E3_MICROSTEPS || HAS_E4_MICROSTEPS)
+#define HAS_STEPPER_RESET (PIN_EXISTS(STEPPER_RESET))
+#define HAS_DIGIPOTSS (PIN_EXISTS(DIGIPOTSS))
+#define HAS_MOTOR_CURRENT_PWM (PIN_EXISTS(MOTOR_CURRENT_PWM_XY) || PIN_EXISTS(MOTOR_CURRENT_PWM_Z) || PIN_EXISTS(MOTOR_CURRENT_PWM_E))
+
+#if !HAS_TEMP_SENSOR
+ #undef AUTO_REPORT_TEMPERATURES
+#endif
+
+#define HAS_AUTO_REPORTING (ENABLED(AUTO_REPORT_TEMPERATURES) || ENABLED(AUTO_REPORT_SD_STATUS))
+
+/**
+ * This setting is also used by M109 when trying to calculate
+ * a ballpark safe margin to prevent wait-forever situation.
+ */
+#ifndef EXTRUDE_MINTEMP
+ #define EXTRUDE_MINTEMP 170
+#endif
+
+/**
+ * Helper Macros for heaters and extruder fan
+ */
+#define WRITE_HEATER_0P(v) WRITE(HEATER_0_PIN, v)
+#if HOTENDS > 1 || ENABLED(HEATERS_PARALLEL)
+ #define WRITE_HEATER_1(v) WRITE(HEATER_1_PIN, v)
+ #if HOTENDS > 2
+ #define WRITE_HEATER_2(v) WRITE(HEATER_2_PIN, v)
+ #if HOTENDS > 3
+ #define WRITE_HEATER_3(v) WRITE(HEATER_3_PIN, v)
+ #if HOTENDS > 4
+ #define WRITE_HEATER_4(v) WRITE(HEATER_4_PIN, v)
+ #endif // HOTENDS > 4
+ #endif // HOTENDS > 3
+ #endif // HOTENDS > 2
+#endif // HOTENDS > 1
+#if ENABLED(HEATERS_PARALLEL)
+ #define WRITE_HEATER_0(v) { WRITE_HEATER_0P(v); WRITE_HEATER_1(v); }
+#else
+ #define WRITE_HEATER_0(v) WRITE_HEATER_0P(v)
+#endif
+
+/**
+ * Heated bed requires settings
+ */
+#if HAS_HEATED_BED
+ #ifndef MAX_BED_POWER
+ #define MAX_BED_POWER 255
+ #endif
+ #ifndef HEATER_BED_INVERTING
+ #define HEATER_BED_INVERTING false
+ #endif
+ #define WRITE_HEATER_BED(v) WRITE(HEATER_BED_PIN, (v) ^ HEATER_BED_INVERTING)
+#endif
+
+/**
+ * Up to 3 PWM fans
+ */
+#if HAS_FAN2
+ #define FAN_COUNT 3
+#elif HAS_FAN1
+ #define FAN_COUNT 2
+#elif HAS_FAN0
+ #define FAN_COUNT 1
+#else
+ #define FAN_COUNT 0
+#endif
+
+#if HAS_FAN0
+ #define WRITE_FAN(v) WRITE(FAN_PIN, v)
+ #define WRITE_FAN0(v) WRITE_FAN(v)
+#endif
+#if HAS_FAN1
+ #define WRITE_FAN1(v) WRITE(FAN1_PIN, v)
+#endif
+#if HAS_FAN2
+ #define WRITE_FAN2(v) WRITE(FAN2_PIN, v)
+#endif
+#define WRITE_FAN_N(n, v) WRITE_FAN##n(v)
+
+/**
+ * Part Cooling fan multipliexer
+ */
+#define HAS_FANMUX PIN_EXISTS(FANMUX0)
+
+/**
+ * MIN/MAX fan PWM scaling
+ */
+#ifndef FAN_MIN_PWM
+ #define FAN_MIN_PWM 0
+#endif
+#ifndef FAN_MAX_PWM
+ #define FAN_MAX_PWM 255
+#endif
+#if FAN_MIN_PWM < 0 || FAN_MIN_PWM > 255
+ #error "FAN_MIN_PWM must be a value from 0 to 255."
+#elif FAN_MAX_PWM < 0 || FAN_MAX_PWM > 255
+ #error "FAN_MAX_PWM must be a value from 0 to 255."
+#elif FAN_MIN_PWM > FAN_MAX_PWM
+ #error "FAN_MIN_PWM must be less than or equal to FAN_MAX_PWM."
+#endif
+
+/**
+ * Bed Probe dependencies
+ */
+#if HAS_BED_PROBE
+ #if ENABLED(ENDSTOPPULLUPS) && HAS_Z_MIN_PROBE_PIN
+ #define ENDSTOPPULLUP_ZMIN_PROBE
+ #endif
+ #ifndef Z_PROBE_OFFSET_RANGE_MIN
+ #define Z_PROBE_OFFSET_RANGE_MIN -20
+ #endif
+ #ifndef Z_PROBE_OFFSET_RANGE_MAX
+ #define Z_PROBE_OFFSET_RANGE_MAX 20
+ #endif
+ #ifndef XY_PROBE_SPEED
+ #ifdef HOMING_FEEDRATE_XY
+ #define XY_PROBE_SPEED HOMING_FEEDRATE_XY
+ #else
+ #define XY_PROBE_SPEED 4000
#endif
- #ifndef Z_PROBE_OFFSET_RANGE_MIN
- #define Z_PROBE_OFFSET_RANGE_MIN -20
- #endif
- #ifndef Z_PROBE_OFFSET_RANGE_MAX
- #define Z_PROBE_OFFSET_RANGE_MAX 20
- #endif
- #ifndef XY_PROBE_SPEED
- #ifdef HOMING_FEEDRATE_XY
- #define XY_PROBE_SPEED HOMING_FEEDRATE_XY
+ #endif
+#else
+ #undef X_PROBE_OFFSET_FROM_EXTRUDER
+ #undef Y_PROBE_OFFSET_FROM_EXTRUDER
+ #undef Z_PROBE_OFFSET_FROM_EXTRUDER
+ #define X_PROBE_OFFSET_FROM_EXTRUDER 0
+ #define Y_PROBE_OFFSET_FROM_EXTRUDER 0
+ #define Z_PROBE_OFFSET_FROM_EXTRUDER 0
+#endif
+
+/**
+ * XYZ Bed Skew Correction
+ */
+#if ENABLED(SKEW_CORRECTION)
+ #define SKEW_FACTOR_MIN -1
+ #define SKEW_FACTOR_MAX 1
+
+ #define _GET_SIDE(a,b,c) (SQRT(2*sq(a)+2*sq(b)-4*sq(c))*0.5)
+ #define _SKEW_SIDE(a,b,c) tan(M_PI*0.5-acos((sq(a)-sq(b)-sq(c))/(2*c*b)))
+ #define _SKEW_FACTOR(a,b,c) _SKEW_SIDE(float(a),_GET_SIDE(float(a),float(b),float(c)),float(c))
+
+ #ifndef XY_SKEW_FACTOR
+ constexpr float XY_SKEW_FACTOR = (
+ #if defined(XY_DIAG_AC) && defined(XY_DIAG_BD) && defined(XY_SIDE_AD)
+ _SKEW_FACTOR(XY_DIAG_AC, XY_DIAG_BD, XY_SIDE_AD)
#else
- #define XY_PROBE_SPEED 4000
+ 0.0
#endif
- #endif
- #else
- #undef X_PROBE_OFFSET_FROM_EXTRUDER
- #undef Y_PROBE_OFFSET_FROM_EXTRUDER
- #undef Z_PROBE_OFFSET_FROM_EXTRUDER
- #define X_PROBE_OFFSET_FROM_EXTRUDER 0
- #define Y_PROBE_OFFSET_FROM_EXTRUDER 0
- #define Z_PROBE_OFFSET_FROM_EXTRUDER 0
+ );
#endif
-
- /**
- * XYZ Bed Skew Correction
- */
- #if ENABLED(SKEW_CORRECTION)
- #define SKEW_FACTOR_MIN -1
- #define SKEW_FACTOR_MAX 1
-
- #define _GET_SIDE(a,b,c) (SQRT(2*sq(a)+2*sq(b)-4*sq(c))*0.5)
- #define _SKEW_SIDE(a,b,c) tan(M_PI*0.5-acos((sq(a)-sq(b)-sq(c))/(2*c*b)))
- #define _SKEW_FACTOR(a,b,c) _SKEW_SIDE(float(a),_GET_SIDE(float(a),float(b),float(c)),float(c))
-
- #ifndef XY_SKEW_FACTOR
- constexpr float XY_SKEW_FACTOR = (
- #if defined(XY_DIAG_AC) && defined(XY_DIAG_BD) && defined(XY_SIDE_AD)
- _SKEW_FACTOR(XY_DIAG_AC, XY_DIAG_BD, XY_SIDE_AD)
- #else
- 0.0
- #endif
- );
+ #ifndef XZ_SKEW_FACTOR
+ #if defined(XY_SIDE_AD) && !defined(XZ_SIDE_AD)
+ #define XZ_SIDE_AD XY_SIDE_AD
#endif
- #ifndef XZ_SKEW_FACTOR
- #if defined(XY_SIDE_AD) && !defined(XZ_SIDE_AD)
- #define XZ_SIDE_AD XY_SIDE_AD
+ constexpr float XZ_SKEW_FACTOR = (
+ #if defined(XZ_DIAG_AC) && defined(XZ_DIAG_BD) && defined(XZ_SIDE_AD)
+ _SKEW_FACTOR(XZ_DIAG_AC, XZ_DIAG_BD, XZ_SIDE_AD)
+ #else
+ 0.0
#endif
- constexpr float XZ_SKEW_FACTOR = (
- #if defined(XZ_DIAG_AC) && defined(XZ_DIAG_BD) && defined(XZ_SIDE_AD)
- _SKEW_FACTOR(XZ_DIAG_AC, XZ_DIAG_BD, XZ_SIDE_AD)
- #else
- 0.0
- #endif
- );
+ );
+ #endif
+ #ifndef YZ_SKEW_FACTOR
+ constexpr float YZ_SKEW_FACTOR = (
+ #if defined(YZ_DIAG_AC) && defined(YZ_DIAG_BD) && defined(YZ_SIDE_AD)
+ _SKEW_FACTOR(YZ_DIAG_AC, YZ_DIAG_BD, YZ_SIDE_AD)
+ #else
+ 0.0
+ #endif
+ );
+ #endif
+#endif // SKEW_CORRECTION
+
+/**
+ * Set granular options based on the specific type of leveling
+ */
+#define UBL_SEGMENTED (ENABLED(AUTO_BED_LEVELING_UBL) && (ENABLED(DELTA)))
+#define ABL_PLANAR (ENABLED(AUTO_BED_LEVELING_LINEAR) || ENABLED(AUTO_BED_LEVELING_3POINT))
+#define ABL_GRID (ENABLED(AUTO_BED_LEVELING_LINEAR) || ENABLED(AUTO_BED_LEVELING_BILINEAR))
+#define OLDSCHOOL_ABL (ABL_PLANAR || ABL_GRID)
+#define HAS_ABL (OLDSCHOOL_ABL || ENABLED(AUTO_BED_LEVELING_UBL))
+#define HAS_LEVELING (HAS_ABL || ENABLED(MESH_BED_LEVELING))
+#define HAS_AUTOLEVEL (HAS_ABL && DISABLED(PROBE_MANUALLY))
+#define HAS_MESH (ENABLED(AUTO_BED_LEVELING_BILINEAR) || ENABLED(AUTO_BED_LEVELING_UBL) || ENABLED(MESH_BED_LEVELING))
+#define PLANNER_LEVELING (OLDSCHOOL_ABL || ENABLED(MESH_BED_LEVELING) || UBL_SEGMENTED || ENABLED(SKEW_CORRECTION))
+#define HAS_PROBING_PROCEDURE (HAS_ABL || ENABLED(Z_MIN_PROBE_REPEATABILITY_TEST))
+#define HAS_UBL_AND_CURVES (ENABLED(AUTO_BED_LEVELING_UBL) && !PLANNER_LEVELING && (ENABLED(ARC_SUPPORT) || ENABLED(BEZIER_CURVE_SUPPORT)))
+
+#if ENABLED(AUTO_BED_LEVELING_UBL)
+ #undef LCD_BED_LEVELING
+#endif
+
+/**
+ * Heater & Fan Pausing
+ */
+#if FAN_COUNT == 0
+ #undef PROBING_FANS_OFF
+#endif
+#define QUIET_PROBING (HAS_BED_PROBE && (ENABLED(PROBING_HEATERS_OFF) || ENABLED(PROBING_FANS_OFF) || DELAY_BEFORE_PROBING > 0))
+#define HEATER_IDLE_HANDLER (ENABLED(ADVANCED_PAUSE_FEATURE) || ENABLED(PROBING_HEATERS_OFF))
+
+#if ENABLED(ADVANCED_PAUSE_FEATURE) && !defined(FILAMENT_CHANGE_SLOW_LOAD_LENGTH)
+ #define FILAMENT_CHANGE_SLOW_LOAD_LENGTH 0
+#endif
+
+/**
+ * Only constrain Z on DELTA / SCARA machines
+ */
+#if IS_KINEMATIC
+ #undef MIN_SOFTWARE_ENDSTOP_X
+ #undef MIN_SOFTWARE_ENDSTOP_Y
+ #undef MAX_SOFTWARE_ENDSTOP_X
+ #undef MAX_SOFTWARE_ENDSTOP_Y
+#endif
+
+/**
+ * Bed Probing rectangular bounds
+ * These can be further constrained in code for Delta and SCARA
+ */
+
+#ifndef MIN_PROBE_EDGE
+ #define MIN_PROBE_EDGE 0
+#endif
+
+#if ENABLED(DELTA)
+ /**
+ * Delta radius/rod trimmers/angle trimmers
+ */
+ #define _PROBE_RADIUS (DELTA_PRINTABLE_RADIUS - (MIN_PROBE_EDGE))
+ #ifndef DELTA_CALIBRATION_RADIUS
+ #ifdef X_PROBE_OFFSET_FROM_EXTRUDER
+ #define DELTA_CALIBRATION_RADIUS (DELTA_PRINTABLE_RADIUS - MAX3(abs(X_PROBE_OFFSET_FROM_EXTRUDER), abs(Y_PROBE_OFFSET_FROM_EXTRUDER), abs(MIN_PROBE_EDGE)))
+ #else
+ #define DELTA_CALIBRATION_RADIUS _PROBE_RADIUS
#endif
- #ifndef YZ_SKEW_FACTOR
- constexpr float YZ_SKEW_FACTOR = (
- #if defined(YZ_DIAG_AC) && defined(YZ_DIAG_BD) && defined(YZ_SIDE_AD)
- _SKEW_FACTOR(YZ_DIAG_AC, YZ_DIAG_BD, YZ_SIDE_AD)
- #else
- 0.0
- #endif
- );
- #endif
- #endif // SKEW_CORRECTION
-
- /**
- * Set granular options based on the specific type of leveling
- */
- #define UBL_SEGMENTED (ENABLED(AUTO_BED_LEVELING_UBL) && (ENABLED(DELTA)))
- #define ABL_PLANAR (ENABLED(AUTO_BED_LEVELING_LINEAR) || ENABLED(AUTO_BED_LEVELING_3POINT))
- #define ABL_GRID (ENABLED(AUTO_BED_LEVELING_LINEAR) || ENABLED(AUTO_BED_LEVELING_BILINEAR))
- #define OLDSCHOOL_ABL (ABL_PLANAR || ABL_GRID)
- #define HAS_ABL (OLDSCHOOL_ABL || ENABLED(AUTO_BED_LEVELING_UBL))
- #define HAS_LEVELING (HAS_ABL || ENABLED(MESH_BED_LEVELING))
- #define HAS_AUTOLEVEL (HAS_ABL && DISABLED(PROBE_MANUALLY))
- #define HAS_MESH (ENABLED(AUTO_BED_LEVELING_BILINEAR) || ENABLED(AUTO_BED_LEVELING_UBL) || ENABLED(MESH_BED_LEVELING))
- #define PLANNER_LEVELING (OLDSCHOOL_ABL || ENABLED(MESH_BED_LEVELING) || UBL_SEGMENTED || ENABLED(SKEW_CORRECTION))
- #define HAS_PROBING_PROCEDURE (HAS_ABL || ENABLED(Z_MIN_PROBE_REPEATABILITY_TEST))
- #define HAS_UBL_AND_CURVES (ENABLED(AUTO_BED_LEVELING_UBL) && !PLANNER_LEVELING && (ENABLED(ARC_SUPPORT) || ENABLED(BEZIER_CURVE_SUPPORT)))
-
- #if ENABLED(AUTO_BED_LEVELING_UBL)
- #undef LCD_BED_LEVELING
+ #endif
+ #ifndef DELTA_ENDSTOP_ADJ
+ #define DELTA_ENDSTOP_ADJ { 0, 0, 0 }
+ #endif
+ #ifndef DELTA_TOWER_ANGLE_TRIM
+ #define DELTA_TOWER_ANGLE_TRIM {0, 0, 0}
+ #endif
+ #ifndef DELTA_RADIUS_TRIM_TOWER
+ #define DELTA_RADIUS_TRIM_TOWER {0, 0, 0}
+ #endif
+ #ifndef DELTA_DIAGONAL_ROD_TRIM_TOWER
+ #define DELTA_DIAGONAL_ROD_TRIM_TOWER {0, 0, 0}
#endif
- /**
- * Heater & Fan Pausing
- */
- #if FAN_COUNT == 0
- #undef PROBING_FANS_OFF
- #endif
- #define QUIET_PROBING (HAS_BED_PROBE && (ENABLED(PROBING_HEATERS_OFF) || ENABLED(PROBING_FANS_OFF) || DELAY_BEFORE_PROBING > 0))
- #define HEATER_IDLE_HANDLER (ENABLED(ADVANCED_PAUSE_FEATURE) || ENABLED(PROBING_HEATERS_OFF))
+ // Probing points may be verified at compile time within the radius
+ // using static_assert(HYPOT2(X2-X1,Y2-Y1)<=sq(DELTA_PRINTABLE_RADIUS),"bad probe point!")
+ // so that may be added to SanityCheck.h in the future.
+ #define _MIN_PROBE_X (X_CENTER - (_PROBE_RADIUS))
+ #define _MIN_PROBE_Y (Y_CENTER - (_PROBE_RADIUS))
+ #define _MAX_PROBE_X (X_CENTER + _PROBE_RADIUS)
+ #define _MAX_PROBE_Y (Y_CENTER + _PROBE_RADIUS)
- #if ENABLED(ADVANCED_PAUSE_FEATURE) && !defined(FILAMENT_CHANGE_SLOW_LOAD_LENGTH)
- #define FILAMENT_CHANGE_SLOW_LOAD_LENGTH 0
- #endif
+#elif IS_SCARA
- /**
- * Only constrain Z on DELTA / SCARA machines
- */
+ #define SCARA_PRINTABLE_RADIUS (SCARA_LINKAGE_1 + SCARA_LINKAGE_2)
+ #define _PROBE_RADIUS (SCARA_PRINTABLE_RADIUS - (MIN_PROBE_EDGE))
+ #define _MIN_PROBE_X (X_CENTER - (SCARA_PRINTABLE_RADIUS) + MIN_PROBE_EDGE)
+ #define _MIN_PROBE_Y (Y_CENTER - (SCARA_PRINTABLE_RADIUS) + MIN_PROBE_EDGE)
+ #define _MAX_PROBE_X (X_CENTER + SCARA_PRINTABLE_RADIUS - (MIN_PROBE_EDGE))
+ #define _MAX_PROBE_Y (Y_CENTER + SCARA_PRINTABLE_RADIUS - (MIN_PROBE_EDGE))
+
+#else
+
+ // Boundaries for Cartesian probing based on bed limits
+ #define _MIN_PROBE_X (max(X_MIN_BED + MIN_PROBE_EDGE, X_MIN_POS + X_PROBE_OFFSET_FROM_EXTRUDER))
+ #define _MIN_PROBE_Y (max(Y_MIN_BED + MIN_PROBE_EDGE, Y_MIN_POS + Y_PROBE_OFFSET_FROM_EXTRUDER))
+ #define _MAX_PROBE_X (min(X_MAX_BED - (MIN_PROBE_EDGE), X_MAX_POS + X_PROBE_OFFSET_FROM_EXTRUDER))
+ #define _MAX_PROBE_Y (min(Y_MAX_BED - (MIN_PROBE_EDGE), Y_MAX_POS + Y_PROBE_OFFSET_FROM_EXTRUDER))
+
+#endif
+
+#if ENABLED(SEGMENT_LEVELED_MOVES) && !defined(LEVELED_SEGMENT_LENGTH)
+ #define LEVELED_SEGMENT_LENGTH 5
+#endif
+
+// These may be overridden in Configuration.h if a smaller area is desired
+#ifndef MIN_PROBE_X
+ #define MIN_PROBE_X _MIN_PROBE_X
+#endif
+#ifndef MIN_PROBE_Y
+ #define MIN_PROBE_Y _MIN_PROBE_Y
+#endif
+#ifndef MAX_PROBE_X
+ #define MAX_PROBE_X _MAX_PROBE_X
+#endif
+#ifndef MAX_PROBE_Y
+ #define MAX_PROBE_Y _MAX_PROBE_Y
+#endif
+
+/**
+ * Default mesh area is an area with an inset margin on the print area.
+ */
+#if ENABLED(MESH_BED_LEVELING) || ENABLED(AUTO_BED_LEVELING_UBL)
#if IS_KINEMATIC
- #undef MIN_SOFTWARE_ENDSTOP_X
- #undef MIN_SOFTWARE_ENDSTOP_Y
- #undef MAX_SOFTWARE_ENDSTOP_X
- #undef MAX_SOFTWARE_ENDSTOP_Y
- #endif
-
- /**
- * Bed Probing rectangular bounds
- * These can be further constrained in code for Delta and SCARA
- */
-
- #ifndef MIN_PROBE_EDGE
- #define MIN_PROBE_EDGE 0
- #endif
-
- #if ENABLED(DELTA)
- /**
- * Delta radius/rod trimmers/angle trimmers
- */
- #define _PROBE_RADIUS (DELTA_PRINTABLE_RADIUS - (MIN_PROBE_EDGE))
- #ifndef DELTA_CALIBRATION_RADIUS
- #ifdef X_PROBE_OFFSET_FROM_EXTRUDER
- #define DELTA_CALIBRATION_RADIUS (DELTA_PRINTABLE_RADIUS - MAX3(abs(X_PROBE_OFFSET_FROM_EXTRUDER), abs(Y_PROBE_OFFSET_FROM_EXTRUDER), abs(MIN_PROBE_EDGE)))
- #else
- #define DELTA_CALIBRATION_RADIUS _PROBE_RADIUS
- #endif
- #endif
- #ifndef DELTA_ENDSTOP_ADJ
- #define DELTA_ENDSTOP_ADJ { 0, 0, 0 }
- #endif
- #ifndef DELTA_TOWER_ANGLE_TRIM
- #define DELTA_TOWER_ANGLE_TRIM {0, 0, 0}
- #endif
- #ifndef DELTA_RADIUS_TRIM_TOWER
- #define DELTA_RADIUS_TRIM_TOWER {0, 0, 0}
- #endif
- #ifndef DELTA_DIAGONAL_ROD_TRIM_TOWER
- #define DELTA_DIAGONAL_ROD_TRIM_TOWER {0, 0, 0}
- #endif
-
// Probing points may be verified at compile time within the radius
// using static_assert(HYPOT2(X2-X1,Y2-Y1)<=sq(DELTA_PRINTABLE_RADIUS),"bad probe point!")
// so that may be added to SanityCheck.h in the future.
- #define _MIN_PROBE_X (X_CENTER - (_PROBE_RADIUS))
- #define _MIN_PROBE_Y (Y_CENTER - (_PROBE_RADIUS))
- #define _MAX_PROBE_X (X_CENTER + _PROBE_RADIUS)
- #define _MAX_PROBE_Y (Y_CENTER + _PROBE_RADIUS)
-
- #elif IS_SCARA
-
- #define SCARA_PRINTABLE_RADIUS (SCARA_LINKAGE_1 + SCARA_LINKAGE_2)
- #define _PROBE_RADIUS (SCARA_PRINTABLE_RADIUS - (MIN_PROBE_EDGE))
- #define _MIN_PROBE_X (X_CENTER - (SCARA_PRINTABLE_RADIUS) + MIN_PROBE_EDGE)
- #define _MIN_PROBE_Y (Y_CENTER - (SCARA_PRINTABLE_RADIUS) + MIN_PROBE_EDGE)
- #define _MAX_PROBE_X (X_CENTER + SCARA_PRINTABLE_RADIUS - (MIN_PROBE_EDGE))
- #define _MAX_PROBE_Y (Y_CENTER + SCARA_PRINTABLE_RADIUS - (MIN_PROBE_EDGE))
-
+ #define _MESH_MIN_X (X_MIN_BED + MESH_INSET)
+ #define _MESH_MIN_Y (Y_MIN_BED + MESH_INSET)
+ #define _MESH_MAX_X (X_MAX_BED - (MESH_INSET))
+ #define _MESH_MAX_Y (Y_MAX_BED - (MESH_INSET))
#else
-
- // Boundaries for Cartesian probing based on bed limits
- #define _MIN_PROBE_X (max(X_MIN_BED + MIN_PROBE_EDGE, X_MIN_POS + X_PROBE_OFFSET_FROM_EXTRUDER))
- #define _MIN_PROBE_Y (max(Y_MIN_BED + MIN_PROBE_EDGE, Y_MIN_POS + Y_PROBE_OFFSET_FROM_EXTRUDER))
- #define _MAX_PROBE_X (min(X_MAX_BED - (MIN_PROBE_EDGE), X_MAX_POS + X_PROBE_OFFSET_FROM_EXTRUDER))
- #define _MAX_PROBE_Y (min(Y_MAX_BED - (MIN_PROBE_EDGE), Y_MAX_POS + Y_PROBE_OFFSET_FROM_EXTRUDER))
-
- #endif
-
- #if ENABLED(SEGMENT_LEVELED_MOVES) && !defined(LEVELED_SEGMENT_LENGTH)
- #define LEVELED_SEGMENT_LENGTH 5
+ // Boundaries for Cartesian probing based on set limits
+ #if ENABLED(AUTO_BED_LEVELING_UBL)
+ #define _MESH_MIN_X (max(X_MIN_BED + MESH_INSET, X_MIN_POS)) // UBL is careful not to probe off the bed. It does not
+ #define _MESH_MIN_Y (max(Y_MIN_BED + MESH_INSET, Y_MIN_POS)) // need *_PROBE_OFFSET_FROM_EXTRUDER in the mesh dimensions
+ #define _MESH_MAX_X (min(X_MAX_BED - (MESH_INSET), X_MAX_POS))
+ #define _MESH_MAX_Y (min(Y_MAX_BED - (MESH_INSET), Y_MAX_POS))
+ #else
+ #define _MESH_MIN_X (max(X_MIN_BED + MESH_INSET, X_MIN_POS + X_PROBE_OFFSET_FROM_EXTRUDER))
+ #define _MESH_MIN_Y (max(Y_MIN_BED + MESH_INSET, Y_MIN_POS + Y_PROBE_OFFSET_FROM_EXTRUDER))
+ #define _MESH_MAX_X (min(X_MAX_BED - (MESH_INSET), X_MAX_POS + X_PROBE_OFFSET_FROM_EXTRUDER))
+ #define _MESH_MAX_Y (min(Y_MAX_BED - (MESH_INSET), Y_MAX_POS + Y_PROBE_OFFSET_FROM_EXTRUDER))
+ #endif
#endif
// These may be overridden in Configuration.h if a smaller area is desired
- #ifndef MIN_PROBE_X
- #define MIN_PROBE_X _MIN_PROBE_X
+ #ifndef MESH_MIN_X
+ #define MESH_MIN_X _MESH_MIN_X
#endif
- #ifndef MIN_PROBE_Y
- #define MIN_PROBE_Y _MIN_PROBE_Y
+ #ifndef MESH_MIN_Y
+ #define MESH_MIN_Y _MESH_MIN_Y
#endif
- #ifndef MAX_PROBE_X
- #define MAX_PROBE_X _MAX_PROBE_X
+ #ifndef MESH_MAX_X
+ #define MESH_MAX_X _MESH_MAX_X
#endif
- #ifndef MAX_PROBE_Y
- #define MAX_PROBE_Y _MAX_PROBE_Y
+ #ifndef MESH_MAX_Y
+ #define MESH_MAX_Y _MESH_MAX_Y
#endif
- /**
- * Default mesh area is an area with an inset margin on the print area.
- */
- #if ENABLED(MESH_BED_LEVELING) || ENABLED(AUTO_BED_LEVELING_UBL)
- #if IS_KINEMATIC
- // Probing points may be verified at compile time within the radius
- // using static_assert(HYPOT2(X2-X1,Y2-Y1)<=sq(DELTA_PRINTABLE_RADIUS),"bad probe point!")
- // so that may be added to SanityCheck.h in the future.
- #define _MESH_MIN_X (X_MIN_BED + MESH_INSET)
- #define _MESH_MIN_Y (Y_MIN_BED + MESH_INSET)
- #define _MESH_MAX_X (X_MAX_BED - (MESH_INSET))
- #define _MESH_MAX_Y (Y_MAX_BED - (MESH_INSET))
- #else
- // Boundaries for Cartesian probing based on set limits
- #if ENABLED(AUTO_BED_LEVELING_UBL)
- #define _MESH_MIN_X (max(X_MIN_BED + MESH_INSET, X_MIN_POS)) // UBL is careful not to probe off the bed. It does not
- #define _MESH_MIN_Y (max(Y_MIN_BED + MESH_INSET, Y_MIN_POS)) // need *_PROBE_OFFSET_FROM_EXTRUDER in the mesh dimensions
- #define _MESH_MAX_X (min(X_MAX_BED - (MESH_INSET), X_MAX_POS))
- #define _MESH_MAX_Y (min(Y_MAX_BED - (MESH_INSET), Y_MAX_POS))
- #else
- #define _MESH_MIN_X (max(X_MIN_BED + MESH_INSET, X_MIN_POS + X_PROBE_OFFSET_FROM_EXTRUDER))
- #define _MESH_MIN_Y (max(Y_MIN_BED + MESH_INSET, Y_MIN_POS + Y_PROBE_OFFSET_FROM_EXTRUDER))
- #define _MESH_MAX_X (min(X_MAX_BED - (MESH_INSET), X_MAX_POS + X_PROBE_OFFSET_FROM_EXTRUDER))
- #define _MESH_MAX_Y (min(Y_MAX_BED - (MESH_INSET), Y_MAX_POS + Y_PROBE_OFFSET_FROM_EXTRUDER))
- #endif
- #endif
+#endif // MESH_BED_LEVELING || AUTO_BED_LEVELING_UBL
- // These may be overridden in Configuration.h if a smaller area is desired
- #ifndef MESH_MIN_X
- #define MESH_MIN_X _MESH_MIN_X
+#if ENABLED(AUTO_BED_LEVELING_UBL) || ENABLED(AUTO_BED_LEVELING_3POINT)
+ #if IS_KINEMATIC
+ #define SIN0 0.0
+ #define SIN120 0.866025
+ #define SIN240 -0.866025
+ #define COS0 1.0
+ #define COS120 -0.5
+ #define COS240 -0.5
+ #ifndef PROBE_PT_1_X
+ #define PROBE_PT_1_X (X_CENTER + (_PROBE_RADIUS) * COS0)
#endif
- #ifndef MESH_MIN_Y
- #define MESH_MIN_Y _MESH_MIN_Y
+ #ifndef PROBE_PT_1_Y
+ #define PROBE_PT_1_Y (Y_CENTER + (_PROBE_RADIUS) * SIN0)
#endif
- #ifndef MESH_MAX_X
- #define MESH_MAX_X _MESH_MAX_X
+ #ifndef PROBE_PT_2_X
+ #define PROBE_PT_2_X (X_CENTER + (_PROBE_RADIUS) * COS120)
#endif
- #ifndef MESH_MAX_Y
- #define MESH_MAX_Y _MESH_MAX_Y
+ #ifndef PROBE_PT_2_Y
+ #define PROBE_PT_2_Y (Y_CENTER + (_PROBE_RADIUS) * SIN120)
#endif
-
- #endif // MESH_BED_LEVELING || AUTO_BED_LEVELING_UBL
-
- #if ENABLED(AUTO_BED_LEVELING_UBL) || ENABLED(AUTO_BED_LEVELING_3POINT)
- #if IS_KINEMATIC
- #define SIN0 0.0
- #define SIN120 0.866025
- #define SIN240 -0.866025
- #define COS0 1.0
- #define COS120 -0.5
- #define COS240 -0.5
- #ifndef PROBE_PT_1_X
- #define PROBE_PT_1_X (X_CENTER + (_PROBE_RADIUS) * COS0)
- #endif
- #ifndef PROBE_PT_1_Y
- #define PROBE_PT_1_Y (Y_CENTER + (_PROBE_RADIUS) * SIN0)
- #endif
- #ifndef PROBE_PT_2_X
- #define PROBE_PT_2_X (X_CENTER + (_PROBE_RADIUS) * COS120)
- #endif
- #ifndef PROBE_PT_2_Y
- #define PROBE_PT_2_Y (Y_CENTER + (_PROBE_RADIUS) * SIN120)
- #endif
- #ifndef PROBE_PT_3_X
- #define PROBE_PT_3_X (X_CENTER + (_PROBE_RADIUS) * COS240)
- #endif
- #ifndef PROBE_PT_3_Y
- #define PROBE_PT_3_Y (Y_CENTER + (_PROBE_RADIUS) * SIN240)
- #endif
- #else
- #ifndef PROBE_PT_1_X
- #define PROBE_PT_1_X MIN_PROBE_X
- #endif
- #ifndef PROBE_PT_1_Y
- #define PROBE_PT_1_Y MIN_PROBE_Y
- #endif
- #ifndef PROBE_PT_2_X
- #define PROBE_PT_2_X MAX_PROBE_X
- #endif
- #ifndef PROBE_PT_2_Y
- #define PROBE_PT_2_Y MIN_PROBE_Y
- #endif
- #ifndef PROBE_PT_3_X
- #define PROBE_PT_3_X X_CENTER
- #endif
- #ifndef PROBE_PT_3_Y
- #define PROBE_PT_3_Y MAX_PROBE_Y
- #endif
+ #ifndef PROBE_PT_3_X
+ #define PROBE_PT_3_X (X_CENTER + (_PROBE_RADIUS) * COS240)
#endif
- #endif
-
- #if ENABLED(AUTO_BED_LEVELING_LINEAR) || ENABLED(AUTO_BED_LEVELING_BILINEAR)
- #ifndef LEFT_PROBE_BED_POSITION
- #define LEFT_PROBE_BED_POSITION MIN_PROBE_X
- #endif
- #ifndef RIGHT_PROBE_BED_POSITION
- #define RIGHT_PROBE_BED_POSITION MAX_PROBE_X
- #endif
- #ifndef FRONT_PROBE_BED_POSITION
- #define FRONT_PROBE_BED_POSITION MIN_PROBE_Y
- #endif
- #ifndef BACK_PROBE_BED_POSITION
- #define BACK_PROBE_BED_POSITION MAX_PROBE_Y
- #endif
- #endif
-
- /**
- * Buzzer/Speaker
- */
- #if ENABLED(LCD_USE_I2C_BUZZER)
- #ifndef LCD_FEEDBACK_FREQUENCY_HZ
- #define LCD_FEEDBACK_FREQUENCY_HZ 1000
- #endif
- #ifndef LCD_FEEDBACK_FREQUENCY_DURATION_MS
- #define LCD_FEEDBACK_FREQUENCY_DURATION_MS 100
+ #ifndef PROBE_PT_3_Y
+ #define PROBE_PT_3_Y (Y_CENTER + (_PROBE_RADIUS) * SIN240)
#endif
#else
- #ifndef LCD_FEEDBACK_FREQUENCY_HZ
- #define LCD_FEEDBACK_FREQUENCY_HZ 5000
+ #ifndef PROBE_PT_1_X
+ #define PROBE_PT_1_X MIN_PROBE_X
#endif
- #ifndef LCD_FEEDBACK_FREQUENCY_DURATION_MS
- #define LCD_FEEDBACK_FREQUENCY_DURATION_MS 2
+ #ifndef PROBE_PT_1_Y
+ #define PROBE_PT_1_Y MIN_PROBE_Y
+ #endif
+ #ifndef PROBE_PT_2_X
+ #define PROBE_PT_2_X MAX_PROBE_X
+ #endif
+ #ifndef PROBE_PT_2_Y
+ #define PROBE_PT_2_Y MIN_PROBE_Y
+ #endif
+ #ifndef PROBE_PT_3_X
+ #define PROBE_PT_3_X X_CENTER
+ #endif
+ #ifndef PROBE_PT_3_Y
+ #define PROBE_PT_3_Y MAX_PROBE_Y
#endif
#endif
+#endif
- /**
- * VIKI2, miniVIKI, AZSMZ_12864, and MKS_12864OLED_SSD1306 require DOGLCD_SCK and DOGLCD_MOSI to be defined.
- */
- #if ENABLED(VIKI2) || ENABLED(miniVIKI) || ENABLED(AZSMZ_12864) || ENABLED(MKS_12864OLED_SSD1306)
- #ifndef DOGLCD_SCK
- #define DOGLCD_SCK SCK_PIN
- #endif
- #ifndef DOGLCD_MOSI
- #define DOGLCD_MOSI MOSI_PIN
- #endif
+#if ENABLED(AUTO_BED_LEVELING_LINEAR) || ENABLED(AUTO_BED_LEVELING_BILINEAR)
+ #ifndef LEFT_PROBE_BED_POSITION
+ #define LEFT_PROBE_BED_POSITION MIN_PROBE_X
#endif
+ #ifndef RIGHT_PROBE_BED_POSITION
+ #define RIGHT_PROBE_BED_POSITION MAX_PROBE_X
+ #endif
+ #ifndef FRONT_PROBE_BED_POSITION
+ #define FRONT_PROBE_BED_POSITION MIN_PROBE_Y
+ #endif
+ #ifndef BACK_PROBE_BED_POSITION
+ #define BACK_PROBE_BED_POSITION MAX_PROBE_Y
+ #endif
+#endif
- /**
- * Z_HOMING_HEIGHT / Z_CLEARANCE_BETWEEN_PROBES
- */
- #ifndef Z_HOMING_HEIGHT
- #ifndef Z_CLEARANCE_BETWEEN_PROBES
- #define Z_HOMING_HEIGHT 0
- #else
- #define Z_HOMING_HEIGHT Z_CLEARANCE_BETWEEN_PROBES
- #endif
+/**
+ * Buzzer/Speaker
+ */
+#if ENABLED(LCD_USE_I2C_BUZZER)
+ #ifndef LCD_FEEDBACK_FREQUENCY_HZ
+ #define LCD_FEEDBACK_FREQUENCY_HZ 1000
#endif
+ #ifndef LCD_FEEDBACK_FREQUENCY_DURATION_MS
+ #define LCD_FEEDBACK_FREQUENCY_DURATION_MS 100
+ #endif
+#else
+ #ifndef LCD_FEEDBACK_FREQUENCY_HZ
+ #define LCD_FEEDBACK_FREQUENCY_HZ 5000
+ #endif
+ #ifndef LCD_FEEDBACK_FREQUENCY_DURATION_MS
+ #define LCD_FEEDBACK_FREQUENCY_DURATION_MS 2
+ #endif
+#endif
+
+/**
+ * VIKI2, miniVIKI, AZSMZ_12864, and MKS_12864OLED_SSD1306 require DOGLCD_SCK and DOGLCD_MOSI to be defined.
+ */
+#if ENABLED(VIKI2) || ENABLED(miniVIKI) || ENABLED(AZSMZ_12864) || ENABLED(MKS_12864OLED_SSD1306)
+ #ifndef DOGLCD_SCK
+ #define DOGLCD_SCK SCK_PIN
+ #endif
+ #ifndef DOGLCD_MOSI
+ #define DOGLCD_MOSI MOSI_PIN
+ #endif
+#endif
+
+/**
+ * Z_HOMING_HEIGHT / Z_CLEARANCE_BETWEEN_PROBES
+ */
+#ifndef Z_HOMING_HEIGHT
#ifndef Z_CLEARANCE_BETWEEN_PROBES
- #define Z_CLEARANCE_BETWEEN_PROBES Z_HOMING_HEIGHT
- #endif
- #if Z_CLEARANCE_BETWEEN_PROBES > Z_HOMING_HEIGHT
- #define MANUAL_PROBE_HEIGHT Z_CLEARANCE_BETWEEN_PROBES
+ #define Z_HOMING_HEIGHT 0
#else
- #define MANUAL_PROBE_HEIGHT Z_HOMING_HEIGHT
+ #define Z_HOMING_HEIGHT Z_CLEARANCE_BETWEEN_PROBES
#endif
+#endif
+#ifndef Z_CLEARANCE_BETWEEN_PROBES
+ #define Z_CLEARANCE_BETWEEN_PROBES Z_HOMING_HEIGHT
+#endif
+#if Z_CLEARANCE_BETWEEN_PROBES > Z_HOMING_HEIGHT
+ #define MANUAL_PROBE_HEIGHT Z_CLEARANCE_BETWEEN_PROBES
+#else
+ #define MANUAL_PROBE_HEIGHT Z_HOMING_HEIGHT
+#endif
- // Stepper pulse duration, in cycles
- #define STEP_PULSE_CYCLES ((MINIMUM_STEPPER_PULSE) * CYCLES_PER_MICROSECOND)
+// Stepper pulse duration, in cycles
+#define STEP_PULSE_CYCLES ((MINIMUM_STEPPER_PULSE) * CYCLES_PER_MICROSECOND)
- // Updated G92 behavior shifts the workspace
- #define HAS_POSITION_SHIFT DISABLED(NO_WORKSPACE_OFFSETS)
- // The home offset also shifts the coordinate space
- #define HAS_HOME_OFFSET (DISABLED(NO_WORKSPACE_OFFSETS) && DISABLED(DELTA))
- // Either offset yields extra calculations on all moves
- #define HAS_WORKSPACE_OFFSET (HAS_POSITION_SHIFT || HAS_HOME_OFFSET)
- // M206 doesn't apply to DELTA
- #define HAS_M206_COMMAND (HAS_HOME_OFFSET && DISABLED(DELTA))
+// Updated G92 behavior shifts the workspace
+#define HAS_POSITION_SHIFT DISABLED(NO_WORKSPACE_OFFSETS)
+// The home offset also shifts the coordinate space
+#define HAS_HOME_OFFSET (DISABLED(NO_WORKSPACE_OFFSETS) && DISABLED(DELTA))
+// Either offset yields extra calculations on all moves
+#define HAS_WORKSPACE_OFFSET (HAS_POSITION_SHIFT || HAS_HOME_OFFSET)
+// M206 doesn't apply to DELTA
+#define HAS_M206_COMMAND (HAS_HOME_OFFSET && DISABLED(DELTA))
- // LCD timeout to status screen default is 15s
- #ifndef LCD_TIMEOUT_TO_STATUS
- #define LCD_TIMEOUT_TO_STATUS 15000
+// LCD timeout to status screen default is 15s
+#ifndef LCD_TIMEOUT_TO_STATUS
+ #define LCD_TIMEOUT_TO_STATUS 15000
+#endif
+
+// Shorthand
+#define GRID_MAX_POINTS ((GRID_MAX_POINTS_X) * (GRID_MAX_POINTS_Y))
+
+// Add commands that need sub-codes to this list
+#define USE_GCODE_SUBCODES ENABLED(G38_PROBE_TARGET) || ENABLED(CNC_COORDINATE_SYSTEMS) || ENABLED(POWER_LOSS_RECOVERY)
+
+// Parking Extruder
+#if ENABLED(PARKING_EXTRUDER)
+ #ifndef PARKING_EXTRUDER_GRAB_DISTANCE
+ #define PARKING_EXTRUDER_GRAB_DISTANCE 0
#endif
+ #ifndef PARKING_EXTRUDER_SOLENOIDS_PINS_ACTIVE
+ #define PARKING_EXTRUDER_SOLENOIDS_PINS_ACTIVE HIGH
+ #endif
+#endif
- // Shorthand
- #define GRID_MAX_POINTS ((GRID_MAX_POINTS_X) * (GRID_MAX_POINTS_Y))
+// Number of VFAT entries used. Each entry has 13 UTF-16 characters
+#if ENABLED(SCROLL_LONG_FILENAMES)
+ #define MAX_VFAT_ENTRIES (5)
+#else
+ #define MAX_VFAT_ENTRIES (2)
+#endif
- // Add commands that need sub-codes to this list
- #define USE_GCODE_SUBCODES ENABLED(G38_PROBE_TARGET) || ENABLED(CNC_COORDINATE_SYSTEMS) || ENABLED(POWER_LOSS_RECOVERY)
-
- // Parking Extruder
- #if ENABLED(PARKING_EXTRUDER)
- #ifndef PARKING_EXTRUDER_GRAB_DISTANCE
- #define PARKING_EXTRUDER_GRAB_DISTANCE 0
- #endif
- #ifndef PARKING_EXTRUDER_SOLENOIDS_PINS_ACTIVE
- #define PARKING_EXTRUDER_SOLENOIDS_PINS_ACTIVE HIGH
+// Set defaults for unspecified LED user colors
+#if ENABLED(LED_CONTROL_MENU)
+ #ifndef LED_USER_PRESET_RED
+ #define LED_USER_PRESET_RED 255
+ #endif
+ #ifndef LED_USER_PRESET_GREEN
+ #define LED_USER_PRESET_GREEN 255
+ #endif
+ #ifndef LED_USER_PRESET_BLUE
+ #define LED_USER_PRESET_BLUE 255
+ #endif
+ #ifndef LED_USER_PRESET_WHITE
+ #define LED_USER_PRESET_WHITE 0
+ #endif
+ #ifndef LED_USER_PRESET_BRIGHTNESS
+ #ifdef NEOPIXEL_BRIGHTNESS
+ #define LED_USER_PRESET_BRIGHTNESS NEOPIXEL_BRIGHTNESS
+ #else
+ #define LED_USER_PRESET_BRIGHTNESS 255
#endif
#endif
+#endif
- // Number of VFAT entries used. Each entry has 13 UTF-16 characters
- #if ENABLED(SCROLL_LONG_FILENAMES)
- #define MAX_VFAT_ENTRIES (5)
- #else
- #define MAX_VFAT_ENTRIES (2)
- #endif
+// Nozzle park
+#if ENABLED(NOZZLE_PARK_FEATURE) && ENABLED(DELTA)
+ #undef NOZZLE_PARK_Z_FEEDRATE
+ #define NOZZLE_PARK_Z_FEEDRATE NOZZLE_PARK_XY_FEEDRATE
+#endif
- // Set defaults for unspecified LED user colors
- #if ENABLED(LED_CONTROL_MENU)
- #ifndef LED_USER_PRESET_RED
- #define LED_USER_PRESET_RED 255
- #endif
- #ifndef LED_USER_PRESET_GREEN
- #define LED_USER_PRESET_GREEN 255
- #endif
- #ifndef LED_USER_PRESET_BLUE
- #define LED_USER_PRESET_BLUE 255
- #endif
- #ifndef LED_USER_PRESET_WHITE
- #define LED_USER_PRESET_WHITE 0
- #endif
- #ifndef LED_USER_PRESET_BRIGHTNESS
- #ifdef NEOPIXEL_BRIGHTNESS
- #define LED_USER_PRESET_BRIGHTNESS NEOPIXEL_BRIGHTNESS
- #else
- #define LED_USER_PRESET_BRIGHTNESS 255
- #endif
- #endif
- #endif
-
- // Nozzle park
- #if ENABLED(NOZZLE_PARK_FEATURE) && ENABLED(DELTA)
- #undef NOZZLE_PARK_Z_FEEDRATE
- #define NOZZLE_PARK_Z_FEEDRATE NOZZLE_PARK_XY_FEEDRATE
- #endif
-
- #if ENABLED(SDCARD_SORT_ALPHA)
- #define HAS_FOLDER_SORTING (FOLDER_SORTING || ENABLED(SDSORT_GCODE))
- #endif
+#if ENABLED(SDCARD_SORT_ALPHA)
+ #define HAS_FOLDER_SORTING (FOLDER_SORTING || ENABLED(SDSORT_GCODE))
+#endif
#endif // CONDITIONALS_POST_H
diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h
index eba39f17d9..63c2d3ff0a 100644
--- a/Marlin/Configuration.h
+++ b/Marlin/Configuration.h
@@ -30,7 +30,7 @@
/*
* Enables a filament sensor plugged into the laser pin. Disables the laser
*/
-#define FilamentSensor
+//#define FilamentSensor
/**
* Configuration.h
*
diff --git a/Marlin/G26_Mesh_Validation_Tool.cpp b/Marlin/G26_Mesh_Validation_Tool.cpp
index 365cd903aa..8a3f951c8c 100644
--- a/Marlin/G26_Mesh_Validation_Tool.cpp
+++ b/Marlin/G26_Mesh_Validation_Tool.cpp
@@ -134,9 +134,6 @@
// External references
extern Planner planner;
- #if ENABLED(ULTRA_LCD)
- extern char lcd_status_message[];
- #endif
// Private functions
@@ -158,7 +155,7 @@
static int8_t g26_prime_flag;
- #if ENABLED(NEWPANEL)
+ #if ENABLED(ULTIPANEL)
/**
* If the LCD is clicked, cancel, wait for release, return true
@@ -183,9 +180,9 @@
void G26_line_to_destination(const float &feed_rate) {
const float save_feedrate = feedrate_mm_s;
- feedrate_mm_s = feed_rate; // use specified feed rate
+ feedrate_mm_s = feed_rate;
prepare_move_to_destination(); // will ultimately call ubl.line_to_destination_cartesian or ubl.prepare_linear_move_to for UBL_SEGMENTED
- feedrate_mm_s = save_feedrate; // restore global feed rate
+ feedrate_mm_s = save_feedrate;
}
void move_to(const float &rx, const float &ry, const float &z, const float &e_delta) {
@@ -242,7 +239,7 @@
*/
inline bool prime_nozzle() {
- #if ENABLED(NEWPANEL)
+ #if ENABLED(ULTIPANEL)
float Total_Prime = 0.0;
if (g26_prime_flag == -1) { // The user wants to control how much filament gets purged
@@ -264,7 +261,7 @@
#endif
G26_line_to_destination(planner.max_feedrate_mm_s[E_AXIS] / 15.0);
set_destination_from_current();
- stepper.synchronize(); // Without this synchronize, the purge is more consistent,
+ planner.synchronize(); // Without this synchronize, the purge is more consistent,
// but because the planner has a buffer, we won't be able
// to stop as quickly. So we put up with the less smooth
// action to give the user a more responsive 'Stop'.
@@ -274,8 +271,6 @@
wait_for_release();
- strcpy_P(lcd_status_message, PSTR("Done Priming")); // Hack to get the message up. May be obsolete.
-
lcd_setstatusPGM(PSTR("Done Priming"), 99);
lcd_quick_feedback(true);
lcd_external_control = false;
@@ -363,7 +358,7 @@
// If the end point of the line is closer to the nozzle, flip the direction,
// moving from the end to the start. On very small lines the optimization isn't worth it.
- if (dist_end < dist_start && (INTERSECTION_CIRCLE_RADIUS) < FABS(line_length))
+ if (dist_end < dist_start && (INTERSECTION_CIRCLE_RADIUS) < ABS(line_length))
return print_line_from_here_to_there(ex, ey, ez, sx, sy, sz);
// Decide whether to retract & bump
@@ -389,7 +384,7 @@
for (uint8_t i = 0; i < GRID_MAX_POINTS_X; i++) {
for (uint8_t j = 0; j < GRID_MAX_POINTS_Y; j++) {
- #if ENABLED(NEWPANEL)
+ #if ENABLED(ULTIPANEL)
if (user_canceled()) return true; // Check if the user wants to stop the Mesh Validation
#endif
@@ -480,14 +475,14 @@
if (g26_bed_temp > 25) {
lcd_setstatusPGM(PSTR("G26 Heating Bed."), 99);
lcd_quick_feedback(true);
- #if ENABLED(NEWPANEL)
+ #if ENABLED(ULTIPANEL)
lcd_external_control = true;
#endif
#endif
thermalManager.setTargetBed(g26_bed_temp);
- while (abs(thermalManager.degBed() - g26_bed_temp) > 3) {
+ while (ABS(thermalManager.degBed() - g26_bed_temp) > 3) {
- #if ENABLED(NEWPANEL)
+ #if ENABLED(ULTIPANEL)
if (is_lcd_clicked()) return exit_from_g26();
#endif
@@ -508,9 +503,9 @@
// Start heating the nozzle and wait for it to reach temperature.
thermalManager.setTargetHotend(g26_hotend_temp, 0);
- while (abs(thermalManager.degHotend(0) - g26_hotend_temp) > 3) {
+ while (ABS(thermalManager.degHotend(0) - g26_hotend_temp) > 3) {
- #if ENABLED(NEWPANEL)
+ #if ENABLED(ULTIPANEL)
if (is_lcd_clicked()) return exit_from_g26();
#endif
@@ -623,7 +618,7 @@
if (parser.seen('P')) {
if (!parser.has_value()) {
- #if ENABLED(NEWPANEL)
+ #if ENABLED(ULTIPANEL)
g26_prime_flag = -1;
#else
SERIAL_PROTOCOLLNPGM("?Prime length must be specified when not using an LCD.");
@@ -668,7 +663,7 @@
}
int16_t g26_repeats;
- #if ENABLED(NEWPANEL)
+ #if ENABLED(ULTIPANEL)
g26_repeats = parser.intval('R', GRID_MAX_POINTS + 1);
#else
if (!parser.seen('R')) {
@@ -727,27 +722,31 @@
move_to(destination, 0.0);
move_to(destination, g26_ooze_amount);
- #if ENABLED(NEWPANEL)
+ #if ENABLED(ULTIPANEL)
lcd_external_control = true;
#endif
//debug_current_and_destination(PSTR("Starting G26 Mesh Validation Pattern."));
- /**
- * Pre-generate radius offset values at 30 degree intervals to reduce CPU load.
- */
- #define A_INT 30
- #define _ANGS (360 / A_INT)
- #define A_CNT (_ANGS / 2)
- #define _IND(A) ((A + _ANGS * 8) % _ANGS)
- #define _COS(A) (trig_table[_IND(A) % A_CNT] * (_IND(A) >= A_CNT ? -1 : 1))
- #define _SIN(A) (-_COS((A + A_CNT / 2) % _ANGS))
- #if A_CNT & 1
- #error "A_CNT must be a positive value. Please change A_INT."
- #endif
- float trig_table[A_CNT];
- for (uint8_t i = 0; i < A_CNT; i++)
- trig_table[i] = INTERSECTION_CIRCLE_RADIUS * cos(RADIANS(i * A_INT));
+ #if DISABLED(ARC_SUPPORT)
+
+ /**
+ * Pre-generate radius offset values at 30 degree intervals to reduce CPU load.
+ */
+ #define A_INT 30
+ #define _ANGS (360 / A_INT)
+ #define A_CNT (_ANGS / 2)
+ #define _IND(A) ((A + _ANGS * 8) % _ANGS)
+ #define _COS(A) (trig_table[_IND(A) % A_CNT] * (_IND(A) >= A_CNT ? -1 : 1))
+ #define _SIN(A) (-_COS((A + A_CNT / 2) % _ANGS))
+ #if A_CNT & 1
+ #error "A_CNT must be a positive value. Please change A_INT."
+ #endif
+ float trig_table[A_CNT];
+ for (uint8_t i = 0; i < A_CNT; i++)
+ trig_table[i] = INTERSECTION_CIRCLE_RADIUS * cos(RADIANS(i * A_INT));
+
+ #endif // !ARC_SUPPORT
mesh_index_pair location;
do {
@@ -766,52 +765,127 @@
// which is always drawn counter-clockwise.
const uint8_t xi = location.x_index, yi = location.y_index;
const bool f = yi == 0, r = xi >= GRID_MAX_POINTS_X - 1, b = yi >= GRID_MAX_POINTS_Y - 1;
- int8_t start_ind = -2, end_ind = 9; // Assume a full circle (from 5:00 to 5:00)
- if (xi == 0) { // Left edge? Just right half.
- start_ind = f ? 0 : -3; // 03:00 to 12:00 for front-left
- end_ind = b ? 0 : 2; // 06:00 to 03:00 for back-left
- }
- else if (r) { // Right edge? Just left half.
- start_ind = b ? 6 : 3; // 12:00 to 09:00 for front-right
- end_ind = f ? 5 : 8; // 09:00 to 06:00 for back-right
- }
- else if (f) { // Front edge? Just back half.
- start_ind = 0; // 03:00
- end_ind = 5; // 09:00
- }
- else if (b) { // Back edge? Just front half.
- start_ind = 6; // 09:00
- end_ind = 11; // 03:00
- }
- for (int8_t ind = start_ind; ind <= end_ind; ind++) {
+ #if ENABLED(ARC_SUPPORT)
- #if ENABLED(NEWPANEL)
- if (user_canceled()) goto LEAVE; // Check if the user wants to stop the Mesh Validation
+ #define ARC_LENGTH(quarters) (INTERSECTION_CIRCLE_RADIUS * M_PI * (quarters) / 2)
+ float sx = circle_x + INTERSECTION_CIRCLE_RADIUS, // default to full circle
+ ex = circle_x + INTERSECTION_CIRCLE_RADIUS,
+ sy = circle_y, ey = circle_y,
+ arc_length = ARC_LENGTH(4);
+
+ // Figure out where to start and end the arc - we always print counterclockwise
+ if (xi == 0) { // left edge
+ sx = f ? circle_x + INTERSECTION_CIRCLE_RADIUS : circle_x;
+ ex = b ? circle_x + INTERSECTION_CIRCLE_RADIUS : circle_x;
+ sy = f ? circle_y : circle_y - INTERSECTION_CIRCLE_RADIUS;
+ ey = b ? circle_y : circle_y + INTERSECTION_CIRCLE_RADIUS;
+ arc_length = (f || b) ? ARC_LENGTH(1) : ARC_LENGTH(2);
+ }
+ else if (r) { // right edge
+ sx = b ? circle_x - INTERSECTION_CIRCLE_RADIUS : circle_x;
+ ex = f ? circle_x - INTERSECTION_CIRCLE_RADIUS : circle_x;
+ sy = b ? circle_y : circle_y + INTERSECTION_CIRCLE_RADIUS;
+ ey = f ? circle_y : circle_y - INTERSECTION_CIRCLE_RADIUS;
+ arc_length = (f || b) ? ARC_LENGTH(1) : ARC_LENGTH(2);
+ }
+ else if (f) {
+ sx = circle_x + INTERSECTION_CIRCLE_RADIUS;
+ ex = circle_x - INTERSECTION_CIRCLE_RADIUS;
+ sy = ey = circle_y;
+ arc_length = ARC_LENGTH(2);
+ }
+ else if (b) {
+ sx = circle_x - INTERSECTION_CIRCLE_RADIUS;
+ ex = circle_x + INTERSECTION_CIRCLE_RADIUS;
+ sy = ey = circle_y;
+ arc_length = ARC_LENGTH(2);
+ }
+ const float arc_offset[2] = {
+ circle_x - sx,
+ circle_y - sy
+ };
+
+ const float dx_s = current_position[X_AXIS] - sx, // find our distance from the start of the actual circle
+ dy_s = current_position[Y_AXIS] - sy,
+ dist_start = HYPOT2(dx_s, dy_s);
+ const float endpoint[XYZE] = {
+ ex, ey,
+ g26_layer_height,
+ current_position[E_AXIS] + (arc_length * g26_e_axis_feedrate * g26_extrusion_multiplier)
+ };
+
+ if (dist_start > 2.0) {
+ retract_filament(destination);
+ //todo: parameterize the bump height with a define
+ move_to(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS] + 0.500, 0.0); // Z bump to minimize scraping
+ move_to(sx, sy, g26_layer_height + 0.500, 0.0); // Get to the starting point with no extrusion while bumped
+ }
+
+ move_to(sx, sy, g26_layer_height, 0.0); // Get to the starting point with no extrusion / un-Z bump
+
+ recover_filament(destination);
+ const float save_feedrate = feedrate_mm_s;
+ feedrate_mm_s = PLANNER_XY_FEEDRATE() / 10.0;
+ plan_arc(endpoint, arc_offset, false); // Draw a counter-clockwise arc
+ feedrate_mm_s = save_feedrate;
+ set_destination_from_current();
+ #if ENABLED(ULTIPANEL)
+ if (user_canceled()) goto LEAVE; // Check if the user wants to stop the Mesh Validation
#endif
- float rx = circle_x + _COS(ind), // For speed, these are now a lookup table entry
- ry = circle_y + _SIN(ind),
- xe = circle_x + _COS(ind + 1),
- ye = circle_y + _SIN(ind + 1);
+ #else // !ARC_SUPPORT
- #if IS_KINEMATIC
- // Check to make sure this segment is entirely on the bed, skip if not.
- if (!position_is_reachable(rx, ry) || !position_is_reachable(xe, ye)) continue;
- #else // not, we need to skip
- rx = constrain(rx, X_MIN_POS + 1, X_MAX_POS - 1); // This keeps us from bumping the endstops
- ry = constrain(ry, Y_MIN_POS + 1, Y_MAX_POS - 1);
- xe = constrain(xe, X_MIN_POS + 1, X_MAX_POS - 1);
- ye = constrain(ye, Y_MIN_POS + 1, Y_MAX_POS - 1);
- #endif
+ int8_t start_ind = -2, end_ind = 9; // Assume a full circle (from 5:00 to 5:00)
+ if (xi == 0) { // Left edge? Just right half.
+ start_ind = f ? 0 : -3; // 03:00 to 12:00 for front-left
+ end_ind = b ? 0 : 2; // 06:00 to 03:00 for back-left
+ }
+ else if (r) { // Right edge? Just left half.
+ start_ind = b ? 6 : 3; // 12:00 to 09:00 for front-right
+ end_ind = f ? 5 : 8; // 09:00 to 06:00 for back-right
+ }
+ else if (f) { // Front edge? Just back half.
+ start_ind = 0; // 03:00
+ end_ind = 5; // 09:00
+ }
+ else if (b) { // Back edge? Just front half.
+ start_ind = 6; // 09:00
+ end_ind = 11; // 03:00
+ }
- print_line_from_here_to_there(rx, ry, g26_layer_height, xe, ye, g26_layer_height);
- SERIAL_FLUSH(); // Prevent host M105 buffer overrun.
- }
- if (look_for_lines_to_connect())
- goto LEAVE;
+ for (int8_t ind = start_ind; ind <= end_ind; ind++) {
+
+ #if ENABLED(ULTIPANEL)
+ if (user_canceled()) goto LEAVE; // Check if the user wants to stop the Mesh Validation
+ #endif
+
+ float rx = circle_x + _COS(ind), // For speed, these are now a lookup table entry
+ ry = circle_y + _SIN(ind),
+ xe = circle_x + _COS(ind + 1),
+ ye = circle_y + _SIN(ind + 1);
+
+ #if IS_KINEMATIC
+ // Check to make sure this segment is entirely on the bed, skip if not.
+ if (!position_is_reachable(rx, ry) || !position_is_reachable(xe, ye)) continue;
+ #else // not, we need to skip
+ rx = constrain(rx, X_MIN_POS + 1, X_MAX_POS - 1); // This keeps us from bumping the endstops
+ ry = constrain(ry, Y_MIN_POS + 1, Y_MAX_POS - 1);
+ xe = constrain(xe, X_MIN_POS + 1, X_MAX_POS - 1);
+ ye = constrain(ye, Y_MIN_POS + 1, Y_MAX_POS - 1);
+ #endif
+
+ print_line_from_here_to_there(rx, ry, g26_layer_height, xe, ye, g26_layer_height);
+ SERIAL_FLUSH(); // Prevent host M105 buffer overrun.
+ }
+
+ #endif // !ARC_SUPPORT
+
+ if (look_for_lines_to_connect()) goto LEAVE;
}
+
SERIAL_FLUSH(); // Prevent host M105 buffer overrun.
+
} while (--g26_repeats && location.x_index >= 0 && location.y_index >= 0);
LEAVE:
@@ -831,7 +905,7 @@
move_to(destination, 0); // Move back to the starting position
//debug_current_and_destination(PSTR("done doing X/Y move."));
- #if ENABLED(NEWPANEL)
+ #if ENABLED(ULTIPANEL)
lcd_external_control = false; // Give back control of the LCD Panel!
#endif
diff --git a/Marlin/HAL.h b/Marlin/HAL.h
new file mode 100644
index 0000000000..e4c2f805a2
--- /dev/null
+++ b/Marlin/HAL.h
@@ -0,0 +1,305 @@
+/* **************************************************************************
+
+ Marlin 3D Printer Firmware
+ Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+
+ Copyright (c) 2016 Bob Cousins bobcousins42@googlemail.com
+
+ 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 .
+****************************************************************************/
+
+/**
+ * Description: HAL for __AVR__
+ */
+
+#ifndef _HAL_AVR_H_
+#define _HAL_AVR_H_
+
+// --------------------------------------------------------------------------
+// Includes
+// --------------------------------------------------------------------------
+
+#include "fastio.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// --------------------------------------------------------------------------
+// Defines
+// --------------------------------------------------------------------------
+
+//#define analogInputToDigitalPin(IO) IO
+
+// Bracket code that shouldn't be interrupted
+#ifndef CRITICAL_SECTION_START
+ #define CRITICAL_SECTION_START unsigned char _sreg = SREG; cli();
+ #define CRITICAL_SECTION_END SREG = _sreg;
+#endif
+
+// --------------------------------------------------------------------------
+// Types
+// --------------------------------------------------------------------------
+
+typedef uint16_t hal_timer_t;
+#define HAL_TIMER_TYPE_MAX 0xFFFF
+
+typedef int8_t pin_t;
+
+#define HAL_SERVO_LIB Servo
+
+// --------------------------------------------------------------------------
+// Public Variables
+// --------------------------------------------------------------------------
+
+//extern uint8_t MCUSR;
+
+// --------------------------------------------------------------------------
+// Public functions
+// --------------------------------------------------------------------------
+
+//void cli(void);
+
+//void _delay_ms(const int delay);
+
+inline void HAL_clear_reset_source(void) { MCUSR = 0; }
+inline uint8_t HAL_get_reset_source(void) { return MCUSR; }
+
+// eeprom
+//void eeprom_write_byte(unsigned char *pos, unsigned char value);
+//unsigned char eeprom_read_byte(unsigned char *pos);
+
+// timers
+#define HAL_TIMER_RATE ((F_CPU) / 8) // i.e., 2MHz or 2.5MHz
+
+#define STEP_TIMER_NUM 1
+#define TEMP_TIMER_NUM 0
+#define PULSE_TIMER_NUM TEMP_TIMER_NUM
+
+#define HAL_STEPPER_TIMER_RATE HAL_TIMER_RATE
+#define HAL_TICKS_PER_US ((HAL_STEPPER_TIMER_RATE) / 1000000) // Cannot be of type double
+#define STEPPER_TIMER_PRESCALE 8
+#define STEP_TIMER_MIN_INTERVAL 8 // minimum time in µs between stepper interrupts
+
+#define TEMP_TIMER_FREQUENCY ((F_CPU) / 64.0 / 256.0)
+
+#define TIMER_OCR_1 OCR1A
+#define TIMER_COUNTER_1 TCNT1
+
+#define TIMER_OCR_0 OCR0A
+#define TIMER_COUNTER_0 TCNT0
+
+#define PULSE_TIMER_PRESCALE 8
+
+#define ENABLE_STEPPER_DRIVER_INTERRUPT() SBI(TIMSK1, OCIE1A)
+#define DISABLE_STEPPER_DRIVER_INTERRUPT() CBI(TIMSK1, OCIE1A)
+#define STEPPER_ISR_ENABLED() TEST(TIMSK1, OCIE1A)
+
+#define ENABLE_TEMPERATURE_INTERRUPT() SBI(TIMSK0, OCIE0B)
+#define DISABLE_TEMPERATURE_INTERRUPT() CBI(TIMSK0, OCIE0B)
+#define TEMPERATURE_ISR_ENABLED() TEST(TIMSK0, OCIE0B)
+
+#define HAL_timer_start(timer_num, frequency)
+
+#define _CAT(a, ...) a ## __VA_ARGS__
+#define HAL_timer_set_compare(timer, compare) (_CAT(TIMER_OCR_, timer) = compare)
+#define HAL_timer_restrain(timer, interval_ticks) NOLESS(_CAT(TIMER_OCR_, timer), _CAT(TIMER_COUNTER_, timer) + interval_ticks)
+
+#define HAL_timer_get_compare(timer) _CAT(TIMER_OCR_, timer)
+#define HAL_timer_get_count(timer) _CAT(TIMER_COUNTER_, timer)
+
+/**
+ * On AVR there is no hardware prioritization and preemption of
+ * interrupts, so this emulates it. The UART has first priority
+ * (otherwise, characters will be lost due to UART overflow).
+ * Then: Stepper, Endstops, Temperature, and -finally- all others.
+ */
+#define HAL_timer_isr_prologue(TIMER_NUM)
+#define HAL_timer_isr_epilogue(TIMER_NUM)
+
+/* 18 cycles maximum latency */
+#define HAL_STEP_TIMER_ISR \
+extern "C" void TIMER1_COMPA_vect (void) __attribute__ ((signal, naked, used, externally_visible)); \
+extern "C" void TIMER1_COMPA_vect_bottom (void) asm ("TIMER1_COMPA_vect_bottom") __attribute__ ((used, externally_visible, noinline)); \
+void TIMER1_COMPA_vect (void) { \
+ __asm__ __volatile__ ( \
+ A("push r16") /* 2 Save R16 */ \
+ A("in r16, __SREG__") /* 1 Get SREG */ \
+ A("push r16") /* 2 Save SREG into stack */ \
+ A("lds r16, %[timsk0]") /* 2 Load into R0 the Temperature timer Interrupt mask register */ \
+ A("push r16") /* 2 Save TIMSK0 into the stack */ \
+ A("andi r16,~%[msk0]") /* 1 Disable the temperature ISR */ \
+ A("sts %[timsk0], r16") /* 2 And set the new value */ \
+ A("lds r16, %[timsk1]") /* 2 Load into R0 the stepper timer Interrupt mask register [TIMSK1] */ \
+ A("andi r16,~%[msk1]") /* 1 Disable the stepper ISR */ \
+ A("sts %[timsk1], r16") /* 2 And set the new value */ \
+ A("sei") /* 1 Enable global interrupts - stepper and temperature ISRs are disabled, so no risk of reentry or being preempted by the temperature ISR */ \
+ A("push r16") /* 2 Save TIMSK1 into stack */ \
+ A("in r16, 0x3B") /* 1 Get RAMPZ register */ \
+ A("push r16") /* 2 Save RAMPZ into stack */ \
+ A("in r16, 0x3C") /* 1 Get EIND register */ \
+ A("push r0") /* C runtime can modify all the following registers without restoring them */ \
+ A("push r1") \
+ A("push r18") \
+ A("push r19") \
+ A("push r20") \
+ A("push r21") \
+ A("push r22") \
+ A("push r23") \
+ A("push r24") \
+ A("push r25") \
+ A("push r26") \
+ A("push r27") \
+ A("push r30") \
+ A("push r31") \
+ A("clr r1") /* C runtime expects this register to be 0 */ \
+ A("call TIMER1_COMPA_vect_bottom") /* Call the bottom handler - No inlining allowed, otherwise registers used are not saved */ \
+ A("pop r31") \
+ A("pop r30") \
+ A("pop r27") \
+ A("pop r26") \
+ A("pop r25") \
+ A("pop r24") \
+ A("pop r23") \
+ A("pop r22") \
+ A("pop r21") \
+ A("pop r20") \
+ A("pop r19") \
+ A("pop r18") \
+ A("pop r1") \
+ A("pop r0") \
+ A("out 0x3C, r16") /* 1 Restore EIND register */ \
+ A("pop r16") /* 2 Get the original RAMPZ register value */ \
+ A("out 0x3B, r16") /* 1 Restore RAMPZ register to its original value */ \
+ A("pop r16") /* 2 Get the original TIMSK1 value but with stepper ISR disabled */ \
+ A("ori r16,%[msk1]") /* 1 Reenable the stepper ISR */ \
+ A("cli") /* 1 Disable global interrupts - Reenabling Stepper ISR can reenter amd temperature can reenter, and we want that, if it happens, after this ISR has ended */ \
+ A("sts %[timsk1], r16") /* 2 And restore the old value - This reenables the stepper ISR */ \
+ A("pop r16") /* 2 Get the temperature timer Interrupt mask register [TIMSK0] */ \
+ A("sts %[timsk0], r16") /* 2 And restore the old value - This reenables the temperature ISR */ \
+ A("pop r16") /* 2 Get the old SREG value */ \
+ A("out __SREG__, r16") /* 1 And restore the SREG value */ \
+ A("pop r16") /* 2 Restore R16 value */ \
+ A("reti") /* 4 Return from interrupt */ \
+ : \
+ : [timsk0] "i" ((uint16_t)&TIMSK0), \
+ [timsk1] "i" ((uint16_t)&TIMSK1), \
+ [msk0] "M" ((uint8_t)(1< 7) ADCSRB = _BV(MUX5); else ADCSRB = 0; SET_ADMUX_ADCSRA(pin)
+#else
+ #define HAL_START_ADC(pin) ADCSRB = 0; SET_ADMUX_ADCSRA(pin)
+#endif
+
+#define HAL_READ_ADC ADC
+
+#define GET_PIN_MAP_PIN(index) index
+#define GET_PIN_MAP_INDEX(pin) pin
+#define PARSED_PIN_INDEX(code, dval) parser.intval(code, dval)
+
+#define HAL_SENSITIVE_PINS 0, 1
+
+#endif // _HAL_AVR_H_
diff --git a/Marlin/I2CPositionEncoder.cpp b/Marlin/I2CPositionEncoder.cpp
index 433c917b4f..ce7029237b 100644
--- a/Marlin/I2CPositionEncoder.cpp
+++ b/Marlin/I2CPositionEncoder.cpp
@@ -99,7 +99,7 @@
//the encoder likely lost its place when the error occured, so we'll reset and use the printer's
//idea of where it the axis is to re-initialise
- float position = stepper.get_axis_position_mm(encoderAxis);
+ float position = planner.get_axis_position_mm(encoderAxis);
int32_t positionInTicks = position * get_ticks_unit();
//shift position from previous to current position
@@ -134,7 +134,7 @@
#ifdef I2CPE_EC_THRESH_PROPORTIONAL
const millis_t deltaTime = positionTime - lastPositionTime;
- const uint32_t distance = abs(position - lastPosition),
+ const uint32_t distance = ABS(position - lastPosition),
speed = distance / deltaTime;
const float threshold = constrain((speed / 50), 1, 50) * ecThreshold;
#else
@@ -150,7 +150,7 @@
LOOP_L_N(i, I2CPE_ERR_ARRAY_SIZE) {
sum += err[i];
- if (i) diffSum += abs(err[i-1] - err[i]);
+ if (i) diffSum += ABS(err[i-1] - err[i]);
}
const int32_t error = int32_t(sum / (I2CPE_ERR_ARRAY_SIZE + 1)); //calculate average for error
@@ -163,7 +163,7 @@
//SERIAL_ECHOLN(error);
#ifdef I2CPE_ERR_THRESH_ABORT
- if (labs(error) > I2CPE_ERR_THRESH_ABORT * planner.axis_steps_per_mm[encoderAxis]) {
+ if (ABS(error) > I2CPE_ERR_THRESH_ABORT * planner.axis_steps_per_mm[encoderAxis]) {
//kill("Significant Error");
SERIAL_ECHOPGM("Axis error greater than set threshold, aborting!");
SERIAL_ECHOLN(error);
@@ -175,8 +175,8 @@
if (errIdx == 0) {
// In order to correct for "error" but avoid correcting for noise and non-skips
// it must be > threshold and have a difference average of < 10 and be < 2000 steps
- if (labs(error) > threshold * planner.axis_steps_per_mm[encoderAxis] &&
- diffSum < 10 * (I2CPE_ERR_ARRAY_SIZE - 1) && labs(error) < 2000) { // Check for persistent error (skip)
+ if (ABS(error) > threshold * planner.axis_steps_per_mm[encoderAxis] &&
+ diffSum < 10 * (I2CPE_ERR_ARRAY_SIZE - 1) && ABS(error) < 2000) { // Check for persistent error (skip)
errPrst[errPrstIdx++] = error; // Error must persist for I2CPE_ERR_PRST_ARRAY_SIZE error cycles. This also serves to improve the average accuracy
if (errPrstIdx >= I2CPE_ERR_PRST_ARRAY_SIZE) {
float sumP = 0;
@@ -193,14 +193,14 @@
errPrstIdx = 0;
}
#else
- if (labs(error) > threshold * planner.axis_steps_per_mm[encoderAxis]) {
+ if (ABS(error) > threshold * planner.axis_steps_per_mm[encoderAxis]) {
//SERIAL_ECHOLN(error);
//SERIAL_ECHOLN(position);
thermalManager.babystepsTodo[encoderAxis] = -LROUND(error / 2);
}
#endif
- if (labs(error) > I2CPE_ERR_CNT_THRESH * planner.axis_steps_per_mm[encoderAxis]) {
+ if (ABS(error) > I2CPE_ERR_CNT_THRESH * planner.axis_steps_per_mm[encoderAxis]) {
const millis_t ms = millis();
if (ELAPSED(ms, nextErrorCountTime)) {
SERIAL_ECHOPAIR("Large error on ", axis_codes[encoderAxis]);
@@ -254,11 +254,11 @@
float I2CPositionEncoder::get_axis_error_mm(const bool report) {
float target, actual, error;
- target = stepper.get_axis_position_mm(encoderAxis);
+ target = planner.get_axis_position_mm(encoderAxis);
actual = mm_from_count(position);
error = actual - target;
- if (labs(error) > 10000) error = 0; // ?
+ if (ABS(error) > 10000) error = 0; // ?
if (report) {
SERIAL_ECHO(axis_codes[encoderAxis]);
@@ -293,7 +293,7 @@
error = (encoderCountInStepperTicksScaled - target);
//suppress discontinuities (might be caused by bad I2C readings...?)
- bool suppressOutput = (labs(error - errorPrev) > 100);
+ const bool suppressOutput = (ABS(error - errorPrev) > 100);
if (report) {
SERIAL_ECHO(axis_codes[encoderAxis]);
@@ -349,18 +349,18 @@
ec = false;
LOOP_NA(i) {
- startCoord[i] = stepper.get_axis_position_mm((AxisEnum)i);
- endCoord[i] = stepper.get_axis_position_mm((AxisEnum)i);
+ startCoord[i] = planner.get_axis_position_mm((AxisEnum)i);
+ endCoord[i] = planner.get_axis_position_mm((AxisEnum)i);
}
startCoord[encoderAxis] = startPosition;
endCoord[encoderAxis] = endPosition;
- stepper.synchronize();
+ planner.synchronize();
planner.buffer_line(startCoord[X_AXIS], startCoord[Y_AXIS], startCoord[Z_AXIS],
- stepper.get_axis_position_mm(E_AXIS), feedrate, 0);
- stepper.synchronize();
+ planner.get_axis_position_mm(E_AXIS), feedrate, 0);
+ planner.synchronize();
// if the module isn't currently trusted, wait until it is (or until it should be if things are working)
if (!trusted) {
@@ -371,8 +371,8 @@
if (trusted) { // if trusted, commence test
planner.buffer_line(endCoord[X_AXIS], endCoord[Y_AXIS], endCoord[Z_AXIS],
- stepper.get_axis_position_mm(E_AXIS), feedrate, 0);
- stepper.synchronize();
+ planner.get_axis_position_mm(E_AXIS), feedrate, 0);
+ planner.synchronize();
}
return trusted;
@@ -408,19 +408,19 @@
travelDistance = endDistance - startDistance;
LOOP_NA(i) {
- startCoord[i] = stepper.get_axis_position_mm((AxisEnum)i);
- endCoord[i] = stepper.get_axis_position_mm((AxisEnum)i);
+ startCoord[i] = planner.get_axis_position_mm((AxisEnum)i);
+ endCoord[i] = planner.get_axis_position_mm((AxisEnum)i);
}
startCoord[encoderAxis] = startDistance;
endCoord[encoderAxis] = endDistance;
- stepper.synchronize();
+ planner.synchronize();
LOOP_L_N(i, iter) {
planner.buffer_line(startCoord[X_AXIS], startCoord[Y_AXIS], startCoord[Z_AXIS],
- stepper.get_axis_position_mm(E_AXIS), feedrate, 0);
- stepper.synchronize();
+ planner.get_axis_position_mm(E_AXIS), feedrate, 0);
+ planner.synchronize();
delay(250);
startCount = get_position();
@@ -428,14 +428,14 @@
//do_blocking_move_to(endCoord[X_AXIS],endCoord[Y_AXIS],endCoord[Z_AXIS]);
planner.buffer_line(endCoord[X_AXIS], endCoord[Y_AXIS], endCoord[Z_AXIS],
- stepper.get_axis_position_mm(E_AXIS), feedrate, 0);
- stepper.synchronize();
+ planner.get_axis_position_mm(E_AXIS), feedrate, 0);
+ planner.synchronize();
//Read encoder distance
delay(250);
stopCount = get_position();
- travelledDistance = mm_from_count(abs(stopCount - startCount));
+ travelledDistance = mm_from_count(ABS(stopCount - startCount));
SERIAL_ECHOPAIR("Attempted to travel: ", travelDistance);
SERIAL_ECHOLNPGM("mm.");
diff --git a/Marlin/Marlin.h b/Marlin/Marlin.h
index 40b101fb4f..efb73d172a 100644
--- a/Marlin/Marlin.h
+++ b/Marlin/Marlin.h
@@ -448,10 +448,6 @@ void report_current_position();
filament_change_load_length[EXTRUDERS];
#endif
-#if ENABLED(PID_EXTRUSION_SCALING)
- extern int lpq_len;
-#endif
-
#if HAS_POWER_SWITCH
extern bool powersupply_on;
#define PSU_PIN_ON() do{ OUT_WRITE(PS_ON_PIN, PS_ON_AWAKE); powersupply_on = true; }while(0)
@@ -477,6 +473,10 @@ void do_blocking_move_to_x(const float &rx, const float &fr_mm_s=0.0);
void do_blocking_move_to_z(const float &rz, const float &fr_mm_s=0.0);
void do_blocking_move_to_xy(const float &rx, const float &ry, const float &fr_mm_s=0.0);
+#if ENABLED(ARC_SUPPORT)
+ void plan_arc(const float(&cart)[XYZE], const float(&offset)[2], const bool clockwise);
+#endif
+
#define HAS_AXIS_UNHOMED_ERR ( \
ENABLED(Z_PROBE_ALLEN_KEY) \
|| ENABLED(Z_PROBE_SLED) \
@@ -522,7 +522,7 @@ void do_blocking_move_to_xy(const float &rx, const float &ry, const float &fr_mm
// Note: This won't work on SCARA since the probe offset rotates with the arm.
inline bool position_is_reachable_by_probe(const float &rx, const float &ry) {
return position_is_reachable(rx - (X_PROBE_OFFSET_FROM_EXTRUDER), ry - (Y_PROBE_OFFSET_FROM_EXTRUDER))
- && position_is_reachable(rx, ry, FABS(MIN_PROBE_EDGE));
+ && position_is_reachable(rx, ry, ABS(MIN_PROBE_EDGE));
}
#endif
diff --git a/Marlin/MarlinConfig.h b/Marlin/MarlinConfig.h
index 5f77dba0e4..f0aa130443 100644
--- a/Marlin/MarlinConfig.h
+++ b/Marlin/MarlinConfig.h
@@ -23,21 +23,25 @@
#ifndef MARLIN_CONFIG_H
#define MARLIN_CONFIG_H
-#include "fastio.h"
-#include "macros.h"
#include "boards.h"
+#include "macros.h"
#include "Version.h"
#include "Configuration.h"
#include "Conditionals_LCD.h"
#include "Configuration_adv.h"
-#include "pins.h"
-#if defined(__AVR__) && !defined(USBCON)
+
+#if USE_MARLINSERIAL
#define HardwareSerial_h // trick to disable the standard HWserial
#endif
-#include "Arduino.h"
+
+#include "types.h"
+#include "HAL.h"
+#include "pins.h"
#include "Conditionals_post.h"
#include "SanityCheck.h"
-
-#include
+#include "enum.h"
+#include "language.h"
+#include "utility.h"
+#include "serial.h"
#endif // MARLIN_CONFIG_H
diff --git a/Marlin/MarlinSerial.cpp b/Marlin/MarlinSerial.cpp
index cd4dd03ade..83bc49edc4 100644
--- a/Marlin/MarlinSerial.cpp
+++ b/Marlin/MarlinSerial.cpp
@@ -34,7 +34,7 @@
#include "MarlinConfig.h"
-#if !(defined(__AVR__) && defined(USBCON)) && (defined(UBRRH) || defined(UBRR0H) || defined(UBRR1H) || defined(UBRR2H) || defined(UBRR3H))
+#if USE_MARLINSERIAL && (defined(UBRRH) || defined(UBRR0H) || defined(UBRR1H) || defined(UBRR2H) || defined(UBRR3H))
#include "MarlinSerial.h"
#include "Marlin.h"
@@ -561,9 +561,9 @@
// Preinstantiate
MarlinSerial customizedSerial;
-#endif // !(__AVR__ && USBCON) && (UBRRH || UBRR0H || UBRR1H || UBRR2H || UBRR3H)
+#endif // USE_MARLINSERIAL && (UBRRH || UBRR0H || UBRR1H || UBRR2H || UBRR3H)
// For AT90USB targets use the UART for BT interfacing
-#if defined(__AVR__) && defined(USBCON) && ENABLED(BLUETOOTH)
+#if !USE_MARLINSERIAL && ENABLED(BLUETOOTH)
HardwareSerial bluetoothSerial;
#endif
diff --git a/Marlin/MarlinSerial.h b/Marlin/MarlinSerial.h
index 9060f668ad..96071f800c 100644
--- a/Marlin/MarlinSerial.h
+++ b/Marlin/MarlinSerial.h
@@ -85,7 +85,7 @@
#define TX_BUFFER_SIZE 32
#endif
-#if !(defined(__AVR__) && defined(USBCON))
+#if USE_MARLINSERIAL
#if RX_BUFFER_SIZE > 256
typedef uint16_t ring_buffer_pos_t;
@@ -159,10 +159,10 @@
extern MarlinSerial customizedSerial;
-#endif // !(__AVR__ && USBCON)
+#endif // USE_MARLINSERIAL
// Use the UART for Bluetooth in AT90USB configurations
-#if defined(__AVR__) && defined(USBCON) && ENABLED(BLUETOOTH)
+#if !USE_MARLINSERIAL && ENABLED(BLUETOOTH)
extern HardwareSerial bluetoothSerial;
#endif
diff --git a/Marlin/Marlin_main.cpp b/Marlin/Marlin_main.cpp
index 2cd3950648..0511a6b863 100644
--- a/Marlin/Marlin_main.cpp
+++ b/Marlin/Marlin_main.cpp
@@ -336,10 +336,6 @@
#include "I2CPositionEncoder.h"
#endif
-#if ENABLED(ENDSTOP_INTERRUPTS_FEATURE)
- #include "endstop_interrupts.h"
-#endif
-
#if ENABLED(M100_FREE_MEMORY_WATCHER)
void gcode_M100();
void M100_dump_routine(const char * const title, const char *start, const char *end);
@@ -537,10 +533,6 @@ static millis_t stepper_inactive_time = (DEFAULT_STEPPER_DEACTIVE_TIME) * 1000UL
#define BUZZ(d,f) NOOP
#endif
-#if ENABLED(SWITCHING_NOZZLE)
- #define DO_SWITCH_EXTRUDER (SWITCHING_EXTRUDER_SERVO_NR != SWITCHING_NOZZLE_SERVO_NR)
-#endif
-
uint8_t target_extruder;
#if HAS_BED_PROBE
@@ -696,10 +688,6 @@ static bool send_ok[BUFSIZE];
bool chdkActive = false;
#endif
-#if ENABLED(PID_EXTRUSION_SCALING)
- int lpq_len = 20;
-#endif
-
#if ENABLED(HOST_KEEPALIVE_FEATURE)
MarlinBusyState busy_state = NOT_BUSY;
static millis_t next_busy_signal_ms = 0;
@@ -751,7 +739,7 @@ void set_current_from_steppers_for_axis(const AxisEnum axis);
#endif
#if ENABLED(BEZIER_CURVE_SUPPORT)
- void plan_cubic_move(const float (&offset)[4]);
+ void plan_cubic_move(const float (&cart)[XYZE], const float (&offset)[4]);
#endif
void tool_change(const uint8_t tmp_extruder, const float fr_mm_s=0.0, bool no_move=false);
@@ -1342,7 +1330,7 @@ bool get_target_extruder_from_command(const uint16_t code) {
if (axis == X_AXIS) {
// In Dual X mode hotend_offset[X] is T1's home position
- float dual_max_x = max(hotend_offset[X_AXIS][1], X2_MAX_POS);
+ float dual_max_x = MAX(hotend_offset[X_AXIS][1], X2_MAX_POS);
if (active_extruder != 0) {
// T1 can move from X2_MIN_POS to X2_MAX_POS or X2 home position (whichever is larger)
@@ -1353,7 +1341,7 @@ bool get_target_extruder_from_command(const uint16_t code) {
// In Duplication Mode, T0 can move as far left as X_MIN_POS
// but not so far to the right that T1 would move past the end
soft_endstop_min[X_AXIS] = base_min_pos(X_AXIS);
- soft_endstop_max[X_AXIS] = min(base_max_pos(X_AXIS), dual_max_x - duplicate_extruder_x_offset);
+ soft_endstop_max[X_AXIS] = MIN(base_max_pos(X_AXIS), dual_max_x - duplicate_extruder_x_offset);
}
else {
// In other modes, T0 can move from X_MIN_POS to X_MAX_POS
@@ -1389,7 +1377,7 @@ bool get_target_extruder_from_command(const uint16_t code) {
case X_AXIS:
case Y_AXIS:
// Get a minimum radius for clamping
- soft_endstop_radius = MIN3(FABS(max(soft_endstop_min[X_AXIS], soft_endstop_min[Y_AXIS])), soft_endstop_max[X_AXIS], soft_endstop_max[Y_AXIS]);
+ soft_endstop_radius = MIN3(ABS(MAX(soft_endstop_min[X_AXIS], soft_endstop_min[Y_AXIS])), soft_endstop_max[X_AXIS], soft_endstop_max[Y_AXIS]);
soft_endstop_radius_2 = sq(soft_endstop_radius);
break;
#endif
@@ -1717,7 +1705,7 @@ void do_blocking_move_to(const float rx, const float ry, const float rz, const f
#endif
- stepper.synchronize();
+ planner.synchronize();
feedrate_mm_s = old_feedrate_mm_s;
@@ -2128,7 +2116,7 @@ void clean_up_after_endstop_or_probe_move() {
#endif
if (deploy_stow_condition && unknown_condition)
- do_probe_raise(max(Z_CLEARANCE_BETWEEN_PROBES, Z_CLEARANCE_DEPLOY_PROBE));
+ do_probe_raise(MAX(Z_CLEARANCE_BETWEEN_PROBES, Z_CLEARANCE_DEPLOY_PROBE));
#if ENABLED(Z_PROBE_SLED) || ENABLED(Z_PROBE_ALLEN_KEY)
#if ENABLED(Z_PROBE_SLED)
@@ -2236,7 +2224,7 @@ void clean_up_after_endstop_or_probe_move() {
do_blocking_move_to_z(z, fr_mm_s);
// Check to see if the probe was triggered
- const bool probe_triggered = TEST(Endstops::endstop_hit_bits,
+ const bool probe_triggered = TEST(endstops.trigger_state(),
#if ENABLED(Z_MIN_PROBE_USES_Z_MIN_ENDSTOP_PIN)
Z_MIN
#else
@@ -2393,7 +2381,7 @@ void clean_up_after_endstop_or_probe_move() {
const float nz =
#if ENABLED(DELTA)
// Move below clip height or xy move will be aborted by do_blocking_move_to
- min(current_position[Z_AXIS], delta_clip_start_height)
+ MIN(current_position[Z_AXIS], delta_clip_start_height)
#else
current_position[Z_AXIS]
#endif
@@ -2452,7 +2440,7 @@ void clean_up_after_endstop_or_probe_move() {
#elif ENABLED(AUTO_BED_LEVELING_BILINEAR)
!!bilinear_grid_spacing[X_AXIS]
#elif ENABLED(AUTO_BED_LEVELING_UBL)
- true
+ ubl.mesh_is_valid()
#else // 3POINT, LINEAR
true
#endif
@@ -2988,7 +2976,7 @@ static void do_homing_move(const AxisEnum axis, const float distance, const floa
planner.buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], fr_mm_s ? fr_mm_s : homing_feedrate(axis), active_extruder);
#endif
- stepper.synchronize();
+ planner.synchronize();
if (is_home_dir) {
@@ -3031,7 +3019,7 @@ static void do_homing_move(const AxisEnum axis, const float distance, const floa
* before updating the current position.
*/
-#define HOMEAXIS(LETTER) homeaxis(LETTER##_AXIS)
+#define HOMEAXIS(A) homeaxis(_AXIS(A))
static void homeaxis(const AxisEnum axis) {
@@ -3040,7 +3028,7 @@ static void homeaxis(const AxisEnum axis) {
if (axis != Z_AXIS) { BUZZ(100, 880); return; }
#else
#define CAN_HOME(A) \
- (axis == A##_AXIS && ((A##_MIN_PIN > -1 && A##_HOME_DIR < 0) || (A##_MAX_PIN > -1 && A##_HOME_DIR > 0)))
+ (axis == _AXIS(A) && ((A##_MIN_PIN > -1 && A##_HOME_DIR < 0) || (A##_MAX_PIN > -1 && A##_HOME_DIR > 0)))
if (!CAN_HOME(X) && !CAN_HOME(Y) && !CAN_HOME(Z)) return;
#endif
@@ -3083,7 +3071,7 @@ static void homeaxis(const AxisEnum axis) {
// When homing Z with probe respect probe clearance
const float bump = axis_home_dir * (
#if HOMING_Z_WITH_PROBE
- (axis == Z_AXIS && (Z_HOME_BUMP_MM)) ? max(Z_CLEARANCE_BETWEEN_PROBES, Z_HOME_BUMP_MM) :
+ (axis == Z_AXIS && (Z_HOME_BUMP_MM)) ? MAX(Z_CLEARANCE_BETWEEN_PROBES, Z_HOME_BUMP_MM) :
#endif
home_bump_mm(axis)
);
@@ -3115,7 +3103,7 @@ static void homeaxis(const AxisEnum axis) {
#if ENABLED(X_DUAL_ENDSTOPS)
if (axis == X_AXIS) {
const bool lock_x1 = pos_dir ? (endstops.x_endstop_adj > 0) : (endstops.x_endstop_adj < 0);
- const float adj = FABS(endstops.x_endstop_adj);
+ const float adj = ABS(endstops.x_endstop_adj);
if (lock_x1) stepper.set_x_lock(true); else stepper.set_x2_lock(true);
do_homing_move(axis, pos_dir ? -adj : adj);
if (lock_x1) stepper.set_x_lock(false); else stepper.set_x2_lock(false);
@@ -3125,7 +3113,7 @@ static void homeaxis(const AxisEnum axis) {
#if ENABLED(Y_DUAL_ENDSTOPS)
if (axis == Y_AXIS) {
const bool lock_y1 = pos_dir ? (endstops.y_endstop_adj > 0) : (endstops.y_endstop_adj < 0);
- const float adj = FABS(endstops.y_endstop_adj);
+ const float adj = ABS(endstops.y_endstop_adj);
if (lock_y1) stepper.set_y_lock(true); else stepper.set_y2_lock(true);
do_homing_move(axis, pos_dir ? -adj : adj);
if (lock_y1) stepper.set_y_lock(false); else stepper.set_y2_lock(false);
@@ -3135,7 +3123,7 @@ static void homeaxis(const AxisEnum axis) {
#if ENABLED(Z_DUAL_ENDSTOPS)
if (axis == Z_AXIS) {
const bool lock_z1 = pos_dir ? (endstops.z_endstop_adj > 0) : (endstops.z_endstop_adj < 0);
- const float adj = FABS(endstops.z_endstop_adj);
+ const float adj = ABS(endstops.z_endstop_adj);
if (lock_z1) stepper.set_z_lock(true); else stepper.set_z2_lock(true);
do_homing_move(axis, pos_dir ? -adj : adj);
if (lock_z1) stepper.set_z_lock(false); else stepper.set_z2_lock(false);
@@ -3337,7 +3325,7 @@ inline void gcode_G0_G1(
if (fwretract.autoretract_enabled && parser.seen('E') && !(parser.seen('X') || parser.seen('Y') || parser.seen('Z'))) {
const float echange = destination[E_AXIS] - current_position[E_AXIS];
// Is this a retract or prime move?
- if (WITHIN(FABS(echange), MIN_AUTORETRACT, MAX_AUTORETRACT) && fwretract.retracted[active_extruder] == (echange > 0.0)) {
+ if (WITHIN(ABS(echange), MIN_AUTORETRACT, MAX_AUTORETRACT) && fwretract.retracted[active_extruder] == (echange > 0.0)) {
current_position[E_AXIS] = destination[E_AXIS]; // Hide a G1-based retract/prime from calculations
sync_plan_position_e(); // AND from the planner
return fwretract.retract(echange < 0.0); // Firmware-based retract/prime (double-retract ignored)
@@ -3359,7 +3347,7 @@ inline void gcode_G0_G1(
#define _MOVE_SYNC parser.seenval('Z') // Only for Z move
#endif
if (_MOVE_SYNC) {
- stepper.synchronize();
+ planner.synchronize();
SERIAL_ECHOLNPGM(MSG_Z_MOVE_COMP);
}
#endif
@@ -3474,7 +3462,7 @@ inline void gcode_G4() {
if (parser.seenval('P')) dwell_ms = parser.value_millis(); // milliseconds to wait
if (parser.seenval('S')) dwell_ms = parser.value_millis_from_seconds(); // seconds to wait
- stepper.synchronize();
+ planner.synchronize();
#if ENABLED(NANODLP_Z_SYNC)
SERIAL_ECHOLNPGM(MSG_Z_MOVE_COMP);
#endif
@@ -3520,7 +3508,7 @@ inline void gcode_G4() {
parser.linearval('Q')
};
- plan_cubic_move(offset);
+ plan_cubic_move(destination, offset);
}
}
@@ -3703,7 +3691,7 @@ inline void gcode_G4() {
const float mlx = max_length(X_AXIS),
mly = max_length(Y_AXIS),
mlratio = mlx > mly ? mly / mlx : mlx / mly,
- fr_mm_s = min(homing_feedrate(X_AXIS), homing_feedrate(Y_AXIS)) * SQRT(sq(mlratio) + 1.0);
+ fr_mm_s = MIN(homing_feedrate(X_AXIS), homing_feedrate(Y_AXIS)) * SQRT(sq(mlratio) + 1.0);
#if ENABLED(SENSORLESS_HOMING)
sensorless_homing_per_axis(X_AXIS);
@@ -3801,9 +3789,9 @@ inline void gcode_G4() {
#endif
#if ABL_PLANAR
const float diff[XYZ] = {
- stepper.get_axis_position_mm(X_AXIS) - current_position[X_AXIS],
- stepper.get_axis_position_mm(Y_AXIS) - current_position[Y_AXIS],
- stepper.get_axis_position_mm(Z_AXIS) - current_position[Z_AXIS]
+ planner.get_axis_position_mm(X_AXIS) - current_position[X_AXIS],
+ planner.get_axis_position_mm(Y_AXIS) - current_position[Y_AXIS],
+ planner.get_axis_position_mm(Z_AXIS) - current_position[Z_AXIS]
};
SERIAL_ECHOPGM("ABL Adjustment X");
if (diff[X_AXIS] > 0) SERIAL_CHAR('+');
@@ -3845,7 +3833,11 @@ inline void gcode_G4() {
SERIAL_ECHOPGM("Mesh Bed Leveling");
if (planner.leveling_active) {
SERIAL_ECHOLNPGM(" (enabled)");
- SERIAL_ECHOPAIR("MBL Adjustment Z", ftostr43sign(mbl.get_z(current_position[X_AXIS], current_position[Y_AXIS], 1.0), '+'));
+ SERIAL_ECHOPAIR("MBL Adjustment Z", ftostr43sign(mbl.get_z(current_position[X_AXIS], current_position[Y_AXIS]
+ #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
+ , 1.0
+ #endif
+ ), '+'));
#if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
if (planner.z_fade_height) {
SERIAL_ECHOPAIR(" (", ftostr43sign(
@@ -3896,7 +3888,7 @@ inline void gcode_G4() {
current_position[X_AXIS] = current_position[Y_AXIS] = current_position[Z_AXIS] = (delta_height + 10);
feedrate_mm_s = homing_feedrate(X_AXIS);
buffer_line_to_current_position();
- stepper.synchronize();
+ planner.synchronize();
// Re-enable stealthChop if used. Disable diag1 pin on driver.
#if ENABLED(SENSORLESS_HOMING)
@@ -3905,7 +3897,7 @@ inline void gcode_G4() {
// If an endstop was not hit, then damage can occur if homing is continued.
// This can occur if the delta height not set correctly.
- if (!(Endstops::endstop_hit_bits & (_BV(X_MAX) | _BV(Y_MAX) | _BV(Z_MAX)))) {
+ if (!(endstops.trigger_state() & (_BV(X_MAX) | _BV(Y_MAX) | _BV(Z_MAX)))) {
LCD_MESSAGEPGM(MSG_ERR_HOMING_FAILED);
SERIAL_ERROR_START();
SERIAL_ERRORLNPGM(MSG_ERR_HOMING_FAILED);
@@ -4040,7 +4032,7 @@ inline void gcode_G28(const bool always_home_all) {
#endif
// Wait for planner moves to finish!
- stepper.synchronize();
+ planner.synchronize();
// Cancel the active G29 session
#if ENABLED(PROBE_MANUALLY)
@@ -4354,7 +4346,7 @@ void home_all_axes() { gcode_G28(true); }
// One last "return to the bed" (as originally coded) at completion
current_position[Z_AXIS] = MANUAL_PROBE_HEIGHT;
buffer_line_to_current_position();
- stepper.synchronize();
+ planner.synchronize();
// After recording the last point, activate home and activate
mbl_probe_index = -1;
@@ -4369,7 +4361,7 @@ void home_all_axes() { gcode_G28(true); }
current_position[Z_AXIS] = 0;
set_destination_from_current();
buffer_line_to_destination(homing_feedrate(Z_AXIS));
- stepper.synchronize();
+ planner.synchronize();
#endif
#if ENABLED(LCD_BED_LEVELING)
@@ -4427,7 +4419,7 @@ void home_all_axes() { gcode_G28(true); }
} // switch(state)
if (state == MeshNext) {
- SERIAL_PROTOCOLPAIR("MBL G29 point ", min(mbl_probe_index, GRID_MAX_POINTS));
+ SERIAL_PROTOCOLPAIR("MBL G29 point ", MIN(mbl_probe_index, GRID_MAX_POINTS));
SERIAL_PROTOCOLLNPAIR(" of ", int(GRID_MAX_POINTS));
}
@@ -4779,7 +4771,7 @@ void home_all_axes() { gcode_G28(true); }
SERIAL_EOL();
}
- stepper.synchronize();
+ planner.synchronize();
// Disable auto bed leveling during G29.
// Be formal so G29 can be done successively without G28.
@@ -4859,7 +4851,7 @@ void home_all_axes() { gcode_G28(true); }
if (verbose_level || seenQ) {
SERIAL_PROTOCOLPGM("Manual G29 ");
if (g29_in_progress) {
- SERIAL_PROTOCOLPAIR("point ", min(abl_probe_index + 1, abl_points));
+ SERIAL_PROTOCOLPAIR("point ", MIN(abl_probe_index + 1, abl_points));
SERIAL_PROTOCOLLNPAIR(" of ", abl_points);
}
else
@@ -5337,7 +5329,7 @@ void home_all_axes() { gcode_G28(true); }
#if ENABLED(DEBUG_LEVELING_FEATURE)
if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPAIR("Z Probe End Script: ", Z_PROBE_END_SCRIPT);
#endif
- stepper.synchronize();
+ planner.synchronize();
enqueue_and_echo_commands_P(PSTR(Z_PROBE_END_SCRIPT));
#endif
@@ -5375,7 +5367,7 @@ void home_all_axes() { gcode_G28(true); }
*
* X Probe X position (default current X)
* Y Probe Y position (default current Y)
- * E Engage the probe for each probe
+ * E Engage the probe for each probe (default 1)
*/
inline void gcode_G30() {
const float xpos = parser.linearval('X', current_position[X_AXIS] + X_PROBE_OFFSET_FROM_EXTRUDER),
@@ -5390,7 +5382,7 @@ void home_all_axes() { gcode_G28(true); }
setup_for_endstop_or_probe_move();
- const ProbePtRaise raise_after = parser.boolval('E') ? PROBE_PT_STOW : PROBE_PT_NONE;
+ const ProbePtRaise raise_after = parser.boolval('E', true) ? PROBE_PT_STOW : PROBE_PT_NONE;
const float measured_z = probe_pt(xpos, ypos, raise_after, parser.intval('V', 1));
if (!isnan(measured_z)) {
@@ -5468,7 +5460,7 @@ void home_all_axes() { gcode_G28(true); }
tool_change(0, 0, true);
#endif
- stepper.synchronize();
+ planner.synchronize();
setup_for_endstop_or_probe_move();
#if HAS_LEVELING
@@ -5902,15 +5894,12 @@ void home_all_axes() { gcode_G28(true); }
}
// Report settings
-
const char *checkingac = PSTR("Checking... AC");
serialprintPGM(checkingac);
if (verbose_level == 0) SERIAL_PROTOCOLPGM(" (DRY-RUN)");
if (set_up) SERIAL_PROTOCOLPGM(" (SET-UP)");
SERIAL_EOL();
- char mess[11];
- strcpy_P(mess, checkingac);
- lcd_setstatus(mess);
+ lcd_setstatusPGM(checkingac);
print_calibration_settings(_endstop_results, _angle_results);
@@ -6130,17 +6119,17 @@ void home_all_axes() { gcode_G28(true); }
float retract_mm[XYZ];
LOOP_XYZ(i) {
float dist = destination[i] - current_position[i];
- retract_mm[i] = FABS(dist) < G38_MINIMUM_MOVE ? 0 : home_bump_mm((AxisEnum)i) * (dist > 0 ? -1 : 1);
+ retract_mm[i] = ABS(dist) < G38_MINIMUM_MOVE ? 0 : home_bump_mm((AxisEnum)i) * (dist > 0 ? -1 : 1);
}
#endif
// Move until destination reached or target hit
- stepper.synchronize();
+ planner.synchronize();
endstops.enable(true);
G38_move = true;
G38_endstop_hit = false;
prepare_move_to_destination();
- stepper.synchronize();
+ planner.synchronize();
G38_move = false;
endstops.hit_on_purpose();
@@ -6163,11 +6152,11 @@ void home_all_axes() { gcode_G28(true); }
// Bump the target more slowly
LOOP_XYZ(i) destination[i] -= retract_mm[i] * 2;
- stepper.synchronize();
+ planner.synchronize();
endstops.enable(true);
G38_move = true;
prepare_move_to_destination();
- stepper.synchronize();
+ planner.synchronize();
G38_move = false;
set_current_from_steppers_for_axis(ALL_AXES);
@@ -6194,7 +6183,7 @@ void home_all_axes() { gcode_G28(true); }
// If any axis has enough movement, do the move
LOOP_XYZ(i)
- if (FABS(destination[i] - current_position[i]) >= G38_MINIMUM_MOVE) {
+ if (ABS(destination[i] - current_position[i]) >= G38_MINIMUM_MOVE) {
if (!parser.seenval('F')) feedrate_mm_s = homing_feedrate((AxisEnum)i);
// If G38.2 fails throw an error
if (!G38_run_probe() && is_38_2) {
@@ -6345,7 +6334,7 @@ inline void gcode_G92() {
const bool has_message = !hasP && !hasS && args && *args;
- stepper.synchronize();
+ planner.synchronize();
#if ENABLED(ULTIPANEL)
@@ -6446,7 +6435,7 @@ inline void gcode_G92() {
inline void gcode_M3_M4(bool is_M3) {
- stepper.synchronize(); // wait until previous movement commands (G0/G0/G2/G3) have completed before playing with the spindle
+ planner.synchronize(); // wait until previous movement commands (G0/G0/G2/G3) have completed before playing with the spindle
#if SPINDLE_DIR_CHANGE
const bool rotation_dir = (is_M3 && !SPINDLE_INVERT_DIR || !is_M3 && SPINDLE_INVERT_DIR) ? HIGH : LOW;
if (SPINDLE_STOP_ON_DIR_CHANGE \
@@ -6496,7 +6485,7 @@ inline void gcode_G92() {
* M5 turn off spindle
*/
inline void gcode_M5() {
- stepper.synchronize();
+ planner.synchronize();
WRITE(SPINDLE_LASER_ENABLE_PIN, !SPINDLE_LASER_ENABLE_INVERT);
#if ENABLED(SPINDLE_LASER_PWM)
analogWrite(SPINDLE_LASER_PWM_PIN, SPINDLE_LASER_PWM_INVERT ? 255 : 0);
@@ -6511,7 +6500,7 @@ inline void gcode_G92() {
* M3, M4: Laser On
*/
inline void gcode_M3_M4(bool is_M3) {
- stepper.synchronize();
+ planner.synchronize();
fanSpeeds[FAN_NUM_AS_LASER] = parser.byteval('S', 255);
}
@@ -6519,7 +6508,7 @@ inline void gcode_G92() {
* M5: Laser Off
*/
inline void gcode_M5() {
- stepper.synchronize();
+ planner.synchronize();
fanSpeeds[FAN_NUM_AS_LASER] = 0;
}
#endif
@@ -6539,7 +6528,7 @@ inline void gcode_M17() {
destination[E_AXIS] += length / planner.e_factor[active_extruder];
planner.buffer_line_kinematic(destination, fr, active_extruder);
set_current_from_destination();
- stepper.synchronize();
+ planner.synchronize();
}
static float resume_position[XYZE];
@@ -6831,7 +6820,7 @@ inline void gcode_M17() {
COPY(resume_position, current_position);
// Wait for synchronize steppers
- stepper.synchronize();
+ planner.synchronize();
// Initial retract before move to filament change position
if (retract && thermalManager.hotEnoughToExtrude(active_extruder))
@@ -7163,7 +7152,7 @@ inline void gcode_M31() {
*
*/
inline void gcode_M32() {
- if (card.sdprinting) stepper.synchronize();
+ if (card.sdprinting) planner.synchronize();
if (card.cardOK) {
const bool call_procedure = parser.boolval('P');
@@ -7654,7 +7643,7 @@ inline void gcode_M42() {
0.1250000000 * (DELTA_PRINTABLE_RADIUS),
0.3333333333 * (DELTA_PRINTABLE_RADIUS)
#else
- 5.0, 0.125 * min(X_BED_SIZE, Y_BED_SIZE)
+ 5.0, 0.125 * MIN(X_BED_SIZE, Y_BED_SIZE)
#endif
);
@@ -7890,18 +7879,9 @@ inline void gcode_M104() {
*/
if (parser.value_celsius() <= (EXTRUDE_MINTEMP) / 2) {
print_job_timer.stop();
- LCD_MESSAGEPGM(WELCOME_MSG);
+ lcd_reset_status();
}
#endif
-
- #if ENABLED(ULTRA_LCD)
- if (parser.value_celsius() > thermalManager.degHotend(target_extruder))
- #if HOTENDS > 1
- lcd_status_printf_P(0, PSTR("E%i " MSG_HEATING), target_extruder + 1);
- #else
- LCD_MESSAGEPGM("E " MSG_HEATING);
- #endif
- #endif
}
#if ENABLED(AUTOTEMP)
@@ -7973,14 +7953,14 @@ inline void gcode_M105() {
fanSpeeds[p] = new_fanSpeeds[p];
break;
default:
- new_fanSpeeds[p] = min(t, 255);
+ new_fanSpeeds[p] = MIN(t, 255);
break;
}
return;
}
#endif // EXTRA_FAN_SPEED
const uint16_t s = parser.ushortval('S', 255);
- fanSpeeds[p] = min(s, 255);
+ fanSpeeds[p] = MIN(s, 255U);
}
}
@@ -8060,7 +8040,7 @@ inline void gcode_M109() {
*/
if (parser.value_celsius() <= (EXTRUDE_MINTEMP) / 2) {
print_job_timer.stop();
- LCD_MESSAGEPGM(WELCOME_MSG);
+ lcd_reset_status();
}
else
print_job_timer.start();
@@ -8152,7 +8132,7 @@ inline void gcode_M109() {
#if TEMP_RESIDENCY_TIME > 0
- const float temp_diff = FABS(target_temp - temp);
+ const float temp_diff = ABS(target_temp - temp);
if (!residency_start_ms) {
// Start the TEMP_RESIDENCY_TIME timer when we reach target temp for the first time.
@@ -8298,7 +8278,7 @@ inline void gcode_M109() {
#if TEMP_BED_RESIDENCY_TIME > 0
- const float temp_diff = FABS(target_temp - temp);
+ const float temp_diff = ABS(target_temp - temp);
if (!residency_start_ms) {
// Start the TEMP_BED_RESIDENCY_TIME timer when we reach target temp for the first time.
@@ -8504,7 +8484,7 @@ inline void gcode_M111() {
#endif
#if ENABLED(ULTIPANEL)
- LCD_MESSAGEPGM(WELCOME_MSG);
+ lcd_reset_status();
#endif
}
@@ -8517,7 +8497,7 @@ inline void gcode_M111() {
*/
inline void gcode_M81() {
thermalManager.disable_all_heaters();
- stepper.finish_and_disable();
+ planner.finish_and_disable();
#if FAN_COUNT > 0
for (uint8_t i = 0; i < FAN_COUNT; i++) fanSpeeds[i] = 0;
@@ -8560,10 +8540,10 @@ inline void gcode_M18_M84() {
else {
bool all_axis = !(parser.seen('X') || parser.seen('Y') || parser.seen('Z') || parser.seen('E'));
if (all_axis) {
- stepper.finish_and_disable();
+ planner.finish_and_disable();
}
else {
- stepper.synchronize();
+ planner.synchronize();
if (parser.seen('X')) disable_X();
if (parser.seen('Y')) disable_Y();
if (parser.seen('Z')) disable_Z();
@@ -8642,8 +8622,8 @@ void report_current_position() {
stepper.report_positions();
#if IS_SCARA
- SERIAL_PROTOCOLPAIR("SCARA Theta:", stepper.get_axis_position_degrees(A_AXIS));
- SERIAL_PROTOCOLLNPAIR(" Psi+Theta:", stepper.get_axis_position_degrees(B_AXIS));
+ SERIAL_PROTOCOLPAIR("SCARA Theta:", planner.get_axis_position_degrees(A_AXIS));
+ SERIAL_PROTOCOLLNPAIR(" Psi+Theta:", planner.get_axis_position_degrees(B_AXIS));
SERIAL_EOL();
#endif
}
@@ -8699,7 +8679,7 @@ void report_current_position() {
report_xyz(delta);
#endif
- stepper.synchronize();
+ planner.synchronize();
SERIAL_PROTOCOLPGM("Stepper:");
LOOP_XYZE(i) {
@@ -8712,8 +8692,8 @@ void report_current_position() {
#if IS_SCARA
const float deg[XYZ] = {
- stepper.get_axis_position_degrees(A_AXIS),
- stepper.get_axis_position_degrees(B_AXIS)
+ planner.get_axis_position_degrees(A_AXIS),
+ planner.get_axis_position_degrees(B_AXIS)
};
SERIAL_PROTOCOLPGM("Degrees:");
report_xyze(deg, 2);
@@ -8721,7 +8701,7 @@ void report_current_position() {
SERIAL_PROTOCOLPGM("FromStp:");
get_cartesian_from_steppers(); // writes cartes[XYZ] (with forward kinematics)
- const float from_steppers[XYZE] = { cartes[X_AXIS], cartes[Y_AXIS], cartes[Z_AXIS], stepper.get_axis_position_mm(E_AXIS) };
+ const float from_steppers[XYZE] = { cartes[X_AXIS], cartes[Y_AXIS], cartes[Z_AXIS], planner.get_axis_position_mm(E_AXIS) };
report_xyze(from_steppers);
const float diff[XYZE] = {
@@ -8747,7 +8727,7 @@ inline void gcode_M114() {
}
#endif
- stepper.synchronize();
+ planner.synchronize();
report_current_position();
}
@@ -8887,7 +8867,7 @@ inline void gcode_M117() {
/**
* M118: Display a message in the host console.
*
- * A1 Append '// ' for an action command, as in OctoPrint
+ * A1 Prepend '// ' for an action command, as in OctoPrint
* E1 Have the host 'echo:' the text
*/
inline void gcode_M118() {
@@ -8940,7 +8920,7 @@ inline void gcode_M121() { endstops.enable_globally(false); }
inline void gcode_M125() {
// Initial retract before move to filament change position
- const float retract = -FABS(parser.seen('L') ? parser.value_axis_units(E_AXIS) : 0
+ const float retract = -ABS(parser.seen('L') ? parser.value_axis_units(E_AXIS) : 0
#ifdef PAUSE_PARK_RETRACT_LENGTH
+ (PAUSE_PARK_RETRACT_LENGTH)
#endif
@@ -9375,7 +9355,7 @@ inline void gcode_M211() {
* T
* X
* Y
- * Z - Available with DUAL_X_CARRIAGE and SWITCHING_NOZZLE
+ * Z - Available with DUAL_X_CARRIAGE, SWITCHING_NOZZLE, and PARKING_EXTRUDER
*/
inline void gcode_M218() {
if (get_target_extruder_from_command(218) || target_extruder == 0) return;
@@ -9390,7 +9370,7 @@ inline void gcode_M211() {
report = false;
}
- #if ENABLED(DUAL_X_CARRIAGE) || ENABLED(SWITCHING_NOZZLE) || ENABLED(PARKING_EXTRUDER)
+ #if HAS_HOTEND_OFFSET_Z
if (parser.seenval('Z')) {
hotend_offset[Z_AXIS][target_extruder] = parser.value_linear_units();
report = false;
@@ -9405,7 +9385,7 @@ inline void gcode_M211() {
SERIAL_ECHO(hotend_offset[X_AXIS][e]);
SERIAL_CHAR(',');
SERIAL_ECHO(hotend_offset[Y_AXIS][e]);
- #if ENABLED(DUAL_X_CARRIAGE) || ENABLED(SWITCHING_NOZZLE) || ENABLED(PARKING_EXTRUDER)
+ #if HAS_HOTEND_OFFSET_Z
SERIAL_CHAR(',');
SERIAL_ECHO(hotend_offset[Z_AXIS][e]);
#endif
@@ -9459,7 +9439,7 @@ inline void gcode_M226() {
int target = LOW;
- stepper.synchronize();
+ planner.synchronize();
pinMode(pin, INPUT);
switch (pin_state) {
@@ -9624,7 +9604,7 @@ inline void gcode_M226() {
* With PID_EXTRUSION_SCALING:
*
* C[float] Kc term
- * L[float] LPQ length
+ * L[int] LPQ length
*/
inline void gcode_M301() {
@@ -9638,8 +9618,9 @@ inline void gcode_M226() {
if (parser.seen('D')) PID_PARAM(Kd, e) = scalePID_d(parser.value_float());
#if ENABLED(PID_EXTRUSION_SCALING)
if (parser.seen('C')) PID_PARAM(Kc, e) = parser.value_float();
- if (parser.seen('L')) lpq_len = parser.value_float();
- NOMORE(lpq_len, LPQ_MAX_LEN);
+ if (parser.seen('L')) thermalManager.lpq_len = parser.value_float();
+ NOMORE(thermalManager.lpq_len, LPQ_MAX_LEN);
+ NOLESS(thermalManager.lpq_len, 0);
#endif
thermalManager.updatePID();
@@ -9922,7 +9903,7 @@ inline void gcode_M303() {
/**
* M400: Finish all moves
*/
-inline void gcode_M400() { stepper.synchronize(); }
+inline void gcode_M400() { planner.synchronize(); }
#if HAS_BED_PROBE
@@ -10005,8 +9986,8 @@ inline void gcode_M400() { stepper.synchronize(); }
#endif // FILAMENT_WIDTH_SENSOR
void quickstop_stepper() {
- stepper.quick_stop();
- stepper.synchronize();
+ planner.quick_stop();
+ planner.synchronize();
set_current_from_steppers_for_axis(ALL_AXES);
SYNC_PLAN_POSITION_KINEMATIC();
}
@@ -10077,8 +10058,9 @@ void quickstop_stepper() {
// L or V display the map info
if (parser.seen('L') || parser.seen('V')) {
ubl.display_map(parser.byteval('T'));
- SERIAL_ECHOLNPAIR("ubl.mesh_is_valid = ", ubl.mesh_is_valid());
- SERIAL_ECHOLNPAIR("ubl.storage_slot = ", ubl.storage_slot);
+ SERIAL_ECHOPGM("Mesh is ");
+ if (!ubl.mesh_is_valid()) SERIAL_ECHOPGM("in");
+ SERIAL_ECHOLNPAIR("valid\nStorage slot: ", ubl.storage_slot);
}
#endif // AUTO_BED_LEVELING_UBL
@@ -10383,7 +10365,7 @@ inline void gcode_M502() {
* M540: Set whether SD card print should abort on endstop hit (M540 S<0|1>)
*/
inline void gcode_M540() {
- if (parser.seen('S')) stepper.abort_on_endstop_hit = parser.value_bool();
+ if (parser.seen('S')) planner.abort_on_endstop_hit = parser.value_bool();
}
#endif // ABORT_ON_ENDSTOP_HIT_FEATURE_ENABLED
@@ -10529,7 +10511,7 @@ inline void gcode_M502() {
#endif
// Initial retract before move to filament change position
- const float retract = -FABS(parser.seen('E') ? parser.value_axis_units(E_AXIS) : 0
+ const float retract = -ABS(parser.seen('E') ? parser.value_axis_units(E_AXIS) : 0
#ifdef PAUSE_PARK_RETRACT_LENGTH
+ (PAUSE_PARK_RETRACT_LENGTH)
#endif
@@ -10548,14 +10530,14 @@ inline void gcode_M502() {
#endif
// Unload filament
- const float unload_length = -FABS(parser.seen('U') ? parser.value_axis_units(E_AXIS) :
+ const float unload_length = -ABS(parser.seen('U') ? parser.value_axis_units(E_AXIS) :
filament_change_unload_length[active_extruder]);
// Slow load filament
constexpr float slow_load_length = FILAMENT_CHANGE_SLOW_LOAD_LENGTH;
// Fast load filament
- const float fast_load_length = FABS(parser.seen('L') ? parser.value_axis_units(E_AXIS) :
+ const float fast_load_length = ABS(parser.seen('L') ? parser.value_axis_units(E_AXIS) :
filament_change_load_length[active_extruder]);
const int beep_count = parser.intval('B',
@@ -10597,7 +10579,7 @@ inline void gcode_M502() {
// Unload length
if (parser.seen('U')) {
- filament_change_unload_length[target_extruder] = FABS(parser.value_axis_units(E_AXIS));
+ filament_change_unload_length[target_extruder] = ABS(parser.value_axis_units(E_AXIS));
#if ENABLED(PREVENT_LENGTHY_EXTRUDE)
NOMORE(filament_change_unload_length[target_extruder], EXTRUDE_MAXLENGTH);
#endif
@@ -10605,7 +10587,7 @@ inline void gcode_M502() {
// Load length
if (parser.seen('L')) {
- filament_change_load_length[target_extruder] = FABS(parser.value_axis_units(E_AXIS));
+ filament_change_load_length[target_extruder] = ABS(parser.value_axis_units(E_AXIS));
#if ENABLED(PREVENT_LENGTHY_EXTRUDE)
NOMORE(filament_change_load_length[target_extruder], EXTRUDE_MAXLENGTH);
#endif
@@ -10617,7 +10599,7 @@ inline void gcode_M502() {
#if ENABLED(MK2_MULTIPLEXER)
inline void select_multiplexed_stepper(const uint8_t e) {
- stepper.synchronize();
+ planner.synchronize();
disable_e_steppers();
WRITE(E_MUX0_PIN, TEST(e, 0) ? HIGH : LOW);
WRITE(E_MUX1_PIN, TEST(e, 1) ? HIGH : LOW);
@@ -10642,14 +10624,14 @@ inline void gcode_M502() {
* Note: the X axis should be homed after changing dual x-carriage mode.
*/
inline void gcode_M605() {
- stepper.synchronize();
+ planner.synchronize();
if (parser.seen('S')) dual_x_carriage_mode = (DualXMode)parser.value_byte();
switch (dual_x_carriage_mode) {
case DXC_FULL_CONTROL_MODE:
case DXC_AUTO_PARK_MODE:
break;
case DXC_DUPLICATION_MODE:
- if (parser.seen('X')) duplicate_extruder_x_offset = max(parser.value_linear_units(), X2_MIN_POS - x_home_pos(0));
+ if (parser.seen('X')) duplicate_extruder_x_offset = MAX(parser.value_linear_units(), X2_MIN_POS - x_home_pos(0));
if (parser.seen('R')) duplicate_extruder_temp_offset = parser.value_celsius_diff();
SERIAL_ECHO_START();
SERIAL_ECHOPGM(MSG_HOTEND_OFFSET);
@@ -10674,7 +10656,7 @@ inline void gcode_M502() {
#elif ENABLED(DUAL_NOZZLE_DUPLICATION_MODE)
inline void gcode_M605() {
- stepper.synchronize();
+ planner.synchronize();
extruder_duplication_enabled = parser.intval('S') == (int)DXC_DUPLICATION_MODE;
SERIAL_ECHO_START();
SERIAL_ECHOLNPAIR(MSG_DUPLICATION_MODE, extruder_duplication_enabled ? MSG_ON : MSG_OFF);
@@ -10720,16 +10702,16 @@ inline void gcode_M502() {
// Lift Z axis
if (park_point.z > 0)
- do_blocking_move_to_z(min(current_position[Z_AXIS] + park_point.z, Z_MAX_POS), NOZZLE_PARK_Z_FEEDRATE);
+ do_blocking_move_to_z(MIN(current_position[Z_AXIS] + park_point.z, Z_MAX_POS), NOZZLE_PARK_Z_FEEDRATE);
constexpr float slow_load_length = FILAMENT_CHANGE_SLOW_LOAD_LENGTH;
- const float fast_load_length = FABS(parser.seen('L') ? parser.value_axis_units(E_AXIS) : filament_change_load_length[active_extruder]);
+ const float fast_load_length = ABS(parser.seen('L') ? parser.value_axis_units(E_AXIS) : filament_change_load_length[active_extruder]);
load_filament(slow_load_length, fast_load_length, ADVANCED_PAUSE_PURGE_LENGTH, FILAMENT_CHANGE_ALERT_BEEPS,
true, thermalManager.wait_for_heating(target_extruder), ADVANCED_PAUSE_MODE_LOAD_FILAMENT);
// Restore Z axis
if (park_point.z > 0)
- do_blocking_move_to_z(max(current_position[Z_AXIS] - park_point.z, 0), NOZZLE_PARK_Z_FEEDRATE);
+ do_blocking_move_to_z(MAX(current_position[Z_AXIS] - park_point.z, 0), NOZZLE_PARK_Z_FEEDRATE);
#if EXTRUDERS > 1
// Restore toolhead if it was changed
@@ -10780,7 +10762,7 @@ inline void gcode_M502() {
// Lift Z axis
if (park_point.z > 0)
- do_blocking_move_to_z(min(current_position[Z_AXIS] + park_point.z, Z_MAX_POS), NOZZLE_PARK_Z_FEEDRATE);
+ do_blocking_move_to_z(MIN(current_position[Z_AXIS] + park_point.z, Z_MAX_POS), NOZZLE_PARK_Z_FEEDRATE);
// Unload filament
#if EXTRUDERS > 1 && ENABLED(FILAMENT_UNLOAD_ALL_EXTRUDERS)
@@ -10794,7 +10776,7 @@ inline void gcode_M502() {
#endif
{
// Unload length
- const float unload_length = -FABS(parser.seen('U') ? parser.value_axis_units(E_AXIS) :
+ const float unload_length = -ABS(parser.seen('U') ? parser.value_axis_units(E_AXIS) :
filament_change_unload_length[target_extruder]);
unload_filament(unload_length, true, ADVANCED_PAUSE_MODE_UNLOAD_FILAMENT);
@@ -10802,7 +10784,7 @@ inline void gcode_M502() {
// Restore Z axis
if (park_point.z > 0)
- do_blocking_move_to_z(max(current_position[Z_AXIS] - park_point.z, 0), NOZZLE_PARK_Z_FEEDRATE);
+ do_blocking_move_to_z(MAX(current_position[Z_AXIS] - park_point.z, 0), NOZZLE_PARK_Z_FEEDRATE);
#if EXTRUDERS > 1
// Restore toolhead if it was changed
@@ -10828,7 +10810,7 @@ inline void gcode_M502() {
if (parser.seenval('K')) {
const float newK = parser.floatval('K');
if (WITHIN(newK, 0, 10)) {
- stepper.synchronize();
+ planner.synchronize();
planner.extruder_advance_K = newK;
}
else
@@ -10857,7 +10839,7 @@ inline void gcode_M502() {
*/
inline void gcode_M906() {
#define TMC_SAY_CURRENT(Q) tmc_get_current(stepper##Q, TMC_##Q)
- #define TMC_SET_CURRENT(Q) tmc_set_current(stepper##Q, TMC_##Q, value)
+ #define TMC_SET_CURRENT(Q) tmc_set_current(stepper##Q, value)
bool report = true;
const uint8_t index = parser.byteval('I');
@@ -10956,48 +10938,119 @@ inline void gcode_M502() {
}
}
+ #define M91x_USE(A) (ENABLED(A##_IS_TMC2130) || (ENABLED(A##_IS_TMC2208) && PIN_EXISTS(A##_SERIAL_RX)))
+ #define M91x_USE_E(N) (E_STEPPERS > N && M91x_USE(E##N))
+ #define M91x_USE_X (ENABLED(IS_TRAMS) || M91x_USE(X))
+ #define M91x_USE_Y (ENABLED(IS_TRAMS) || M91x_USE(Y))
+ #define M91x_USE_Z (ENABLED(IS_TRAMS) || M91x_USE(Z))
+ #define M91x_USE_E0 (ENABLED(IS_TRAMS) || M91x_USE_E(0))
+
/**
* M911: Report TMC stepper driver overtemperature pre-warn flag
- * The flag is held by the library and persist until manually cleared by M912
+ * This flag is held by the library, persisting until cleared by M912
*/
inline void gcode_M911() {
- #if ENABLED(X_IS_TMC2130) || (ENABLED(X_IS_TMC2208) && PIN_EXISTS(X_SERIAL_RX)) || ENABLED(IS_TRAMS)
+ #if M91x_USE_X
tmc_report_otpw(stepperX, TMC_X);
#endif
- #if ENABLED(Y_IS_TMC2130) || (ENABLED(Y_IS_TMC2208) && PIN_EXISTS(Y_SERIAL_RX)) || ENABLED(IS_TRAMS)
+ #if M91x_USE(X2)
+ tmc_report_otpw(stepperX2, TMC_X2);
+ #endif
+ #if M91x_USE_Y
tmc_report_otpw(stepperY, TMC_Y);
#endif
- #if ENABLED(Z_IS_TMC2130) || (ENABLED(Z_IS_TMC2208) && PIN_EXISTS(Z_SERIAL_RX)) || ENABLED(IS_TRAMS)
+ #if M91x_USE(Y2)
+ tmc_report_otpw(stepperY2, TMC_Y2);
+ #endif
+ #if M91x_USE_Z
tmc_report_otpw(stepperZ, TMC_Z);
#endif
- #if ENABLED(E0_IS_TMC2130) || (ENABLED(E0_IS_TMC2208) && PIN_EXISTS(E0_SERIAL_RX)) || ENABLED(IS_TRAMS)
+ #if M91x_USE(Z2)
+ tmc_report_otpw(stepperZ2, TMC_Z2);
+ #endif
+ #if M91x_USE_E0
tmc_report_otpw(stepperE0, TMC_E0);
#endif
+ #if M91x_USE_E(1)
+ tmc_report_otpw(stepperE1, TMC_E1);
+ #endif
+ #if M91x_USE_E(2)
+ tmc_report_otpw(stepperE2, TMC_E2);
+ #endif
+ #if M91x_USE_E(3)
+ tmc_report_otpw(stepperE3, TMC_E3);
+ #endif
+ #if M91x_USE_E(4)
+ tmc_report_otpw(stepperE4, TMC_E4);
+ #endif
}
/**
* M912: Clear TMC stepper driver overtemperature pre-warn flag held by the library
+ * Specify one or more axes with X, Y, Z, X1, Y1, Z1, X2, Y2, Z2, and E[index].
+ * If no axes are given, clear all.
+ *
+ * Examples:
+ * M912 X ; clear X and X2
+ * M912 X1 ; clear X1 only
+ * M912 X2 ; clear X2 only
+ * M912 X E ; clear X, X2, and all E
+ * M912 E1 ; clear E1 only
*/
inline void gcode_M912() {
- const bool clearX = parser.seen(axis_codes[X_AXIS]), clearY = parser.seen(axis_codes[Y_AXIS]), clearZ = parser.seen(axis_codes[Z_AXIS]), clearE = parser.seen(axis_codes[E_AXIS]),
- clearAll = (!clearX && !clearY && !clearZ && !clearE) || (clearX && clearY && clearZ && clearE);
- #if ENABLED(X_IS_TMC2130) || ENABLED(IS_TRAMS) || (ENABLED(X_IS_TMC2208) && PIN_EXISTS(X_SERIAL_RX))
- if (clearX || clearAll) tmc_clear_otpw(stepperX, TMC_X);
- #endif
- #if ENABLED(X2_IS_TMC2130) || (ENABLED(X2_IS_TMC2208) && PIN_EXISTS(X_SERIAL_RX))
- if (clearX || clearAll) tmc_clear_otpw(stepperX, TMC_X);
+ const bool hasX = parser.seen(axis_codes[X_AXIS]),
+ hasY = parser.seen(axis_codes[Y_AXIS]),
+ hasZ = parser.seen(axis_codes[Z_AXIS]),
+ hasE = parser.seen(axis_codes[E_AXIS]),
+ hasNone = !hasX && !hasY && !hasZ && !hasE;
+
+ #if M91x_USE_X || M91x_USE(X2)
+ const uint8_t xval = parser.byteval(axis_codes[X_AXIS], 10);
+ #if M91x_USE_X
+ if (hasNone || xval == 1 || (hasX && xval == 10)) tmc_clear_otpw(stepperX, TMC_X);
+ #endif
+ #if M91x_USE(X2)
+ if (hasNone || xval == 2 || (hasX && xval == 10)) tmc_clear_otpw(stepperX2, TMC_X2);
+ #endif
#endif
- #if ENABLED(Y_IS_TMC2130) || (ENABLED(Y_IS_TMC2208) && PIN_EXISTS(Y_SERIAL_RX))
- if (clearY || clearAll) tmc_clear_otpw(stepperY, TMC_Y);
+ #if M91x_USE_Y || M91x_USE(Y2)
+ const uint8_t yval = parser.byteval(axis_codes[Y_AXIS], 10);
+ #if M91x_USE_Y
+ if (hasNone || yval == 1 || (hasY && yval == 10)) tmc_clear_otpw(stepperY, TMC_Y);
+ #endif
+ #if M91x_USE(Y2)
+ if (hasNone || yval == 2 || (hasY && yval == 10)) tmc_clear_otpw(stepperY2, TMC_Y2);
+ #endif
#endif
- #if ENABLED(Z_IS_TMC2130) || (ENABLED(Z_IS_TMC2208) && PIN_EXISTS(Z_SERIAL_RX))
- if (clearZ || clearAll) tmc_clear_otpw(stepperZ, TMC_Z);
+ #if M91x_USE_Z || M91x_USE(Z2)
+ const uint8_t zval = parser.byteval(axis_codes[Z_AXIS], 10);
+ #if M91x_USE_Z
+ if (hasNone || zval == 1 || (hasZ && zval == 10)) tmc_clear_otpw(stepperZ, TMC_Z);
+ #endif
+ #if M91x_USE(Z2)
+ if (hasNone || zval == 2 || (hasZ && zval == 10)) tmc_clear_otpw(stepperZ2, TMC_Z2);
+ #endif
#endif
- #if ENABLED(E0_IS_TMC2130) || (ENABLED(E0_IS_TMC2208) && PIN_EXISTS(E0_SERIAL_RX))
- if (clearE || clearAll) tmc_clear_otpw(stepperE0, TMC_E0);
+ #if M91x_USE_E0 || M91x_USE_E(1) || M91x_USE_E(2) || M91x_USE_E(3) || M91x_USE_E(4)
+ const uint8_t eval = parser.byteval(axis_codes[E_AXIS], 10);
+ #if M91x_USE_E0
+ if (hasNone || eval == 0 || (hasE && eval == 10)) tmc_clear_otpw(stepperE0, TMC_E0);
+ #endif
+ #if M91x_USE_E(1)
+ if (hasNone || eval == 1 || (hasE && eval == 10)) tmc_clear_otpw(stepperE1, TMC_E1);
+ #endif
+ #if M91x_USE_E(2)
+ if (hasNone || eval == 2 || (hasE && eval == 10)) tmc_clear_otpw(stepperE2, TMC_E2);
+ #endif
+ #if M91x_USE_E(3)
+ if (hasNone || eval == 3 || (hasE && eval == 10)) tmc_clear_otpw(stepperE3, TMC_E3);
+ #endif
+ #if M91x_USE_E(4)
+ if (hasNone || eval == 4 || (hasE && eval == 10)) tmc_clear_otpw(stepperE4, TMC_E4);
+ #endif
#endif
}
@@ -11006,10 +11059,10 @@ inline void gcode_M502() {
*/
#if ENABLED(HYBRID_THRESHOLD)
inline void gcode_M913() {
- #define TMC_SAY_PWMTHRS(P,Q) tmc_get_pwmthrs(stepper##Q, TMC_##Q, planner.axis_steps_per_mm[P##_AXIS])
- #define TMC_SET_PWMTHRS(P,Q) tmc_set_pwmthrs(stepper##Q, TMC_##Q, value, planner.axis_steps_per_mm[P##_AXIS])
+ #define TMC_SAY_PWMTHRS(A,Q) tmc_get_pwmthrs(stepper##Q, TMC_##Q, planner.axis_steps_per_mm[_AXIS(A)])
+ #define TMC_SET_PWMTHRS(A,Q) tmc_set_pwmthrs(stepper##Q, value, planner.axis_steps_per_mm[_AXIS(A)])
#define TMC_SAY_PWMTHRS_E(E) do{ const uint8_t extruder = E; tmc_get_pwmthrs(stepperE##E, TMC_E##E, planner.axis_steps_per_mm[E_AXIS_N]); }while(0)
- #define TMC_SET_PWMTHRS_E(E) do{ const uint8_t extruder = E; tmc_set_pwmthrs(stepperE##E, TMC_E##E, value, planner.axis_steps_per_mm[E_AXIS_N]); }while(0)
+ #define TMC_SET_PWMTHRS_E(E) do{ const uint8_t extruder = E; tmc_set_pwmthrs(stepperE##E, value, planner.axis_steps_per_mm[E_AXIS_N]); }while(0)
bool report = true;
const uint8_t index = parser.byteval('I');
@@ -11115,7 +11168,7 @@ inline void gcode_M502() {
#if ENABLED(SENSORLESS_HOMING)
inline void gcode_M914() {
#define TMC_SAY_SGT(Q) tmc_get_sgt(stepper##Q, TMC_##Q)
- #define TMC_SET_SGT(Q) tmc_set_sgt(stepper##Q, TMC_##Q, value)
+ #define TMC_SET_SGT(Q) tmc_set_sgt(stepper##Q, value)
bool report = true;
const uint8_t index = parser.byteval('I');
@@ -11478,7 +11531,7 @@ inline void gcode_M999() {
flush_and_request_resend();
}
-#if ENABLED(SWITCHING_EXTRUDER)
+#if DO_SWITCH_EXTRUDER
#if EXTRUDERS > 3
#define REQ_ANGLES 4
#define _SERVO_NR (e < 2 ? SWITCHING_EXTRUDER_SERVO_NR : SWITCHING_EXTRUDER_E23_SERVO_NR)
@@ -11489,7 +11542,7 @@ inline void gcode_M999() {
inline void move_extruder_servo(const uint8_t e) {
constexpr int16_t angles[] = SWITCHING_EXTRUDER_SERVO_ANGLES;
static_assert(COUNT(angles) == REQ_ANGLES, "SWITCHING_EXTRUDER_SERVO_ANGLES needs " STRINGIFY(REQ_ANGLES) " angles.");
- stepper.synchronize();
+ planner.synchronize();
#if EXTRUDERS & 1
if (e < EXTRUDERS - 1)
#endif
@@ -11498,12 +11551,12 @@ inline void gcode_M999() {
safe_delay(500);
}
}
-#endif // SWITCHING_EXTRUDER
+#endif // DO_SWITCH_EXTRUDER
#if ENABLED(SWITCHING_NOZZLE)
inline void move_nozzle_servo(const uint8_t e) {
const int16_t angles[2] = SWITCHING_NOZZLE_SERVO_ANGLES;
- stepper.synchronize();
+ planner.synchronize();
MOVE_SERVO(SWITCHING_NOZZLE_SERVO_NR, angles[e]);
safe_delay(500);
}
@@ -11622,7 +11675,7 @@ inline void invalid_extruder_error(const uint8_t e) {
planner.max_feedrate_mm_s[i == 1 ? X_AXIS : Z_AXIS],
active_extruder
);
- stepper.synchronize();
+ planner.synchronize();
}
// Apply Y & Z extruder offset (X offset is used as home pos with Dual X)
@@ -11720,7 +11773,7 @@ inline void invalid_extruder_error(const uint8_t e) {
if (DEBUGGING(LEVELING)) DEBUG_POS("Moving to Raised Z-Position", current_position);
#endif
planner.buffer_line_kinematic(current_position, planner.max_feedrate_mm_s[Z_AXIS], active_extruder);
- stepper.synchronize();
+ planner.synchronize();
// STEP 2
current_position[X_AXIS] = parkingposx[active_extruder] + hotend_offset[X_AXIS][active_extruder];
@@ -11729,7 +11782,7 @@ inline void invalid_extruder_error(const uint8_t e) {
if (DEBUGGING(LEVELING)) DEBUG_POS("Moving ParkPos", current_position);
#endif
planner.buffer_line_kinematic(current_position, planner.max_feedrate_mm_s[X_AXIS], active_extruder);
- stepper.synchronize();
+ planner.synchronize();
// STEP 3
#if ENABLED(DEBUG_LEVELING_FEATURE)
@@ -11747,7 +11800,7 @@ inline void invalid_extruder_error(const uint8_t e) {
if (DEBUGGING(LEVELING)) DEBUG_POS("Moving away from parked extruder", current_position);
#endif
planner.buffer_line_kinematic(current_position, planner.max_feedrate_mm_s[X_AXIS], active_extruder);
- stepper.synchronize();
+ planner.synchronize();
// STEP 5
#if ENABLED(DEBUG_LEVELING_FEATURE)
@@ -11768,7 +11821,7 @@ inline void invalid_extruder_error(const uint8_t e) {
if (DEBUGGING(LEVELING)) DEBUG_POS("Move UnparkPos", current_position);
#endif
planner.buffer_line_kinematic(current_position, planner.max_feedrate_mm_s[X_AXIS]/2, active_extruder);
- stepper.synchronize();
+ planner.synchronize();
// Step 7
current_position[X_AXIS] = midpos - hotend_offset[X_AXIS][tmp_extruder];
@@ -11777,7 +11830,7 @@ inline void invalid_extruder_error(const uint8_t e) {
if (DEBUGGING(LEVELING)) DEBUG_POS("Move midway to new extruder", current_position);
#endif
planner.buffer_line_kinematic(current_position, planner.max_feedrate_mm_s[X_AXIS], active_extruder);
- stepper.synchronize();
+ planner.synchronize();
#if ENABLED(DEBUG_LEVELING_FEATURE)
SERIAL_ECHOLNPGM("Autopark done.");
#endif
@@ -11905,6 +11958,9 @@ void tool_change(const uint8_t tmp_extruder, const float fr_mm_s/*=0.0*/, bool n
#endif
// Move back to the original (or tweaked) position
do_blocking_move_to(destination[X_AXIS], destination[Y_AXIS], destination[Z_AXIS]);
+ #if ENABLED(DUAL_X_CARRIAGE)
+ active_extruder_parked = false;
+ #endif
}
#if ENABLED(SWITCHING_NOZZLE)
else {
@@ -11914,7 +11970,7 @@ void tool_change(const uint8_t tmp_extruder, const float fr_mm_s/*=0.0*/, bool n
#endif
} // (tmp_extruder != active_extruder)
- stepper.synchronize();
+ planner.synchronize();
#if ENABLED(EXT_SOLENOID) && !ENABLED(PARKING_EXTRUDER)
disable_all_solenoids();
@@ -11941,7 +11997,7 @@ void tool_change(const uint8_t tmp_extruder, const float fr_mm_s/*=0.0*/, bool n
#endif // HOTENDS <= 1
#if DO_SWITCH_EXTRUDER
- stepper.synchronize();
+ planner.synchronize();
move_extruder_servo(active_extruder);
#endif
@@ -12596,7 +12652,7 @@ void ok_to_send() {
#endif
gridx = gx;
- nextx = min(gridx + 1, ABL_BG_POINTS_X - 1);
+ nextx = MIN(gridx + 1, ABL_BG_POINTS_X - 1);
}
if (last_y != ry || last_gridx != gridx) {
@@ -12613,7 +12669,7 @@ void ok_to_send() {
#endif
gridy = gy;
- nexty = min(gridy + 1, ABL_BG_POINTS_Y - 1);
+ nexty = MIN(gridy + 1, ABL_BG_POINTS_Y - 1);
}
if (last_gridx != gridx || last_gridy != gridy) {
@@ -12637,7 +12693,7 @@ void ok_to_send() {
/*
static float last_offset = 0;
- if (FABS(last_offset - offset) > 0.2) {
+ if (ABS(last_offset - offset) > 0.2) {
SERIAL_ECHOPGM("Sudden Shift at ");
SERIAL_ECHOPAIR("x=", rx);
SERIAL_ECHOPAIR(" / ", bilinear_grid_spacing[X_AXIS]);
@@ -12760,7 +12816,7 @@ void ok_to_send() {
const float centered_extent = delta[A_AXIS];
cartesian[Y_AXIS] = DELTA_PRINTABLE_RADIUS;
inverse_kinematics(cartesian);
- return FABS(centered_extent - delta[A_AXIS]);
+ return ABS(centered_extent - delta[A_AXIS]);
}
/**
@@ -12864,21 +12920,21 @@ void ok_to_send() {
void get_cartesian_from_steppers() {
#if ENABLED(DELTA)
forward_kinematics_DELTA(
- stepper.get_axis_position_mm(A_AXIS),
- stepper.get_axis_position_mm(B_AXIS),
- stepper.get_axis_position_mm(C_AXIS)
+ planner.get_axis_position_mm(A_AXIS),
+ planner.get_axis_position_mm(B_AXIS),
+ planner.get_axis_position_mm(C_AXIS)
);
#else
#if IS_SCARA
forward_kinematics_SCARA(
- stepper.get_axis_position_degrees(A_AXIS),
- stepper.get_axis_position_degrees(B_AXIS)
+ planner.get_axis_position_degrees(A_AXIS),
+ planner.get_axis_position_degrees(B_AXIS)
);
#else
- cartes[X_AXIS] = stepper.get_axis_position_mm(X_AXIS);
- cartes[Y_AXIS] = stepper.get_axis_position_mm(Y_AXIS);
+ cartes[X_AXIS] = planner.get_axis_position_mm(X_AXIS);
+ cartes[Y_AXIS] = planner.get_axis_position_mm(Y_AXIS);
#endif
- cartes[Z_AXIS] = stepper.get_axis_position_mm(Z_AXIS);
+ cartes[Z_AXIS] = planner.get_axis_position_mm(Z_AXIS);
#endif
}
@@ -12933,7 +12989,7 @@ void set_current_from_steppers_for_axis(const AxisEnum axis) {
// If the move is very short, check the E move distance
// No E move either? Game over.
float cartesian_mm = SQRT(sq(xdiff) + sq(ydiff) + sq(zdiff));
- if (UNEAR_ZERO(cartesian_mm)) cartesian_mm = FABS(ediff);
+ if (UNEAR_ZERO(cartesian_mm)) cartesian_mm = ABS(ediff);
if (UNEAR_ZERO(cartesian_mm)) return;
// The length divided by the segment size
@@ -12968,7 +13024,8 @@ void set_current_from_steppers_for_axis(const AxisEnum axis) {
idle();
}
LOOP_XYZE(i) raw[i] += segment_distance[i];
- planner.buffer_line_kinematic(raw, fr_mm_s, active_extruder, cartesian_segment_mm);
+ if (!planner.buffer_line_kinematic(raw, fr_mm_s, active_extruder, cartesian_segment_mm))
+ break;
}
// Since segment_distance is only approximate,
@@ -13000,10 +13057,10 @@ void set_current_from_steppers_for_axis(const AxisEnum axis) {
return;
}
- #define MBL_SEGMENT_END(A) (current_position[A ##_AXIS] + (destination[A ##_AXIS] - current_position[A ##_AXIS]) * normalized_dist)
+ #define MBL_SEGMENT_END(A) (current_position[_AXIS(A)] + (destination[_AXIS(A)] - current_position[_AXIS(A)]) * normalized_dist)
float normalized_dist, end[XYZE];
- const int8_t gcx = max(cx1, cx2), gcy = max(cy1, cy2);
+ const int8_t gcx = MAX(cx1, cx2), gcy = MAX(cy1, cy2);
// Crosses on the X and not already split on this X?
// The x_splits flags are insurance against rounding errors.
@@ -13044,7 +13101,7 @@ void set_current_from_steppers_for_axis(const AxisEnum axis) {
#elif ENABLED(AUTO_BED_LEVELING_BILINEAR)
- #define CELL_INDEX(A,V) ((V - bilinear_start[A##_AXIS]) * ABL_BG_FACTOR(A##_AXIS))
+ #define CELL_INDEX(A,V) ((V - bilinear_start[_AXIS(A)]) * ABL_BG_FACTOR(_AXIS(A)))
/**
* Prepare a bilinear-leveled linear move on Cartesian,
@@ -13068,10 +13125,10 @@ void set_current_from_steppers_for_axis(const AxisEnum axis) {
return;
}
- #define LINE_SEGMENT_END(A) (current_position[A ##_AXIS] + (destination[A ##_AXIS] - current_position[A ##_AXIS]) * normalized_dist)
+ #define LINE_SEGMENT_END(A) (current_position[_AXIS(A)] + (destination[_AXIS(A)] - current_position[_AXIS(A)]) * normalized_dist)
float normalized_dist, end[XYZE];
- const int8_t gcx = max(cx1, cx2), gcy = max(cy1, cy2);
+ const int8_t gcx = MAX(cx1, cx2), gcy = MAX(cy1, cy2);
// Crosses on the X and not already split on this X?
// The x_splits flags are insurance against rounding errors.
@@ -13164,7 +13221,7 @@ void set_current_from_steppers_for_axis(const AxisEnum axis) {
// If the move is very short, check the E move distance
// No E move either? Game over.
float cartesian_mm = SQRT(sq(xdiff) + sq(ydiff) + sq(zdiff));
- if (UNEAR_ZERO(cartesian_mm)) cartesian_mm = FABS(ediff);
+ if (UNEAR_ZERO(cartesian_mm)) cartesian_mm = ABS(ediff);
if (UNEAR_ZERO(cartesian_mm)) return true;
// Minimum number of seconds to move the given distance
@@ -13254,7 +13311,8 @@ void set_current_from_steppers_for_axis(const AxisEnum axis) {
#if ENABLED(SCARA_FEEDRATE_SCALING)
// For SCARA scale the feed rate from mm/s to degrees/s
// i.e., Complete the angular vector in the given time.
- planner.buffer_segment(delta[A_AXIS], delta[B_AXIS], raw[Z_AXIS], raw[E_AXIS], HYPOT(delta[A_AXIS] - oldA, delta[B_AXIS] - oldB) * inverse_secs, active_extruder);
+ if (!planner.buffer_segment(delta[A_AXIS], delta[B_AXIS], raw[Z_AXIS], raw[E_AXIS], HYPOT(delta[A_AXIS] - oldA, delta[B_AXIS] - oldB) * inverse_secs, active_extruder))
+ break;
/*
SERIAL_ECHO(segments);
SERIAL_ECHOPAIR(": X=", raw[X_AXIS]); SERIAL_ECHOPAIR(" Y=", raw[Y_AXIS]);
@@ -13264,7 +13322,8 @@ void set_current_from_steppers_for_axis(const AxisEnum axis) {
//*/
oldA = delta[A_AXIS]; oldB = delta[B_AXIS];
#else
- planner.buffer_line(delta[A_AXIS], delta[B_AXIS], delta[C_AXIS], raw[E_AXIS], _feedrate_mm_s, active_extruder, cartesian_segment_mm);
+ if (!planner.buffer_line(delta[A_AXIS], delta[B_AXIS], delta[C_AXIS], raw[E_AXIS], _feedrate_mm_s, active_extruder, cartesian_segment_mm))
+ break;
#endif
}
@@ -13358,14 +13417,14 @@ void set_current_from_steppers_for_axis(const AxisEnum axis) {
}
// unpark extruder: 1) raise, 2) move into starting XY position, 3) lower
for (uint8_t i = 0; i < 3; i++)
- planner.buffer_line(
+ if (!planner.buffer_line(
i == 0 ? raised_parked_position[X_AXIS] : current_position[X_AXIS],
i == 0 ? raised_parked_position[Y_AXIS] : current_position[Y_AXIS],
i == 2 ? current_position[Z_AXIS] : raised_parked_position[Z_AXIS],
current_position[E_AXIS],
i == 1 ? PLANNER_XY_FEEDRATE() : planner.max_feedrate_mm_s[Z_AXIS],
- active_extruder
- );
+ active_extruder)
+ ) break;
delayed_move_time = 0;
active_extruder_parked = false;
#if ENABLED(DEBUG_LEVELING_FEATURE)
@@ -13382,18 +13441,13 @@ void set_current_from_steppers_for_axis(const AxisEnum axis) {
}
#endif
// move duplicate extruder into correct duplication position.
- planner.set_position_mm(
- inactive_extruder_x_pos,
- current_position[Y_AXIS],
- current_position[Z_AXIS],
- current_position[E_AXIS]
- );
- planner.buffer_line(
+ planner.set_position_mm(inactive_extruder_x_pos, current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
+ if (!planner.buffer_line(
current_position[X_AXIS] + duplicate_extruder_x_offset,
current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS],
- planner.max_feedrate_mm_s[X_AXIS], 1
- );
- stepper.synchronize();
+ planner.max_feedrate_mm_s[X_AXIS], 1)
+ ) break;
+ planner.synchronize();
SYNC_PLAN_POSITION_KINEMATIC();
extruder_duplication_enabled = true;
active_extruder_parked = false;
@@ -13437,7 +13491,7 @@ void prepare_move_to_destination() {
}
#endif // PREVENT_COLD_EXTRUSION
#if ENABLED(PREVENT_LENGTHY_EXTRUDE)
- if (FABS(destination[E_AXIS] - current_position[E_AXIS]) * planner.e_factor[active_extruder] > (EXTRUDE_MAXLENGTH)) {
+ if (ABS(destination[E_AXIS] - current_position[E_AXIS]) * planner.e_factor[active_extruder] > (EXTRUDE_MAXLENGTH)) {
current_position[E_AXIS] = destination[E_AXIS]; // Behave as if the move really took place, but ignore E part
SERIAL_ECHO_START();
SERIAL_ECHOLNPGM(MSG_ERR_LONG_EXTRUDE_STOP);
@@ -13519,7 +13573,7 @@ void prepare_move_to_destination() {
angular_travel = RADIANS(360);
const float flat_mm = radius * angular_travel,
- mm_of_travel = linear_travel ? HYPOT(flat_mm, linear_travel) : FABS(flat_mm);
+ mm_of_travel = linear_travel ? HYPOT(flat_mm, linear_travel) : ABS(flat_mm);
if (mm_of_travel < 0.001) return;
uint16_t segments = FLOOR(mm_of_travel / (MM_PER_ARC_SEGMENT));
@@ -13625,14 +13679,17 @@ void prepare_move_to_destination() {
// i.e., Complete the angular vector in the given time.
inverse_kinematics(raw);
ADJUST_DELTA(raw);
- planner.buffer_segment(delta[A_AXIS], delta[B_AXIS], raw[Z_AXIS], raw[E_AXIS], HYPOT(delta[A_AXIS] - oldA, delta[B_AXIS] - oldB) * inverse_secs, active_extruder);
+ if (!planner.buffer_segment(delta[A_AXIS], delta[B_AXIS], raw[Z_AXIS], raw[E_AXIS], HYPOT(delta[A_AXIS] - oldA, delta[B_AXIS] - oldB) * inverse_secs, active_extruder))
+ break;
oldA = delta[A_AXIS]; oldB = delta[B_AXIS];
#elif HAS_UBL_AND_CURVES
float pos[XYZ] = { raw[X_AXIS], raw[Y_AXIS], raw[Z_AXIS] };
planner.apply_leveling(pos);
- planner.buffer_segment(pos[X_AXIS], pos[Y_AXIS], pos[Z_AXIS], raw[E_AXIS], fr_mm_s, active_extruder);
+ if (!planner.buffer_segment(pos[X_AXIS], pos[Y_AXIS], pos[Z_AXIS], raw[E_AXIS], fr_mm_s, active_extruder))
+ break;
#else
- planner.buffer_line_kinematic(raw, fr_mm_s, active_extruder);
+ if (!planner.buffer_line_kinematic(raw, fr_mm_s, active_extruder))
+ break;
#endif
}
@@ -13651,23 +13708,16 @@ void prepare_move_to_destination() {
planner.buffer_line_kinematic(cart, fr_mm_s, active_extruder);
#endif
- // As far as the parser is concerned, the position is now == target. In reality the
- // motion control system might still be processing the action and the real tool position
- // in any intermediate location.
- set_current_from_destination();
+ COPY(current_position, cart);
} // plan_arc
#endif // ARC_SUPPORT
#if ENABLED(BEZIER_CURVE_SUPPORT)
- void plan_cubic_move(const float (&offset)[4]) {
- cubic_b_spline(current_position, destination, offset, MMS_SCALED(feedrate_mm_s), active_extruder);
-
- // As far as the parser is concerned, the position is now == destination. In reality the
- // motion control system might still be processing the action and the real tool position
- // in any intermediate location.
- set_current_from_destination();
+ void plan_cubic_move(const float (&cart)[XYZE], const float (&offset)[4]) {
+ cubic_b_spline(current_position, cart, offset, MMS_SCALED(feedrate_mm_s), active_extruder);
+ COPY(current_position, cart);
}
#endif // BEZIER_CURVE_SUPPORT
@@ -14011,7 +14061,7 @@ void manage_inactivity(const bool ignore_stepper_queue/*=false*/) {
current_position[E_AXIS] = olde;
planner.set_e_position_mm(olde);
- stepper.synchronize();
+ planner.synchronize();
#if ENABLED(SWITCHING_EXTRUDER)
E0_ENABLE_WRITE(oldstatus);
#else
@@ -14271,7 +14321,9 @@ void setup() {
print_job_timer.init(); // Initial setup of print job timer
- stepper.init(); // Initialize stepper, this enables interrupts!
+ endstops.init(); // Init endstops and pullups
+
+ stepper.init(); // Init stepper. This enables interrupts!
servo_init(); // Initialize all servos, stow servo probe
@@ -14356,7 +14408,7 @@ void setup() {
#endif
lcd_init();
- LCD_MESSAGEPGM(WELCOME_MSG);
+ lcd_reset_status();
#if ENABLED(SHOW_BOOTSCREEN)
lcd_bootscreen();
@@ -14396,10 +14448,6 @@ void setup() {
i2c.onRequest(i2c_on_request);
#endif
- #if ENABLED(ENDSTOP_INTERRUPTS_FEATURE)
- setup_endstop_interrupts();
- #endif
-
#if DO_SWITCH_EXTRUDER
move_extruder_servo(0); // Initialize extruder servo
#endif
@@ -14478,15 +14526,14 @@ void loop() {
card.closefile();
SERIAL_PROTOCOLLNPGM(MSG_FILE_SAVED);
- #if !(defined(__AVR__) && defined(USBCON))
+ #if USE_MARLINSERIAL
#if ENABLED(SERIAL_STATS_DROPPED_RX)
SERIAL_ECHOLNPAIR("Dropped bytes: ", customizedSerial.dropped());
#endif
-
#if ENABLED(SERIAL_STATS_MAX_RX_QUEUED)
SERIAL_ECHOLNPAIR("Max RX Queue Size: ", customizedSerial.rxMaxEnqueued());
#endif
- #endif // !(__AVR__ && USBCON)
+ #endif
ok_to_send();
}
diff --git a/Marlin/Max7219_Debug_LEDs.cpp b/Marlin/Max7219_Debug_LEDs.cpp
index 83b285a08c..ad2cf3effc 100644
--- a/Marlin/Max7219_Debug_LEDs.cpp
+++ b/Marlin/Max7219_Debug_LEDs.cpp
@@ -60,11 +60,12 @@
#include "planner.h"
#include "stepper.h"
#include "Marlin.h"
+#include "delay.h"
static uint8_t LEDs[8] = { 0 };
// Delay for 0.1875µs (16MHz AVR) or 0.15µs (20MHz AVR)
-#define SIG_DELAY() DELAY_3_NOP
+#define SIG_DELAY() DELAY_NS(188)
void Max7219_PutByte(uint8_t data) {
CRITICAL_SECTION_START
@@ -341,8 +342,8 @@ void Max7219_idle_tasks() {
NOMORE(current_depth, 16); // if the BLOCK_BUFFER_SIZE is greater than 16, two lines
// of LEDs is enough to see if the buffer is draining
- const uint8_t st = min(current_depth, last_depth),
- en = max(current_depth, last_depth);
+ const uint8_t st = MIN(current_depth, last_depth),
+ en = MAX(current_depth, last_depth);
if (current_depth < last_depth)
for (uint8_t i = st; i <= en; i++) // clear the highest order LEDs
Max7219_LED_Off(MAX7219_DEBUG_STEPPER_QUEUE + (i & 1), i / 2);
diff --git a/Marlin/SanityCheck.h b/Marlin/SanityCheck.h
index 6e7b622862..bf6d449cb7 100644
--- a/Marlin/SanityCheck.h
+++ b/Marlin/SanityCheck.h
@@ -307,7 +307,7 @@
/**
* Serial
*/
-#if !(defined(__AVR__) && defined(USBCON))
+#if USE_MARLINSERIAL
#if ENABLED(SERIAL_XON_XOFF) && RX_BUFFER_SIZE < 1024
#error "SERIAL_XON_XOFF requires RX_BUFFER_SIZE >= 1024 for reliable transfers without drops."
#elif RX_BUFFER_SIZE && (RX_BUFFER_SIZE < 2 || !IS_POWER_OF_2(RX_BUFFER_SIZE))
@@ -1274,7 +1274,7 @@ static_assert(X_MAX_LENGTH >= X_BED_SIZE && Y_MAX_LENGTH >= Y_BED_SIZE,
/**
* emergency-command parser
*/
-#if ENABLED(EMERGENCY_PARSER) && defined(__AVR__) && defined(USBCON)
+#if ENABLED(EMERGENCY_PARSER) && !USE_MARLINSERIAL
#error "EMERGENCY_PARSER does not work on boards with AT90USB processors (USBCON)."
#endif
diff --git a/Marlin/SdBaseFile.cpp b/Marlin/SdBaseFile.cpp
index 3460b24d3f..3754fefb32 100644
--- a/Marlin/SdBaseFile.cpp
+++ b/Marlin/SdBaseFile.cpp
@@ -368,7 +368,7 @@ int8_t SdBaseFile::lsPrintNext(uint8_t flags, uint8_t indent) {
// print size if requested
if (!DIR_IS_SUBDIR(&dir) && (flags & LS_SIZE)) {
SERIAL_CHAR(' ');
- SERIAL_PROTOCOL(dir.fileSize);
+ SERIAL_ECHO(dir.fileSize);
}
SERIAL_EOL();
return DIR_IS_FILE(&dir) ? 1 : 2;
@@ -601,7 +601,7 @@ bool SdBaseFile::open(SdBaseFile* dirFile, const uint8_t dname[11], uint8_t ofla
// search for file
while (dirFile->curPosition_ < dirFile->fileSize_) {
- index = 0XF & (dirFile->curPosition_ >> 5);
+ index = 0xF & (dirFile->curPosition_ >> 5);
p = dirFile->readDirCache();
if (!p) return false;
@@ -705,7 +705,7 @@ bool SdBaseFile::open(SdBaseFile* dirFile, uint16_t index, uint8_t oflag) {
return false;
}
// open cached entry
- return openCachedEntry(index & 0XF, oflag);
+ return openCachedEntry(index & 0xF, oflag);
}
// open a cached directory entry. Assumes vol_ is initialized
@@ -775,7 +775,7 @@ bool SdBaseFile::openNext(SdBaseFile* dirFile, uint8_t oflag) {
vol_ = dirFile->vol_;
while (1) {
- index = 0XF & (dirFile->curPosition_ >> 5);
+ index = 0xF & (dirFile->curPosition_ >> 5);
// read entry into cache
p = dirFile->readDirCache();
@@ -902,11 +902,10 @@ int SdBaseFile::peek() {
return c;
}
-
// print uint8_t with width 2
-static void print2u(uint8_t v) {
+static void print2u(const uint8_t v) {
if (v < 10) SERIAL_CHAR('0');
- SERIAL_PRINT(v, DEC);
+ SERIAL_ECHO_F(v, DEC);
}
/**
@@ -927,7 +926,7 @@ static void print2u(uint8_t v) {
* \param[in] fatDate The date field from a directory entry.
*/
void SdBaseFile::printFatDate(uint16_t fatDate) {
- SERIAL_PROTOCOL(FAT_YEAR(fatDate));
+ SERIAL_ECHO(FAT_YEAR(fatDate));
SERIAL_CHAR('-');
print2u(FAT_MONTH(fatDate));
SERIAL_CHAR('-');
@@ -959,7 +958,7 @@ void SdBaseFile::printFatTime(uint16_t fatTime) {
bool SdBaseFile::printName() {
char name[FILENAME_LENGTH];
if (!getFilename(name)) return false;
- SERIAL_PROTOCOL(name);
+ SERIAL_ECHO(name);
return true;
}
@@ -1104,7 +1103,7 @@ dir_t* SdBaseFile::readDirCache() {
if (!isDir()) return 0;
// index of entry in cache
- i = (curPosition_ >> 5) & 0XF;
+ i = (curPosition_ >> 5) & 0xF;
// use read to locate and cache block
if (read() < 0) return 0;
@@ -1726,8 +1725,4 @@ int16_t SdBaseFile::write(const void* buf, uint16_t nbyte) {
return -1;
}
-#if ALLOW_DEPRECATED_FUNCTIONS
- void (*SdBaseFile::oldDateTime_)(uint16_t &date, uint16_t &time) = 0;
-#endif
-
#endif // SDSUPPORT
diff --git a/Marlin/SdBaseFile.h b/Marlin/SdBaseFile.h
index 425c65f9b2..12216bdc3c 100644
--- a/Marlin/SdBaseFile.h
+++ b/Marlin/SdBaseFile.h
@@ -37,6 +37,8 @@
#include "SdFatConfig.h"
#include "SdVolume.h"
+#include
+
/**
* \struct filepos_t
* \brief internal type for istream
@@ -383,119 +385,6 @@ class SdBaseFile {
bool open(SdBaseFile* dirFile, const uint8_t dname[11], uint8_t oflag);
bool openCachedEntry(uint8_t cacheIndex, uint8_t oflags);
dir_t* readDirCache();
-
-// Deprecated functions
-#if ALLOW_DEPRECATED_FUNCTIONS
- public:
-
- /**
- * \deprecated Use:
- * bool contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock);
- * \param[out] bgnBlock the first block address for the file.
- * \param[out] endBlock the last block address for the file.
- * \return true for success or false for failure.
- */
- bool contiguousRange(uint32_t& bgnBlock, uint32_t& endBlock) {
- return contiguousRange(&bgnBlock, &endBlock);
- }
-
- /**
- * \deprecated Use:
- * bool createContiguous(SdBaseFile* dirFile, const char* path, uint32_t size)
- * \param[in] dirFile The directory where the file will be created.
- * \param[in] path A path with a valid DOS 8.3 file name.
- * \param[in] size The desired file size.
- * \return true for success or false for failure.
- */
- bool createContiguous(SdBaseFile& dirFile, const char* path, uint32_t size) {
- return createContiguous(&dirFile, path, size);
- }
-
- /**
- * \deprecated Use:
- * static void dateTimeCallback(
- * void (*dateTime)(uint16_t* date, uint16_t* time));
- * \param[in] dateTime The user's call back function.
- */
- static void dateTimeCallback(
- void (*dateTime)(uint16_t &date, uint16_t &time)) {
- oldDateTime_ = dateTime;
- dateTime_ = dateTime ? oldToNew : 0;
- }
-
- /**
- * \deprecated Use:
- * bool open(SdBaseFile* dirFile, const char* path, uint8_t oflag);
- * \param[in] dirFile An open SdFat instance for the directory containing the
- * file to be opened.
- * \param[in] path A path with a valid 8.3 DOS name for the file.
- * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive
- * OR of flags O_READ, O_WRITE, O_TRUNC, and O_SYNC.
- * \return true for success or false for failure.
- */
- bool open(SdBaseFile& dirFile, const char* path, uint8_t oflag) {
- return open(&dirFile, path, oflag);
- }
-
- /**
- * \deprecated Do not use in new apps
- * \param[in] dirFile An open SdFat instance for the directory containing the
- * file to be opened.
- * \param[in] path A path with a valid 8.3 DOS name for a file to be opened.
- * \return true for success or false for failure.
- */
- bool open(SdBaseFile& dirFile, const char* path) {
- return open(dirFile, path, O_RDWR);
- }
-
- /**
- * \deprecated Use:
- * bool open(SdBaseFile* dirFile, uint16_t index, uint8_t oflag);
- * \param[in] dirFile An open SdFat instance for the directory.
- * \param[in] index The \a index of the directory entry for the file to be
- * opened. The value for \a index is (directory file position)/32.
- * \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive
- * OR of flags O_READ, O_WRITE, O_TRUNC, and O_SYNC.
- * \return true for success or false for failure.
- */
- bool open(SdBaseFile& dirFile, uint16_t index, uint8_t oflag) {
- return open(&dirFile, index, oflag);
- }
-
- /**
- * \deprecated Use: bool openRoot(SdVolume* vol);
- * \param[in] vol The FAT volume containing the root directory to be opened.
- * \return true for success or false for failure.
- */
- bool openRoot(SdVolume& vol) { return openRoot(&vol); }
-
- /**
- * \deprecated Use: int8_t readDir(dir_t* dir);
- * \param[out] dir The dir_t struct that will receive the data.
- * \return bytes read for success zero for eof or -1 for failure.
- */
- int8_t readDir(dir_t& dir, char* longFilename) {
- return readDir(&dir, longFilename);
- }
-
- /**
- * \deprecated Use:
- * static uint8_t remove(SdBaseFile* dirFile, const char* path);
- * \param[in] dirFile The directory that contains the file.
- * \param[in] path The name of the file to be removed.
- * \return true for success or false for failure.
- */
- static bool remove(SdBaseFile& dirFile, const char* path) { return remove(&dirFile, path); }
-
- private:
- static void (*oldDateTime_)(uint16_t &date, uint16_t &time);
- static void oldToNew(uint16_t * const date, uint16_t * const time) {
- uint16_t d, t;
- oldDateTime_(d, t);
- *date = d;
- *time = t;
- }
-#endif // ALLOW_DEPRECATED_FUNCTIONS
};
#endif // _SDBASEFILE_H_
diff --git a/Marlin/SdFatConfig.h b/Marlin/SdFatConfig.h
index 606a66f171..cfa5e34d18 100644
--- a/Marlin/SdFatConfig.h
+++ b/Marlin/SdFatConfig.h
@@ -61,11 +61,6 @@
*/
#define ENDL_CALLS_FLUSH 0
-/**
- * Allow use of deprecated functions if ALLOW_DEPRECATED_FUNCTIONS is nonzero
- */
-#define ALLOW_DEPRECATED_FUNCTIONS 1
-
/**
* Allow FAT12 volumes if FAT12_SUPPORT is nonzero.
* FAT12 has not been well tested.
diff --git a/Marlin/SdVolume.cpp b/Marlin/SdVolume.cpp
index bf8abc5797..df781cb6b6 100644
--- a/Marlin/SdVolume.cpp
+++ b/Marlin/SdVolume.cpp
@@ -204,7 +204,7 @@ bool SdVolume::fatPut(uint32_t cluster, uint32_t value) {
index &= 0x1FF;
uint8_t tmp = value;
if (cluster & 1) {
- tmp = (cacheBuffer_.data[index] & 0XF) | tmp << 4;
+ tmp = (cacheBuffer_.data[index] & 0xF) | tmp << 4;
}
cacheBuffer_.data[index] = tmp;
index++;
diff --git a/Marlin/Version.h b/Marlin/Version.h
index 081b2f3bb6..4a92d97f32 100644
--- a/Marlin/Version.h
+++ b/Marlin/Version.h
@@ -35,7 +35,7 @@
/**
* Marlin release version identifier
*/
- #define SHORT_BUILD_VERSION "TM3D 1.1.8_R4"
+ #define SHORT_BUILD_VERSION "TM3D 1.1.8_R5"
/**
* Verbose version identifier which should contain a reference to the location
@@ -48,7 +48,7 @@
* here we define this default string as the date where the latest release
* version was tagged.
*/
- #define STRING_DISTRIBUTION_DATE "2018-05-09"
+ #define STRING_DISTRIBUTION_DATE "2018-05-26"
/**
* Required minimum Configuration.h and Configuration_adv.h file versions.
diff --git a/Marlin/cardreader.cpp b/Marlin/cardreader.cpp
index 38c6d1001f..72eaa26940 100644
--- a/Marlin/cardreader.cpp
+++ b/Marlin/cardreader.cpp
@@ -88,25 +88,25 @@ void CardReader::lsDive(const char *prepend, SdFile parent, const char * const m
uint8_t cnt = 0;
// Read the next entry from a directory
- while (parent.readDir(p, longFilename) > 0) {
+ while (parent.readDir(&p, longFilename) > 0) {
// If the entry is a directory and the action is LS_SerialPrint
if (DIR_IS_SUBDIR(&p) && lsAction != LS_Count && lsAction != LS_GetFilename) {
// Get the short name for the item, which we know is a folder
- char lfilename[FILENAME_LENGTH];
- createFilename(lfilename, p);
+ char dosFilename[FILENAME_LENGTH];
+ createFilename(dosFilename, p);
// Allocate enough stack space for the full path to a folder, trailing slash, and nul
bool prepend_is_empty = (prepend[0] == '\0');
- int len = (prepend_is_empty ? 1 : strlen(prepend)) + strlen(lfilename) + 1 + 1;
+ int len = (prepend_is_empty ? 1 : strlen(prepend)) + strlen(dosFilename) + 1 + 1;
char path[len];
// Append the FOLDERNAME12/ to the passed string.
// It contains the full path to the "parent" argument.
// We now have the full path to the item in this folder.
strcpy(path, prepend_is_empty ? "/" : prepend); // root slash if prepend is empty
- strcat(path, lfilename); // FILENAME_LENGTH-1 characters maximum
+ strcat(path, dosFilename); // FILENAME_LENGTH-1 characters maximum
strcat(path, "/"); // 1 character
// Serial.print(path);
@@ -114,11 +114,11 @@ void CardReader::lsDive(const char *prepend, SdFile parent, const char * const m
// Get a new directory object using the full path
// and dive recursively into it.
SdFile dir;
- if (!dir.open(parent, lfilename, O_READ)) {
+ if (!dir.open(&parent, dosFilename, O_READ)) {
if (lsAction == LS_SerialPrint) {
SERIAL_ECHO_START();
SERIAL_ECHOPGM(MSG_SD_CANT_OPEN_SUBDIR);
- SERIAL_ECHOLN(lfilename);
+ SERIAL_ECHOLN(dosFilename);
}
}
lsDive(path, dir);
@@ -214,7 +214,7 @@ void CardReader::ls() {
// Open the sub-item as the new dive parent
SdFile dir;
- if (!dir.open(diveDir, segment, O_READ)) {
+ if (!dir.open(&diveDir, segment, O_READ)) {
SERIAL_EOL();
SERIAL_ECHO_START();
SERIAL_ECHOPGM(MSG_SD_CANT_OPEN_SUBDIR);
@@ -237,11 +237,11 @@ void CardReader::ls() {
*/
void CardReader::printFilename() {
if (file.isOpen()) {
- char lfilename[FILENAME_LENGTH];
- file.getFilename(lfilename);
- SERIAL_ECHO(lfilename);
+ char dosFilename[FILENAME_LENGTH];
+ file.getFilename(dosFilename);
+ SERIAL_ECHO(dosFilename);
#if ENABLED(LONG_FILENAME_HOST_SUPPORT)
- getfilename(0, lfilename);
+ getfilename(0, dosFilename);
if (longFilename[0]) {
SERIAL_ECHO(' ');
SERIAL_ECHO(longFilename);
@@ -262,16 +262,16 @@ void CardReader::initsd() {
#define SPI_SPEED SPI_FULL_SPEED
#endif
- if (!card.init(SPI_SPEED, SDSS)
+ if (!sd2card.init(SPI_SPEED, SDSS)
#if defined(LCD_SDSS) && (LCD_SDSS != SDSS)
- && !card.init(SPI_SPEED, LCD_SDSS)
+ && !sd2card.init(SPI_SPEED, LCD_SDSS)
#endif
) {
- //if (!card.init(SPI_HALF_SPEED,SDSS))
+ //if (!sd2card.init(SPI_HALF_SPEED,SDSS))
SERIAL_ECHO_START();
SERIAL_ECHOLNPGM(MSG_SD_INIT_FAIL);
}
- else if (!volume.init(&card)) {
+ else if (!volume.init(&sd2card)) {
SERIAL_ERROR_START();
SERIAL_ERRORLNPGM(MSG_SD_VOL_INIT_FAIL);
}
@@ -287,17 +287,6 @@ void CardReader::initsd() {
setroot();
}
-void CardReader::setroot() {
- /*if (!workDir.openRoot(&volume)) {
- SERIAL_ECHOLNPGM(MSG_SD_WORKDIR_FAIL);
- }*/
- workDir = root;
- curDir = &workDir;
- #if ENABLED(SDCARD_SORT_ALPHA)
- presort();
- #endif
-}
-
void CardReader::release() {
sdprinting = false;
cardOK = false;
@@ -335,9 +324,9 @@ void CardReader::stopSDPrint(
#endif
}
-void CardReader::openLogFile(char* name) {
+void CardReader::openLogFile(char * const path) {
logging = true;
- openFile(name, false);
+ openFile(path, false);
}
void appendAtom(SdFile &file, char *& dst, uint8_t &cnt) {
@@ -360,7 +349,7 @@ void CardReader::getAbsFilename(char *t) {
*t = '\0';
}
-void CardReader::openFile(char* name, const bool read, const bool subcall/*=false*/) {
+void CardReader::openFile(char * const path, const bool read, const bool subcall/*=false*/) {
if (!cardOK) return;
@@ -380,7 +369,7 @@ void CardReader::openFile(char* name, const bool read, const bool subcall/*=fals
filespos[file_subcall_ctr] = sdpos;
SERIAL_ECHO_START();
- SERIAL_ECHOPAIR("SUBROUTINE CALL target:\"", name);
+ SERIAL_ECHOPAIR("SUBROUTINE CALL target:\"", path);
SERIAL_ECHOPAIR("\" parent:\"", proc_filenames[file_subcall_ctr]);
SERIAL_ECHOLNPAIR("\" pos", sdpos);
file_subcall_ctr++;
@@ -401,48 +390,14 @@ void CardReader::openFile(char* name, const bool read, const bool subcall/*=fals
SERIAL_ECHO_START();
SERIAL_ECHOPGM("Now ");
serialprintPGM(doing == 1 ? PSTR("doing") : PSTR("fresh"));
- SERIAL_ECHOLNPAIR(" file: ", name);
+ SERIAL_ECHOLNPAIR(" file: ", path);
}
stopSDPrint();
- SdFile myDir;
- curDir = &root;
- char *fname = name;
- char *dirname_start, *dirname_end;
-
- if (name[0] == '/') {
- dirname_start = &name[1];
- while (dirname_start != NULL) {
- dirname_end = strchr(dirname_start, '/');
- //SERIAL_ECHOPGM("start:");SERIAL_ECHOLN((int)(dirname_start - name));
- //SERIAL_ECHOPGM("end :");SERIAL_ECHOLN((int)(dirname_end - name));
- if (dirname_end != NULL && dirname_end > dirname_start) {
- char subdirname[FILENAME_LENGTH];
- strncpy(subdirname, dirname_start, dirname_end - dirname_start);
- subdirname[dirname_end - dirname_start] = '\0';
- if (!myDir.open(curDir, subdirname, O_READ)) {
- SERIAL_PROTOCOLPAIR(MSG_SD_OPEN_FILE_FAIL, subdirname);
- SERIAL_PROTOCOLCHAR('.');
- return;
- }
- else {
- //SERIAL_ECHOLNPGM("dive ok");
- }
-
- curDir = &myDir;
- dirname_start = dirname_end + 1;
- }
- else { // the remainder after all /fsa/fdsa/ is the filename
- fname = dirname_start;
- //SERIAL_ECHOLNPGM("remainder");
- //SERIAL_ECHOLN(fname);
- break;
- }
- }
- }
- else
- curDir = &workDir; // Relative paths start in current directory
+ SdFile *curDir;
+ const char * const fname = diveToFile(curDir, path, false);
+ if (!fname) return;
if (read) {
if (file.open(curDir, fname, O_READ)) {
@@ -472,7 +427,7 @@ void CardReader::openFile(char* name, const bool read, const bool subcall/*=fals
}
else {
saving = true;
- SERIAL_PROTOCOLLNPAIR(MSG_SD_WRITE_TO_FILE, name);
+ SERIAL_PROTOCOLLNPAIR(MSG_SD_WRITE_TO_FILE, path);
lcd_setstatus(fname);
}
}
@@ -483,40 +438,9 @@ void CardReader::removeFile(const char * const name) {
stopSDPrint();
- SdFile myDir;
- curDir = &root;
- const char *fname = name;
-
- char *dirname_start, *dirname_end;
- if (name[0] == '/') {
- dirname_start = strchr(name, '/') + 1;
- while (dirname_start != NULL) {
- dirname_end = strchr(dirname_start, '/');
- //SERIAL_ECHOPGM("start:");SERIAL_ECHOLN((int)(dirname_start - name));
- //SERIAL_ECHOPGM("end :");SERIAL_ECHOLN((int)(dirname_end - name));
- if (dirname_end != NULL && dirname_end > dirname_start) {
- char subdirname[FILENAME_LENGTH];
- strncpy(subdirname, dirname_start, dirname_end - dirname_start);
- subdirname[dirname_end - dirname_start] = 0;
- SERIAL_ECHOLN(subdirname);
- if (!myDir.open(curDir, subdirname, O_READ)) {
- SERIAL_PROTOCOLPAIR(MSG_SD_OPEN_FILE_FAIL, subdirname);
- SERIAL_PROTOCOLCHAR('.');
- SERIAL_EOL();
- return;
- }
-
- curDir = &myDir;
- dirname_start = dirname_end + 1;
- }
- else {
- fname = dirname_start;
- break;
- }
- }
- }
- else // Relative paths are rooted in the current directory
- curDir = &workDir;
+ SdFile *curDir;
+ const char * const fname = diveToFile(curDir, name, false);
+ if (!fname) return;
if (file.remove(curDir, fname)) {
SERIAL_PROTOCOLPGM("File deleted:");
@@ -582,7 +506,7 @@ void CardReader::checkautostart() {
sprintf_P(autoname, PSTR("auto%i.g"), autostart_index);
dir_t p;
root.rewind();
- while (root.readDir(p, NULL) > 0) {
+ while (root.readDir(&p, NULL) > 0) {
for (int8_t i = (int8_t)strlen((char*)p.name); i--;) p.name[i] = tolower(p.name[i]);
if (p.name[9] != '~' && strncmp((char*)p.name, autoname, 5) == 0) {
openAndPrintFile(autoname);
@@ -612,6 +536,7 @@ void CardReader::closefile(const bool store_location) {
/**
* Get the name of a file in the current directory by index
+ * with optional name to match.
*/
void CardReader::getfilename(uint16_t nr, const char * const match/*=NULL*/) {
#if ENABLED(SDSORT_CACHE_NAMES)
@@ -628,35 +553,60 @@ void CardReader::getfilename(uint16_t nr, const char * const match/*=NULL*/) {
return;
}
#endif // SDSORT_CACHE_NAMES
- curDir = &workDir;
lsAction = LS_GetFilename;
nrFile_index = nr;
- curDir->rewind();
- lsDive(NULL, *curDir, match);
+ workDir.rewind();
+ lsDive(NULL, workDir, match);
}
uint16_t CardReader::getnrfilenames() {
- curDir = &workDir;
lsAction = LS_Count;
nrFiles = 0;
- curDir->rewind();
- lsDive(NULL, *curDir);
+ workDir.rewind();
+ lsDive(NULL, workDir);
//SERIAL_ECHOLN(nrFiles);
return nrFiles;
}
+/**
+ * Dive to the given file path, with optional echo.
+ * On exit set curDir and return the name part of the path.
+ * A NULL result indicates an unrecoverable error.
+ */
+const char* CardReader::diveToFile(SdFile*& curDir, const char * const path, const bool echo) {
+ SdFile myDir;
+ if (path[0] != '/') { curDir = &workDir; return path; }
+
+ curDir = &root;
+ const char *dirname_start = &path[1];
+ while (dirname_start) {
+ char * const dirname_end = strchr(dirname_start, '/');
+ if (dirname_end <= dirname_start) break;
+
+ char dosSubdirname[FILENAME_LENGTH];
+ const uint8_t len = dirname_end - dirname_start;
+ strncpy(dosSubdirname, dirname_start, len);
+ dosSubdirname[len] = 0;
+
+ if (echo) SERIAL_ECHOLN(dosSubdirname);
+
+ if (!myDir.open(curDir, dosSubdirname, O_READ)) {
+ SERIAL_PROTOCOLPAIR(MSG_SD_OPEN_FILE_FAIL, dosSubdirname);
+ SERIAL_PROTOCOLCHAR('.');
+ SERIAL_EOL();
+ return NULL;
+ }
+ curDir = &myDir;
+ dirname_start = dirname_end + 1;
+ }
+ return dirname_start;
+}
+
void CardReader::chdir(const char * relpath) {
SdFile newDir;
- SdFile *parent = &root;
+ SdFile *parent = workDir.isOpen() ? &workDir : &root;
- if (workDir.isOpen()) parent = &workDir;
-
- if (!newDir.open(*parent, relpath, O_READ)) {
- SERIAL_ECHO_START();
- SERIAL_ECHOPGM(MSG_SD_CANT_ENTER_SUBDIR);
- SERIAL_ECHOLN(relpath);
- }
- else {
+ if (newDir.open(parent, relpath, O_READ)) {
workDir = newDir;
if (workDirDepth < MAX_DIR_DEPTH)
workDirParents[workDirDepth++] = workDir;
@@ -664,6 +614,11 @@ void CardReader::chdir(const char * relpath) {
presort();
#endif
}
+ else {
+ SERIAL_ECHO_START();
+ SERIAL_ECHOPGM(MSG_SD_CANT_ENTER_SUBDIR);
+ SERIAL_ECHOLN(relpath);
+ }
}
int8_t CardReader::updir() {
@@ -676,6 +631,16 @@ int8_t CardReader::updir() {
return workDirDepth;
}
+void CardReader::setroot() {
+ /*if (!workDir.openRoot(&volume)) {
+ SERIAL_ECHOLNPGM(MSG_SD_WORKDIR_FAIL);
+ }*/
+ workDir = root;
+ #if ENABLED(SDCARD_SORT_ALPHA)
+ presort();
+ #endif
+}
+
#if ENABLED(SDCARD_SORT_ALPHA)
/**
@@ -921,7 +886,7 @@ uint16_t CardReader::get_num_Files() {
}
void CardReader::printingHasFinished() {
- stepper.synchronize();
+ planner.synchronize();
file.close();
if (file_subcall_ctr > 0) { // Heading up to a parent file that called current as a procedure.
file_subcall_ctr--;
@@ -941,7 +906,7 @@ void CardReader::printingHasFinished() {
#endif
#if ENABLED(SD_FINISHED_STEPPERRELEASE) && defined(SD_FINISHED_RELEASECOMMAND)
- stepper.cleaning_buffer_counter = 1; // The command will fire from the Stepper ISR
+ planner.finish_and_disable();
#endif
print_job_timer.stop();
if (print_job_timer.duration() > 60)
diff --git a/Marlin/cardreader.h b/Marlin/cardreader.h
index c74a45048d..dcdc2c48da 100644
--- a/Marlin/cardreader.h
+++ b/Marlin/cardreader.h
@@ -32,8 +32,6 @@
#define MAX_DIR_DEPTH 10 // Maximum folder depth
#include "SdFile.h"
-#include "types.h"
-#include "enum.h"
class CardReader {
public:
@@ -45,8 +43,8 @@ public:
void beginautostart();
void checkautostart();
- void openFile(char* name, const bool read, const bool subcall=false);
- void openLogFile(char* name);
+ void openFile(char * const path, const bool read, const bool subcall=false);
+ void openLogFile(char * const path);
void removeFile(const char * const name);
void closefile(const bool store_location=false);
void release();
@@ -75,6 +73,8 @@ public:
int8_t updir();
void setroot();
+ const char* diveToFile(SdFile*& curDir, const char * const path, const bool echo);
+
uint16_t get_num_Files();
#if ENABLED(SDCARD_SORT_ALPHA)
@@ -114,12 +114,12 @@ public:
}
#endif
+public:
bool saving, logging, sdprinting, cardOK, filenameIsDir;
char filename[FILENAME_LENGTH], longFilename[LONG_FILENAME_LENGTH];
int autostart_index;
-
private:
- SdFile root, *curDir, workDir, workDirParents[MAX_DIR_DEPTH];
+ SdFile root, workDir, workDirParents[MAX_DIR_DEPTH];
uint8_t workDirDepth;
// Sort files and folders alphabetically.
@@ -172,7 +172,7 @@ private:
#endif // SDCARD_SORT_ALPHA
- Sd2Card card;
+ Sd2Card sd2card;
SdVolume volume;
SdFile file;
diff --git a/Marlin/configuration_store.cpp b/Marlin/configuration_store.cpp
index ab49001df5..66926643b1 100644
--- a/Marlin/configuration_store.cpp
+++ b/Marlin/configuration_store.cpp
@@ -62,7 +62,7 @@
#if HAS_TRINAMIC
#include "stepper_indirection.h"
#include "tmc_util.h"
- #define TMC_GET_PWMTHRS(P,Q) _tmc_thrs(stepper##Q.microsteps(), stepper##Q.TPWMTHRS(), planner.axis_steps_per_mm[P##_AXIS])
+ #define TMC_GET_PWMTHRS(A,Q) _tmc_thrs(stepper##Q.microsteps(), stepper##Q.TPWMTHRS(), planner.axis_steps_per_mm[_AXIS(A)])
#endif
#if ENABLED(AUTO_BED_LEVELING_UBL)
@@ -73,6 +73,10 @@
#include "fwretract.h"
#endif
+#if ENABLED(PID_EXTRUSION_SCALING)
+ #define LPQ_LEN thermalManager.lpq_len
+#endif
+
#pragma pack(push, 1) // No padding between variables
typedef struct PID { float Kp, Ki, Kd; } PID;
@@ -183,7 +187,7 @@ typedef struct SettingsDataStruct {
//
PIDC hotendPID[MAX_EXTRUDERS]; // M301 En PIDC / M303 En U
- int lpq_len; // M301 L
+ int16_t lpq_len; // M301 L
//
// PIDTEMPBED
@@ -609,9 +613,9 @@ void MarlinSettings::postprocess() {
_FIELD_TEST(lpq_len);
#if DISABLED(PID_EXTRUSION_SCALING)
- int lpq_len = 20;
+ const int16_t LPQ_LEN = 20;
#endif
- EEPROM_WRITE(lpq_len);
+ EEPROM_WRITE(LPQ_LEN);
#if DISABLED(PIDTEMPBED)
dummy = DUMMY_PID_VALUE;
@@ -968,7 +972,6 @@ void MarlinSettings::postprocess() {
SERIAL_ECHOPAIR("(EEPROM=", stored_ver);
SERIAL_ECHOLNPGM(" Marlin=" EEPROM_VERSION ")");
#endif
- if (!validating) reset();
eeprom_error = true;
}
else {
@@ -1214,9 +1217,9 @@ void MarlinSettings::postprocess() {
_FIELD_TEST(lpq_len);
#if DISABLED(PID_EXTRUSION_SCALING)
- int lpq_len;
+ int16_t LPQ_LEN;
#endif
- EEPROM_READ(lpq_len);
+ EEPROM_READ(LPQ_LEN);
//
// Heated Bed PID
@@ -1340,7 +1343,7 @@ void MarlinSettings::postprocess() {
#endif
#if ENABLED(HYBRID_THRESHOLD)
- #define TMC_SET_PWMTHRS(P,Q) tmc_set_pwmthrs(stepper##Q, TMC_##Q, tmc_hybrid_threshold[TMC_##Q], planner.axis_steps_per_mm[P##_AXIS])
+ #define TMC_SET_PWMTHRS(A,Q) tmc_set_pwmthrs(stepper##Q, tmc_hybrid_threshold[TMC_##Q], planner.axis_steps_per_mm[_AXIS(A)])
uint32_t tmc_hybrid_threshold[TMC_AXES];
EEPROM_READ(tmc_hybrid_threshold);
if (!validating) {
@@ -1523,14 +1526,12 @@ void MarlinSettings::postprocess() {
#endif
}
- if (!validating) {
- if (eeprom_error) reset(); else postprocess();
- }
+ if (!validating && !eeprom_error) postprocess();
#if ENABLED(AUTO_BED_LEVELING_UBL)
- ubl.report_state();
-
if (!validating) {
+ ubl.report_state();
+
if (!ubl.sanity_check()) {
SERIAL_EOL();
#if ENABLED(EEPROM_CHITCHAT)
@@ -1717,7 +1718,7 @@ void MarlinSettings::reset() {
constexpr float tmp4[XYZ][HOTENDS] = {
HOTEND_OFFSET_X,
HOTEND_OFFSET_Y
- #ifdef HOTEND_OFFSET_Z
+ #if HAS_HOTEND_OFFSET_Z
, HOTEND_OFFSET_Z
#else
, { 0 }
@@ -1811,7 +1812,7 @@ void MarlinSettings::reset() {
#endif
}
#if ENABLED(PID_EXTRUSION_SCALING)
- lpq_len = 20; // default last-position-queue size
+ thermalManager.lpq_len = 20; // default last-position-queue size
#endif
#endif // PIDTEMP
@@ -2108,7 +2109,7 @@ void MarlinSettings::reset() {
SERIAL_ECHOPAIR(" M218 T", (int)e);
SERIAL_ECHOPAIR(" X", LINEAR_UNIT(hotend_offset[X_AXIS][e]));
SERIAL_ECHOPAIR(" Y", LINEAR_UNIT(hotend_offset[Y_AXIS][e]));
- #if ENABLED(DUAL_X_CARRIAGE) || ENABLED(SWITCHING_NOZZLE) ||ENABLED(PARKING_EXTRUDER)
+ #if HAS_HOTEND_OFFSET_Z
SERIAL_ECHOPAIR(" Z", LINEAR_UNIT(hotend_offset[Z_AXIS][e]));
#endif
SERIAL_EOL();
@@ -2274,7 +2275,7 @@ void MarlinSettings::reset() {
SERIAL_ECHOPAIR(" D", unscalePID_d(PID_PARAM(Kd, e)));
#if ENABLED(PID_EXTRUSION_SCALING)
SERIAL_ECHOPAIR(" C", PID_PARAM(Kc, e));
- if (e == 0) SERIAL_ECHOPAIR(" L", lpq_len);
+ if (e == 0) SERIAL_ECHOPAIR(" L", thermalManager.lpq_len);
#endif
SERIAL_EOL();
}
@@ -2289,7 +2290,7 @@ void MarlinSettings::reset() {
SERIAL_ECHOPAIR(" D", unscalePID_d(PID_PARAM(Kd, 0)));
#if ENABLED(PID_EXTRUSION_SCALING)
SERIAL_ECHOPAIR(" C", PID_PARAM(Kc, 0));
- SERIAL_ECHOPAIR(" L", lpq_len);
+ SERIAL_ECHOPAIR(" L", thermalManager.lpq_len);
#endif
SERIAL_EOL();
}
diff --git a/Marlin/configuration_store.h b/Marlin/configuration_store.h
index 1f0a33c599..2bba57930e 100644
--- a/Marlin/configuration_store.h
+++ b/Marlin/configuration_store.h
@@ -35,15 +35,16 @@ class MarlinSettings {
static bool save(); // Return 'true' if data was saved
FORCE_INLINE static bool init_eeprom() {
- bool success = true;
reset();
#if ENABLED(EEPROM_SETTINGS)
- success = save();
+ const bool success = save();
#if ENABLED(EEPROM_CHITCHAT)
if (success) report();
#endif
+ return success;
+ #else
+ return true;
#endif
- return success;
}
#if ENABLED(EEPROM_SETTINGS)
diff --git a/Marlin/delay.h b/Marlin/delay.h
new file mode 100644
index 0000000000..5689b2b4c1
--- /dev/null
+++ b/Marlin/delay.h
@@ -0,0 +1,77 @@
+/**
+ * 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 .
+ *
+ */
+
+/**
+ * AVR busy wait delay Cycles routines:
+ *
+ * DELAY_CYCLES(count): Delay execution in cycles
+ * DELAY_NS(count): Delay execution in nanoseconds
+ * DELAY_US(count): Delay execution in microseconds
+ */
+
+#ifndef MARLIN_DELAY_H
+#define MARLIN_DELAY_H
+
+#define nop() __asm__ __volatile__("nop;\n\t":::)
+
+FORCE_INLINE static void __delay_4cycles(uint8_t cy) {
+ __asm__ __volatile__(
+ L("1")
+ A("dec %[cnt]")
+ A("nop")
+ A("brne 1b")
+ : [cnt] "+r"(cy) // output: +r means input+output
+ : // input:
+ : "cc" // clobbers:
+ );
+}
+
+/* ---------------- Delay in cycles */
+FORCE_INLINE static void DELAY_CYCLES(uint16_t x) {
+
+ if (__builtin_constant_p(x)) {
+ #define MAXNOPS 4
+
+ if (x <= (MAXNOPS)) {
+ switch (x) { case 4: nop(); case 3: nop(); case 2: nop(); case 1: nop(); }
+ }
+ else {
+ const uint32_t rem = (x) % (MAXNOPS);
+ switch (rem) { case 3: nop(); case 2: nop(); case 1: nop(); }
+ if ((x = (x) / (MAXNOPS)))
+ __delay_4cycles(x); // if need more then 4 nop loop is more optimal
+ }
+
+ #undef MAXNOPS
+ }
+ else
+ __delay_4cycles(x / 4);
+}
+#undef nop
+
+/* ---------------- Delay in nanoseconds */
+#define DELAY_NS(x) DELAY_CYCLES( (x) * (F_CPU/1000000L) / 1000L )
+
+/* ---------------- Delay in microseconds */
+#define DELAY_US(x) DELAY_CYCLES( (x) * (F_CPU/1000000L) )
+
+#endif // MARLIN_DELAY_H
diff --git a/Marlin/digipot_mcp4018.cpp b/Marlin/digipot_mcp4018.cpp
index 06622d057f..e29e929e93 100644
--- a/Marlin/digipot_mcp4018.cpp
+++ b/Marlin/digipot_mcp4018.cpp
@@ -89,7 +89,7 @@ static void i2c_send(const uint8_t channel, const byte v) {
// This is for the MCP4018 I2C based digipot
void digipot_i2c_set_current(uint8_t channel, float current) {
- i2c_send(channel, current_to_wiper(min(max(current, 0.0f), float(DIGIPOT_A4988_MAX_CURRENT))));
+ i2c_send(channel, current_to_wiper(MIN(MAX(current, 0.0f), float(DIGIPOT_A4988_MAX_CURRENT))));
}
void digipot_i2c_init() {
diff --git a/Marlin/digipot_mcp4451.cpp b/Marlin/digipot_mcp4451.cpp
index d79915cc94..8e372220cb 100644
--- a/Marlin/digipot_mcp4451.cpp
+++ b/Marlin/digipot_mcp4451.cpp
@@ -50,7 +50,7 @@ static void i2c_send(const byte addr, const byte a, const byte b) {
// This is for the MCP4451 I2C based digipot
void digipot_i2c_set_current(uint8_t channel, float current) {
- current = min((float) max(current, 0.0f), DIGIPOT_I2C_MAX_CURRENT);
+ current = MIN((float) MAX(current, 0.0f), DIGIPOT_I2C_MAX_CURRENT);
// these addresses are specific to Azteeg X3 Pro, can be set to others,
// In this case first digipot is at address A0=0, A1= 0, second one is at A0=0, A1= 1
byte addr = 0x2C; // channel 0-3
diff --git a/Marlin/dogm_bitmaps.h b/Marlin/dogm_bitmaps.h
index f75aa41c92..42b94b7dbb 100644
--- a/Marlin/dogm_bitmaps.h
+++ b/Marlin/dogm_bitmaps.h
@@ -137,6 +137,7 @@
#else // !CUSTOM_STATUS_SCREEN_IMAGE
+ // Can also be overridden in Configuration.h
// If you can afford it, try the 3-frame fan animation!
#ifndef FAN_ANIM_FRAMES
#define FAN_ANIM_FRAMES 2
@@ -1148,7 +1149,9 @@
#define CUSTOM_BOOTSCREEN_BMPHEIGHT (sizeof(custom_start_bmp) / (CUSTOM_BOOTSCREEN_BMP_BYTEWIDTH))
#endif
-#if FAN_ANIM_FRAMES > 3
+#ifndef FAN_ANIM_FRAMES
+ #define FAN_ANIM_FRAMES 2
+#elif FAN_ANIM_FRAMES > 3
#error "Only 3 fan animation frames currently supported."
#endif
#ifndef STATUS_SCREEN_X
diff --git a/Marlin/endstop_interrupts.h b/Marlin/endstop_interrupts.h
index 6ad4fa55a4..65f0d1a5bd 100644
--- a/Marlin/endstop_interrupts.h
+++ b/Marlin/endstop_interrupts.h
@@ -24,7 +24,7 @@
* Endstop Interrupts
*
* Without endstop interrupts the endstop pins must be polled continually in
- * the stepper-ISR via endstops.update(), most of the time finding no change.
+ * the temperature-ISR via endstops.update(), most of the time finding no change.
* With this feature endstops.update() is called only when we know that at
* least one endstop has changed state, saving valuable CPU cycles.
*
@@ -40,6 +40,9 @@
#include "macros.h"
+// One ISR for all EXT-Interrupts
+void endstop_ISR(void) { endstops.check_possible_change(); }
+
/**
* Patch for pins_arduino.h (...\Arduino\hardware\arduino\avr\variants\mega\pins_arduino.h)
*
@@ -72,40 +75,30 @@
0 )
#endif
-volatile uint8_t e_hit = 0; // Different from 0 when the endstops should be tested in detail.
- // Must be reset to 0 by the test function when finished.
// Install Pin change interrupt for a pin. Can be called multiple times.
-void pciSetup(byte pin) {
+void pciSetup(const int8_t pin) {
SBI(*digitalPinToPCMSK(pin), digitalPinToPCMSKbit(pin)); // enable pin
SBI(PCIFR, digitalPinToPCICRbit(pin)); // clear any outstanding interrupt
SBI(PCICR, digitalPinToPCICRbit(pin)); // enable interrupt for the group
}
-// This is what is really done inside the interrupts.
-FORCE_INLINE void endstop_ISR_worker( void ) {
- e_hit = 2; // Because the detection of a e-stop hit has a 1 step debouncer it has to be called at least twice.
-}
-
-// Use one Routine to handle each group
-// One ISR for all EXT-Interrupts
-void endstop_ISR(void) { endstop_ISR_worker(); }
// Handlers for pin change interrupts
#ifdef PCINT0_vect
- ISR(PCINT0_vect) { endstop_ISR_worker(); }
+ ISR(PCINT0_vect) { endstop_ISR(); }
#endif
#ifdef PCINT1_vect
- ISR(PCINT1_vect) { endstop_ISR_worker(); }
+ ISR(PCINT1_vect) { endstop_ISR(); }
#endif
#ifdef PCINT2_vect
- ISR(PCINT2_vect) { endstop_ISR_worker(); }
+ ISR(PCINT2_vect) { endstop_ISR(); }
#endif
#ifdef PCINT3_vect
- ISR(PCINT3_vect) { endstop_ISR_worker(); }
+ ISR(PCINT3_vect) { endstop_ISR(); }
#endif
void setup_endstop_interrupts( void ) {
diff --git a/Marlin/endstops.cpp b/Marlin/endstops.cpp
index f51a11b8af..f122b27bbc 100644
--- a/Marlin/endstops.cpp
+++ b/Marlin/endstops.cpp
@@ -31,18 +31,29 @@
#include "stepper.h"
#include "ultralcd.h"
-// TEST_ENDSTOP: test the old and the current status of an endstop
-#define TEST_ENDSTOP(ENDSTOP) (TEST(current_endstop_bits & old_endstop_bits, ENDSTOP))
+#if ENABLED(ENDSTOP_INTERRUPTS_FEATURE)
+ #include "endstop_interrupts.h"
+#endif
+
+#if HAS_BED_PROBE
+ #define ENDSTOPS_ENABLED (enabled || z_probe_enabled)
+#else
+ #define ENDSTOPS_ENABLED enabled
+#endif
Endstops endstops;
// public:
bool Endstops::enabled, Endstops::enabled_globally; // Initialized by settings.load()
-volatile char Endstops::endstop_hit_bits; // use X_MIN, Y_MIN, Z_MIN and Z_MIN_PROBE as BIT value
+volatile uint8_t Endstops::hit_state;
-Endstops::esbits_t Endstops::current_endstop_bits = 0,
- Endstops::old_endstop_bits = 0;
+Endstops::esbits_t Endstops::live_state = 0;
+#if ENABLED(ENDSTOP_NOISE_FILTER)
+ Endstops::esbits_t Endstops::old_live_state,
+ Endstops::validated_live_state;
+ uint8_t Endstops::endstop_poll_count;
+#endif
#if HAS_BED_PROBE
volatile bool Endstops::z_probe_enabled = false;
@@ -169,10 +180,85 @@ void Endstops::init() {
#endif
#endif
+ #if ENABLED(ENDSTOP_INTERRUPTS_FEATURE)
+ setup_endstop_interrupts();
+ #endif
+
+ // Enable endstops
+ enable_globally(
+ #if ENABLED(ENDSTOPS_ALWAYS_ON_DEFAULT)
+ true
+ #else
+ false
+ #endif
+ );
+
} // Endstops::init
+// Called from ISR. A change was detected. Find out what happened!
+void Endstops::check_possible_change() { if (ENDSTOPS_ENABLED) update(); }
+
+// Called from ISR: Poll endstop state if required
+void Endstops::poll() {
+
+ #if ENABLED(PINS_DEBUGGING)
+ run_monitor(); // report changes in endstop status
+ #endif
+
+ #if DISABLED(ENDSTOP_INTERRUPTS_FEATURE) || ENABLED(ENDSTOP_NOISE_FILTER)
+ if (ENDSTOPS_ENABLED) update();
+ #endif
+}
+
+void Endstops::enable_globally(const bool onoff) {
+ enabled_globally = enabled = onoff;
+
+ #if ENABLED(ENDSTOP_INTERRUPTS_FEATURE)
+ if (onoff) update(); // If enabling, update state now
+ #endif
+}
+
+// Enable / disable endstop checking
+void Endstops::enable(const bool onoff) {
+ enabled = onoff;
+
+ #if ENABLED(ENDSTOP_INTERRUPTS_FEATURE)
+ if (onoff) update(); // If enabling, update state now
+ #endif
+}
+
+// Disable / Enable endstops based on ENSTOPS_ONLY_FOR_HOMING and global enable
+void Endstops::not_homing() {
+ enabled = enabled_globally;
+
+ #if ENABLED(ENDSTOP_INTERRUPTS_FEATURE)
+ if (enabled) update(); // If enabling, update state now
+ #endif
+}
+
+// Enable / disable endstop z-probe checking
+#if HAS_BED_PROBE
+ void Endstops::enable_z_probe(bool onoff) {
+ z_probe_enabled = onoff;
+
+ #if ENABLED(ENDSTOP_INTERRUPTS_FEATURE)
+ if (enabled) update(); // If enabling, update state now
+ #endif
+ }
+#endif
+
+#if ENABLED(PINS_DEBUGGING)
+ void Endstops::run_monitor() {
+ if (!monitor_flag) return;
+ static uint8_t monitor_count = 16; // offset this check from the others
+ monitor_count += _BV(1); // 15 Hz
+ monitor_count &= 0x7F;
+ if (!monitor_count) monitor(); // report changes in endstop status
+ }
+#endif
+
void Endstops::report_state() {
- if (endstop_hit_bits) {
+ if (hit_state) {
#if ENABLED(ULTRA_LCD)
char chrX = ' ', chrY = ' ', chrZ = ' ', chrP = ' ';
#define _SET_STOP_CHAR(A,C) (chr## A = C)
@@ -181,11 +267,11 @@ void Endstops::report_state() {
#endif
#define _ENDSTOP_HIT_ECHO(A,C) do{ \
- SERIAL_ECHOPAIR(" " STRINGIFY(A) ":", stepper.triggered_position_mm(A ##_AXIS)); \
+ SERIAL_ECHOPAIR(" " STRINGIFY(A) ":", planner.triggered_position_mm(_AXIS(A))); \
_SET_STOP_CHAR(A,C); }while(0)
#define _ENDSTOP_HIT_TEST(A,C) \
- if (TEST(endstop_hit_bits, A ##_MIN) || TEST(endstop_hit_bits, A ##_MAX)) \
+ if (TEST(hit_state, A ##_MIN) || TEST(hit_state, A ##_MAX)) \
_ENDSTOP_HIT_ECHO(A,C)
#define ENDSTOP_HIT_TEST_X() _ENDSTOP_HIT_TEST(X,'X')
@@ -200,7 +286,7 @@ void Endstops::report_state() {
#if ENABLED(Z_MIN_PROBE_ENDSTOP)
#define P_AXIS Z_AXIS
- if (TEST(endstop_hit_bits, Z_MIN_PROBE)) _ENDSTOP_HIT_ECHO(P, 'P');
+ if (TEST(hit_state, Z_MIN_PROBE)) _ENDSTOP_HIT_ECHO(P, 'P');
#endif
SERIAL_EOL();
@@ -211,7 +297,7 @@ void Endstops::report_state() {
hit_on_purpose();
#if ENABLED(ABORT_ON_ENDSTOP_HIT_FEATURE_ENABLED) && ENABLED(SDSUPPORT)
- if (stepper.abort_on_endstop_hit) {
+ if (planner.abort_on_endstop_hit) {
card.sdprinting = false;
card.closefile();
quickstop_stepper();
@@ -273,144 +359,29 @@ void Endstops::M119() {
#endif
} // Endstops::M119
-#if ENABLED(X_DUAL_ENDSTOPS)
- void Endstops::test_dual_x_endstops(const EndstopEnum es1, const EndstopEnum es2) {
- const byte x_test = TEST_ENDSTOP(es1) | (TEST_ENDSTOP(es2) << 1); // bit 0 for X, bit 1 for X2
- if (x_test && stepper.current_block->steps[X_AXIS] > 0) {
- SBI(endstop_hit_bits, X_MIN);
- if (!stepper.performing_homing || (x_test == 0x3)) //if not performing home or if both endstops were trigged during homing...
- stepper.kill_current_block();
- }
- }
-#endif
-#if ENABLED(Y_DUAL_ENDSTOPS)
- void Endstops::test_dual_y_endstops(const EndstopEnum es1, const EndstopEnum es2) {
- const byte y_test = TEST_ENDSTOP(es1) | (TEST_ENDSTOP(es2) << 1); // bit 0 for Y, bit 1 for Y2
- if (y_test && stepper.current_block->steps[Y_AXIS] > 0) {
- SBI(endstop_hit_bits, Y_MIN);
- if (!stepper.performing_homing || (y_test == 0x3)) //if not performing home or if both endstops were trigged during homing...
- stepper.kill_current_block();
- }
- }
-#endif
-#if ENABLED(Z_DUAL_ENDSTOPS)
- void Endstops::test_dual_z_endstops(const EndstopEnum es1, const EndstopEnum es2) {
- const byte z_test = TEST_ENDSTOP(es1) | (TEST_ENDSTOP(es2) << 1); // bit 0 for Z, bit 1 for Z2
- if (z_test && stepper.current_block->steps[Z_AXIS] > 0) {
- SBI(endstop_hit_bits, Z_MIN);
- if (!stepper.performing_homing || (z_test == 0x3)) //if not performing home or if both endstops were trigged during homing...
- stepper.kill_current_block();
- }
- }
-#endif
+// The following routines are called from an ISR context. It could be the temperature ISR, the
+// endstop ISR or the Stepper ISR.
-// Check endstops - Called from ISR!
+#define _ENDSTOP(AXIS, MINMAX) AXIS ##_## MINMAX
+#define _ENDSTOP_PIN(AXIS, MINMAX) AXIS ##_## MINMAX ##_PIN
+#define _ENDSTOP_INVERTING(AXIS, MINMAX) AXIS ##_## MINMAX ##_ENDSTOP_INVERTING
+
+// Check endstops - Could be called from ISR!
void Endstops::update() {
- #define _ENDSTOP(AXIS, MINMAX) AXIS ##_## MINMAX
- #define _ENDSTOP_PIN(AXIS, MINMAX) AXIS ##_## MINMAX ##_PIN
- #define _ENDSTOP_INVERTING(AXIS, MINMAX) AXIS ##_## MINMAX ##_ENDSTOP_INVERTING
- #define _ENDSTOP_HIT(AXIS, MINMAX) SBI(endstop_hit_bits, _ENDSTOP(AXIS, MINMAX))
-
- #define SET_BIT(N,B,TF) do{ if (TF) SBI(N,B); else CBI(N,B); }while(0)
// UPDATE_ENDSTOP_BIT: set the current endstop bits for an endstop to its status
- #define UPDATE_ENDSTOP_BIT(AXIS, MINMAX) SET_BIT(current_endstop_bits, _ENDSTOP(AXIS, MINMAX), (READ(_ENDSTOP_PIN(AXIS, MINMAX)) != _ENDSTOP_INVERTING(AXIS, MINMAX)))
+ #define UPDATE_ENDSTOP_BIT(AXIS, MINMAX) SET_BIT_TO(live_state, _ENDSTOP(AXIS, MINMAX), (READ(_ENDSTOP_PIN(AXIS, MINMAX)) != _ENDSTOP_INVERTING(AXIS, MINMAX)))
// COPY_BIT: copy the value of SRC_BIT to DST_BIT in DST
- #define COPY_BIT(DST, SRC_BIT, DST_BIT) SET_BIT(DST, DST_BIT, TEST(DST, SRC_BIT))
-
- #define UPDATE_ENDSTOP(AXIS,MINMAX) do { \
- UPDATE_ENDSTOP_BIT(AXIS, MINMAX); \
- if (TEST_ENDSTOP(_ENDSTOP(AXIS, MINMAX))) { \
- _ENDSTOP_HIT(AXIS, MINMAX); \
- stepper.endstop_triggered(_AXIS(AXIS)); \
- } \
- }while(0)
+ #define COPY_BIT(DST, SRC_BIT, DST_BIT) SET_BIT_TO(DST, DST_BIT, TEST(DST, SRC_BIT))
#if ENABLED(G38_PROBE_TARGET) && PIN_EXISTS(Z_MIN_PROBE) && !(CORE_IS_XY || CORE_IS_XZ)
// If G38 command is active check Z_MIN_PROBE for ALL movement
- if (G38_move) {
- UPDATE_ENDSTOP_BIT(Z, MIN_PROBE);
- if (TEST_ENDSTOP(_ENDSTOP(Z, MIN_PROBE))) {
- if (stepper.current_block->steps[_AXIS(X)] > 0) { _ENDSTOP_HIT(X, MIN); stepper.endstop_triggered(_AXIS(X)); }
- else if (stepper.current_block->steps[_AXIS(Y)] > 0) { _ENDSTOP_HIT(Y, MIN); stepper.endstop_triggered(_AXIS(Y)); }
- else if (stepper.current_block->steps[_AXIS(Z)] > 0) { _ENDSTOP_HIT(Z, MIN); stepper.endstop_triggered(_AXIS(Z)); }
- G38_endstop_hit = true;
- }
- }
- #endif
-
- /**
- * Define conditions for checking endstops
- */
-
- #if IS_CORE
- #define S_(N) stepper.current_block->steps[CORE_AXIS_##N]
- #define D_(N) stepper.motor_direction(CORE_AXIS_##N)
- #endif
-
- #if CORE_IS_XY || CORE_IS_XZ
- /**
- * Head direction in -X axis for CoreXY and CoreXZ bots.
- *
- * If steps differ, both axes are moving.
- * If DeltaA == -DeltaB, the movement is only in the 2nd axis (Y or Z, handled below)
- * If DeltaA == DeltaB, the movement is only in the 1st axis (X)
- */
- #if ENABLED(COREXY) || ENABLED(COREXZ)
- #define X_CMP ==
- #else
- #define X_CMP !=
- #endif
- #define X_MOVE_TEST ( S_(1) != S_(2) || (S_(1) > 0 && D_(1) X_CMP D_(2)) )
- #define X_AXIS_HEAD X_HEAD
- #else
- #define X_MOVE_TEST stepper.current_block->steps[X_AXIS] > 0
- #define X_AXIS_HEAD X_AXIS
- #endif
-
- #if CORE_IS_XY || CORE_IS_YZ
- /**
- * Head direction in -Y axis for CoreXY / CoreYZ bots.
- *
- * If steps differ, both axes are moving
- * If DeltaA == DeltaB, the movement is only in the 1st axis (X or Y)
- * If DeltaA == -DeltaB, the movement is only in the 2nd axis (Y or Z)
- */
- #if ENABLED(COREYX) || ENABLED(COREYZ)
- #define Y_CMP ==
- #else
- #define Y_CMP !=
- #endif
- #define Y_MOVE_TEST ( S_(1) != S_(2) || (S_(1) > 0 && D_(1) Y_CMP D_(2)) )
- #define Y_AXIS_HEAD Y_HEAD
- #else
- #define Y_MOVE_TEST stepper.current_block->steps[Y_AXIS] > 0
- #define Y_AXIS_HEAD Y_AXIS
- #endif
-
- #if CORE_IS_XZ || CORE_IS_YZ
- /**
- * Head direction in -Z axis for CoreXZ or CoreYZ bots.
- *
- * If steps differ, both axes are moving
- * If DeltaA == DeltaB, the movement is only in the 1st axis (X or Y, already handled above)
- * If DeltaA == -DeltaB, the movement is only in the 2nd axis (Z)
- */
- #if ENABLED(COREZX) || ENABLED(COREZY)
- #define Z_CMP ==
- #else
- #define Z_CMP !=
- #endif
- #define Z_MOVE_TEST ( S_(1) != S_(2) || (S_(1) > 0 && D_(1) Z_CMP D_(2)) )
- #define Z_AXIS_HEAD Z_HEAD
- #else
- #define Z_MOVE_TEST stepper.current_block->steps[Z_AXIS] > 0
- #define Z_AXIS_HEAD Z_AXIS
+ if (G38_move) UPDATE_ENDSTOP_BIT(Z, MIN_PROBE);
#endif
// With Dual X, endstops are only checked in the homing direction for the active extruder
#if ENABLED(DUAL_X_CARRIAGE)
- #define E0_ACTIVE stepper.current_block->active_extruder == 0
+ #define E0_ACTIVE stepper.movement_extruder() == 0
#define X_MIN_TEST ((X_HOME_DIR < 0 && E0_ACTIVE) || (X2_HOME_DIR < 0 && !E0_ACTIVE))
#define X_MAX_TEST ((X_HOME_DIR > 0 && E0_ACTIVE) || (X2_HOME_DIR > 0 && !E0_ACTIVE))
#else
@@ -418,127 +389,376 @@ void Endstops::update() {
#define X_MAX_TEST true
#endif
+ // Use HEAD for core axes, AXIS for others
+ #if CORE_IS_XY || CORE_IS_XZ
+ #define X_AXIS_HEAD X_HEAD
+ #else
+ #define X_AXIS_HEAD X_AXIS
+ #endif
+ #if CORE_IS_XY || CORE_IS_YZ
+ #define Y_AXIS_HEAD Y_HEAD
+ #else
+ #define Y_AXIS_HEAD Y_AXIS
+ #endif
+ #if CORE_IS_XZ || CORE_IS_YZ
+ #define Z_AXIS_HEAD Z_HEAD
+ #else
+ #define Z_AXIS_HEAD Z_AXIS
+ #endif
+
/**
* Check and update endstops according to conditions
*/
- if (stepper.current_block) {
-
- if (X_MOVE_TEST) {
- if (stepper.motor_direction(X_AXIS_HEAD)) { // -direction
- #if HAS_X_MIN
- #if ENABLED(X_DUAL_ENDSTOPS)
- UPDATE_ENDSTOP_BIT(X, MIN);
- #if HAS_X2_MIN
- UPDATE_ENDSTOP_BIT(X2, MIN);
- #else
- COPY_BIT(current_endstop_bits, X_MIN, X2_MIN);
- #endif
- test_dual_x_endstops(X_MIN, X2_MIN);
+ if (stepper.axis_is_moving(X_AXIS)) {
+ if (stepper.motor_direction(X_AXIS_HEAD)) { // -direction
+ #if HAS_X_MIN
+ #if ENABLED(X_DUAL_ENDSTOPS)
+ UPDATE_ENDSTOP_BIT(X, MIN);
+ #if HAS_X2_MIN
+ UPDATE_ENDSTOP_BIT(X2, MIN);
#else
- if (X_MIN_TEST) UPDATE_ENDSTOP(X, MIN);
+ COPY_BIT(live_state, X_MIN, X2_MIN);
#endif
+ #else
+ if (X_MIN_TEST) UPDATE_ENDSTOP_BIT(X, MIN);
#endif
- }
- else { // +direction
- #if HAS_X_MAX
- #if ENABLED(X_DUAL_ENDSTOPS)
- UPDATE_ENDSTOP_BIT(X, MAX);
- #if HAS_X2_MAX
- UPDATE_ENDSTOP_BIT(X2, MAX);
- #else
- COPY_BIT(current_endstop_bits, X_MAX, X2_MAX);
- #endif
- test_dual_x_endstops(X_MAX, X2_MAX);
- #else
- if (X_MAX_TEST) UPDATE_ENDSTOP(X, MAX);
- #endif
- #endif
- }
+ #endif
}
-
- if (Y_MOVE_TEST) {
- if (stepper.motor_direction(Y_AXIS_HEAD)) { // -direction
- #if HAS_Y_MIN
- #if ENABLED(Y_DUAL_ENDSTOPS)
- UPDATE_ENDSTOP_BIT(Y, MIN);
- #if HAS_Y2_MIN
- UPDATE_ENDSTOP_BIT(Y2, MIN);
- #else
- COPY_BIT(current_endstop_bits, Y_MIN, Y2_MIN);
- #endif
- test_dual_y_endstops(Y_MIN, Y2_MIN);
+ else { // +direction
+ #if HAS_X_MAX
+ #if ENABLED(X_DUAL_ENDSTOPS)
+ UPDATE_ENDSTOP_BIT(X, MAX);
+ #if HAS_X2_MAX
+ UPDATE_ENDSTOP_BIT(X2, MAX);
#else
- UPDATE_ENDSTOP(Y, MIN);
+ COPY_BIT(live_state, X_MAX, X2_MAX);
#endif
+ #else
+ if (X_MAX_TEST) UPDATE_ENDSTOP_BIT(X, MAX);
#endif
- }
- else { // +direction
- #if HAS_Y_MAX
- #if ENABLED(Y_DUAL_ENDSTOPS)
- UPDATE_ENDSTOP_BIT(Y, MAX);
- #if HAS_Y2_MAX
- UPDATE_ENDSTOP_BIT(Y2, MAX);
- #else
- COPY_BIT(current_endstop_bits, Y_MAX, Y2_MAX);
- #endif
- test_dual_y_endstops(Y_MAX, Y2_MAX);
- #else
- UPDATE_ENDSTOP(Y, MAX);
- #endif
- #endif
- }
+ #endif
}
+ }
- if (Z_MOVE_TEST) {
- if (stepper.motor_direction(Z_AXIS_HEAD)) { // Z -direction. Gantry down, bed up.
- #if HAS_Z_MIN
- #if ENABLED(Z_DUAL_ENDSTOPS)
+ if (stepper.axis_is_moving(Y_AXIS)) {
+ if (stepper.motor_direction(Y_AXIS_HEAD)) { // -direction
+ #if HAS_Y_MIN
+ #if ENABLED(Y_DUAL_ENDSTOPS)
+ UPDATE_ENDSTOP_BIT(Y, MIN);
+ #if HAS_Y2_MIN
+ UPDATE_ENDSTOP_BIT(Y2, MIN);
+ #else
+ COPY_BIT(live_state, Y_MIN, Y2_MIN);
+ #endif
+ #else
+ UPDATE_ENDSTOP_BIT(Y, MIN);
+ #endif
+ #endif
+ }
+ else { // +direction
+ #if HAS_Y_MAX
+ #if ENABLED(Y_DUAL_ENDSTOPS)
+ UPDATE_ENDSTOP_BIT(Y, MAX);
+ #if HAS_Y2_MAX
+ UPDATE_ENDSTOP_BIT(Y2, MAX);
+ #else
+ COPY_BIT(live_state, Y_MAX, Y2_MAX);
+ #endif
+ #else
+ UPDATE_ENDSTOP_BIT(Y, MAX);
+ #endif
+ #endif
+ }
+ }
+
+ if (stepper.axis_is_moving(Z_AXIS)) {
+ if (stepper.motor_direction(Z_AXIS_HEAD)) { // Z -direction. Gantry down, bed up.
+ #if HAS_Z_MIN
+ #if ENABLED(Z_DUAL_ENDSTOPS)
+ UPDATE_ENDSTOP_BIT(Z, MIN);
+ #if HAS_Z2_MIN
+ UPDATE_ENDSTOP_BIT(Z2, MIN);
+ #else
+ COPY_BIT(live_state, Z_MIN, Z2_MIN);
+ #endif
+ #else
+ #if ENABLED(Z_MIN_PROBE_USES_Z_MIN_ENDSTOP_PIN)
+ if (z_probe_enabled) UPDATE_ENDSTOP_BIT(Z, MIN);
+ #else
UPDATE_ENDSTOP_BIT(Z, MIN);
- #if HAS_Z2_MIN
- UPDATE_ENDSTOP_BIT(Z2, MIN);
- #else
- COPY_BIT(current_endstop_bits, Z_MIN, Z2_MIN);
- #endif
- test_dual_z_endstops(Z_MIN, Z2_MIN);
- #else
- #if ENABLED(Z_MIN_PROBE_USES_Z_MIN_ENDSTOP_PIN)
- if (z_probe_enabled) UPDATE_ENDSTOP(Z, MIN);
- #else
- UPDATE_ENDSTOP(Z, MIN);
- #endif
#endif
#endif
+ #endif
- // When closing the gap check the enabled probe
- #if ENABLED(Z_MIN_PROBE_ENDSTOP)
- if (z_probe_enabled) {
- UPDATE_ENDSTOP(Z, MIN_PROBE);
- if (TEST_ENDSTOP(Z_MIN_PROBE)) SBI(endstop_hit_bits, Z_MIN_PROBE);
- }
+ // When closing the gap check the enabled probe
+ #if ENABLED(Z_MIN_PROBE_ENDSTOP)
+ if (z_probe_enabled) UPDATE_ENDSTOP_BIT(Z, MIN_PROBE);
+ #endif
+ }
+ else { // Z +direction. Gantry up, bed down.
+ #if HAS_Z_MAX
+ // Check both Z dual endstops
+ #if ENABLED(Z_DUAL_ENDSTOPS)
+ UPDATE_ENDSTOP_BIT(Z, MAX);
+ #if HAS_Z2_MAX
+ UPDATE_ENDSTOP_BIT(Z2, MAX);
+ #else
+ COPY_BIT(live_state, Z_MAX, Z2_MAX);
+ #endif
+ // If this pin is not hijacked for the bed probe
+ // then it belongs to the Z endstop
+ #elif DISABLED(Z_MIN_PROBE_ENDSTOP) || Z_MAX_PIN != Z_MIN_PROBE_PIN
+ UPDATE_ENDSTOP_BIT(Z, MAX);
#endif
+ #endif
+ }
+ }
+
+ // All endstops were updated.
+ #if ENABLED(ENDSTOP_NOISE_FILTER)
+ if (old_live_state != live_state) { // We detected a change. Reinit the timeout
+ /**
+ * Filtering out noise on endstops requires a delayed decision. Let's assume, due to noise,
+ * that 50% of endstop signal samples are good and 50% are bad (assuming normal distribution
+ * of random noise). Then the first sample has a 50% chance to be good or bad. The 2nd sample
+ * also has a 50% chance to be good or bad. The chances of 2 samples both being bad becomes
+ * 50% of 50%, or 25%. That was the previous implementation of Marlin endstop handling. It
+ * reduces chances of bad readings in half, at the cost of 1 extra sample period, but chances
+ * still exist. The only way to reduce them further is to increase the number of samples.
+ * To reduce the chance to 1% (1/128th) requires 7 samples (adding 7ms of delay).
+ */
+ endstop_poll_count = 7;
+ old_live_state = live_state;
+ }
+ else if (endstop_poll_count && !--endstop_poll_count)
+ validated_live_state = live_state;
+
+ #else
+
+ // Lets accept the new endstop values as valid - We assume hardware filtering of lines
+ esbits_t validated_live_state = live_state;
+
+ #endif
+
+ // Endstop readings are validated in validated_live_state
+
+ // Test the current status of an endstop
+ #define TEST_ENDSTOP(ENDSTOP) (TEST(validated_live_state, ENDSTOP))
+
+ // Record endstop was hit
+ #define _ENDSTOP_HIT(AXIS, MINMAX) SBI(hit_state, _ENDSTOP(AXIS, MINMAX))
+
+ // Call the endstop triggered routine for single endstops
+ #define PROCESS_ENDSTOP(AXIS,MINMAX) do { \
+ if (TEST_ENDSTOP(_ENDSTOP(AXIS, MINMAX))) { \
+ _ENDSTOP_HIT(AXIS, MINMAX); \
+ planner.endstop_triggered(_AXIS(AXIS)); \
+ } \
+ }while(0)
+
+ // Call the endstop triggered routine for single endstops
+ #define PROCESS_DUAL_ENDSTOP(AXIS1, AXIS2, MINMAX) do { \
+ if (TEST_ENDSTOP(_ENDSTOP(AXIS1, MINMAX)) || TEST_ENDSTOP(_ENDSTOP(AXIS2, MINMAX))) { \
+ _ENDSTOP_HIT(AXIS1, MINMAX); \
+ planner.endstop_triggered(_AXIS(AXIS1)); \
+ } \
+ }while(0)
+
+ #if ENABLED(G38_PROBE_TARGET) && PIN_EXISTS(Z_MIN_PROBE) && !(CORE_IS_XY || CORE_IS_XZ)
+ // If G38 command is active check Z_MIN_PROBE for ALL movement
+ if (G38_move) {
+ if (TEST_ENDSTOP(_ENDSTOP(Z, MIN_PROBE))) {
+ if (stepper.axis_is_moving(X_AXIS)) { _ENDSTOP_HIT(X, MIN); planner.endstop_triggered(X_AXIS); }
+ else if (stepper.axis_is_moving(Y_AXIS)) { _ENDSTOP_HIT(Y, MIN); planner.endstop_triggered(Y_AXIS); }
+ else if (stepper.axis_is_moving(Z_AXIS)) { _ENDSTOP_HIT(Z, MIN); planner.endstop_triggered(Z_AXIS); }
+ G38_endstop_hit = true;
}
- else { // Z +direction. Gantry up, bed down.
- #if HAS_Z_MAX
- // Check both Z dual endstops
- #if ENABLED(Z_DUAL_ENDSTOPS)
- UPDATE_ENDSTOP_BIT(Z, MAX);
- #if HAS_Z2_MAX
- UPDATE_ENDSTOP_BIT(Z2, MAX);
- #else
- COPY_BIT(current_endstop_bits, Z_MAX, Z2_MAX);
- #endif
- test_dual_z_endstops(Z_MAX, Z2_MAX);
+ }
+ #endif
+
+ // Now, we must signal, after validation, if an endstop limit is pressed or not
+ if (stepper.axis_is_moving(X_AXIS)) {
+ if (stepper.motor_direction(X_AXIS_HEAD)) { // -direction
+ #if HAS_X_MIN
+ #if ENABLED(X_DUAL_ENDSTOPS)
+ PROCESS_DUAL_ENDSTOP(X, X2, MIN);
+ #else
+ if (X_MIN_TEST) PROCESS_ENDSTOP(X, MIN);
+ #endif
+ #endif
+ }
+ else { // +direction
+ #if HAS_X_MAX
+ #if ENABLED(X_DUAL_ENDSTOPS)
+ PROCESS_DUAL_ENDSTOP(X, X2, MAX);
+ #else
+ if (X_MAX_TEST) PROCESS_ENDSTOP(X, MAX);
+ #endif
+ #endif
+ }
+ }
+
+ if (stepper.axis_is_moving(Y_AXIS)) {
+ if (stepper.motor_direction(Y_AXIS_HEAD)) { // -direction
+ #if HAS_Y_MIN
+ #if ENABLED(Y_DUAL_ENDSTOPS)
+ PROCESS_DUAL_ENDSTOP(Y, Y2, MIN);
+ #else
+ PROCESS_ENDSTOP(Y, MIN);
+ #endif
+ #endif
+ }
+ else { // +direction
+ #if HAS_Y_MAX
+ #if ENABLED(Y_DUAL_ENDSTOPS)
+ PROCESS_DUAL_ENDSTOP(Y, Y2, MAX);
+ #else
+ PROCESS_ENDSTOP(Y, MAX);
+ #endif
+ #endif
+ }
+ }
+
+ if (stepper.axis_is_moving(Z_AXIS)) {
+ if (stepper.motor_direction(Z_AXIS_HEAD)) { // Z -direction. Gantry down, bed up.
+ #if HAS_Z_MIN
+ #if ENABLED(Z_DUAL_ENDSTOPS)
+ PROCESS_DUAL_ENDSTOP(Z, Z2, MIN);
+ #else
+ #if ENABLED(Z_MIN_PROBE_USES_Z_MIN_ENDSTOP_PIN)
+ if (z_probe_enabled) PROCESS_ENDSTOP(Z, MIN);
+ #else
+ PROCESS_ENDSTOP(Z, MIN);
+ #endif
+ #endif
+ #endif
+
+ // When closing the gap check the enabled probe
+ #if ENABLED(Z_MIN_PROBE_ENDSTOP)
+ if (z_probe_enabled) PROCESS_ENDSTOP(Z, MIN_PROBE);
+ #endif
+ }
+ else { // Z +direction. Gantry up, bed down.
+ #if HAS_Z_MAX
+ #if ENABLED(Z_DUAL_ENDSTOPS)
+ PROCESS_DUAL_ENDSTOP(Z, Z2, MAX);
+ #elif DISABLED(Z_MIN_PROBE_ENDSTOP) || Z_MAX_PIN != Z_MIN_PROBE_PIN
// If this pin is not hijacked for the bed probe
// then it belongs to the Z endstop
- #elif DISABLED(Z_MIN_PROBE_ENDSTOP) || Z_MAX_PIN != Z_MIN_PROBE_PIN
- UPDATE_ENDSTOP(Z, MAX);
- #endif
+ PROCESS_ENDSTOP(Z, MAX);
#endif
- }
+ #endif
}
-
- } // stepper.current_block
-
- old_endstop_bits = current_endstop_bits;
-
+ }
} // Endstops::update()
+
+#if ENABLED(PINS_DEBUGGING)
+
+ bool Endstops::monitor_flag = false;
+
+ /**
+ * monitors endstops & Z probe for changes
+ *
+ * If a change is detected then the LED is toggled and
+ * a message is sent out the serial port
+ *
+ * Yes, we could miss a rapid back & forth change but
+ * that won't matter because this is all manual.
+ *
+ */
+ void Endstops::monitor() {
+
+ static uint16_t old_live_state_local = 0;
+ static uint8_t local_LED_status = 0;
+ uint16_t live_state_local = 0;
+
+ #if HAS_X_MIN
+ if (READ(X_MIN_PIN)) SBI(live_state_local, X_MIN);
+ #endif
+ #if HAS_X_MAX
+ if (READ(X_MAX_PIN)) SBI(live_state_local, X_MAX);
+ #endif
+ #if HAS_Y_MIN
+ if (READ(Y_MIN_PIN)) SBI(live_state_local, Y_MIN);
+ #endif
+ #if HAS_Y_MAX
+ if (READ(Y_MAX_PIN)) SBI(live_state_local, Y_MAX);
+ #endif
+ #if HAS_Z_MIN
+ if (READ(Z_MIN_PIN)) SBI(live_state_local, Z_MIN);
+ #endif
+ #if HAS_Z_MAX
+ if (READ(Z_MAX_PIN)) SBI(live_state_local, Z_MAX);
+ #endif
+ #if HAS_Z_MIN_PROBE_PIN
+ if (READ(Z_MIN_PROBE_PIN)) SBI(live_state_local, Z_MIN_PROBE);
+ #endif
+ #if HAS_X2_MIN
+ if (READ(X2_MIN_PIN)) SBI(live_state_local, X2_MIN);
+ #endif
+ #if HAS_X2_MAX
+ if (READ(X2_MAX_PIN)) SBI(live_state_local, X2_MAX);
+ #endif
+ #if HAS_Y2_MIN
+ if (READ(Y2_MIN_PIN)) SBI(live_state_local, Y2_MIN);
+ #endif
+ #if HAS_Y2_MAX
+ if (READ(Y2_MAX_PIN)) SBI(live_state_local, Y2_MAX);
+ #endif
+ #if HAS_Z2_MIN
+ if (READ(Z2_MIN_PIN)) SBI(live_state_local, Z2_MIN);
+ #endif
+ #if HAS_Z2_MAX
+ if (READ(Z2_MAX_PIN)) SBI(live_state_local, Z2_MAX);
+ #endif
+
+ uint16_t endstop_change = live_state_local ^ old_live_state_local;
+
+ if (endstop_change) {
+ #if HAS_X_MIN
+ if (TEST(endstop_change, X_MIN)) SERIAL_PROTOCOLPAIR(" X_MIN:", TEST(live_state_local, X_MIN));
+ #endif
+ #if HAS_X_MAX
+ if (TEST(endstop_change, X_MAX)) SERIAL_PROTOCOLPAIR(" X_MAX:", TEST(live_state_local, X_MAX));
+ #endif
+ #if HAS_Y_MIN
+ if (TEST(endstop_change, Y_MIN)) SERIAL_PROTOCOLPAIR(" Y_MIN:", TEST(live_state_local, Y_MIN));
+ #endif
+ #if HAS_Y_MAX
+ if (TEST(endstop_change, Y_MAX)) SERIAL_PROTOCOLPAIR(" Y_MAX:", TEST(live_state_local, Y_MAX));
+ #endif
+ #if HAS_Z_MIN
+ if (TEST(endstop_change, Z_MIN)) SERIAL_PROTOCOLPAIR(" Z_MIN:", TEST(live_state_local, Z_MIN));
+ #endif
+ #if HAS_Z_MAX
+ if (TEST(endstop_change, Z_MAX)) SERIAL_PROTOCOLPAIR(" Z_MAX:", TEST(live_state_local, Z_MAX));
+ #endif
+ #if HAS_Z_MIN_PROBE_PIN
+ if (TEST(endstop_change, Z_MIN_PROBE)) SERIAL_PROTOCOLPAIR(" PROBE:", TEST(live_state_local, Z_MIN_PROBE));
+ #endif
+ #if HAS_X2_MIN
+ if (TEST(endstop_change, X2_MIN)) SERIAL_PROTOCOLPAIR(" X2_MIN:", TEST(live_state_local, X2_MIN));
+ #endif
+ #if HAS_X2_MAX
+ if (TEST(endstop_change, X2_MAX)) SERIAL_PROTOCOLPAIR(" X2_MAX:", TEST(live_state_local, X2_MAX));
+ #endif
+ #if HAS_Y2_MIN
+ if (TEST(endstop_change, Y2_MIN)) SERIAL_PROTOCOLPAIR(" Y2_MIN:", TEST(live_state_local, Y2_MIN));
+ #endif
+ #if HAS_Y2_MAX
+ if (TEST(endstop_change, Y2_MAX)) SERIAL_PROTOCOLPAIR(" Y2_MAX:", TEST(live_state_local, Y2_MAX));
+ #endif
+ #if HAS_Z2_MIN
+ if (TEST(endstop_change, Z2_MIN)) SERIAL_PROTOCOLPAIR(" Z2_MIN:", TEST(live_state_local, Z2_MIN));
+ #endif
+ #if HAS_Z2_MAX
+ if (TEST(endstop_change, Z2_MAX)) SERIAL_PROTOCOLPAIR(" Z2_MAX:", TEST(live_state_local, Z2_MAX));
+ #endif
+ SERIAL_PROTOCOLPGM("\n\n");
+ analogWrite(LED_PIN, local_LED_status);
+ local_LED_status ^= 255;
+ old_live_state_local = live_state_local;
+ }
+ }
+
+#endif // PINS_DEBUGGING
diff --git a/Marlin/endstops.h b/Marlin/endstops.h
index 96cb3d089c..8f38992d7d 100644
--- a/Marlin/endstops.h
+++ b/Marlin/endstops.h
@@ -27,15 +27,29 @@
#ifndef __ENDSTOPS_H__
#define __ENDSTOPS_H__
-#include "enum.h"
#include "MarlinConfig.h"
+enum EndstopEnum : char {
+ X_MIN,
+ Y_MIN,
+ Z_MIN,
+ Z_MIN_PROBE,
+ X_MAX,
+ Y_MAX,
+ Z_MAX,
+ X2_MIN,
+ X2_MAX,
+ Y2_MIN,
+ Y2_MAX,
+ Z2_MIN,
+ Z2_MAX
+};
+
class Endstops {
public:
static bool enabled, enabled_globally;
- static volatile char endstop_hit_bits; // use X_MIN, Y_MIN, Z_MIN and Z_MIN_PROBE as BIT value
#if ENABLED(X_DUAL_ENDSTOPS) || ENABLED(Y_DUAL_ENDSTOPS) || ENABLED(Z_DUAL_ENDSTOPS)
typedef uint16_t esbits_t;
@@ -49,35 +63,56 @@ class Endstops {
static float z_endstop_adj;
#endif
#else
- typedef byte esbits_t;
+ typedef uint8_t esbits_t;
#endif
- static esbits_t current_endstop_bits, old_endstop_bits;
+ private:
+ static esbits_t live_state;
+ static volatile uint8_t hit_state; // Use X_MIN, Y_MIN, Z_MIN and Z_MIN_PROBE as BIT index
+ #if ENABLED(ENDSTOP_NOISE_FILTER)
+ static esbits_t old_live_state, // Old endstop value for debouncing and denoising
+ validated_live_state; // The validated (accepted as true) endstop bits
+ static uint8_t endstop_poll_count; // Countdown from threshold for polling
+ #endif
- Endstops() {
- enable_globally(
- #if ENABLED(ENDSTOPS_ALWAYS_ON_DEFAULT)
- true
- #else
- false
- #endif
- );
- };
+ public:
+ Endstops() {};
/**
* Initialize the endstop pins
*/
static void init();
+ /**
+ * A change was detected or presumed to be in endstops pins. Find out what
+ * changed, if anything. Called from ISR contexts
+ */
+ static void check_possible_change();
+
+ /**
+ * Periodic call to poll endstops if required. Called from temperature ISR
+ */
+ static void poll();
+
/**
* Update the endstops bits from the pins
*/
static void update();
/**
- * Print an error message reporting the position when the endstops were last hit.
+ * Get Endstop hit state.
*/
- static void report_state(); //call from somewhere to create an serial error message with the locations the endstops where hit, in case they were triggered
+ FORCE_INLINE static uint8_t trigger_state() { return hit_state; }
+
+ /**
+ * Get current endstops state
+ */
+ FORCE_INLINE static esbits_t state() { return live_state; }
+
+ /**
+ * Report endstop hits to serial. Called from loop().
+ */
+ static void report_state();
/**
* Report endstop positions in response to M119
@@ -85,42 +120,31 @@ class Endstops {
static void M119();
// Enable / disable endstop checking globally
- static void enable_globally(bool onoff=true) { enabled_globally = enabled = onoff; }
+ static void enable_globally(const bool onoff=true);
// Enable / disable endstop checking
- static void enable(bool onoff=true) { enabled = onoff; }
+ static void enable(const bool onoff=true);
// Disable / Enable endstops based on ENSTOPS_ONLY_FOR_HOMING and global enable
- static void not_homing() { enabled = enabled_globally; }
+ static void not_homing();
// Clear endstops (i.e., they were hit intentionally) to suppress the report
- static void hit_on_purpose() { endstop_hit_bits = 0; }
+ FORCE_INLINE static void hit_on_purpose() { hit_state = 0; }
// Enable / disable endstop z-probe checking
#if HAS_BED_PROBE
static volatile bool z_probe_enabled;
- static void enable_z_probe(bool onoff=true) { z_probe_enabled = onoff; }
+ static void enable_z_probe(bool onoff=true);
#endif
- private:
-
- #if ENABLED(X_DUAL_ENDSTOPS)
- static void test_dual_x_endstops(const EndstopEnum es1, const EndstopEnum es2);
- #endif
- #if ENABLED(Y_DUAL_ENDSTOPS)
- static void test_dual_y_endstops(const EndstopEnum es1, const EndstopEnum es2);
- #endif
- #if ENABLED(Z_DUAL_ENDSTOPS)
- static void test_dual_z_endstops(const EndstopEnum es1, const EndstopEnum es2);
+ // Debugging of endstops
+ #if ENABLED(PINS_DEBUGGING)
+ static bool monitor_flag;
+ static void monitor();
+ static void run_monitor();
#endif
};
extern Endstops endstops;
-#if HAS_BED_PROBE
- #define ENDSTOPS_ENABLED (endstops.enabled || endstops.z_probe_enabled)
-#else
- #define ENDSTOPS_ENABLED endstops.enabled
-#endif
-
#endif // __ENDSTOPS_H__
diff --git a/Marlin/enum.h b/Marlin/enum.h
index 378e47f320..d525e8ee9b 100644
--- a/Marlin/enum.h
+++ b/Marlin/enum.h
@@ -28,10 +28,9 @@
/**
* Axis indices as enumerated constants
*
- * Special axis:
- * - A_AXIS and B_AXIS are used by COREXY printers
- * - X_HEAD and Y_HEAD is used for systems that don't have a 1:1 relationship
- * between X_AXIS and X Head movement, like CoreXY bots
+ * - X_AXIS, Y_AXIS, and Z_AXIS should be used for axes in Cartesian space
+ * - A_AXIS, B_AXIS, and C_AXIS should be used for Steppers, corresponding to XYZ on Cartesians
+ * - X_HEAD, Y_HEAD, and Z_HEAD should be used for Steppers on Core kinematics
*/
enum AxisEnum : unsigned char {
X_AXIS = 0,
@@ -88,22 +87,6 @@ enum DebugFlags : unsigned char {
DEBUG_ALL = 0xFF
};
-enum EndstopEnum : char {
- X_MIN,
- Y_MIN,
- Z_MIN,
- Z_MIN_PROBE,
- X_MAX,
- Y_MAX,
- Z_MAX,
- X2_MIN,
- X2_MAX,
- Y2_MIN,
- Y2_MAX,
- Z2_MIN,
- Z2_MAX
-};
-
#if ENABLED(ADVANCED_PAUSE_FEATURE)
enum AdvancedPauseMenuResponse : char {
ADVANCED_PAUSE_RESPONSE_WAIT_FOR,
diff --git a/Marlin/fastio.h b/Marlin/fastio.h
index 03a3a90366..839db99293 100644
--- a/Marlin/fastio.h
+++ b/Marlin/fastio.h
@@ -28,7 +28,6 @@
#include
-typedef int8_t pin_t;
#ifndef _FASTIO_ARDUINO_H_
#define _FASTIO_ARDUINO_H_
diff --git a/Marlin/least_squares_fit.cpp b/Marlin/least_squares_fit.cpp
index 66821ce58f..9e59804f09 100644
--- a/Marlin/least_squares_fit.cpp
+++ b/Marlin/least_squares_fit.cpp
@@ -59,7 +59,7 @@ int finish_incremental_LSF(struct linear_fit_data *lsf) {
lsf->xzbar = lsf->xzbar / N - lsf->xbar * lsf->zbar;
const float DD = lsf->x2bar * lsf->y2bar - sq(lsf->xybar);
- if (FABS(DD) <= 1e-10 * (lsf->max_absx + lsf->max_absy))
+ if (ABS(DD) <= 1e-10 * (lsf->max_absx + lsf->max_absy))
return 1;
lsf->A = (lsf->yzbar * lsf->xybar - lsf->xzbar * lsf->y2bar) / DD;
diff --git a/Marlin/least_squares_fit.h b/Marlin/least_squares_fit.h
index 9ed923ab49..68aa62b9c5 100644
--- a/Marlin/least_squares_fit.h
+++ b/Marlin/least_squares_fit.h
@@ -65,8 +65,8 @@ void inline incremental_WLSF(struct linear_fit_data *lsf, const float &x, const
lsf->xzbar += w * x * z;
lsf->yzbar += w * y * z;
lsf->N += w;
- lsf->max_absx = max(FABS(w * x), lsf->max_absx);
- lsf->max_absy = max(FABS(w * y), lsf->max_absy);
+ lsf->max_absx = MAX(ABS(w * x), lsf->max_absx);
+ lsf->max_absy = MAX(ABS(w * y), lsf->max_absy);
}
void inline incremental_LSF(struct linear_fit_data *lsf, const float &x, const float &y, const float &z) {
@@ -79,8 +79,8 @@ void inline incremental_LSF(struct linear_fit_data *lsf, const float &x, const f
lsf->xybar += x * y;
lsf->xzbar += x * z;
lsf->yzbar += y * z;
- lsf->max_absx = max(FABS(x), lsf->max_absx);
- lsf->max_absy = max(FABS(y), lsf->max_absy);
+ lsf->max_absx = MAX(ABS(x), lsf->max_absx);
+ lsf->max_absy = MAX(ABS(y), lsf->max_absy);
lsf->N += 1.0;
}
diff --git a/Marlin/macros.h b/Marlin/macros.h
index 74dd68ec6b..4d9865a57e 100644
--- a/Marlin/macros.h
+++ b/Marlin/macros.h
@@ -30,7 +30,7 @@
#define XYZ 3
// For use in macros that take a single axis letter
-#define _AXIS(AXIS) AXIS ##_AXIS
+#define _AXIS(A) (A##_AXIS)
#define _XMIN_ 100
#define _YMIN_ 200
@@ -47,68 +47,12 @@
#define _O2 __attribute__((optimize("O2")))
#define _O3 __attribute__((optimize("O3")))
-// Bracket code that shouldn't be interrupted
-#ifndef CRITICAL_SECTION_START
- #define CRITICAL_SECTION_START unsigned char _sreg = SREG; cli();
- #define CRITICAL_SECTION_END SREG = _sreg;
-#endif
-
// Clock speed factors
#define CYCLES_PER_MICROSECOND (F_CPU / 1000000L) // 16 or 20
#define INT0_PRESCALER 8
-// Processor-level delays for hardware interfaces
-#ifndef _NOP
- #define _NOP() do { __asm__ volatile ("nop"); } while (0)
-#endif
-#define DELAY_NOPS(X) \
- switch (X) { \
- case 20: _NOP(); case 19: _NOP(); case 18: _NOP(); case 17: _NOP(); \
- case 16: _NOP(); case 15: _NOP(); case 14: _NOP(); case 13: _NOP(); \
- case 12: _NOP(); case 11: _NOP(); case 10: _NOP(); case 9: _NOP(); \
- case 8: _NOP(); case 7: _NOP(); case 6: _NOP(); case 5: _NOP(); \
- case 4: _NOP(); case 3: _NOP(); case 2: _NOP(); case 1: _NOP(); \
- }
-#define DELAY_0_NOP NOOP
-#define DELAY_1_NOP DELAY_NOPS( 1)
-#define DELAY_2_NOP DELAY_NOPS( 2)
-#define DELAY_3_NOP DELAY_NOPS( 3)
-#define DELAY_4_NOP DELAY_NOPS( 4)
-#define DELAY_5_NOP DELAY_NOPS( 5)
-#define DELAY_10_NOP DELAY_NOPS(10)
-#define DELAY_20_NOP DELAY_NOPS(20)
-
-#if CYCLES_PER_MICROSECOND <= 200
- #define DELAY_100NS DELAY_NOPS((CYCLES_PER_MICROSECOND + 9) / 10)
-#else
- #define DELAY_100NS DELAY_20_NOP
-#endif
-
-// Microsecond delays for hardware interfaces
-#if CYCLES_PER_MICROSECOND <= 20
- #define DELAY_1US DELAY_NOPS(CYCLES_PER_MICROSECOND)
- #define DELAY_US(X) \
- switch (X) { \
- case 20: DELAY_1US; case 19: DELAY_1US; case 18: DELAY_1US; case 17: DELAY_1US; \
- case 16: DELAY_1US; case 15: DELAY_1US; case 14: DELAY_1US; case 13: DELAY_1US; \
- case 12: DELAY_1US; case 11: DELAY_1US; case 10: DELAY_1US; case 9: DELAY_1US; \
- case 8: DELAY_1US; case 7: DELAY_1US; case 6: DELAY_1US; case 5: DELAY_1US; \
- case 4: DELAY_1US; case 3: DELAY_1US; case 2: DELAY_1US; case 1: DELAY_1US; \
- }
-#else
- #define DELAY_US(X) delayMicroseconds(X) // May not be usable in CRITICAL_SECTION
- #define DELAY_1US DELAY_US(1)
-#endif
-#define DELAY_2US DELAY_US( 2)
-#define DELAY_3US DELAY_US( 3)
-#define DELAY_4US DELAY_US( 4)
-#define DELAY_5US DELAY_US( 5)
-#define DELAY_6US DELAY_US( 6)
-#define DELAY_7US DELAY_US( 7)
-#define DELAY_8US DELAY_US( 8)
-#define DELAY_9US DELAY_US( 9)
-#define DELAY_10US DELAY_US(10)
-#define DELAY_20US DELAY_US(20)
+// Nanoseconds per cycle
+#define NANOSECONDS_PER_CYCLE (1000000000.0 / F_CPU)
// Remove compiler warning on an unused variable
#define UNUSED(x) (void) (x)
@@ -122,10 +66,11 @@
// Macros for bit masks
#undef _BV
-#define _BV(b) (1<<(b))
+#define _BV(b) (1 << (b))
#define TEST(n,b) !!((n)&_BV(b))
#define SBI(n,b) (n |= _BV(b))
#define CBI(n,b) (n &= ~_BV(b))
+#define SET_BIT_TO(N,B,TF) do{ if (TF) SBI(N,B); else CBI(N,B); }while(0)
#define _BV32(b) (1UL << (b))
#define TEST32(n,b) !!((n)&_BV32(b))
@@ -152,6 +97,7 @@
// Macros to contrain values
#define NOLESS(v,n) do{ if (v < n) v = n; }while(0)
#define NOMORE(v,n) do{ if (v > n) v = n; }while(0)
+#define LIMIT(v,n1,n2) do{ if (v < n1) v = n1; else if (v > n2) v = n2; }while(0)
// Macros to support option testing
#define _CAT(a, ...) a ## __VA_ARGS__
@@ -159,9 +105,11 @@
#define SWITCH_ENABLED_true 1
#define SWITCH_ENABLED_0 0
#define SWITCH_ENABLED_1 1
+#define SWITCH_ENABLED_0x0 0
+#define SWITCH_ENABLED_0x1 1
#define SWITCH_ENABLED_ 1
#define ENABLED(b) _CAT(SWITCH_ENABLED_, b)
-#define DISABLED(b) (!_CAT(SWITCH_ENABLED_, b))
+#define DISABLED(b) !ENABLED(b)
#define WITHIN(V,L,H) ((V) >= (L) && (V) <= (H))
#define NUMERIC(a) WITHIN(a, '0', '9')
@@ -170,7 +118,7 @@
#define DECIMAL_SIGNED(a) (DECIMAL(a) || (a) == '-' || (a) == '+')
#define COUNT(a) (sizeof(a)/sizeof(*a))
#define ZERO(a) memset(a,0,sizeof(a))
-#define COPY(a,b) memcpy(a,b,min(sizeof(a),sizeof(b)))
+#define COPY(a,b) memcpy(a,b,MIN(sizeof(a),sizeof(b)))
// Macros for initializing arrays
#define ARRAY_6(v1, v2, v3, v4, v5, v6, ...) { v1, v2, v3, v4, v5, v6 }
@@ -221,12 +169,48 @@
#define CEILING(x,y) (((x) + (y) - 1) / (y))
-#define MIN3(a, b, c) min(min(a, b), c)
-#define MIN4(a, b, c, d) min(MIN3(a, b, c), d)
-#define MIN5(a, b, c, d, e) min(MIN4(a, b, c, d), e)
-#define MAX3(a, b, c) max(max(a, b), c)
-#define MAX4(a, b, c, d) max(MAX3(a, b, c), d)
-#define MAX5(a, b, c, d, e) max(MAX4(a, b, c, d), e)
+// Avoid double evaluation of arguments on MIN/MAX/ABS
+#undef MIN
+#undef MAX
+#undef ABS
+#ifdef __cplusplus
+
+ // C++11 solution that is standards compliant. Return type is deduced automatically
+ template static inline constexpr auto MIN(const L lhs, const R rhs) -> decltype(lhs + rhs) {
+ return lhs < rhs ? lhs : rhs;
+ }
+ template static inline constexpr auto MAX(const L lhs, const R rhs) -> decltype(lhs + rhs){
+ return lhs > rhs ? lhs : rhs;
+ }
+ template static inline constexpr const T ABS(const T v) {
+ return v >= 0 ? v : -v;
+ }
+#else
+
+ // Using GCC extensions, but Travis GCC version does not like it and gives
+ // "error: statement-expressions are not allowed outside functions nor in template-argument lists"
+ #define MIN(a, b) \
+ ({__typeof__(a) _a = (a); \
+ __typeof__(b) _b = (b); \
+ _a < _b ? _a : _b;})
+
+ #define MAX(a, b) \
+ ({__typeof__(a) _a = (a); \
+ __typeof__(b) _b = (b); \
+ _a > _b ? _a : _b;})
+
+ #define ABS(a) \
+ ({__typeof__(a) _a = (a); \
+ _a >= 0 ? _a : -_a;})
+
+#endif
+
+#define MIN3(a, b, c) MIN(MIN(a, b), c)
+#define MIN4(a, b, c, d) MIN(MIN3(a, b, c), d)
+#define MIN5(a, b, c, d, e) MIN(MIN4(a, b, c, d), e)
+#define MAX3(a, b, c) MAX(MAX(a, b), c)
+#define MAX4(a, b, c, d) MAX(MAX3(a, b, c), d)
+#define MAX5(a, b, c, d, e) MAX(MAX4(a, b, c, d), e)
#define UNEAR_ZERO(x) ((x) < 0.000001)
#define NEAR_ZERO(x) WITHIN(x, -0.000001, 0.000001)
@@ -239,7 +223,6 @@
// Maths macros that can be overridden by HAL
//
#define ATAN2(y, x) atan2(y, x)
-#define FABS(x) fabs(x)
#define POW(x, y) pow(x, y)
#define SQRT(x) sqrt(x)
#define CEIL(x) ceil(x)
diff --git a/Marlin/malyanlcd.cpp b/Marlin/malyanlcd.cpp
index 894b8ae642..e8d6c25143 100644
--- a/Marlin/malyanlcd.cpp
+++ b/Marlin/malyanlcd.cpp
@@ -45,8 +45,13 @@
#if ENABLED(MALYAN_LCD)
-#include "cardreader.h"
-#include "SdFatConfig.h"
+#if ENABLED(SDSUPPORT)
+ #include "cardreader.h"
+ #include "SdFatConfig.h"
+#else
+ #define LONG_FILENAME_LENGTH 0
+#endif
+
#include "temperature.h"
#include "planner.h"
#include "stepper.h"
@@ -57,6 +62,15 @@
#include "Marlin.h"
+#if USE_MARLINSERIAL
+ // Make an exception to use HardwareSerial too
+ #undef HardwareSerial_h
+ #include
+ #define USB_STATUS true
+#else
+ #define USB_STATUS Serial
+#endif
+
// On the Malyan M200, this will be Serial1. On a RAMPS board,
// it might not be.
#define LCD_SERIAL Serial1
@@ -72,7 +86,7 @@ int inbound_count;
// Everything written needs the high bit set.
void write_to_lcd_P(const char * const message) {
char encoded_message[MAX_CURLY_COMMAND];
- uint8_t message_length = min(strlen_P(message), sizeof(encoded_message));
+ uint8_t message_length = MIN(strlen_P(message), sizeof(encoded_message));
for (uint8_t i = 0; i < message_length; i++)
encoded_message[i] = pgm_read_byte(&message[i]) | 0x80;
@@ -82,7 +96,7 @@ void write_to_lcd_P(const char * const message) {
void write_to_lcd(const char * const message) {
char encoded_message[MAX_CURLY_COMMAND];
- const uint8_t message_length = min(strlen(message), sizeof(encoded_message));
+ const uint8_t message_length = MIN(strlen(message), sizeof(encoded_message));
for (uint8_t i = 0; i < message_length; i++)
encoded_message[i] = message[i] | 0x80;
@@ -132,8 +146,6 @@ void process_lcd_c_command(const char* command) {
void process_lcd_eb_command(const char* command) {
char elapsed_buffer[10];
duration_t elapsed;
- bool has_days;
- uint8_t len;
switch (command[0]) {
case '0': {
elapsed = print_job_timer.duration();
@@ -144,9 +156,17 @@ void process_lcd_eb_command(const char* command) {
PSTR("{T0:%03.0f/%03i}{T1:000/000}{TP:%03.0f/%03i}{TQ:%03i}{TT:%s}"),
thermalManager.degHotend(0),
thermalManager.degTargetHotend(0),
- thermalManager.degBed(),
- thermalManager.degTargetBed(),
- card.percentDone(),
+ #if HAS_HEATED_BED
+ thermalManager.degBed(),
+ thermalManager.degTargetBed(),
+ #else
+ 0, 0,
+ #endif
+ #if ENABLED(SDSUPPORT)
+ card.percentDone(),
+ #else
+ 0,
+ #endif
elapsed_buffer);
write_to_lcd(message_buffer);
} break;
@@ -223,51 +243,55 @@ void process_lcd_p_command(const char* command) {
switch (command[0]) {
case 'X':
- // cancel print
- write_to_lcd_P(PSTR("{SYS:CANCELING}"));
- card.stopSDPrint(
- #if SD_RESORT
- true
+ #if ENABLED(SDSUPPORT)
+ // cancel print
+ write_to_lcd_P(PSTR("{SYS:CANCELING}"));
+ card.stopSDPrint(
+ #if SD_RESORT
+ true
+ #endif
+ );
+ clear_command_queue();
+ quickstop_stepper();
+ print_job_timer.stop();
+ thermalManager.disable_all_heaters();
+ #if FAN_COUNT > 0
+ for (uint8_t i = 0; i < FAN_COUNT; i++) fanSpeeds[i] = 0;
#endif
- );
- clear_command_queue();
- quickstop_stepper();
- print_job_timer.stop();
- thermalManager.disable_all_heaters();
- #if FAN_COUNT > 0
- for (uint8_t i = 0; i < FAN_COUNT; i++) fanSpeeds[i] = 0;
+ wait_for_heatup = false;
+ write_to_lcd_P(PSTR("{SYS:STARTED}"));
#endif
- wait_for_heatup = false;
- write_to_lcd_P(PSTR("{SYS:STARTED}"));
break;
case 'H':
// Home all axis
enqueue_and_echo_commands_now_P(PSTR("G28"));
break;
default: {
- // Print file 000 - a three digit number indicating which
- // file to print in the SD card. If it's a directory,
- // then switch to the directory.
+ #if ENABLED(SDSUPPORT)
+ // Print file 000 - a three digit number indicating which
+ // file to print in the SD card. If it's a directory,
+ // then switch to the directory.
- // Find the name of the file to print.
- // It's needed to echo the PRINTFILE option.
- // The {S:L} command should've ensured the SD card was mounted.
- card.getfilename(atoi(command));
+ // Find the name of the file to print.
+ // It's needed to echo the PRINTFILE option.
+ // The {S:L} command should've ensured the SD card was mounted.
+ card.getfilename(atoi(command));
- // There may be a difference in how V1 and V2 LCDs handle subdirectory
- // prints. Investigate more. This matches the V1 motion controller actions
- // but the V2 LCD switches to "print" mode on {SYS:DIR} response.
- if (card.filenameIsDir) {
- card.chdir(card.filename);
- write_to_lcd_P(PSTR("{SYS:DIR}"));
- }
- else {
- char message_buffer[MAX_CURLY_COMMAND];
- sprintf_P(message_buffer, PSTR("{PRINTFILE:%s}"), card.filename);
- write_to_lcd(message_buffer);
- write_to_lcd_P(PSTR("{SYS:BUILD}"));
- card.openAndPrintFile(card.filename);
- }
+ // There may be a difference in how V1 and V2 LCDs handle subdirectory
+ // prints. Investigate more. This matches the V1 motion controller actions
+ // but the V2 LCD switches to "print" mode on {SYS:DIR} response.
+ if (card.filenameIsDir) {
+ card.chdir(card.filename);
+ write_to_lcd_P(PSTR("{SYS:DIR}"));
+ }
+ else {
+ char message_buffer[MAX_CURLY_COMMAND];
+ sprintf_P(message_buffer, PSTR("{PRINTFILE:%s}"), card.filename);
+ write_to_lcd(message_buffer);
+ write_to_lcd_P(PSTR("{SYS:BUILD}"));
+ card.openAndPrintFile(card.filename);
+ }
+ #endif
} break; // default
} // switch
}
@@ -292,7 +316,11 @@ void process_lcd_s_command(const char* command) {
char message_buffer[MAX_CURLY_COMMAND];
sprintf_P(message_buffer, PSTR("{T0:%03.0f/%03i}{T1:000/000}{TP:%03.0f/%03i}"),
thermalManager.degHotend(0), thermalManager.degTargetHotend(0),
- thermalManager.degBed(), thermalManager.degTargetBed()
+ #if HAS_HEATED_BED
+ thermalManager.degBed(), thermalManager.degTargetBed()
+ #else
+ 0, 0
+ #endif
);
write_to_lcd(message_buffer);
} break;
@@ -303,23 +331,25 @@ void process_lcd_s_command(const char* command) {
break;
case 'L': {
- if (!card.cardOK) card.initsd();
+ #if ENABLED(SDSUPPORT)
+ if (!card.cardOK) card.initsd();
- // A more efficient way to do this would be to
- // implement a callback in the ls_SerialPrint code, but
- // that requires changes to the core cardreader class that
- // would not benefit the majority of users. Since one can't
- // select a file for printing during a print, there's
- // little reason not to do it this way.
- char message_buffer[MAX_CURLY_COMMAND];
- uint16_t file_count = card.get_num_Files();
- for (uint16_t i = 0; i < file_count; i++) {
- card.getfilename(i);
- sprintf_P(message_buffer, card.filenameIsDir ? PSTR("{DIR:%s}") : PSTR("{FILE:%s}"), card.filename);
- write_to_lcd(message_buffer);
- }
+ // A more efficient way to do this would be to
+ // implement a callback in the ls_SerialPrint code, but
+ // that requires changes to the core cardreader class that
+ // would not benefit the majority of users. Since one can't
+ // select a file for printing during a print, there's
+ // little reason not to do it this way.
+ char message_buffer[MAX_CURLY_COMMAND];
+ uint16_t file_count = card.get_num_Files();
+ for (uint16_t i = 0; i < file_count; i++) {
+ card.getfilename(i);
+ sprintf_P(message_buffer, card.filenameIsDir ? PSTR("{DIR:%s}") : PSTR("{FILE:%s}"), card.filename);
+ write_to_lcd(message_buffer);
+ }
- write_to_lcd_P(PSTR("{SYS:OK}"));
+ write_to_lcd_P(PSTR("{SYS:OK}"));
+ #endif
} break;
default:
@@ -371,15 +401,15 @@ void process_lcd_command(const char* command) {
/**
* UC means connected.
* UD means disconnected
- * The stock firmware considers USB initialied as "connected."
+ * The stock firmware considers USB initialized as "connected."
*/
void update_usb_status(const bool forceUpdate) {
static bool last_usb_connected_status = false;
// This is mildly different than stock, which
// appears to use the usb discovery status.
// This is more logical.
- if (last_usb_connected_status != Serial || forceUpdate) {
- last_usb_connected_status = Serial;
+ if (last_usb_connected_status != USB_STATUS || forceUpdate) {
+ last_usb_connected_status = USB_STATUS;
write_to_lcd_P(last_usb_connected_status ? PSTR("{R:UC}\r\n") : PSTR("{R:UD}\r\n"));
}
}
@@ -390,7 +420,7 @@ void update_usb_status(const bool forceUpdate) {
* The optimize attribute fixes a register Compile
* error for amtel.
*/
-void lcd_update() _O2 {
+void _O2 lcd_update() {
static char inbound_buffer[MAX_CURLY_COMMAND];
// First report USB status.
@@ -408,15 +438,17 @@ void lcd_update() _O2 {
}
}
- // If there's a print in progress, we need to emit the status as
- // {TQ:}
- if (card.sdprinting) {
- // We also need to send: T:-2538.0 E:0
- // I have no idea what this means.
- char message_buffer[10];
- sprintf_P(message_buffer, PSTR("{TQ:%03i}"), card.percentDone());
- write_to_lcd(message_buffer);
- }
+ #if ENABLED(SDSUPPORT)
+ // If there's a print in progress, we need to emit the status as
+ // {TQ:}
+ if (card.sdprinting) {
+ // We also need to send: T:-2538.0 E:0
+ // I have no idea what this means.
+ char message_buffer[10];
+ sprintf_P(message_buffer, PSTR("{TQ:%03i}"), card.percentDone());
+ write_to_lcd(message_buffer);
+ }
+ #endif
}
/**
diff --git a/Marlin/nozzle.cpp b/Marlin/nozzle.cpp
index da43e264bf..8bff692e44 100644
--- a/Marlin/nozzle.cpp
+++ b/Marlin/nozzle.cpp
@@ -78,7 +78,7 @@
do_blocking_move_to(start.x, start.y, start.z);
const uint8_t zigs = objects << 1;
- const bool horiz = FABS(diffx) >= FABS(diffy); // Do a horizontal wipe?
+ const bool horiz = ABS(diffx) >= ABS(diffy); // Do a horizontal wipe?
const float P = (horiz ? diffx : diffy) / zigs; // Period of each zig / zag
const point_t *side;
for (uint8_t j = 0; j < strokes; j++) {
@@ -171,11 +171,11 @@
break;
case 2: // Raise by Z-park height
- do_blocking_move_to_z(min(current_position[Z_AXIS] + park.z, Z_MAX_POS), fr_z);
+ do_blocking_move_to_z(MIN(current_position[Z_AXIS] + park.z, Z_MAX_POS), fr_z);
break;
default: // Raise to at least the Z-park height
- do_blocking_move_to_z(max(park.z, current_position[Z_AXIS]), fr_z);
+ do_blocking_move_to_z(MAX(park.z, current_position[Z_AXIS]), fr_z);
}
do_blocking_move_to_xy(park.x, park.y, fr_xy);
diff --git a/Marlin/parser.h b/Marlin/parser.h
index 6676f228db..4184191506 100644
--- a/Marlin/parser.h
+++ b/Marlin/parser.h
@@ -153,7 +153,7 @@ public:
// Code is found in the string. If not found, value_ptr is unchanged.
// This allows "if (seen('A')||seen('B'))" to use the last-found value.
static bool seen(const char c) {
- const char *p = strchr(command_args, c);
+ char *p = strchr(command_args, c);
const bool b = !!p;
if (b) value_ptr = valid_float(&p[1]) ? &p[1] : (char*)NULL;
return b;
@@ -317,7 +317,7 @@ public:
// Provide simple value accessors with default option
FORCE_INLINE static float floatval(const char c, const float dval=0.0) { return seenval(c) ? value_float() : dval; }
- FORCE_INLINE static bool boolval(const char c) { return seenval(c) ? value_bool() : seen(c); }
+ FORCE_INLINE static bool boolval(const char c, const bool dval=false) { return seenval(c) ? value_bool() : (seen(c) ? true : dval); }
FORCE_INLINE static uint8_t byteval(const char c, const uint8_t dval=0) { return seenval(c) ? value_byte() : dval; }
FORCE_INLINE static int16_t intval(const char c, const int16_t dval=0) { return seenval(c) ? value_int() : dval; }
FORCE_INLINE static uint16_t ushortval(const char c, const uint16_t dval=0) { return seenval(c) ? value_ushort() : dval; }
diff --git a/Marlin/pins.h b/Marlin/pins.h
index 0d1a2380d7..20ba513a56 100644
--- a/Marlin/pins.h
+++ b/Marlin/pins.h
@@ -248,22 +248,21 @@
//
#elif MB(TEENSYLU)
- #include "pins_TEENSYLU.h" // AT90USB1286, AT90USB1286P env:teensy20
+ #include "pins_TEENSYLU.h" // AT90USB1286, AT90USB1286P env:at90USB1286_CDC
#elif MB(PRINTRBOARD)
- #include "pins_PRINTRBOARD.h" // AT90USB1286 env:teensy20
+ #include "pins_PRINTRBOARD.h" // AT90USB1286 env:at90USB1286_DFU
#elif MB(PRINTRBOARD_REVF)
- #include "pins_PRINTRBOARD_REVF.h" // AT90USB1286 env:teensy20
+ #include "pins_PRINTRBOARD_REVF.h" // AT90USB1286 env:at90USB1286_DFU
#elif MB(BRAINWAVE)
- #include "pins_BRAINWAVE.h" // AT90USB646 env:teensy20
+ #include "pins_BRAINWAVE.h" // AT90USB646 env:at90USB1286_CDC
#elif MB(BRAINWAVE_PRO)
- #include "pins_BRAINWAVE_PRO.h" // AT90USB1286 env:teensy20
+ #include "pins_BRAINWAVE_PRO.h" // AT90USB1286 env:at90USB1286_CDC
#elif MB(SAV_MKI)
- #include "pins_SAV_MKI.h" // AT90USB1286 env:teensy20
+ #include "pins_SAV_MKI.h" // AT90USB1286 env:at90USB1286_CDC
#elif MB(TEENSY2)
#include "pins_TEENSY2.h" // AT90USB1286 env:teensy20
#elif MB(5DPRINT)
- #include "pins_5DPRINT.h" // AT90USB1286 env:teensy20
-
+ #include "pins_5DPRINT.h" // AT90USB1286 ?env:at90USB1286_DFU
#else
#error "Unknown MOTHERBOARD value set in Configuration.h"
diff --git a/Marlin/pins_ANET_10.h b/Marlin/pins_ANET_10.h
index d7c113d355..e8393c4c72 100644
--- a/Marlin/pins_ANET_10.h
+++ b/Marlin/pins_ANET_10.h
@@ -89,7 +89,7 @@
*/
#ifndef __AVR_ATmega1284P__
- #error "Oops! Make sure you have 'Anet V1.0', 'Anet V1.0 (Optiboot)' or 'Sanguino' selected from the 'Tools -> Boards' menu."
+ #error "Oops! Make sure you have 'Anet V1.0', 'Anet V1.0 (Optiboot)' or 'Sanguino' selected in the 'Tools -> Boards' menu and ATmega1284P selected in 'Tools -> Processor' menu."
#endif
#ifndef BOARD_NAME
@@ -153,7 +153,7 @@
#if ENABLED(ULTRA_LCD) && ENABLED(NEWPANEL)
#define LCD_SDSS 28
#if ENABLED(ADC_KEYPAD)
- #define SERVO0_PIN 27 // free for BLTouch/3D-Touch
+ #define SERVO0_PIN 27 // free for BLTouch/3D-Touch
#define LCD_PINS_RS 28
#define LCD_PINS_ENABLE 29
#define LCD_PINS_D4 10
@@ -168,7 +168,7 @@
// Pin definitions for the Anet A6 Full Graphics display and the RepRapDiscount Full Graphics
// display using an adapter board // https://go.aisler.net/benlye/anet-lcd-adapter/pcb
// See below for alternative pin definitions for use with https://www.thingiverse.com/thing:2103748
- #define SERVO0_PIN 29 // free for BLTouch/3D-Touch
+ #define SERVO0_PIN 29 // free for BLTouch/3D-Touch
#define BEEPER_PIN 17
#define LCD_PINS_RS 27
#define LCD_PINS_ENABLE 28
@@ -177,13 +177,13 @@
#define BTN_EN2 10
#define BTN_ENC 16
#ifndef ST7920_DELAY_1
- #define ST7920_DELAY_1 DELAY_0_NOP
+ #define ST7920_DELAY_1 DELAY_NS(0)
#endif
#ifndef ST7920_DELAY_2
- #define ST7920_DELAY_2 DELAY_1_NOP
+ #define ST7920_DELAY_2 DELAY_NS(63)
#endif
#ifndef ST7920_DELAY_3
- #define ST7920_DELAY_3 DELAY_2_NOP
+ #define ST7920_DELAY_3 DELAY_NS(125)
#endif
#define STD_ENCODER_PULSES_PER_STEP 4
#define STD_ENCODER_STEPS_PER_MENU_ITEM 1
@@ -201,7 +201,7 @@
* published by oderwat on Thingiverse at https://www.thingiverse.com/thing:2103748.
*
* Using that adapter requires changing the pin definition as follows:
- * #define SERVO0_PIN 27 // free for BLTouch/3D-Touch
+ * #define SERVO0_PIN 27 // free for BLTouch/3D-Touch
* #define BEEPER_PIN 28
* #define LCD_PINS_RS 30
* #define LCD_PINS_ENABLE 29
diff --git a/Marlin/pins_MELZI_CREALITY.h b/Marlin/pins_MELZI_CREALITY.h
index b42075b76d..8bfd3b860a 100644
--- a/Marlin/pins_MELZI_CREALITY.h
+++ b/Marlin/pins_MELZI_CREALITY.h
@@ -55,13 +55,13 @@
// Alter timing for graphical display
#ifndef ST7920_DELAY_1
- #define ST7920_DELAY_1 DELAY_2_NOP
+ #define ST7920_DELAY_1 DELAY_NS(125)
#endif
#ifndef ST7920_DELAY_2
- #define ST7920_DELAY_2 DELAY_2_NOP
+ #define ST7920_DELAY_2 DELAY_NS(125)
#endif
#ifndef ST7920_DELAY_3
- #define ST7920_DELAY_3 DELAY_2_NOP
+ #define ST7920_DELAY_3 DELAY_NS(125)
#endif
#if ENABLED(MINIPANEL)
diff --git a/Marlin/pins_MELZI_MALYAN.h b/Marlin/pins_MELZI_MALYAN.h
index 3888b537d1..a81526f065 100644
--- a/Marlin/pins_MELZI_MALYAN.h
+++ b/Marlin/pins_MELZI_MALYAN.h
@@ -44,11 +44,11 @@
// Alter timing for graphical display
#ifndef ST7920_DELAY_1
- #define ST7920_DELAY_1 DELAY_2_NOP
+ #define ST7920_DELAY_1 DELAY_NS(125)
#endif
#ifndef ST7920_DELAY_2
- #define ST7920_DELAY_2 DELAY_2_NOP
+ #define ST7920_DELAY_2 DELAY_NS(125)
#endif
#ifndef ST7920_DELAY_3
- #define ST7920_DELAY_3 DELAY_2_NOP
+ #define ST7920_DELAY_3 DELAY_NS(125)
#endif
diff --git a/Marlin/pins_MELZI_TRONXY.h b/Marlin/pins_MELZI_TRONXY.h
index 7c66c50c60..0da7934302 100644
--- a/Marlin/pins_MELZI_TRONXY.h
+++ b/Marlin/pins_MELZI_TRONXY.h
@@ -51,11 +51,11 @@
#define BTN_ENC 26
#ifndef ST7920_DELAY_1
- #define ST7920_DELAY_1 DELAY_0_NOP
+ #define ST7920_DELAY_1 DELAY_NS(0)
#endif
#ifndef ST7920_DELAY_2
- #define ST7920_DELAY_2 DELAY_2_NOP
+ #define ST7920_DELAY_2 DELAY_NS(125)
#endif
#ifndef ST7920_DELAY_3
- #define ST7920_DELAY_3 DELAY_0_NOP
+ #define ST7920_DELAY_3 DELAY_NS(0)
#endif
diff --git a/Marlin/pins_PRINTRBOARD.h b/Marlin/pins_PRINTRBOARD.h
index a8197af9a4..28392fb8df 100644
--- a/Marlin/pins_PRINTRBOARD.h
+++ b/Marlin/pins_PRINTRBOARD.h
@@ -74,11 +74,7 @@
// Limit Switches
//
#define X_STOP_PIN 47 // E3
-#if ENABLED(SDSUPPORT)
- #define Y_STOP_PIN 37 // E5 - Move Ystop to Estop socket
-#else
- #define Y_STOP_PIN 20 // B0 SS - Ystop in Ystop socket
-#endif
+#define Y_STOP_PIN 20 // B0 SS
#define Z_STOP_PIN 36 // E4
//
@@ -120,7 +116,7 @@
//
// Misc. Functions
//
-#define SDSS 20 // B0 SS
+#define SDSS 26 // B6 SDCS
#define FILWIDTH_PIN 2 // Analog Input
//
@@ -146,16 +142,18 @@
#define BTN_EN2 3 // D3 RX1 JP2-7
#define BTN_ENC 45 // F7 TDI JP2-12
+ #undef SDSS
#define SDSS 43 // F5 TMS JP2-8
- #define STAT_LED_RED_PIN 12 // C2 JP11-14
- #define STAT_LED_BLUE_PIN 10 // C0 JP11-12
+ #define STAT_LED_RED_PIN 12 // C2 JP11-14
+ #define STAT_LED_BLUE_PIN 10 // C0 JP11-12
#elif ENABLED(LCD_I2C_PANELOLU2)
#define BTN_EN1 3 // D3 RX1 JP2-7
#define BTN_EN2 2 // D2 TX1 JP2-5
#define BTN_ENC 41 // F3 JP2-4
+ #undef SDSS
#define SDSS 38 // F0 B-THERM connector - use SD card on Panelolu2
#else
diff --git a/Marlin/pins_PRINTRBOARD_REVF.h b/Marlin/pins_PRINTRBOARD_REVF.h
index bf3a023c4d..359a8b85db 100644
--- a/Marlin/pins_PRINTRBOARD_REVF.h
+++ b/Marlin/pins_PRINTRBOARD_REVF.h
@@ -244,13 +244,13 @@
// increase delays
#ifndef ST7920_DELAY_1
- #define ST7920_DELAY_1 DELAY_5_NOP
+ #define ST7920_DELAY_1 DELAY_NS(313)
#endif
#ifndef ST7920_DELAY_2
- #define ST7920_DELAY_2 DELAY_5_NOP
+ #define ST7920_DELAY_2 DELAY_NS(313)
#endif
#ifndef ST7920_DELAY_3
- #define ST7920_DELAY_3 DELAY_5_NOP
+ #define ST7920_DELAY_3 DELAY_NS(313)
#endif
#else
diff --git a/Marlin/pins_SANGUINOLOLU_11.h b/Marlin/pins_SANGUINOLOLU_11.h
index c27eab1c8a..ed4a521e7e 100644
--- a/Marlin/pins_SANGUINOLOLU_11.h
+++ b/Marlin/pins_SANGUINOLOLU_11.h
@@ -239,13 +239,13 @@
#define BTN_EN2 30
#ifndef ST7920_DELAY_1
- #define ST7920_DELAY_1 DELAY_0_NOP
+ #define ST7920_DELAY_1 DELAY_NS(0)
#endif
#ifndef ST7920_DELAY_2
- #define ST7920_DELAY_2 DELAY_3_NOP
+ #define ST7920_DELAY_2 DELAY_NS(188)
#endif
#ifndef ST7920_DELAY_3
- #define ST7920_DELAY_3 DELAY_0_NOP
+ #define ST7920_DELAY_3 DELAY_NS(0)
#endif
#elif ENABLED(ZONESTAR_LCD) // For the Tronxy Melzi boards
diff --git a/Marlin/planner.cpp b/Marlin/planner.cpp
index d32290f7da..ffbee93ad5 100644
--- a/Marlin/planner.cpp
+++ b/Marlin/planner.cpp
@@ -56,6 +56,10 @@
*
* IntersectionDistance[s1_, s2_, a_, d_] := (2 a d - s1^2 + s2^2)/(4 a)
*
+ * --
+ *
+ * The fast inverse function needed for Bézier interpolation for AVR
+ * was designed, written and tested by Eduardo José Tagle on April/2018
*/
#include "planner.h"
@@ -77,6 +81,10 @@
#include "power.h"
#endif
+// Delay for delivery of first block to the stepper ISR, if the queue contains 2 or
+// fewer movements. The delay is measured in milliseconds, and must be less than 250ms
+#define BLOCK_DELAY_FOR_1ST_MOVE 50
+
Planner planner;
// public:
@@ -85,13 +93,20 @@ Planner planner;
* A ring buffer of moves described in steps
*/
block_t Planner::block_buffer[BLOCK_BUFFER_SIZE];
-volatile uint8_t Planner::block_buffer_head, // Index of the next block to be pushed
- Planner::block_buffer_tail;
+volatile uint8_t Planner::block_buffer_head, // Index of the next block to be pushed
+ Planner::block_buffer_tail; // Index of the busy block, if any
+uint16_t Planner::cleaning_buffer_counter; // A counter to disable queuing of blocks
+uint8_t Planner::delay_before_delivering, // This counter delays delivery of blocks when queue becomes empty to allow the opportunity of merging blocks
+ Planner::block_buffer_planned; // Index of the optimally planned block
-float Planner::max_feedrate_mm_s[XYZE_N], // Max speeds in mm per second
+float Planner::max_feedrate_mm_s[XYZE_N], // Max speeds in mm per second
Planner::axis_steps_per_mm[XYZE_N],
Planner::steps_to_mm[XYZE_N];
+#if ENABLED(ABORT_ON_ENDSTOP_HIT_FEATURE_ENABLED)
+ bool Planner::abort_on_endstop_hit = false;
+#endif
+
#if ENABLED(DISTINCT_E_FACTORS)
uint8_t Planner::last_extruder = 0; // Respond to extruder change
#endif
@@ -160,7 +175,7 @@ int32_t Planner::position[NUM_AXIS] = { 0 };
uint32_t Planner::cutoff_long;
float Planner::previous_speed[NUM_AXIS],
- Planner::previous_nominal_speed;
+ Planner::previous_nominal_speed_sqr;
#if ENABLED(DISABLE_INACTIVE_EXTRUDER)
uint8_t Planner::g_uc_extruder_last_move[EXTRUDERS] = { 0 };
@@ -197,11 +212,13 @@ void Planner::init() {
ZERO(position_float);
#endif
ZERO(previous_speed);
- previous_nominal_speed = 0.0;
+ previous_nominal_speed_sqr = 0.0;
#if ABL_PLANAR
bed_level_matrix.set_to_identity();
#endif
clear_block_buffer();
+ block_buffer_planned = 0;
+ delay_before_delivering = 0;
}
#if ENABLED(BEZIER_JERK_CONTROL)
@@ -347,7 +364,7 @@ void Planner::init() {
//
static uint32_t get_period_inverse(uint32_t d) {
- static const uint8_t inv_tab[256] PROGMEM = {
+ static const uint8_t inv_tab[256] PROGMEM = {
255,253,252,250,248,246,244,242,240,238,236,234,233,231,229,227,
225,224,222,220,218,217,215,213,212,210,208,207,205,203,202,200,
199,197,195,194,192,191,189,188,186,185,183,182,180,179,178,176,
@@ -406,12 +423,12 @@ void Planner::init() {
// for the same result - Using C division, it takes 500cycles to complete .
A("clr %3") // idx = 0
- A("mov %14,%6")
- A("mov %15,%7")
+ A("mov %14,%6")
+ A("mov %15,%7")
A("mov %16,%8") // nr = interval
A("tst %16") // nr & 0xFF0000 == 0 ?
A("brne 2f") // No, skip this
- A("mov %16,%15")
+ A("mov %16,%15")
A("mov %15,%14") // nr <<= 8, %14 not needed
A("subi %3,-8") // idx += 8
A("tst %16") // nr & 0xFF0000 == 0 ?
@@ -426,7 +443,7 @@ void Planner::init() {
A("brcc 3f") // No, skip this
A("swap %15") // Swap nibbles
A("swap %16") // Swap nibbles. Low nibble is 0
- A("mov %14, %15")
+ A("mov %14, %15")
A("andi %14,0x0F") // Isolate low nibble
A("andi %15,0xF0") // Keep proper nibble in %15
A("or %16, %14") // %16:%15 <<= 4
@@ -435,23 +452,23 @@ void Planner::init() {
L("3")
A("cpi %16,0x40") // (nr & 0xC00000) == 0 ?
A("brcc 4f") // No, skip this
- A("add %15,%15")
- A("adc %16,%16")
- A("add %15,%15")
+ A("add %15,%15")
+ A("adc %16,%16")
+ A("add %15,%15")
A("adc %16,%16") // %16:%15 <<= 2
A("subi %3,-2") // idx += 2
L("4")
A("cpi %16,0x80") // (nr & 0x800000) == 0 ?
A("brcc 5f") // No, skip this
- A("add %15,%15")
+ A("add %15,%15")
A("adc %16,%16") // %16:%15 <<= 1
A("inc %3") // idx += 1
// Now %16:%15 contains its MSBit set to 1, or %16:%15 is == 0. We are now absolutely sure
// we have at least 9 MSBits available to enter the initial estimation table
L("5")
- A("add %15,%15")
+ A("add %15,%15")
A("adc %16,%16") // %16:%15 = tidx = (nr <<= 1), we lose the top MSBit (always set to 1, %16 is the index into the inverse table)
A("add r30,%16") // Only use top 8 bits
A("adc r31,%13") // r31:r30 = inv_tab + (tidx)
@@ -467,31 +484,31 @@ void Planner::init() {
// idx > 8, now %3 = idx - 8. We must perform a left shift. idx range:[1-8]
A("sbrs %3,0") // shift by 1bit position?
A("rjmp 8f") // No
- A("add %14,%14")
+ A("add %14,%14")
A("adc %15,%15") // %15:16 <<= 1
L("8")
A("sbrs %3,1") // shift by 2bit position?
A("rjmp 9f") // No
- A("add %14,%14")
- A("adc %15,%15")
- A("add %14,%14")
+ A("add %14,%14")
+ A("adc %15,%15")
+ A("add %14,%14")
A("adc %15,%15") // %15:16 <<= 1
L("9")
A("sbrs %3,2") // shift by 4bits position?
A("rjmp 16f") // No
A("swap %15") // Swap nibbles. lo nibble of %15 will always be 0
A("swap %14") // Swap nibbles
- A("mov %12,%14")
+ A("mov %12,%14")
A("andi %12,0x0F") // isolate low nibble
A("andi %14,0xF0") // and clear it
A("or %15,%12") // %15:%16 <<= 4
L("16")
A("sbrs %3,3") // shift by 8bits position?
A("rjmp 6f") // No, we are done
- A("mov %16,%15")
- A("mov %15,%14")
- A("clr %14")
- A("jmp 6f")
+ A("mov %16,%15")
+ A("mov %15,%14")
+ A("clr %14")
+ A("jmp 6f")
// idx < 8, now %3 = idx - 8. Get the count of bits
L("7")
@@ -499,14 +516,14 @@ void Planner::init() {
A("sbrs %3,0") // shift by 1 bit position ?
A("rjmp 10f") // No, skip it
A("asr %15") // (bit7 is always 0 here)
- A("ror %14")
+ A("ror %14")
L("10")
A("sbrs %3,1") // shift by 2 bit position ?
A("rjmp 11f") // No, skip it
A("asr %15") // (bit7 is always 0 here)
- A("ror %14")
+ A("ror %14")
A("asr %15") // (bit7 is always 0 here)
- A("ror %14")
+ A("ror %14")
L("11")
A("sbrs %3,2") // shift by 4 bit position ?
A("rjmp 12f") // No, skip it
@@ -518,9 +535,9 @@ void Planner::init() {
L("12")
A("sbrs %3,3") // shift by 8 bit position ?
A("rjmp 6f") // No, skip it
- A("mov %14,%15")
- A("clr %15")
- L("6") // %16:%15:%14 = initial estimation of 0x1000000 / d)
+ A("mov %14,%15")
+ A("clr %15")
+ L("6") // %16:%15:%14 = initial estimation of 0x1000000 / d
// Now, we must refine the estimation present on %16:%15:%14 using 1 iteration
// of Newton-Raphson. As it has a quadratic convergence, 1 iteration is enough
@@ -533,33 +550,33 @@ void Planner::init() {
// %3:%2:%1:%0 = working accumulator
// Compute 1<<25 - x*d. Result should never exceed 25 bits and should always be positive
- A("clr %0")
- A("clr %1")
- A("clr %2")
+ A("clr %0")
+ A("clr %1")
+ A("clr %2")
A("ldi %3,2") // %3:%2:%1:%0 = 0x2000000
A("mul %6,%14") // r1:r0 = LO(d) * LO(x)
- A("sub %0,r0")
- A("sbc %1,r1")
- A("sbc %2,%13")
+ A("sub %0,r0")
+ A("sbc %1,r1")
+ A("sbc %2,%13")
A("sbc %3,%13") // %3:%2:%1:%0 -= LO(d) * LO(x)
A("mul %7,%14") // r1:r0 = MI(d) * LO(x)
- A("sub %1,r0")
- A("sbc %2,r1")
+ A("sub %1,r0")
+ A("sbc %2,r1")
A("sbc %3,%13") // %3:%2:%1:%0 -= MI(d) * LO(x) << 8
A("mul %8,%14") // r1:r0 = HI(d) * LO(x)
- A("sub %2,r0")
+ A("sub %2,r0")
A("sbc %3,r1") // %3:%2:%1:%0 -= MIL(d) * LO(x) << 16
A("mul %6,%15") // r1:r0 = LO(d) * MI(x)
- A("sub %1,r0")
- A("sbc %2,r1")
+ A("sub %1,r0")
+ A("sbc %2,r1")
A("sbc %3,%13") // %3:%2:%1:%0 -= LO(d) * MI(x) << 8
A("mul %7,%15") // r1:r0 = MI(d) * MI(x)
- A("sub %2,r0")
+ A("sub %2,r0")
A("sbc %3,r1") // %3:%2:%1:%0 -= MI(d) * MI(x) << 16
A("mul %8,%15") // r1:r0 = HI(d) * MI(x)
A("sub %3,r0") // %3:%2:%1:%0 -= MIL(d) * MI(x) << 24
A("mul %6,%16") // r1:r0 = LO(d) * HI(x)
- A("sub %2,r0")
+ A("sub %2,r0")
A("sbc %3,r1") // %3:%2:%1:%0 -= LO(d) * HI(x) << 16
A("mul %7,%16") // r1:r0 = MI(d) * HI(x)
A("sub %3,r0") // %3:%2:%1:%0 -= MI(d) * HI(x) << 24
@@ -573,58 +590,58 @@ void Planner::init() {
// result = %11:%10:%9:%5:%4
A("mul %14,%0") // r1:r0 = LO(x) * LO(acc)
- A("mov %4,r1")
- A("clr %5")
- A("clr %9")
- A("clr %10")
+ A("mov %4,r1")
+ A("clr %5")
+ A("clr %9")
+ A("clr %10")
A("clr %11") // %11:%10:%9:%5:%4 = LO(x) * LO(acc) >> 8
A("mul %15,%0") // r1:r0 = MI(x) * LO(acc)
- A("add %4,r0")
- A("adc %5,r1")
- A("adc %9,%13")
- A("adc %10,%13")
+ A("add %4,r0")
+ A("adc %5,r1")
+ A("adc %9,%13")
+ A("adc %10,%13")
A("adc %11,%13") // %11:%10:%9:%5:%4 += MI(x) * LO(acc)
A("mul %16,%0") // r1:r0 = HI(x) * LO(acc)
- A("add %5,r0")
- A("adc %9,r1")
- A("adc %10,%13")
+ A("add %5,r0")
+ A("adc %9,r1")
+ A("adc %10,%13")
A("adc %11,%13") // %11:%10:%9:%5:%4 += MI(x) * LO(acc) << 8
A("mul %14,%1") // r1:r0 = LO(x) * MIL(acc)
- A("add %4,r0")
- A("adc %5,r1")
- A("adc %9,%13")
- A("adc %10,%13")
+ A("add %4,r0")
+ A("adc %5,r1")
+ A("adc %9,%13")
+ A("adc %10,%13")
A("adc %11,%13") // %11:%10:%9:%5:%4 = LO(x) * MIL(acc)
A("mul %15,%1") // r1:r0 = MI(x) * MIL(acc)
- A("add %5,r0")
- A("adc %9,r1")
- A("adc %10,%13")
+ A("add %5,r0")
+ A("adc %9,r1")
+ A("adc %10,%13")
A("adc %11,%13") // %11:%10:%9:%5:%4 += MI(x) * MIL(acc) << 8
A("mul %16,%1") // r1:r0 = HI(x) * MIL(acc)
- A("add %9,r0")
- A("adc %10,r1")
+ A("add %9,r0")
+ A("adc %10,r1")
A("adc %11,%13") // %11:%10:%9:%5:%4 += MI(x) * MIL(acc) << 16
A("mul %14,%2") // r1:r0 = LO(x) * MIH(acc)
- A("add %5,r0")
- A("adc %9,r1")
- A("adc %10,%13")
+ A("add %5,r0")
+ A("adc %9,r1")
+ A("adc %10,%13")
A("adc %11,%13") // %11:%10:%9:%5:%4 = LO(x) * MIH(acc) << 8
A("mul %15,%2") // r1:r0 = MI(x) * MIH(acc)
- A("add %9,r0")
- A("adc %10,r1")
+ A("add %9,r0")
+ A("adc %10,r1")
A("adc %11,%13") // %11:%10:%9:%5:%4 += MI(x) * MIH(acc) << 16
A("mul %16,%2") // r1:r0 = HI(x) * MIH(acc)
- A("add %10,r0")
+ A("add %10,r0")
A("adc %11,r1") // %11:%10:%9:%5:%4 += MI(x) * MIH(acc) << 24
A("mul %14,%3") // r1:r0 = LO(x) * HI(acc)
- A("add %9,r0")
- A("adc %10,r1")
+ A("add %9,r0")
+ A("adc %10,r1")
A("adc %11,%13") // %11:%10:%9:%5:%4 = LO(x) * HI(acc) << 16
A("mul %15,%3") // r1:r0 = MI(x) * HI(acc)
- A("add %10,r0")
+ A("add %10,r0")
A("adc %11,r1") // %11:%10:%9:%5:%4 += MI(x) * HI(acc) << 24
A("mul %16,%3") // r1:r0 = HI(x) * HI(acc)
A("add %11,r0") // %11:%10:%9:%5:%4 += MI(x) * HI(acc) << 32
@@ -635,33 +652,33 @@ void Planner::init() {
// (1<<24) - x*d
// %11:%10:%9 = x
// %8:%7:%6 = d = interval" "\n\t"
- A("ldi %3,1")
- A("clr %2")
- A("clr %1")
+ A("ldi %3,1")
+ A("clr %2")
+ A("clr %1")
A("clr %0") // %3:%2:%1:%0 = 0x1000000
A("mul %6,%9") // r1:r0 = LO(d) * LO(x)
- A("sub %0,r0")
- A("sbc %1,r1")
- A("sbc %2,%13")
+ A("sub %0,r0")
+ A("sbc %1,r1")
+ A("sbc %2,%13")
A("sbc %3,%13") // %3:%2:%1:%0 -= LO(d) * LO(x)
A("mul %7,%9") // r1:r0 = MI(d) * LO(x)
- A("sub %1,r0")
- A("sbc %2,r1")
+ A("sub %1,r0")
+ A("sbc %2,r1")
A("sbc %3,%13") // %3:%2:%1:%0 -= MI(d) * LO(x) << 8
A("mul %8,%9") // r1:r0 = HI(d) * LO(x)
- A("sub %2,r0")
+ A("sub %2,r0")
A("sbc %3,r1") // %3:%2:%1:%0 -= MIL(d) * LO(x) << 16
A("mul %6,%10") // r1:r0 = LO(d) * MI(x)
- A("sub %1,r0")
- A("sbc %2,r1")
+ A("sub %1,r0")
+ A("sbc %2,r1")
A("sbc %3,%13") // %3:%2:%1:%0 -= LO(d) * MI(x) << 8
A("mul %7,%10") // r1:r0 = MI(d) * MI(x)
- A("sub %2,r0")
+ A("sub %2,r0")
A("sbc %3,r1") // %3:%2:%1:%0 -= MI(d) * MI(x) << 16
A("mul %8,%10") // r1:r0 = HI(d) * MI(x)
A("sub %3,r0") // %3:%2:%1:%0 -= MIL(d) * MI(x) << 24
A("mul %6,%11") // r1:r0 = LO(d) * HI(x)
- A("sub %2,r0")
+ A("sub %2,r0")
A("sbc %3,r1") // %3:%2:%1:%0 -= LO(d) * HI(x) << 16
A("mul %7,%11") // r1:r0 = MI(d) * HI(x)
A("sub %3,r0") // %3:%2:%1:%0 -= MI(d) * HI(x) << 24
@@ -669,15 +686,15 @@ void Planner::init() {
// %8:%7:%6 = d = interval
// Perform the final correction
- A("sub %0,%6")
- A("sbc %1,%7")
+ A("sub %0,%6")
+ A("sbc %1,%7")
A("sbc %2,%8") // r -= d
A("brcs 14f") // if ( r >= d)
// %11:%10:%9 = x
- A("ldi %3,1")
- A("add %9,%3")
- A("adc %10,%13")
+ A("ldi %3,1")
+ A("add %9,%3")
+ A("adc %10,%13")
A("adc %11,%13") // x++
L("14")
@@ -709,7 +726,6 @@ void Planner::init() {
// Return the result
return r11 | (uint16_t(r12) << 8) | (uint32_t(r13) << 16);
}
-
#endif // BEZIER_JERK_CONTROL
#define MINIMAL_STEP_RATE 120
@@ -719,12 +735,13 @@ void Planner::init() {
* by the provided factors.
*/
void Planner::calculate_trapezoid_for_block(block_t* const block, const float &entry_factor, const float &exit_factor) {
+
uint32_t initial_rate = CEIL(block->nominal_rate * entry_factor),
final_rate = CEIL(block->nominal_rate * exit_factor); // (steps per second)
// Limit minimal step rate (Otherwise the timer will overflow.)
- NOLESS(initial_rate, MINIMAL_STEP_RATE);
- NOLESS(final_rate, MINIMAL_STEP_RATE);
+ NOLESS(initial_rate, uint32_t(MINIMAL_STEP_RATE));
+ NOLESS(final_rate, uint32_t(MINIMAL_STEP_RATE));
#if ENABLED(BEZIER_JERK_CONTROL)
uint32_t cruise_rate = initial_rate;
@@ -733,19 +750,18 @@ void Planner::calculate_trapezoid_for_block(block_t* const block, const float &e
const int32_t accel = block->acceleration_steps_per_s2;
// Steps required for acceleration, deceleration to/from nominal rate
- int32_t accelerate_steps = CEIL(estimate_acceleration_distance(initial_rate, block->nominal_rate, accel)),
- decelerate_steps = FLOOR(estimate_acceleration_distance(block->nominal_rate, final_rate, -accel)),
+ uint32_t accelerate_steps = CEIL(estimate_acceleration_distance(initial_rate, block->nominal_rate, accel)),
+ decelerate_steps = FLOOR(estimate_acceleration_distance(block->nominal_rate, final_rate, -accel));
// Steps between acceleration and deceleration, if any
- plateau_steps = block->step_event_count - accelerate_steps - decelerate_steps;
+ int32_t plateau_steps = block->step_event_count - accelerate_steps - decelerate_steps;
// Does accelerate_steps + decelerate_steps exceed step_event_count?
// Then we can't possibly reach the nominal rate, there will be no cruising.
// Use intersection_distance() to calculate accel / braking time in order to
// reach the final_rate exactly at the end of this block.
if (plateau_steps < 0) {
- accelerate_steps = CEIL(intersection_distance(initial_rate, final_rate, accel, block->step_event_count));
- NOLESS(accelerate_steps, 0); // Check limits due to numerical round-off
- accelerate_steps = min((uint32_t)accelerate_steps, block->step_event_count);//(We can cast here to unsigned, because the above line ensures that we are above zero)
+ const float accelerate_steps_float = CEIL(intersection_distance(initial_rate, final_rate, accel, block->step_event_count));
+ accelerate_steps = MIN(uint32_t(MAX(accelerate_steps_float, 0)), block->step_event_count);
plateau_steps = 0;
#if ENABLED(BEZIER_JERK_CONTROL)
@@ -772,8 +788,12 @@ void Planner::calculate_trapezoid_for_block(block_t* const block, const float &e
#endif
- CRITICAL_SECTION_START; // Fill variables used by the stepper in a critical section
- if (!TEST(block->flag, BLOCK_BIT_BUSY)) { // Don't update variables if block is busy.
+ // Fill variables used by the stepper in a critical section
+ const bool was_enabled = STEPPER_ISR_ENABLED();
+ if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT();
+
+ // Don't update variables if block is busy: It is being interpreted by the planner
+ if (!TEST(block->flag, BLOCK_BIT_BUSY)) {
block->accelerate_until = accelerate_steps;
block->decelerate_after = accelerate_steps + plateau_steps;
block->initial_rate = initial_rate;
@@ -786,100 +806,209 @@ void Planner::calculate_trapezoid_for_block(block_t* const block, const float &e
#endif
block->final_rate = final_rate;
}
- CRITICAL_SECTION_END;
+ if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT();
}
-// "Junction jerk" in this context is the immediate change in speed at the junction of two blocks.
-// This method will calculate the junction jerk as the euclidean distance between the nominal
-// velocities of the respective blocks.
-//inline float junction_jerk(block_t *before, block_t *after) {
-// return SQRT(
-// POW((before->speed_x-after->speed_x), 2)+POW((before->speed_y-after->speed_y), 2));
-//}
+/* PLANNER SPEED DEFINITION
+ +--------+ <- current->nominal_speed
+ / \
+ current->entry_speed -> + \
+ | + <- next->entry_speed (aka exit speed)
+ +-------------+
+ time -->
+ Recalculates the motion plan according to the following basic guidelines:
+
+ 1. Go over every feasible block sequentially in reverse order and calculate the junction speeds
+ (i.e. current->entry_speed) such that:
+ a. No junction speed exceeds the pre-computed maximum junction speed limit or nominal speeds of
+ neighboring blocks.
+ b. A block entry speed cannot exceed one reverse-computed from its exit speed (next->entry_speed)
+ with a maximum allowable deceleration over the block travel distance.
+ c. The last (or newest appended) block is planned from a complete stop (an exit speed of zero).
+ 2. Go over every block in chronological (forward) order and dial down junction speed values if
+ a. The exit speed exceeds the one forward-computed from its entry speed with the maximum allowable
+ acceleration over the block travel distance.
+
+ When these stages are complete, the planner will have maximized the velocity profiles throughout the all
+ of the planner blocks, where every block is operating at its maximum allowable acceleration limits. In
+ other words, for all of the blocks in the planner, the plan is optimal and no further speed improvements
+ are possible. If a new block is added to the buffer, the plan is recomputed according to the said
+ guidelines for a new optimal plan.
+
+ To increase computational efficiency of these guidelines, a set of planner block pointers have been
+ created to indicate stop-compute points for when the planner guidelines cannot logically make any further
+ changes or improvements to the plan when in normal operation and new blocks are streamed and added to the
+ planner buffer. For example, if a subset of sequential blocks in the planner have been planned and are
+ bracketed by junction velocities at their maximums (or by the first planner block as well), no new block
+ added to the planner buffer will alter the velocity profiles within them. So we no longer have to compute
+ them. Or, if a set of sequential blocks from the first block in the planner (or a optimal stop-compute
+ point) are all accelerating, they are all optimal and can not be altered by a new block added to the
+ planner buffer, as this will only further increase the plan speed to chronological blocks until a maximum
+ junction velocity is reached. However, if the operational conditions of the plan changes from infrequently
+ used feed holds or feedrate overrides, the stop-compute pointers will be reset and the entire plan is
+ recomputed as stated in the general guidelines.
+
+ Planner buffer index mapping:
+ - block_buffer_tail: Points to the beginning of the planner buffer. First to be executed or being executed.
+ - block_buffer_head: Points to the buffer block after the last block in the buffer. Used to indicate whether
+ the buffer is full or empty. As described for standard ring buffers, this block is always empty.
+ - block_buffer_planned: Points to the first buffer block after the last optimally planned block for normal
+ streaming operating conditions. Use for planning optimizations by avoiding recomputing parts of the
+ planner buffer that don't change with the addition of a new block, as describe above. In addition,
+ this block can never be less than block_buffer_tail and will always be pushed forward and maintain
+ this requirement when encountered by the plan_discard_current_block() routine during a cycle.
+
+ NOTE: Since the planner only computes on what's in the planner buffer, some motions with lots of short
+ line segments, like G2/3 arcs or complex curves, may seem to move slow. This is because there simply isn't
+ enough combined distance traveled in the entire buffer to accelerate up to the nominal speed and then
+ decelerate to a complete stop at the end of the buffer, as stated by the guidelines. If this happens and
+ becomes an annoyance, there are a few simple solutions: (1) Maximize the machine acceleration. The planner
+ will be able to compute higher velocity profiles within the same combined distance. (2) Maximize line
+ motion(s) distance per block to a desired tolerance. The more combined distance the planner has to use,
+ the faster it can go. (3) Maximize the planner buffer size. This also will increase the combined distance
+ for the planner to compute over. It also increases the number of computations the planner has to perform
+ to compute an optimal plan, so select carefully.
+*/
// The kernel called by recalculate() when scanning the plan from last to first entry.
void Planner::reverse_pass_kernel(block_t* const current, const block_t * const next) {
- if (!current || !next) return;
- // If entry speed is already at the maximum entry speed, no need to recheck. Block is cruising.
- // If not, block in state of acceleration or deceleration. Reset entry speed to maximum and
- // check for maximum allowable speed reductions to ensure maximum possible planned speed.
- float max_entry_speed = current->max_entry_speed;
- if (current->entry_speed != max_entry_speed) {
- // If nominal length true, max junction speed is guaranteed to be reached. Only compute
- // for max allowable speed if block is decelerating and nominal length is false.
- current->entry_speed = (TEST(current->flag, BLOCK_BIT_NOMINAL_LENGTH) || max_entry_speed <= next->entry_speed)
- ? max_entry_speed
- : min(max_entry_speed, max_allowable_speed(-current->acceleration, next->entry_speed, current->millimeters));
- SBI(current->flag, BLOCK_BIT_RECALCULATE);
- }
-}
+ if (current) {
+ // If entry speed is already at the maximum entry speed, and there was no change of speed
+ // in the next block, there is no need to recheck. Block is cruising and there is no need to
+ // compute anything for this block,
+ // If not, block entry speed needs to be recalculated to ensure maximum possible planned speed.
+ const float max_entry_speed_sqr = current->max_entry_speed_sqr;
-/**
- * recalculate() needs to go over the current plan twice.
- * Once in reverse and once forward. This implements the reverse pass.
- */
-void Planner::reverse_pass() {
- if (movesplanned() > 2) {
- const uint8_t endnr = BLOCK_MOD(block_buffer_tail + 1); // tail is running. tail+1 shouldn't be altered because it's connected to the running block.
- uint8_t blocknr = prev_block_index(block_buffer_head);
- block_t* current = &block_buffer[blocknr];
+ // Compute maximum entry speed decelerating over the current block from its exit speed.
+ // If not at the maximum entry speed, or the previous block entry speed changed
+ if (current->entry_speed_sqr != max_entry_speed_sqr || (next && TEST(next->flag, BLOCK_BIT_RECALCULATE))) {
- // Last/newest block in buffer:
- const float max_entry_speed = current->max_entry_speed;
- if (current->entry_speed != max_entry_speed) {
- // If nominal length true, max junction speed is guaranteed to be reached. Only compute
- // for max allowable speed if block is decelerating and nominal length is false.
- current->entry_speed = TEST(current->flag, BLOCK_BIT_NOMINAL_LENGTH)
- ? max_entry_speed
- : min(max_entry_speed, max_allowable_speed(-current->acceleration, MINIMUM_PLANNER_SPEED, current->millimeters));
- SBI(current->flag, BLOCK_BIT_RECALCULATE);
- }
+ // If nominal length true, max junction speed is guaranteed to be reached.
+ // If a block can de/ac-celerate from nominal speed to zero within the length of the block, then
+ // the current block and next block junction speeds are guaranteed to always be at their maximum
+ // junction speeds in deceleration and acceleration, respectively. This is due to how the current
+ // block nominal speed limits both the current and next maximum junction speeds. Hence, in both
+ // the reverse and forward planners, the corresponding block junction speed will always be at the
+ // the maximum junction speed and may always be ignored for any speed reduction checks.
- do {
- const block_t * const next = current;
- blocknr = prev_block_index(blocknr);
- current = &block_buffer[blocknr];
- reverse_pass_kernel(current, next);
- } while (blocknr != endnr);
- }
-}
+ const float new_entry_speed_sqr = TEST(current->flag, BLOCK_BIT_NOMINAL_LENGTH)
+ ? max_entry_speed_sqr
+ : MIN(max_entry_speed_sqr, max_allowable_speed_sqr(-current->acceleration, next ? next->entry_speed_sqr : sq(MINIMUM_PLANNER_SPEED), current->millimeters));
+ if (current->entry_speed_sqr != new_entry_speed_sqr) {
+ current->entry_speed_sqr = new_entry_speed_sqr;
-// The kernel called by recalculate() when scanning the plan from first to last entry.
-void Planner::forward_pass_kernel(const block_t * const previous, block_t* const current) {
- if (!previous) return;
-
- // If the previous block is an acceleration block, but it is not long enough to complete the
- // full speed change within the block, we need to adjust the entry speed accordingly. Entry
- // speeds have already been reset, maximized, and reverse planned by reverse planner.
- // If nominal length is true, max junction speed is guaranteed to be reached. No need to recheck.
- if (!TEST(previous->flag, BLOCK_BIT_NOMINAL_LENGTH)) {
- if (previous->entry_speed < current->entry_speed) {
- float entry_speed = min(current->entry_speed,
- max_allowable_speed(-previous->acceleration, previous->entry_speed, previous->millimeters));
- // Check for junction speed change
- if (current->entry_speed != entry_speed) {
- current->entry_speed = entry_speed;
+ // Need to recalculate the block speed
SBI(current->flag, BLOCK_BIT_RECALCULATE);
}
}
}
}
+/**
+ * recalculate() needs to go over the current plan twice.
+ * Once in reverse and once forward. This implements the reverse pass.
+ */
+void Planner::reverse_pass() {
+ // Initialize block index to the last block in the planner buffer.
+ uint8_t block_index = prev_block_index(block_buffer_head);
+
+ // Read the index of the last buffer planned block.
+ // The ISR may change it so get a stable local copy.
+ uint8_t planned_block_index = block_buffer_planned;
+
+ // If there was a race condition and block_buffer_planned was incremented
+ // or was pointing at the head (queue empty) break loop now and avoid
+ // planning already consumed blocks
+ if (planned_block_index == block_buffer_head) return;
+
+ // Reverse Pass: Coarsely maximize all possible deceleration curves back-planning from the last
+ // block in buffer. Cease planning when the last optimal planned or tail pointer is reached.
+ // NOTE: Forward pass will later refine and correct the reverse pass to create an optimal plan.
+ block_t *current;
+ const block_t *next = NULL;
+ while (block_index != planned_block_index) {
+
+ // Perform the reverse pass
+ current = &block_buffer[block_index];
+
+ // Only consider non sync blocks
+ if (!TEST(current->flag, BLOCK_BIT_SYNC_POSITION)) {
+ reverse_pass_kernel(current, next);
+ next = current;
+ }
+
+ // Advance to the next
+ block_index = prev_block_index(block_index);
+ }
+}
+
+// The kernel called by recalculate() when scanning the plan from first to last entry.
+void Planner::forward_pass_kernel(const block_t* const previous, block_t* const current, const uint8_t block_index) {
+ if (previous) {
+ // If the previous block is an acceleration block, too short to complete the full speed
+ // change, adjust the entry speed accordingly. Entry speeds have already been reset,
+ // maximized, and reverse-planned. If nominal length is set, max junction speed is
+ // guaranteed to be reached. No need to recheck.
+ if (!TEST(previous->flag, BLOCK_BIT_NOMINAL_LENGTH) &&
+ previous->entry_speed_sqr < current->entry_speed_sqr) {
+
+ // Compute the maximum allowable speed
+ const float new_entry_speed_sqr = max_allowable_speed_sqr(-previous->acceleration, previous->entry_speed_sqr, previous->millimeters);
+
+ // If true, current block is full-acceleration and we can move the planned pointer forward.
+ if (new_entry_speed_sqr < current->entry_speed_sqr) {
+
+ // Always <= max_entry_speed_sqr. Backward pass sets this.
+ current->entry_speed_sqr = new_entry_speed_sqr; // Always <= max_entry_speed_sqr. Backward pass sets this.
+
+ // Set optimal plan pointer.
+ block_buffer_planned = block_index;
+
+ // And mark we need to recompute the trapezoidal shape
+ SBI(current->flag, BLOCK_BIT_RECALCULATE);
+ }
+ }
+
+ // Any block set at its maximum entry speed also creates an optimal plan up to this
+ // point in the buffer. When the plan is bracketed by either the beginning of the
+ // buffer and a maximum entry speed or two maximum entry speeds, every block in between
+ // cannot logically be further improved. Hence, we don't have to recompute them anymore.
+ if (current->entry_speed_sqr == current->max_entry_speed_sqr)
+ block_buffer_planned = block_index;
+ }
+}
+
/**
* recalculate() needs to go over the current plan twice.
* Once in reverse and once forward. This implements the forward pass.
*/
void Planner::forward_pass() {
- block_t* block[3] = { NULL, NULL, NULL };
- for (uint8_t b = block_buffer_tail; b != block_buffer_head; b = next_block_index(b)) {
- block[0] = block[1];
- block[1] = block[2];
- block[2] = &block_buffer[b];
- forward_pass_kernel(block[0], block[1]);
+ // Forward Pass: Forward plan the acceleration curve from the planned pointer onward.
+ // Also scans for optimal plan breakpoints and appropriately updates the planned pointer.
+
+ // Begin at buffer planned pointer. Note that block_buffer_planned can be modified
+ // by the stepper ISR, so read it ONCE. It it guaranteed that block_buffer_planned
+ // will never lead head, so the loop is safe to execute. Also note that the forward
+ // pass will never modify the values at the tail.
+ uint8_t block_index = block_buffer_planned;
+
+ block_t *current;
+ const block_t * previous = NULL;
+ while (block_index != block_buffer_head) {
+
+ // Perform the forward pass
+ current = &block_buffer[block_index];
+
+ // Skip SYNC blocks
+ if (!TEST(current->flag, BLOCK_BIT_SYNC_POSITION)) {
+ forward_pass_kernel(previous, current, block_index);
+ previous = current;
+ }
+ // Advance to the previous
+ block_index = next_block_index(block_index);
}
- forward_pass_kernel(block[1], block[2]);
}
/**
@@ -888,38 +1017,73 @@ void Planner::forward_pass() {
* recalculate() after updating the blocks.
*/
void Planner::recalculate_trapezoids() {
- int8_t block_index = block_buffer_tail;
- block_t *current, *next = NULL;
+ // The tail may be changed by the ISR so get a local copy.
+ uint8_t block_index = block_buffer_tail;
+
+ // As there could be a sync block in the head of the queue, and the next loop must not
+ // recalculate the head block (as it needs to be specially handled), scan backwards until
+ // we find the first non SYNC block
+ uint8_t head_block_index = block_buffer_head;
+ while (head_block_index != block_index) {
+
+ // Go back (head always point to the first free block)
+ uint8_t prev_index = prev_block_index(head_block_index);
+
+ // Get the pointer to the block
+ block_t *prev = &block_buffer[prev_index];
+
+ // If not dealing with a sync block, we are done. The last block is not a SYNC block
+ if (!TEST(prev->flag, BLOCK_BIT_SYNC_POSITION)) break;
+
+ // Examine the previous block. This and all following are SYNC blocks
+ head_block_index = prev_index;
+ };
+
+ // Go from the tail (currently executed block) to the first block, without including it)
+ block_t *current = NULL, *next = NULL;
+ float current_entry_speed = 0.0, next_entry_speed = 0.0;
+ while (block_index != head_block_index) {
- while (block_index != block_buffer_head) {
- current = next;
next = &block_buffer[block_index];
- if (current) {
- // Recalculate if current block entry or exit junction speed has changed.
- if (TEST(current->flag, BLOCK_BIT_RECALCULATE) || TEST(next->flag, BLOCK_BIT_RECALCULATE)) {
- // NOTE: Entry and exit factors always > 0 by all previous logic operations.
- const float nomr = 1.0 / current->nominal_speed;
- calculate_trapezoid_for_block(current, current->entry_speed * nomr, next->entry_speed * nomr);
- #if ENABLED(LIN_ADVANCE)
- if (current->use_advance_lead) {
- const float comp = current->e_D_ratio * extruder_advance_K * axis_steps_per_mm[E_AXIS];
- current->max_adv_steps = current->nominal_speed * comp;
- current->final_adv_steps = next->entry_speed * comp;
- }
- #endif
- CBI(current->flag, BLOCK_BIT_RECALCULATE); // Reset current only to ensure next trapezoid is computed
+
+ // Skip sync blocks
+ if (!TEST(next->flag, BLOCK_BIT_SYNC_POSITION)) {
+ next_entry_speed = SQRT(next->entry_speed_sqr);
+
+ if (current) {
+ // Recalculate if current block entry or exit junction speed has changed.
+ if (TEST(current->flag, BLOCK_BIT_RECALCULATE) || TEST(next->flag, BLOCK_BIT_RECALCULATE)) {
+ // NOTE: Entry and exit factors always > 0 by all previous logic operations.
+ const float current_nominal_speed = SQRT(current->nominal_speed_sqr),
+ nomr = 1.0 / current_nominal_speed;
+ calculate_trapezoid_for_block(current, current_entry_speed * nomr, next_entry_speed * nomr);
+ #if ENABLED(LIN_ADVANCE)
+ if (current->use_advance_lead) {
+ const float comp = current->e_D_ratio * extruder_advance_K * axis_steps_per_mm[E_AXIS];
+ current->max_adv_steps = current_nominal_speed * comp;
+ current->final_adv_steps = next_entry_speed * comp;
+ }
+ #endif
+ CBI(current->flag, BLOCK_BIT_RECALCULATE); // Reset current only to ensure next trapezoid is computed
+ }
}
+
+ current = next;
+ current_entry_speed = next_entry_speed;
}
+
block_index = next_block_index(block_index);
}
+
// Last/newest block in buffer. Exit speed is set with MINIMUM_PLANNER_SPEED. Always recalculated.
if (next) {
- const float nomr = 1.0 / next->nominal_speed;
- calculate_trapezoid_for_block(next, next->entry_speed * nomr, (MINIMUM_PLANNER_SPEED) * nomr);
+ const float next_nominal_speed = SQRT(next->nominal_speed_sqr),
+ nomr = 1.0 / next_nominal_speed;
+ calculate_trapezoid_for_block(next, next_entry_speed * nomr, (MINIMUM_PLANNER_SPEED) * nomr);
#if ENABLED(LIN_ADVANCE)
if (next->use_advance_lead) {
const float comp = next->e_D_ratio * extruder_advance_K * axis_steps_per_mm[E_AXIS];
- next->max_adv_steps = next->nominal_speed * comp;
+ next->max_adv_steps = next_nominal_speed * comp;
next->final_adv_steps = (MINIMUM_PLANNER_SPEED) * comp;
}
#endif
@@ -927,33 +1091,14 @@ void Planner::recalculate_trapezoids() {
}
}
-/**
- * Recalculate the motion plan according to the following algorithm:
- *
- * 1. Go over every block in reverse order...
- *
- * Calculate a junction speed reduction (block_t.entry_factor) so:
- *
- * a. The junction jerk is within the set limit, and
- *
- * b. No speed reduction within one block requires faster
- * deceleration than the one, true constant acceleration.
- *
- * 2. Go over every block in chronological order...
- *
- * Dial down junction speed reduction values if:
- * a. The speed increase within one block would require faster
- * acceleration than the one, true constant acceleration.
- *
- * After that, all blocks will have an entry_factor allowing all speed changes to
- * be performed using only the one, true constant acceleration, and where no junction
- * jerk is jerkier than the set limit, Jerky. Finally it will:
- *
- * 3. Recalculate "trapezoids" for all blocks.
- */
void Planner::recalculate() {
- reverse_pass();
- forward_pass();
+ // Initialize block index to the last block in the planner buffer.
+ const uint8_t block_index = prev_block_index(block_buffer_head);
+ // If there is just one block, no planning can be done. Avoid it!
+ if (block_index != block_buffer_planned) {
+ reverse_pass();
+ forward_pass();
+ }
recalculate_trapezoids();
}
@@ -969,7 +1114,7 @@ void Planner::recalculate() {
for (uint8_t b = block_buffer_tail; b != block_buffer_head; b = next_block_index(b)) {
block_t* block = &block_buffer[b];
if (block->steps[X_AXIS] || block->steps[Y_AXIS] || block->steps[Z_AXIS]) {
- float se = (float)block->steps[E_AXIS] / block->step_event_count * block->nominal_speed; // mm/sec;
+ const float se = (float)block->steps[E_AXIS] / block->step_event_count * SQRT(block->nominal_speed_sqr); // mm/sec;
NOLESS(high, se);
}
}
@@ -1079,8 +1224,8 @@ void Planner::check_axes_activity() {
#endif // FAN_KICKSTART_TIME > 0
- #ifdef FAN_MIN_PWM
- #define CALC_FAN_SPEED(f) (tail_fan_speed[f] ? ( FAN_MIN_PWM + (tail_fan_speed[f] * (255 - FAN_MIN_PWM)) / 255 ) : 0)
+ #if FAN_MIN_PWM != 0 || FAN_MAX_PWM != 255
+ #define CALC_FAN_SPEED(f) (tail_fan_speed[f] ? map(tail_fan_speed[f], 1, 255, FAN_MIN_PWM, FAN_MAX_PWM) : 0)
#else
#define CALC_FAN_SPEED(f) tail_fan_speed[f]
#endif
@@ -1270,16 +1415,160 @@ void Planner::check_axes_activity() {
#endif // PLANNER_LEVELING
+void Planner::quick_stop() {
+
+ // Remove all the queued blocks. Note that this function is NOT
+ // called from the Stepper ISR, so we must consider tail as readonly!
+ // that is why we set head to tail - But there is a race condition that
+ // must be handled: The tail could change between the read and the assignment
+ // so this must be enclosed in a critical section
+
+ const bool was_enabled = STEPPER_ISR_ENABLED();
+ if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT();
+
+ // Drop all queue entries
+ block_buffer_planned = block_buffer_head = block_buffer_tail;
+
+ // Restart the block delay for the first movement - As the queue was
+ // forced to empty, there's no risk the ISR will touch this.
+ delay_before_delivering = BLOCK_DELAY_FOR_1ST_MOVE;
+
+ #if ENABLED(ULTRA_LCD)
+ // Clear the accumulated runtime
+ clear_block_buffer_runtime();
+ #endif
+
+ // Make sure to drop any attempt of queuing moves for at least 1 second
+ cleaning_buffer_counter = 1000;
+
+ // Reenable Stepper ISR
+ if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT();
+
+ // And stop the stepper ISR
+ stepper.quick_stop();
+}
+
+void Planner::endstop_triggered(const AxisEnum axis) {
+ // Record stepper position and discard the current block
+ stepper.endstop_triggered(axis);
+}
+
+float Planner::triggered_position_mm(const AxisEnum axis) {
+ return stepper.triggered_position(axis) * steps_to_mm[axis];
+}
+
+void Planner::finish_and_disable() {
+ while (has_blocks_queued() || cleaning_buffer_counter) idle();
+ disable_all_steppers();
+}
+
+/**
+ * Get an axis position according to stepper position(s)
+ * For CORE machines apply translation from ABC to XYZ.
+ */
+float Planner::get_axis_position_mm(const AxisEnum axis) {
+ float axis_steps;
+ #if IS_CORE
+ // Requesting one of the "core" axes?
+ if (axis == CORE_AXIS_1 || axis == CORE_AXIS_2) {
+
+ // Protect the access to the position.
+ const bool was_enabled = STEPPER_ISR_ENABLED();
+ if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT();
+
+ // ((a1+a2)+(a1-a2))/2 -> (a1+a2+a1-a2)/2 -> (a1+a1)/2 -> a1
+ // ((a1+a2)-(a1-a2))/2 -> (a1+a2-a1+a2)/2 -> (a2+a2)/2 -> a2
+ axis_steps = 0.5f * (
+ axis == CORE_AXIS_2 ? CORESIGN(stepper.position(CORE_AXIS_1) - stepper.position(CORE_AXIS_2))
+ : stepper.position(CORE_AXIS_1) + stepper.position(CORE_AXIS_2)
+ );
+
+ if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT();
+ }
+ else
+ axis_steps = stepper.position(axis);
+ #else
+ axis_steps = stepper.position(axis);
+ #endif
+ return axis_steps * steps_to_mm[axis];
+}
+
+/**
+ * Block until all buffered steps are executed / cleaned
+ */
+void Planner::synchronize() { while (has_blocks_queued() || cleaning_buffer_counter) idle(); }
+
/**
* Planner::_buffer_steps
*
- * Add a new linear movement to the buffer (in terms of steps).
+ * Add a new linear movement to the planner queue (in terms of steps).
*
* target - target position in steps units
* fr_mm_s - (target) speed of the move
* extruder - target extruder
+ * millimeters - the length of the movement, if known
+ *
+ * Returns true if movement was properly queued, false otherwise
*/
-void Planner::_buffer_steps(const int32_t (&target)[XYZE]
+bool Planner::_buffer_steps(const int32_t (&target)[XYZE]
+ #if HAS_POSITION_FLOAT
+ , const float (&target_float)[XYZE]
+ #endif
+ , float fr_mm_s, const uint8_t extruder, const float &millimeters
+) {
+
+ // If we are cleaning, do not accept queuing of movements
+ if (cleaning_buffer_counter) return false;
+
+ // Wait for the next available block
+ uint8_t next_buffer_head;
+ block_t * const block = get_next_free_block(next_buffer_head);
+
+ // Fill the block with the specified movement
+ if (!_populate_block(block, false, target
+ #if HAS_POSITION_FLOAT
+ , target_float
+ #endif
+ , fr_mm_s, extruder, millimeters
+ )) {
+ // Movement was not queued, probably because it was too short.
+ // Simply accept that as movement queued and done
+ return true;
+ }
+
+ // If this is the first added movement, reload the delay, otherwise, cancel it.
+ if (block_buffer_head == block_buffer_tail) {
+ // If it was the first queued block, restart the 1st block delivery delay, to
+ // give the planner an opportunity to queue more movements and plan them
+ // As there are no queued movements, the Stepper ISR will not touch this
+ // variable, so there is no risk setting this here (but it MUST be done
+ // before the following line!!)
+ delay_before_delivering = BLOCK_DELAY_FOR_1ST_MOVE;
+ }
+
+ // Move buffer head
+ block_buffer_head = next_buffer_head;
+
+ // Recalculate and optimize trapezoidal speed profiles
+ recalculate();
+
+ // Movement successfully queued!
+ return true;
+}
+
+/**
+ * Planner::_populate_block
+ *
+ * Fills a new linear movement in the block (in terms of steps).
+ *
+ * target - target position in steps units
+ * fr_mm_s - (target) speed of the move
+ * extruder - target extruder
+ *
+ * Returns true is movement is acceptable, false otherwise
+ */
+bool Planner::_populate_block(block_t * const block, bool split_move,
+ const int32_t (&target)[XYZE]
#if HAS_POSITION_FLOAT
, const float (&target_float)[XYZE]
#endif
@@ -1293,7 +1582,7 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
int32_t de = target[E_AXIS] - position[E_AXIS];
/* <-- add a slash to enable
- SERIAL_ECHOPAIR(" _buffer_steps FR:", fr_mm_s);
+ SERIAL_ECHOPAIR(" _populate_block FR:", fr_mm_s);
SERIAL_ECHOPAIR(" A:", target[A_AXIS]);
SERIAL_ECHOPAIR(" (", da);
SERIAL_ECHOPAIR(" steps) B:", target[B_AXIS]);
@@ -1319,7 +1608,7 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
}
#endif // PREVENT_COLD_EXTRUSION
#if ENABLED(PREVENT_LENGTHY_EXTRUDE)
- if (labs(de * e_factor[extruder]) > (int32_t)axis_steps_per_mm[E_AXIS_N] * (EXTRUDE_MAXLENGTH)) { // It's not important to get max. extrusion length in a precision < 1mm, so save some cycles and cast to int
+ if (ABS(de * e_factor[extruder]) > (int32_t)axis_steps_per_mm[E_AXIS_N] * (EXTRUDE_MAXLENGTH)) { // It's not important to get max. extrusion length in a precision < 1mm, so save some cycles and cast to int
position[E_AXIS] = target[E_AXIS]; // Behave as if the move really took place, but ignore E part
#if HAS_POSITION_FLOAT
position_float[E_AXIS] = target_float[E_AXIS];
@@ -1360,11 +1649,7 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
if (de < 0) SBI(dm, E_AXIS);
const float esteps_float = de * e_factor[extruder];
- const int32_t esteps = abs(esteps_float) + 0.5;
-
- // Wait for the next available block
- uint8_t next_buffer_head;
- block_t * const block = get_next_free_block(next_buffer_head);
+ const uint32_t esteps = ABS(esteps_float) + 0.5;
// Clear all flags, including the "busy" bit
block->flag = 0x00;
@@ -1375,33 +1660,33 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
// Number of steps for each axis
// See http://www.corexy.com/theory.html
#if CORE_IS_XY
- block->steps[A_AXIS] = labs(da + db);
- block->steps[B_AXIS] = labs(da - db);
- block->steps[Z_AXIS] = labs(dc);
+ block->steps[A_AXIS] = ABS(da + db);
+ block->steps[B_AXIS] = ABS(da - db);
+ block->steps[Z_AXIS] = ABS(dc);
#elif CORE_IS_XZ
- block->steps[A_AXIS] = labs(da + dc);
- block->steps[Y_AXIS] = labs(db);
- block->steps[C_AXIS] = labs(da - dc);
+ block->steps[A_AXIS] = ABS(da + dc);
+ block->steps[Y_AXIS] = ABS(db);
+ block->steps[C_AXIS] = ABS(da - dc);
#elif CORE_IS_YZ
- block->steps[X_AXIS] = labs(da);
- block->steps[B_AXIS] = labs(db + dc);
- block->steps[C_AXIS] = labs(db - dc);
+ block->steps[X_AXIS] = ABS(da);
+ block->steps[B_AXIS] = ABS(db + dc);
+ block->steps[C_AXIS] = ABS(db - dc);
#elif IS_SCARA
- block->steps[A_AXIS] = labs(da);
- block->steps[B_AXIS] = labs(db);
- block->steps[Z_AXIS] = labs(dc);
+ block->steps[A_AXIS] = ABS(da);
+ block->steps[B_AXIS] = ABS(db);
+ block->steps[Z_AXIS] = ABS(dc);
#else
// default non-h-bot planning
- block->steps[A_AXIS] = labs(da);
- block->steps[B_AXIS] = labs(db);
- block->steps[C_AXIS] = labs(dc);
+ block->steps[A_AXIS] = ABS(da);
+ block->steps[B_AXIS] = ABS(db);
+ block->steps[C_AXIS] = ABS(dc);
#endif
block->steps[E_AXIS] = esteps;
block->step_event_count = MAX4(block->steps[A_AXIS], block->steps[B_AXIS], block->steps[C_AXIS], esteps);
// Bail if this is a zero-length block
- if (block->step_event_count < MIN_STEPS_PER_SEGMENT) return;
+ if (block->step_event_count < MIN_STEPS_PER_SEGMENT) return false;
// For a mixing extruder, get a magnified step_event_count for each
#if ENABLED(MIXING_EXTRUDER)
@@ -1595,7 +1880,7 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
delta_mm[E_AXIS] = esteps_float * steps_to_mm[E_AXIS_N];
if (block->steps[A_AXIS] < MIN_STEPS_PER_SEGMENT && block->steps[B_AXIS] < MIN_STEPS_PER_SEGMENT && block->steps[C_AXIS] < MIN_STEPS_PER_SEGMENT) {
- block->millimeters = FABS(delta_mm[E_AXIS]);
+ block->millimeters = ABS(delta_mm[E_AXIS]);
}
else if (!millimeters) {
block->millimeters = SQRT(
@@ -1641,12 +1926,16 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
#endif
#if ENABLED(ULTRA_LCD)
- CRITICAL_SECTION_START
- block_buffer_runtime_us += segment_time_us;
- CRITICAL_SECTION_END
+ // Protect the access to the position.
+ const bool was_enabled = STEPPER_ISR_ENABLED();
+ if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT();
+
+ block_buffer_runtime_us += segment_time_us;
+
+ if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT();
#endif
- block->nominal_speed = block->millimeters * inverse_secs; // (mm/sec) Always > 0
+ block->nominal_speed_sqr = sq(block->millimeters * inverse_secs); // (mm/sec)^2 Always > 0
block->nominal_rate = CEIL(block->step_event_count * inverse_secs); // (step/sec) Always > 0
#if ENABLED(FILAMENT_WIDTH_SENSOR)
@@ -1686,7 +1975,7 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
// Calculate and limit speed in mm/sec for each axis
float current_speed[NUM_AXIS], speed_factor = 1.0; // factor <1 decreases speed
LOOP_XYZE(i) {
- const float cs = FABS((current_speed[i] = delta_mm[i] * inverse_secs));
+ const float cs = ABS((current_speed[i] = delta_mm[i] * inverse_secs));
#if ENABLED(DISTINCT_E_FACTORS)
if (i == E_AXIS) i += extruder;
#endif
@@ -1724,7 +2013,7 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
const uint32_t max_x_segment_time = MAX3(xs0, xs1, xs2),
max_y_segment_time = MAX3(ys0, ys1, ys2),
- min_xy_segment_time = min(max_x_segment_time, max_y_segment_time);
+ min_xy_segment_time = MIN(max_x_segment_time, max_y_segment_time);
if (min_xy_segment_time < MAX_FREQ_TIME_US) {
const float low_sf = speed_factor * min_xy_segment_time / (MAX_FREQ_TIME_US);
NOMORE(speed_factor, low_sf);
@@ -1734,8 +2023,8 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
// Correct the speed
if (speed_factor < 1.0) {
LOOP_XYZE(i) current_speed[i] *= speed_factor;
- block->nominal_speed *= speed_factor;
block->nominal_rate *= speed_factor;
+ block->nominal_speed_sqr = block->nominal_speed_sqr * sq(speed_factor);
}
// Compute and limit the acceleration rate for the trapezoid generator.
@@ -1830,13 +2119,13 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
block->acceleration_steps_per_s2 = accel;
block->acceleration = accel / steps_per_mm;
#if DISABLED(BEZIER_JERK_CONTROL)
- block->acceleration_rate = (long)(accel * (4096.0 * 4096.0 / (HAL_STEPPER_TIMER_RATE))); // * 8.388608
+ block->acceleration_rate = (uint32_t)(accel * (4096.0 * 4096.0 / (HAL_STEPPER_TIMER_RATE)));
#endif
#if ENABLED(LIN_ADVANCE)
if (block->use_advance_lead) {
- block->advance_speed = (HAL_STEPPER_TIMER_RATE) / (extruder_advance_K * block->e_D_ratio * block->acceleration * axis_steps_per_mm[E_AXIS]);
+ block->advance_speed = (HAL_STEPPER_TIMER_RATE) / (extruder_advance_K * block->e_D_ratio * block->acceleration * axis_steps_per_mm[E_AXIS_N]);
#if ENABLED(LA_DEBUG)
- if (extruder_advance_K * block->e_D_ratio * block->acceleration * 2 < block->nominal_speed * block->e_D_ratio)
+ if (extruder_advance_K * block->e_D_ratio * block->acceleration * 2 < SQRT(block->nominal_speed_sqr) * block->e_D_ratio)
SERIAL_ECHOLNPGM("More than 2 steps per eISR loop executed.");
if (block->advance_speed < 200)
SERIAL_ECHOLNPGM("eISR running at > 10kHz.");
@@ -1844,33 +2133,43 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
}
#endif
- float vmax_junction; // Initial limit on the segment entry velocity
+ float vmax_junction_sqr; // Initial limit on the segment entry velocity (mm/s)^2
#if ENABLED(JUNCTION_DEVIATION)
/**
* Compute maximum allowable entry speed at junction by centripetal acceleration approximation.
- * Let a circle be tangent to both previous and current path line segments, where the junction
- * deviation is defined as the distance from the junction to the closest edge of the circle,
- * colinear with the circle center. The circular segment joining the two paths represents the
+ * Let a circle be tangent to both previous and current path line segments, where the junction
+ * deviation is defined as the distance from the junction to the closest edge of the circle,
+ * colinear with the circle center. The circular segment joining the two paths represents the
* path of centripetal acceleration. Solve for max velocity based on max acceleration about the
- * radius of the circle, defined indirectly by junction deviation. This may be also viewed as
- * path width or max_jerk in the previous Grbl version. This approach does not actually deviate
+ * radius of the circle, defined indirectly by junction deviation. This may be also viewed as
+ * path width or max_jerk in the previous Grbl version. This approach does not actually deviate
* from path, but used as a robust way to compute cornering speeds, as it takes into account the
* nonlinearities of both the junction angle and junction velocity.
*
- * NOTE: If the junction deviation value is finite, Grbl executes the motions in an exact path
+ * NOTE: If the junction deviation value is finite, Grbl executes the motions in an exact path
* mode (G61). If the junction deviation value is zero, Grbl will execute the motion in an exact
* stop mode (G61.1) manner. In the future, if continuous mode (G64) is desired, the math here
* is exactly the same. Instead of motioning all the way to junction point, the machine will
* just follow the arc circle defined here. The Arduino doesn't have the CPU cycles to perform
- * a continuous mode path, but ARM-based microcontrollers most certainly do.
- *
+ * a continuous mode path, but ARM-based microcontrollers most certainly do.
+ *
* NOTE: The max junction speed is a fixed value, since machine acceleration limits cannot be
* changed dynamically during operation nor can the line move geometry. This must be kept in
- * memory in the event of a feedrate override changing the nominal speeds of blocks, which can
+ * memory in the event of a feedrate override changing the nominal speeds of blocks, which can
* change the overall maximum entry speed conditions of all blocks.
- */
+ *
+ * #######
+ * https://github.com/MarlinFirmware/Marlin/issues/10341#issuecomment-388191754
+ *
+ * hoffbaked: on May 10 2018 tuned and improved the GRBL algorithm for Marlin:
+ Okay! It seems to be working good. I somewhat arbitrarily cut it off at 1mm
+ on then on anything with less sides than an octagon. With this, and the
+ reverse pass actually recalculating things, a corner acceleration value
+ of 1000 junction deviation of .05 are pretty reasonable. If the cycles
+ can be spared, a better acos could be used. For all I know, it may be
+ already calculated in a different place. */
// Unit vector of previous path line segment
static float previous_unit_vec[
@@ -1891,7 +2190,7 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
};
// Skip first block or when previous_nominal_speed is used as a flag for homing and offset cycles.
- if (moves_queued && !UNEAR_ZERO(previous_nominal_speed)) {
+ if (moves_queued && !UNEAR_ZERO(previous_nominal_speed_sqr)) {
// Compute cosine of angle between previous and current path. (prev_unit_vec is negative)
// NOTE: Max junction velocity is computed without sin() or acos() by trig half angle identity.
float junction_cos_theta = -previous_unit_vec[X_AXIS] * unit_vec[X_AXIS]
@@ -1905,21 +2204,33 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
// NOTE: Computed without any expensive trig, sin() or acos(), by trig half angle identity of cos(theta).
if (junction_cos_theta > 0.999999) {
// For a 0 degree acute junction, just set minimum junction speed.
- vmax_junction = MINIMUM_PLANNER_SPEED;
+ vmax_junction_sqr = sq(MINIMUM_PLANNER_SPEED);
}
else {
- junction_cos_theta = max(junction_cos_theta, -0.999999); // Check for numerical round-off to avoid divide by zero.
+ NOLESS(junction_cos_theta, -0.999999); // Check for numerical round-off to avoid divide by zero.
const float sin_theta_d2 = SQRT(0.5 * (1.0 - junction_cos_theta)); // Trig half angle identity. Always positive.
// TODO: Technically, the acceleration used in calculation needs to be limited by the minimum of the
// two junctions. However, this shouldn't be a significant problem except in extreme circumstances.
- vmax_junction = SQRT((block->acceleration * JUNCTION_DEVIATION_FACTOR * sin_theta_d2) / (1.0 - sin_theta_d2));
+ vmax_junction_sqr = (JUNCTION_ACCELERATION_FACTOR * JUNCTION_DEVIATION_FACTOR * sin_theta_d2) / (1.0 - sin_theta_d2);
+ if (block->millimeters < 1.0) {
+
+ // Fast acos approximation, minus the error bar to be safe
+ const float junction_theta = (RADIANS(-40) * sq(junction_cos_theta) - RADIANS(50)) * junction_cos_theta + RADIANS(90) - 0.18;
+
+ // If angle is greater than 135 degrees (octagon), find speed for approximate arc
+ if (junction_theta > RADIANS(135)) {
+ const float limit_sqr = block->millimeters / (RADIANS(180) - junction_theta) * JUNCTION_ACCELERATION_FACTOR;
+ NOMORE(vmax_junction_sqr, limit_sqr);
+ }
+ }
}
- vmax_junction = MIN3(vmax_junction, block->nominal_speed, previous_nominal_speed);
+ // Get the lowest speed
+ vmax_junction_sqr = MIN3(vmax_junction_sqr, block->nominal_speed_sqr, previous_nominal_speed_sqr);
}
else // Init entry speed to zero. Assume it starts from rest. Planner will correct this later.
- vmax_junction = 0.0;
+ vmax_junction_sqr = 0.0;
COPY(previous_unit_vec, unit_vec);
@@ -1935,13 +2246,15 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
// Exit speed limited by a jerk to full halt of a previous last segment
static float previous_safe_speed;
- float safe_speed = block->nominal_speed;
+ const float nominal_speed = SQRT(block->nominal_speed_sqr);
+ float safe_speed = nominal_speed;
+
uint8_t limited = 0;
LOOP_XYZE(i) {
- const float jerk = FABS(current_speed[i]), maxj = max_jerk[i];
+ const float jerk = ABS(current_speed[i]), maxj = max_jerk[i];
if (jerk > maxj) {
if (limited) {
- const float mjerk = maxj * block->nominal_speed;
+ const float mjerk = maxj * nominal_speed;
if (jerk * safe_speed > mjerk) safe_speed = mjerk / jerk;
}
else {
@@ -1951,19 +2264,21 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
}
}
- if (moves_queued && !UNEAR_ZERO(previous_nominal_speed)) {
+ float vmax_junction;
+ if (moves_queued && !UNEAR_ZERO(previous_nominal_speed_sqr)) {
// Estimate a maximum velocity allowed at a joint of two successive segments.
// If this maximum velocity allowed is lower than the minimum of the entry / exit safe velocities,
// then the machine is not coasting anymore and the safe entry / exit velocities shall be used.
- // The junction velocity will be shared between successive segments. Limit the junction velocity to their minimum.
- // Pick the smaller of the nominal speeds. Higher speed shall not be achieved at the junction during coasting.
- vmax_junction = min(block->nominal_speed, previous_nominal_speed);
-
// Factor to multiply the previous / current nominal velocities to get componentwise limited velocities.
float v_factor = 1;
limited = 0;
+ // The junction velocity will be shared between successive segments. Limit the junction velocity to their minimum.
+ // Pick the smaller of the nominal speeds. Higher speed shall not be achieved at the junction during coasting.
+ const float previous_nominal_speed = SQRT(previous_nominal_speed_sqr);
+ vmax_junction = MIN(nominal_speed, previous_nominal_speed);
+
// Now limit the jerk in all axes.
const float smaller_speed_factor = vmax_junction / previous_nominal_speed;
LOOP_XYZE(axis) {
@@ -1978,9 +2293,9 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
// Calculate jerk depending on whether the axis is coasting in the same direction or reversing.
const float jerk = (v_exit > v_entry)
? // coasting axis reversal
- ( (v_entry > 0 || v_exit < 0) ? (v_exit - v_entry) : max(v_exit, -v_entry) )
+ ( (v_entry > 0 || v_exit < 0) ? (v_exit - v_entry) : MAX(v_exit, -v_entry) )
: // v_exit <= v_entry coasting axis reversal
- ( (v_entry < 0 || v_exit > 0) ? (v_entry - v_exit) : max(-v_exit, v_entry) );
+ ( (v_entry < 0 || v_exit > 0) ? (v_entry - v_exit) : MAX(-v_exit, v_entry) );
if (jerk > max_jerk[axis]) {
v_factor *= max_jerk[axis] / jerk;
@@ -1996,18 +2311,21 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
}
else
vmax_junction = safe_speed;
-
+
previous_safe_speed = safe_speed;
+ vmax_junction_sqr = sq(vmax_junction);
+
#endif // Classic Jerk Limiting
// Max entry speed of this block equals the max exit speed of the previous block.
- block->max_entry_speed = vmax_junction;
+ block->max_entry_speed_sqr = vmax_junction_sqr;
// Initialize block entry speed. Compute based on deceleration to user-defined MINIMUM_PLANNER_SPEED.
- const float v_allowable = max_allowable_speed(-block->acceleration, MINIMUM_PLANNER_SPEED, block->millimeters);
- // If stepper ISR is disabled, this indicates buffer_segment wants to add a split block.
- // In this case start with the max. allowed speed to avoid an interrupted first move.
- block->entry_speed = STEPPER_ISR_ENABLED() ? MINIMUM_PLANNER_SPEED : min(vmax_junction, v_allowable);
+ const float v_allowable_sqr = max_allowable_speed_sqr(-block->acceleration, sq(MINIMUM_PLANNER_SPEED), block->millimeters);
+
+ // If we are trying to add a split block, start with the
+ // max. allowed speed to avoid an interrupted first move.
+ block->entry_speed_sqr = !split_move ? sq(MINIMUM_PLANNER_SPEED) : MIN(vmax_junction_sqr, v_allowable_sqr);
// Initialize planner efficiency flags
// Set flag if block will always reach maximum junction speed regardless of entry/exit speeds.
@@ -2017,25 +2335,22 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
// block nominal speed limits both the current and next maximum junction speeds. Hence, in both
// the reverse and forward planners, the corresponding block junction speed will always be at the
// the maximum junction speed and may always be ignored for any speed reduction checks.
- block->flag |= block->nominal_speed <= v_allowable ? BLOCK_FLAG_RECALCULATE | BLOCK_FLAG_NOMINAL_LENGTH : BLOCK_FLAG_RECALCULATE;
+ block->flag |= block->nominal_speed_sqr <= v_allowable_sqr ? BLOCK_FLAG_RECALCULATE | BLOCK_FLAG_NOMINAL_LENGTH : BLOCK_FLAG_RECALCULATE;
// Update previous path unit_vector and nominal speed
COPY(previous_speed, current_speed);
- previous_nominal_speed = block->nominal_speed;
+ previous_nominal_speed_sqr = block->nominal_speed_sqr;
- // Move buffer head
- block_buffer_head = next_buffer_head;
-
- // Update the position (only when a move was queued)
+ // Update the position
static_assert(COUNT(target) > 1, "Parameter to _buffer_steps must be (&target)[XYZE]!");
COPY(position, target);
#if HAS_POSITION_FLOAT
COPY(position_float, target_float);
#endif
- recalculate();
-
-} // _buffer_steps()
+ // Movement was accepted
+ return true;
+} // _populate_block()
/**
* Planner::buffer_sync_block
@@ -2046,31 +2361,28 @@ void Planner::buffer_sync_block() {
uint8_t next_buffer_head;
block_t * const block = get_next_free_block(next_buffer_head);
+ // Clear block
+ memset(block, 0, sizeof(block_t));
+
block->flag = BLOCK_FLAG_SYNC_POSITION;
- block->steps[A_AXIS] = position[A_AXIS];
- block->steps[B_AXIS] = position[B_AXIS];
- block->steps[C_AXIS] = position[C_AXIS];
- block->steps[E_AXIS] = position[E_AXIS];
+ block->position[A_AXIS] = position[A_AXIS];
+ block->position[B_AXIS] = position[B_AXIS];
+ block->position[C_AXIS] = position[C_AXIS];
+ block->position[E_AXIS] = position[E_AXIS];
- #if ENABLED(LIN_ADVANCE)
- block->use_advance_lead = false;
- #endif
-
- block->nominal_speed =
- block->entry_speed =
- block->max_entry_speed =
- block->millimeters =
- block->acceleration = 0;
-
- block->step_event_count =
- block->nominal_rate =
- block->initial_rate =
- block->final_rate =
- block->acceleration_steps_per_s2 =
- block->segment_time_us = 0;
+ // If this is the first added movement, reload the delay, otherwise, cancel it.
+ if (block_buffer_head == block_buffer_tail) {
+ // If it was the first queued block, restart the 1st block delivery delay, to
+ // give the planner an opportunity to queue more movements and plan them
+ // As there are no queued movements, the Stepper ISR will not touch this
+ // variable, so there is no risk setting this here (but it MUST be done
+ // before the following line!!)
+ delay_before_delivering = BLOCK_DELAY_FOR_1ST_MOVE;
+ }
block_buffer_head = next_buffer_head;
+
stepper.wake_up();
} // buffer_sync_block()
@@ -2086,7 +2398,11 @@ void Planner::buffer_sync_block() {
* extruder - target extruder
* millimeters - the length of the movement, if known
*/
-void Planner::buffer_segment(const float &a, const float &b, const float &c, const float &e, const float &fr_mm_s, const uint8_t extruder, const float &millimeters/*=0.0*/) {
+bool Planner::buffer_segment(const float &a, const float &b, const float &c, const float &e, const float &fr_mm_s, const uint8_t extruder, const float &millimeters/*=0.0*/) {
+
+ // If we are cleaning, do not accept queuing of movements
+ if (cleaning_buffer_counter) return false;
+
// When changing extruders recalculate steps corresponding to the E position
#if ENABLED(DISTINCT_E_FACTORS)
if (last_extruder != extruder && axis_steps_per_mm[E_AXIS_N] != axis_steps_per_mm[E_AXIS + last_extruder]) {
@@ -2144,48 +2460,18 @@ void Planner::buffer_segment(const float &a, const float &b, const float &c, con
SERIAL_ECHOLNPGM(")");
//*/
- // Always split the first move into two (if not homing or probing)
- if (!has_blocks_queued()) {
-
- #define _BETWEEN(A) (position[A##_AXIS] + target[A##_AXIS]) >> 1
- const int32_t between[ABCE] = { _BETWEEN(A), _BETWEEN(B), _BETWEEN(C), _BETWEEN(E) };
-
- #if HAS_POSITION_FLOAT
- #define _BETWEEN_F(A) (position_float[A##_AXIS] + target_float[A##_AXIS]) * 0.5
- const float between_float[ABCE] = { _BETWEEN_F(A), _BETWEEN_F(B), _BETWEEN_F(C), _BETWEEN_F(E) };
- #endif
-
- DISABLE_STEPPER_DRIVER_INTERRUPT();
-
- _buffer_steps(between
- #if HAS_POSITION_FLOAT
- , between_float
- #endif
- , fr_mm_s, extruder, millimeters * 0.5
- );
-
- const uint8_t next = block_buffer_head;
-
- _buffer_steps(target
- #if HAS_POSITION_FLOAT
- , target_float
- #endif
- , fr_mm_s, extruder, millimeters * 0.5
- );
-
- SBI(block_buffer[next].flag, BLOCK_BIT_CONTINUED);
- ENABLE_STEPPER_DRIVER_INTERRUPT();
- }
- else
- _buffer_steps(target
+ // Queue the movement
+ if (
+ !_buffer_steps(target
#if HAS_POSITION_FLOAT
, target_float
#endif
, fr_mm_s, extruder, millimeters
- );
+ )
+ ) return false;
stepper.wake_up();
-
+ return true;
} // buffer_segment()
/**
@@ -2212,7 +2498,7 @@ void Planner::_set_position_mm(const float &a, const float &b, const float &c, c
position_float[C_AXIS] = c;
position_float[E_AXIS] = e;
#endif
- previous_nominal_speed = 0.0; // Resets planner junction speeds. Assumes start from rest.
+ previous_nominal_speed_sqr = 0.0; // Resets planner junction speeds. Assumes start from rest.
ZERO(previous_speed);
buffer_sync_block();
}
@@ -2232,22 +2518,6 @@ void Planner::set_position_mm_kinematic(const float (&cart)[XYZE]) {
#endif
}
-/**
- * Sync from the stepper positions. (e.g., after an interrupted move)
- */
-void Planner::sync_from_steppers() {
- LOOP_XYZE(i) {
- position[i] = stepper.position((AxisEnum)i);
- #if HAS_POSITION_FLOAT
- position_float[i] = position[i] * steps_to_mm[i
- #if ENABLED(DISTINCT_E_FACTORS)
- + (i == E_AXIS ? active_extruder : 0)
- #endif
- ];
- #endif
- }
-}
-
/**
* Setters for planner position (also setting stepper position).
*/
diff --git a/Marlin/planner.h b/Marlin/planner.h
index edc61ea6ee..1c4f2a1e8e 100644
--- a/Marlin/planner.h
+++ b/Marlin/planner.h
@@ -49,7 +49,7 @@ enum BlockFlagBit : char {
// from a safe speed (in consideration of jerking from zero speed).
BLOCK_BIT_NOMINAL_LENGTH,
- // The block is busy
+ // The block is busy, being interpreted by the stepper ISR
BLOCK_BIT_BUSY,
// The block is segment 2+ of a longer move
@@ -80,24 +80,35 @@ typedef struct {
uint8_t flag; // Block flags (See BlockFlag enum above)
- unsigned char active_extruder; // The extruder to move (if E move)
+ // Fields used by the motion planner to manage acceleration
+ float nominal_speed_sqr, // The nominal speed for this block in (mm/sec)^2
+ entry_speed_sqr, // Entry speed at previous-current junction in (mm/sec)^2
+ max_entry_speed_sqr, // Maximum allowable junction entry speed in (mm/sec)^2
+ millimeters, // The total travel of this block in mm
+ acceleration; // acceleration mm/sec^2
- // Fields used by the Bresenham algorithm for tracing the line
- int32_t steps[NUM_AXIS]; // Step count along each axis
+ union {
+ // Data used by all move blocks
+ struct {
+ // Fields used by the Bresenham algorithm for tracing the line
+ uint32_t steps[NUM_AXIS]; // Step count along each axis
+ };
+ // Data used by all sync blocks
+ struct {
+ int32_t position[NUM_AXIS]; // New position to force when this sync block is executed
+ };
+ };
uint32_t step_event_count; // The number of step events required to complete this block
+ uint8_t active_extruder; // The extruder to move (if E move)
+
#if ENABLED(MIXING_EXTRUDER)
uint32_t mix_event_count[MIXING_STEPPERS]; // Scaled step_event_count for the mixing steppers
#endif
// Settings for the trapezoid generator
- int32_t accelerate_until, // The index of the step event on which to stop acceleration
- decelerate_after; // The index of the step event on which to start decelerating
-
- uint32_t nominal_rate, // The nominal step rate for this block in step_events/sec
- initial_rate, // The jerk-adjusted step rate at start of block
- final_rate, // The minimal rate at exit
- acceleration_steps_per_s2; // acceleration steps/sec^2
+ uint32_t accelerate_until, // The index of the step event on which to stop acceleration
+ decelerate_after; // The index of the step event on which to start decelerating
#if ENABLED(BEZIER_JERK_CONTROL)
uint32_t cruise_rate; // The actual cruise rate to use, between end of the acceleration phase and start of deceleration phase
@@ -106,7 +117,7 @@ typedef struct {
uint32_t acceleration_time_inverse, // Inverse of acceleration and deceleration periods, expressed as integer. Scale depends on CPU being used
deceleration_time_inverse;
#else
- int32_t acceleration_rate; // The acceleration rate used for acceleration calculation
+ uint32_t acceleration_rate; // The acceleration rate used for acceleration calculation
#endif
uint8_t direction_bits; // The direction bit set for this block (refers to *_DIRECTION_BIT in config.h)
@@ -120,12 +131,10 @@ typedef struct {
float e_D_ratio;
#endif
- // Fields used by the motion planner to manage acceleration
- float nominal_speed, // The nominal speed for this block in mm/sec
- entry_speed, // Entry speed at previous-current junction in mm/sec
- max_entry_speed, // Maximum allowable junction entry speed in mm/sec
- millimeters, // The total travel of this block in mm
- acceleration; // acceleration mm/sec^2
+ uint32_t nominal_rate, // The nominal step rate for this block in step_events/sec
+ initial_rate, // The jerk-adjusted step rate at start of block
+ final_rate, // The minimal rate at exit
+ acceleration_steps_per_s2; // acceleration steps/sec^2
#if FAN_COUNT > 0
uint16_t fan_speed[FAN_COUNT];
@@ -162,6 +171,10 @@ class Planner {
static block_t block_buffer[BLOCK_BUFFER_SIZE];
static volatile uint8_t block_buffer_head, // Index of the next block to be pushed
block_buffer_tail; // Index of the busy block, if any
+ static uint16_t cleaning_buffer_counter; // A counter to disable queuing of blocks
+ static uint8_t delay_before_delivering, // This counter delays delivery of blocks when queue becomes empty to allow the opportunity of merging blocks
+ block_buffer_planned; // Index of the optimally planned block
+
#if ENABLED(DISTINCT_E_FACTORS)
static uint8_t last_extruder; // Respond to extruder change
@@ -229,6 +242,10 @@ class Planner {
#endif
#endif
+ #if ENABLED(ABORT_ON_ENDSTOP_HIT_FEATURE_ENABLED)
+ static bool abort_on_endstop_hit;
+ #endif
+
private:
/**
@@ -243,9 +260,9 @@ class Planner {
static float previous_speed[NUM_AXIS];
/**
- * Nominal speed of previous path line segment
+ * Nominal speed of previous path line segment (mm/s)^2
*/
- static float previous_nominal_speed;
+ static float previous_nominal_speed_sqr;
/**
* Limit where 64bit math is necessary for acceleration calculation
@@ -304,15 +321,6 @@ class Planner {
// Manage fans, paste pressure, etc.
static void check_axes_activity();
- /**
- * Number of moves currently in the planner
- */
- FORCE_INLINE static uint8_t movesplanned() { return BLOCK_MOD(block_buffer_head - block_buffer_tail + BLOCK_BUFFER_SIZE); }
-
- FORCE_INLINE static void clear_block_buffer() { block_buffer_head = block_buffer_tail = 0; }
-
- FORCE_INLINE static bool is_full() { return block_buffer_tail == next_block_index(block_buffer_head); }
-
// Update multipliers based on new diameter measurements
static void calculate_volumetric_multipliers();
@@ -420,16 +428,32 @@ class Planner {
#define ARG_Z const float &rz
#endif
+ // Number of moves currently in the planner
+ FORCE_INLINE static uint8_t movesplanned() { return BLOCK_MOD(block_buffer_head - block_buffer_tail); }
+
+ // Remove all blocks from the buffer
+ FORCE_INLINE static void clear_block_buffer() { block_buffer_head = block_buffer_tail = 0; }
+
+ // Check if movement queue is full
+ FORCE_INLINE static bool is_full() { return block_buffer_tail == next_block_index(block_buffer_head); }
+
+ // Get count of movement slots free
+ FORCE_INLINE static uint8_t moves_free() { return BLOCK_BUFFER_SIZE - 1 - movesplanned(); }
+
/**
* Planner::get_next_free_block
*
- * - Get the next head index (passed by reference)
- * - Wait for a space to open up in the planner
- * - Return the head block
+ * - Get the next head indices (passed by reference)
+ * - Wait for the number of spaces to open up in the planner
+ * - Return the first head block
*/
- FORCE_INLINE static block_t* get_next_free_block(uint8_t &next_buffer_head) {
+ FORCE_INLINE static block_t* get_next_free_block(uint8_t &next_buffer_head, const uint8_t count=1) {
+
+ // Wait until there are enough slots free
+ while (moves_free() < count) { idle(); }
+
+ // Return the first available block
next_buffer_head = next_block_index(block_buffer_head);
- while (block_buffer_tail == next_buffer_head) idle(); // while (is_full)
return &block_buffer[block_buffer_head];
}
@@ -442,8 +466,30 @@ class Planner {
* fr_mm_s - (target) speed of the move
* extruder - target extruder
* millimeters - the length of the movement, if known
+ *
+ * Returns true if movement was buffered, false otherwise
*/
- static void _buffer_steps(const int32_t (&target)[XYZE]
+ static bool _buffer_steps(const int32_t (&target)[XYZE]
+ #if HAS_POSITION_FLOAT
+ , const float (&target_float)[XYZE]
+ #endif
+ , float fr_mm_s, const uint8_t extruder, const float &millimeters=0.0
+ );
+
+ /**
+ * Planner::_populate_block
+ *
+ * Fills a new linear movement in the block (in terms of steps).
+ *
+ * target - target position in steps units
+ * fr_mm_s - (target) speed of the move
+ * extruder - target extruder
+ * millimeters - the length of the movement, if known
+ *
+ * Returns true is movement is acceptable, false otherwise
+ */
+ static bool _populate_block(block_t * const block, bool split_move,
+ const int32_t (&target)[XYZE]
#if HAS_POSITION_FLOAT
, const float (&target_float)[XYZE]
#endif
@@ -468,7 +514,7 @@ class Planner {
* extruder - target extruder
* millimeters - the length of the movement, if known
*/
- static void buffer_segment(const float &a, const float &b, const float &c, const float &e, const float &fr_mm_s, const uint8_t extruder, const float &millimeters=0.0);
+ static bool buffer_segment(const float &a, const float &b, const float &c, const float &e, const float &fr_mm_s, const uint8_t extruder, const float &millimeters=0.0);
static void _set_position_mm(const float &a, const float &b, const float &c, const float &e);
@@ -485,11 +531,11 @@ class Planner {
* extruder - target extruder
* millimeters - the length of the movement, if known
*/
- FORCE_INLINE static void buffer_line(ARG_X, ARG_Y, ARG_Z, const float &e, const float &fr_mm_s, const uint8_t extruder, const float millimeters = 0.0) {
+ FORCE_INLINE static bool buffer_line(ARG_X, ARG_Y, ARG_Z, const float &e, const float &fr_mm_s, const uint8_t extruder, const float millimeters = 0.0) {
#if PLANNER_LEVELING && IS_CARTESIAN
apply_leveling(rx, ry, rz);
#endif
- buffer_segment(rx, ry, rz, e, fr_mm_s, extruder, millimeters);
+ return buffer_segment(rx, ry, rz, e, fr_mm_s, extruder, millimeters);
}
/**
@@ -502,7 +548,7 @@ class Planner {
* extruder - target extruder
* millimeters - the length of the movement, if known
*/
- FORCE_INLINE static void buffer_line_kinematic(const float (&cart)[XYZE], const float &fr_mm_s, const uint8_t extruder, const float millimeters = 0.0) {
+ FORCE_INLINE static bool buffer_line_kinematic(const float (&cart)[XYZE], const float &fr_mm_s, const uint8_t extruder, const float millimeters = 0.0) {
#if PLANNER_LEVELING
float raw[XYZ] = { cart[X_AXIS], cart[Y_AXIS], cart[Z_AXIS] };
apply_leveling(raw);
@@ -511,9 +557,9 @@ class Planner {
#endif
#if IS_KINEMATIC
inverse_kinematics(raw);
- buffer_segment(delta[A_AXIS], delta[B_AXIS], delta[C_AXIS], cart[E_AXIS], fr_mm_s, extruder, millimeters);
+ return buffer_segment(delta[A_AXIS], delta[B_AXIS], delta[C_AXIS], cart[E_AXIS], fr_mm_s, extruder, millimeters);
#else
- buffer_segment(raw[X_AXIS], raw[Y_AXIS], raw[Z_AXIS], cart[E_AXIS], fr_mm_s, extruder, millimeters);
+ return buffer_segment(raw[X_AXIS], raw[Y_AXIS], raw[Z_AXIS], cart[E_AXIS], fr_mm_s, extruder, millimeters);
#endif
}
@@ -538,72 +584,122 @@ class Planner {
FORCE_INLINE static void set_e_position_mm(const float &e) { set_position_mm(E_AXIS, e); }
/**
- * Sync from the stepper positions. (e.g., after an interrupted move)
+ * Get an axis position according to stepper position(s)
+ * For CORE machines apply translation from ABC to XYZ.
*/
- static void sync_from_steppers();
+ static float get_axis_position_mm(const AxisEnum axis);
+
+ // SCARA AB axes are in degrees, not mm
+ #if IS_SCARA
+ FORCE_INLINE static float get_axis_position_degrees(const AxisEnum axis) { return get_axis_position_mm(axis); }
+ #endif
+
+ // Called to force a quick stop of the machine (for example, when an emergency
+ // stop is required, or when endstops are hit)
+ static void quick_stop();
+
+ // Called when an endstop is triggered. Causes the machine to stop inmediately
+ static void endstop_triggered(const AxisEnum axis);
+
+ // Triggered position of an axis in mm (not core-savvy)
+ static float triggered_position_mm(const AxisEnum axis);
+
+ // Block until all buffered steps are executed / cleaned
+ static void synchronize();
+
+ // Wait for moves to finish and disable all steppers
+ static void finish_and_disable();
+
+ // Periodic tick to handle cleaning timeouts
+ // Called from the Temperature ISR at ~1kHz
+ static void tick() {
+ if (cleaning_buffer_counter) {
+ --cleaning_buffer_counter;
+ #if ENABLED(SD_FINISHED_STEPPERRELEASE) && defined(SD_FINISHED_RELEASECOMMAND)
+ if (!cleaning_buffer_counter) enqueue_and_echo_commands_P(PSTR(SD_FINISHED_RELEASECOMMAND));
+ #endif
+ }
+ }
/**
* Does the buffer have any blocks queued?
*/
FORCE_INLINE static bool has_blocks_queued() { return (block_buffer_head != block_buffer_tail); }
- /**
- * "Discard" the block and "release" the memory.
- * Called when the current block is no longer needed.
- */
- FORCE_INLINE static void discard_current_block() {
- if (has_blocks_queued())
- block_buffer_tail = BLOCK_MOD(block_buffer_tail + 1);
- }
-
- /**
- * "Discard" the next block if it's continued.
- * Called after an interrupted move to throw away the rest of the move.
- */
- FORCE_INLINE static bool discard_continued_block() {
- const bool discard = has_blocks_queued() && TEST(block_buffer[block_buffer_tail].flag, BLOCK_BIT_CONTINUED);
- if (discard) discard_current_block();
- return discard;
- }
-
/**
* The current block. NULL if the buffer is empty.
* This also marks the block as busy.
* WARNING: Called from Stepper ISR context!
*/
static block_t* get_current_block() {
- if (has_blocks_queued()) {
+
+ // Get the number of moves in the planner queue so far
+ uint8_t nr_moves = movesplanned();
+
+ // If there are any moves queued ...
+ if (nr_moves) {
+
+ // If there is still delay of delivery of blocks running, decrement it
+ if (delay_before_delivering) {
+ --delay_before_delivering;
+ // If the number of movements queued is less than 3, and there is still time
+ // to wait, do not deliver anything
+ if (nr_moves < 3 && delay_before_delivering) return NULL;
+ delay_before_delivering = 0;
+ }
+
+ // If we are here, there is no excuse to deliver the block
block_t * const block = &block_buffer[block_buffer_tail];
- // If the block has no trapezoid calculated, it's unsafe to execute.
- if (movesplanned() > 1) {
- const block_t * const next = &block_buffer[next_block_index(block_buffer_tail)];
- if (TEST(block->flag, BLOCK_BIT_RECALCULATE) || TEST(next->flag, BLOCK_BIT_RECALCULATE))
- return NULL;
- }
- else if (TEST(block->flag, BLOCK_BIT_RECALCULATE))
- return NULL;
+ // No trapezoid calculated? Don't execute yet.
+ if (TEST(block->flag, BLOCK_BIT_RECALCULATE)) return NULL;
#if ENABLED(ULTRA_LCD)
block_buffer_runtime_us -= block->segment_time_us; // We can't be sure how long an active block will take, so don't count it.
#endif
+
+ // Mark the block as busy, so the planner does not attempt to replan it
SBI(block->flag, BLOCK_BIT_BUSY);
return block;
}
- else {
- #if ENABLED(ULTRA_LCD)
- clear_block_buffer_runtime(); // paranoia. Buffer is empty now - so reset accumulated time to zero.
- #endif
- return NULL;
+
+ // The queue became empty
+ #if ENABLED(ULTRA_LCD)
+ clear_block_buffer_runtime(); // paranoia. Buffer is empty now - so reset accumulated time to zero.
+ #endif
+
+ return NULL;
+ }
+
+ /**
+ * "Discard" the block and "release" the memory.
+ * Called when the current block is no longer needed.
+ * NB: There MUST be a current block to call this function!!
+ */
+ FORCE_INLINE static void discard_current_block() {
+ if (has_blocks_queued()) { // Discard non-empty buffer.
+ uint8_t block_index = next_block_index( block_buffer_tail );
+
+ // Push block_buffer_planned pointer, if encountered.
+ if (!has_blocks_queued()) block_buffer_planned = block_index;
+
+ block_buffer_tail = block_index;
}
}
#if ENABLED(ULTRA_LCD)
static uint16_t block_buffer_runtime() {
- CRITICAL_SECTION_START
- millis_t bbru = block_buffer_runtime_us;
- CRITICAL_SECTION_END
+ // Protect the access to the variable. Only required for AVR, as
+ // any 32bit CPU offers atomic access to 32bit variables
+ bool was_enabled = STEPPER_ISR_ENABLED();
+ if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT();
+
+ millis_t bbru = block_buffer_runtime_us;
+
+ // Reenable Stepper ISR
+ if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT();
+
// To translate µs to ms a division by 1000 would be required.
// We introduce 2.4% error here by dividing by 1024.
// Doesn't matter because block_buffer_runtime_us is already too small an estimation.
@@ -614,9 +710,15 @@ class Planner {
}
static void clear_block_buffer_runtime() {
- CRITICAL_SECTION_START
- block_buffer_runtime_us = 0;
- CRITICAL_SECTION_END
+ // Protect the access to the variable. Only required for AVR, as
+ // any 32bit CPU offers atomic access to 32bit variables
+ bool was_enabled = STEPPER_ISR_ENABLED();
+ if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT();
+
+ block_buffer_runtime_us = 0;
+
+ // Reenable Stepper ISR
+ if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT();
}
#endif
@@ -633,8 +735,8 @@ class Planner {
/**
* Get the index of the next / previous block in the ring buffer
*/
- static constexpr int8_t next_block_index(const int8_t block_index) { return BLOCK_MOD(block_index + 1); }
- static constexpr int8_t prev_block_index(const int8_t block_index) { return BLOCK_MOD(block_index - 1); }
+ static constexpr uint8_t next_block_index(const uint8_t block_index) { return BLOCK_MOD(block_index + 1); }
+ static constexpr uint8_t prev_block_index(const uint8_t block_index) { return BLOCK_MOD(block_index - 1); }
/**
* Calculate the distance (not time) it takes to accelerate
@@ -659,12 +761,12 @@ class Planner {
}
/**
- * Calculate the maximum allowable speed at this point, in order
- * to reach 'target_velocity' using 'acceleration' within a given
+ * Calculate the maximum allowable speed squared at this point, in order
+ * to reach 'target_velocity_sqr' using 'acceleration' within a given
* 'distance'.
*/
- static float max_allowable_speed(const float &accel, const float &target_velocity, const float &distance) {
- return SQRT(sq(target_velocity) - 2 * accel * distance);
+ static float max_allowable_speed_sqr(const float &accel, const float &target_velocity_sqr, const float &distance) {
+ return target_velocity_sqr - 2 * accel * distance;
}
#if ENABLED(BEZIER_JERK_CONTROL)
@@ -679,7 +781,7 @@ class Planner {
static void calculate_trapezoid_for_block(block_t* const block, const float &entry_factor, const float &exit_factor);
static void reverse_pass_kernel(block_t* const current, const block_t * const next);
- static void forward_pass_kernel(const block_t * const previous, block_t* const current);
+ static void forward_pass_kernel(const block_t * const previous, block_t* const current, uint8_t block_index);
static void reverse_pass();
static void forward_pass();
@@ -690,7 +792,7 @@ class Planner {
};
-#define PLANNER_XY_FEEDRATE() (min(planner.max_feedrate_mm_s[X_AXIS], planner.max_feedrate_mm_s[Y_AXIS]))
+#define PLANNER_XY_FEEDRATE() (MIN(planner.max_feedrate_mm_s[X_AXIS], planner.max_feedrate_mm_s[Y_AXIS]))
extern Planner planner;
diff --git a/Marlin/planner_bezier.cpp b/Marlin/planner_bezier.cpp
index d6dd35f1c9..5ed7c043ce 100644
--- a/Marlin/planner_bezier.cpp
+++ b/Marlin/planner_bezier.cpp
@@ -41,8 +41,7 @@
#define MAX_STEP 0.1
#define SIGMA 0.1
-/* Compute the linear interpolation between to real numbers.
-*/
+// Compute the linear interpolation between two real numbers.
inline static float interp(float a, float b, float t) { return (1.0 - t) * a + t * b; }
/**
@@ -65,7 +64,7 @@ inline static float eval_bezier(float a, float b, float c, float d, float t) {
* We approximate Euclidean distance with the sum of the coordinates
* offset (so-called "norm 1"), which is quicker to compute.
*/
-inline static float dist1(float x1, float y1, float x2, float y2) { return FABS(x1 - x2) + FABS(y1 - y2); }
+inline static float dist1(float x1, float y1, float x2, float y2) { return ABS(x1 - x2) + ABS(y1 - y2); }
/**
* The algorithm for computing the step is loosely based on the one in Kig
@@ -188,12 +187,15 @@ void cubic_b_spline(const float position[NUM_AXIS], const float target[NUM_AXIS]
bez_target[Z_AXIS] = interp(position[Z_AXIS], target[Z_AXIS], t);
bez_target[E_AXIS] = interp(position[E_AXIS], target[E_AXIS], t);
clamp_to_software_endstops(bez_target);
+
#if HAS_UBL_AND_CURVES
float pos[XYZ] = { bez_target[X_AXIS], bez_target[Y_AXIS], bez_target[Z_AXIS] };
planner.apply_leveling(pos);
- planner.buffer_segment(pos[X_AXIS], pos[Y_AXIS], pos[Z_AXIS], bez_target[E_AXIS], fr_mm_s, active_extruder);
+ if (!planner.buffer_segment(pos[X_AXIS], pos[Y_AXIS], pos[Z_AXIS], bez_target[E_AXIS], fr_mm_s, active_extruder))
+ break;
#else
- planner.buffer_line_kinematic(bez_target, fr_mm_s, extruder);
+ if (!planner.buffer_line_kinematic(bez_target, fr_mm_s, extruder))
+ break;
#endif
}
}
diff --git a/Marlin/power_loss_recovery.cpp b/Marlin/power_loss_recovery.cpp
index 5b5948b924..023c1c25cb 100644
--- a/Marlin/power_loss_recovery.cpp
+++ b/Marlin/power_loss_recovery.cpp
@@ -127,13 +127,15 @@ void do_print_job_recovery() {
#endif
));
+ char str_1[16], str_2[16];
+
#if HAS_LEVELING
// Restore leveling state before G92 sets Z
// This ensures the steppers correspond to the native Z
- sprintf_P(job_recovery_commands[ind++], PSTR("M420 S%i Z%s"), int(job_recovery_info.leveling), job_recovery_info.fade);
+ dtostrf(job_recovery_info.fade, 1, 1, str_1);
+ sprintf_P(job_recovery_commands[ind++], PSTR("M420 S%i Z%s"), int(job_recovery_info.leveling), str_1);
#endif
- char str_1[16], str_2[16];
dtostrf(job_recovery_info.current_position[Z_AXIS] + 2, 1, 3, str_1);
dtostrf(job_recovery_info.current_position[E_AXIS]
#if ENABLED(SAVE_EACH_CMD_MODE)
diff --git a/Marlin/runout.h b/Marlin/runout.h
index d94c21ef64..c4c88b6c15 100644
--- a/Marlin/runout.h
+++ b/Marlin/runout.h
@@ -48,7 +48,7 @@ class FilamentRunoutSensor {
if ((IS_SD_PRINTING || print_job_timer.isRunning()) && check() && !filament_ran_out) {
filament_ran_out = true;
enqueue_and_echo_commands_P(PSTR(FILAMENT_RUNOUT_SCRIPT));
- stepper.synchronize();
+ planner.synchronize();
}
}
private:
diff --git a/Marlin/serial.h b/Marlin/serial.h
index dc1da87353..139f99c110 100644
--- a/Marlin/serial.h
+++ b/Marlin/serial.h
@@ -25,7 +25,10 @@
#include "MarlinConfig.h"
-#if defined(__AVR__) && defined(USBCON)
+#if USE_MARLINSERIAL
+ #include "MarlinSerial.h"
+ #define MYSERIAL0 customizedSerial
+#else
#include
#if ENABLED(BLUETOOTH)
extern HardwareSerial bluetoothSerial;
@@ -33,9 +36,6 @@
#else
#define MYSERIAL0 Serial
#endif // BLUETOOTH
-#else
- #include "MarlinSerial.h"
- #define MYSERIAL0 customizedSerial
#endif
extern const char echomagic[] PROGMEM;
diff --git a/Marlin/servo.cpp b/Marlin/servo.cpp
index 7a1c2b8c60..e1d11573ab 100644
--- a/Marlin/servo.cpp
+++ b/Marlin/servo.cpp
@@ -259,7 +259,7 @@ int8_t Servo::attach(const int pin, const int min, const int max) {
if (pin > 0) servo_info[this->servoIndex].Pin.nbr = pin;
pinMode(servo_info[this->servoIndex].Pin.nbr, OUTPUT); // set servo pin to output
- // todo min/max check: abs(min - MIN_PULSE_WIDTH) /4 < 128
+ // todo min/max check: ABS(min - MIN_PULSE_WIDTH) /4 < 128
this->min = (MIN_PULSE_WIDTH - min) / 4; //resolution of min/max is 4 uS
this->max = (MAX_PULSE_WIDTH - max) / 4;
diff --git a/Marlin/status_screen_DOGM.h b/Marlin/status_screen_DOGM.h
index cee8856f95..5faeb6e406 100644
--- a/Marlin/status_screen_DOGM.h
+++ b/Marlin/status_screen_DOGM.h
@@ -71,7 +71,7 @@ FORCE_INLINE void _draw_heater_status(const uint8_t x, const int8_t heater, cons
);
}
- if (PAGE_CONTAINS(21, 28))
+ if (PAGE_CONTAINS(21, 28)) {
_draw_centered_temp(0.5 + (
#if HAS_HEATED_BED
isBed ? thermalManager.degBed() :
@@ -80,37 +80,43 @@ FORCE_INLINE void _draw_heater_status(const uint8_t x, const int8_t heater, cons
), x, 28
);
- if (PAGE_CONTAINS(17, 20)) {
- const uint8_t h = isBed ? 7 : HEAT_INDICATOR_X,
- y = isBed ? 18 : 17;
- if (
- #if HAS_HEATED_BED
- isBed ? thermalManager.isHeatingBed() :
- #endif
- thermalManager.isHeatingHotend(heater)
- ) {
- u8g.setColorIndex(0); // white on black
- u8g.drawBox(x + h, y, 2, 2);
- u8g.setColorIndex(1); // black on white
+ if (PAGE_CONTAINS(17, 20)) {
+ const uint8_t h = isBed ? 7 : HEAT_INDICATOR_X,
+ y = isBed ? 18 : 17;
+ if (
+ #if HAS_HEATED_BED
+ isBed ? thermalManager.isHeatingBed() :
+ #endif
+ thermalManager.isHeatingHotend(heater)
+ ) {
+ u8g.setColorIndex(0); // white on black
+ u8g.drawBox(x + h, y, 2, 2);
+ u8g.setColorIndex(1); // black on white
+ }
+ else
+ u8g.drawBox(x + h, y, 2, 2);
}
- else
- u8g.drawBox(x + h, y, 2, 2);
}
}
-FORCE_INLINE void _draw_axis_label(const AxisEnum axis, const char* const pstr, const bool blink) {
+//
+// Before homing, blink '123' <-> '???'.
+// Homed but unknown... '123' <-> ' '.
+// Homed and known, display constantly.
+//
+FORCE_INLINE void _draw_axis_value(const AxisEnum axis, const char *value, const bool blink) {
if (blink)
- lcd_printPGM(pstr);
+ lcd_print(value);
else {
if (!axis_homed[axis])
- u8g.print('?');
+ while (const char c = *value++) lcd_print(c <= '.' ? c : '?');
else {
#if DISABLED(HOME_AFTER_DEACTIVATE) && DISABLED(DISABLE_REDUCED_ACCURACY_WARNING)
if (!axis_known_position[axis])
- u8g.print(' ');
+ lcd_printPGM(axis == Z_AXIS ? PSTR(" ") : PSTR(" "));
else
#endif
- lcd_printPGM(pstr);
+ lcd_print(value);
}
}
}
@@ -118,36 +124,77 @@ FORCE_INLINE void _draw_axis_label(const AxisEnum axis, const char* const pstr,
inline void lcd_implementation_status_message(const bool blink) {
#if ENABLED(STATUS_MESSAGE_SCROLLING)
static bool last_blink = false;
- const uint8_t slen = lcd_strlen(lcd_status_message);
- const char *stat = lcd_status_message + status_scroll_pos;
- if (slen <= LCD_WIDTH)
- lcd_print_utf(stat); // The string isn't scrolling
+
+ // Get the UTF8 character count of the string
+ uint8_t slen = lcd_strlen(lcd_status_message);
+
+ // If the string fits into the LCD, just print it and do not scroll it
+ if (slen <= LCD_WIDTH) {
+
+ // The string isn't scrolling and may not fill the screen
+ lcd_print_utf(lcd_status_message);
+
+ // Fill the rest with spaces
+ while (slen < LCD_WIDTH) {
+ u8g.print(' ');
+ ++slen;
+ }
+ }
else {
- if (status_scroll_pos <= slen - LCD_WIDTH)
- lcd_print_utf(stat); // The string fills the screen
+ // String is larger than the available space in screen.
+
+ // Get a pointer to the next valid UTF8 character
+ const char *stat = lcd_status_message + status_scroll_offset;
+
+ // Get the string remaining length
+ const uint8_t rlen = lcd_strlen(stat);
+
+ // If we have enough characters to display
+ if (rlen >= LCD_WIDTH) {
+ // The remaining string fills the screen - Print it
+ lcd_print_utf(stat, LCD_WIDTH);
+ }
else {
- uint8_t chars = LCD_WIDTH;
- if (status_scroll_pos < slen) { // First string still visible
- lcd_print_utf(stat); // The string leaves space
- chars -= slen - status_scroll_pos; // Amount of space left
- }
- u8g.print('.'); // Always at 1+ spaces left, draw a dot
- if (--chars) {
- if (status_scroll_pos < slen + 1) // Draw a second dot if there's space
- --chars, u8g.print('.');
- if (chars) lcd_print_utf(lcd_status_message, chars); // Print a second copy of the message
+ // The remaining string does not completely fill the screen
+ lcd_print_utf(stat, LCD_WIDTH); // The string leaves space
+ uint8_t chars = LCD_WIDTH - rlen; // Amount of space left in characters
+
+ u8g.print('.'); // Always at 1+ spaces left, draw a dot
+ if (--chars) { // Draw a second dot if there's space
+ u8g.print('.');
+ if (--chars) {
+ // Print a second copy of the message
+ lcd_print_utf(lcd_status_message, LCD_WIDTH - (rlen+2));
+ }
}
}
- if (last_blink != blink) {
- last_blink = blink;
- // Skip any non-printing bytes
- if (status_scroll_pos < slen) while (!PRINTABLE(lcd_status_message[status_scroll_pos])) status_scroll_pos++;
- if (++status_scroll_pos >= slen + 2) status_scroll_pos = 0;
+ if (last_blink != blink) {
+ last_blink = blink;
+
+ // Adjust by complete UTF8 characters
+ if (status_scroll_offset < slen) {
+ status_scroll_offset++;
+ while (!START_OF_UTF8_CHAR(lcd_status_message[status_scroll_offset]))
+ status_scroll_offset++;
+ }
+ else
+ status_scroll_offset = 0;
}
}
#else
UNUSED(blink);
- lcd_print_utf(lcd_status_message);
+
+ // Get the UTF8 character count of the string
+ uint8_t slen = lcd_strlen(lcd_status_message);
+
+ // Just print the string to the LCD
+ lcd_print_utf(lcd_status_message, LCD_WIDTH);
+
+ // Fill the rest with spaces if there are missing spaces
+ while (slen < LCD_WIDTH) {
+ u8g.print(' ');
+ ++slen;
+ }
#endif
}
@@ -220,7 +267,7 @@ static void lcd_implementation_status_screen() {
#endif
#if HAS_FAN0
- if (PAGE_CONTAINS(20, 27)) {
+ if (PAGE_CONTAINS(STATUS_SCREEN_FAN_TEXT_Y - 7, STATUS_SCREEN_FAN_TEXT_Y)) {
// Fan
const int16_t per = ((fanSpeeds[0] + 1) * 100) / 256;
if (per) {
@@ -330,10 +377,6 @@ static void lcd_implementation_status_screen() {
#define XYZ_FRAME_HEIGHT INFO_FONT_HEIGHT + 1
#endif
- // Before homing the axis letters are blinking 'X' <-> '?'.
- // When axis is homed but axis_known_position is false the axis letters are blinking 'X' <-> ' '.
- // When everything is ok you see a constant 'X'.
-
static char xstring[5], ystring[5], zstring[7];
#if ENABLED(FILAMENT_LCD_DISPLAY)
static char wstring[5], mstring[4];
@@ -370,19 +413,19 @@ static void lcd_implementation_status_screen() {
#endif
u8g.setPrintPos(0 * XYZ_SPACING + X_LABEL_POS, XYZ_BASELINE);
- _draw_axis_label(X_AXIS, PSTR(MSG_X), blink);
+ lcd_printPGM(PSTR(MSG_X));
u8g.setPrintPos(0 * XYZ_SPACING + X_VALUE_POS, XYZ_BASELINE);
- lcd_print(xstring);
+ _draw_axis_value(X_AXIS, xstring, blink);
u8g.setPrintPos(1 * XYZ_SPACING + X_LABEL_POS, XYZ_BASELINE);
- _draw_axis_label(Y_AXIS, PSTR(MSG_Y), blink);
+ lcd_printPGM(PSTR(MSG_Y));
u8g.setPrintPos(1 * XYZ_SPACING + X_VALUE_POS, XYZ_BASELINE);
- lcd_print(ystring);
+ _draw_axis_value(Y_AXIS, ystring, blink);
u8g.setPrintPos(2 * XYZ_SPACING + X_LABEL_POS, XYZ_BASELINE);
- _draw_axis_label(Z_AXIS, PSTR(MSG_Z), blink);
+ lcd_printPGM(PSTR(MSG_Z));
u8g.setPrintPos(2 * XYZ_SPACING + X_VALUE_POS, XYZ_BASELINE);
- lcd_print(zstring);
+ _draw_axis_value(Z_AXIS, zstring, blink);
#if DISABLED(XYZ_HOLLOW_FRAME)
u8g.setColorIndex(1); // black on white
diff --git a/Marlin/status_screen_lite_ST7920.h b/Marlin/status_screen_lite_ST7920.h
index 987aba4f76..37eabe14bf 100644
--- a/Marlin/status_screen_lite_ST7920.h
+++ b/Marlin/status_screen_lite_ST7920.h
@@ -615,36 +615,71 @@ void ST7920_Lite_Status_Screen::draw_feedrate_percentage(const uint8_t percentag
void ST7920_Lite_Status_Screen::draw_status_message(const char *str) {
set_ddram_address(DDRAM_LINE_4);
begin_data();
+ const uint8_t lcd_len = 16;
#if ENABLED(STATUS_MESSAGE_SCROLLING)
- const uint8_t lcd_len = 16;
- const uint8_t padding = 2;
- uint8_t str_len = strlen(str);
+
+ uint8_t slen = lcd_strlen(str);
- // Trim whitespace at the end of the str, as for some reason
- // messages like "Card Inserted" are padded with many spaces
- while (str_len && str[str_len - 1] == ' ') str_len--;
+ // If the string fits into the LCD, just print it and do not scroll it
+ if (slen <= lcd_len) {
- if (str_len <= lcd_len) {
- // It all fits on the LCD without scrolling
+ // The string isn't scrolling and may not fill the screen
write_str(str);
+
+ // Fill the rest with spaces
+ while (slen < lcd_len) {
+ write_byte(' ');
+ ++slen;
+ }
}
else {
- // Print the message repeatedly until covering the LCD
- uint8_t c = status_scroll_pos;
- for (uint8_t n = 0; n < lcd_len; n++) {
- write_byte(c < str_len ? str[c] : ' ');
- c++;
- c %= str_len + padding; // Wrap around
+ // String is larger than the available space in screen.
+
+ // Get a pointer to the next valid UTF8 character
+ const char *stat = str + status_scroll_offset;
+
+ // Get the string remaining length
+ const uint8_t rlen = lcd_strlen(stat);
+
+ // If we have enough characters to display
+ if (rlen >= lcd_len) {
+ // The remaining string fills the screen - Print it
+ write_str(stat, lcd_len);
+ }
+ else {
+ // The remaining string does not completely fill the screen
+ write_str(stat); // The string leaves space
+ uint8_t chars = lcd_len - rlen; // Amount of space left in characters
+
+ write_byte('.'); // Always at 1+ spaces left, draw a dot
+ if (--chars) { // Draw a second dot if there's space
+ write_byte('.');
+ if (--chars)
+ write_str(str, chars); // Print a second copy of the message
+ }
}
- // Scroll the message
- if (status_scroll_pos == str_len + padding)
- status_scroll_pos = 0;
+ // Adjust by complete UTF8 characters
+ if (status_scroll_offset < slen) {
+ status_scroll_offset++;
+ while (!START_OF_UTF8_CHAR(str[status_scroll_offset]))
+ status_scroll_offset++;
+ }
else
- status_scroll_pos++;
+ status_scroll_offset = 0;
}
#else
- write_str(str, 16);
+ // Get the UTF8 character count of the string
+ uint8_t slen = lcd_strlen(str);
+
+ // Just print the string to the LCD
+ write_str(str, lcd_len);
+
+ // Fill the rest with spaces if there are missing spaces
+ while (slen < lcd_len) {
+ write_byte(' ');
+ ++slen;
+ }
#endif
}
@@ -792,7 +827,7 @@ void ST7920_Lite_Status_Screen::update_status_or_position(bool forceUpdate) {
*/
if (forceUpdate || status_changed()) {
#if ENABLED(STATUS_MESSAGE_SCROLLING)
- status_scroll_pos = 0;
+ status_scroll_offset = 0;
#endif
#if STATUS_EXPIRE_SECONDS
countdown = lcd_status_message[0] ? STATUS_EXPIRE_SECONDS : 0;
diff --git a/Marlin/stepper.cpp b/Marlin/stepper.cpp
index 1f03e3ccb3..bd3f1b77da 100644
--- a/Marlin/stepper.cpp
+++ b/Marlin/stepper.cpp
@@ -50,6 +50,8 @@
* Jerk controlled movements planner added Apr 2018 by Eduardo José Tagle.
* Equations based on Synthethos TinyG2 sources, but the fixed-point
* implementation is new, as we are running the ISR with a variable period.
+ * Also implemented the Bézier velocity curve evaluation in ARM assembler,
+ * to avoid impacting ISR speed.
*/
#include "Marlin.h"
@@ -61,6 +63,7 @@
#include "language.h"
#include "cardreader.h"
#include "speed_lookuptable.h"
+#include "delay.h"
#if HAS_DIGIPOTSS
#include
@@ -72,10 +75,6 @@ Stepper stepper; // Singleton
block_t* Stepper::current_block = NULL; // A pointer to the block currently being traced
-#if ENABLED(ABORT_ON_ENDSTOP_HIT_FEATURE_ENABLED)
- bool Stepper::abort_on_endstop_hit = false;
-#endif
-
#if ENABLED(X_DUAL_ENDSTOPS) || ENABLED(Y_DUAL_ENDSTOPS) || ENABLED(Z_DUAL_ENDSTOPS)
bool Stepper::performing_homing = false;
#endif
@@ -86,8 +85,10 @@ block_t* Stepper::current_block = NULL; // A pointer to the block currently bei
// private:
-uint8_t Stepper::last_direction_bits = 0; // The next stepping-bits to be output
-int16_t Stepper::cleaning_buffer_counter = 0;
+uint8_t Stepper::last_direction_bits = 0,
+ Stepper::last_movement_extruder = 0xFF,
+ Stepper::axis_did_move;
+bool Stepper::abort_current_block;
#if ENABLED(X_DUAL_ENDSTOPS)
bool Stepper::locked_x_motor = false, Stepper::locked_x2_motor = false;
@@ -104,7 +105,7 @@ int32_t Stepper::counter_X = 0,
Stepper::counter_Z = 0,
Stepper::counter_E = 0;
-volatile uint32_t Stepper::step_events_completed = 0; // The number of step events executed in the current block
+uint32_t Stepper::step_events_completed = 0; // The number of step events executed in the current block
#if ENABLED(BEZIER_JERK_CONTROL)
int32_t __attribute__((used)) Stepper::bezier_A __asm__("bezier_A"); // A coefficient in Bézier speed curve with alias for assembler
@@ -116,16 +117,17 @@ volatile uint32_t Stepper::step_events_completed = 0; // The number of step even
bool Stepper::bezier_2nd_half; // =false If Bézier curve has been initialized or not
#endif
+uint32_t Stepper::nextMainISR = 0;
+bool Stepper::all_steps_done = false;
+
#if ENABLED(LIN_ADVANCE)
uint32_t Stepper::LA_decelerate_after;
- constexpr uint16_t ADV_NEVER = 65535;
-
- uint16_t Stepper::nextMainISR = 0,
- Stepper::nextAdvanceISR = ADV_NEVER,
- Stepper::eISR_Rate = ADV_NEVER,
- Stepper::current_adv_steps = 0,
+ constexpr uint32_t ADV_NEVER = 0xFFFFFFFF;
+ uint32_t Stepper::nextAdvanceISR = ADV_NEVER,
+ Stepper::eISR_Rate = ADV_NEVER;
+ uint16_t Stepper::current_adv_steps = 0,
Stepper::final_adv_steps,
Stepper::max_adv_steps;
@@ -141,7 +143,7 @@ volatile uint32_t Stepper::step_events_completed = 0; // The number of step even
#endif // LIN_ADVANCE
-int32_t Stepper::acceleration_time, Stepper::deceleration_time;
+uint32_t Stepper::acceleration_time, Stepper::deceleration_time;
volatile int32_t Stepper::count_position[NUM_AXIS] = { 0 };
volatile signed char Stepper::count_direction[NUM_AXIS] = { 1, 1, 1, 1 };
@@ -150,11 +152,11 @@ volatile signed char Stepper::count_direction[NUM_AXIS] = { 1, 1, 1, 1 };
int32_t Stepper::counter_m[MIXING_STEPPERS];
#endif
+uint32_t Stepper::ticks_nominal;
uint8_t Stepper::step_loops, Stepper::step_loops_nominal;
-uint16_t Stepper::OCR1A_nominal;
#if DISABLED(BEZIER_JERK_CONTROL)
- uint16_t Stepper::acc_step_rate; // needed for deceleration start point
+ uint32_t Stepper::acc_step_rate; // needed for deceleration start point
#endif
volatile int32_t Stepper::endstops_trigsteps[XYZ];
@@ -166,20 +168,20 @@ volatile int32_t Stepper::endstops_trigsteps[XYZ];
#define LOCKED_X2_MOTOR locked_x2_motor
#define LOCKED_Y2_MOTOR locked_y2_motor
#define LOCKED_Z2_MOTOR locked_z2_motor
- #define DUAL_ENDSTOP_APPLY_STEP(AXIS,v) \
- if (performing_homing) { \
- if (AXIS##_HOME_DIR < 0) { \
- if (!(TEST(endstops.old_endstop_bits, AXIS##_MIN) && count_direction[AXIS##_AXIS] < 0) && !LOCKED_##AXIS##_MOTOR) AXIS##_STEP_WRITE(v); \
- if (!(TEST(endstops.old_endstop_bits, AXIS##2_MIN) && count_direction[AXIS##_AXIS] < 0) && !LOCKED_##AXIS##2_MOTOR) AXIS##2_STEP_WRITE(v); \
- } \
- else { \
- if (!(TEST(endstops.old_endstop_bits, AXIS##_MAX) && count_direction[AXIS##_AXIS] > 0) && !LOCKED_##AXIS##_MOTOR) AXIS##_STEP_WRITE(v); \
- if (!(TEST(endstops.old_endstop_bits, AXIS##2_MAX) && count_direction[AXIS##_AXIS] > 0) && !LOCKED_##AXIS##2_MOTOR) AXIS##2_STEP_WRITE(v); \
- } \
- } \
- else { \
- AXIS##_STEP_WRITE(v); \
- AXIS##2_STEP_WRITE(v); \
+ #define DUAL_ENDSTOP_APPLY_STEP(A,V) \
+ if (performing_homing) { \
+ if (A##_HOME_DIR < 0) { \
+ if (!(TEST(endstops.state(), A##_MIN) && count_direction[_AXIS(A)] < 0) && !LOCKED_##A##_MOTOR) A##_STEP_WRITE(V); \
+ if (!(TEST(endstops.state(), A##2_MIN) && count_direction[_AXIS(A)] < 0) && !LOCKED_##A##2_MOTOR) A##2_STEP_WRITE(V); \
+ } \
+ else { \
+ if (!(TEST(endstops.state(), A##_MAX) && count_direction[_AXIS(A)] > 0) && !LOCKED_##A##_MOTOR) A##_STEP_WRITE(V); \
+ if (!(TEST(endstops.state(), A##2_MAX) && count_direction[_AXIS(A)] > 0) && !LOCKED_##A##2_MOTOR) A##2_STEP_WRITE(V); \
+ } \
+ } \
+ else { \
+ A##_STEP_WRITE(V); \
+ A##2_STEP_WRITE(V); \
}
#endif
@@ -242,59 +244,63 @@ volatile int32_t Stepper::endstops_trigsteps[XYZ];
// intRes = longIn1 * longIn2 >> 24
// uses:
-// r26 to store 0
-// r27 to store bits 16-23 of the 48bit result. The top bit is used to round the two byte result.
+// A[tmp] to store 0
+// B[tmp] to store bits 16-23 of the 48bit result. The top bit is used to round the two byte result.
// note that the lower two bytes and the upper byte of the 48bit result are not calculated.
// this can cause the result to be out by one as the lower bytes may cause carries into the upper ones.
-// B0 A0 are bits 24-39 and are the returned value
-// C1 B1 A1 is longIn1
-// D2 C2 B2 A2 is longIn2
+// B A are bits 24-39 and are the returned value
+// C B A is longIn1
+// D C B A is longIn2
//
-#define MultiU24X32toH16(intRes, longIn1, longIn2) \
- asm volatile ( \
- A("clr r26") \
- A("mul %A1, %B2") \
- A("mov r27, r1") \
- A("mul %B1, %C2") \
- A("movw %A0, r0") \
- A("mul %C1, %C2") \
- A("add %B0, r0") \
- A("mul %C1, %B2") \
- A("add %A0, r0") \
- A("adc %B0, r1") \
- A("mul %A1, %C2") \
- A("add r27, r0") \
- A("adc %A0, r1") \
- A("adc %B0, r26") \
- A("mul %B1, %B2") \
- A("add r27, r0") \
- A("adc %A0, r1") \
- A("adc %B0, r26") \
- A("mul %C1, %A2") \
- A("add r27, r0") \
- A("adc %A0, r1") \
- A("adc %B0, r26") \
- A("mul %B1, %A2") \
- A("add r27, r1") \
- A("adc %A0, r26") \
- A("adc %B0, r26") \
- A("lsr r27") \
- A("adc %A0, r26") \
- A("adc %B0, r26") \
- A("mul %D2, %A1") \
- A("add %A0, r0") \
- A("adc %B0, r1") \
- A("mul %D2, %B1") \
- A("add %B0, r0") \
- A("clr r1") \
- : \
- "=&r" (intRes) \
- : \
- "d" (longIn1), \
- "d" (longIn2) \
- : \
- "r26" , "r27" \
- )
+static FORCE_INLINE uint16_t MultiU24X32toH16(uint32_t longIn1, uint32_t longIn2) {
+ register uint8_t tmp1;
+ register uint8_t tmp2;
+ register uint16_t intRes;
+ __asm__ __volatile__(
+ A("clr %[tmp1]")
+ A("mul %A[longIn1], %B[longIn2]")
+ A("mov %[tmp2], r1")
+ A("mul %B[longIn1], %C[longIn2]")
+ A("movw %A[intRes], r0")
+ A("mul %C[longIn1], %C[longIn2]")
+ A("add %B[intRes], r0")
+ A("mul %C[longIn1], %B[longIn2]")
+ A("add %A[intRes], r0")
+ A("adc %B[intRes], r1")
+ A("mul %A[longIn1], %C[longIn2]")
+ A("add %[tmp2], r0")
+ A("adc %A[intRes], r1")
+ A("adc %B[intRes], %[tmp1]")
+ A("mul %B[longIn1], %B[longIn2]")
+ A("add %[tmp2], r0")
+ A("adc %A[intRes], r1")
+ A("adc %B[intRes], %[tmp1]")
+ A("mul %C[longIn1], %A[longIn2]")
+ A("add %[tmp2], r0")
+ A("adc %A[intRes], r1")
+ A("adc %B[intRes], %[tmp1]")
+ A("mul %B[longIn1], %A[longIn2]")
+ A("add %[tmp2], r1")
+ A("adc %A[intRes], %[tmp1]")
+ A("adc %B[intRes], %[tmp1]")
+ A("lsr %[tmp2]")
+ A("adc %A[intRes], %[tmp1]")
+ A("adc %B[intRes], %[tmp1]")
+ A("mul %D[longIn2], %A[longIn1]")
+ A("add %A[intRes], r0")
+ A("adc %B[intRes], r1")
+ A("mul %D[longIn2], %B[longIn1]")
+ A("add %B[intRes], r0")
+ A("clr r1")
+ : [intRes] "=&r" (intRes),
+ [tmp1] "=&r" (tmp1),
+ [tmp2] "=&r" (tmp2)
+ : [longIn1] "d" (longIn1),
+ [longIn2] "d" (longIn2)
+ : "cc"
+ );
+ return intRes;
+}
// Some useful constants
@@ -329,14 +335,14 @@ void Stepper::wake_up() {
*/
void Stepper::set_directions() {
- #define SET_STEP_DIR(AXIS) \
- if (motor_direction(AXIS ##_AXIS)) { \
- AXIS ##_APPLY_DIR(INVERT_## AXIS ##_DIR, false); \
- count_direction[AXIS ##_AXIS] = -1; \
+ #define SET_STEP_DIR(A) \
+ if (motor_direction(_AXIS(A))) { \
+ A##_APPLY_DIR(INVERT_## A##_DIR, false); \
+ count_direction[_AXIS(A)] = -1; \
} \
else { \
- AXIS ##_APPLY_DIR(!INVERT_## AXIS ##_DIR, false); \
- count_direction[AXIS ##_AXIS] = 1; \
+ A##_APPLY_DIR(!INVERT_## A##_DIR, false); \
+ count_direction[_AXIS(A)] = 1; \
}
#if HAS_X_DIR
@@ -361,10 +367,6 @@ void Stepper::set_directions() {
#endif // !LIN_ADVANCE
}
-#if ENABLED(ENDSTOP_INTERRUPTS_FEATURE)
- extern volatile uint8_t e_hit;
-#endif
-
#if ENABLED(BEZIER_JERK_CONTROL)
/**
* We are using a quintic (fifth-degree) Bézier polynomial for the velocity curve.
@@ -421,7 +423,7 @@ void Stepper::set_directions() {
*
* Floating point arithmetic execution time cost is prohibitive, so we will transform the math to
* use fixed point values to be able to evaluate it in realtime. Assuming a maximum of 250000 steps
- * per second (driver pulses should at least be 2uS hi/2uS lo), and allocating 2 bits to avoid
+ * per second (driver pulses should at least be 2µS hi/2µS lo), and allocating 2 bits to avoid
* overflows on the evaluation of the Bézier curve, means we can use
*
* t: unsigned Q0.32 (0 <= t < 1) |range 0 to 0xFFFFFFFF unsigned
@@ -625,69 +627,69 @@ void Stepper::set_directions() {
/* %10 (must be high register!)*/
/* Store initial velocity*/
- A("sts bezier_F, %0")
- A("sts bezier_F+1, %1")
- A("sts bezier_F+2, %10") /* bezier_F = %10:%1:%0 = v0 */
+ A("sts bezier_F, %0")
+ A("sts bezier_F+1, %1")
+ A("sts bezier_F+2, %10") /* bezier_F = %10:%1:%0 = v0 */
/* Get delta speed */
- A("ldi %2,-1") /* %2 = 0xFF, means A_negative = true */
- A("clr %8") /* %8 = 0 */
- A("sub %0,%3")
- A("sbc %1,%4")
- A("sbc %10,%5") /* v0 -= v1, C=1 if result is negative */
- A("brcc 1f") /* branch if result is positive (C=0), that means v0 >= v1 */
+ A("ldi %2,-1") /* %2 = 0xFF, means A_negative = true */
+ A("clr %8") /* %8 = 0 */
+ A("sub %0,%3")
+ A("sbc %1,%4")
+ A("sbc %10,%5") /* v0 -= v1, C=1 if result is negative */
+ A("brcc 1f") /* branch if result is positive (C=0), that means v0 >= v1 */
/* Result was negative, get the absolute value*/
- A("com %10")
- A("com %1")
- A("neg %0")
- A("sbc %1,%2")
- A("sbc %10,%2") /* %10:%1:%0 +1 -> %10:%1:%0 = -(v0 - v1) = (v1 - v0) */
- A("clr %2") /* %2 = 0, means A_negative = false */
+ A("com %10")
+ A("com %1")
+ A("neg %0")
+ A("sbc %1,%2")
+ A("sbc %10,%2") /* %10:%1:%0 +1 -> %10:%1:%0 = -(v0 - v1) = (v1 - v0) */
+ A("clr %2") /* %2 = 0, means A_negative = false */
/* Store negative flag*/
L("1")
- A("sts A_negative, %2") /* Store negative flag */
+ A("sts A_negative, %2") /* Store negative flag */
/* Compute coefficients A,B and C [20 cycles worst case]*/
- A("ldi %9,6") /* %9 = 6 */
- A("mul %0,%9") /* r1:r0 = 6*LO(v0-v1) */
- A("sts bezier_A, r0")
- A("mov %6,r1")
- A("clr %7") /* %7:%6:r0 = 6*LO(v0-v1) */
- A("mul %1,%9") /* r1:r0 = 6*MI(v0-v1) */
- A("add %6,r0")
- A("adc %7,r1") /* %7:%6:?? += 6*MI(v0-v1) << 8 */
- A("mul %10,%9") /* r1:r0 = 6*HI(v0-v1) */
- A("add %7,r0") /* %7:%6:?? += 6*HI(v0-v1) << 16 */
- A("sts bezier_A+1, %6")
- A("sts bezier_A+2, %7") /* bezier_A = %7:%6:?? = 6*(v0-v1) [35 cycles worst] */
+ A("ldi %9,6") /* %9 = 6 */
+ A("mul %0,%9") /* r1:r0 = 6*LO(v0-v1) */
+ A("sts bezier_A, r0")
+ A("mov %6,r1")
+ A("clr %7") /* %7:%6:r0 = 6*LO(v0-v1) */
+ A("mul %1,%9") /* r1:r0 = 6*MI(v0-v1) */
+ A("add %6,r0")
+ A("adc %7,r1") /* %7:%6:?? += 6*MI(v0-v1) << 8 */
+ A("mul %10,%9") /* r1:r0 = 6*HI(v0-v1) */
+ A("add %7,r0") /* %7:%6:?? += 6*HI(v0-v1) << 16 */
+ A("sts bezier_A+1, %6")
+ A("sts bezier_A+2, %7") /* bezier_A = %7:%6:?? = 6*(v0-v1) [35 cycles worst] */
- A("ldi %9,15") /* %9 = 15 */
- A("mul %0,%9") /* r1:r0 = 5*LO(v0-v1) */
- A("sts bezier_B, r0")
- A("mov %6,r1")
- A("clr %7") /* %7:%6:?? = 5*LO(v0-v1) */
- A("mul %1,%9") /* r1:r0 = 5*MI(v0-v1) */
- A("add %6,r0")
- A("adc %7,r1") /* %7:%6:?? += 5*MI(v0-v1) << 8 */
- A("mul %10,%9") /* r1:r0 = 5*HI(v0-v1) */
- A("add %7,r0") /* %7:%6:?? += 5*HI(v0-v1) << 16 */
- A("sts bezier_B+1, %6")
- A("sts bezier_B+2, %7") /* bezier_B = %7:%6:?? = 5*(v0-v1) [50 cycles worst] */
+ A("ldi %9,15") /* %9 = 15 */
+ A("mul %0,%9") /* r1:r0 = 5*LO(v0-v1) */
+ A("sts bezier_B, r0")
+ A("mov %6,r1")
+ A("clr %7") /* %7:%6:?? = 5*LO(v0-v1) */
+ A("mul %1,%9") /* r1:r0 = 5*MI(v0-v1) */
+ A("add %6,r0")
+ A("adc %7,r1") /* %7:%6:?? += 5*MI(v0-v1) << 8 */
+ A("mul %10,%9") /* r1:r0 = 5*HI(v0-v1) */
+ A("add %7,r0") /* %7:%6:?? += 5*HI(v0-v1) << 16 */
+ A("sts bezier_B+1, %6")
+ A("sts bezier_B+2, %7") /* bezier_B = %7:%6:?? = 5*(v0-v1) [50 cycles worst] */
- A("ldi %9,10") /* %9 = 10 */
- A("mul %0,%9") /* r1:r0 = 10*LO(v0-v1) */
- A("sts bezier_C, r0")
- A("mov %6,r1")
- A("clr %7") /* %7:%6:?? = 10*LO(v0-v1) */
- A("mul %1,%9") /* r1:r0 = 10*MI(v0-v1) */
- A("add %6,r0")
- A("adc %7,r1") /* %7:%6:?? += 10*MI(v0-v1) << 8 */
- A("mul %10,%9") /* r1:r0 = 10*HI(v0-v1) */
- A("add %7,r0") /* %7:%6:?? += 10*HI(v0-v1) << 16 */
- A("sts bezier_C+1, %6")
- " sts bezier_C+2, %7" /* bezier_C = %7:%6:?? = 10*(v0-v1) [65 cycles worst] */
+ A("ldi %9,10") /* %9 = 10 */
+ A("mul %0,%9") /* r1:r0 = 10*LO(v0-v1) */
+ A("sts bezier_C, r0")
+ A("mov %6,r1")
+ A("clr %7") /* %7:%6:?? = 10*LO(v0-v1) */
+ A("mul %1,%9") /* r1:r0 = 10*MI(v0-v1) */
+ A("add %6,r0")
+ A("adc %7,r1") /* %7:%6:?? += 10*MI(v0-v1) << 8 */
+ A("mul %10,%9") /* r1:r0 = 10*HI(v0-v1) */
+ A("add %7,r0") /* %7:%6:?? += 10*HI(v0-v1) << 16 */
+ A("sts bezier_C+1, %6")
+ " sts bezier_C+2, %7" /* bezier_C = %7:%6:?? = 10*(v0-v1) [65 cycles worst] */
: "+r" (r2),
"+d" (r3),
"=r" (r4),
@@ -718,359 +720,359 @@ void Stepper::set_directions() {
__asm__ __volatile(
/* umul24x24to16hi(t, bezier_AV, curr_step); t: Range 0 - 1^16 = 16 bits*/
- A("lds %9,bezier_AV") /* %9 = LO(AV)*/
- A("mul %9,%2") /* r1:r0 = LO(bezier_AV)*LO(curr_step)*/
- A("mov %7,r1") /* %7 = LO(bezier_AV)*LO(curr_step) >> 8*/
- A("clr %8") /* %8:%7 = LO(bezier_AV)*LO(curr_step) >> 8*/
- A("lds %10,bezier_AV+1") /* %10 = MI(AV)*/
- A("mul %10,%2") /* r1:r0 = MI(bezier_AV)*LO(curr_step)*/
- A("add %7,r0")
- A("adc %8,r1") /* %8:%7 += MI(bezier_AV)*LO(curr_step)*/
- A("lds r1,bezier_AV+2") /* r11 = HI(AV)*/
- A("mul r1,%2") /* r1:r0 = HI(bezier_AV)*LO(curr_step)*/
- A("add %8,r0") /* %8:%7 += HI(bezier_AV)*LO(curr_step) << 8*/
- A("mul %9,%3") /* r1:r0 = LO(bezier_AV)*MI(curr_step)*/
- A("add %7,r0")
- A("adc %8,r1") /* %8:%7 += LO(bezier_AV)*MI(curr_step)*/
- A("mul %10,%3") /* r1:r0 = MI(bezier_AV)*MI(curr_step)*/
- A("add %8,r0") /* %8:%7 += LO(bezier_AV)*MI(curr_step) << 8*/
- A("mul %9,%4") /* r1:r0 = LO(bezier_AV)*HI(curr_step)*/
- A("add %8,r0") /* %8:%7 += LO(bezier_AV)*HI(curr_step) << 8*/
+ A("lds %9,bezier_AV") /* %9 = LO(AV)*/
+ A("mul %9,%2") /* r1:r0 = LO(bezier_AV)*LO(curr_step)*/
+ A("mov %7,r1") /* %7 = LO(bezier_AV)*LO(curr_step) >> 8*/
+ A("clr %8") /* %8:%7 = LO(bezier_AV)*LO(curr_step) >> 8*/
+ A("lds %10,bezier_AV+1") /* %10 = MI(AV)*/
+ A("mul %10,%2") /* r1:r0 = MI(bezier_AV)*LO(curr_step)*/
+ A("add %7,r0")
+ A("adc %8,r1") /* %8:%7 += MI(bezier_AV)*LO(curr_step)*/
+ A("lds r1,bezier_AV+2") /* r11 = HI(AV)*/
+ A("mul r1,%2") /* r1:r0 = HI(bezier_AV)*LO(curr_step)*/
+ A("add %8,r0") /* %8:%7 += HI(bezier_AV)*LO(curr_step) << 8*/
+ A("mul %9,%3") /* r1:r0 = LO(bezier_AV)*MI(curr_step)*/
+ A("add %7,r0")
+ A("adc %8,r1") /* %8:%7 += LO(bezier_AV)*MI(curr_step)*/
+ A("mul %10,%3") /* r1:r0 = MI(bezier_AV)*MI(curr_step)*/
+ A("add %8,r0") /* %8:%7 += LO(bezier_AV)*MI(curr_step) << 8*/
+ A("mul %9,%4") /* r1:r0 = LO(bezier_AV)*HI(curr_step)*/
+ A("add %8,r0") /* %8:%7 += LO(bezier_AV)*HI(curr_step) << 8*/
/* %8:%7 = t*/
/* uint16_t f = t;*/
- A("mov %5,%7") /* %6:%5 = f*/
- A("mov %6,%8")
+ A("mov %5,%7") /* %6:%5 = f*/
+ A("mov %6,%8")
/* %6:%5 = f*/
/* umul16x16to16hi(f, f, t); / Range 16 bits (unsigned) [17] */
- A("mul %5,%7") /* r1:r0 = LO(f) * LO(t)*/
- A("mov %9,r1") /* store MIL(LO(f) * LO(t)) in %9, we need it for rounding*/
- A("clr %10") /* %10 = 0*/
- A("clr %11") /* %11 = 0*/
- A("mul %5,%8") /* r1:r0 = LO(f) * HI(t)*/
- A("add %9,r0") /* %9 += LO(LO(f) * HI(t))*/
- A("adc %10,r1") /* %10 = HI(LO(f) * HI(t))*/
- A("adc %11,%0") /* %11 += carry*/
- A("mul %6,%7") /* r1:r0 = HI(f) * LO(t)*/
- A("add %9,r0") /* %9 += LO(HI(f) * LO(t))*/
- A("adc %10,r1") /* %10 += HI(HI(f) * LO(t)) */
- A("adc %11,%0") /* %11 += carry*/
- A("mul %6,%8") /* r1:r0 = HI(f) * HI(t)*/
- A("add %10,r0") /* %10 += LO(HI(f) * HI(t))*/
- A("adc %11,r1") /* %11 += HI(HI(f) * HI(t))*/
- A("mov %5,%10") /* %6:%5 = */
- A("mov %6,%11") /* f = %10:%11*/
+ A("mul %5,%7") /* r1:r0 = LO(f) * LO(t)*/
+ A("mov %9,r1") /* store MIL(LO(f) * LO(t)) in %9, we need it for rounding*/
+ A("clr %10") /* %10 = 0*/
+ A("clr %11") /* %11 = 0*/
+ A("mul %5,%8") /* r1:r0 = LO(f) * HI(t)*/
+ A("add %9,r0") /* %9 += LO(LO(f) * HI(t))*/
+ A("adc %10,r1") /* %10 = HI(LO(f) * HI(t))*/
+ A("adc %11,%0") /* %11 += carry*/
+ A("mul %6,%7") /* r1:r0 = HI(f) * LO(t)*/
+ A("add %9,r0") /* %9 += LO(HI(f) * LO(t))*/
+ A("adc %10,r1") /* %10 += HI(HI(f) * LO(t)) */
+ A("adc %11,%0") /* %11 += carry*/
+ A("mul %6,%8") /* r1:r0 = HI(f) * HI(t)*/
+ A("add %10,r0") /* %10 += LO(HI(f) * HI(t))*/
+ A("adc %11,r1") /* %11 += HI(HI(f) * HI(t))*/
+ A("mov %5,%10") /* %6:%5 = */
+ A("mov %6,%11") /* f = %10:%11*/
/* umul16x16to16hi(f, f, t); / Range 16 bits : f = t^3 (unsigned) [17]*/
- A("mul %5,%7") /* r1:r0 = LO(f) * LO(t)*/
- A("mov %1,r1") /* store MIL(LO(f) * LO(t)) in %1, we need it for rounding*/
- A("clr %10") /* %10 = 0*/
- A("clr %11") /* %11 = 0*/
- A("mul %5,%8") /* r1:r0 = LO(f) * HI(t)*/
- A("add %1,r0") /* %1 += LO(LO(f) * HI(t))*/
- A("adc %10,r1") /* %10 = HI(LO(f) * HI(t))*/
- A("adc %11,%0") /* %11 += carry*/
- A("mul %6,%7") /* r1:r0 = HI(f) * LO(t)*/
- A("add %1,r0") /* %1 += LO(HI(f) * LO(t))*/
- A("adc %10,r1") /* %10 += HI(HI(f) * LO(t))*/
- A("adc %11,%0") /* %11 += carry*/
- A("mul %6,%8") /* r1:r0 = HI(f) * HI(t)*/
- A("add %10,r0") /* %10 += LO(HI(f) * HI(t))*/
- A("adc %11,r1") /* %11 += HI(HI(f) * HI(t))*/
- A("mov %5,%10") /* %6:%5 =*/
- A("mov %6,%11") /* f = %10:%11*/
+ A("mul %5,%7") /* r1:r0 = LO(f) * LO(t)*/
+ A("mov %1,r1") /* store MIL(LO(f) * LO(t)) in %1, we need it for rounding*/
+ A("clr %10") /* %10 = 0*/
+ A("clr %11") /* %11 = 0*/
+ A("mul %5,%8") /* r1:r0 = LO(f) * HI(t)*/
+ A("add %1,r0") /* %1 += LO(LO(f) * HI(t))*/
+ A("adc %10,r1") /* %10 = HI(LO(f) * HI(t))*/
+ A("adc %11,%0") /* %11 += carry*/
+ A("mul %6,%7") /* r1:r0 = HI(f) * LO(t)*/
+ A("add %1,r0") /* %1 += LO(HI(f) * LO(t))*/
+ A("adc %10,r1") /* %10 += HI(HI(f) * LO(t))*/
+ A("adc %11,%0") /* %11 += carry*/
+ A("mul %6,%8") /* r1:r0 = HI(f) * HI(t)*/
+ A("add %10,r0") /* %10 += LO(HI(f) * HI(t))*/
+ A("adc %11,r1") /* %11 += HI(HI(f) * HI(t))*/
+ A("mov %5,%10") /* %6:%5 =*/
+ A("mov %6,%11") /* f = %10:%11*/
/* [15 +17*2] = [49]*/
/* %4:%3:%2 will be acc from now on*/
/* uint24_t acc = bezier_F; / Range 20 bits (unsigned)*/
- A("clr %9") /* "decimal place we get for free"*/
- A("lds %2,bezier_F")
- A("lds %3,bezier_F+1")
- A("lds %4,bezier_F+2") /* %4:%3:%2 = acc*/
+ A("clr %9") /* "decimal place we get for free"*/
+ A("lds %2,bezier_F")
+ A("lds %3,bezier_F+1")
+ A("lds %4,bezier_F+2") /* %4:%3:%2 = acc*/
/* if (A_negative) {*/
- A("lds r0,A_negative")
- A("or r0,%0") /* Is flag signalling negative? */
- A("brne 3f") /* If yes, Skip next instruction if A was negative*/
- A("rjmp 1f") /* Otherwise, jump */
+ A("lds r0,A_negative")
+ A("or r0,%0") /* Is flag signalling negative? */
+ A("brne 3f") /* If yes, Skip next instruction if A was negative*/
+ A("rjmp 1f") /* Otherwise, jump */
/* uint24_t v; */
/* umul16x24to24hi(v, f, bezier_C); / Range 21bits [29] */
/* acc -= v; */
L("3")
- A("lds %10, bezier_C") /* %10 = LO(bezier_C)*/
- A("mul %10,%5") /* r1:r0 = LO(bezier_C) * LO(f)*/
- A("sub %9,r1")
- A("sbc %2,%0")
- A("sbc %3,%0")
- A("sbc %4,%0") /* %4:%3:%2:%9 -= HI(LO(bezier_C) * LO(f))*/
- A("lds %11, bezier_C+1") /* %11 = MI(bezier_C)*/
- A("mul %11,%5") /* r1:r0 = MI(bezier_C) * LO(f)*/
- A("sub %9,r0")
- A("sbc %2,r1")
- A("sbc %3,%0")
- A("sbc %4,%0") /* %4:%3:%2:%9 -= MI(bezier_C) * LO(f)*/
- A("lds %1, bezier_C+2") /* %1 = HI(bezier_C)*/
- A("mul %1,%5") /* r1:r0 = MI(bezier_C) * LO(f)*/
- A("sub %2,r0")
- A("sbc %3,r1")
- A("sbc %4,%0") /* %4:%3:%2:%9 -= HI(bezier_C) * LO(f) << 8*/
- A("mul %10,%6") /* r1:r0 = LO(bezier_C) * MI(f)*/
- A("sub %9,r0")
- A("sbc %2,r1")
- A("sbc %3,%0")
- A("sbc %4,%0") /* %4:%3:%2:%9 -= LO(bezier_C) * MI(f)*/
- A("mul %11,%6") /* r1:r0 = MI(bezier_C) * MI(f)*/
- A("sub %2,r0")
- A("sbc %3,r1")
- A("sbc %4,%0") /* %4:%3:%2:%9 -= MI(bezier_C) * MI(f) << 8*/
- A("mul %1,%6") /* r1:r0 = HI(bezier_C) * LO(f)*/
- A("sub %3,r0")
- A("sbc %4,r1") /* %4:%3:%2:%9 -= HI(bezier_C) * LO(f) << 16*/
+ A("lds %10, bezier_C") /* %10 = LO(bezier_C)*/
+ A("mul %10,%5") /* r1:r0 = LO(bezier_C) * LO(f)*/
+ A("sub %9,r1")
+ A("sbc %2,%0")
+ A("sbc %3,%0")
+ A("sbc %4,%0") /* %4:%3:%2:%9 -= HI(LO(bezier_C) * LO(f))*/
+ A("lds %11, bezier_C+1") /* %11 = MI(bezier_C)*/
+ A("mul %11,%5") /* r1:r0 = MI(bezier_C) * LO(f)*/
+ A("sub %9,r0")
+ A("sbc %2,r1")
+ A("sbc %3,%0")
+ A("sbc %4,%0") /* %4:%3:%2:%9 -= MI(bezier_C) * LO(f)*/
+ A("lds %1, bezier_C+2") /* %1 = HI(bezier_C)*/
+ A("mul %1,%5") /* r1:r0 = MI(bezier_C) * LO(f)*/
+ A("sub %2,r0")
+ A("sbc %3,r1")
+ A("sbc %4,%0") /* %4:%3:%2:%9 -= HI(bezier_C) * LO(f) << 8*/
+ A("mul %10,%6") /* r1:r0 = LO(bezier_C) * MI(f)*/
+ A("sub %9,r0")
+ A("sbc %2,r1")
+ A("sbc %3,%0")
+ A("sbc %4,%0") /* %4:%3:%2:%9 -= LO(bezier_C) * MI(f)*/
+ A("mul %11,%6") /* r1:r0 = MI(bezier_C) * MI(f)*/
+ A("sub %2,r0")
+ A("sbc %3,r1")
+ A("sbc %4,%0") /* %4:%3:%2:%9 -= MI(bezier_C) * MI(f) << 8*/
+ A("mul %1,%6") /* r1:r0 = HI(bezier_C) * LO(f)*/
+ A("sub %3,r0")
+ A("sbc %4,r1") /* %4:%3:%2:%9 -= HI(bezier_C) * LO(f) << 16*/
/* umul16x16to16hi(f, f, t); / Range 16 bits : f = t^3 (unsigned) [17]*/
- A("mul %5,%7") /* r1:r0 = LO(f) * LO(t)*/
- A("mov %1,r1") /* store MIL(LO(f) * LO(t)) in %1, we need it for rounding*/
- A("clr %10") /* %10 = 0*/
- A("clr %11") /* %11 = 0*/
- A("mul %5,%8") /* r1:r0 = LO(f) * HI(t)*/
- A("add %1,r0") /* %1 += LO(LO(f) * HI(t))*/
- A("adc %10,r1") /* %10 = HI(LO(f) * HI(t))*/
- A("adc %11,%0") /* %11 += carry*/
- A("mul %6,%7") /* r1:r0 = HI(f) * LO(t)*/
- A("add %1,r0") /* %1 += LO(HI(f) * LO(t))*/
- A("adc %10,r1") /* %10 += HI(HI(f) * LO(t))*/
- A("adc %11,%0") /* %11 += carry*/
- A("mul %6,%8") /* r1:r0 = HI(f) * HI(t)*/
- A("add %10,r0") /* %10 += LO(HI(f) * HI(t))*/
- A("adc %11,r1") /* %11 += HI(HI(f) * HI(t))*/
- A("mov %5,%10") /* %6:%5 =*/
- A("mov %6,%11") /* f = %10:%11*/
+ A("mul %5,%7") /* r1:r0 = LO(f) * LO(t)*/
+ A("mov %1,r1") /* store MIL(LO(f) * LO(t)) in %1, we need it for rounding*/
+ A("clr %10") /* %10 = 0*/
+ A("clr %11") /* %11 = 0*/
+ A("mul %5,%8") /* r1:r0 = LO(f) * HI(t)*/
+ A("add %1,r0") /* %1 += LO(LO(f) * HI(t))*/
+ A("adc %10,r1") /* %10 = HI(LO(f) * HI(t))*/
+ A("adc %11,%0") /* %11 += carry*/
+ A("mul %6,%7") /* r1:r0 = HI(f) * LO(t)*/
+ A("add %1,r0") /* %1 += LO(HI(f) * LO(t))*/
+ A("adc %10,r1") /* %10 += HI(HI(f) * LO(t))*/
+ A("adc %11,%0") /* %11 += carry*/
+ A("mul %6,%8") /* r1:r0 = HI(f) * HI(t)*/
+ A("add %10,r0") /* %10 += LO(HI(f) * HI(t))*/
+ A("adc %11,r1") /* %11 += HI(HI(f) * HI(t))*/
+ A("mov %5,%10") /* %6:%5 =*/
+ A("mov %6,%11") /* f = %10:%11*/
/* umul16x24to24hi(v, f, bezier_B); / Range 22bits [29]*/
/* acc += v; */
- A("lds %10, bezier_B") /* %10 = LO(bezier_B)*/
- A("mul %10,%5") /* r1:r0 = LO(bezier_B) * LO(f)*/
- A("add %9,r1")
- A("adc %2,%0")
- A("adc %3,%0")
- A("adc %4,%0") /* %4:%3:%2:%9 += HI(LO(bezier_B) * LO(f))*/
- A("lds %11, bezier_B+1") /* %11 = MI(bezier_B)*/
- A("mul %11,%5") /* r1:r0 = MI(bezier_B) * LO(f)*/
- A("add %9,r0")
- A("adc %2,r1")
- A("adc %3,%0")
- A("adc %4,%0") /* %4:%3:%2:%9 += MI(bezier_B) * LO(f)*/
- A("lds %1, bezier_B+2") /* %1 = HI(bezier_B)*/
- A("mul %1,%5") /* r1:r0 = MI(bezier_B) * LO(f)*/
- A("add %2,r0")
- A("adc %3,r1")
- A("adc %4,%0") /* %4:%3:%2:%9 += HI(bezier_B) * LO(f) << 8*/
- A("mul %10,%6") /* r1:r0 = LO(bezier_B) * MI(f)*/
- A("add %9,r0")
- A("adc %2,r1")
- A("adc %3,%0")
- A("adc %4,%0") /* %4:%3:%2:%9 += LO(bezier_B) * MI(f)*/
- A("mul %11,%6") /* r1:r0 = MI(bezier_B) * MI(f)*/
- A("add %2,r0")
- A("adc %3,r1")
- A("adc %4,%0") /* %4:%3:%2:%9 += MI(bezier_B) * MI(f) << 8*/
- A("mul %1,%6") /* r1:r0 = HI(bezier_B) * LO(f)*/
- A("add %3,r0")
- A("adc %4,r1") /* %4:%3:%2:%9 += HI(bezier_B) * LO(f) << 16*/
+ A("lds %10, bezier_B") /* %10 = LO(bezier_B)*/
+ A("mul %10,%5") /* r1:r0 = LO(bezier_B) * LO(f)*/
+ A("add %9,r1")
+ A("adc %2,%0")
+ A("adc %3,%0")
+ A("adc %4,%0") /* %4:%3:%2:%9 += HI(LO(bezier_B) * LO(f))*/
+ A("lds %11, bezier_B+1") /* %11 = MI(bezier_B)*/
+ A("mul %11,%5") /* r1:r0 = MI(bezier_B) * LO(f)*/
+ A("add %9,r0")
+ A("adc %2,r1")
+ A("adc %3,%0")
+ A("adc %4,%0") /* %4:%3:%2:%9 += MI(bezier_B) * LO(f)*/
+ A("lds %1, bezier_B+2") /* %1 = HI(bezier_B)*/
+ A("mul %1,%5") /* r1:r0 = MI(bezier_B) * LO(f)*/
+ A("add %2,r0")
+ A("adc %3,r1")
+ A("adc %4,%0") /* %4:%3:%2:%9 += HI(bezier_B) * LO(f) << 8*/
+ A("mul %10,%6") /* r1:r0 = LO(bezier_B) * MI(f)*/
+ A("add %9,r0")
+ A("adc %2,r1")
+ A("adc %3,%0")
+ A("adc %4,%0") /* %4:%3:%2:%9 += LO(bezier_B) * MI(f)*/
+ A("mul %11,%6") /* r1:r0 = MI(bezier_B) * MI(f)*/
+ A("add %2,r0")
+ A("adc %3,r1")
+ A("adc %4,%0") /* %4:%3:%2:%9 += MI(bezier_B) * MI(f) << 8*/
+ A("mul %1,%6") /* r1:r0 = HI(bezier_B) * LO(f)*/
+ A("add %3,r0")
+ A("adc %4,r1") /* %4:%3:%2:%9 += HI(bezier_B) * LO(f) << 16*/
/* umul16x16to16hi(f, f, t); / Range 16 bits : f = t^5 (unsigned) [17]*/
- A("mul %5,%7") /* r1:r0 = LO(f) * LO(t)*/
- A("mov %1,r1") /* store MIL(LO(f) * LO(t)) in %1, we need it for rounding*/
- A("clr %10") /* %10 = 0*/
- A("clr %11") /* %11 = 0*/
- A("mul %5,%8") /* r1:r0 = LO(f) * HI(t)*/
- A("add %1,r0") /* %1 += LO(LO(f) * HI(t))*/
- A("adc %10,r1") /* %10 = HI(LO(f) * HI(t))*/
- A("adc %11,%0") /* %11 += carry*/
- A("mul %6,%7") /* r1:r0 = HI(f) * LO(t)*/
- A("add %1,r0") /* %1 += LO(HI(f) * LO(t))*/
- A("adc %10,r1") /* %10 += HI(HI(f) * LO(t))*/
- A("adc %11,%0") /* %11 += carry*/
- A("mul %6,%8") /* r1:r0 = HI(f) * HI(t)*/
- A("add %10,r0") /* %10 += LO(HI(f) * HI(t))*/
- A("adc %11,r1") /* %11 += HI(HI(f) * HI(t))*/
- A("mov %5,%10") /* %6:%5 =*/
- A("mov %6,%11") /* f = %10:%11*/
+ A("mul %5,%7") /* r1:r0 = LO(f) * LO(t)*/
+ A("mov %1,r1") /* store MIL(LO(f) * LO(t)) in %1, we need it for rounding*/
+ A("clr %10") /* %10 = 0*/
+ A("clr %11") /* %11 = 0*/
+ A("mul %5,%8") /* r1:r0 = LO(f) * HI(t)*/
+ A("add %1,r0") /* %1 += LO(LO(f) * HI(t))*/
+ A("adc %10,r1") /* %10 = HI(LO(f) * HI(t))*/
+ A("adc %11,%0") /* %11 += carry*/
+ A("mul %6,%7") /* r1:r0 = HI(f) * LO(t)*/
+ A("add %1,r0") /* %1 += LO(HI(f) * LO(t))*/
+ A("adc %10,r1") /* %10 += HI(HI(f) * LO(t))*/
+ A("adc %11,%0") /* %11 += carry*/
+ A("mul %6,%8") /* r1:r0 = HI(f) * HI(t)*/
+ A("add %10,r0") /* %10 += LO(HI(f) * HI(t))*/
+ A("adc %11,r1") /* %11 += HI(HI(f) * HI(t))*/
+ A("mov %5,%10") /* %6:%5 =*/
+ A("mov %6,%11") /* f = %10:%11*/
/* umul16x24to24hi(v, f, bezier_A); / Range 21bits [29]*/
/* acc -= v; */
- A("lds %10, bezier_A") /* %10 = LO(bezier_A)*/
- A("mul %10,%5") /* r1:r0 = LO(bezier_A) * LO(f)*/
- A("sub %9,r1")
- A("sbc %2,%0")
- A("sbc %3,%0")
- A("sbc %4,%0") /* %4:%3:%2:%9 -= HI(LO(bezier_A) * LO(f))*/
- A("lds %11, bezier_A+1") /* %11 = MI(bezier_A)*/
- A("mul %11,%5") /* r1:r0 = MI(bezier_A) * LO(f)*/
- A("sub %9,r0")
- A("sbc %2,r1")
- A("sbc %3,%0")
- A("sbc %4,%0") /* %4:%3:%2:%9 -= MI(bezier_A) * LO(f)*/
- A("lds %1, bezier_A+2") /* %1 = HI(bezier_A)*/
- A("mul %1,%5") /* r1:r0 = MI(bezier_A) * LO(f)*/
- A("sub %2,r0")
- A("sbc %3,r1")
- A("sbc %4,%0") /* %4:%3:%2:%9 -= HI(bezier_A) * LO(f) << 8*/
- A("mul %10,%6") /* r1:r0 = LO(bezier_A) * MI(f)*/
- A("sub %9,r0")
- A("sbc %2,r1")
- A("sbc %3,%0")
- A("sbc %4,%0") /* %4:%3:%2:%9 -= LO(bezier_A) * MI(f)*/
- A("mul %11,%6") /* r1:r0 = MI(bezier_A) * MI(f)*/
- A("sub %2,r0")
- A("sbc %3,r1")
- A("sbc %4,%0") /* %4:%3:%2:%9 -= MI(bezier_A) * MI(f) << 8*/
- A("mul %1,%6") /* r1:r0 = HI(bezier_A) * LO(f)*/
- A("sub %3,r0")
- A("sbc %4,r1") /* %4:%3:%2:%9 -= HI(bezier_A) * LO(f) << 16*/
- A("jmp 2f") /* Done!*/
+ A("lds %10, bezier_A") /* %10 = LO(bezier_A)*/
+ A("mul %10,%5") /* r1:r0 = LO(bezier_A) * LO(f)*/
+ A("sub %9,r1")
+ A("sbc %2,%0")
+ A("sbc %3,%0")
+ A("sbc %4,%0") /* %4:%3:%2:%9 -= HI(LO(bezier_A) * LO(f))*/
+ A("lds %11, bezier_A+1") /* %11 = MI(bezier_A)*/
+ A("mul %11,%5") /* r1:r0 = MI(bezier_A) * LO(f)*/
+ A("sub %9,r0")
+ A("sbc %2,r1")
+ A("sbc %3,%0")
+ A("sbc %4,%0") /* %4:%3:%2:%9 -= MI(bezier_A) * LO(f)*/
+ A("lds %1, bezier_A+2") /* %1 = HI(bezier_A)*/
+ A("mul %1,%5") /* r1:r0 = MI(bezier_A) * LO(f)*/
+ A("sub %2,r0")
+ A("sbc %3,r1")
+ A("sbc %4,%0") /* %4:%3:%2:%9 -= HI(bezier_A) * LO(f) << 8*/
+ A("mul %10,%6") /* r1:r0 = LO(bezier_A) * MI(f)*/
+ A("sub %9,r0")
+ A("sbc %2,r1")
+ A("sbc %3,%0")
+ A("sbc %4,%0") /* %4:%3:%2:%9 -= LO(bezier_A) * MI(f)*/
+ A("mul %11,%6") /* r1:r0 = MI(bezier_A) * MI(f)*/
+ A("sub %2,r0")
+ A("sbc %3,r1")
+ A("sbc %4,%0") /* %4:%3:%2:%9 -= MI(bezier_A) * MI(f) << 8*/
+ A("mul %1,%6") /* r1:r0 = HI(bezier_A) * LO(f)*/
+ A("sub %3,r0")
+ A("sbc %4,r1") /* %4:%3:%2:%9 -= HI(bezier_A) * LO(f) << 16*/
+ A("jmp 2f") /* Done!*/
L("1")
/* uint24_t v; */
/* umul16x24to24hi(v, f, bezier_C); / Range 21bits [29]*/
/* acc += v; */
- A("lds %10, bezier_C") /* %10 = LO(bezier_C)*/
- A("mul %10,%5") /* r1:r0 = LO(bezier_C) * LO(f)*/
- A("add %9,r1")
- A("adc %2,%0")
- A("adc %3,%0")
- A("adc %4,%0") /* %4:%3:%2:%9 += HI(LO(bezier_C) * LO(f))*/
- A("lds %11, bezier_C+1") /* %11 = MI(bezier_C)*/
- A("mul %11,%5") /* r1:r0 = MI(bezier_C) * LO(f)*/
- A("add %9,r0")
- A("adc %2,r1")
- A("adc %3,%0")
- A("adc %4,%0") /* %4:%3:%2:%9 += MI(bezier_C) * LO(f)*/
- A("lds %1, bezier_C+2") /* %1 = HI(bezier_C)*/
- A("mul %1,%5") /* r1:r0 = MI(bezier_C) * LO(f)*/
- A("add %2,r0")
- A("adc %3,r1")
- A("adc %4,%0") /* %4:%3:%2:%9 += HI(bezier_C) * LO(f) << 8*/
- A("mul %10,%6") /* r1:r0 = LO(bezier_C) * MI(f)*/
- A("add %9,r0")
- A("adc %2,r1")
- A("adc %3,%0")
- A("adc %4,%0") /* %4:%3:%2:%9 += LO(bezier_C) * MI(f)*/
- A("mul %11,%6") /* r1:r0 = MI(bezier_C) * MI(f)*/
- A("add %2,r0")
- A("adc %3,r1")
- A("adc %4,%0") /* %4:%3:%2:%9 += MI(bezier_C) * MI(f) << 8*/
- A("mul %1,%6") /* r1:r0 = HI(bezier_C) * LO(f)*/
- A("add %3,r0")
- A("adc %4,r1") /* %4:%3:%2:%9 += HI(bezier_C) * LO(f) << 16*/
+ A("lds %10, bezier_C") /* %10 = LO(bezier_C)*/
+ A("mul %10,%5") /* r1:r0 = LO(bezier_C) * LO(f)*/
+ A("add %9,r1")
+ A("adc %2,%0")
+ A("adc %3,%0")
+ A("adc %4,%0") /* %4:%3:%2:%9 += HI(LO(bezier_C) * LO(f))*/
+ A("lds %11, bezier_C+1") /* %11 = MI(bezier_C)*/
+ A("mul %11,%5") /* r1:r0 = MI(bezier_C) * LO(f)*/
+ A("add %9,r0")
+ A("adc %2,r1")
+ A("adc %3,%0")
+ A("adc %4,%0") /* %4:%3:%2:%9 += MI(bezier_C) * LO(f)*/
+ A("lds %1, bezier_C+2") /* %1 = HI(bezier_C)*/
+ A("mul %1,%5") /* r1:r0 = MI(bezier_C) * LO(f)*/
+ A("add %2,r0")
+ A("adc %3,r1")
+ A("adc %4,%0") /* %4:%3:%2:%9 += HI(bezier_C) * LO(f) << 8*/
+ A("mul %10,%6") /* r1:r0 = LO(bezier_C) * MI(f)*/
+ A("add %9,r0")
+ A("adc %2,r1")
+ A("adc %3,%0")
+ A("adc %4,%0") /* %4:%3:%2:%9 += LO(bezier_C) * MI(f)*/
+ A("mul %11,%6") /* r1:r0 = MI(bezier_C) * MI(f)*/
+ A("add %2,r0")
+ A("adc %3,r1")
+ A("adc %4,%0") /* %4:%3:%2:%9 += MI(bezier_C) * MI(f) << 8*/
+ A("mul %1,%6") /* r1:r0 = HI(bezier_C) * LO(f)*/
+ A("add %3,r0")
+ A("adc %4,r1") /* %4:%3:%2:%9 += HI(bezier_C) * LO(f) << 16*/
/* umul16x16to16hi(f, f, t); / Range 16 bits : f = t^3 (unsigned) [17]*/
- A("mul %5,%7") /* r1:r0 = LO(f) * LO(t)*/
- A("mov %1,r1") /* store MIL(LO(f) * LO(t)) in %1, we need it for rounding*/
- A("clr %10") /* %10 = 0*/
- A("clr %11") /* %11 = 0*/
- A("mul %5,%8") /* r1:r0 = LO(f) * HI(t)*/
- A("add %1,r0") /* %1 += LO(LO(f) * HI(t))*/
- A("adc %10,r1") /* %10 = HI(LO(f) * HI(t))*/
- A("adc %11,%0") /* %11 += carry*/
- A("mul %6,%7") /* r1:r0 = HI(f) * LO(t)*/
- A("add %1,r0") /* %1 += LO(HI(f) * LO(t))*/
- A("adc %10,r1") /* %10 += HI(HI(f) * LO(t))*/
- A("adc %11,%0") /* %11 += carry*/
- A("mul %6,%8") /* r1:r0 = HI(f) * HI(t)*/
- A("add %10,r0") /* %10 += LO(HI(f) * HI(t))*/
- A("adc %11,r1") /* %11 += HI(HI(f) * HI(t))*/
- A("mov %5,%10") /* %6:%5 =*/
- A("mov %6,%11") /* f = %10:%11*/
+ A("mul %5,%7") /* r1:r0 = LO(f) * LO(t)*/
+ A("mov %1,r1") /* store MIL(LO(f) * LO(t)) in %1, we need it for rounding*/
+ A("clr %10") /* %10 = 0*/
+ A("clr %11") /* %11 = 0*/
+ A("mul %5,%8") /* r1:r0 = LO(f) * HI(t)*/
+ A("add %1,r0") /* %1 += LO(LO(f) * HI(t))*/
+ A("adc %10,r1") /* %10 = HI(LO(f) * HI(t))*/
+ A("adc %11,%0") /* %11 += carry*/
+ A("mul %6,%7") /* r1:r0 = HI(f) * LO(t)*/
+ A("add %1,r0") /* %1 += LO(HI(f) * LO(t))*/
+ A("adc %10,r1") /* %10 += HI(HI(f) * LO(t))*/
+ A("adc %11,%0") /* %11 += carry*/
+ A("mul %6,%8") /* r1:r0 = HI(f) * HI(t)*/
+ A("add %10,r0") /* %10 += LO(HI(f) * HI(t))*/
+ A("adc %11,r1") /* %11 += HI(HI(f) * HI(t))*/
+ A("mov %5,%10") /* %6:%5 =*/
+ A("mov %6,%11") /* f = %10:%11*/
/* umul16x24to24hi(v, f, bezier_B); / Range 22bits [29]*/
/* acc -= v;*/
- A("lds %10, bezier_B") /* %10 = LO(bezier_B)*/
- A("mul %10,%5") /* r1:r0 = LO(bezier_B) * LO(f)*/
- A("sub %9,r1")
- A("sbc %2,%0")
- A("sbc %3,%0")
- A("sbc %4,%0") /* %4:%3:%2:%9 -= HI(LO(bezier_B) * LO(f))*/
- A("lds %11, bezier_B+1") /* %11 = MI(bezier_B)*/
- A("mul %11,%5") /* r1:r0 = MI(bezier_B) * LO(f)*/
- A("sub %9,r0")
- A("sbc %2,r1")
- A("sbc %3,%0")
- A("sbc %4,%0") /* %4:%3:%2:%9 -= MI(bezier_B) * LO(f)*/
- A("lds %1, bezier_B+2") /* %1 = HI(bezier_B)*/
- A("mul %1,%5") /* r1:r0 = MI(bezier_B) * LO(f)*/
- A("sub %2,r0")
- A("sbc %3,r1")
- A("sbc %4,%0") /* %4:%3:%2:%9 -= HI(bezier_B) * LO(f) << 8*/
- A("mul %10,%6") /* r1:r0 = LO(bezier_B) * MI(f)*/
- A("sub %9,r0")
- A("sbc %2,r1")
- A("sbc %3,%0")
- A("sbc %4,%0") /* %4:%3:%2:%9 -= LO(bezier_B) * MI(f)*/
- A("mul %11,%6") /* r1:r0 = MI(bezier_B) * MI(f)*/
- A("sub %2,r0")
- A("sbc %3,r1")
- A("sbc %4,%0") /* %4:%3:%2:%9 -= MI(bezier_B) * MI(f) << 8*/
- A("mul %1,%6") /* r1:r0 = HI(bezier_B) * LO(f)*/
- A("sub %3,r0")
- A("sbc %4,r1") /* %4:%3:%2:%9 -= HI(bezier_B) * LO(f) << 16*/
+ A("lds %10, bezier_B") /* %10 = LO(bezier_B)*/
+ A("mul %10,%5") /* r1:r0 = LO(bezier_B) * LO(f)*/
+ A("sub %9,r1")
+ A("sbc %2,%0")
+ A("sbc %3,%0")
+ A("sbc %4,%0") /* %4:%3:%2:%9 -= HI(LO(bezier_B) * LO(f))*/
+ A("lds %11, bezier_B+1") /* %11 = MI(bezier_B)*/
+ A("mul %11,%5") /* r1:r0 = MI(bezier_B) * LO(f)*/
+ A("sub %9,r0")
+ A("sbc %2,r1")
+ A("sbc %3,%0")
+ A("sbc %4,%0") /* %4:%3:%2:%9 -= MI(bezier_B) * LO(f)*/
+ A("lds %1, bezier_B+2") /* %1 = HI(bezier_B)*/
+ A("mul %1,%5") /* r1:r0 = MI(bezier_B) * LO(f)*/
+ A("sub %2,r0")
+ A("sbc %3,r1")
+ A("sbc %4,%0") /* %4:%3:%2:%9 -= HI(bezier_B) * LO(f) << 8*/
+ A("mul %10,%6") /* r1:r0 = LO(bezier_B) * MI(f)*/
+ A("sub %9,r0")
+ A("sbc %2,r1")
+ A("sbc %3,%0")
+ A("sbc %4,%0") /* %4:%3:%2:%9 -= LO(bezier_B) * MI(f)*/
+ A("mul %11,%6") /* r1:r0 = MI(bezier_B) * MI(f)*/
+ A("sub %2,r0")
+ A("sbc %3,r1")
+ A("sbc %4,%0") /* %4:%3:%2:%9 -= MI(bezier_B) * MI(f) << 8*/
+ A("mul %1,%6") /* r1:r0 = HI(bezier_B) * LO(f)*/
+ A("sub %3,r0")
+ A("sbc %4,r1") /* %4:%3:%2:%9 -= HI(bezier_B) * LO(f) << 16*/
/* umul16x16to16hi(f, f, t); / Range 16 bits : f = t^5 (unsigned) [17]*/
- A("mul %5,%7") /* r1:r0 = LO(f) * LO(t)*/
- A("mov %1,r1") /* store MIL(LO(f) * LO(t)) in %1, we need it for rounding*/
- A("clr %10") /* %10 = 0*/
- A("clr %11") /* %11 = 0*/
- A("mul %5,%8") /* r1:r0 = LO(f) * HI(t)*/
- A("add %1,r0") /* %1 += LO(LO(f) * HI(t))*/
- A("adc %10,r1") /* %10 = HI(LO(f) * HI(t))*/
- A("adc %11,%0") /* %11 += carry*/
- A("mul %6,%7") /* r1:r0 = HI(f) * LO(t)*/
- A("add %1,r0") /* %1 += LO(HI(f) * LO(t))*/
- A("adc %10,r1") /* %10 += HI(HI(f) * LO(t))*/
- A("adc %11,%0") /* %11 += carry*/
- A("mul %6,%8") /* r1:r0 = HI(f) * HI(t)*/
- A("add %10,r0") /* %10 += LO(HI(f) * HI(t))*/
- A("adc %11,r1") /* %11 += HI(HI(f) * HI(t))*/
- A("mov %5,%10") /* %6:%5 =*/
- A("mov %6,%11") /* f = %10:%11*/
+ A("mul %5,%7") /* r1:r0 = LO(f) * LO(t)*/
+ A("mov %1,r1") /* store MIL(LO(f) * LO(t)) in %1, we need it for rounding*/
+ A("clr %10") /* %10 = 0*/
+ A("clr %11") /* %11 = 0*/
+ A("mul %5,%8") /* r1:r0 = LO(f) * HI(t)*/
+ A("add %1,r0") /* %1 += LO(LO(f) * HI(t))*/
+ A("adc %10,r1") /* %10 = HI(LO(f) * HI(t))*/
+ A("adc %11,%0") /* %11 += carry*/
+ A("mul %6,%7") /* r1:r0 = HI(f) * LO(t)*/
+ A("add %1,r0") /* %1 += LO(HI(f) * LO(t))*/
+ A("adc %10,r1") /* %10 += HI(HI(f) * LO(t))*/
+ A("adc %11,%0") /* %11 += carry*/
+ A("mul %6,%8") /* r1:r0 = HI(f) * HI(t)*/
+ A("add %10,r0") /* %10 += LO(HI(f) * HI(t))*/
+ A("adc %11,r1") /* %11 += HI(HI(f) * HI(t))*/
+ A("mov %5,%10") /* %6:%5 =*/
+ A("mov %6,%11") /* f = %10:%11*/
/* umul16x24to24hi(v, f, bezier_A); / Range 21bits [29]*/
/* acc += v; */
- A("lds %10, bezier_A") /* %10 = LO(bezier_A)*/
- A("mul %10,%5") /* r1:r0 = LO(bezier_A) * LO(f)*/
- A("add %9,r1")
- A("adc %2,%0")
- A("adc %3,%0")
- A("adc %4,%0") /* %4:%3:%2:%9 += HI(LO(bezier_A) * LO(f))*/
- A("lds %11, bezier_A+1") /* %11 = MI(bezier_A)*/
- A("mul %11,%5") /* r1:r0 = MI(bezier_A) * LO(f)*/
- A("add %9,r0")
- A("adc %2,r1")
- A("adc %3,%0")
- A("adc %4,%0") /* %4:%3:%2:%9 += MI(bezier_A) * LO(f)*/
- A("lds %1, bezier_A+2") /* %1 = HI(bezier_A)*/
- A("mul %1,%5") /* r1:r0 = MI(bezier_A) * LO(f)*/
- A("add %2,r0")
- A("adc %3,r1")
- A("adc %4,%0") /* %4:%3:%2:%9 += HI(bezier_A) * LO(f) << 8*/
- A("mul %10,%6") /* r1:r0 = LO(bezier_A) * MI(f)*/
- A("add %9,r0")
- A("adc %2,r1")
- A("adc %3,%0")
- A("adc %4,%0") /* %4:%3:%2:%9 += LO(bezier_A) * MI(f)*/
- A("mul %11,%6") /* r1:r0 = MI(bezier_A) * MI(f)*/
- A("add %2,r0")
- A("adc %3,r1")
- A("adc %4,%0") /* %4:%3:%2:%9 += MI(bezier_A) * MI(f) << 8*/
- A("mul %1,%6") /* r1:r0 = HI(bezier_A) * LO(f)*/
- A("add %3,r0")
- A("adc %4,r1") /* %4:%3:%2:%9 += HI(bezier_A) * LO(f) << 16*/
+ A("lds %10, bezier_A") /* %10 = LO(bezier_A)*/
+ A("mul %10,%5") /* r1:r0 = LO(bezier_A) * LO(f)*/
+ A("add %9,r1")
+ A("adc %2,%0")
+ A("adc %3,%0")
+ A("adc %4,%0") /* %4:%3:%2:%9 += HI(LO(bezier_A) * LO(f))*/
+ A("lds %11, bezier_A+1") /* %11 = MI(bezier_A)*/
+ A("mul %11,%5") /* r1:r0 = MI(bezier_A) * LO(f)*/
+ A("add %9,r0")
+ A("adc %2,r1")
+ A("adc %3,%0")
+ A("adc %4,%0") /* %4:%3:%2:%9 += MI(bezier_A) * LO(f)*/
+ A("lds %1, bezier_A+2") /* %1 = HI(bezier_A)*/
+ A("mul %1,%5") /* r1:r0 = MI(bezier_A) * LO(f)*/
+ A("add %2,r0")
+ A("adc %3,r1")
+ A("adc %4,%0") /* %4:%3:%2:%9 += HI(bezier_A) * LO(f) << 8*/
+ A("mul %10,%6") /* r1:r0 = LO(bezier_A) * MI(f)*/
+ A("add %9,r0")
+ A("adc %2,r1")
+ A("adc %3,%0")
+ A("adc %4,%0") /* %4:%3:%2:%9 += LO(bezier_A) * MI(f)*/
+ A("mul %11,%6") /* r1:r0 = MI(bezier_A) * MI(f)*/
+ A("add %2,r0")
+ A("adc %3,r1")
+ A("adc %4,%0") /* %4:%3:%2:%9 += MI(bezier_A) * MI(f) << 8*/
+ A("mul %1,%6") /* r1:r0 = HI(bezier_A) * LO(f)*/
+ A("add %3,r0")
+ A("adc %4,r1") /* %4:%3:%2:%9 += HI(bezier_A) * LO(f) << 16*/
L("2")
- " clr __zero_reg__" /* C runtime expects r1 = __zero_reg__ = 0 */
+ " clr __zero_reg__" /* C runtime expects r1 = __zero_reg__ = 0 */
: "+r"(r0),
"+r"(r1),
"+r"(r2),
@@ -1105,201 +1107,148 @@ void Stepper::set_directions() {
* 2000 1 KHz - sleep rate
* 4000 500 Hz - init rate
*/
-ISR(TIMER1_COMPA_vect) {
- /**
- * On AVR there is no hardware prioritization and preemption of
- * interrupts, so this emulates it. The UART has first priority
- * (otherwise, characters will be lost due to UART overflow).
- * Then: Stepper, Endstops, Temperature, and -finally- all others.
- *
- * This ISR needs to run with as little preemption as possible, so
- * the Temperature ISR is disabled here. Now only the UART, Endstops,
- * and Arduino-defined interrupts can preempt.
- */
- const bool temp_isr_was_enabled = TEMPERATURE_ISR_ENABLED();
- DISABLE_TEMPERATURE_INTERRUPT();
- DISABLE_STEPPER_DRIVER_INTERRUPT();
- sei();
- #if ENABLED(LIN_ADVANCE)
- Stepper::advance_isr_scheduler();
- #else
- Stepper::isr();
- #endif
+HAL_STEP_TIMER_ISR {
+ HAL_timer_isr_prologue(STEP_TIMER_NUM);
- // Disable global interrupts and reenable this ISR
- cli();
- ENABLE_STEPPER_DRIVER_INTERRUPT();
- // Reenable the temperature ISR (if it was enabled)
- if (temp_isr_was_enabled) ENABLE_TEMPERATURE_INTERRUPT();
+ // Program timer compare for the maximum period, so it does NOT
+ // flag an interrupt while this ISR is running - So changes from small
+ // periods to big periods are respected and the timer does not reset to 0
+ HAL_timer_set_compare(STEP_TIMER_NUM, HAL_TIMER_TYPE_MAX);
+
+ // Call the ISR scheduler
+ hal_timer_t ticks = Stepper::isr_scheduler();
+
+ // Now 'ticks' contains the period to the next Stepper ISR - And we are
+ // sure that the time has not arrived yet - Warrantied by the scheduler
+
+ // Set the next ISR to fire at the proper time
+ HAL_timer_set_compare(STEP_TIMER_NUM, ticks);
+
+ HAL_timer_isr_epilogue(STEP_TIMER_NUM);
}
-void Stepper::isr() {
+#define STEP_MULTIPLY(A,B) MultiU24X32toH16(A, B)
- uint16_t ocr_val;
+hal_timer_t Stepper::isr_scheduler() {
+ uint32_t interval;
- #define ENDSTOP_NOMINAL_OCR_VAL 3000 // Check endstops every 1.5ms to guarantee two stepper ISRs within 5ms for BLTouch
- #define OCR_VAL_TOLERANCE 1000 // First max delay is 2.0ms, last min delay is 0.5ms, all others 1.5ms
+ // Count of ticks for the next ISR
+ hal_timer_t next_isr_ticks = 0;
- #define _SPLIT(L) (ocr_val = (uint16_t)L)
- #if ENABLED(ENDSTOP_INTERRUPTS_FEATURE)
+ // Limit the amount of iterations
+ uint8_t max_loops = 10;
+
+ // We need this variable here to be able to use it in the following loop
+ hal_timer_t min_ticks;
+ do {
+ // Run main stepping pulse phase ISR if we have to
+ if (!nextMainISR) Stepper::stepper_pulse_phase_isr();
- #define SPLIT(L) _SPLIT(L)
+ #if ENABLED(LIN_ADVANCE)
+ // Run linear advance stepper ISR if we have to
+ if (!nextAdvanceISR) nextAdvanceISR = Stepper::advance_isr();
+ #endif
- #else // !ENDSTOP_INTERRUPTS_FEATURE : Sample endstops between stepping ISRs
+ // ^== Time critical. NOTHING besides pulse generation should be above here!!!
- static uint32_t step_remaining = 0;
+ // Run main stepping block processing ISR if we have to
+ if (!nextMainISR) nextMainISR = Stepper::stepper_block_phase_isr();
- #define SPLIT(L) do { \
- _SPLIT(L); \
- if (ENDSTOPS_ENABLED && L > ENDSTOP_NOMINAL_OCR_VAL) { \
- const uint16_t remainder = (uint16_t)L % (ENDSTOP_NOMINAL_OCR_VAL); \
- ocr_val = (remainder < OCR_VAL_TOLERANCE) ? ENDSTOP_NOMINAL_OCR_VAL + remainder : ENDSTOP_NOMINAL_OCR_VAL; \
- step_remaining = (uint16_t)L - ocr_val; \
- } \
- }while(0)
+ #if ENABLED(LIN_ADVANCE)
+ // Select the closest interval in time
+ interval = (nextAdvanceISR <= nextMainISR) ? nextAdvanceISR : nextMainISR;
+ #else
+ // The interval is just the remaining time to the stepper ISR
+ interval = nextMainISR;
+ #endif
- if (step_remaining && ENDSTOPS_ENABLED) { // Just check endstops - not yet time for a step
- endstops.update();
+ // Limit the value to the maximum possible value of the timer
+ NOMORE(interval, HAL_TIMER_TYPE_MAX);
- // Next ISR either for endstops or stepping
- ocr_val = step_remaining <= ENDSTOP_NOMINAL_OCR_VAL ? step_remaining : ENDSTOP_NOMINAL_OCR_VAL;
- step_remaining -= ocr_val;
- _NEXT_ISR(ocr_val);
- NOLESS(OCR1A, TCNT1 + 16);
- return;
- }
+ // Compute the time remaining for the main isr
+ nextMainISR -= interval;
- #endif // !ENDSTOP_INTERRUPTS_FEATURE
+ #if ENABLED(LIN_ADVANCE)
+ // Compute the time remaining for the advance isr
+ if (nextAdvanceISR != ADV_NEVER) nextAdvanceISR -= interval;
+ #endif
- //
- // When cleaning, discard the current block and run fast
- //
- if (cleaning_buffer_counter) {
- if (cleaning_buffer_counter < 0) { // Count up for endstop hit
- if (current_block) planner.discard_current_block(); // Discard the active block that led to the trigger
- if (!planner.discard_continued_block()) // Discard next CONTINUED block
- cleaning_buffer_counter = 0; // Keep discarding until non-CONTINUED
- }
- else {
+ /**
+ * This needs to avoid a race-condition caused by interleaving
+ * of interrupts required by both the LA and Stepper algorithms.
+ *
+ * Assume the following tick times for stepper pulses:
+ * Stepper ISR (S): 1 1000 2000 3000 4000
+ * Linear Adv. (E): 10 1010 2010 3010 4010
+ *
+ * The current algorithm tries to interleave them, giving:
+ * 1:S 10:E 1000:S 1010:E 2000:S 2010:E 3000:S 3010:E 4000:S 4010:E
+ *
+ * Ideal timing would yield these delta periods:
+ * 1:S 9:E 990:S 10:E 990:S 10:E 990:S 10:E 990:S 10:E
+ *
+ * But, since each event must fire an ISR with a minimum duration, the
+ * minimum delta might be 900, so deltas under 900 get rounded up:
+ * 900:S d900:E d990:S d900:E d990:S d900:E d990:S d900:E d990:S d900:E
+ *
+ * It works, but divides the speed of all motors by half, leading to a sudden
+ * reduction to 1/2 speed! Such jumps in speed lead to lost steps (not even
+ * accounting for double/quad stepping, which makes it even worse).
+ */
+
+ // Compute the tick count for the next ISR
+ next_isr_ticks += interval;
+
+ /**
+ * Get the current tick value + margin
+ * Assuming at least 6µs between calls to this ISR...
+ * On AVR the ISR epilogue is estimated at 40 instructions - close to 2.5µS.
+ * On ARM the ISR epilogue is estimated at 10 instructions - close to 200nS.
+ * In either case leave at least 8µS for other tasks to execute - That allows
+ * up to 100khz stepping rates
+ */
+ min_ticks = HAL_timer_get_count(STEP_TIMER_NUM) + hal_timer_t((HAL_TICKS_PER_US) * 8); // ISR never takes more than 1ms, so this shouldn't cause trouble
+
+ /**
+ * NB: If for some reason the stepper monopolizes the MPU, eventually the
+ * timer will wrap around (and so will 'next_isr_ticks'). So, limit the
+ * loop to 10 iterations. Beyond that, there's no way to ensure correct pulse
+ * timing, since the MCU isn't fast enough.
+ */
+ if (!--max_loops) next_isr_ticks = min_ticks;
+
+ // Advance pulses if not enough time to wait for the next ISR
+ } while (next_isr_ticks < min_ticks);
+
+ // Return the count of ticks for the next ISR
+ return (hal_timer_t)next_isr_ticks;
+}
+
+/**
+ * This phase of the ISR should ONLY create the pulses for the steppers.
+ * This prevents jitter caused by the interval between the start of the
+ * interrupt and the start of the pulses. DON'T add any logic ahead of the
+ * call to this method that might cause variation in the timing. The aim
+ * is to keep pulse timing as regular as possible.
+ */
+void Stepper::stepper_pulse_phase_isr() {
+
+ // If we must abort the current block, do so!
+ if (abort_current_block) {
+ abort_current_block = false;
+ if (current_block) {
+ axis_did_move = 0;
+ current_block = NULL;
planner.discard_current_block();
- --cleaning_buffer_counter; // Count down for abort print
- #if ENABLED(SD_FINISHED_STEPPERRELEASE) && defined(SD_FINISHED_RELEASECOMMAND)
- if (!cleaning_buffer_counter) enqueue_and_echo_commands_P(PSTR(SD_FINISHED_RELEASECOMMAND));
- #endif
- }
- current_block = NULL; // Prep to get a new block after cleaning
- _NEXT_ISR(200); // Run at max speed - 10 KHz
- return;
- }
-
- // If there is no current block, attempt to pop one from the buffer
- if (!current_block) {
-
- // Anything in the buffer?
- if ((current_block = planner.get_current_block())) {
-
- // Sync block? Sync the stepper counts and return
- while (TEST(current_block->flag, BLOCK_BIT_SYNC_POSITION)) {
- _set_position(
- current_block->steps[A_AXIS], current_block->steps[B_AXIS],
- current_block->steps[C_AXIS], current_block->steps[E_AXIS]
- );
- planner.discard_current_block();
- if (!(current_block = planner.get_current_block())) return;
- }
-
- // Initialize the trapezoid generator from the current block.
- static int8_t last_extruder = -1;
-
- #if ENABLED(LIN_ADVANCE)
- #if E_STEPPERS > 1
- if (current_block->active_extruder != last_extruder) {
- current_adv_steps = 0; // If the now active extruder wasn't in use during the last move, its pressure is most likely gone.
- LA_active_extruder = current_block->active_extruder;
- }
- #endif
-
- if ((use_advance_lead = current_block->use_advance_lead)) {
- LA_decelerate_after = current_block->decelerate_after;
- final_adv_steps = current_block->final_adv_steps;
- max_adv_steps = current_block->max_adv_steps;
- }
- #endif
-
- if (current_block->direction_bits != last_direction_bits || current_block->active_extruder != last_extruder) {
- last_direction_bits = current_block->direction_bits;
- last_extruder = current_block->active_extruder;
- set_directions();
- }
-
- // No acceleration / deceleration time elapsed so far
- acceleration_time = deceleration_time = 0;
-
- // No step events completed so far
- step_events_completed = 0;
-
- // step_rate to timer interval
- OCR1A_nominal = calc_timer_interval(current_block->nominal_rate);
-
- // make a note of the number of step loops required at nominal speed
- step_loops_nominal = step_loops;
-
- #if DISABLED(BEZIER_JERK_CONTROL)
- // Set as deceleration point the initial rate of the block
- acc_step_rate = current_block->initial_rate;
- #endif
-
- #if ENABLED(BEZIER_JERK_CONTROL)
- // Initialize the Bézier speed curve
- _calc_bezier_curve_coeffs(current_block->initial_rate, current_block->cruise_rate, current_block->acceleration_time_inverse);
-
- // We have not started the 2nd half of the trapezoid
- bezier_2nd_half = false;
- #endif
-
- // Initialize Bresenham counters to 1/2 the ceiling
- counter_X = counter_Y = counter_Z = counter_E = -(current_block->step_event_count >> 1);
- #if ENABLED(MIXING_EXTRUDER)
- MIXING_STEPPERS_LOOP(i)
- counter_m[i] = -(current_block->mix_event_count[i] >> 1);
- #endif
-
- // No step events completed so far
- step_events_completed = 0;
-
- #if ENABLED(ENDSTOP_INTERRUPTS_FEATURE)
- e_hit = 2; // Needed for the case an endstop is already triggered before the new move begins.
- // No 'change' can be detected.
- #endif
-
- #if ENABLED(Z_LATE_ENABLE)
- // If delayed Z enable, postpone move for 1mS
- if (current_block->steps[Z_AXIS] > 0) {
- enable_Z();
- _NEXT_ISR(2000); // Run at slow speed - 1 KHz
- return;
- }
- #endif
- }
- else {
- _NEXT_ISR(2000); // Run at slow speed - 1 KHz
- return;
}
}
- // Update endstops state, if enabled
- #if ENABLED(ENDSTOP_INTERRUPTS_FEATURE)
- if (e_hit && ENDSTOPS_ENABLED) {
- endstops.update();
- e_hit--;
- }
- #else
- if (ENDSTOPS_ENABLED) endstops.update();
- #endif
+ // If there is no current block, do nothing
+ if (!current_block) return;
// Take multiple steps per interrupt (For high speed moves)
- bool all_steps_done = false;
+ all_steps_done = false;
for (uint8_t i = step_loops; i--;) {
#define _COUNTER(AXIS) counter_## AXIS
@@ -1387,7 +1336,7 @@ void Stepper::isr() {
* 10µs = 160 or 200 cycles.
*/
#if EXTRA_CYCLES_XYZE > 20
- uint32_t pulse_start = TCNT0;
+ hal_timer_t pulse_start = HAL_timer_get_count(PULSE_TIMER_NUM);
#endif
#if HAS_X_STEP
@@ -1452,10 +1401,10 @@ void Stepper::isr() {
// For minimum pulse time wait before stopping pulses
#if EXTRA_CYCLES_XYZE > 20
- while (EXTRA_CYCLES_XYZE > (uint32_t)(TCNT0 - pulse_start) * (INT0_PRESCALER)) { /* nada */ }
- pulse_start = TCNT0;
+ while (EXTRA_CYCLES_XYZE > (uint32_t)(HAL_timer_get_count(PULSE_TIMER_NUM) - pulse_start) * (PULSE_TIMER_PRESCALE)) { /* nada */ }
+ pulse_start = HAL_timer_get_count(PULSE_TIMER_NUM);
#elif EXTRA_CYCLES_XYZE > 0
- DELAY_NOPS(EXTRA_CYCLES_XYZE);
+ DELAY_NS(EXTRA_CYCLES_XYZE * NANOSECONDS_PER_CYCLE);
#endif
#if HAS_X_STEP
@@ -1488,124 +1437,289 @@ void Stepper::isr() {
// For minimum pulse time wait after stopping pulses also
#if EXTRA_CYCLES_XYZE > 20
- if (i) while (EXTRA_CYCLES_XYZE > (uint32_t)(TCNT0 - pulse_start) * (INT0_PRESCALER)) { /* nada */ }
+ if (i) while (EXTRA_CYCLES_XYZE > (uint32_t)(HAL_timer_get_count(PULSE_TIMER_NUM) - pulse_start) * (PULSE_TIMER_PRESCALE)) { /* nada */ }
#elif EXTRA_CYCLES_XYZE > 0
- if (i) DELAY_NOPS(EXTRA_CYCLES_XYZE);
+ if (i) DELAY_NS(EXTRA_CYCLES_XYZE * NANOSECONDS_PER_CYCLE);
#endif
} // steps_loop
+}
- // Calculate new timer value
- if (step_events_completed <= (uint32_t)current_block->accelerate_until) {
+// This is the last half of the stepper interrupt: This one processes and
+// properly schedules blocks from the planner. This is executed after creating
+// the step pulses, so it is not time critical, as pulses are already done.
- #if ENABLED(BEZIER_JERK_CONTROL)
- // Get the next speed to use (Jerk limited!)
- uint16_t acc_step_rate =
- acceleration_time < current_block->acceleration_time
- ? _eval_bezier_curve(acceleration_time)
- : current_block->cruise_rate;
- #else
- MultiU24X32toH16(acc_step_rate, acceleration_time, current_block->acceleration_rate);
- acc_step_rate += current_block->initial_rate;
+uint32_t Stepper::stepper_block_phase_isr() {
- // upper limit
- NOMORE(acc_step_rate, current_block->nominal_rate);
- #endif
+ // If no queued movements, just wait 1ms for the next move
+ uint32_t interval = (HAL_STEPPER_TIMER_RATE / 1000);
- // step_rate to timer interval
- const uint16_t interval = calc_timer_interval(acc_step_rate);
+ // If there is a current block
+ if (current_block) {
- SPLIT(interval); // split step into multiple ISRs if larger than ENDSTOP_NOMINAL_OCR_VAL
- _NEXT_ISR(ocr_val);
+ // Calculate new timer value
+ if (step_events_completed <= current_block->accelerate_until) {
- acceleration_time += interval;
+ #if ENABLED(BEZIER_JERK_CONTROL)
+ // Get the next speed to use (Jerk limited!)
+ uint32_t acc_step_rate =
+ acceleration_time < current_block->acceleration_time
+ ? _eval_bezier_curve(acceleration_time)
+ : current_block->cruise_rate;
+ #else
+ acc_step_rate = STEP_MULTIPLY(acceleration_time, current_block->acceleration_rate) + current_block->initial_rate;
+ NOMORE(acc_step_rate, current_block->nominal_rate);
+ #endif
- #if ENABLED(LIN_ADVANCE)
- if (current_block->use_advance_lead) {
- if (step_events_completed == step_loops || (e_steps && eISR_Rate != current_block->advance_speed)) {
- nextAdvanceISR = 0; // Wake up eISR on first acceleration loop and fire ISR if final adv_rate is reached
- eISR_Rate = current_block->advance_speed;
+ // step_rate to timer interval
+ interval = calc_timer_interval(acc_step_rate);
+ acceleration_time += interval;
+
+ #if ENABLED(LIN_ADVANCE)
+ if (current_block->use_advance_lead) {
+ if (step_events_completed == step_loops || (e_steps && eISR_Rate != current_block->advance_speed)) {
+ nextAdvanceISR = 0; // Wake up eISR on first acceleration loop and fire ISR if final adv_rate is reached
+ eISR_Rate = current_block->advance_speed;
+ }
}
- }
- else {
- eISR_Rate = ADV_NEVER;
- if (e_steps) nextAdvanceISR = 0;
- }
- #endif // LIN_ADVANCE
+ else {
+ eISR_Rate = ADV_NEVER;
+ if (e_steps) nextAdvanceISR = 0;
+ }
+ #endif // LIN_ADVANCE
+ }
+ else if (step_events_completed > current_block->decelerate_after) {
+ uint32_t step_rate;
+
+ #if ENABLED(BEZIER_JERK_CONTROL)
+ // If this is the 1st time we process the 2nd half of the trapezoid...
+ if (!bezier_2nd_half) {
+ // Initialize the Bézier speed curve
+ _calc_bezier_curve_coeffs(current_block->cruise_rate, current_block->final_rate, current_block->deceleration_time_inverse);
+ bezier_2nd_half = true;
+ }
+
+ // Calculate the next speed to use
+ step_rate = deceleration_time < current_block->deceleration_time
+ ? _eval_bezier_curve(deceleration_time)
+ : current_block->final_rate;
+ #else
+
+ // Using the old trapezoidal control
+ step_rate = STEP_MULTIPLY(deceleration_time, current_block->acceleration_rate);
+ if (step_rate < acc_step_rate) { // Still decelerating?
+ step_rate = acc_step_rate - step_rate;
+ NOLESS(step_rate, current_block->final_rate);
+ }
+ else
+ step_rate = current_block->final_rate;
+ #endif
+
+ // step_rate to timer interval
+ interval = calc_timer_interval(step_rate);
+ deceleration_time += interval;
+
+ #if ENABLED(LIN_ADVANCE)
+ if (current_block->use_advance_lead) {
+ if (step_events_completed <= current_block->decelerate_after + step_loops || (e_steps && eISR_Rate != current_block->advance_speed)) {
+ nextAdvanceISR = 0; // Wake up eISR on first deceleration loop
+ eISR_Rate = current_block->advance_speed;
+ }
+ }
+ else {
+ eISR_Rate = ADV_NEVER;
+ if (e_steps) nextAdvanceISR = 0;
+ }
+ #endif // LIN_ADVANCE
+ }
+ else {
+
+ #if ENABLED(LIN_ADVANCE)
+ // If there are any esteps, fire the next advance_isr "now"
+ if (e_steps && eISR_Rate != current_block->advance_speed) nextAdvanceISR = 0;
+ #endif
+
+ // The timer interval is just the nominal value for the nominal speed
+ interval = ticks_nominal;
+
+ // Ensure this runs at the correct step rate, even if it just came off an acceleration
+ step_loops = step_loops_nominal;
+ }
+
+ // If current block is finished, reset pointer
+ if (all_steps_done) {
+ axis_did_move = 0;
+ current_block = NULL;
+ planner.discard_current_block();
+ }
}
- else if (step_events_completed > (uint32_t)current_block->decelerate_after) {
- uint16_t step_rate;
- #if ENABLED(BEZIER_JERK_CONTROL)
- // If this is the 1st time we process the 2nd half of the trapezoid...
- if (!bezier_2nd_half) {
+ // If there is no current block at this point, attempt to pop one from the buffer
+ // and prepare its movement
+ if (!current_block) {
+ // Anything in the buffer?
+ if ((current_block = planner.get_current_block())) {
+
+ // Sync block? Sync the stepper counts and return
+ while (TEST(current_block->flag, BLOCK_BIT_SYNC_POSITION)) {
+ _set_position(
+ current_block->position[A_AXIS], current_block->position[B_AXIS],
+ current_block->position[C_AXIS], current_block->position[E_AXIS]
+ );
+ planner.discard_current_block();
+
+ // Try to get a new block
+ if (!(current_block = planner.get_current_block()))
+ return interval; // No more queued movements!
+ }
+
+ // Flag all moving axes for proper endstop handling
+
+ #if IS_CORE
+ // Define conditions for checking endstops
+ #define S_(N) current_block->steps[CORE_AXIS_##N]
+ #define D_(N) TEST(current_block->direction_bits, CORE_AXIS_##N)
+ #endif
+
+ #if CORE_IS_XY || CORE_IS_XZ
+ /**
+ * Head direction in -X axis for CoreXY and CoreXZ bots.
+ *
+ * If steps differ, both axes are moving.
+ * If DeltaA == -DeltaB, the movement is only in the 2nd axis (Y or Z, handled below)
+ * If DeltaA == DeltaB, the movement is only in the 1st axis (X)
+ */
+ #if ENABLED(COREXY) || ENABLED(COREXZ)
+ #define X_CMP ==
+ #else
+ #define X_CMP !=
+ #endif
+ #define X_MOVE_TEST ( S_(1) != S_(2) || (S_(1) > 0 && D_(1) X_CMP D_(2)) )
+ #else
+ #define X_MOVE_TEST !!current_block->steps[A_AXIS]
+ #endif
+
+ #if CORE_IS_XY || CORE_IS_YZ
+ /**
+ * Head direction in -Y axis for CoreXY / CoreYZ bots.
+ *
+ * If steps differ, both axes are moving
+ * If DeltaA == DeltaB, the movement is only in the 1st axis (X or Y)
+ * If DeltaA == -DeltaB, the movement is only in the 2nd axis (Y or Z)
+ */
+ #if ENABLED(COREYX) || ENABLED(COREYZ)
+ #define Y_CMP ==
+ #else
+ #define Y_CMP !=
+ #endif
+ #define Y_MOVE_TEST ( S_(1) != S_(2) || (S_(1) > 0 && D_(1) Y_CMP D_(2)) )
+ #else
+ #define Y_MOVE_TEST !!current_block->steps[B_AXIS]
+ #endif
+
+ #if CORE_IS_XZ || CORE_IS_YZ
+ /**
+ * Head direction in -Z axis for CoreXZ or CoreYZ bots.
+ *
+ * If steps differ, both axes are moving
+ * If DeltaA == DeltaB, the movement is only in the 1st axis (X or Y, already handled above)
+ * If DeltaA == -DeltaB, the movement is only in the 2nd axis (Z)
+ */
+ #if ENABLED(COREZX) || ENABLED(COREZY)
+ #define Z_CMP ==
+ #else
+ #define Z_CMP !=
+ #endif
+ #define Z_MOVE_TEST ( S_(1) != S_(2) || (S_(1) > 0 && D_(1) Z_CMP D_(2)) )
+ #else
+ #define Z_MOVE_TEST !!current_block->steps[C_AXIS]
+ #endif
+
+ uint8_t axis_bits = 0;
+ if (X_MOVE_TEST) SBI(axis_bits, A_AXIS);
+ if (Y_MOVE_TEST) SBI(axis_bits, B_AXIS);
+ if (Z_MOVE_TEST) SBI(axis_bits, C_AXIS);
+ //if (!!current_block->steps[E_AXIS]) SBI(axis_bits, E_AXIS);
+ //if (!!current_block->steps[A_AXIS]) SBI(axis_bits, X_HEAD);
+ //if (!!current_block->steps[B_AXIS]) SBI(axis_bits, Y_HEAD);
+ //if (!!current_block->steps[C_AXIS]) SBI(axis_bits, Z_HEAD);
+ axis_did_move = axis_bits;
+
+ // Initialize the trapezoid generator from the current block.
+ #if ENABLED(LIN_ADVANCE)
+ #if E_STEPPERS > 1
+ if (current_block->active_extruder != last_movement_extruder) {
+ current_adv_steps = 0; // If the now active extruder wasn't in use during the last move, its pressure is most likely gone.
+ LA_active_extruder = current_block->active_extruder;
+ }
+ #endif
+
+ if ((use_advance_lead = current_block->use_advance_lead)) {
+ LA_decelerate_after = current_block->decelerate_after;
+ final_adv_steps = current_block->final_adv_steps;
+ max_adv_steps = current_block->max_adv_steps;
+ }
+ #endif
+
+ if (current_block->direction_bits != last_direction_bits || current_block->active_extruder != last_movement_extruder) {
+ last_direction_bits = current_block->direction_bits;
+ last_movement_extruder = current_block->active_extruder;
+ set_directions();
+ }
+
+ // At this point, we must ensure the movement about to execute isn't
+ // trying to force the head against a limit switch. If using interrupt-
+ // driven change detection, and already against a limit then no call to
+ // the endstop_triggered method will be done and the movement will be
+ // done against the endstop. So, check the limits here: If the movement
+ // is against the limits, the block will be marked as to be killed, and
+ // on the next call to this ISR, will be discarded.
+ endstops.check_possible_change();
+
+ // No acceleration / deceleration time elapsed so far
+ acceleration_time = deceleration_time = 0;
+
+ // No step events completed so far
+ step_events_completed = 0;
+
+ // step_rate to timer interval for the nominal speed
+ ticks_nominal = calc_timer_interval(current_block->nominal_rate);
+
+ // make a note of the number of step loops required at nominal speed
+ step_loops_nominal = step_loops;
+
+ #if DISABLED(BEZIER_JERK_CONTROL)
+ // Set as deceleration point the initial rate of the block
+ acc_step_rate = current_block->initial_rate;
+ #endif
+
+ #if ENABLED(BEZIER_JERK_CONTROL)
// Initialize the Bézier speed curve
- _calc_bezier_curve_coeffs(current_block->cruise_rate, current_block->final_rate, current_block->deceleration_time_inverse);
- bezier_2nd_half = true;
- }
+ _calc_bezier_curve_coeffs(current_block->initial_rate, current_block->cruise_rate, current_block->acceleration_time_inverse);
- // Calculate the next speed to use
- step_rate = deceleration_time < current_block->deceleration_time
- ? _eval_bezier_curve(deceleration_time)
- : current_block->final_rate;
- #else
+ // We have not started the 2nd half of the trapezoid
+ bezier_2nd_half = false;
+ #endif
- // Using the old trapezoidal control
- MultiU24X32toH16(step_rate, deceleration_time, current_block->acceleration_rate);
+ // Initialize Bresenham counters to 1/2 the ceiling
+ counter_X = counter_Y = counter_Z = counter_E = -((int32_t)(current_block->step_event_count >> 1));
+ #if ENABLED(MIXING_EXTRUDER)
+ MIXING_STEPPERS_LOOP(i)
+ counter_m[i] = -(current_block->mix_event_count[i] >> 1);
+ #endif
- if (step_rate < acc_step_rate) { // Still decelerating?
- step_rate = acc_step_rate - step_rate;
- NOLESS(step_rate, current_block->final_rate);
- }
- else
- step_rate = current_block->final_rate;
- #endif
-
- // step_rate to timer interval
- const uint16_t interval = calc_timer_interval(step_rate);
-
- SPLIT(interval); // split step into multiple ISRs if larger than ENDSTOP_NOMINAL_OCR_VAL
- _NEXT_ISR(ocr_val);
-
- deceleration_time += interval;
-
- #if ENABLED(LIN_ADVANCE)
- if (current_block->use_advance_lead) {
- if (step_events_completed <= (uint32_t)current_block->decelerate_after + step_loops || (e_steps && eISR_Rate != current_block->advance_speed)) {
- nextAdvanceISR = 0; // Wake up eISR on first deceleration loop
- eISR_Rate = current_block->advance_speed;
- }
- }
- else {
- eISR_Rate = ADV_NEVER;
- if (e_steps) nextAdvanceISR = 0;
- }
- #endif // LIN_ADVANCE
- }
- else {
-
- #if ENABLED(LIN_ADVANCE)
- // If we have esteps to execute, fire the next advance_isr "now"
- if (e_steps && eISR_Rate != current_block->advance_speed) nextAdvanceISR = 0;
- #endif
-
- SPLIT(OCR1A_nominal); // split step into multiple ISRs if larger than ENDSTOP_NOMINAL_OCR_VAL
- _NEXT_ISR(ocr_val);
-
- // ensure we're running at the correct step rate, even if we just came off an acceleration
- step_loops = step_loops_nominal;
+ #if ENABLED(Z_LATE_ENABLE)
+ // If delayed Z enable, enable it now. This option will severely interfere with
+ // timing between pulses when chaining motion between blocks, and it could lead
+ // to lost steps in both X and Y axis, so avoid using it unless strictly necessary!!
+ if (current_block->steps[Z_AXIS]) enable_Z();
+ #endif
+ }
}
- #if DISABLED(LIN_ADVANCE)
- NOLESS(OCR1A, TCNT1 + 16);
- #endif
-
- // If current block is finished, reset pointer
- if (all_steps_done) {
- current_block = NULL;
- planner.discard_current_block();
- }
+ // Return the interval to wait
+ return interval;
}
#if ENABLED(LIN_ADVANCE)
@@ -1614,20 +1728,59 @@ void Stepper::isr() {
#define EXTRA_CYCLES_E (STEP_PULSE_CYCLES - (CYCLES_EATEN_E))
// Timer interrupt for E. e_steps is set in the main routine;
-
- void Stepper::advance_isr() {
+ uint32_t Stepper::advance_isr() {
+ uint32_t interval;
#if ENABLED(MK2_MULTIPLEXER) // For SNMM even-numbered steppers are reversed
#define SET_E_STEP_DIR(INDEX) do{ if (e_steps) E0_DIR_WRITE(e_steps < 0 ? !INVERT_E## INDEX ##_DIR ^ TEST(INDEX, 0) : INVERT_E## INDEX ##_DIR ^ TEST(INDEX, 0)); }while(0)
#elif ENABLED(DUAL_X_CARRIAGE) || ENABLED(DUAL_NOZZLE_DUPLICATION_MODE)
#define SET_E_STEP_DIR(INDEX) do{ if (e_steps) { if (e_steps < 0) REV_E_DIR(); else NORM_E_DIR(); } }while(0)
+ #elif ENABLED(SWITCHING_EXTRUDER)
+ #if EXTRUDERS > 4
+ #define SET_E_STEP_DIR(INDEX) do{ if (e_steps) { switch (INDEX) { \
+ case 0: case 1: E0_DIR_WRITE(!INVERT_E0_DIR ^ TEST(INDEX, 0) ^ (e_steps < 0)); break; \
+ case 2: case 3: E1_DIR_WRITE(!INVERT_E1_DIR ^ TEST(INDEX, 0) ^ (e_steps < 0)); break; \
+ case 4: E2_DIR_WRITE(!INVERT_E2_DIR ^ TEST(INDEX, 0) ^ (e_steps < 0)); \
+ } } }while(0)
+ #elif EXTRUDERS > 2
+ #define SET_E_STEP_DIR(INDEX) do{ if (e_steps) { switch (INDEX) { \
+ case 0: case 1: E0_DIR_WRITE(!INVERT_E0_DIR ^ TEST(INDEX, 0) ^ (e_steps < 0)); break; \
+ case 2: case 3: E1_DIR_WRITE(!INVERT_E1_DIR ^ TEST(INDEX, 0) ^ (e_steps < 0)); break; \
+ } } }while(0)
+ #else
+ #define SET_E_STEP_DIR(INDEX) do{ if (e_steps) E0_DIR_WRITE(!INVERT_E0_DIR ^ TEST(INDEX, 0) ^ (e_steps < 0)); }while(0)
+ #endif
#else
- #define SET_E_STEP_DIR(INDEX) do{ if (e_steps) E## INDEX ##_DIR_WRITE(e_steps < 0 ? INVERT_E## INDEX ##_DIR : !INVERT_E## INDEX ##_DIR); }while(0)
+ #define SET_E_STEP_DIR(INDEX) do{ if (e_steps) E## INDEX ##_DIR_WRITE(!INVERT_E## INDEX ##_DIR ^ (e_steps < 0)); }while(0)
#endif
#if ENABLED(DUAL_X_CARRIAGE) || ENABLED(DUAL_NOZZLE_DUPLICATION_MODE)
#define START_E_PULSE(INDEX) do{ if (e_steps) E_STEP_WRITE(!INVERT_E_STEP_PIN); }while(0)
- #define STOP_E_PULSE(INDEX) do{ if (e_steps) { E_STEP_WRITE(INVERT_E_STEP_PIN); e_steps < 0 ? ++e_steps : --e_steps; } }while(0)
+ #define STOP_E_PULSE(INDEX) do{ if (e_steps) { e_steps < 0 ? ++e_steps : --e_steps; E_STEP_WRITE(INVERT_E_STEP_PIN); } }while(0)
+ #elif ENABLED(SWITCHING_EXTRUDER)
+ #if EXTRUDERS > 4
+ #define START_E_PULSE(INDEX) do{ if (e_steps) { switch (INDEX) { \
+ case 0: case 1: E0_DIR_WRITE(!INVERT_E_STEP_PIN); break; \
+ case 2: case 3: E1_DIR_WRITE(!INVERT_E_STEP_PIN); break; \
+ case 4: E2_DIR_WRITE(!INVERT_E_STEP_PIN); } \
+ } }while(0)
+ #define STOP_E_PULSE(INDEX) do{ if (e_steps) { \
+ e_steps < 0 ? ++e_steps : --e_steps; \
+ switch (INDEX) { \
+ case 0: case 1: E0_DIR_WRITE(INVERT_E_STEP_PIN); break; \
+ case 2: case 3: E1_DIR_WRITE(INVERT_E_STEP_PIN); break; \
+ case 4: E2_DIR_WRITE(INVERT_E_STEP_PIN); } \
+ } }while(0)
+ #elif EXTRUDERS > 2
+ #define START_E_PULSE(INDEX) do{ if (e_steps) { if (INDEX < 2) E0_DIR_WRITE(!INVERT_E_STEP_PIN); else E1_DIR_WRITE(!INVERT_E_STEP_PIN); } }while(0)
+ #define STOP_E_PULSE(INDEX) do{ if (e_steps) { \
+ e_steps < 0 ? ++e_steps : --e_steps; \
+ if (INDEX < 2) E0_DIR_WRITE(INVERT_E_STEP_PIN); else E1_DIR_WRITE(INVERT_E_STEP_PIN); \
+ } }while(0)
+ #else
+ #define START_E_PULSE(INDEX) do{ if (e_steps) E0_DIR_WRITE(!INVERT_E_STEP_PIN); }while(0)
+ #define STOP_E_PULSE(INDEX) do{ if (e_steps) { e_steps < 0 ? ++e_steps : --e_steps; E0_DIR_WRITE(INVERT_E_STEP_PIN); }while(0)
+ #endif
#else
#define START_E_PULSE(INDEX) do{ if (e_steps) E## INDEX ##_STEP_WRITE(!INVERT_E_STEP_PIN); }while(0)
#define STOP_E_PULSE(INDEX) do { if (e_steps) { e_steps < 0 ? ++e_steps : --e_steps; E## INDEX ##_STEP_WRITE(INVERT_E_STEP_PIN); } }while(0)
@@ -1637,21 +1790,21 @@ void Stepper::isr() {
if (step_events_completed > LA_decelerate_after && current_adv_steps > final_adv_steps) {
e_steps--;
current_adv_steps--;
- nextAdvanceISR = eISR_Rate;
+ interval = eISR_Rate;
}
else if (step_events_completed < LA_decelerate_after && current_adv_steps < max_adv_steps) {
//step_events_completed <= (uint32_t)current_block->accelerate_until) {
e_steps++;
current_adv_steps++;
- nextAdvanceISR = eISR_Rate;
+ interval = eISR_Rate;
}
else {
- nextAdvanceISR = ADV_NEVER;
+ interval = ADV_NEVER;
eISR_Rate = ADV_NEVER;
}
}
else
- nextAdvanceISR = ADV_NEVER;
+ interval = ADV_NEVER;
switch (LA_active_extruder) {
case 0: SET_E_STEP_DIR(0); break;
@@ -1673,7 +1826,7 @@ void Stepper::isr() {
while (e_steps) {
#if EXTRA_CYCLES_E > 20
- uint32_t pulse_start = TCNT0;
+ hal_timer_t pulse_start = HAL_timer_get_count(PULSE_TIMER_NUM);
#endif
switch (LA_active_extruder) {
@@ -1694,10 +1847,10 @@ void Stepper::isr() {
// For minimum pulse time wait before stopping pulses
#if EXTRA_CYCLES_E > 20
- while (EXTRA_CYCLES_E > (uint32_t)(TCNT0 - pulse_start) * (INT0_PRESCALER)) { /* nada */ }
- pulse_start = TCNT0;
+ while (EXTRA_CYCLES_E > (hal_timer_t)(HAL_timer_get_count(PULSE_TIMER_NUM) - pulse_start) * (PULSE_TIMER_PRESCALE)) { /* nada */ }
+ pulse_start = HAL_timer_get_count(PULSE_TIMER_NUM);
#elif EXTRA_CYCLES_E > 0
- DELAY_NOPS(EXTRA_CYCLES_E);
+ DELAY_NS(EXTRA_CYCLES_E * NANOSECONDS_PER_CYCLE);
#endif
switch (LA_active_extruder) {
@@ -1718,45 +1871,15 @@ void Stepper::isr() {
// For minimum pulse time wait before looping
#if EXTRA_CYCLES_E > 20
- if (e_steps) while (EXTRA_CYCLES_E > (uint32_t)(TCNT0 - pulse_start) * (INT0_PRESCALER)) { /* nada */ }
+ if (e_steps) while (EXTRA_CYCLES_E > (hal_timer_t)(HAL_timer_get_count(PULSE_TIMER_NUM) - pulse_start) * (PULSE_TIMER_PRESCALE)) { /* nada */ }
#elif EXTRA_CYCLES_E > 0
- if (e_steps) DELAY_NOPS(EXTRA_CYCLES_E);
+ if (e_steps) DELAY_NS(EXTRA_CYCLES_E * NANOSECONDS_PER_CYCLE);
#endif
} // e_steps
+
+ return interval;
}
-
- void Stepper::advance_isr_scheduler() {
-
- // Run main stepping ISR if flagged
- if (!nextMainISR) isr();
-
- // Run Advance stepping ISR if flagged
- if (!nextAdvanceISR) advance_isr();
-
- // Is the next advance ISR scheduled before the next main ISR?
- if (nextAdvanceISR <= nextMainISR) {
- // Set up the next interrupt
- OCR1A = nextAdvanceISR;
- // New interval for the next main ISR
- if (nextMainISR) nextMainISR -= nextAdvanceISR;
- // Will call Stepper::advance_isr on the next interrupt
- nextAdvanceISR = 0;
- }
- else {
- // The next main ISR comes first
- OCR1A = nextMainISR;
- // New interval for the next advance ISR, if any
- if (nextAdvanceISR && nextAdvanceISR != ADV_NEVER)
- nextAdvanceISR -= nextMainISR;
- // Will call Stepper::isr on the next interrupt
- nextMainISR = 0;
- }
-
- // Don't run the ISR faster than possible
- NOLESS(OCR1A, TCNT1 + 16);
- }
-
#endif // LIN_ADVANCE
void Stepper::init() {
@@ -1852,9 +1975,6 @@ void Stepper::init() {
if (!E_ENABLE_ON) E4_ENABLE_WRITE(HIGH);
#endif
- // Init endstops and pullups
- endstops.init();
-
#define _STEP_INIT(AXIS) AXIS ##_STEP_INIT
#define _WRITE_STEP(AXIS, HIGHLOW) AXIS ##_STEP_WRITE(HIGHLOW)
#define _DISABLE(AXIS) disable_## AXIS()
@@ -1932,12 +2052,6 @@ void Stepper::init() {
set_directions(); // Init directions to last_direction_bits = 0
}
-
-/**
- * Block until all buffered steps are executed / cleaned
- */
-void Stepper::synchronize() { while (planner.has_blocks_queued() || cleaning_buffer_counter) idle(); }
-
/**
* Set the stepper positions directly in steps
*
@@ -1977,57 +2091,29 @@ void Stepper::_set_position(const int32_t &a, const int32_t &b, const int32_t &c
* Get a stepper's position in steps.
*/
int32_t Stepper::position(const AxisEnum axis) {
- CRITICAL_SECTION_START;
- const int32_t count_pos = count_position[axis];
- CRITICAL_SECTION_END;
- return count_pos;
-}
-
-/**
- * Get an axis position according to stepper position(s)
- * For CORE machines apply translation from ABC to XYZ.
- */
-float Stepper::get_axis_position_mm(const AxisEnum axis) {
- float axis_steps;
- #if IS_CORE
- // Requesting one of the "core" axes?
- if (axis == CORE_AXIS_1 || axis == CORE_AXIS_2) {
- CRITICAL_SECTION_START;
- // ((a1+a2)+(a1-a2))/2 -> (a1+a2+a1-a2)/2 -> (a1+a1)/2 -> a1
- // ((a1+a2)-(a1-a2))/2 -> (a1+a2-a1+a2)/2 -> (a2+a2)/2 -> a2
- axis_steps = 0.5f * (
- axis == CORE_AXIS_2 ? CORESIGN(count_position[CORE_AXIS_1] - count_position[CORE_AXIS_2])
- : count_position[CORE_AXIS_1] + count_position[CORE_AXIS_2]
- );
- CRITICAL_SECTION_END;
- }
- else
- axis_steps = position(axis);
- #else
- axis_steps = position(axis);
- #endif
- return axis_steps * planner.steps_to_mm[axis];
-}
-
-void Stepper::finish_and_disable() {
- synchronize();
- disable_all_steppers();
-}
-
-void Stepper::quick_stop() {
- DISABLE_STEPPER_DRIVER_INTERRUPT();
- kill_current_block();
- current_block = NULL;
- cleaning_buffer_counter = 5000;
- planner.clear_block_buffer();
- ENABLE_STEPPER_DRIVER_INTERRUPT();
- #if ENABLED(ULTRA_LCD)
- planner.clear_block_buffer_runtime();
- #endif
+ // Protect the access to the position. Only required for AVR, as
+ // any 32bit CPU offers atomic access to 32bit variables
+ const bool was_enabled = STEPPER_ISR_ENABLED();
+ if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT();
+
+ const int32_t v = count_position[axis];
+
+ // Reenable Stepper ISR
+ if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT();
+ return v;
}
+// Signal endstops were triggered - This function can be called from
+// an ISR context (Temperature, Stepper or limits ISR), so we must
+// be very careful here. If the interrupt being preempted was the
+// Stepper ISR (this CAN happen with the endstop limits ISR) then
+// when the stepper ISR resumes, we must be very sure that the movement
+// is properly cancelled
void Stepper::endstop_triggered(const AxisEnum axis) {
+ const bool was_enabled = STEPPER_ISR_ENABLED();
+ if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT();
+
#if IS_CORE
endstops_trigsteps[axis] = 0.5f * (
@@ -2041,16 +2127,37 @@ void Stepper::endstop_triggered(const AxisEnum axis) {
#endif // !COREXY && !COREXZ && !COREYZ
- kill_current_block();
- cleaning_buffer_counter = -1; // Discard the rest of the move
+ // Discard the rest of the move if there is a current block
+ quick_stop();
+
+ if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT();
+}
+
+int32_t Stepper::triggered_position(const AxisEnum axis) {
+ // Protect the access to the position. Only required for AVR, as
+ // any 32bit CPU offers atomic access to 32bit variables
+ const bool was_enabled = STEPPER_ISR_ENABLED();
+ if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT();
+
+ const int32_t v = endstops_trigsteps[axis];
+
+ // Reenable Stepper ISR
+ if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT();
+
+ return v;
}
void Stepper::report_positions() {
- CRITICAL_SECTION_START;
+
+ // Protect the access to the position.
+ const bool was_enabled = STEPPER_ISR_ENABLED();
+ if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT();
+
const int32_t xpos = count_position[X_AXIS],
ypos = count_position[Y_AXIS],
zpos = count_position[Z_AXIS];
- CRITICAL_SECTION_END;
+
+ if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT();
#if CORE_IS_XY || CORE_IS_XZ || IS_DELTA || IS_SCARA
SERIAL_PROTOCOLPGM(MSG_COUNT_A);
@@ -2091,27 +2198,27 @@ void Stepper::report_positions() {
#define _APPLY_DIR(AXIS, INVERT) AXIS ##_APPLY_DIR(INVERT, true)
#if EXTRA_CYCLES_BABYSTEP > 20
- #define _SAVE_START const uint32_t pulse_start = TCNT0
- #define _PULSE_WAIT while (EXTRA_CYCLES_BABYSTEP > (uint32_t)(TCNT0 - pulse_start) * (INT0_PRESCALER)) { /* nada */ }
+ #define _SAVE_START const hal_timer_t pulse_start = HAL_timer_get_count(STEP_TIMER_NUM)
+ #define _PULSE_WAIT while (EXTRA_CYCLES_BABYSTEP > (uint32_t)(HAL_timer_get_count(STEP_TIMER_NUM) - pulse_start) * (PULSE_TIMER_PRESCALE)) { /* nada */ }
#else
#define _SAVE_START NOOP
#if EXTRA_CYCLES_BABYSTEP > 0
- #define _PULSE_WAIT DELAY_NOPS(EXTRA_CYCLES_BABYSTEP)
+ #define _PULSE_WAIT DELAY_NS(EXTRA_CYCLES_BABYSTEP * NANOSECONDS_PER_CYCLE)
#elif STEP_PULSE_CYCLES > 0
#define _PULSE_WAIT NOOP
#elif ENABLED(DELTA)
- #define _PULSE_WAIT delayMicroseconds(2);
+ #define _PULSE_WAIT DELAY_US(2);
#else
- #define _PULSE_WAIT delayMicroseconds(4);
+ #define _PULSE_WAIT DELAY_US(4);
#endif
#endif
#define BABYSTEP_AXIS(AXIS, INVERT, DIR) { \
const uint8_t old_dir = _READ_DIR(AXIS); \
_ENABLE(AXIS); \
- _SAVE_START; \
_APPLY_DIR(AXIS, _INVERT_DIR(AXIS)^DIR^INVERT); \
- _PULSE_WAIT; \
+ DELAY_NS(400); /* DRV8825 */ \
+ _SAVE_START; \
_APPLY_STEP(AXIS)(!_INVERT_STEP_PIN(AXIS), true); \
_PULSE_WAIT; \
_APPLY_STEP(AXIS)(_INVERT_STEP_PIN(AXIS), true); \
@@ -2182,6 +2289,8 @@ void Stepper::report_positions() {
Y_DIR_WRITE(INVERT_Y_DIR ^ z_direction);
Z_DIR_WRITE(INVERT_Z_DIR ^ z_direction);
+ DELAY_NS(400); // DRV8825
+
_SAVE_START;
X_STEP_WRITE(!INVERT_X_STEP_PIN);
diff --git a/Marlin/stepper.h b/Marlin/stepper.h
index 087b8593e4..5cded30ed0 100644
--- a/Marlin/stepper.h
+++ b/Marlin/stepper.h
@@ -52,35 +52,32 @@
class Stepper;
extern Stepper stepper;
-#define ENABLE_STEPPER_DRIVER_INTERRUPT() SBI(TIMSK1, OCIE1A)
-#define DISABLE_STEPPER_DRIVER_INTERRUPT() CBI(TIMSK1, OCIE1A)
-#define STEPPER_ISR_ENABLED() TEST(TIMSK1, OCIE1A)
-#define HAL_STEPPER_TIMER_RATE ((F_CPU) * 0.125)
-
// intRes = intIn1 * intIn2 >> 16
// uses:
// r26 to store 0
// r27 to store the byte 1 of the 24 bit result
-#define MultiU16X8toH16(intRes, charIn1, intIn2) \
- asm volatile ( \
- A("clr r26") \
- A("mul %A1, %B2") \
- A("movw %A0, r0") \
- A("mul %A1, %A2") \
- A("add %A0, r1") \
- A("adc %B0, r26") \
- A("lsr r0") \
- A("adc %A0, r26") \
- A("adc %B0, r26") \
- A("clr r1") \
- : \
- "=&r" (intRes) \
- : \
- "d" (charIn1), \
- "d" (intIn2) \
- : \
- "r26" \
- )
+static FORCE_INLINE uint16_t MultiU16X8toH16(uint8_t charIn1, uint16_t intIn2) {
+ register uint8_t tmp;
+ register uint16_t intRes;
+ __asm__ __volatile__ (
+ A("clr %[tmp]")
+ A("mul %[charIn1], %B[intIn2]")
+ A("movw %A[intRes], r0")
+ A("mul %[charIn1], %A[intIn2]")
+ A("add %A[intRes], r1")
+ A("adc %B[intRes], %[tmp]")
+ A("lsr r0")
+ A("adc %A[intRes], %[tmp]")
+ A("adc %B[intRes], %[tmp]")
+ A("clr r1")
+ : [intRes] "=&r" (intRes),
+ [tmp] "=&r" (tmp)
+ : [charIn1] "d" (charIn1),
+ [intIn2] "d" (intIn2)
+ : "cc"
+ );
+ return intRes;
+}
class Stepper {
@@ -88,10 +85,6 @@ class Stepper {
static block_t* current_block; // A pointer to the block currently being traced
- #if ENABLED(ABORT_ON_ENDSTOP_HIT_FEATURE_ENABLED)
- static bool abort_on_endstop_hit;
- #endif
-
#if ENABLED(X_DUAL_ENDSTOPS) || ENABLED(Y_DUAL_ENDSTOPS) || ENABLED(Z_DUAL_ENDSTOPS)
static bool performing_homing;
#endif
@@ -103,11 +96,12 @@ class Stepper {
static uint32_t motor_current_setting[3];
#endif
- static int16_t cleaning_buffer_counter;
-
private:
- static uint8_t last_direction_bits; // The next stepping-bits to be output
+ static uint8_t last_direction_bits, // The next stepping-bits to be output
+ last_movement_extruder, // Last movement extruder, as computed when the last movement was fetched from planner
+ axis_did_move; // Last Movement in the given direction is not null, as computed when the last movement was fetched from planner
+ static bool abort_current_block; // Signals to the stepper that current block should be aborted
#if ENABLED(X_DUAL_ENDSTOPS)
static bool locked_x_motor, locked_x2_motor;
@@ -121,7 +115,7 @@ class Stepper {
// Counter variables for the Bresenham line tracer
static int32_t counter_X, counter_Y, counter_Z, counter_E;
- static volatile uint32_t step_events_completed; // The number of step events executed in the current block
+ static uint32_t step_events_completed; // The number of step events executed in the current block
#if ENABLED(BEZIER_JERK_CONTROL)
static int32_t bezier_A, // A coefficient in Bézier speed curve
@@ -133,12 +127,14 @@ class Stepper {
bezier_2nd_half; // If Bézier curve has been initialized or not
#endif
+ static uint32_t nextMainISR; // time remaining for the next Step ISR
+ static bool all_steps_done; // all steps done
+
#if ENABLED(LIN_ADVANCE)
static uint32_t LA_decelerate_after; // Copy from current executed block. Needed because current_block is set to NULL "too early".
- static uint16_t nextMainISR, nextAdvanceISR, eISR_Rate, current_adv_steps,
- final_adv_steps, max_adv_steps; // Copy from current executed block. Needed because current_block is set to NULL "too early".
- #define _NEXT_ISR(T) nextMainISR = T
+ static uint32_t nextAdvanceISR, eISR_Rate;
+ static uint16_t current_adv_steps, final_adv_steps, max_adv_steps; // Copy from current executed block. Needed because current_block is set to NULL "too early".
static int8_t e_steps;
static bool use_advance_lead;
#if E_STEPPERS > 1
@@ -147,18 +143,14 @@ class Stepper {
static constexpr int8_t LA_active_extruder = 0;
#endif
- #else // !LIN_ADVANCE
+ #endif // LIN_ADVANCE
- #define _NEXT_ISR(T) OCR1A = T
-
- #endif // !LIN_ADVANCE
-
- static int32_t acceleration_time, deceleration_time;
+ static uint32_t acceleration_time, deceleration_time;
static uint8_t step_loops, step_loops_nominal;
- static uint16_t OCR1A_nominal;
+ static uint32_t ticks_nominal;
#if DISABLED(BEZIER_JERK_CONTROL)
- static uint16_t acc_step_rate; // needed for deceleration start point
+ static uint32_t acc_step_rate; // needed for deceleration start point
#endif
static volatile int32_t endstops_trigsteps[XYZ];
@@ -191,105 +183,53 @@ class Stepper {
//
Stepper() { };
- //
// Initialize stepper hardware
- //
static void init();
- //
// Interrupt Service Routines
- //
- static void isr();
+ // The ISR scheduler
+ static hal_timer_t isr_scheduler();
+
+ // The stepper pulse phase ISR
+ static void stepper_pulse_phase_isr();
+
+ // The stepper block processing phase ISR
+ static uint32_t stepper_block_phase_isr();
#if ENABLED(LIN_ADVANCE)
- static void advance_isr();
- static void advance_isr_scheduler();
+ // The Linear advance stepper ISR
+ static uint32_t advance_isr();
#endif
- //
- // Block until all buffered steps are executed
- //
- static void synchronize();
-
- //
- // Set the current position in steps
- //
- static void _set_position(const int32_t &a, const int32_t &b, const int32_t &c, const int32_t &e);
-
- FORCE_INLINE static void _set_position(const AxisEnum a, const int32_t &v) { count_position[a] = v; }
-
- FORCE_INLINE static void set_position(const int32_t &a, const int32_t &b, const int32_t &c, const int32_t &e) {
- synchronize();
- CRITICAL_SECTION_START;
- _set_position(a, b, c, e);
- CRITICAL_SECTION_END;
- }
-
- static void set_position(const AxisEnum a, const int32_t &v) {
- synchronize();
- CRITICAL_SECTION_START;
- count_position[a] = v;
- CRITICAL_SECTION_END;
- }
-
- FORCE_INLINE static void _set_e_position(const int32_t &e) { count_position[E_AXIS] = e; }
-
- static void set_e_position(const int32_t &e) {
- synchronize();
- CRITICAL_SECTION_START;
- count_position[E_AXIS] = e;
- CRITICAL_SECTION_END;
- }
-
- //
- // Set direction bits for all steppers
- //
- static void set_directions();
-
- //
// Get the position of a stepper, in steps
- //
static int32_t position(const AxisEnum axis);
- //
// Report the positions of the steppers, in steps
- //
static void report_positions();
- //
- // Get the position (mm) of an axis based on stepper position(s)
- //
- static float get_axis_position_mm(const AxisEnum axis);
-
- //
- // SCARA AB axes are in degrees, not mm
- //
- #if IS_SCARA
- FORCE_INLINE static float get_axis_position_degrees(const AxisEnum axis) { return get_axis_position_mm(axis); }
- #endif
-
- //
// The stepper subsystem goes to sleep when it runs out of things to execute. Call this
// to notify the subsystem that it is time to go to work.
- //
static void wake_up();
- //
- // Wait for moves to finish and disable all steppers
- //
- static void finish_and_disable();
+ // Quickly stop all steppers
+ FORCE_INLINE static void quick_stop() { abort_current_block = true; }
- //
- // Quickly stop all steppers and clear the blocks queue
- //
- static void quick_stop();
-
- //
// The direction of a single motor
- //
FORCE_INLINE static bool motor_direction(const AxisEnum axis) { return TEST(last_direction_bits, axis); }
+ // The last movement direction was not null on the specified axis. Note that motor direction is not necessarily the same.
+ FORCE_INLINE static bool axis_is_moving(const AxisEnum axis) { return TEST(axis_did_move, axis); }
+
+ // The extruder associated to the last movement
+ FORCE_INLINE static uint8_t movement_extruder() { return last_movement_extruder; }
+
+ // Handle a triggered endstop
+ static void endstop_triggered(const AxisEnum axis);
+
+ // Triggered position of an axis in steps
+ static int32_t triggered_position(const AxisEnum axis);
+
#if HAS_DIGIPOTSS || HAS_MOTOR_CURRENT_PWM
static void digitalPotWrite(const int16_t address, const int16_t value);
static void digipot_current(const uint8_t driver, const int16_t current);
@@ -321,32 +261,37 @@ class Stepper {
static void babystep(const AxisEnum axis, const bool direction); // perform a short step with a single stepper motor, outside of any convention
#endif
- static inline void kill_current_block() {
- step_events_completed = current_block->step_event_count;
- }
-
- //
- // Handle a triggered endstop
- //
- static void endstop_triggered(const AxisEnum axis);
-
- //
- // Triggered position of an axis in mm (not core-savvy)
- //
- FORCE_INLINE static float triggered_position_mm(const AxisEnum axis) {
- return endstops_trigsteps[axis] * planner.steps_to_mm[axis];
- }
-
#if HAS_MOTOR_CURRENT_PWM
static void refresh_motor_power();
#endif
+ // Set the current position in steps
+ inline static void set_position(const int32_t &a, const int32_t &b, const int32_t &c, const int32_t &e) {
+ planner.synchronize();
+ CRITICAL_SECTION_START;
+ _set_position(a, b, c, e);
+ CRITICAL_SECTION_END;
+ }
+
+ inline static void set_position(const AxisEnum a, const int32_t &v) {
+ planner.synchronize();
+ CRITICAL_SECTION_START;
+ count_position[a] = v;
+ CRITICAL_SECTION_END;
+ }
+
private:
- FORCE_INLINE static uint16_t calc_timer_interval(uint16_t step_rate) {
- uint16_t timer;
+ // Set the current position in steps
+ static void _set_position(const int32_t &a, const int32_t &b, const int32_t &c, const int32_t &e);
- NOMORE(step_rate, MAX_STEP_FREQUENCY);
+ // Set direction bits for all steppers
+ static void set_directions();
+
+ FORCE_INLINE static uint32_t calc_timer_interval(uint32_t step_rate) {
+ uint32_t timer;
+
+ NOMORE(step_rate, uint32_t(MAX_STEP_FREQUENCY));
if (step_rate > 20000) { // If steprate > 20kHz >> step 4 times
step_rate >>= 2;
@@ -360,26 +305,26 @@ class Stepper {
step_loops = 1;
}
- NOLESS(step_rate, F_CPU / 500000);
+ NOLESS(step_rate, uint32_t(F_CPU / 500000U));
step_rate -= F_CPU / 500000; // Correct for minimal speed
if (step_rate >= (8 * 256)) { // higher step rate
- unsigned short table_address = (unsigned short)&speed_lookuptable_fast[(unsigned char)(step_rate >> 8)][0];
- unsigned char tmp_step_rate = (step_rate & 0x00FF);
- unsigned short gain = (unsigned short)pgm_read_word_near(table_address + 2);
- MultiU16X8toH16(timer, tmp_step_rate, gain);
- timer = (unsigned short)pgm_read_word_near(table_address) - timer;
+ const uint8_t tmp_step_rate = (step_rate & 0x00FF);
+ const uint16_t table_address = (uint16_t)&speed_lookuptable_fast[(uint8_t)(step_rate >> 8)][0],
+ gain = (uint16_t)pgm_read_word_near(table_address + 2);
+ timer = MultiU16X8toH16(tmp_step_rate, gain);
+ timer = (uint16_t)pgm_read_word_near(table_address) - timer;
}
else { // lower step rates
- unsigned short table_address = (unsigned short)&speed_lookuptable_slow[0][0];
+ uint16_t table_address = (uint16_t)&speed_lookuptable_slow[0][0];
table_address += ((step_rate) >> 1) & 0xFFFC;
- timer = (unsigned short)pgm_read_word_near(table_address);
- timer -= (((unsigned short)pgm_read_word_near(table_address + 2) * (unsigned char)(step_rate & 0x0007)) >> 3);
+ timer = (uint16_t)pgm_read_word_near(table_address)
+ - (((uint16_t)pgm_read_word_near(table_address + 2) * (uint8_t)(step_rate & 0x0007)) >> 3);
}
if (timer < 100) { // (20kHz - this should never happen)
timer = 100;
- SERIAL_PROTOCOL(MSG_STEPPER_TOO_HIGH);
- SERIAL_PROTOCOLLN(step_rate);
+ SERIAL_ECHOLNPAIR(MSG_STEPPER_TOO_HIGH, step_rate);
}
+
return timer;
}
diff --git a/Marlin/stepper_indirection.cpp b/Marlin/stepper_indirection.cpp
index b5898cded1..9010470218 100644
--- a/Marlin/stepper_indirection.cpp
+++ b/Marlin/stepper_indirection.cpp
@@ -179,6 +179,10 @@
// Following values from Trinamic's spreadsheet with values for a NEMA17 (42BYGHW609)
// https://www.trinamic.com/products/integrated-circuits/details/tmc2130/
void tmc2130_init(TMC2130Stepper &st, const uint16_t mA, const uint16_t microsteps, const uint32_t thrs, const float spmm) {
+ #if DISABLED(STEALTHCHOP) || DISABLED(HYBRID_THRESHOLD)
+ UNUSED(thrs);
+ UNUSED(spmm);
+ #endif
st.begin();
st.setCurrent(mA, R_SENSE, HOLD_MULTIPLIER);
st.microsteps(microsteps);
@@ -196,9 +200,6 @@
st.stealthChop(1);
#if ENABLED(HYBRID_THRESHOLD)
st.stealth_max_speed(12650000UL*microsteps/(256*thrs*spmm));
- #else
- UNUSED(thrs);
- UNUSED(spmm);
#endif
#elif ENABLED(SENSORLESS_HOMING)
st.coolstep_min_speed(1024UL * 1024UL - 1UL);
diff --git a/Marlin/stepper_indirection.h b/Marlin/stepper_indirection.h
index 472b0884bf..34a2582d00 100644
--- a/Marlin/stepper_indirection.h
+++ b/Marlin/stepper_indirection.h
@@ -447,44 +447,48 @@ void reset_stepper_drivers(); // Called by settings.load / settings.reset
* Extruder indirection for the single E axis
*/
#if ENABLED(SWITCHING_EXTRUDER)
- #if EXTRUDERS == 2
+ #if EXTRUDERS > 4
+ #define E_STEP_WRITE(v) do{ if (current_block->active_extruder < 2) { E0_STEP_WRITE(v); } else if (current_block->active_extruder < 4) { E1_STEP_WRITE(v); } else { E2_STEP_WRITE(v); } }while(0)
+ #define NORM_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 1: E0_DIR_WRITE( INVERT_E0_DIR); break; case 2: E1_DIR_WRITE(!INVERT_E1_DIR); break; case 3: E1_DIR_WRITE( INVERT_E1_DIR); break; case 4: E2_DIR_WRITE(!INVERT_E2_DIR); } }while(0)
+ #define REV_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE( INVERT_E0_DIR); break; case 1: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 2: E1_DIR_WRITE( INVERT_E1_DIR); break; case 3: E1_DIR_WRITE(!INVERT_E1_DIR); break; case 4: E2_DIR_WRITE( INVERT_E2_DIR); } }while(0)
+ #elif EXTRUDERS > 3
+ #define E_STEP_WRITE(v) do{ if (current_block->active_extruder < 2) { E0_STEP_WRITE(v); } else { E1_STEP_WRITE(v); } }while(0)
+ #define NORM_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 1: E0_DIR_WRITE( INVERT_E0_DIR); break; case 2: E1_DIR_WRITE(!INVERT_E1_DIR); break; case 3: E1_DIR_WRITE( INVERT_E1_DIR); } }while(0)
+ #define REV_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE( INVERT_E0_DIR); break; case 1: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 2: E1_DIR_WRITE( INVERT_E1_DIR); break; case 3: E1_DIR_WRITE(!INVERT_E1_DIR); } }while(0)
+ #elif EXTRUDERS > 2
+ #define E_STEP_WRITE(v) do{ if (current_block->active_extruder < 2) { E0_STEP_WRITE(v); } else { E1_STEP_WRITE(v); } }while(0)
+ #define NORM_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 1: E0_DIR_WRITE( INVERT_E0_DIR); break; case 2: E1_DIR_WRITE(!INVERT_E1_DIR); } }while(0)
+ #define REV_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE( INVERT_E0_DIR); break; case 1: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 2: E1_DIR_WRITE( INVERT_E1_DIR); } }while(0)
+ #else
#define E_STEP_WRITE(v) E0_STEP_WRITE(v)
#define NORM_E_DIR() do{ E0_DIR_WRITE(current_block->active_extruder ? INVERT_E0_DIR : !INVERT_E0_DIR); }while(0)
- #define REV_E_DIR() do{ E0_DIR_WRITE(current_block->active_extruder ? !INVERT_E0_DIR : INVERT_E0_DIR); }while(0)
- #elif EXTRUDERS > 4
- #define E_STEP_WRITE(v) do{ if (current_block->active_extruder < 2) { E0_STEP_WRITE(v); } else if (current_block->active_extruder < 4) { E1_STEP_WRITE(v); } else { E2_STEP_WRITE(v); } }while(0)
- #define NORM_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 1: E0_DIR_WRITE(INVERT_E0_DIR); break; case 2: E1_DIR_WRITE(!INVERT_E1_DIR); break; case 3: E1_DIR_WRITE(INVERT_E1_DIR); break; case 4: E2_DIR_WRITE(!INVERT_E2_DIR); } }while(0)
- #define REV_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE(INVERT_E0_DIR); break; case 1: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 2: E1_DIR_WRITE(INVERT_E1_DIR); break; case 3: E1_DIR_WRITE(!INVERT_E1_DIR); break; case 4: E2_DIR_WRITE(INVERT_E2_DIR); } }while(0)
- #elif EXTRUDERS > 2
- #define E_STEP_WRITE(v) do{ if (current_block->active_extruder < 2) { E0_STEP_WRITE(v); } else if (current_block->active_extruder < 4) { E1_STEP_WRITE(v); } else { E1_STEP_WRITE(v); } }while(0)
- #define NORM_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 1: E0_DIR_WRITE(INVERT_E0_DIR); break; case 2: E1_DIR_WRITE(!INVERT_E1_DIR); break; case 3: E1_DIR_WRITE(INVERT_E1_DIR); } }while(0)
- #define REV_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE(INVERT_E0_DIR); break; case 1: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 2: E1_DIR_WRITE(INVERT_E1_DIR); break; case 3: E1_DIR_WRITE(!INVERT_E1_DIR); } }while(0)
+ #define REV_E_DIR() do{ E0_DIR_WRITE(current_block->active_extruder ? !INVERT_E0_DIR : INVERT_E0_DIR); }while(0)
#endif
#elif ENABLED(MK2_MULTIPLEXER) // Even-numbered steppers are reversed
#define E_STEP_WRITE(v) E0_STEP_WRITE(v)
#define NORM_E_DIR() do{ E0_DIR_WRITE(TEST(current_block->active_extruder, 0) ? !INVERT_E0_DIR: INVERT_E0_DIR); }while(0)
- #define REV_E_DIR() do{ E0_DIR_WRITE(TEST(current_block->active_extruder, 0) ? INVERT_E0_DIR: !INVERT_E0_DIR); }while(0)
+ #define REV_E_DIR() do{ E0_DIR_WRITE(TEST(current_block->active_extruder, 0) ? INVERT_E0_DIR: !INVERT_E0_DIR); }while(0)
#elif EXTRUDERS > 4
#define E_STEP_WRITE(v) do{ switch (current_block->active_extruder) { case 0: E0_STEP_WRITE(v); break; case 1: E1_STEP_WRITE(v); break; case 2: E2_STEP_WRITE(v); break; case 3: E3_STEP_WRITE(v); break; case 4: E4_STEP_WRITE(v); } }while(0)
#define NORM_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 1: E1_DIR_WRITE(!INVERT_E1_DIR); break; case 2: E2_DIR_WRITE(!INVERT_E2_DIR); break; case 3: E3_DIR_WRITE(!INVERT_E3_DIR); break; case 4: E4_DIR_WRITE(!INVERT_E4_DIR); } }while(0)
- #define REV_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE(INVERT_E0_DIR); break; case 1: E1_DIR_WRITE(INVERT_E1_DIR); break; case 2: E2_DIR_WRITE(INVERT_E2_DIR); break; case 3: E3_DIR_WRITE(INVERT_E3_DIR); break; case 4: E4_DIR_WRITE(INVERT_E4_DIR); } }while(0)
+ #define REV_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE( INVERT_E0_DIR); break; case 1: E1_DIR_WRITE( INVERT_E1_DIR); break; case 2: E2_DIR_WRITE( INVERT_E2_DIR); break; case 3: E3_DIR_WRITE( INVERT_E3_DIR); break; case 4: E4_DIR_WRITE( INVERT_E4_DIR); } }while(0)
#elif EXTRUDERS > 3
#define E_STEP_WRITE(v) do{ switch (current_block->active_extruder) { case 0: E0_STEP_WRITE(v); break; case 1: E1_STEP_WRITE(v); break; case 2: E2_STEP_WRITE(v); break; case 3: E3_STEP_WRITE(v); } }while(0)
#define NORM_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 1: E1_DIR_WRITE(!INVERT_E1_DIR); break; case 2: E2_DIR_WRITE(!INVERT_E2_DIR); break; case 3: E3_DIR_WRITE(!INVERT_E3_DIR); } }while(0)
- #define REV_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE(INVERT_E0_DIR); break; case 1: E1_DIR_WRITE(INVERT_E1_DIR); break; case 2: E2_DIR_WRITE(INVERT_E2_DIR); break; case 3: E3_DIR_WRITE(INVERT_E3_DIR); } }while(0)
+ #define REV_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE( INVERT_E0_DIR); break; case 1: E1_DIR_WRITE( INVERT_E1_DIR); break; case 2: E2_DIR_WRITE( INVERT_E2_DIR); break; case 3: E3_DIR_WRITE( INVERT_E3_DIR); } }while(0)
#elif EXTRUDERS > 2
#define E_STEP_WRITE(v) do{ switch (current_block->active_extruder) { case 0: E0_STEP_WRITE(v); break; case 1: E1_STEP_WRITE(v); break; case 2: E2_STEP_WRITE(v); } }while(0)
#define NORM_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE(!INVERT_E0_DIR); break; case 1: E1_DIR_WRITE(!INVERT_E1_DIR); break; case 2: E2_DIR_WRITE(!INVERT_E2_DIR); } }while(0)
- #define REV_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE(INVERT_E0_DIR); break; case 1: E1_DIR_WRITE(INVERT_E1_DIR); break; case 2: E2_DIR_WRITE(INVERT_E2_DIR); } }while(0)
+ #define REV_E_DIR() do{ switch (current_block->active_extruder) { case 0: E0_DIR_WRITE( INVERT_E0_DIR); break; case 1: E1_DIR_WRITE( INVERT_E1_DIR); break; case 2: E2_DIR_WRITE( INVERT_E2_DIR); } }while(0)
#elif EXTRUDERS > 1
#if ENABLED(DUAL_X_CARRIAGE) || ENABLED(DUAL_NOZZLE_DUPLICATION_MODE)
#define E_STEP_WRITE(v) do{ if (extruder_duplication_enabled) { E0_STEP_WRITE(v); E1_STEP_WRITE(v); } else if (current_block->active_extruder == 0) { E0_STEP_WRITE(v); } else { E1_STEP_WRITE(v); } }while(0)
#define NORM_E_DIR() do{ if (extruder_duplication_enabled) { E0_DIR_WRITE(!INVERT_E0_DIR); E1_DIR_WRITE(!INVERT_E1_DIR); } else if (current_block->active_extruder == 0) { E0_DIR_WRITE(!INVERT_E0_DIR); } else { E1_DIR_WRITE(!INVERT_E1_DIR); } }while(0)
- #define REV_E_DIR() do{ if (extruder_duplication_enabled) { E0_DIR_WRITE(INVERT_E0_DIR); E1_DIR_WRITE(INVERT_E1_DIR); } else if (current_block->active_extruder == 0) { E0_DIR_WRITE(INVERT_E0_DIR); } else { E1_DIR_WRITE(INVERT_E1_DIR); } }while(0)
+ #define REV_E_DIR() do{ if (extruder_duplication_enabled) { E0_DIR_WRITE( INVERT_E0_DIR); E1_DIR_WRITE( INVERT_E1_DIR); } else if (current_block->active_extruder == 0) { E0_DIR_WRITE( INVERT_E0_DIR); } else { E1_DIR_WRITE( INVERT_E1_DIR); } }while(0)
#else
#define E_STEP_WRITE(v) do{ if (current_block->active_extruder == 0) { E0_STEP_WRITE(v); } else { E1_STEP_WRITE(v); } }while(0)
#define NORM_E_DIR() do{ if (current_block->active_extruder == 0) { E0_DIR_WRITE(!INVERT_E0_DIR); } else { E1_DIR_WRITE(!INVERT_E1_DIR); } }while(0)
- #define REV_E_DIR() do{ if (current_block->active_extruder == 0) { E0_DIR_WRITE(INVERT_E0_DIR); } else { E1_DIR_WRITE(INVERT_E1_DIR); } }while(0)
+ #define REV_E_DIR() do{ if (current_block->active_extruder == 0) { E0_DIR_WRITE( INVERT_E0_DIR); } else { E1_DIR_WRITE( INVERT_E1_DIR); } }while(0)
#endif
#elif ENABLED(MIXING_EXTRUDER)
#define E_STEP_WRITE(v) NOOP /* not used for mixing extruders! */
@@ -508,7 +512,7 @@ void reset_stepper_drivers(); // Called by settings.load / settings.reset
#else
#define E_STEP_WRITE(v) E0_STEP_WRITE(v)
#define NORM_E_DIR() E0_DIR_WRITE(!INVERT_E0_DIR)
- #define REV_E_DIR() E0_DIR_WRITE(INVERT_E0_DIR)
+ #define REV_E_DIR() E0_DIR_WRITE( INVERT_E0_DIR)
#endif
#endif // STEPPER_INDIRECTION_H
diff --git a/Marlin/temperature.cpp b/Marlin/temperature.cpp
index cd6fc054aa..814cdeea11 100644
--- a/Marlin/temperature.cpp
+++ b/Marlin/temperature.cpp
@@ -31,6 +31,8 @@
#include "planner.h"
#include "language.h"
#include "printcounter.h"
+#include "delay.h"
+#include "endstops.h"
#if ENABLED(HEATER_0_USES_MAX6675)
#include "MarlinSPI.h"
@@ -40,10 +42,6 @@
#include "stepper.h"
#endif
-#if ENABLED(ENDSTOP_INTERRUPTS_FEATURE)
- #include "endstops.h"
-#endif
-
#if ENABLED(USE_WATCHDOG)
#include "watchdog.h"
#endif
@@ -52,12 +50,14 @@
#include "emergency_parser.h"
#endif
-#if ENABLED(TEMP_SENSOR_1_AS_REDUNDANT)
- static void* heater_ttbl_map[2] = { (void*)HEATER_0_TEMPTABLE, (void*)HEATER_1_TEMPTABLE };
- static uint8_t heater_ttbllen_map[2] = { HEATER_0_TEMPTABLE_LEN, HEATER_1_TEMPTABLE_LEN };
-#else
- static void* heater_ttbl_map[HOTENDS] = ARRAY_BY_HOTENDS((void*)HEATER_0_TEMPTABLE, (void*)HEATER_1_TEMPTABLE, (void*)HEATER_2_TEMPTABLE, (void*)HEATER_3_TEMPTABLE, (void*)HEATER_4_TEMPTABLE);
- static uint8_t heater_ttbllen_map[HOTENDS] = ARRAY_BY_HOTENDS(HEATER_0_TEMPTABLE_LEN, HEATER_1_TEMPTABLE_LEN, HEATER_2_TEMPTABLE_LEN, HEATER_3_TEMPTABLE_LEN, HEATER_4_TEMPTABLE_LEN);
+#if HOTEND_USES_THERMISTOR
+ #if ENABLED(TEMP_SENSOR_1_AS_REDUNDANT)
+ static void* heater_ttbl_map[2] = { (void*)HEATER_0_TEMPTABLE, (void*)HEATER_1_TEMPTABLE };
+ static uint8_t heater_ttbllen_map[2] = { HEATER_0_TEMPTABLE_LEN, HEATER_1_TEMPTABLE_LEN };
+ #else
+ static void* heater_ttbl_map[HOTENDS] = ARRAY_BY_HOTENDS((void*)HEATER_0_TEMPTABLE, (void*)HEATER_1_TEMPTABLE, (void*)HEATER_2_TEMPTABLE, (void*)HEATER_3_TEMPTABLE, (void*)HEATER_4_TEMPTABLE);
+ static uint8_t heater_ttbllen_map[HOTENDS] = ARRAY_BY_HOTENDS(HEATER_0_TEMPTABLE_LEN, HEATER_1_TEMPTABLE_LEN, HEATER_2_TEMPTABLE_LEN, HEATER_3_TEMPTABLE_LEN, HEATER_4_TEMPTABLE_LEN);
+ #endif
#endif
Temperature thermalManager;
@@ -235,6 +235,10 @@ uint8_t Temperature::soft_pwm_amount[HOTENDS];
uint8_t Temperature::ADCKey_count = 0;
#endif
+#if ENABLED(PID_EXTRUSION_SCALING)
+ int16_t Temperature::lpq_len; // Initialized in configuration_store
+#endif
+
#if HAS_PID_HEATING
/**
@@ -664,14 +668,14 @@ float Temperature::get_pid_output(const int8_t e) {
#if ENABLED(PID_EXTRUSION_SCALING)
cTerm[HOTEND_INDEX] = 0;
if (_HOTEND_TEST) {
- long e_position = stepper.position(E_AXIS);
+ const long e_position = stepper.position(E_AXIS);
if (e_position > last_e_position) {
lpq[lpq_ptr] = e_position - last_e_position;
last_e_position = e_position;
}
- else {
+ else
lpq[lpq_ptr] = 0;
- }
+
if (++lpq_ptr >= lpq_len) lpq_ptr = 0;
cTerm[HOTEND_INDEX] = (lpq[lpq_ptr] * planner.steps_to_mm[E_AXIS]) * PID_PARAM(Kc, HOTEND_INDEX);
pid_output += cTerm[HOTEND_INDEX];
@@ -785,8 +789,8 @@ void Temperature::manage_heater() {
updateTemperaturesFromRawValues(); // also resets the watchdog
#if ENABLED(HEATER_0_USES_MAX6675)
- if (current_temperature[0] > min(HEATER_0_MAXTEMP, MAX6675_TMAX - 1.0)) max_temp_error(0);
- if (current_temperature[0] < max(HEATER_0_MINTEMP, MAX6675_TMIN + .01)) min_temp_error(0);
+ if (current_temperature[0] > MIN(HEATER_0_MAXTEMP, MAX6675_TMAX - 1.0)) max_temp_error(0);
+ if (current_temperature[0] < MAX(HEATER_0_MINTEMP, MAX6675_TMIN + .01)) min_temp_error(0);
#endif
#if WATCH_HOTENDS || WATCH_THE_BED || DISABLED(PIDTEMPBED) || HAS_AUTO_FAN || HEATER_IDLE_HANDLER
@@ -819,7 +823,7 @@ void Temperature::manage_heater() {
#if ENABLED(TEMP_SENSOR_1_AS_REDUNDANT)
// Make sure measured temperatures are close together
- if (FABS(current_temperature[0] - redundant_temperature) > MAX_REDUNDANT_TEMP_SENSOR_DIFF)
+ if (ABS(current_temperature[0] - redundant_temperature) > MAX_REDUNDANT_TEMP_SENSOR_DIFF)
_temp_error(0, PSTR(MSG_REDUNDANCY), PSTR(MSG_ERR_REDUNDANT_TEMP));
#endif
@@ -911,7 +915,21 @@ void Temperature::manage_heater() {
#endif // HAS_HEATED_BED
}
-#define PGM_RD_W(x) (short)pgm_read_word(&x)
+#define TEMP_AD595(RAW) ((RAW) * 5.0 * 100.0 / 1024.0 / (OVERSAMPLENR) * (TEMP_SENSOR_AD595_GAIN) + TEMP_SENSOR_AD595_OFFSET)
+#define TEMP_AD8495(RAW) ((RAW) * 6.6 * 100.0 / 1024.0 / (OVERSAMPLENR) * (TEMP_SENSOR_AD8495_GAIN) + TEMP_SENSOR_AD8495_OFFSET)
+
+#define SCAN_THERMISTOR_TABLE(TBL,LEN) do{ \
+ for (uint8_t i = 1; i < LEN; i++) { \
+ const short entry10 = (short)pgm_read_word(&TBL[i][0]); \
+ if (entry10 > raw) { \
+ const short entry00 = (short)pgm_read_word(&TBL[i-1][0]), \
+ entry01 = (short)pgm_read_word(&TBL[i-1][1]), \
+ entry11 = (short)pgm_read_word(&TBL[i][1]); \
+ return entry01 + (raw - entry00) * float(entry11 - entry01) / float(entry10 - entry00); \
+ } \
+ } \
+ return (short)pgm_read_word(&TBL[LEN-1][1]); \
+}while(0)
// Derived from RepRap FiveD extruder::getTemperature()
// For hot end temperature measurement.
@@ -929,68 +947,73 @@ float Temperature::analog2temp(const int raw, const uint8_t e) {
return 0.0;
}
- #if ENABLED(HEATER_0_USES_MAX6675)
- if (e == 0) return 0.25 * raw;
- #endif
-
- // Thermistor with conversion table?
- if (heater_ttbl_map[e] != NULL) {
- short(*tt)[][2] = (short(*)[][2])(heater_ttbl_map[e]);
- for (uint8_t i = 1; i < heater_ttbllen_map[e]; i++) {
- const short entry10 = PGM_RD_W((*tt)[i][0]);
- if (entry10 > raw) {
- const short entry00 = PGM_RD_W((*tt)[i - 1][0]),
- entry01 = PGM_RD_W((*tt)[i - 1][1]),
- entry11 = PGM_RD_W((*tt)[i][1]);
- return entry01 + (raw - entry00) * float(entry11 - entry01) / float(entry10 - entry00);
- }
- }
- return PGM_RD_W((*tt)[heater_ttbllen_map[e] - 1][1]); // Overflow: Return last value in the table
+ switch (e) {
+ case 0:
+ #if ENABLED(HEATER_0_USES_MAX6675)
+ return raw * 0.25;
+ #elif ENABLED(HEATER_0_USES_AD595)
+ return TEMP_AD595(raw);
+ #elif ENABLED(HEATER_0_USES_AD8495)
+ return TEMP_AD8495(raw);
+ #else
+ break;
+ #endif
+ case 1:
+ #if ENABLED(HEATER_1_USES_AD595)
+ return TEMP_AD595(raw);
+ #elif ENABLED(HEATER_1_USES_AD8495)
+ return TEMP_AD8495(raw);
+ #else
+ break;
+ #endif
+ case 2:
+ #if ENABLED(HEATER_2_USES_AD595)
+ return TEMP_AD595(raw);
+ #elif ENABLED(HEATER_2_USES_AD8495)
+ return TEMP_AD8495(raw);
+ #else
+ break;
+ #endif
+ case 3:
+ #if ENABLED(HEATER_3_USES_AD595)
+ return TEMP_AD595(raw);
+ #elif ENABLED(HEATER_3_USES_AD8495)
+ return TEMP_AD8495(raw);
+ #else
+ break;
+ #endif
+ case 4:
+ #if ENABLED(HEATER_4_USES_AD595)
+ return TEMP_AD595(raw);
+ #elif ENABLED(HEATER_4_USES_AD8495)
+ return TEMP_AD8495(raw);
+ #else
+ break;
+ #endif
+ default: break;
}
- // Thermocouple with amplifier ADC interface
- return (raw *
- #if HEATER_USES_AD8495
- 660.0 / 1024.0 / (OVERSAMPLENR) * (TEMP_SENSOR_AD8495_GAIN) + TEMP_SENSOR_AD8495_OFFSET
- #elif HEATER_USES_AD595
- 5.0 * 100.0 / 1024.0 / (OVERSAMPLENR) * (TEMP_SENSOR_AD595_GAIN) + TEMP_SENSOR_AD595_OFFSET
- #else
- 0
- #endif
- );
+ #if HOTEND_USES_THERMISTOR
+ // Thermistor with conversion table?
+ const short(*tt)[][2] = (short(*)[][2])(heater_ttbl_map[e]);
+ SCAN_THERMISTOR_TABLE((*tt), heater_ttbllen_map[e]);
+ #endif
+
+ return 0;
}
#if HAS_HEATED_BED
// Derived from RepRap FiveD extruder::getTemperature()
// For bed temperature measurement.
float Temperature::analog2tempBed(const int raw) {
- #if ENABLED(BED_USES_THERMISTOR)
-
- // Thermistor with conversion table
- for (uint8_t i = 1; i < BEDTEMPTABLE_LEN; i++) {
- const short entry10 = PGM_RD_W(BEDTEMPTABLE[i][0]);
- if (entry10 > raw) {
- const short entry00 = PGM_RD_W(BEDTEMPTABLE[i - 1][0]),
- entry01 = PGM_RD_W(BEDTEMPTABLE[i - 1][1]),
- entry11 = PGM_RD_W(BEDTEMPTABLE[i][1]);
- return entry01 + (raw - entry00) * float(entry11 - entry01) / float(entry10 - entry00);
- }
- }
- return PGM_RD_W(BEDTEMPTABLE[BEDTEMPTABLE_LEN - 1][1]); // Overflow: Return last value in the table
-
+ #if ENABLED(HEATER_BED_USES_THERMISTOR)
+ SCAN_THERMISTOR_TABLE(BEDTEMPTABLE, BEDTEMPTABLE_LEN);
+ #elif ENABLED(HEATER_BED_USES_AD595)
+ return TEMP_AD595(raw);
+ #elif ENABLED(HEATER_BED_USES_AD8495)
+ return TEMP_AD8495(raw);
#else
-
- // Thermocouple with amplifier ADC interface
- return (raw *
- #if ENABLED(CHAMBER_USES_AD595)
- 5.0 * 100.0 / 1024.0 / (OVERSAMPLENR) * (TEMP_SENSOR_AD595_GAIN) + TEMP_SENSOR_AD595_OFFSET
- #elif ENABLED(CHAMBER_USES_AD8495)
- 660.0 / 1024.0 / (OVERSAMPLENR) * (TEMP_SENSOR_AD8495_GAIN) + TEMP_SENSOR_AD8495_OFFSET
- #else
- 0
- #endif
- );
-
+ return 0;
#endif
}
#endif // HAS_HEATED_BED
@@ -999,33 +1022,14 @@ float Temperature::analog2temp(const int raw, const uint8_t e) {
// Derived from RepRap FiveD extruder::getTemperature()
// For chamber temperature measurement.
float Temperature::analog2tempChamber(const int raw) {
- #if ENABLED(CHAMBER_USES_THERMISTOR)
-
- // Thermistor with conversion table
- for (uint8_t i = 1; i < CHAMBERTEMPTABLE_LEN; i++) {
- const short entry10 = PGM_RD_W(CHAMBERTEMPTABLE[i][0]);
- if (entry10 > raw) {
- const short entry00 = PGM_RD_W(CHAMBERTEMPTABLE[i - 1][0]),
- entry01 = PGM_RD_W(CHAMBERTEMPTABLE[i - 1][1]),
- entry11 = PGM_RD_W(CHAMBERTEMPTABLE[i][1]);
- return entry01 + (raw - entry00) * float(entry11 - entry01) / float(entry10 - entry00);
- }
- }
- return PGM_RD_W(CHAMBERTEMPTABLE[CHAMBERTEMPTABLE_LEN - 1][1]); // Overflow: Return last value in the table
-
+ #if ENABLED(HEATER_CHAMBER_USES_THERMISTOR)
+ SCAN_THERMISTOR_TABLE(CHAMBERTEMPTABLE, CHAMBERTEMPTABLE_LEN);
+ #elif ENABLED(HEATER_CHAMBER_USES_AD595)
+ return TEMP_AD595(raw);
+ #elif ENABLED(HEATER_CHAMBER_USES_AD8495)
+ return TEMP_AD8495(raw);
#else
-
- // Thermocouple with amplifier ADC interface
- return (raw *
- #if ENABLED(BED_USES_AD595)
- 5.0 * 100.0 / 1024.0 / (OVERSAMPLENR) * (TEMP_SENSOR_AD595_GAIN) + TEMP_SENSOR_AD595_OFFSET
- #elif ENABLED(BED_USES_AD8495)
- 660.0 / 1024.0 / (OVERSAMPLENR) * (TEMP_SENSOR_AD8495_GAIN) + TEMP_SENSOR_AD8495_OFFSET
- #else
- 0
- #endif
- );
-
+ return 0;
#endif
}
#endif // HAS_TEMP_CHAMBER
@@ -1040,8 +1044,7 @@ void Temperature::updateTemperaturesFromRawValues() {
#if ENABLED(HEATER_0_USES_MAX6675)
current_temperature_raw[0] = read_max6675();
#endif
- HOTEND_LOOP()
- current_temperature[e] = Temperature::analog2temp(current_temperature_raw[e], e);
+ HOTEND_LOOP() current_temperature[e] = Temperature::analog2temp(current_temperature_raw[e], e);
#if HAS_HEATED_BED
current_temperature_bed = Temperature::analog2tempBed(current_temperature_bed_raw);
#endif
@@ -1060,9 +1063,7 @@ void Temperature::updateTemperaturesFromRawValues() {
watchdog_reset();
#endif
- CRITICAL_SECTION_START;
temp_meas_ready = false;
- CRITICAL_SECTION_END;
}
@@ -1082,7 +1083,7 @@ void Temperature::updateTemperaturesFromRawValues() {
* a return value of 1.
*/
int8_t Temperature::widthFil_to_size_ratio() {
- if (FABS(filament_width_nominal - filament_width_meas) <= FILWIDTH_ERROR_MARGIN)
+ if (ABS(filament_width_nominal - filament_width_meas) <= FILWIDTH_ERROR_MARGIN)
return int(100.0 * filament_width_nominal / filament_width_meas) - 100;
return 0;
}
@@ -1105,7 +1106,9 @@ void Temperature::updateTemperaturesFromRawValues() {
*/
void Temperature::init() {
- #if MB(RUMBA) && (TEMP_SENSOR_0 == -1 || TEMP_SENSOR_1 == -1 || TEMP_SENSOR_2 == -1 || TEMP_SENSOR_BED == -1 || TEMP_SENSOR_CHAMBER == -1)
+ #if MB(RUMBA) && ( \
+ ENABLED(HEATER_0_USES_AD595) || ENABLED(HEATER_1_USES_AD595) || ENABLED(HEATER_2_USES_AD595) || ENABLED(HEATER_3_USES_AD595) || ENABLED(HEATER_4_USES_AD595) || ENABLED(HEATER_BED_USES_AD595) || ENABLED(HEATER_CHAMBER_USES_AD595) \
+ || ENABLED(HEATER_0_USES_AD8495) || ENABLED(HEATER_1_USES_AD8495) || ENABLED(HEATER_2_USES_AD8495) || ENABLED(HEATER_3_USES_AD8495) || ENABLED(HEATER_4_USES_AD8495) || ENABLED(HEATER_BED_USES_AD8495) || ENABLED(HEATER_CHAMBER_USES_AD8495))
// Disable RUMBA JTAG in case the thermocouple extension is plugged on top of JTAG connector
MCUCR = _BV(JTD);
MCUCR = _BV(JTD);
@@ -1171,43 +1174,38 @@ void Temperature::init() {
#endif // HEATER_0_USES_MAX6675
- #ifdef DIDR2
- #define ANALOG_SELECT(pin) do{ if (pin < 8) SBI(DIDR0, pin); else SBI(DIDR2, pin & 0x07); }while(0)
- #else
- #define ANALOG_SELECT(pin) do{ SBI(DIDR0, pin); }while(0)
- #endif
+ HAL_adc_init();
- // Set analog inputs
- ADCSRA = _BV(ADEN) | _BV(ADSC) | _BV(ADIF) | 0x07;
- DIDR0 = 0;
- #ifdef DIDR2
- DIDR2 = 0;
- #endif
#if HAS_TEMP_ADC_0
- ANALOG_SELECT(TEMP_0_PIN);
+ HAL_ANALOG_SELECT(TEMP_0_PIN);
#endif
#if HAS_TEMP_ADC_1
- ANALOG_SELECT(TEMP_1_PIN);
+ HAL_ANALOG_SELECT(TEMP_1_PIN);
#endif
#if HAS_TEMP_ADC_2
- ANALOG_SELECT(TEMP_2_PIN);
+ HAL_ANALOG_SELECT(TEMP_2_PIN);
#endif
#if HAS_TEMP_ADC_3
- ANALOG_SELECT(TEMP_3_PIN);
+ HAL_ANALOG_SELECT(TEMP_3_PIN);
#endif
#if HAS_TEMP_ADC_4
- ANALOG_SELECT(TEMP_4_PIN);
+ HAL_ANALOG_SELECT(TEMP_4_PIN);
#endif
#if HAS_HEATED_BED
- ANALOG_SELECT(TEMP_BED_PIN);
+ HAL_ANALOG_SELECT(TEMP_BED_PIN);
#endif
#if HAS_TEMP_CHAMBER
- ANALOG_SELECT(TEMP_CHAMBER_PIN);
+ HAL_ANALOG_SELECT(TEMP_CHAMBER_PIN);
#endif
#if ENABLED(FILAMENT_WIDTH_SENSOR)
- ANALOG_SELECT(FILWIDTH_PIN);
+ HAL_ANALOG_SELECT(FILWIDTH_PIN);
#endif
+ // Use timer0 for temperature measurement
+ // Interleave temperature interrupt with millies interrupt
+ OCR0B = 128;
+ ENABLE_TEMPERATURE_INTERRUPT();
+
#if HAS_AUTO_FAN_0
#if E0_AUTO_FAN_PIN == FAN1_PIN
SET_OUTPUT(E0_AUTO_FAN_PIN);
@@ -1269,11 +1267,6 @@ void Temperature::init() {
#endif
#endif
- // Use timer0 for temperature measurement
- // Interleave temperature interrupt with millies interrupt
- OCR0B = 128;
- ENABLE_TEMPERATURE_INTERRUPT();
-
// Wait for temperature measurement to settle
delay(250);
@@ -1618,7 +1611,7 @@ void Temperature::disable_all_heaters() {
WRITE(MAX6675_SS, 0); // enable TT_MAX6675
- DELAY_100NS; // Ensure 100ns delay
+ DELAY_NS(100); // Ensure 100ns delay
// Read a big-endian temperature value
max6675_temp = 0;
@@ -1702,71 +1695,71 @@ void Temperature::set_current_temp_raw() {
*
*/
void endstop_monitor() {
- static uint16_t old_endstop_bits_local = 0;
+ static uint16_t old_live_state_local = 0;
static uint8_t local_LED_status = 0;
- uint16_t current_endstop_bits_local = 0;
+ uint16_t live_state_local = 0;
#if HAS_X_MIN
- if (READ(X_MIN_PIN)) SBI(current_endstop_bits_local, X_MIN);
+ if (READ(X_MIN_PIN)) SBI(live_state_local, X_MIN);
#endif
#if HAS_X_MAX
- if (READ(X_MAX_PIN)) SBI(current_endstop_bits_local, X_MAX);
+ if (READ(X_MAX_PIN)) SBI(live_state_local, X_MAX);
#endif
#if HAS_Y_MIN
- if (READ(Y_MIN_PIN)) SBI(current_endstop_bits_local, Y_MIN);
+ if (READ(Y_MIN_PIN)) SBI(live_state_local, Y_MIN);
#endif
#if HAS_Y_MAX
- if (READ(Y_MAX_PIN)) SBI(current_endstop_bits_local, Y_MAX);
+ if (READ(Y_MAX_PIN)) SBI(live_state_local, Y_MAX);
#endif
#if HAS_Z_MIN
- if (READ(Z_MIN_PIN)) SBI(current_endstop_bits_local, Z_MIN);
+ if (READ(Z_MIN_PIN)) SBI(live_state_local, Z_MIN);
#endif
#if HAS_Z_MAX
- if (READ(Z_MAX_PIN)) SBI(current_endstop_bits_local, Z_MAX);
+ if (READ(Z_MAX_PIN)) SBI(live_state_local, Z_MAX);
#endif
#if HAS_Z_MIN_PROBE_PIN
- if (READ(Z_MIN_PROBE_PIN)) SBI(current_endstop_bits_local, Z_MIN_PROBE);
+ if (READ(Z_MIN_PROBE_PIN)) SBI(live_state_local, Z_MIN_PROBE);
#endif
#if HAS_Z2_MIN
- if (READ(Z2_MIN_PIN)) SBI(current_endstop_bits_local, Z2_MIN);
+ if (READ(Z2_MIN_PIN)) SBI(live_state_local, Z2_MIN);
#endif
#if HAS_Z2_MAX
- if (READ(Z2_MAX_PIN)) SBI(current_endstop_bits_local, Z2_MAX);
+ if (READ(Z2_MAX_PIN)) SBI(live_state_local, Z2_MAX);
#endif
- uint16_t endstop_change = current_endstop_bits_local ^ old_endstop_bits_local;
+ uint16_t endstop_change = live_state_local ^ old_live_state_local;
if (endstop_change) {
#if HAS_X_MIN
- if (TEST(endstop_change, X_MIN)) SERIAL_PROTOCOLPAIR(" X_MIN:", !!TEST(current_endstop_bits_local, X_MIN));
+ if (TEST(endstop_change, X_MIN)) SERIAL_PROTOCOLPAIR(" X_MIN:", !!TEST(live_state_local, X_MIN));
#endif
#if HAS_X_MAX
- if (TEST(endstop_change, X_MAX)) SERIAL_PROTOCOLPAIR(" X_MAX:", !!TEST(current_endstop_bits_local, X_MAX));
+ if (TEST(endstop_change, X_MAX)) SERIAL_PROTOCOLPAIR(" X_MAX:", !!TEST(live_state_local, X_MAX));
#endif
#if HAS_Y_MIN
- if (TEST(endstop_change, Y_MIN)) SERIAL_PROTOCOLPAIR(" Y_MIN:", !!TEST(current_endstop_bits_local, Y_MIN));
+ if (TEST(endstop_change, Y_MIN)) SERIAL_PROTOCOLPAIR(" Y_MIN:", !!TEST(live_state_local, Y_MIN));
#endif
#if HAS_Y_MAX
- if (TEST(endstop_change, Y_MAX)) SERIAL_PROTOCOLPAIR(" Y_MAX:", !!TEST(current_endstop_bits_local, Y_MAX));
+ if (TEST(endstop_change, Y_MAX)) SERIAL_PROTOCOLPAIR(" Y_MAX:", !!TEST(live_state_local, Y_MAX));
#endif
#if HAS_Z_MIN
- if (TEST(endstop_change, Z_MIN)) SERIAL_PROTOCOLPAIR(" Z_MIN:", !!TEST(current_endstop_bits_local, Z_MIN));
+ if (TEST(endstop_change, Z_MIN)) SERIAL_PROTOCOLPAIR(" Z_MIN:", !!TEST(live_state_local, Z_MIN));
#endif
#if HAS_Z_MAX
- if (TEST(endstop_change, Z_MAX)) SERIAL_PROTOCOLPAIR(" Z_MAX:", !!TEST(current_endstop_bits_local, Z_MAX));
+ if (TEST(endstop_change, Z_MAX)) SERIAL_PROTOCOLPAIR(" Z_MAX:", !!TEST(live_state_local, Z_MAX));
#endif
#if HAS_Z_MIN_PROBE_PIN
- if (TEST(endstop_change, Z_MIN_PROBE)) SERIAL_PROTOCOLPAIR(" PROBE:", !!TEST(current_endstop_bits_local, Z_MIN_PROBE));
+ if (TEST(endstop_change, Z_MIN_PROBE)) SERIAL_PROTOCOLPAIR(" PROBE:", !!TEST(live_state_local, Z_MIN_PROBE));
#endif
#if HAS_Z2_MIN
- if (TEST(endstop_change, Z2_MIN)) SERIAL_PROTOCOLPAIR(" Z2_MIN:", !!TEST(current_endstop_bits_local, Z2_MIN));
+ if (TEST(endstop_change, Z2_MIN)) SERIAL_PROTOCOLPAIR(" Z2_MIN:", !!TEST(live_state_local, Z2_MIN));
#endif
#if HAS_Z2_MAX
- if (TEST(endstop_change, Z2_MAX)) SERIAL_PROTOCOLPAIR(" Z2_MAX:", !!TEST(current_endstop_bits_local, Z2_MAX));
+ if (TEST(endstop_change, Z2_MAX)) SERIAL_PROTOCOLPAIR(" Z2_MAX:", !!TEST(live_state_local, Z2_MAX));
#endif
SERIAL_PROTOCOLPGM("\n\n");
analogWrite(LED_PIN, local_LED_status);
local_LED_status ^= 255;
- old_endstop_bits_local = current_endstop_bits_local;
+ old_live_state_local = live_state_local;
}
}
#endif // PINS_DEBUGGING
@@ -1784,24 +1777,14 @@ void Temperature::set_current_temp_raw() {
* - Step the babysteps value for each axis towards 0
* - For PINS_DEBUGGING, monitor and report endstop pins
* - For ENDSTOP_INTERRUPTS_FEATURE check endstops if flagged
+ * - Call planner.tick to count down its "ignore" time
*/
-ISR(TIMER0_COMPB_vect) {
- /**
- * AVR has no hardware interrupt preemption, so emulate priorization
- * and preemption of this ISR by all others by disabling the timer
- * interrupt generation capability and reenabling global interrupts.
- * Any interrupt can then interrupt this handler and preempt it.
- * This ISR becomes the lowest priority one so the UART, Endstops
- * and Stepper ISRs can all preempt it.
- */
- DISABLE_TEMPERATURE_INTERRUPT();
- sei();
+HAL_TEMP_TIMER_ISR {
+ HAL_timer_isr_prologue(TEMP_TIMER_NUM);
Temperature::isr();
- // Disable global interrupts and reenable this ISR
- cli();
- ENABLE_TEMPERATURE_INTERRUPT();
+ HAL_timer_isr_epilogue(TEMP_TIMER_NUM);
}
void Temperature::isr() {
@@ -2099,13 +2082,6 @@ void Temperature::isr() {
* This gives each ADC 0.9765ms to charge up.
*/
- #define SET_ADMUX_ADCSRA(pin) ADMUX = _BV(REFS0) | (pin & 0x07); SBI(ADCSRA, ADSC)
- #ifdef MUX5
- #define START_ADC(pin) if (pin > 7) ADCSRB = _BV(MUX5); else ADCSRB = 0; SET_ADMUX_ADCSRA(pin)
- #else
- #define START_ADC(pin) ADCSRB = 0; SET_ADMUX_ADCSRA(pin)
- #endif
-
switch (adc_sensor_state) {
case SensorsReady: {
@@ -2125,25 +2101,25 @@ void Temperature::isr() {
#if HAS_TEMP_ADC_0
case PrepareTemp_0:
- START_ADC(TEMP_0_PIN);
+ HAL_START_ADC(TEMP_0_PIN);
break;
case MeasureTemp_0:
- raw_temp_value[0] += ADC;
+ raw_temp_value[0] += HAL_READ_ADC;
break;
#endif
#if HAS_HEATED_BED
case PrepareTemp_BED:
- START_ADC(TEMP_BED_PIN);
+ HAL_START_ADC(TEMP_BED_PIN);
break;
case MeasureTemp_BED:
- raw_temp_bed_value += ADC;
+ raw_temp_bed_value += HAL_READ_ADC;
break;
#endif
#if HAS_TEMP_CHAMBER
case PrepareTemp_CHAMBER:
- START_ADC(TEMP_CHAMBER_PIN);
+ HAL_START_ADC(TEMP_CHAMBER_PIN);
break;
case MeasureTemp_CHAMBER:
raw_temp_chamber_value += ADC;
@@ -2152,55 +2128,55 @@ void Temperature::isr() {
#if HAS_TEMP_ADC_1
case PrepareTemp_1:
- START_ADC(TEMP_1_PIN);
+ HAL_START_ADC(TEMP_1_PIN);
break;
case MeasureTemp_1:
- raw_temp_value[1] += ADC;
+ raw_temp_value[1] += HAL_READ_ADC;
break;
#endif
#if HAS_TEMP_ADC_2
case PrepareTemp_2:
- START_ADC(TEMP_2_PIN);
+ HAL_START_ADC(TEMP_2_PIN);
break;
case MeasureTemp_2:
- raw_temp_value[2] += ADC;
+ raw_temp_value[2] += HAL_READ_ADC;
break;
#endif
#if HAS_TEMP_ADC_3
case PrepareTemp_3:
- START_ADC(TEMP_3_PIN);
+ HAL_START_ADC(TEMP_3_PIN);
break;
case MeasureTemp_3:
- raw_temp_value[3] += ADC;
+ raw_temp_value[3] += HAL_READ_ADC;
break;
#endif
#if HAS_TEMP_ADC_4
case PrepareTemp_4:
- START_ADC(TEMP_4_PIN);
+ HAL_START_ADC(TEMP_4_PIN);
break;
case MeasureTemp_4:
- raw_temp_value[4] += ADC;
+ raw_temp_value[4] += HAL_READ_ADC;
break;
#endif
#if ENABLED(FILAMENT_WIDTH_SENSOR)
case Prepare_FILWIDTH:
- START_ADC(FILWIDTH_PIN);
+ HAL_START_ADC(FILWIDTH_PIN);
break;
case Measure_FILWIDTH:
- if (ADC > 102) { // Make sure ADC is reading > 0.5 volts, otherwise don't read.
+ if (HAL_READ_ADC > 102) { // Make sure ADC is reading > 0.5 volts, otherwise don't read.
raw_filwidth_value -= (raw_filwidth_value >> 7); // Subtract 1/128th of the raw_filwidth_value
- raw_filwidth_value += ((unsigned long)ADC << 7); // Add new ADC reading, scaled by 128
+ raw_filwidth_value += ((unsigned long)HAL_READ_ADC << 7); // Add new ADC reading, scaled by 128
}
break;
#endif
#if ENABLED(ADC_KEYPAD)
case Prepare_ADC_KEY:
- START_ADC(ADC_KEYPAD_PIN);
+ HAL_START_ADC(ADC_KEYPAD_PIN);
break;
case Measure_ADC_KEY:
if (ADCKey_count < 16) {
@@ -2322,26 +2298,11 @@ void Temperature::isr() {
}
#endif // BABYSTEPPING
- #if ENABLED(PINS_DEBUGGING)
- extern bool endstop_monitor_flag;
- // run the endstop monitor at 15Hz
- static uint8_t endstop_monitor_count = 16; // offset this check from the others
- if (endstop_monitor_flag) {
- endstop_monitor_count += _BV(1); // 15 Hz
- endstop_monitor_count &= 0x7F;
- if (!endstop_monitor_count) endstop_monitor(); // report changes in endstop status
- }
- #endif
+ // Poll endstops state, if required
+ endstops.poll();
- #if ENABLED(ENDSTOP_INTERRUPTS_FEATURE)
-
- extern volatile uint8_t e_hit;
-
- if (e_hit && ENDSTOPS_ENABLED) {
- endstops.update(); // call endstop update routine
- e_hit--;
- }
- #endif
+ // Periodically call the planner timer
+ planner.tick();
}
#if HAS_TEMP_SENSOR
diff --git a/Marlin/temperature.h b/Marlin/temperature.h
index 27358bac68..c30bb396c9 100644
--- a/Marlin/temperature.h
+++ b/Marlin/temperature.h
@@ -106,7 +106,7 @@ enum ADCSensorState : char {
// get all oversampled sensor readings
#define MIN_ADC_ISR_LOOPS 10
-#define ACTUAL_ADC_SAMPLES max(int(MIN_ADC_ISR_LOOPS), int(SensorsReady))
+#define ACTUAL_ADC_SAMPLES MAX(int(MIN_ADC_ISR_LOOPS), int(SensorsReady))
#if HAS_PID_HEATING
#define PID_K2 (1.0-PID_K1)
@@ -304,6 +304,10 @@ class Temperature {
static uint8_t ADCKey_count;
#endif
+ #if ENABLED(PID_EXTRUSION_SCALING)
+ static int16_t lpq_len;
+ #endif
+
/**
* Instance Methods
*/
@@ -445,7 +449,7 @@ class Temperature {
#endif
target_temperature_bed =
#ifdef BED_MAXTEMP
- min(celsius, BED_MAXTEMP)
+ MIN(celsius, BED_MAXTEMP)
#else
celsius
#endif
@@ -468,7 +472,7 @@ class Temperature {
#endif
FORCE_INLINE static bool wait_for_heating(const uint8_t e) {
- return degTargetHotend(e) > TEMP_HYSTERESIS && abs(degHotend(e) - degTargetHotend(e)) > TEMP_HYSTERESIS;
+ return degTargetHotend(e) > TEMP_HYSTERESIS && ABS(degHotend(e) - degTargetHotend(e)) > TEMP_HYSTERESIS;
}
/**
diff --git a/Marlin/thermistortables.h b/Marlin/thermistortables.h
index 737fca816d..1835b306da 100644
--- a/Marlin/thermistortables.h
+++ b/Marlin/thermistortables.h
@@ -132,7 +132,7 @@
#define _TT_NAME(_N) temptable_ ## _N
#define TT_NAME(_N) _TT_NAME(_N)
-#ifdef THERMISTORHEATER_0
+#if THERMISTORHEATER_0
#define HEATER_0_TEMPTABLE TT_NAME(THERMISTORHEATER_0)
#define HEATER_0_TEMPTABLE_LEN COUNT(HEATER_0_TEMPTABLE)
#elif defined(HEATER_0_USES_THERMISTOR)
@@ -142,7 +142,7 @@
#define HEATER_0_TEMPTABLE_LEN 0
#endif
-#ifdef THERMISTORHEATER_1
+#if THERMISTORHEATER_1
#define HEATER_1_TEMPTABLE TT_NAME(THERMISTORHEATER_1)
#define HEATER_1_TEMPTABLE_LEN COUNT(HEATER_1_TEMPTABLE)
#elif defined(HEATER_1_USES_THERMISTOR)
@@ -152,7 +152,7 @@
#define HEATER_1_TEMPTABLE_LEN 0
#endif
-#ifdef THERMISTORHEATER_2
+#if THERMISTORHEATER_2
#define HEATER_2_TEMPTABLE TT_NAME(THERMISTORHEATER_2)
#define HEATER_2_TEMPTABLE_LEN COUNT(HEATER_2_TEMPTABLE)
#elif defined(HEATER_2_USES_THERMISTOR)
@@ -162,7 +162,7 @@
#define HEATER_2_TEMPTABLE_LEN 0
#endif
-#ifdef THERMISTORHEATER_3
+#if THERMISTORHEATER_3
#define HEATER_3_TEMPTABLE TT_NAME(THERMISTORHEATER_3)
#define HEATER_3_TEMPTABLE_LEN COUNT(HEATER_3_TEMPTABLE)
#elif defined(HEATER_3_USES_THERMISTOR)
@@ -172,7 +172,7 @@
#define HEATER_3_TEMPTABLE_LEN 0
#endif
-#ifdef THERMISTORHEATER_4
+#if THERMISTORHEATER_4
#define HEATER_4_TEMPTABLE TT_NAME(THERMISTORHEATER_4)
#define HEATER_4_TEMPTABLE_LEN COUNT(HEATER_4_TEMPTABLE)
#elif defined(HEATER_4_USES_THERMISTOR)
@@ -186,7 +186,7 @@
#define BEDTEMPTABLE TT_NAME(THERMISTORBED)
#define BEDTEMPTABLE_LEN COUNT(BEDTEMPTABLE)
#else
- #ifdef BED_USES_THERMISTOR
+ #ifdef HEATER_BED_USES_THERMISTOR
#error "No bed thermistor table specified"
#endif
#endif
@@ -195,7 +195,7 @@
#define CHAMBERTEMPTABLE TT_NAME(THERMISTORCHAMBER)
#define CHAMBERTEMPTABLE_LEN COUNT(CHAMBERTEMPTABLE)
#else
- #ifdef CHAMBER_USES_THERMISTOR
+ #ifdef HEATER_CHAMBER_USES_THERMISTOR
#error "No chamber thermistor table specified"
#endif
#endif
@@ -249,7 +249,7 @@
#endif
#endif
#ifndef HEATER_BED_RAW_HI_TEMP
- #ifdef BED_USES_THERMISTOR
+ #ifdef HEATER_BED_USES_THERMISTOR
#define HEATER_BED_RAW_HI_TEMP 0
#define HEATER_BED_RAW_LO_TEMP 16383
#else
@@ -258,7 +258,7 @@
#endif
#endif
#ifndef HEATER_CHAMBER_RAW_HI_TEMP
- #ifdef CHAMBER_USES_THERMISTOR
+ #ifdef HEATER_CHAMBER_USES_THERMISTOR
#define HEATER_CHAMBER_RAW_HI_TEMP 0
#define HEATER_CHAMBER_RAW_LO_TEMP 16383
#else
diff --git a/Marlin/tmc_util.h b/Marlin/tmc_util.h
index 8de85e2159..08d461e424 100644
--- a/Marlin/tmc_util.h
+++ b/Marlin/tmc_util.h
@@ -53,7 +53,7 @@ void tmc_get_current(TMC &st, const TMC_AxisEnum axis) {
_tmc_say_current(axis, st.getCurrent());
}
template
-void tmc_set_current(TMC &st, const TMC_AxisEnum axis, const int mA) {
+void tmc_set_current(TMC &st, const int mA) {
st.setCurrent(mA, R_SENSE, HOLD_MULTIPLIER);
}
template
@@ -70,7 +70,7 @@ void tmc_get_pwmthrs(TMC &st, const TMC_AxisEnum axis, const uint16_t spmm) {
_tmc_say_pwmthrs(axis, _tmc_thrs(st.microsteps(), st.TPWMTHRS(), spmm));
}
template
-void tmc_set_pwmthrs(TMC &st, const TMC_AxisEnum axis, const int32_t thrs, const uint32_t spmm) {
+void tmc_set_pwmthrs(TMC &st, const int32_t thrs, const uint32_t spmm) {
st.TPWMTHRS(_tmc_thrs(st.microsteps(), thrs, spmm));
}
template
@@ -78,7 +78,7 @@ void tmc_get_sgt(TMC &st, const TMC_AxisEnum axis) {
_tmc_say_sgt(axis, st.sgt());
}
template
-void tmc_set_sgt(TMC &st, const TMC_AxisEnum axis, const int8_t sgt_val) {
+void tmc_set_sgt(TMC &st, const int8_t sgt_val) {
st.sgt(sgt_val);
}
diff --git a/Marlin/ubl.h b/Marlin/ubl.h
index 5a401df2f8..7b9c08fe66 100644
--- a/Marlin/ubl.h
+++ b/Marlin/ubl.h
@@ -61,7 +61,6 @@ extern uint8_t ubl_cnt;
///////////////////////////////////////////////////////////////////////////////////////////////////////
#if ENABLED(ULTRA_LCD)
- extern char lcd_status_message[];
void lcd_quick_feedback(const bool clear_buttons);
#endif
@@ -235,7 +234,7 @@ class unified_bed_leveling {
const float xratio = (rx0 - mesh_index_to_xpos(x1_i)) * (1.0 / (MESH_X_DIST)),
z1 = z_values[x1_i][yi];
- return z1 + xratio * (z_values[min(x1_i, GRID_MAX_POINTS_X - 2) + 1][yi] - z1); // Don't allow x1_i+1 to be past the end of the array
+ return z1 + xratio * (z_values[MIN(x1_i, GRID_MAX_POINTS_X - 2) + 1][yi] - z1); // Don't allow x1_i+1 to be past the end of the array
// If it is, it is clamped to the last element of the
// z_values[][] array and no correction is applied.
}
@@ -269,7 +268,7 @@ class unified_bed_leveling {
const float yratio = (ry0 - mesh_index_to_ypos(y1_i)) * (1.0 / (MESH_Y_DIST)),
z1 = z_values[xi][y1_i];
- return z1 + yratio * (z_values[xi][min(y1_i, GRID_MAX_POINTS_Y - 2) + 1] - z1); // Don't allow y1_i+1 to be past the end of the array
+ return z1 + yratio * (z_values[xi][MIN(y1_i, GRID_MAX_POINTS_Y - 2) + 1] - z1); // Don't allow y1_i+1 to be past the end of the array
// If it is, it is clamped to the last element of the
// z_values[][] array and no correction is applied.
}
@@ -295,11 +294,11 @@ class unified_bed_leveling {
const float z1 = calc_z0(rx0,
mesh_index_to_xpos(cx), z_values[cx][cy],
- mesh_index_to_xpos(cx + 1), z_values[min(cx, GRID_MAX_POINTS_X - 2) + 1][cy]);
+ mesh_index_to_xpos(cx + 1), z_values[MIN(cx, GRID_MAX_POINTS_X - 2) + 1][cy]);
const float z2 = calc_z0(rx0,
- mesh_index_to_xpos(cx), z_values[cx][min(cy, GRID_MAX_POINTS_Y - 2) + 1],
- mesh_index_to_xpos(cx + 1), z_values[min(cx, GRID_MAX_POINTS_X - 2) + 1][min(cy, GRID_MAX_POINTS_Y - 2) + 1]);
+ mesh_index_to_xpos(cx), z_values[cx][MIN(cy, GRID_MAX_POINTS_Y - 2) + 1],
+ mesh_index_to_xpos(cx + 1), z_values[MIN(cx, GRID_MAX_POINTS_X - 2) + 1][MIN(cy, GRID_MAX_POINTS_Y - 2) + 1]);
float z0 = calc_z0(ry0,
mesh_index_to_ypos(cy), z1,
@@ -356,17 +355,11 @@ class unified_bed_leveling {
static void line_to_destination_cartesian(const float &fr, const uint8_t e);
#endif
- #define _CMPZ(a,b) (z_values[a][b] == z_values[a][b+1])
- #define CMPZ(a) (_CMPZ(a, 0) && _CMPZ(a, 1))
- #define ZZER(a) (z_values[a][0] == 0)
-
- FORCE_INLINE bool mesh_is_valid() {
- return !(
- ( CMPZ(0) && CMPZ(1) && CMPZ(2) // adjacent z values all equal?
- && ZZER(0) && ZZER(1) && ZZER(2) // all zero at the edge?
- )
- || isnan(z_values[0][0])
- );
+ inline static bool mesh_is_valid() {
+ for (uint8_t x = 0; x < GRID_MAX_POINTS_X; x++)
+ for (uint8_t y = 0; y < GRID_MAX_POINTS_Y; y++)
+ if (isnan(z_values[x][y])) return false;
+ return true;
}
}; // class unified_bed_leveling
diff --git a/Marlin/ubl_G29.cpp b/Marlin/ubl_G29.cpp
index d28edc161b..a4c7b33287 100644
--- a/Marlin/ubl_G29.cpp
+++ b/Marlin/ubl_G29.cpp
@@ -446,7 +446,7 @@
if (parser.seen('B')) {
g29_card_thickness = parser.has_value() ? parser.value_float() : measure_business_card_thickness((float) Z_CLEARANCE_BETWEEN_PROBES);
- if (FABS(g29_card_thickness) > 1.5) {
+ if (ABS(g29_card_thickness) > 1.5) {
SERIAL_PROTOCOLLNPGM("?Error in Business Card measurement.");
return;
}
@@ -791,8 +791,8 @@
save_ubl_active_state_and_disable(); // Disable bed level correction for probing
do_blocking_move_to(0.5 * (MESH_MAX_X - (MESH_MIN_X)), 0.5 * (MESH_MAX_Y - (MESH_MIN_Y)), in_height);
- //, min(planner.max_feedrate_mm_s[X_AXIS], planner.max_feedrate_mm_s[Y_AXIS]) / 2.0);
- stepper.synchronize();
+ //, MIN(planner.max_feedrate_mm_s[X_AXIS], planner.max_feedrate_mm_s[Y_AXIS]) / 2.0);
+ planner.synchronize();
SERIAL_PROTOCOLPGM("Place shim under nozzle");
LCD_MESSAGEPGM(MSG_UBL_BC_INSERT);
@@ -801,7 +801,7 @@
const float z1 = measure_point_with_encoder();
do_blocking_move_to_z(current_position[Z_AXIS] + SIZE_OF_LITTLE_RAISE);
- stepper.synchronize();
+ planner.synchronize();
SERIAL_PROTOCOLPGM("Remove shim");
LCD_MESSAGEPGM(MSG_UBL_BC_REMOVE);
@@ -811,7 +811,7 @@
do_blocking_move_to_z(current_position[Z_AXIS] + Z_CLEARANCE_BETWEEN_PROBES);
- const float thickness = abs(z1 - z2);
+ const float thickness = ABS(z1 - z2);
if (g29_verbose_level > 1) {
SERIAL_PROTOCOLPGM("Business Card is ");
@@ -1494,10 +1494,10 @@
#include "vector_3.h"
void unified_bed_leveling::tilt_mesh_based_on_probed_grid(const bool do_3_pt_leveling) {
- constexpr int16_t x_min = max(MIN_PROBE_X, MESH_MIN_X),
- x_max = min(MAX_PROBE_X, MESH_MAX_X),
- y_min = max(MIN_PROBE_Y, MESH_MIN_Y),
- y_max = min(MAX_PROBE_Y, MESH_MAX_Y);
+ constexpr int16_t x_min = MAX(MIN_PROBE_X, MESH_MIN_X),
+ x_max = MIN(MAX_PROBE_X, MESH_MAX_X),
+ y_min = MAX(MIN_PROBE_Y, MESH_MIN_Y),
+ y_max = MIN(MAX_PROBE_Y, MESH_MAX_Y);
bool abort_flag = false;
@@ -1765,7 +1765,7 @@
SERIAL_ECHOPGM("Extrapolating mesh...");
- const float weight_scaled = weight_factor * max(MESH_X_DIST, MESH_Y_DIST);
+ const float weight_scaled = weight_factor * MAX(MESH_X_DIST, MESH_Y_DIST);
for (uint8_t jx = 0; jx < GRID_MAX_POINTS_X; jx++)
for (uint8_t jy = 0; jy < GRID_MAX_POINTS_Y; jy++)
diff --git a/Marlin/ubl_motion.cpp b/Marlin/ubl_motion.cpp
index 24a3b7dc46..0e8e7b9092 100644
--- a/Marlin/ubl_motion.cpp
+++ b/Marlin/ubl_motion.cpp
@@ -257,7 +257,8 @@
z_position = end[Z_AXIS];
}
- planner.buffer_segment(rx, ry, z_position + z0, e_position, feed_rate, extruder);
+ if (!planner.buffer_segment(rx, ry, z_position + z0, e_position, feed_rate, extruder))
+ break;
} //else printf("FIRST MOVE PRUNED ");
}
@@ -314,7 +315,8 @@
e_position = end[E_AXIS];
z_position = end[Z_AXIS];
}
- planner.buffer_segment(rx, next_mesh_line_y, z_position + z0, e_position, feed_rate, extruder);
+ if (!planner.buffer_segment(rx, next_mesh_line_y, z_position + z0, e_position, feed_rate, extruder))
+ break;
current_yi += dyi;
yi_cnt--;
}
@@ -337,7 +339,8 @@
z_position = end[Z_AXIS];
}
- planner.buffer_segment(next_mesh_line_x, ry, z_position + z0, e_position, feed_rate, extruder);
+ if (!planner.buffer_segment(next_mesh_line_x, ry, z_position + z0, e_position, feed_rate, extruder))
+ break;
current_xi += dxi;
xi_cnt--;
}
@@ -366,7 +369,7 @@
inline void _O2 ubl_buffer_segment_raw(const float (&in_raw)[XYZE], const float &fr) {
#if ENABLED(SKEW_CORRECTION)
- float raw[XYZE] = { in_raw[X_AXIS], in_raw[Y_AXIS], in_raw[Z_AXIS], in_raw[E_AXIS] };
+ float raw[XYZE] = { in_raw[X_AXIS], in_raw[Y_AXIS], in_raw[Z_AXIS] };
planner.skew(raw[X_AXIS], raw[Y_AXIS], raw[Z_AXIS]);
#else
const float (&raw)[XYZE] = in_raw;
@@ -382,11 +385,11 @@
inverse_kinematics(raw); // this writes delta[ABC] from raw[XYZE]
// should move the feedrate scaling to scara inverse_kinematics
- const float adiff = FABS(delta[A_AXIS] - scara_oldA),
- bdiff = FABS(delta[B_AXIS] - scara_oldB);
+ const float adiff = ABS(delta[A_AXIS] - scara_oldA),
+ bdiff = ABS(delta[B_AXIS] - scara_oldB);
scara_oldA = delta[A_AXIS];
scara_oldB = delta[B_AXIS];
- float s_feedrate = max(adiff, bdiff) * scara_feed_factor;
+ float s_feedrate = MAX(adiff, bdiff) * scara_feed_factor;
planner.buffer_segment(delta[A_AXIS], delta[B_AXIS], delta[C_AXIS], in_raw[E_AXIS], s_feedrate, active_extruder);
@@ -438,13 +441,13 @@
uint16_t segments = lroundf(cartesian_xy_mm * (1.0 / (DELTA_SEGMENT_MIN_LENGTH))); // cartesian fixed segment length
#endif
- NOLESS(segments, 1); // must have at least one segment
+ NOLESS(segments, 1U); // must have at least one segment
const float inv_segments = 1.0 / segments; // divide once, multiply thereafter
#if IS_SCARA // scale the feed rate from mm/s to degrees/s
scara_feed_factor = cartesian_xy_mm * inv_segments * feedrate;
- scara_oldA = stepper.get_axis_position_degrees(A_AXIS);
- scara_oldB = stepper.get_axis_position_degrees(B_AXIS);
+ scara_oldA = planner.get_axis_position_degrees(A_AXIS);
+ scara_oldB = planner.get_axis_position_degrees(B_AXIS);
#endif
const float diff[XYZE] = {
diff --git a/Marlin/ultralcd.cpp b/Marlin/ultralcd.cpp
index 0ef9958f75..8ebe143668 100644
--- a/Marlin/ultralcd.cpp
+++ b/Marlin/ultralcd.cpp
@@ -71,7 +71,7 @@
#else
#define MAX_MESSAGE_LENGTH CHARSIZE * 2 * (LCD_WIDTH)
#endif
- uint8_t status_scroll_pos = 0;
+ uint8_t status_scroll_offset = 0;
#else
#define MAX_MESSAGE_LENGTH CHARSIZE * (LCD_WIDTH)
#endif
@@ -95,8 +95,7 @@ uint8_t lcd_status_update_delay = 1, // First update one loop delayed
#if ENABLED(DOGLCD)
#include "ultralcd_impl_DOGM.h"
#include
- bool drawing_screen, // = false
- first_page;
+ bool drawing_screen, first_page; // = false
#else
#include "ultralcd_impl_HD44780.h"
constexpr bool first_page = true;
@@ -493,6 +492,11 @@ uint16_t max_display_update_time = 0;
void lcd_goto_screen(screenFunc_t screen, const uint32_t encoder/*=0*/) {
if (currentScreen != screen) {
+ #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
+ // Shadow for editing the fade height
+ new_z_fade_height = planner.z_fade_height;
+ #endif
+
#if ENABLED(DOUBLECLICK_FOR_Z_BABYSTEPPING) && ENABLED(BABYSTEPPING)
static millis_t doubleclick_expire_ms = 0;
// Going to lcd_main_menu from status screen? Remember first click time.
@@ -501,7 +505,7 @@ uint16_t max_display_update_time = 0;
if (currentScreen == lcd_status_screen)
doubleclick_expire_ms = millis() + DOUBLECLICK_MAX_INTERVAL;
}
- else if (screen == lcd_status_screen && currentScreen == lcd_main_menu && PENDING(millis(), doubleclick_expire_ms))
+ else if (screen == lcd_status_screen && currentScreen == lcd_main_menu && PENDING(millis(), doubleclick_expire_ms) && (planner.movesplanned() || IS_SD_PRINTING))
screen =
#if ENABLED(BABYSTEP_ZPROBE_OFFSET)
lcd_babystep_zoffset
@@ -559,7 +563,7 @@ uint16_t max_display_update_time = 0;
no_reentry = true;
const screenFunc_t old_screen = currentScreen;
lcd_goto_screen(_lcd_synchronize);
- stepper.synchronize(); // idle() is called until moves complete
+ planner.synchronize(); // idle() is called until moves complete
no_reentry = false;
lcd_goto_screen(old_screen);
}
@@ -618,7 +622,7 @@ uint16_t max_display_update_time = 0;
screen_changed = false;
}
if (screen_items > 0 && encoderLine >= screen_items - limit) {
- encoderLine = max(0, screen_items - limit);
+ encoderLine = MAX(0, screen_items - limit);
encoderPosition = encoderLine * (ENCODER_STEPS_PER_MENU_ITEM);
}
if (is_menu) {
@@ -1067,13 +1071,6 @@ void lcd_quick_feedback(const bool clear_buttons) {
*
*/
- #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
- void _lcd_goto_tune_menu() {
- lcd_goto_screen(lcd_tune_menu);
- new_z_fade_height = planner.z_fade_height;
- }
- #endif
-
void lcd_main_menu() {
START_MENU();
MENU_BACK(MSG_WATCH);
@@ -1100,18 +1097,11 @@ void lcd_quick_feedback(const bool clear_buttons) {
MENU_ITEM_EDIT_CALLBACK(bool, MSG_CASE_LIGHT, (bool*)&case_light_on, update_case_light);
#endif
- if (planner.movesplanned() || IS_SD_PRINTING) {
- MENU_ITEM(submenu, MSG_TUNE,
- #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
- _lcd_goto_tune_menu
- #else
- lcd_tune_menu
- #endif
- );
- }
- else {
+ if (planner.movesplanned() || IS_SD_PRINTING)
+ MENU_ITEM(submenu, MSG_TUNE, lcd_tune_menu);
+ else
MENU_ITEM(submenu, MSG_PREPARE, lcd_prepare_menu);
- }
+
MENU_ITEM(submenu, MSG_CONTROL, lcd_control_menu);
#if ENABLED(SDSUPPORT)
@@ -1353,7 +1343,6 @@ void lcd_quick_feedback(const bool clear_buttons) {
#endif
}
-
// First Fan Speed title in "Tune" and "Control>Temperature" menus
#if FAN_COUNT > 0 && HAS_FAN0
#if FAN_COUNT > 1
@@ -1577,7 +1566,7 @@ void lcd_quick_feedback(const bool clear_buttons) {
*
*/
void _lcd_preheat(const int16_t endnum, const int16_t temph, const int16_t tempb, const int16_t fan) {
- if (temph > 0) thermalManager.setTargetHotend(min(heater_maxtemp[endnum], temph), endnum);
+ if (temph > 0) thermalManager.setTargetHotend(MIN(heater_maxtemp[endnum], temph), endnum);
#if HAS_HEATED_BED
if (tempb >= 0) thermalManager.setTargetBed(tempb);
#else
@@ -1682,7 +1671,7 @@ void lcd_quick_feedback(const bool clear_buttons) {
void lcd_preheat_m2_bedonly() { _lcd_preheat(0, 0, lcd_preheat_bed_temp[1], lcd_preheat_fan_speed[1]); }
#endif
- #if HAS_TEMP_HOTEND && (TEMP_SENSOR_1 != 0 || TEMP_SENSOR_2 != 0 || TEMP_SENSOR_3 != 0 || TEMP_SENSOR_4 != 0 || HAS_HEATED_BED)
+ #if HAS_TEMP_HOTEND || HAS_HEATED_BED
void lcd_preheat_m1_menu() {
START_MENU();
@@ -1694,7 +1683,7 @@ void lcd_quick_feedback(const bool clear_buttons) {
#else
MENU_ITEM(function, MSG_PREHEAT_1, lcd_preheat_m1_e0_only);
#endif
- #else
+ #elif HOTENDS > 1
#if HAS_HEATED_BED
MENU_ITEM(function, MSG_PREHEAT_1_N MSG_H1, lcd_preheat_m1_e0);
MENU_ITEM(function, MSG_PREHEAT_1_END " " MSG_E1, lcd_preheat_m1_e0_only);
@@ -1746,7 +1735,7 @@ void lcd_quick_feedback(const bool clear_buttons) {
#else
MENU_ITEM(function, MSG_PREHEAT_2, lcd_preheat_m2_e0_only);
#endif
- #else
+ #elif HOTENDS > 1
#if HAS_HEATED_BED
MENU_ITEM(function, MSG_PREHEAT_2_N MSG_H1, lcd_preheat_m2_e0);
MENU_ITEM(function, MSG_PREHEAT_2_END " " MSG_E1, lcd_preheat_m2_e0_only);
@@ -1788,7 +1777,7 @@ void lcd_quick_feedback(const bool clear_buttons) {
END_MENU();
}
- #endif // TEMP_SENSOR_0 && (TEMP_SENSOR_1 || TEMP_SENSOR_2 || TEMP_SENSOR_3 || TEMP_SENSOR_4 || TEMP_SENSOR_BED)
+ #endif // HAS_TEMP_HOTEND || HAS_HEATED_BED
void lcd_cooldown() {
#if FAN_COUNT > 0
@@ -2055,13 +2044,6 @@ void lcd_quick_feedback(const bool clear_buttons) {
void _lcd_ubl_level_bed();
- #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
- void _lcd_goto_ubl_level_bed() {
- lcd_goto_screen(_lcd_ubl_level_bed);
- new_z_fade_height = planner.z_fade_height;
- }
- #endif
-
static int16_t ubl_storage_slot = 0,
custom_hotend_temp = 190,
side_points = 3,
@@ -2116,7 +2098,7 @@ void lcd_quick_feedback(const bool clear_buttons) {
char UBL_LCD_GCODE[16];
const int ind = ubl_height_amount > 0 ? 9 : 10;
strcpy_P(UBL_LCD_GCODE, PSTR("G29 P6 C -"));
- sprintf_P(&UBL_LCD_GCODE[ind], PSTR(".%i"), abs(ubl_height_amount));
+ sprintf_P(&UBL_LCD_GCODE[ind], PSTR(".%i"), ABS(ubl_height_amount));
lcd_enqueue_command(UBL_LCD_GCODE);
}
@@ -2416,12 +2398,10 @@ void lcd_quick_feedback(const bool clear_buttons) {
void _lcd_do_nothing() {}
void _lcd_hard_stop() {
- stepper.quick_stop();
const screenFunc_t old_screen = currentScreen;
currentScreen = _lcd_do_nothing;
- while (planner.movesplanned()) idle();
+ planner.quick_stop();
currentScreen = old_screen;
- stepper.cleaning_buffer_counter = 0;
set_current_from_steppers_for_axis(ALL_AXES);
sync_plan_position();
}
@@ -2438,7 +2418,7 @@ void lcd_quick_feedback(const bool clear_buttons) {
if (encoderPosition) {
step_scaler += (int32_t)encoderPosition;
x_plot += step_scaler / (ENCODER_STEPS_PER_MENU_ITEM);
- if (abs(step_scaler) >= ENCODER_STEPS_PER_MENU_ITEM) step_scaler = 0;
+ if (ABS(step_scaler) >= ENCODER_STEPS_PER_MENU_ITEM) step_scaler = 0;
encoderPosition = 0;
lcdDrawUpdate = LCDVIEW_REDRAW_NOW;
}
@@ -2660,13 +2640,6 @@ void lcd_quick_feedback(const bool clear_buttons) {
END_MENU();
}
- #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
- void _lcd_goto_bed_leveling() {
- lcd_goto_screen(lcd_bed_leveling);
- new_z_fade_height = planner.z_fade_height;
- }
- #endif
-
#endif // LCD_BED_LEVELING
/**
@@ -2706,29 +2679,14 @@ void lcd_quick_feedback(const bool clear_buttons) {
//
#if ENABLED(AUTO_BED_LEVELING_UBL)
- MENU_ITEM(submenu, MSG_UBL_LEVEL_BED, (
- #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
- _lcd_goto_ubl_level_bed
- #else
- _lcd_ubl_level_bed
- #endif
- )
- );
+ MENU_ITEM(submenu, MSG_UBL_LEVEL_BED, _lcd_ubl_level_bed);
#elif ENABLED(LCD_BED_LEVELING)
#if ENABLED(PROBE_MANUALLY)
if (!g29_in_progress)
#endif
-
- MENU_ITEM(submenu, MSG_BED_LEVELING, (
- #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
- _lcd_goto_bed_leveling
- #else
- lcd_bed_leveling
- #endif
- )
- );
+ MENU_ITEM(submenu, MSG_BED_LEVELING, lcd_bed_leveling);
#elif PLANNER_LEVELING && DISABLED(SLIM_LCD_MENUS)
@@ -2850,7 +2808,7 @@ void lcd_quick_feedback(const bool clear_buttons) {
do_blocking_move_to_xy(rx, ry);
lcd_synchronize();
- move_menu_scale = max(PROBE_MANUALLY_STEP, MIN_STEPS_PER_SEGMENT / float(DEFAULT_XYZ_STEPS_PER_UNIT));
+ move_menu_scale = MAX(PROBE_MANUALLY_STEP, MIN_STEPS_PER_SEGMENT / float(DEFAULT_XYZ_STEPS_PER_UNIT));
lcd_goto_screen(lcd_move_z);
}
@@ -3265,7 +3223,7 @@ void lcd_quick_feedback(const bool clear_buttons) {
else
MENU_ITEM(gcode, MSG_AUTO_HOME, PSTR("G28"));
- #if ENABLED(SWITCHING_EXTRUDER)
+ #if ENABLED(SWITCHING_EXTRUDER) || ENABLED(SWITCHING_NOZZLE)
#if EXTRUDERS == 4
switch (active_extruder) {
@@ -3330,7 +3288,7 @@ void lcd_quick_feedback(const bool clear_buttons) {
lcd_completion_feedback();
}
- #if ENABLED(EEPROM_SETTINGS)
+ #if ENABLED(EEPROM_SETTINGS) && DISABLED(SLIM_LCD_MENUS)
static void lcd_init_eeprom() {
lcd_completion_feedback(settings.init_eeprom());
@@ -3539,7 +3497,7 @@ void lcd_quick_feedback(const bool clear_buttons) {
//
// Autotemp, Min, Max, Fact
//
- #if ENABLED(AUTOTEMP) && (HAS_TEMP_HOTEND)
+ #if ENABLED(AUTOTEMP) && HAS_TEMP_HOTEND
MENU_ITEM_EDIT(bool, MSG_AUTOTEMP, &planner.autotemp_enabled);
MENU_ITEM_EDIT(float3, MSG_MIN, &planner.autotemp_min, 0, HEATER_0_MAXTEMP - 15);
MENU_ITEM_EDIT(float3, MSG_MAX, &planner.autotemp_max, 0, HEATER_0_MAXTEMP - 15);
@@ -3625,8 +3583,8 @@ void lcd_quick_feedback(const bool clear_buttons) {
#define MINTEMP_ALL MIN3(HEATER_0_MINTEMP, HEATER_1_MINTEMP, HEATER_2_MINTEMP)
#define MAXTEMP_ALL MAX3(HEATER_0_MAXTEMP, HEATER_1_MAXTEMP, HEATER_2_MAXTEMP)
#elif HOTENDS > 1
- #define MINTEMP_ALL min(HEATER_0_MINTEMP, HEATER_1_MINTEMP)
- #define MAXTEMP_ALL max(HEATER_0_MAXTEMP, HEATER_1_MAXTEMP)
+ #define MINTEMP_ALL MIN(HEATER_0_MINTEMP, HEATER_1_MINTEMP)
+ #define MAXTEMP_ALL MAX(HEATER_0_MAXTEMP, HEATER_1_MAXTEMP)
#else
#define MINTEMP_ALL HEATER_0_MINTEMP
#define MAXTEMP_ALL HEATER_0_MAXTEMP
@@ -3854,7 +3812,7 @@ void lcd_quick_feedback(const bool clear_buttons) {
// M540 S - Abort on endstop hit when SD printing
#if ENABLED(ABORT_ON_ENDSTOP_HIT_FEATURE_ENABLED)
- MENU_ITEM_EDIT(bool, MSG_ENDSTOP_ABORT, &stepper.abort_on_endstop_hit);
+ MENU_ITEM_EDIT(bool, MSG_ENDSTOP_ABORT, &planner.abort_on_endstop_hit);
#endif
END_MENU();
@@ -4867,7 +4825,7 @@ void lcd_quick_feedback(const bool clear_buttons) {
callbackFunc = callback; \
liveEdit = live; \
} \
- typedef void _name
+ typedef void _name##_void
DEFINE_MENU_EDIT_TYPE(int16_t, int3, itostr3, 1);
DEFINE_MENU_EDIT_TYPE(uint8_t, int8, i8tostr3, 1);
@@ -5081,7 +5039,7 @@ void lcd_init() {
int16_t lcd_strlen(const char* s) {
int16_t i = 0, j = 0;
while (s[i]) {
- if (PRINTABLE(s[i])) j++;
+ if (START_OF_UTF8_CHAR(s[i])) j++;
i++;
}
return j;
@@ -5090,7 +5048,7 @@ int16_t lcd_strlen(const char* s) {
int16_t lcd_strlen_P(const char* s) {
int16_t j = 0;
while (pgm_read_byte(s)) {
- if (PRINTABLE(pgm_read_byte(s))) j++;
+ if (START_OF_UTF8_CHAR(pgm_read_byte(s))) j++;
s++;
}
return j;
@@ -5247,7 +5205,7 @@ void lcd_update() {
#endif
- const bool encoderPastThreshold = (abs(encoderDiff) >= ENCODER_PULSES_PER_STEP);
+ const bool encoderPastThreshold = (ABS(encoderDiff) >= ENCODER_PULSES_PER_STEP);
if (encoderPastThreshold || lcd_clicked) {
if (encoderPastThreshold) {
int32_t encoderMultiplier = 1;
@@ -5255,7 +5213,7 @@ void lcd_update() {
#if ENABLED(ENCODER_RATE_MULTIPLIER)
if (encoderRateMultiplierEnabled) {
- int32_t encoderMovementSteps = abs(encoderDiff) / ENCODER_PULSES_PER_STEP;
+ int32_t encoderMovementSteps = ABS(encoderDiff) / ENCODER_PULSES_PER_STEP;
if (lastEncoderMovementMillis) {
// Note that the rate is always calculated between two passes through the
@@ -5418,30 +5376,8 @@ void lcd_update() {
} // ELAPSED(ms, next_lcd_update_ms)
}
-inline void pad_message_string() {
- uint8_t i = 0, j = 0;
- char c;
- lcd_status_message[MAX_MESSAGE_LENGTH] = '\0';
- while ((c = lcd_status_message[i]) && j < LCD_WIDTH) {
- if (PRINTABLE(c)) j++;
- i++;
- }
- if (true
- #if ENABLED(STATUS_MESSAGE_SCROLLING)
- && j < LCD_WIDTH
- #endif
- ) {
- // pad with spaces to fill up the line
- while (j++ < LCD_WIDTH) lcd_status_message[i++] = ' ';
- // chop off at the edge
- lcd_status_message[i] = '\0';
- }
-}
-
void lcd_finishstatus(const bool persist=false) {
- pad_message_string();
-
#if !(ENABLED(LCD_PROGRESS_BAR) && (PROGRESS_MSG_EXPIRE > 0))
UNUSED(persist);
#endif
@@ -5459,7 +5395,7 @@ void lcd_finishstatus(const bool persist=false) {
#endif
#if ENABLED(STATUS_MESSAGE_SCROLLING)
- status_scroll_pos = 0;
+ status_scroll_offset = 0;
#endif
}
@@ -5471,7 +5407,26 @@ bool lcd_hasstatus() { return (lcd_status_message[0] != '\0'); }
void lcd_setstatus(const char * const message, const bool persist) {
if (lcd_status_message_level > 0) return;
- strncpy(lcd_status_message, message, MAX_MESSAGE_LENGTH);
+
+ // Here we have a problem. The message is encoded in UTF8, so
+ // arbitrarily cutting it will be a problem. We MUST be sure
+ // that there is no cutting in the middle of a multibyte character!
+
+ // Get a pointer to the null terminator
+ const char* pend = message + strlen(message);
+
+ // If length of supplied UTF8 string is greater than
+ // our buffer size, start cutting whole UTF8 chars
+ while ((pend - message) > MAX_MESSAGE_LENGTH) {
+ --pend;
+ while (!START_OF_UTF8_CHAR(*pend)) --pend;
+ };
+
+ // At this point, we have the proper cut point. Use it
+ uint8_t maxLen = pend - message;
+ strncpy(lcd_status_message, message, maxLen);
+ lcd_status_message[maxLen] = '\0';
+
lcd_finishstatus(persist);
}
@@ -5479,7 +5434,26 @@ void lcd_setstatusPGM(const char * const message, int8_t level) {
if (level < 0) level = lcd_status_message_level = 0;
if (level < lcd_status_message_level) return;
lcd_status_message_level = level;
- strncpy_P(lcd_status_message, message, MAX_MESSAGE_LENGTH);
+
+ // Here we have a problem. The message is encoded in UTF8, so
+ // arbitrarily cutting it will be a problem. We MUST be sure
+ // that there is no cutting in the middle of a multibyte character!
+
+ // Get a pointer to the null terminator
+ const char* pend = message + strlen_P(message);
+
+ // If length of supplied UTF8 string is greater than
+ // our buffer size, start cutting whole UTF8 chars
+ while ((pend - message) > MAX_MESSAGE_LENGTH) {
+ --pend;
+ while (!START_OF_UTF8_CHAR(pgm_read_byte(pend))) --pend;
+ };
+
+ // At this point, we have the proper cut point. Use it
+ uint8_t maxLen = pend - message;
+ strncpy_P(lcd_status_message, message, maxLen);
+ lcd_status_message[maxLen] = '\0';
+
lcd_finishstatus(level > 0);
}
@@ -5553,11 +5527,9 @@ void lcd_reset_alert_level() { lcd_status_message_level = 0; }
#if BUTTON_EXISTS(EN1)
if (BUTTON_PRESSED(EN1)) newbutton |= EN_A;
#endif
-
#if BUTTON_EXISTS(EN2)
if (BUTTON_PRESSED(EN2)) newbutton |= EN_B;
#endif
-
#if BUTTON_EXISTS(ENC)
if (BUTTON_PRESSED(ENC)) newbutton |= EN_C;
#endif
diff --git a/Marlin/ultralcd.h b/Marlin/ultralcd.h
index bf7ae0fef0..e03efceba1 100644
--- a/Marlin/ultralcd.h
+++ b/Marlin/ultralcd.h
@@ -87,7 +87,7 @@
#define BUTTON_EXISTS(BN) (defined(BTN_## BN) && BTN_## BN >= 0)
#define BUTTON_PRESSED(BN) !READ(BTN_## BN)
- #if ENABLED(ULTIPANEL)
+ #if ENABLED(ULTIPANEL) // LCD with a click-wheel input
extern bool defer_return_to_status;
@@ -111,27 +111,6 @@
void lcd_goto_screen(screenFunc_t screen, const uint32_t encoder=0);
- // Encoder click is directly connected
-
- #define BLEN_A 0
- #define BLEN_B 1
-
- #define EN_A (_BV(BLEN_A))
- #define EN_B (_BV(BLEN_B))
-
- #if BUTTON_EXISTS(ENC)
- #define BLEN_C 2
- #define EN_C (_BV(BLEN_C))
- #endif
-
- #if BUTTON_EXISTS(BACK)
- #define BLEN_D 3
- #define EN_D _BV(BLEN_D)
- #define LCD_BACK_CLICKED (buttons & EN_D)
- #endif
-
- extern volatile uint8_t buttons; // The last-checked buttons in a bit array.
- void lcd_buttons_update();
void lcd_completion_feedback(const bool good=true);
#if ENABLED(ADVANCED_PAUSE_FEATURE)
@@ -139,7 +118,7 @@
void lcd_advanced_pause_show_message(const AdvancedPauseMessage message,
const AdvancedPauseMode mode=ADVANCED_PAUSE_MODE_PAUSE_PRINT,
const uint8_t extruder=active_extruder);
- #endif // ADVANCED_PAUSE_FEATURE
+ #endif
#if ENABLED(G26_MESH_VALIDATION)
void lcd_chirp();
@@ -152,10 +131,6 @@
float lcd_z_offset_edit();
#endif
- #else
-
- inline void lcd_buttons_update() {}
-
#endif
#if ENABLED(FILAMENT_LCD_DISPLAY) && ENABLED(SDSUPPORT)
@@ -203,12 +178,6 @@
#define REPRAPWORLD_KEYPAD_MOVE_HOME (buttons_reprapworld_keypad & KEYPAD_HOME)
#define REPRAPWORLD_KEYPAD_MOVE_MENU (buttons_reprapworld_keypad & KEYPAD_EN_C)
- #if BUTTON_EXISTS(ENC)
- #define LCD_CLICKED ((buttons & EN_C) || REPRAPWORLD_KEYPAD_MOVE_MENU)
- #else
- #define LCD_CLICKED REPRAPWORLD_KEYPAD_MOVE_MENU
- #endif
-
#define REPRAPWORLD_KEYPAD_PRESSED (buttons_reprapworld_keypad & ( \
EN_REPRAPWORLD_KEYPAD_F3 | \
EN_REPRAPWORLD_KEYPAD_F2 | \
@@ -220,14 +189,6 @@
EN_REPRAPWORLD_KEYPAD_LEFT) \
)
- #elif ENABLED(NEWPANEL)
-
- #define LCD_CLICKED (buttons & EN_C)
-
- #else
-
- #define LCD_CLICKED false
-
#endif
#if ENABLED(AUTO_BED_LEVELING_UBL) || ENABLED(G26_MESH_VALIDATION)
@@ -240,7 +201,6 @@
constexpr bool lcd_wait_for_move = false;
inline void lcd_refresh() {}
- inline void lcd_buttons_update() {}
inline bool lcd_hasstatus() { return false; }
inline void lcd_setstatus(const char* const message, const bool persist=false) { UNUSED(message); UNUSED(persist); }
inline void lcd_setstatusPGM(const char* const message, const int8_t level=0) { UNUSED(message); UNUSED(level); }
@@ -250,6 +210,51 @@
#endif // ULTRA_LCD
+#if ENABLED(ULTIPANEL)
+
+ #if ENABLED(NEWPANEL) // Uses digital switches, not a shift register
+
+ // Wheel spin pins where BA is 00, 10, 11, 01 (1 bit always changes)
+ #define BLEN_A 0
+ #define BLEN_B 1
+
+ #define EN_A _BV(BLEN_A)
+ #define EN_B _BV(BLEN_B)
+
+ #if BUTTON_EXISTS(ENC)
+ #define BLEN_C 2
+ #define EN_C _BV(BLEN_C)
+ #endif
+
+ #if BUTTON_EXISTS(BACK)
+ #define BLEN_D 3
+ #define EN_D _BV(BLEN_D)
+ #define LCD_BACK_CLICKED (buttons & EN_D)
+ #endif
+
+ #endif // NEWPANEL
+
+ extern volatile uint8_t buttons; // The last-checked buttons in a bit array.
+ void lcd_buttons_update();
+
+#else
+
+ inline void lcd_buttons_update() {}
+
+#endif
+
+#if ENABLED(REPRAPWORLD_KEYPAD)
+ #ifdef EN_C
+ #define LCD_CLICKED ((buttons & EN_C) || REPRAPWORLD_KEYPAD_MOVE_MENU)
+ #else
+ #define LCD_CLICKED REPRAPWORLD_KEYPAD_MOVE_MENU
+ #endif
+#elif defined(EN_C)
+ #define LCD_CLICKED (buttons & EN_C)
+#else
+ #define LCD_CLICKED false
+#endif
+
#define LCD_MESSAGEPGM(x) lcd_setstatusPGM(PSTR(x))
#define LCD_ALERTMESSAGEPGM(x) lcd_setalertstatusPGM(PSTR(x))
diff --git a/Marlin/ultralcd_impl_DOGM.h b/Marlin/ultralcd_impl_DOGM.h
index 511a0e9a4a..c5fd1999cb 100644
--- a/Marlin/ultralcd_impl_DOGM.h
+++ b/Marlin/ultralcd_impl_DOGM.h
@@ -33,31 +33,23 @@
* License: http://opensource.org/licenses/BSD-3-Clause
*/
+/**
+ * Implementation of the LCD display routines for a DOGM128 graphic display.
+ * These are common LCD 128x64 pixel graphic displays.
+ */
+
#ifndef ULTRALCD_IMPL_DOGM_H
#define ULTRALCD_IMPL_DOGM_H
#include "MarlinConfig.h"
-/**
- * Implementation of the LCD display routines for a DOGM128 graphic display.
- * These are common LCD 128x64 pixel graphic displays.
- */
+#include
+
#include "ultralcd.h"
-
-#if ENABLED(U8GLIB_ST7920)
- #include "ultralcd_st7920_u8glib_rrd.h"
-#endif
-
-#if ENABLED(U8GLIB_ST7565_64128N)
- #include "ultralcd_st7565_u8glib_VIKI.h"
-#endif
-
#include "dogm_bitmaps.h"
#include "utility.h"
#include "duration_t.h"
-#include
-
#if ENABLED(AUTO_BED_LEVELING_UBL)
#include "ubl.h"
#endif
@@ -68,6 +60,14 @@
#undef USE_SMALL_INFOFONT
#endif
+#if ENABLED(U8GLIB_ST7920)
+ #include "ultralcd_st7920_u8glib_rrd.h"
+#endif
+
+#if ENABLED(U8GLIB_ST7565_64128N)
+ #include "ultralcd_st7565_u8glib_VIKI.h"
+#endif
+
#if ENABLED(USE_SMALL_INFOFONT)
#include "dogm_font_data_6x9_marlin.h"
#define FONT_STATUSMENU_NAME u8g_font_6x9
@@ -603,7 +603,7 @@ void lcd_implementation_clear() { } // Automatically cleared by Picture Loop
name_hash = ((name_hash << 1) | (name_hash >> 7)) ^ filename[l]; // rotate, xor
if (filename_scroll_hash != name_hash) { // If the hash changed...
filename_scroll_hash = name_hash; // Save the new hash
- filename_scroll_max = max(0, lcd_strlen(longFilename) - maxlen); // Update the scroll limit
+ filename_scroll_max = MAX(0, lcd_strlen(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
}
diff --git a/Marlin/ultralcd_impl_HD44780.h b/Marlin/ultralcd_impl_HD44780.h
index 85b2b83b5b..0926c06e49 100644
--- a/Marlin/ultralcd_impl_HD44780.h
+++ b/Marlin/ultralcd_impl_HD44780.h
@@ -67,19 +67,19 @@ extern volatile uint8_t buttons; //an extended version of the last checked butt
#define B_I2C_BTN_OFFSET 3 // (the first three bit positions reserved for EN_A, EN_B, EN_C)
// button and encoder bit positions within 'buttons'
- #define B_LE (BUTTON_LEFT< '???'.
+// Homed but unknown... '123' <-> ' '.
+// Homed and known, display constantly.
+//
+FORCE_INLINE void _draw_axis_value(const AxisEnum axis, const char *value, const bool blink) {
+ lcd_print('X' + uint8_t(axis));
if (blink)
- lcd_printPGM(pstr);
+ lcd.print(value);
else {
if (!axis_homed[axis])
- lcd.write('?');
+ while (const char c = *value++) lcd_print(c <= '.' ? c : '?');
else {
#if DISABLED(HOME_AFTER_DEACTIVATE) && DISABLED(DISABLE_REDUCED_ACCURACY_WARNING)
if (!axis_known_position[axis])
- lcd.write(' ');
+ lcd_printPGM(axis == Z_AXIS ? PSTR(" ") : PSTR(" "));
else
#endif
- lcd_printPGM(pstr);
+ lcd.print(value);
}
}
}
@@ -719,7 +752,7 @@ static void lcd_implementation_status_screen() {
//
// Hotend 1 or Bed Temperature
//
- #if HOTENDS > 1 || TEMP_SENSOR_BED
+ #if HOTENDS > 1 || HAS_HEATED_BED
lcd.setCursor(8, 0);
#if HOTENDS > 1
@@ -730,7 +763,7 @@ static void lcd_implementation_status_screen() {
_draw_heater_status(-1, -1, blink);
#endif
- #endif // HOTENDS > 1 || TEMP_SENSOR_BED
+ #endif // HOTENDS > 1 || HAS_HEATED_BED
#else // LCD_WIDTH >= 20
@@ -742,7 +775,7 @@ static void lcd_implementation_status_screen() {
//
// Hotend 1 or Bed Temperature
//
- #if HOTENDS > 1 || TEMP_SENSOR_BED
+ #if HOTENDS > 1 || HAS_HEATED_BED
lcd.setCursor(10, 0);
#if HOTENDS > 1
_draw_heater_status(1, LCD_STR_THERMOMETER[0], blink);
@@ -755,7 +788,7 @@ static void lcd_implementation_status_screen() {
), blink);
#endif
- #endif // HOTENDS > 1 || TEMP_SENSOR_BED != 0
+ #endif // HOTENDS > 1 || HAS_HEATED_BED
#endif // LCD_WIDTH >= 20
@@ -784,7 +817,7 @@ static void lcd_implementation_status_screen() {
// If the first line has two extruder temps,
// show more temperatures on the next line
- #if HOTENDS > 2 || (HOTENDS > 1 && TEMP_SENSOR_BED)
+ #if HOTENDS > 2 || (HOTENDS > 1 && HAS_HEATED_BED)
#if HOTENDS > 2
_draw_heater_status(2, LCD_STR_THERMOMETER[0], blink);
@@ -798,28 +831,22 @@ static void lcd_implementation_status_screen() {
LCD_BEDTEMP_CHAR
), blink);
- #else // HOTENDS <= 2 && (HOTENDS <= 1 || !TEMP_SENSOR_BED)
- // Before homing the axis letters are blinking 'X' <-> '?'.
- // When axis is homed but axis_known_position is false the axis letters are blinking 'X' <-> ' '.
- // When everything is ok you see a constant 'X'.
+ #else // HOTENDS <= 2 && (HOTENDS <= 1 || !HAS_HEATED_BED)
- _draw_axis_label(X_AXIS, PSTR(MSG_X), blink);
- lcd.print(ftostr4sign(LOGICAL_X_POSITION(current_position[X_AXIS])));
+ _draw_axis_value(X_AXIS, ftostr4sign(LOGICAL_X_POSITION(current_position[X_AXIS])), blink);
lcd.write(' ');
- _draw_axis_label(Y_AXIS, PSTR(MSG_Y), blink);
- lcd.print(ftostr4sign(LOGICAL_Y_POSITION(current_position[Y_AXIS])));
+ _draw_axis_value(Y_AXIS, ftostr4sign(LOGICAL_Y_POSITION(current_position[Y_AXIS])), blink);
- #endif // HOTENDS <= 2 && (HOTENDS <= 1 || !TEMP_SENSOR_BED)
+ #endif // HOTENDS <= 2 && (HOTENDS <= 1 || !HAS_HEATED_BED)
#endif // LCD_WIDTH >= 20
lcd.setCursor(LCD_WIDTH - 8, 1);
- _draw_axis_label(Z_AXIS, PSTR(MSG_Z), blink);
- lcd.print(ftostr52sp(LOGICAL_Z_POSITION(current_position[Z_AXIS])));
+ _draw_axis_value(Z_AXIS, ftostr52sp(LOGICAL_Z_POSITION(current_position[Z_AXIS])), blink);
- #if HAS_LEVELING && !TEMP_SENSOR_BED
+ #if HAS_LEVELING && !HAS_HEATED_BED
lcd.write(planner.leveling_active || blink ? '_' : ' ');
#endif
@@ -897,38 +924,82 @@ static void lcd_implementation_status_screen() {
#if ENABLED(STATUS_MESSAGE_SCROLLING)
static bool last_blink = false;
- const uint8_t slen = lcd_strlen(lcd_status_message);
- const char *stat = lcd_status_message + status_scroll_pos;
- if (slen <= LCD_WIDTH)
- lcd_print_utf(stat); // The string isn't scrolling
+
+ // Get the UTF8 character count of the string
+ uint8_t slen = lcd_strlen(lcd_status_message);
+
+ // If the string fits into the LCD, just print it and do not scroll it
+ if (slen <= LCD_WIDTH) {
+
+ // The string isn't scrolling and may not fill the screen
+ lcd_print_utf(lcd_status_message);
+
+ // Fill the rest with spaces
+ while (slen < LCD_WIDTH) {
+ lcd.write(' ');
+ ++slen;
+ }
+ }
else {
- if (status_scroll_pos <= slen - LCD_WIDTH)
- lcd_print_utf(stat); // The string fills the screen
+ // String is larger than the available space in screen.
+
+ // Get a pointer to the next valid UTF8 character
+ const char *stat = lcd_status_message + status_scroll_offset;
+
+ // Get the string remaining length
+ const uint8_t rlen = lcd_strlen(stat);
+
+ // If we have enough characters to display
+ if (rlen >= LCD_WIDTH) {
+ // The remaining string fills the screen - Print it
+ lcd_print_utf(stat, LCD_WIDTH);
+ }
else {
- uint8_t chars = LCD_WIDTH;
- if (status_scroll_pos < slen) { // First string still visible
- lcd_print_utf(stat); // The string leaves space
- chars -= slen - status_scroll_pos; // Amount of space left
- }
- lcd.write('.'); // Always at 1+ spaces left, draw a dot
- if (--chars) {
- if (status_scroll_pos < slen + 1) // Draw a second dot if there's space
- --chars, lcd.write('.');
- if (chars) lcd_print_utf(lcd_status_message, chars); // Print a second copy of the message
+
+ // The remaining string does not completely fill the screen
+ lcd_print_utf(stat, LCD_WIDTH); // The string leaves space
+ uint8_t chars = LCD_WIDTH - rlen; // Amount of space left in characters
+
+ lcd.write('.'); // Always at 1+ spaces left, draw a dot
+ if (--chars) { // Draw a second dot if there's space
+ lcd.write('.');
+ if (--chars)
+ lcd_print_utf(lcd_status_message, chars); // Print a second copy of the message
}
}
if (last_blink != blink) {
last_blink = blink;
- // Skip any non-printing bytes
- if (status_scroll_pos < slen) while (!PRINTABLE(lcd_status_message[status_scroll_pos])) status_scroll_pos++;
- if (++status_scroll_pos >= slen + 2) status_scroll_pos = 0;
+
+ // Adjust by complete UTF8 characters
+ if (status_scroll_offset < slen) {
+ status_scroll_offset++;
+ while (!START_OF_UTF8_CHAR(lcd_status_message[status_scroll_offset]))
+ status_scroll_offset++;
+ }
+ else
+ status_scroll_offset = 0;
}
}
#else
- lcd_print_utf(lcd_status_message);
+ UNUSED(blink);
+
+ // Get the UTF8 character count of the string
+ uint8_t slen = lcd_strlen(lcd_status_message);
+
+ // Just print the string to the LCD
+ lcd_print_utf(lcd_status_message, LCD_WIDTH);
+
+ // Fill the rest with spaces if there are missing spaces
+ while (slen < LCD_WIDTH) {
+ lcd.write(' ');
+ ++slen;
+ }
#endif
+
}
+
+
#if ENABLED(ULTIPANEL)
#if ENABLED(ADVANCED_PAUSE_FEATURE)
@@ -1034,7 +1105,7 @@ static void lcd_implementation_status_screen() {
name_hash = ((name_hash << 1) | (name_hash >> 7)) ^ filename[l]; // rotate, xor
if (filename_scroll_hash != name_hash) { // If the hash changed...
filename_scroll_hash = name_hash; // Save the new hash
- filename_scroll_max = max(0, lcd_strlen(longFilename) - n); // Update the scroll limit
+ filename_scroll_max = MAX(0, lcd_strlen(longFilename) - n); // Update the scroll limit
filename_scroll_pos = 0; // Reset scroll to the start
lcd_status_update_delay = 8; // Don't scroll right away
}
@@ -1347,7 +1418,7 @@ static void lcd_implementation_status_screen() {
//dump_custom_char("at entry:", &new_char);
clear_custom_char(&new_char);
- const uint8_t ypix = min(upper_left.y_pixel_offset + pixels_per_y_mesh_pnt, ULTRA_Y_PIXELS_PER_CHAR);
+ const uint8_t ypix = MIN(upper_left.y_pixel_offset + pixels_per_y_mesh_pnt, ULTRA_Y_PIXELS_PER_CHAR);
for (j = upper_left.y_pixel_offset; j < ypix; j++) {
i = upper_left.x_pixel_mask;
for (k = 0; k < pixels_per_x_mesh_pnt; k++) {
diff --git a/Marlin/ultralcd_st7565_u8glib_VIKI.h b/Marlin/ultralcd_st7565_u8glib_VIKI.h
index 7f589e2ecf..0e97edfaa0 100644
--- a/Marlin/ultralcd_st7565_u8glib_VIKI.h
+++ b/Marlin/ultralcd_st7565_u8glib_VIKI.h
@@ -24,6 +24,7 @@
#define ULCDST7565_H
#include
+#include "delay.h"
#define ST7565_CLK_PIN DOGLCD_SCK
#define ST7565_DAT_PIN DOGLCD_MOSI
@@ -38,9 +39,9 @@
#pragma GCC optimize (3)
// If you want you can define your own set of delays in Configuration.h
-//#define ST7565_DELAY_1 DELAY_0_NOP
-//#define ST7565_DELAY_2 DELAY_0_NOP
-//#define ST7565_DELAY_3 DELAY_0_NOP
+//#define ST7565_DELAY_1 DELAY_NS(0)
+//#define ST7565_DELAY_2 DELAY_NS(0)
+//#define ST7565_DELAY_3 DELAY_NS(0)
/*
#define ST7565_DELAY_1 u8g_10MicroDelay()
@@ -49,25 +50,25 @@
*/
#if F_CPU >= 20000000
- #define CPU_ST7565_DELAY_1 DELAY_0_NOP
- #define CPU_ST7565_DELAY_2 DELAY_0_NOP
- #define CPU_ST7565_DELAY_3 DELAY_1_NOP
+ #define CPU_ST7565_DELAY_1 DELAY_NS(0)
+ #define CPU_ST7565_DELAY_2 DELAY_NS(0)
+ #define CPU_ST7565_DELAY_3 DELAY_NS(63)
#elif MB(3DRAG) || MB(K8200) || MB(K8400)
- #define CPU_ST7565_DELAY_1 DELAY_0_NOP
- #define CPU_ST7565_DELAY_2 DELAY_3_NOP
- #define CPU_ST7565_DELAY_3 DELAY_0_NOP
+ #define CPU_ST7565_DELAY_1 DELAY_NS(0)
+ #define CPU_ST7565_DELAY_2 DELAY_NS(188)
+ #define CPU_ST7565_DELAY_3 DELAY_NS(0)
#elif MB(MINIRAMBO)
- #define CPU_ST7565_DELAY_1 DELAY_0_NOP
- #define CPU_ST7565_DELAY_2 DELAY_4_NOP
- #define CPU_ST7565_DELAY_3 DELAY_0_NOP
+ #define CPU_ST7565_DELAY_1 DELAY_NS(0)
+ #define CPU_ST7565_DELAY_2 DELAY_NS(250)
+ #define CPU_ST7565_DELAY_3 DELAY_NS(0)
#elif MB(RAMBO)
- #define CPU_ST7565_DELAY_1 DELAY_0_NOP
- #define CPU_ST7565_DELAY_2 DELAY_0_NOP
- #define CPU_ST7565_DELAY_3 DELAY_0_NOP
+ #define CPU_ST7565_DELAY_1 DELAY_NS(0)
+ #define CPU_ST7565_DELAY_2 DELAY_NS(0)
+ #define CPU_ST7565_DELAY_3 DELAY_NS(0)
#elif F_CPU == 16000000
- #define CPU_ST7565_DELAY_1 DELAY_0_NOP
- #define CPU_ST7565_DELAY_2 DELAY_0_NOP
- #define CPU_ST7565_DELAY_3 DELAY_1_NOP
+ #define CPU_ST7565_DELAY_1 DELAY_NS(0)
+ #define CPU_ST7565_DELAY_2 DELAY_NS(0)
+ #define CPU_ST7565_DELAY_3 DELAY_NS(63)
#else
#error "No valid condition for delays in 'ultralcd_st7565_u8glib_VIKI.h'"
#endif
@@ -115,8 +116,8 @@
#endif // !HARDWARE_SPI
-#if defined(DOGM_SPI_DELAY_US) && DOGM_SPI_DELAY_US > 0
- #define U8G_DELAY() delayMicroseconds(DOGM_SPI_DELAY_US)
+#if DOGM_SPI_DELAY_US > 0
+ #define U8G_DELAY() DELAY_US(DOGM_SPI_DELAY_US)
#else
#define U8G_DELAY() u8g_10MicroDelay()
#endif
diff --git a/Marlin/ultralcd_st7920_u8glib_rrd.h b/Marlin/ultralcd_st7920_u8glib_rrd.h
index 3b379726f2..96a1f05f09 100644
--- a/Marlin/ultralcd_st7920_u8glib_rrd.h
+++ b/Marlin/ultralcd_st7920_u8glib_rrd.h
@@ -24,6 +24,7 @@
#define ULCDST7920_H
#include
+#include "delay.h"
#define ST7920_CLK_PIN LCD_PINS_D4
#define ST7920_DAT_PIN LCD_PINS_ENABLE
@@ -40,30 +41,30 @@
#pragma GCC optimize (3)
// If you want you can define your own set of delays in Configuration.h
-//#define ST7920_DELAY_1 DELAY_0_NOP
-//#define ST7920_DELAY_2 DELAY_0_NOP
-//#define ST7920_DELAY_3 DELAY_0_NOP
+//#define ST7920_DELAY_1 DELAY_NS(0)
+//#define ST7920_DELAY_2 DELAY_NS(0)
+//#define ST7920_DELAY_3 DELAY_NS(0)
#if F_CPU >= 20000000
- #define CPU_ST7920_DELAY_1 DELAY_0_NOP
- #define CPU_ST7920_DELAY_2 DELAY_0_NOP
- #define CPU_ST7920_DELAY_3 DELAY_1_NOP
+ #define CPU_ST7920_DELAY_1 DELAY_NS(0)
+ #define CPU_ST7920_DELAY_2 DELAY_NS(0)
+ #define CPU_ST7920_DELAY_3 DELAY_NS(50)
#elif MB(3DRAG) || MB(K8200) || MB(K8400) || MB(SILVER_GATE)
- #define CPU_ST7920_DELAY_1 DELAY_0_NOP
- #define CPU_ST7920_DELAY_2 DELAY_3_NOP
- #define CPU_ST7920_DELAY_3 DELAY_0_NOP
+ #define CPU_ST7920_DELAY_1 DELAY_NS(0)
+ #define CPU_ST7920_DELAY_2 DELAY_NS(188)
+ #define CPU_ST7920_DELAY_3 DELAY_NS(0)
#elif MB(MINIRAMBO)
- #define CPU_ST7920_DELAY_1 DELAY_0_NOP
- #define CPU_ST7920_DELAY_2 DELAY_4_NOP
- #define CPU_ST7920_DELAY_3 DELAY_0_NOP
+ #define CPU_ST7920_DELAY_1 DELAY_NS(0)
+ #define CPU_ST7920_DELAY_2 DELAY_NS(250)
+ #define CPU_ST7920_DELAY_3 DELAY_NS(0)
#elif MB(RAMBO)
- #define CPU_ST7920_DELAY_1 DELAY_0_NOP
- #define CPU_ST7920_DELAY_2 DELAY_0_NOP
- #define CPU_ST7920_DELAY_3 DELAY_0_NOP
+ #define CPU_ST7920_DELAY_1 DELAY_NS(0)
+ #define CPU_ST7920_DELAY_2 DELAY_NS(0)
+ #define CPU_ST7920_DELAY_3 DELAY_NS(0)
#elif F_CPU == 16000000
- #define CPU_ST7920_DELAY_1 DELAY_0_NOP
- #define CPU_ST7920_DELAY_2 DELAY_0_NOP
- #define CPU_ST7920_DELAY_3 DELAY_1_NOP
+ #define CPU_ST7920_DELAY_1 DELAY_NS(0)
+ #define CPU_ST7920_DELAY_2 DELAY_NS(0)
+ #define CPU_ST7920_DELAY_3 DELAY_NS(63)
#else
#error "No valid condition for delays in 'ultralcd_st7920_u8glib_rrd.h'"
#endif
@@ -95,8 +96,8 @@ static void ST7920_SWSPI_SND_8BIT(uint8_t val) {
ST7920_SND_BIT; // 8
}
-#if defined(DOGM_SPI_DELAY_US) && DOGM_SPI_DELAY_US > 0
- #define U8G_DELAY() delayMicroseconds(DOGM_SPI_DELAY_US)
+#if DOGM_SPI_DELAY_US > 0
+ #define U8G_DELAY() DELAY_US(DOGM_SPI_DELAY_US)
#else
#define U8G_DELAY() u8g_10MicroDelay()
#endif
diff --git a/Marlin/utf_mapper.h b/Marlin/utf_mapper.h
index c49e6fc4e7..aacf2f11d5 100644
--- a/Marlin/utf_mapper.h
+++ b/Marlin/utf_mapper.h
@@ -144,7 +144,7 @@
#endif // DISPLAY_CHARSET_HD44780
#endif // SIMULATE_ROMFONT
-#define PRINTABLE(C) (((C) & 0xC0u) != 0x80u)
+#define START_OF_UTF8_CHAR(C) (((C) & 0xC0u) != 0x80u)
#if ENABLED(MAPPER_C2C3)