From 8285f1541efacb8bd8a5e456f9c2e3849acae413 Mon Sep 17 00:00:00 2001 From: InsanityAutomation Date: Thu, 31 May 2018 10:53:24 -0400 Subject: [PATCH] Bump to bugfix brack as of 20180526 --- Marlin/Conditionals_LCD.h | 864 +++++----- Marlin/Conditionals_post.h | 2367 +++++++++++++------------- Marlin/Configuration.h | 2 +- Marlin/G26_Mesh_Validation_Tool.cpp | 222 ++- Marlin/HAL.h | 305 ++++ Marlin/I2CPositionEncoder.cpp | 52 +- Marlin/Marlin.h | 10 +- Marlin/MarlinConfig.h | 18 +- Marlin/MarlinSerial.cpp | 6 +- Marlin/MarlinSerial.h | 6 +- Marlin/Marlin_main.cpp | 489 +++--- Marlin/Max7219_Debug_LEDs.cpp | 7 +- Marlin/SanityCheck.h | 4 +- Marlin/SdBaseFile.cpp | 23 +- Marlin/SdBaseFile.h | 115 +- Marlin/SdFatConfig.h | 5 - Marlin/SdVolume.cpp | 2 +- Marlin/Version.h | 4 +- Marlin/cardreader.cpp | 209 +-- Marlin/cardreader.h | 14 +- Marlin/configuration_store.cpp | 37 +- Marlin/configuration_store.h | 7 +- Marlin/delay.h | 77 + Marlin/digipot_mcp4018.cpp | 2 +- Marlin/digipot_mcp4451.cpp | 2 +- Marlin/dogm_bitmaps.h | 5 +- Marlin/endstop_interrupts.h | 25 +- Marlin/endstops.cpp | 694 +++++--- Marlin/endstops.h | 96 +- Marlin/enum.h | 23 +- Marlin/fastio.h | 1 - Marlin/least_squares_fit.cpp | 2 +- Marlin/least_squares_fit.h | 8 +- Marlin/macros.h | 121 +- Marlin/malyanlcd.cpp | 178 +- Marlin/nozzle.cpp | 6 +- Marlin/parser.h | 4 +- Marlin/pins.h | 15 +- Marlin/pins_ANET_10.h | 14 +- Marlin/pins_MELZI_CREALITY.h | 6 +- Marlin/pins_MELZI_MALYAN.h | 6 +- Marlin/pins_MELZI_TRONXY.h | 6 +- Marlin/pins_PRINTRBOARD.h | 14 +- Marlin/pins_PRINTRBOARD_REVF.h | 6 +- Marlin/pins_SANGUINOLOLU_11.h | 6 +- Marlin/planner.cpp | 1018 +++++++---- Marlin/planner.h | 282 ++- Marlin/planner_bezier.cpp | 12 +- Marlin/power_loss_recovery.cpp | 6 +- Marlin/runout.h | 2 +- Marlin/serial.h | 8 +- Marlin/servo.cpp | 2 +- Marlin/status_screen_DOGM.h | 149 +- Marlin/status_screen_lite_ST7920.h | 75 +- Marlin/stepper.cpp | 1783 ++++++++++--------- Marlin/stepper.h | 245 ++- Marlin/stepper_indirection.cpp | 7 +- Marlin/stepper_indirection.h | 38 +- Marlin/temperature.cpp | 373 ++-- Marlin/temperature.h | 10 +- Marlin/thermistortables.h | 18 +- Marlin/tmc_util.h | 6 +- Marlin/ubl.h | 27 +- Marlin/ubl_G29.cpp | 20 +- Marlin/ubl_motion.cpp | 23 +- Marlin/ultralcd.cpp | 182 +- Marlin/ultralcd.h | 89 +- Marlin/ultralcd_impl_DOGM.h | 32 +- Marlin/ultralcd_impl_HD44780.h | 191 ++- Marlin/ultralcd_st7565_u8glib_VIKI.h | 41 +- Marlin/ultralcd_st7920_u8glib_rrd.h | 41 +- Marlin/utf_mapper.h | 2 +- 72 files changed, 5948 insertions(+), 4819 deletions(-) create mode 100644 Marlin/HAL.h create mode 100644 Marlin/delay.h 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)