Browse Source

Merge branch 'vanilla_fb_2.1.x' into FB4S_WIFI

FB4S_WIFI
Sergey Terentiev 2 years ago
parent
commit
dd1b8567b1
  1. 210
      Marlin/Configuration.h
  2. 358
      Marlin/Configuration_adv.h
  3. 121
      Marlin/Makefile
  4. 4
      Marlin/src/HAL/ESP32/HAL.h
  5. 139
      Marlin/src/HAL/shared/HAL_spi_L6470.cpp
  6. 17
      Marlin/src/MarlinCore.cpp
  7. 78
      Marlin/src/core/boards.h
  8. 17
      Marlin/src/core/drivers.h
  9. 4
      Marlin/src/core/language.h
  10. 4
      Marlin/src/core/utility.cpp
  11. 17
      Marlin/src/feature/bedlevel/ubl/ubl_motion.cpp
  12. 3
      Marlin/src/feature/joystick.cpp
  13. 88
      Marlin/src/feature/spindle_laser.cpp
  14. 333
      Marlin/src/feature/spindle_laser.h
  15. 6
      Marlin/src/feature/spindle_laser_types.h
  16. 29
      Marlin/src/gcode/calibrate/G28.cpp
  17. 134
      Marlin/src/gcode/control/M3-M5.cpp
  18. 151
      Marlin/src/gcode/feature/L6470/M122.cpp
  19. 417
      Marlin/src/gcode/feature/L6470/M906.cpp
  20. 650
      Marlin/src/gcode/feature/L6470/M916-M918.cpp
  21. 4
      Marlin/src/gcode/feature/powerloss/M1000.cpp
  22. 45
      Marlin/src/gcode/gcode.cpp
  23. 17
      Marlin/src/gcode/gcode.h
  24. 89
      Marlin/src/gcode/host/M114.cpp
  25. 2
      Marlin/src/gcode/lcd/M255.cpp
  26. 28
      Marlin/src/gcode/motion/G2_G3.cpp
  27. 2
      Marlin/src/gcode/queue.cpp
  28. 26
      Marlin/src/inc/Conditionals_LCD.h
  29. 18
      Marlin/src/inc/Conditionals_adv.h
  30. 46
      Marlin/src/inc/Conditionals_post.h
  31. 156
      Marlin/src/inc/SanityCheck.h
  32. 2
      Marlin/src/inc/Version.h
  33. 2
      Marlin/src/lcd/dogm/status_screen_DOGM.cpp
  34. 8
      Marlin/src/lcd/dogm/u8g_dev_st7565_64128n_HAL.cpp
  35. 2
      Marlin/src/lcd/e3v2/common/dwin_font.h
  36. 7
      Marlin/src/lcd/e3v2/jyersui/README.md
  37. 4848
      Marlin/src/lcd/e3v2/jyersui/dwin.cpp
  38. 245
      Marlin/src/lcd/e3v2/jyersui/dwin.h
  39. 64
      Marlin/src/lcd/e3v2/jyersui/dwin_lcd.cpp
  40. 2
      Marlin/src/lcd/e3v2/marlinui/ui_common.cpp
  41. 82
      Marlin/src/lcd/e3v2/proui/bedlevel_tools.cpp
  42. 46
      Marlin/src/lcd/e3v2/proui/bedlevel_tools.h
  43. 237
      Marlin/src/lcd/e3v2/proui/dwin.cpp
  44. 11
      Marlin/src/lcd/e3v2/proui/dwin.h
  45. 24
      Marlin/src/lcd/e3v2/proui/dwin_defines.h
  46. 60
      Marlin/src/lcd/e3v2/proui/dwinui.cpp
  47. 157
      Marlin/src/lcd/e3v2/proui/dwinui.h
  48. 8
      Marlin/src/lcd/e3v2/proui/lockscreen.cpp
  49. 26
      Marlin/src/lcd/e3v2/proui/menus.cpp
  50. 6
      Marlin/src/lcd/e3v2/proui/menus.h
  51. 15
      Marlin/src/lcd/e3v2/proui/meshviewer.cpp
  52. 3
      Marlin/src/lcd/e3v2/proui/meshviewer.h
  53. 9
      Marlin/src/lcd/e3v2/proui/plot.cpp
  54. 2
      Marlin/src/lcd/extui/ftdi_eve_touch_ui/ftdi_eve_lib/basic/commands.cpp
  55. 16
      Marlin/src/lcd/extui/ftdi_eve_touch_ui/theme/colors.h
  56. 4
      Marlin/src/lcd/extui/mks_ui/draw_ui.h
  57. 2
      Marlin/src/lcd/extui/ui_api.cpp
  58. 2
      Marlin/src/lcd/extui/ui_api.h
  59. 1
      Marlin/src/lcd/language/language_en.h
  60. 5
      Marlin/src/lcd/marlinui.cpp
  61. 18
      Marlin/src/lcd/marlinui.h
  62. 5
      Marlin/src/lcd/menu/menu_item.h
  63. 8
      Marlin/src/lcd/menu/menu_motion.cpp
  64. 18
      Marlin/src/lcd/menu/menu_spindle_laser.cpp
  65. 998
      Marlin/src/libs/L64XX/L64XX_Marlin.cpp
  66. 141
      Marlin/src/libs/L64XX/L64XX_Marlin.h
  67. 98
      Marlin/src/libs/L64XX/README.md
  68. 2
      Marlin/src/libs/buzzer.cpp
  69. 2
      Marlin/src/libs/nozzle.cpp
  70. 4
      Marlin/src/module/delta.cpp
  71. 4
      Marlin/src/module/delta.h
  72. 34
      Marlin/src/module/motion.cpp
  73. 636
      Marlin/src/module/planner.cpp
  74. 151
      Marlin/src/module/planner.h
  75. 9
      Marlin/src/module/planner_bezier.cpp
  76. 139
      Marlin/src/module/probe.cpp
  77. 12
      Marlin/src/module/probe.h
  78. 35
      Marlin/src/module/settings.cpp
  79. 272
      Marlin/src/module/stepper.cpp
  80. 19
      Marlin/src/module/stepper.h
  81. 264
      Marlin/src/module/stepper/L64xx.cpp
  82. 490
      Marlin/src/module/stepper/L64xx.h
  83. 1
      Marlin/src/module/stepper/indirection.cpp
  84. 4
      Marlin/src/module/stepper/indirection.h
  85. 477
      Marlin/src/module/temperature.cpp
  86. 16
      Marlin/src/module/temperature.h
  87. 2
      Marlin/src/pins/linux/pins_RAMPS_LINUX.h
  88. 2
      Marlin/src/pins/lpc1768/pins_MKS_SBASE.h
  89. 2
      Marlin/src/pins/lpc1768/pins_RAMPS_RE_ARM.h
  90. 2
      Marlin/src/pins/lpc1769/pins_AZTEEG_X5_GT.h
  91. 2
      Marlin/src/pins/lpc1769/pins_AZTEEG_X5_MINI.h
  92. 2
      Marlin/src/pins/lpc1769/pins_SMOOTHIEBOARD.h
  93. 36
      Marlin/src/pins/pins.h
  94. 15
      Marlin/src/pins/pinsDebug_list.h
  95. 2
      Marlin/src/pins/rambo/pins_RAMBO.h
  96. 2
      Marlin/src/pins/rambo/pins_SCOOVO_X9H.h
  97. 2
      Marlin/src/pins/ramps/pins_AZTEEG_X3.h
  98. 4
      Marlin/src/pins/ramps/pins_AZTEEG_X3_PRO.h
  99. 2
      Marlin/src/pins/ramps/pins_MKS_GEN_13.h
  100. 2
      Marlin/src/pins/ramps/pins_RAMPS.h

210
Marlin/Configuration.h

@ -35,7 +35,7 @@
*
* Advanced settings can be found in Configuration_adv.h
*/
#define CONFIGURATION_H_VERSION 02010000
#define CONFIGURATION_H_VERSION 02010100
//===========================================================================
//============================= Getting Started =============================
@ -57,15 +57,6 @@
* https://www.thingiverse.com/thing:1278865
*/
//===========================================================================
//========================== DELTA / SCARA / TPARA ==========================
//===========================================================================
//
// Download configurations from the link above and customize for your machine.
// Examples are located in config/examples/delta, .../SCARA, and .../TPARA.
//
//===========================================================================
// @section info
// Author info of this build printed to the host during boot and M115
@ -158,13 +149,12 @@
*
* Use TMC2208/TMC2208_STANDALONE for TMC2225 drivers and TMC2209/TMC2209_STANDALONE for TMC2226 drivers.
*
* Options: A4988, A5984, DRV8825, LV8729, L6470, L6474, POWERSTEP01,
* TB6560, TB6600, TMC2100,
* Options: A4988, A5984, DRV8825, LV8729, TB6560, TB6600, TMC2100,
* TMC2130, TMC2130_STANDALONE, TMC2160, TMC2160_STANDALONE,
* TMC2208, TMC2208_STANDALONE, TMC2209, TMC2209_STANDALONE,
* TMC26X, TMC26X_STANDALONE, TMC2660, TMC2660_STANDALONE,
* TMC5130, TMC5130_STANDALONE, TMC5160, TMC5160_STANDALONE
* :['A4988', 'A5984', 'DRV8825', 'LV8729', 'L6470', 'L6474', 'POWERSTEP01', 'TB6560', 'TB6600', 'TMC2100', 'TMC2130', 'TMC2130_STANDALONE', 'TMC2160', 'TMC2160_STANDALONE', 'TMC2208', 'TMC2208_STANDALONE', 'TMC2209', 'TMC2209_STANDALONE', 'TMC26X', 'TMC26X_STANDALONE', 'TMC2660', 'TMC2660_STANDALONE', 'TMC5130', 'TMC5130_STANDALONE', 'TMC5160', 'TMC5160_STANDALONE']
* :['A4988', 'A5984', 'DRV8825', 'LV8729', 'TB6560', 'TB6600', 'TMC2100', 'TMC2130', 'TMC2130_STANDALONE', 'TMC2160', 'TMC2160_STANDALONE', 'TMC2208', 'TMC2208_STANDALONE', 'TMC2209', 'TMC2209_STANDALONE', 'TMC26X', 'TMC26X_STANDALONE', 'TMC2660', 'TMC2660_STANDALONE', 'TMC5130', 'TMC5130_STANDALONE', 'TMC5160', 'TMC5160_STANDALONE']
*/
#define X_DRIVER_TYPE A4988
#define Y_DRIVER_TYPE A4988
@ -865,7 +855,135 @@
#define POLAR_SEGMENTS_PER_SECOND 5
#endif
// Enable for an articulated robot (robot arm). Joints are directly mapped to axes (no kinematics).
// Enable for DELTA kinematics and configure below
//#define DELTA
#if ENABLED(DELTA)
// Make delta curves from many straight lines (linear interpolation).
// This is a trade-off between visible corners (not enough segments)
// and processor overload (too many expensive sqrt calls).
#define DELTA_SEGMENTS_PER_SECOND 200
// After homing move down to a height where XY movement is unconstrained
//#define DELTA_HOME_TO_SAFE_ZONE
// Delta calibration menu
// uncomment to add three points calibration menu option.
// See http://minow.blogspot.com/index.html#4918805519571907051
//#define DELTA_CALIBRATION_MENU
// uncomment to add G33 Delta Auto-Calibration (Enable EEPROM_SETTINGS to store results)
//#define DELTA_AUTO_CALIBRATION
// NOTE NB all values for DELTA_* values MUST be floating point, so always have a decimal point in them
#if ENABLED(DELTA_AUTO_CALIBRATION)
// set the default number of probe points : n*n (1 -> 7)
#define DELTA_CALIBRATION_DEFAULT_POINTS 4
#endif
#if EITHER(DELTA_AUTO_CALIBRATION, DELTA_CALIBRATION_MENU)
// Set the steprate for papertest probing
#define PROBE_MANUALLY_STEP 0.05 // (mm)
#endif
// Print surface diameter/2 minus unreachable space (avoid collisions with vertical towers).
#define DELTA_PRINTABLE_RADIUS 140.0 // (mm)
// Maximum reachable area
#define DELTA_MAX_RADIUS 140.0 // (mm)
// Center-to-center distance of the holes in the diagonal push rods.
#define DELTA_DIAGONAL_ROD 250.0 // (mm)
// Distance between bed and nozzle Z home position
#define DELTA_HEIGHT 250.00 // (mm) Get this value from G33 auto calibrate
#define DELTA_ENDSTOP_ADJ { 0.0, 0.0, 0.0 } // Get these values from G33 auto calibrate
// Horizontal distance bridged by diagonal push rods when effector is centered.
#define DELTA_RADIUS 124.0 // (mm) Get this value from G33 auto calibrate
// Trim adjustments for individual towers
// tower angle corrections for X and Y tower / rotate XYZ so Z tower angle = 0
// measured in degrees anticlockwise looking from above the printer
#define DELTA_TOWER_ANGLE_TRIM { 0.0, 0.0, 0.0 } // Get these values from G33 auto calibrate
// Delta radius and diagonal rod adjustments (mm)
//#define DELTA_RADIUS_TRIM_TOWER { 0.0, 0.0, 0.0 }
//#define DELTA_DIAGONAL_ROD_TRIM_TOWER { 0.0, 0.0, 0.0 }
#endif
/**
* MORGAN_SCARA was developed by QHARLEY in South Africa in 2012-2013.
* Implemented and slightly reworked by JCERNY in June, 2014.
*
* Mostly Printed SCARA is an open source design by Tyler Williams. See:
* https://www.thingiverse.com/thing:2487048
* https://www.thingiverse.com/thing:1241491
*/
//#define MORGAN_SCARA
//#define MP_SCARA
#if EITHER(MORGAN_SCARA, MP_SCARA)
// If movement is choppy try lowering this value
#define SCARA_SEGMENTS_PER_SECOND 200
// Length of inner and outer support arms. Measure arm lengths precisely.
#define SCARA_LINKAGE_1 150 // (mm)
#define SCARA_LINKAGE_2 150 // (mm)
// SCARA tower offset (position of Tower relative to bed zero position)
// This needs to be reasonably accurate as it defines the printbed position in the SCARA space.
#define SCARA_OFFSET_X 100 // (mm)
#define SCARA_OFFSET_Y -56 // (mm)
#if ENABLED(MORGAN_SCARA)
//#define DEBUG_SCARA_KINEMATICS
#define SCARA_FEEDRATE_SCALING // Convert XY feedrate from mm/s to degrees/s on the fly
// Radius around the center where the arm cannot reach
#define MIDDLE_DEAD_ZONE_R 0 // (mm)
#define THETA_HOMING_OFFSET 0 // Calculated from Calibration Guide and M360 / M114. See http://reprap.harleystudio.co.za/?page_id=1073
#define PSI_HOMING_OFFSET 0 // Calculated from Calibration Guide and M364 / M114. See http://reprap.harleystudio.co.za/?page_id=1073
#elif ENABLED(MP_SCARA)
#define SCARA_OFFSET_THETA1 12 // degrees
#define SCARA_OFFSET_THETA2 131 // degrees
#endif
#endif
// Enable for TPARA kinematics and configure below
//#define AXEL_TPARA
#if ENABLED(AXEL_TPARA)
#define DEBUG_ROBOT_KINEMATICS
#define ROBOT_SEGMENTS_PER_SECOND 200
// Length of inner and outer support arms. Measure arm lengths precisely.
#define ROBOT_LINKAGE_1 120 // (mm)
#define ROBOT_LINKAGE_2 120 // (mm)
// SCARA tower offset (position of Tower relative to bed zero position)
// This needs to be reasonably accurate as it defines the printbed position in the SCARA space.
#define ROBOT_OFFSET_X 0 // (mm)
#define ROBOT_OFFSET_Y 0 // (mm)
#define ROBOT_OFFSET_Z 0 // (mm)
#define SCARA_FEEDRATE_SCALING // Convert XY feedrate from mm/s to degrees/s on the fly
// Radius around the center where the arm cannot reach
#define MIDDLE_DEAD_ZONE_R 0 // (mm)
// Calculated from Calibration Guide and M360 / M114. See http://reprap.harleystudio.co.za/?page_id=1073
#define THETA_HOMING_OFFSET 0
#define PSI_HOMING_OFFSET 0
#endif
// Articulated robot (arm). Joints are directly mapped to axes with no kinematics.
//#define ARTICULATED_ROBOT_ARM
// For a hot wire cutter with parallel horizontal axes (X, I) where the heights of the two wire
@ -1224,6 +1342,27 @@
#define Z_PROBE_RETRACT_X X_MAX_POS
#endif
/**
* Magnetically Mounted Probe
* For probes such as Euclid, Klicky, Klackender, etc.
*/
//#define MAG_MOUNTED_PROBE
#if ENABLED(MAG_MOUNTED_PROBE)
#define PROBE_DEPLOY_FEEDRATE (133*60) // (mm/min) Probe deploy speed
#define PROBE_STOW_FEEDRATE (133*60) // (mm/min) Probe stow speed
#define MAG_MOUNTED_DEPLOY_1 { PROBE_DEPLOY_FEEDRATE, { 245, 114, 30 } } // Move to side Dock & Attach probe
#define MAG_MOUNTED_DEPLOY_2 { PROBE_DEPLOY_FEEDRATE, { 210, 114, 30 } } // Move probe off dock
#define MAG_MOUNTED_DEPLOY_3 { PROBE_DEPLOY_FEEDRATE, { 0, 0, 0 } } // Extra move if needed
#define MAG_MOUNTED_DEPLOY_4 { PROBE_DEPLOY_FEEDRATE, { 0, 0, 0 } } // Extra move if needed
#define MAG_MOUNTED_DEPLOY_5 { PROBE_DEPLOY_FEEDRATE, { 0, 0, 0 } } // Extra move if needed
#define MAG_MOUNTED_STOW_1 { PROBE_STOW_FEEDRATE, { 245, 114, 20 } } // Move to dock
#define MAG_MOUNTED_STOW_2 { PROBE_STOW_FEEDRATE, { 245, 114, 0 } } // Place probe beside remover
#define MAG_MOUNTED_STOW_3 { PROBE_STOW_FEEDRATE, { 230, 114, 0 } } // Side move to remove probe
#define MAG_MOUNTED_STOW_4 { PROBE_STOW_FEEDRATE, { 210, 114, 20 } } // Side move to remove probe
#define MAG_MOUNTED_STOW_5 { PROBE_STOW_FEEDRATE, { 0, 0, 0 } } // Extra move if needed
#endif
// Duet Smart Effector (for delta printers) - https://bit.ly/2ul5U7J
// When the pin is defined you can use M672 to set/reset the probe sensitivity.
//#define DUET_SMART_EFFECTOR
@ -1239,9 +1378,37 @@
*/
//#define SENSORLESS_PROBING
//
// For Z_PROBE_ALLEN_KEY see the Delta example configurations.
//
/**
* Allen key retractable z-probe as seen on many Kossel delta printers - https://reprap.org/wiki/Kossel#Automatic_bed_leveling_probe
* Deploys by touching z-axis belt. Retracts by pushing the probe down.
*/
//#define Z_PROBE_ALLEN_KEY
#if ENABLED(Z_PROBE_ALLEN_KEY)
// 2 or 3 sets of coordinates for deploying and retracting the spring loaded touch probe on G29,
// if servo actuated touch probe is not defined. Uncomment as appropriate for your printer/probe.
#define Z_PROBE_ALLEN_KEY_DEPLOY_1 { 30.0, DELTA_PRINTABLE_RADIUS, 100.0 }
#define Z_PROBE_ALLEN_KEY_DEPLOY_1_FEEDRATE XY_PROBE_FEEDRATE
#define Z_PROBE_ALLEN_KEY_DEPLOY_2 { 0.0, DELTA_PRINTABLE_RADIUS, 100.0 }
#define Z_PROBE_ALLEN_KEY_DEPLOY_2_FEEDRATE (XY_PROBE_FEEDRATE)/10
#define Z_PROBE_ALLEN_KEY_DEPLOY_3 { 0.0, (DELTA_PRINTABLE_RADIUS) * 0.75, 100.0 }
#define Z_PROBE_ALLEN_KEY_DEPLOY_3_FEEDRATE XY_PROBE_FEEDRATE
#define Z_PROBE_ALLEN_KEY_STOW_1 { -64.0, 56.0, 23.0 } // Move the probe into position
#define Z_PROBE_ALLEN_KEY_STOW_1_FEEDRATE XY_PROBE_FEEDRATE
#define Z_PROBE_ALLEN_KEY_STOW_2 { -64.0, 56.0, 3.0 } // Push it down
#define Z_PROBE_ALLEN_KEY_STOW_2_FEEDRATE (XY_PROBE_FEEDRATE)/10
#define Z_PROBE_ALLEN_KEY_STOW_3 { -64.0, 56.0, 50.0 } // Move it up to clear
#define Z_PROBE_ALLEN_KEY_STOW_3_FEEDRATE XY_PROBE_FEEDRATE
#define Z_PROBE_ALLEN_KEY_STOW_4 { 0.0, 0.0, 50.0 }
#define Z_PROBE_ALLEN_KEY_STOW_4_FEEDRATE XY_PROBE_FEEDRATE
#endif // Z_PROBE_ALLEN_KEY
/**
* Nozzle-to-Probe offsets { X, Y, Z }
@ -1899,7 +2066,7 @@
#define LCD_BED_TRAMMING
#if ENABLED(LCD_BED_TRAMMING)
#define BED_TRAMMING_INSET_LFRB { 30, 30, 30, 30 } // (mm) Left, Front, Right, Back insets
#define BED_TRAMMING_INSET_LFRB { 30, 30, 30, 30 } // (mm) Left, Front, Right, Back insets
#define BED_TRAMMING_HEIGHT 0.0 // (mm) Z height of nozzle at leveling points
#define BED_TRAMMING_Z_HOP 4.0 // (mm) Z height of nozzle between leveling points
//#define BED_TRAMMING_INCLUDE_CENTER // Move to the center after the last corner
@ -2743,6 +2910,12 @@ EEPROM_W25Q
//
//#define SILVER_GATE_GLCD_CONTROLLER
//
// eMotion Tech LCD with SD
// https://www.reprap-france.com/produit/1234568748-ecran-graphique-128-x-64-points-2-1
//
//#define EMOTION_TECH_LCD
//=============================================================================
//============================== OLED Displays ==============================
//=============================================================================
@ -3023,7 +3196,6 @@ EEPROM_W25Q
//
//#define DWIN_CREALITY_LCD // Creality UI
//#define DWIN_LCD_PROUI // Pro UI by MRiscoC
//#define DWIN_CREALITY_LCD_JYERSUI // Jyers UI by Jacob Myers
//#define DWIN_MARLINUI_PORTRAIT // MarlinUI (portrait orientation)
//#define DWIN_MARLINUI_LANDSCAPE // MarlinUI (landscape orientation)

358
Marlin/Configuration_adv.h

@ -30,7 +30,7 @@
*
* Basic settings can be found in Configuration.h
*/
#define CONFIGURATION_ADV_H_VERSION 02010000
#define CONFIGURATION_ADV_H_VERSION 02010100
//===========================================================================
//============================= Thermal Settings ============================
@ -1319,6 +1319,9 @@
#define XATC_Y_POSITION Y_CENTER // (mm) Y position to probe
#define XATC_Z_OFFSETS { 0, 0, 0 } // Z offsets for X axis sample points
#endif
// Show Deploy / Stow Probe options in the Motion menu.
#define PROBE_DEPLOY_STOW_MENU
#endif
// Include a page of printer information in the LCD Main Menu
@ -1335,7 +1338,7 @@
#endif // HAS_MARLINUI_MENU
#if ANY(HAS_DISPLAY, DWIN_LCD_PROUI, DWIN_CREALITY_LCD_JYERSUI)
#if EITHER(HAS_DISPLAY, DWIN_LCD_PROUI)
//#define SOUND_MENU_ITEM // Add a mute option to the LCD menu
#define SOUND_ON_DEFAULT // Buzzer/speaker default enabled state
#endif
@ -3238,253 +3241,6 @@
#endif // HAS_TRINAMIC_CONFIG
// @section L64XX
/**
* L64XX Stepper Driver options
*
* Arduino-L6470 library (0.8.0 or higher) is required.
* https://github.com/ameyer/Arduino-L6470
*
* Requires the following to be defined in your pins_YOUR_BOARD file
* L6470_CHAIN_SCK_PIN
* L6470_CHAIN_MISO_PIN
* L6470_CHAIN_MOSI_PIN
* L6470_CHAIN_SS_PIN
* ENABLE_RESET_L64XX_CHIPS(Q) where Q is 1 to enable and 0 to reset
*/
#if HAS_L64XX
//#define L6470_CHITCHAT // Display additional status info
#if AXIS_IS_L64XX(X)
#define X_MICROSTEPS 128 // Number of microsteps (VALID: 1, 2, 4, 8, 16, 32, 128) - L6474 max is 16
#define X_OVERCURRENT 2000 // (mA) Current where the driver detects an over current
// L6470 & L6474 - VALID: 375 x (1 - 16) - 6A max - rounds down
// POWERSTEP01: VALID: 1000 x (1 - 32) - 32A max - rounds down
#define X_STALLCURRENT 1500 // (mA) Current where the driver detects a stall (VALID: 31.25 * (1-128) - 4A max - rounds down)
// L6470 & L6474 - VALID: 31.25 * (1-128) - 4A max - rounds down
// POWERSTEP01: VALID: 200 x (1 - 32) - 6.4A max - rounds down
// L6474 - STALLCURRENT setting is used to set the nominal (TVAL) current
#define X_MAX_VOLTAGE 127 // 0-255, Maximum effective voltage seen by stepper - not used by L6474
#define X_CHAIN_POS -1 // Position in SPI chain, 0=Not in chain, 1=Nearest MOSI
#define X_SLEW_RATE 1 // 0-3, Slew 0 is slowest, 3 is fastest
#endif
#if AXIS_IS_L64XX(X2)
#define X2_MICROSTEPS X_MICROSTEPS
#define X2_OVERCURRENT 2000
#define X2_STALLCURRENT 1500
#define X2_MAX_VOLTAGE 127
#define X2_CHAIN_POS -1
#define X2_SLEW_RATE 1
#endif
#if AXIS_IS_L64XX(Y)
#define Y_MICROSTEPS 128
#define Y_OVERCURRENT 2000
#define Y_STALLCURRENT 1500
#define Y_MAX_VOLTAGE 127
#define Y_CHAIN_POS -1
#define Y_SLEW_RATE 1
#endif
#if AXIS_IS_L64XX(Y2)
#define Y2_MICROSTEPS Y_MICROSTEPS
#define Y2_OVERCURRENT 2000
#define Y2_STALLCURRENT 1500
#define Y2_MAX_VOLTAGE 127
#define Y2_CHAIN_POS -1
#define Y2_SLEW_RATE 1
#endif
#if AXIS_IS_L64XX(Z)
#define Z_MICROSTEPS 128
#define Z_OVERCURRENT 2000
#define Z_STALLCURRENT 1500
#define Z_MAX_VOLTAGE 127
#define Z_CHAIN_POS -1
#define Z_SLEW_RATE 1
#endif
#if AXIS_IS_L64XX(Z2)
#define Z2_MICROSTEPS Z_MICROSTEPS
#define Z2_OVERCURRENT 2000
#define Z2_STALLCURRENT 1500
#define Z2_MAX_VOLTAGE 127
#define Z2_CHAIN_POS -1
#define Z2_SLEW_RATE 1
#endif
#if AXIS_IS_L64XX(Z3)
#define Z3_MICROSTEPS Z_MICROSTEPS
#define Z3_OVERCURRENT 2000
#define Z3_STALLCURRENT 1500
#define Z3_MAX_VOLTAGE 127
#define Z3_CHAIN_POS -1
#define Z3_SLEW_RATE 1
#endif
#if AXIS_IS_L64XX(Z4)
#define Z4_MICROSTEPS Z_MICROSTEPS
#define Z4_OVERCURRENT 2000
#define Z4_STALLCURRENT 1500
#define Z4_MAX_VOLTAGE 127
#define Z4_CHAIN_POS -1
#define Z4_SLEW_RATE 1
#endif
#if AXIS_IS_L64XX(I)
#define I_MICROSTEPS 128
#define I_OVERCURRENT 2000
#define I_STALLCURRENT 1500
#define I_MAX_VOLTAGE 127
#define I_CHAIN_POS -1
#define I_SLEW_RATE 1
#endif
#if AXIS_IS_L64XX(J)
#define J_MICROSTEPS 128
#define J_OVERCURRENT 2000
#define J_STALLCURRENT 1500
#define J_MAX_VOLTAGE 127
#define J_CHAIN_POS -1
#define J_SLEW_RATE 1
#endif
#if AXIS_IS_L64XX(K)
#define K_MICROSTEPS 128
#define K_OVERCURRENT 2000
#define K_STALLCURRENT 1500
#define K_MAX_VOLTAGE 127
#define K_CHAIN_POS -1
#define K_SLEW_RATE 1
#endif
#if AXIS_IS_L64XX(U)
#define U_MICROSTEPS 128
#define U_OVERCURRENT 2000
#define U_STALLCURRENT 1500
#define U_MAX_VOLTAGE 127
#define U_CHAIN_POS -1
#define U_SLEW_RATE 1
#endif
#if AXIS_IS_L64XX(V)
#define V_MICROSTEPS 128
#define V_OVERCURRENT 2000
#define V_STALLCURRENT 1500
#define V_MAX_VOLTAGE 127
#define V_CHAIN_POS -1
#define V_SLEW_RATE 1
#endif
#if AXIS_IS_L64XX(W)
#define W_MICROSTEPS 128
#define W_OVERCURRENT 2000
#define W_STALLCURRENT 1500
#define W_MAX_VOLTAGE 127
#define W_CHAIN_POS -1
#define W_SLEW_RATE 1
#endif
#if AXIS_IS_L64XX(E0)
#define E0_MICROSTEPS 128
#define E0_OVERCURRENT 2000
#define E0_STALLCURRENT 1500
#define E0_MAX_VOLTAGE 127
#define E0_CHAIN_POS -1
#define E0_SLEW_RATE 1
#endif
#if AXIS_IS_L64XX(E1)
#define E1_MICROSTEPS E0_MICROSTEPS
#define E1_OVERCURRENT 2000
#define E1_STALLCURRENT 1500
#define E1_MAX_VOLTAGE 127
#define E1_CHAIN_POS -1
#define E1_SLEW_RATE 1
#endif
#if AXIS_IS_L64XX(E2)
#define E2_MICROSTEPS E0_MICROSTEPS
#define E2_OVERCURRENT 2000
#define E2_STALLCURRENT 1500
#define E2_MAX_VOLTAGE 127
#define E2_CHAIN_POS -1
#define E2_SLEW_RATE 1
#endif
#if AXIS_IS_L64XX(E3)
#define E3_MICROSTEPS E0_MICROSTEPS
#define E3_OVERCURRENT 2000
#define E3_STALLCURRENT 1500
#define E3_MAX_VOLTAGE 127
#define E3_CHAIN_POS -1
#define E3_SLEW_RATE 1
#endif
#if AXIS_IS_L64XX(E4)
#define E4_MICROSTEPS E0_MICROSTEPS
#define E4_OVERCURRENT 2000
#define E4_STALLCURRENT 1500
#define E4_MAX_VOLTAGE 127
#define E4_CHAIN_POS -1
#define E4_SLEW_RATE 1
#endif
#if AXIS_IS_L64XX(E5)
#define E5_MICROSTEPS E0_MICROSTEPS
#define E5_OVERCURRENT 2000
#define E5_STALLCURRENT 1500
#define E5_MAX_VOLTAGE 127
#define E5_CHAIN_POS -1
#define E5_SLEW_RATE 1
#endif
#if AXIS_IS_L64XX(E6)
#define E6_MICROSTEPS E0_MICROSTEPS
#define E6_OVERCURRENT 2000
#define E6_STALLCURRENT 1500
#define E6_MAX_VOLTAGE 127
#define E6_CHAIN_POS -1
#define E6_SLEW_RATE 1
#endif
#if AXIS_IS_L64XX(E7)
#define E7_MICROSTEPS E0_MICROSTEPS
#define E7_OVERCURRENT 2000
#define E7_STALLCURRENT 1500
#define E7_MAX_VOLTAGE 127
#define E7_CHAIN_POS -1
#define E7_SLEW_RATE 1
#endif
/**
* Monitor L6470 drivers for error conditions like over temperature and over current.
* In the case of over temperature Marlin can decrease the drive until the error condition clears.
* Other detected conditions can be used to stop the current print.
* Relevant G-codes:
* M906 - I1/2/3/4/5 Set or get motor drive level using axis codes X, Y, Z, E. Report values if no axis codes given.
* I not present or I0 or I1 - X, Y, Z or E0
* I2 - X2, Y2, Z2 or E1
* I3 - Z3 or E3
* I4 - Z4 or E4
* I5 - E5
* M916 - Increase drive level until get thermal warning
* M917 - Find minimum current thresholds
* M918 - Increase speed until max or error
* M122 S0/1 - Report driver parameters
*/
//#define MONITOR_L6470_DRIVER_STATUS
#if ENABLED(MONITOR_L6470_DRIVER_STATUS)
#define KVAL_HOLD_STEP_DOWN 1
//#define L6470_STOP_ON_ERROR
#endif
#endif // HAS_L64XX
// @section i2cbus
@ -3670,8 +3426,11 @@
#endif
// Define the minimum and maximum test pulse time values for a laser test fire function
#define LASER_TEST_PULSE_MIN 1 // Used with Laser Control Menu
#define LASER_TEST_PULSE_MAX 999 // Caution: Menu may not show more than 3 characters
#define LASER_TEST_PULSE_MIN 1 // (ms) Used with Laser Control Menu
#define LASER_TEST_PULSE_MAX 999 // (ms) Caution: Menu may not show more than 3 characters
#define SPINDLE_LASER_POWERUP_DELAY 50 // (ms) Delay to allow the spindle/laser to come up to speed/power
#define SPINDLE_LASER_POWERDOWN_DELAY 50 // (ms) Delay to allow the spindle to stop
/**
* Laser Safety Timeout
@ -3684,79 +3443,38 @@
#define LASER_SAFETY_TIMEOUT_MS 1000 // (ms)
/**
* Enable inline laser power to be handled in the planner / stepper routines.
* Inline power is specified by the I (inline) flag in an M3 command (e.g., M3 S20 I)
* or by the 'S' parameter in G0/G1/G2/G3 moves (see LASER_MOVE_POWER).
* Any M3 or G1/2/3/5 command with the 'I' parameter enables continuous inline power mode.
*
* e.g., 'M3 I' enables continuous inline power which is processed by the planner.
* Power is stored in move blocks and applied when blocks are processed by the Stepper ISR.
*
* This allows the laser to keep in perfect sync with the planner and removes
* the powerup/down delay since lasers require negligible time.
* 'M4 I' sets dynamic mode which uses the current feedrate to calculate a laser power OCR value.
*
* Any move in dynamic mode will use the current feedrate to calculate the laser power.
* Feed rates are set by the F parameter of a move command e.g. G1 X0 Y10 F6000
* Laser power would be calculated by bit shifting off 8 LSB's. In binary this is div 256.
* The calculation gives us ocr values from 0 to 255, values over F65535 will be set as 255 .
* More refined power control such as compesation for accell/decell will be addressed in future releases.
*
* M5 I clears inline mode and set power to 0, M5 sets the power output to 0 but leaves inline mode on.
*/
//#define LASER_POWER_INLINE
#if ENABLED(LASER_POWER_INLINE)
/**
* Scale the laser's power in proportion to the movement rate.
*
* - Sets the entry power proportional to the entry speed over the nominal speed.
* - Ramps the power up every N steps to approximate the speed trapezoid.
* - Due to the limited power resolution this is only approximate.
*/
#define LASER_POWER_INLINE_TRAPEZOID
/**
* Continuously calculate the current power (nominal_power * current_rate / nominal_rate).
* Required for accurate power with non-trapezoidal acceleration (e.g., S_CURVE_ACCELERATION).
* This is a costly calculation so this option is discouraged on 8-bit AVR boards.
*
* LASER_POWER_INLINE_TRAPEZOID_CONT_PER defines how many step cycles there are between power updates. If your
* board isn't able to generate steps fast enough (and you are using LASER_POWER_INLINE_TRAPEZOID_CONT), increase this.
* Note that when this is zero it means it occurs every cycle; 1 means a delay wait one cycle then run, etc.
*/
//#define LASER_POWER_INLINE_TRAPEZOID_CONT
/**
* Stepper iterations between power updates. Increase this value if the board
* can't keep up with the processing demands of LASER_POWER_INLINE_TRAPEZOID_CONT.
* Disable (or set to 0) to recalculate power on every stepper iteration.
*/
//#define LASER_POWER_INLINE_TRAPEZOID_CONT_PER 10
/**
* Include laser power in G0/G1/G2/G3/G5 commands with the 'S' parameter
*/
//#define LASER_MOVE_POWER
#if ENABLED(LASER_MOVE_POWER)
// Turn off the laser on G0 moves with no power parameter.
// If a power parameter is provided, use that instead.
//#define LASER_MOVE_G0_OFF
// Turn off the laser on G28 homing.
//#define LASER_MOVE_G28_OFF
#endif
/**
* Inline flag inverted
*
* WARNING: M5 will NOT turn off the laser unless another move
* is done (so G-code files must end with 'M5 I').
*/
//#define LASER_POWER_INLINE_INVERT
/**
* Continuously apply inline power. ('M3 S3' == 'G1 S3' == 'M3 S3 I')
*
* The laser might do some weird things, so only enable this
* feature if you understand the implications.
*/
//#define LASER_POWER_INLINE_CONTINUOUS
#else
#define SPINDLE_LASER_POWERUP_DELAY 50 // (ms) Delay to allow the spindle/laser to come up to speed/power
#define SPINDLE_LASER_POWERDOWN_DELAY 50 // (ms) Delay to allow the spindle to stop
/**
* Enable M3 commands for laser mode inline power planner syncing.
* This feature enables any M3 S-value to be injected into the block buffers while in
* CUTTER_MODE_CONTINUOUS. The option allows M3 laser power to be commited without waiting
* for a planner syncronization
*/
//#define LASER_POWER_SYNC
#endif
/**
* Scale the laser's power in proportion to the movement rate.
*
* - Sets the entry power proportional to the entry speed over the nominal speed.
* - Ramps the power up every N steps to approximate the speed trapezoid.
* - Due to the limited power resolution this is only approximate.
*/
//#define LASER_POWER_TRAP
//
// Laser I2C Ammeter (High precision INA226 low/high side module)

121
Marlin/Makefile

@ -317,123 +317,10 @@ else ifeq ($(HARDWARE_MOTHERBOARD),1159)
else ifeq ($(HARDWARE_MOTHERBOARD),1160)
# Longer LKx PRO / Alfawise Uxx Pro (PRO version)
else ifeq ($(HARDWARE_MOTHERBOARD),1161)
# 3Drag Controller
else ifeq ($(HARDWARE_MOTHERBOARD),1100)
# Velleman K8200 Controller (derived from 3Drag Controller)
else ifeq ($(HARDWARE_MOTHERBOARD),1101)
# Velleman K8400 Controller (derived from 3Drag Controller)
else ifeq ($(HARDWARE_MOTHERBOARD),1102)
# Velleman K8600 Controller (Vertex Nano)
else ifeq ($(HARDWARE_MOTHERBOARD),1103)
# Velleman K8800 Controller (Vertex Delta)
else ifeq ($(HARDWARE_MOTHERBOARD),1104)
# 2PrintBeta BAM&DICE with STK drivers
else ifeq ($(HARDWARE_MOTHERBOARD),1105)
# 2PrintBeta BAM&DICE Due with STK drivers
else ifeq ($(HARDWARE_MOTHERBOARD),1106)
# MKS BASE v1.0
else ifeq ($(HARDWARE_MOTHERBOARD),1107)
# MKS v1.4 with A4982 stepper drivers
else ifeq ($(HARDWARE_MOTHERBOARD),1108)
# MKS v1.5 with Allegro A4982 stepper drivers
else ifeq ($(HARDWARE_MOTHERBOARD),1109)
# MKS v1.6 with Allegro A4982 stepper drivers
else ifeq ($(HARDWARE_MOTHERBOARD),1110)
# MKS BASE 1.0 with Heroic HR4982 stepper drivers
else ifeq ($(HARDWARE_MOTHERBOARD),1111)
# MKS GEN v1.3 or 1.4
else ifeq ($(HARDWARE_MOTHERBOARD),1112)
# MKS GEN L
else ifeq ($(HARDWARE_MOTHERBOARD),1113)
# zrib V2.0 control board (Chinese RAMPS replica)
else ifeq ($(HARDWARE_MOTHERBOARD),1114)
# BigTreeTech or BIQU KFB2.0
else ifeq ($(HARDWARE_MOTHERBOARD),1115)
# Felix 2.0+ Electronics Board (RAMPS like)
else ifeq ($(HARDWARE_MOTHERBOARD),1116)
# Invent-A-Part RigidBoard
else ifeq ($(HARDWARE_MOTHERBOARD),1117)
# Invent-A-Part RigidBoard V2
else ifeq ($(HARDWARE_MOTHERBOARD),1118)
# Sainsmart 2-in-1 board
else ifeq ($(HARDWARE_MOTHERBOARD),1119)
# Ultimaker
else ifeq ($(HARDWARE_MOTHERBOARD),1120)
# Ultimaker (Older electronics. Pre 1.5.4. This is rare)
else ifeq ($(HARDWARE_MOTHERBOARD),1121)
MCU ?= atmega1280
PROG_MCU ?= m1280
# Azteeg X3
else ifeq ($(HARDWARE_MOTHERBOARD),1122)
# Azteeg X3 Pro
else ifeq ($(HARDWARE_MOTHERBOARD),1123)
# Ultimainboard 2.x (Uses TEMP_SENSOR 20)
else ifeq ($(HARDWARE_MOTHERBOARD),1124)
# Rumba
else ifeq ($(HARDWARE_MOTHERBOARD),1125)
# Raise3D Rumba
else ifeq ($(HARDWARE_MOTHERBOARD),1126)
# Rapide Lite RL200 Rumba
else ifeq ($(HARDWARE_MOTHERBOARD),1127)
# Formbot T-Rex 2 Plus
else ifeq ($(HARDWARE_MOTHERBOARD),1128)
# Formbot T-Rex 3
else ifeq ($(HARDWARE_MOTHERBOARD),1129)
# Formbot Raptor
else ifeq ($(HARDWARE_MOTHERBOARD),1130)
# Formbot Raptor 2
else ifeq ($(HARDWARE_MOTHERBOARD),1131)
# bq ZUM Mega 3D
else ifeq ($(HARDWARE_MOTHERBOARD),1132)
# MakeBoard Mini v2.1.2 is a control board sold by MicroMake
else ifeq ($(HARDWARE_MOTHERBOARD),1133)
# TriGorilla Anycubic version 1.3 based on RAMPS EFB
else ifeq ($(HARDWARE_MOTHERBOARD),1134)
# TriGorilla Anycubic version 1.4 based on RAMPS EFB
else ifeq ($(HARDWARE_MOTHERBOARD),1135)
# TriGorilla Anycubic version 1.4 Rev 1.1
else ifeq ($(HARDWARE_MOTHERBOARD),1136)
# Creality: Ender-4, CR-8
else ifeq ($(HARDWARE_MOTHERBOARD),1137)
# Creality: CR10S, CR20, CR-X
else ifeq ($(HARDWARE_MOTHERBOARD),1138)
# Dagoma F5
else ifeq ($(HARDWARE_MOTHERBOARD),1139)
# FYSETC F6 1.3
else ifeq ($(HARDWARE_MOTHERBOARD),1140)
# FYSETC F6 1.5
else ifeq ($(HARDWARE_MOTHERBOARD),1141)
# Duplicator i3 Plus
else ifeq ($(HARDWARE_MOTHERBOARD),1142)
# VORON
else ifeq ($(HARDWARE_MOTHERBOARD),1143)
# TRONXY V3 1.0
else ifeq ($(HARDWARE_MOTHERBOARD),1144)
# Z-Bolt X Series
else ifeq ($(HARDWARE_MOTHERBOARD),1145)
# TT OSCAR
else ifeq ($(HARDWARE_MOTHERBOARD),1146)
# Overlord/Overlord Pro
else ifeq ($(HARDWARE_MOTHERBOARD),1147)
# ADIMLab Gantry v1
else ifeq ($(HARDWARE_MOTHERBOARD),1148)
# ADIMLab Gantry v2
else ifeq ($(HARDWARE_MOTHERBOARD),1149)
# BIQU Tango V1
else ifeq ($(HARDWARE_MOTHERBOARD),1150)
# MKS GEN L V2
else ifeq ($(HARDWARE_MOTHERBOARD),1151)
# MKS GEN L V2.1
else ifeq ($(HARDWARE_MOTHERBOARD),1152)
# Copymaster 3D
else ifeq ($(HARDWARE_MOTHERBOARD),1153)
# Ortur 4
else ifeq ($(HARDWARE_MOTHERBOARD),1154)
# Tenlog D3 Hero
else ifeq ($(HARDWARE_MOTHERBOARD),1155)
# Zonestar zrib V5.3 (Chinese RAMPS replica)
else ifeq ($(HARDWARE_MOTHERBOARD),1162)
# Pxmalion Core I3
else ifeq ($(HARDWARE_MOTHERBOARD),1163)
#
# RAMBo and derivatives

4
Marlin/src/HAL/ESP32/HAL.h

@ -60,8 +60,8 @@
#endif
#endif
#define CRITICAL_SECTION_START() portENTER_CRITICAL(&spinlock)
#define CRITICAL_SECTION_END() portEXIT_CRITICAL(&spinlock)
#define CRITICAL_SECTION_START() portENTER_CRITICAL(&hal.spinlock)
#define CRITICAL_SECTION_END() portEXIT_CRITICAL(&hal.spinlock)
#define HAL_CAN_SET_PWM_FREQ // This HAL supports PWM Frequency adjustment
#define PWM_FREQUENCY 1000u // Default PWM frequency when set_pwm_duty() is called without set_pwm_frequency()

139
Marlin/src/HAL/shared/HAL_spi_L6470.cpp

@ -1,139 +0,0 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
/**
* Software L6470 SPI functions originally from Arduino Sd2Card Library
* Copyright (c) 2009 by William Greiman
*/
#include "../../inc/MarlinConfig.h"
#if HAS_L64XX
#include "Delay.h"
#include "../../core/serial.h"
#include "../../libs/L64XX/L64XX_Marlin.h"
// Make sure GCC optimizes this file.
// Note that this line triggers a bug in GCC which is fixed by casting.
// See the note below.
#pragma GCC optimize (3)
// run at ~4Mhz
inline uint8_t L6470_SpiTransfer_Mode_0(uint8_t b) { // using Mode 0
for (uint8_t bits = 8; bits--;) {
WRITE(L6470_CHAIN_MOSI_PIN, b & 0x80);
b <<= 1; // little setup time
WRITE(L6470_CHAIN_SCK_PIN, HIGH);
DELAY_NS(125); // 10 cycles @ 84mhz
b |= (READ(L6470_CHAIN_MISO_PIN) != 0);
WRITE(L6470_CHAIN_SCK_PIN, LOW);
DELAY_NS(125); // 10 cycles @ 84mhz
}
return b;
}
inline uint8_t L6470_SpiTransfer_Mode_3(uint8_t b) { // using Mode 3
for (uint8_t bits = 8; bits--;) {
WRITE(L6470_CHAIN_SCK_PIN, LOW);
WRITE(L6470_CHAIN_MOSI_PIN, b & 0x80);
DELAY_NS(125); // 10 cycles @ 84mhz
WRITE(L6470_CHAIN_SCK_PIN, HIGH);
DELAY_NS(125); // Need more delay for fast CPUs
b <<= 1; // little setup time
b |= (READ(L6470_CHAIN_MISO_PIN) != 0);
}
DELAY_NS(125); // 10 cycles @ 84mhz
return b;
}
/**
* L64XX methods for SPI init and transfer
*/
void L64XX_Marlin::spi_init() {
OUT_WRITE(L6470_CHAIN_SS_PIN, HIGH);
OUT_WRITE(L6470_CHAIN_SCK_PIN, HIGH);
OUT_WRITE(L6470_CHAIN_MOSI_PIN, HIGH);
SET_INPUT(L6470_CHAIN_MISO_PIN);
#if PIN_EXISTS(L6470_BUSY)
SET_INPUT(L6470_BUSY_PIN);
#endif
OUT_WRITE(L6470_CHAIN_MOSI_PIN, HIGH);
}
uint8_t L64XX_Marlin::transfer_single(uint8_t data, int16_t ss_pin) {
// First device in chain has data sent last
extDigitalWrite(ss_pin, LOW);
hal.isr_off(); // Disable interrupts during SPI transfer (can't allow partial command to chips)
const uint8_t data_out = L6470_SpiTransfer_Mode_3(data);
hal.isr_on(); // Enable interrupts
extDigitalWrite(ss_pin, HIGH);
return data_out;
}
uint8_t L64XX_Marlin::transfer_chain(uint8_t data, int16_t ss_pin, uint8_t chain_position) {
uint8_t data_out = 0;
// first device in chain has data sent last
extDigitalWrite(ss_pin, LOW);
for (uint8_t i = L64XX::chain[0]; !L64xxManager.spi_abort && i >= 1; i--) { // Send data unless aborted
hal.isr_off(); // Disable interrupts during SPI transfer (can't allow partial command to chips)
const uint8_t temp = L6470_SpiTransfer_Mode_3(uint8_t(i == chain_position ? data : dSPIN_NOP));
hal.isr_on(); // Enable interrupts
if (i == chain_position) data_out = temp;
}
extDigitalWrite(ss_pin, HIGH);
return data_out;
}
/**
* Platform-supplied L6470 buffer transfer method
*/
void L64XX_Marlin::transfer(uint8_t L6470_buf[], const uint8_t length) {
// First device in chain has its data sent last
if (spi_active) { // Interrupted SPI transfer so need to
WRITE(L6470_CHAIN_SS_PIN, HIGH); // guarantee min high of 650ns
DELAY_US(1);
}
WRITE(L6470_CHAIN_SS_PIN, LOW);
for (uint8_t i = length; i >= 1; i--)
L6470_SpiTransfer_Mode_3(uint8_t(L6470_buf[i]));
WRITE(L6470_CHAIN_SS_PIN, HIGH);
}
#pragma GCC reset_options
#endif // HAS_L64XX

17
Marlin/src/MarlinCore.cpp

@ -76,8 +76,6 @@
#include "lcd/e3v2/creality/dwin.h"
#elif ENABLED(DWIN_LCD_PROUI)
#include "lcd/e3v2/proui/dwin.h"
#elif ENABLED(DWIN_CREALITY_LCD_JYERSUI)
#include "lcd/e3v2/jyersui/dwin.h"
#endif
#endif
@ -228,10 +226,6 @@
#include "feature/mmu/mmu2.h"
#endif
#if HAS_L64XX
#include "libs/L64XX/L64XX_Marlin.h"
#endif
#if ENABLED(PASSWORD_FEATURE)
#include "feature/password/password.h"
#endif
@ -434,7 +428,7 @@ inline void manage_inactivity(const bool no_stepper_sleep=false) {
if (!has_blocks && !do_reset_timeout && gcode.stepper_inactive_timeout()) {
if (!already_shutdown_steppers) {
already_shutdown_steppers = true; // L6470 SPI will consume 99% of free time without this
already_shutdown_steppers = true;
// Individual axes will be disabled if configured
TERN_(DISABLE_INACTIVE_X, stepper.disable_axis(X_AXIS));
@ -733,8 +727,6 @@ inline void manage_inactivity(const bool no_stepper_sleep=false) {
TERN_(MONITOR_DRIVER_STATUS, monitor_tmc_drivers());
TERN_(MONITOR_L6470_DRIVER_STATUS, L64xxManager.monitor_driver());
// Limit check_axes_activity frequency to 10Hz
static millis_t next_check_axes_ms = 0;
if (ELAPSED(ms, next_check_axes_ms)) {
@ -789,7 +781,7 @@ void idle(bool no_stepper_sleep/*=false*/) {
manage_inactivity(no_stepper_sleep);
// Manage Heaters (and Watchdog)
thermalManager.manage_heater();
thermalManager.task();
// Max7219 heartbeat, animation, etc
TERN_(MAX7219_DEBUG, max7219.idle_tasks());
@ -1064,7 +1056,6 @@ inline void tmc_standby_setup() {
* TMC220x Stepper Drivers (Serial)
* PSU control
* Power-loss Recovery
* L64XX Stepper Drivers (SPI)
* Stepper Driver Reset: DISABLE
* TMC Stepper Drivers (SPI)
* Run hal.init_board() for additional pins setup
@ -1253,10 +1244,6 @@ void setup() {
SETUP_RUN(tmc_init_cs_pins());
#endif
#if HAS_L64XX
SETUP_RUN(L64xxManager.init()); // Set up SPI, init drivers
#endif
#if ENABLED(PSU_CONTROL)
SETUP_LOG("PSU_CONTROL");
powerManager.init();

78
Marlin/src/core/boards.h

@ -343,29 +343,30 @@
#define BOARD_CREALITY_V4 4040 // Creality v4.x (STM32F103RC / STM32F103RE)
#define BOARD_CREALITY_V422 4041 // Creality v4.2.2 (STM32F103RC / STM32F103RE)
#define BOARD_CREALITY_V423 4042 // Creality v4.2.3 (STM32F103RC / STM32F103RE)
#define BOARD_CREALITY_V427 4043 // Creality v4.2.7 (STM32F103RC / STM32F103RE)
#define BOARD_CREALITY_V4210 4044 // Creality v4.2.10 (STM32F103RC / STM32F103RE) as found in the CR-30
#define BOARD_CREALITY_V431 4045 // Creality v4.3.1 (STM32F103RC / STM32F103RE)
#define BOARD_CREALITY_V431_A 4046 // Creality v4.3.1a (STM32F103RC / STM32F103RE)
#define BOARD_CREALITY_V431_B 4047 // Creality v4.3.1b (STM32F103RC / STM32F103RE)
#define BOARD_CREALITY_V431_C 4048 // Creality v4.3.1c (STM32F103RC / STM32F103RE)
#define BOARD_CREALITY_V431_D 4049 // Creality v4.3.1d (STM32F103RC / STM32F103RE)
#define BOARD_CREALITY_V452 4050 // Creality v4.5.2 (STM32F103RC / STM32F103RE)
#define BOARD_CREALITY_V453 4051 // Creality v4.5.3 (STM32F103RC / STM32F103RE)
#define BOARD_CREALITY_V24S1 4052 // Creality v2.4.S1 (STM32F103RC / STM32F103RE) v101 as found in the Ender-7
#define BOARD_CREALITY_V24S1_301 4053 // Creality v2.4.S1_301 (STM32F103RC / STM32F103RE) v301 as found in the Ender-3 S1
#define BOARD_CREALITY_V25S1 4054 // Creality v2.5.S1 (STM32F103RE) as found in the CR-10 Smart Pro
#define BOARD_TRIGORILLA_PRO 4055 // Trigorilla Pro (STM32F103ZE)
#define BOARD_FLY_MINI 4056 // FLYmaker FLY MINI (STM32F103RC)
#define BOARD_FLSUN_HISPEED 4057 // FLSUN HiSpeedV1 (STM32F103VE)
#define BOARD_BEAST 4058 // STM32F103RE Libmaple-based controller
#define BOARD_MINGDA_MPX_ARM_MINI 4059 // STM32F103ZE Mingda MD-16
#define BOARD_GTM32_PRO_VD 4060 // STM32F103VE controller
#define BOARD_ZONESTAR_ZM3E2 4061 // Zonestar ZM3E2 (STM32F103RC)
#define BOARD_ZONESTAR_ZM3E4 4062 // Zonestar ZM3E4 V1 (STM32F103VC)
#define BOARD_ZONESTAR_ZM3E4V2 4063 // Zonestar ZM3E4 V2 (STM32F103VC)
#define BOARD_ERYONE_ERY32_MINI 4064 // Eryone Ery32 mini (STM32F103VE)
#define BOARD_PANDA_PI_V29 4065 // Panda Pi V2.9 - Standalone (STM32F103RC)
#define BOARD_CREALITY_V425 4043 // Creality v4.2.5 (STM32F103RC / STM32F103RE)
#define BOARD_CREALITY_V427 4044 // Creality v4.2.7 (STM32F103RC / STM32F103RE)
#define BOARD_CREALITY_V4210 4045 // Creality v4.2.10 (STM32F103RC / STM32F103RE) as found in the CR-30
#define BOARD_CREALITY_V431 4046 // Creality v4.3.1 (STM32F103RC / STM32F103RE)
#define BOARD_CREALITY_V431_A 4047 // Creality v4.3.1a (STM32F103RC / STM32F103RE)
#define BOARD_CREALITY_V431_B 4048 // Creality v4.3.1b (STM32F103RC / STM32F103RE)
#define BOARD_CREALITY_V431_C 4049 // Creality v4.3.1c (STM32F103RC / STM32F103RE)
#define BOARD_CREALITY_V431_D 4050 // Creality v4.3.1d (STM32F103RC / STM32F103RE)
#define BOARD_CREALITY_V452 4051 // Creality v4.5.2 (STM32F103RC / STM32F103RE)
#define BOARD_CREALITY_V453 4052 // Creality v4.5.3 (STM32F103RC / STM32F103RE)
#define BOARD_CREALITY_V24S1 4053 // Creality v2.4.S1 (STM32F103RC / STM32F103RE) v101 as found in the Ender-7
#define BOARD_CREALITY_V24S1_301 4054 // Creality v2.4.S1_301 (STM32F103RC / STM32F103RE) v301 as found in the Ender-3 S1
#define BOARD_CREALITY_V25S1 4055 // Creality v2.5.S1 (STM32F103RE) as found in the CR-10 Smart Pro
#define BOARD_TRIGORILLA_PRO 4056 // Trigorilla Pro (STM32F103ZE)
#define BOARD_FLY_MINI 4057 // FLYmaker FLY MINI (STM32F103RC)
#define BOARD_FLSUN_HISPEED 4058 // FLSUN HiSpeedV1 (STM32F103VE)
#define BOARD_BEAST 4059 // STM32F103RE Libmaple-based controller
#define BOARD_MINGDA_MPX_ARM_MINI 4060 // STM32F103ZE Mingda MD-16
#define BOARD_GTM32_PRO_VD 4061 // STM32F103VE controller
#define BOARD_ZONESTAR_ZM3E2 4062 // Zonestar ZM3E2 (STM32F103RC)
#define BOARD_ZONESTAR_ZM3E4 4063 // Zonestar ZM3E4 V1 (STM32F103VC)
#define BOARD_ZONESTAR_ZM3E4V2 4064 // Zonestar ZM3E4 V2 (STM32F103VC)
#define BOARD_ERYONE_ERY32_MINI 4065 // Eryone Ery32 mini (STM32F103VE)
#define BOARD_PANDA_PI_V29 4066 // Panda Pi V2.9 - Standalone (STM32F103RC)
//
// ARM Cortex-M4F
@ -385,7 +386,6 @@
#define BOARD_RUMBA32_BTT 4204 // RUMBA32 STM32F446VE based controller from BIGTREETECH
#define BOARD_BLACK_STM32F407VE 4205 // BLACK_STM32F407VE
#define BOARD_BLACK_STM32F407ZE 4206 // BLACK_STM32F407ZE
#define BOARD_STEVAL_3DP001V1 4207 // STEVAL-3DP001V1 3D PRINTER BOARD
#define BOARD_BTT_SKR_PRO_V1_1 4208 // BigTreeTech SKR Pro v1.1 (STM32F407ZG)
#define BOARD_BTT_SKR_PRO_V1_2 4209 // BigTreeTech SKR Pro v1.2 (STM32F407ZG)
#define BOARD_BTT_BTT002_V1_0 4210 // BigTreeTech BTT002 v1.0 (STM32F407VG)
@ -408,17 +408,18 @@
#define BOARD_MKS_ROBIN_PRO_V2 4227 // MKS Robin Pro V2 (STM32F407VE)
#define BOARD_MKS_ROBIN_NANO_V3 4228 // MKS Robin Nano V3 (STM32F407VG)
#define BOARD_MKS_ROBIN_NANO_V3_1 4229 // MKS Robin Nano V3.1 (STM32F407VE)
#define BOARD_MKS_MONSTER8 4230 // MKS Monster8 (STM32F407VG)
#define BOARD_ANET_ET4 4231 // ANET ET4 V1.x (STM32F407VG)
#define BOARD_ANET_ET4P 4232 // ANET ET4P V1.x (STM32F407VG)
#define BOARD_FYSETC_CHEETAH_V20 4233 // FYSETC Cheetah V2.0
#define BOARD_TH3D_EZBOARD_V2 4234 // TH3D EZBoard v2.0
#define BOARD_OPULO_LUMEN_REV3 4235 // Opulo Lumen PnP Controller REV3 (STM32F407VE/VG)
#define BOARD_MKS_ROBIN_NANO_V1_3_F4 4236 // MKS Robin Nano V1.3 and MKS Robin Nano-S V1.3 (STM32F407VE)
#define BOARD_MKS_EAGLE 4237 // MKS Eagle (STM32F407VE)
#define BOARD_ARTILLERY_RUBY 4238 // Artillery Ruby (STM32F401RC)
#define BOARD_FYSETC_SPIDER_V2_2 4239 // FYSETC Spider V2.2 (STM32F446VE)
#define BOARD_CREALITY_V24S1_301F4 4240 // Creality v2.4.S1_301F4 (STM32F401RC) as found in the Ender-3 S1 F4
#define BOARD_MKS_MONSTER8_V1 4230 // MKS Monster8 V1 (STM32F407VG)
#define BOARD_MKS_MONSTER8_V2 4231 // MKS Monster8 V2 (STM32F407VG)
#define BOARD_ANET_ET4 4232 // ANET ET4 V1.x (STM32F407VG)
#define BOARD_ANET_ET4P 4233 // ANET ET4P V1.x (STM32F407VG)
#define BOARD_FYSETC_CHEETAH_V20 4234 // FYSETC Cheetah V2.0
#define BOARD_TH3D_EZBOARD_V2 4235 // TH3D EZBoard v2.0
#define BOARD_OPULO_LUMEN_REV3 4236 // Opulo Lumen PnP Controller REV3 (STM32F407VE/VG)
#define BOARD_MKS_ROBIN_NANO_V1_3_F4 4237 // MKS Robin Nano V1.3 and MKS Robin Nano-S V1.3 (STM32F407VE)
#define BOARD_MKS_EAGLE 4238 // MKS Eagle (STM32F407VE)
#define BOARD_ARTILLERY_RUBY 4239 // Artillery Ruby (STM32F401RC)
#define BOARD_FYSETC_SPIDER_V2_2 4240 // FYSETC Spider V2.2 (STM32F446VE)
#define BOARD_CREALITY_V24S1_301F4 4241 // Creality v2.4.S1_301F4 (STM32F401RC) as found in the Ender-3 S1 F4
//
// ARM Cortex M7
@ -428,9 +429,10 @@
#define BOARD_TEENSY41 5001 // Teensy 4.1
#define BOARD_T41U5XBB 5002 // T41U5XBB Teensy 4.1 breakout board
#define BOARD_NUCLEO_F767ZI 5003 // ST NUCLEO-F767ZI Dev Board
#define BOARD_BTT_SKR_SE_BX 5004 // BigTreeTech SKR SE BX (STM32H743II)
#define BOARD_BTT_SKR_V3_0 5005 // BigTreeTech SKR V3.0 (STM32H743VG)
#define BOARD_BTT_SKR_V3_0_EZ 5006 // BigTreeTech SKR V3.0 EZ (STM32H743VG)
#define BOARD_BTT_SKR_SE_BX_V2 5004 // BigTreeTech SKR SE BX V2.0 (STM32H743II)
#define BOARD_BTT_SKR_SE_BX_V3 5005 // BigTreeTech SKR SE BX V3.0 (STM32H743II)
#define BOARD_BTT_SKR_V3_0 5006 // BigTreeTech SKR V3.0 (STM32H743VG)
#define BOARD_BTT_SKR_V3_0_EZ 5007 // BigTreeTech SKR V3.0 EZ (STM32H743VG)
//
// Espressif ESP32 WiFi

17
Marlin/src/core/drivers.h

@ -30,10 +30,6 @@
#define _A5984 0x5984
#define _DRV8825 0x8825
#define _LV8729 0x8729
#define _L6470 0x6470
#define _L6474 0x6474
#define _L6480 0x6480
#define _POWERSTEP01 0xF00D
#define _TB6560 0x6560
#define _TB6600 0x6600
#define _TMC2100 0x2100
@ -193,16 +189,3 @@
#if HAS_DRIVER(TMC26X)
#define HAS_TMC26X 1
#endif
//
// L64XX Stepper Drivers
//
#if HAS_DRIVER(L6470) || HAS_DRIVER(L6474) || HAS_DRIVER(L6480) || HAS_DRIVER(POWERSTEP01)
#define HAS_L64XX 1
#endif
#if HAS_L64XX && !HAS_DRIVER(L6474)
#define HAS_L64XX_NOT_L6474 1
#endif
#define AXIS_IS_L64XX(A) (AXIS_DRIVER_TYPE_##A(L6470) || AXIS_DRIVER_TYPE_##A(L6474) || AXIS_DRIVER_TYPE_##A(L6480) || AXIS_DRIVER_TYPE_##A(POWERSTEP01))

4
Marlin/src/core/language.h

@ -227,10 +227,6 @@
#define STR_PID_DEBUG " PID_DEBUG "
#define STR_PID_DEBUG_INPUT ": Input "
#define STR_PID_DEBUG_OUTPUT " Output "
#define STR_PID_DEBUG_PTERM " pTerm "
#define STR_PID_DEBUG_ITERM " iTerm "
#define STR_PID_DEBUG_DTERM " dTerm "
#define STR_PID_DEBUG_CTERM " cTerm "
#define STR_INVALID_EXTRUDER_NUM " - Invalid extruder number !"
#define STR_MPC_AUTOTUNE "MPC Autotune"
#define STR_MPC_AUTOTUNE_START " start for " STR_E

4
Marlin/src/core/utility.cpp

@ -29,10 +29,10 @@ void safe_delay(millis_t ms) {
while (ms > 50) {
ms -= 50;
delay(50);
thermalManager.manage_heater();
thermalManager.task();
}
delay(ms);
thermalManager.manage_heater(); // This keeps us safe if too many small safe_delay() calls are made
thermalManager.task(); // This keeps us safe if too many small safe_delay() calls are made
}
// A delay to provide brittle hosts time to receive bytes

17
Marlin/src/feature/bedlevel/ubl/ubl_motion.cpp

@ -374,11 +374,12 @@
#endif
NOLESS(segments, 1U); // Must have at least one segment
const float inv_segments = 1.0f / segments, // Reciprocal to save calculation
segment_xyz_mm = SQRT(cart_xy_mm_2 + sq(total.z)) * inv_segments; // Length of each segment
const float inv_segments = 1.0f / segments; // Reciprocal to save calculation
// Add hints to help optimize the move
PlannerHints hints(SQRT(cart_xy_mm_2 + sq(total.z)) * inv_segments); // Length of each segment
#if ENABLED(SCARA_FEEDRATE_SCALING)
const float inv_duration = scaled_fr_mm_s / segment_xyz_mm;
hints.inv_duration = scaled_fr_mm_s / hints.millimeters;
#endif
xyze_float_t diff = total * inv_segments;
@ -392,13 +393,9 @@
if (!planner.leveling_active || !planner.leveling_active_at_z(destination.z)) {
while (--segments) {
raw += diff;
planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, segment_xyz_mm
OPTARG(SCARA_FEEDRATE_SCALING, inv_duration)
);
planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, hints);
}
planner.buffer_line(destination, scaled_fr_mm_s, active_extruder, segment_xyz_mm
OPTARG(SCARA_FEEDRATE_SCALING, inv_duration)
);
planner.buffer_line(destination, scaled_fr_mm_s, active_extruder, hints);
return false; // Did not set current from destination
}
@ -467,7 +464,7 @@
TERN_(ENABLE_LEVELING_FADE_HEIGHT, * fade_scaling_factor); // apply fade factor to interpolated height
const float oldz = raw.z; raw.z += z_cxcy;
planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, segment_xyz_mm OPTARG(SCARA_FEEDRATE_SCALING, inv_duration) );
planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, hints);
raw.z = oldz;
if (segments == 0) // done with last segment

3
Marlin/src/feature/joystick.cpp

@ -172,8 +172,9 @@ Joystick joystick;
current_position += move_dist;
apply_motion_limits(current_position);
const float length = sqrt(hypot2);
PlannerHints hints(length);
injecting_now = true;
planner.buffer_line(current_position, length / seg_time, active_extruder, length);
planner.buffer_line(current_position, length / seg_time, active_extruder, hints);
injecting_now = false;
}
}

88
Marlin/src/feature/spindle_laser.cpp

@ -39,18 +39,26 @@
#endif
SpindleLaser cutter;
uint8_t SpindleLaser::power,
bool SpindleLaser::enable_state; // Virtual enable state, controls enable pin if present and or apply power if > 0
uint8_t SpindleLaser::power, // Actual power output 0-255 ocr or "0 = off" > 0 = "on"
SpindleLaser::last_power_applied; // = 0 // Basic power state tracking
#if ENABLED(LASER_FEATURE)
cutter_test_pulse_t SpindleLaser::testPulse = 50; // Test fire Pulse time ms value.
cutter_test_pulse_t SpindleLaser::testPulse = 50; // (ms) Test fire pulse default duration
uint8_t SpindleLaser::last_block_power; // = 0 // Track power changes for dynamic inline power
feedRate_t SpindleLaser::feedrate_mm_m = 1500,
SpindleLaser::last_feedrate_mm_m; // = 0 // (mm/min) Track feedrate changes for dynamic power
#endif
bool SpindleLaser::isReady; // Ready to apply power setting from the UI to OCR
cutter_power_t SpindleLaser::menuPower, // Power set via LCD menu in PWM, PERCENT, or RPM
SpindleLaser::unitPower; // LCD status power in PWM, PERCENT, or RPM
#if ENABLED(MARLIN_DEV_MODE)
cutter_frequency_t SpindleLaser::frequency; // PWM frequency setting; range: 2K - 50K
#endif
bool SpindleLaser::isReadyForUI = false; // Ready to apply power setting from the UI to OCR
CutterMode SpindleLaser::cutter_mode = CUTTER_MODE_STANDARD; // Default is standard mode
constexpr cutter_cpower_t SpindleLaser::power_floor;
cutter_power_t SpindleLaser::menuPower = 0, // Power value via LCD menu in PWM, PERCENT, or RPM based on configured format set by CUTTER_POWER_UNIT.
SpindleLaser::unitPower = 0; // Unit power is in PWM, PERCENT, or RPM based on CUTTER_POWER_UNIT.
cutter_frequency_t SpindleLaser::frequency; // PWM frequency setting; range: 2K - 50K
#define SPINDLE_LASER_PWM_OFF TERN(SPINDLE_LASER_PWM_INVERT, 255, 0)
/**
@ -65,14 +73,14 @@ void SpindleLaser::init() {
#if ENABLED(SPINDLE_CHANGE_DIR)
OUT_WRITE(SPINDLE_DIR_PIN, SPINDLE_INVERT_DIR); // Init rotation to clockwise (M3)
#endif
#if ENABLED(HAL_CAN_SET_PWM_FREQ) && SPINDLE_LASER_FREQUENCY
frequency = SPINDLE_LASER_FREQUENCY;
hal.set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), SPINDLE_LASER_FREQUENCY);
#endif
#if ENABLED(SPINDLE_LASER_USE_PWM)
SET_PWM(SPINDLE_LASER_PWM_PIN);
hal.set_pwm_duty(pin_t(SPINDLE_LASER_PWM_PIN), SPINDLE_LASER_PWM_OFF); // Set to lowest speed
#endif
#if ENABLED(HAL_CAN_SET_PWM_FREQ) && SPINDLE_LASER_FREQUENCY
hal.set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), SPINDLE_LASER_FREQUENCY);
TERN_(MARLIN_DEV_MODE, frequency = SPINDLE_LASER_FREQUENCY);
#endif
#if ENABLED(AIR_EVACUATION)
OUT_WRITE(AIR_EVACUATION_PIN, !AIR_EVACUATION_ACTIVE); // Init Vacuum/Blower OFF
#endif
@ -90,7 +98,7 @@ void SpindleLaser::init() {
*/
void SpindleLaser::_set_ocr(const uint8_t ocr) {
#if ENABLED(HAL_CAN_SET_PWM_FREQ) && SPINDLE_LASER_FREQUENCY
hal.set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), TERN(MARLIN_DEV_MODE, frequency, SPINDLE_LASER_FREQUENCY));
hal.set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), frequency);
#endif
hal.set_pwm_duty(pin_t(SPINDLE_LASER_PWM_PIN), ocr ^ SPINDLE_LASER_PWM_OFF);
}
@ -107,35 +115,41 @@ void SpindleLaser::init() {
#endif // SPINDLE_LASER_USE_PWM
/**
* Apply power for laser/spindle
* Apply power for Laser or Spindle
*
* Apply cutter power value for PWM, Servo, and on/off pin.
*
* @param opwr Power value. Range 0 to MAX. When 0 disable spindle/laser.
* @param opwr Power value. Range 0 to MAX.
*/
void SpindleLaser::apply_power(const uint8_t opwr) {
if (opwr == last_power_applied) return;
last_power_applied = opwr;
power = opwr;
#if ENABLED(SPINDLE_LASER_USE_PWM)
if (cutter.unitPower == 0 && CUTTER_UNIT_IS(RPM)) {
ocr_off();
isReady = false;
}
else if (ENABLED(CUTTER_POWER_RELATIVE) || enabled()) {
set_ocr(power);
isReady = true;
}
else {
ocr_off();
isReady = false;
}
#elif ENABLED(SPINDLE_SERVO)
servo[SPINDLE_SERVO_NR].move(power);
#else
WRITE(SPINDLE_LASER_ENA_PIN, enabled() ? SPINDLE_LASER_ACTIVE_STATE : !SPINDLE_LASER_ACTIVE_STATE);
isReady = true;
#endif
if (enabled() || opwr == 0) { // 0 check allows us to disable where no ENA pin exists
// Test and set the last power used to improve performance
if (opwr == last_power_applied) return;
last_power_applied = opwr;
// Handle PWM driven or just simple on/off
#if ENABLED(SPINDLE_LASER_USE_PWM)
if (CUTTER_UNIT_IS(RPM) && unitPower == 0)
ocr_off();
else if (ENABLED(CUTTER_POWER_RELATIVE) || enabled() || opwr == 0) {
set_ocr(opwr);
isReadyForUI = true;
}
else
ocr_off();
#elif ENABLED(SPINDLE_SERVO)
MOVE_SERVO(SPINDLE_SERVO_NR, power);
#else
WRITE(SPINDLE_LASER_ENA_PIN, enabled() ? SPINDLE_LASER_ACTIVE_STATE : !SPINDLE_LASER_ACTIVE_STATE);
isReadyForUI = true;
#endif
}
else {
#if PIN_EXISTS(SPINDLE_LASER_ENA)
WRITE(SPINDLE_LASER_ENA_PIN, !SPINDLE_LASER_ACTIVE_STATE);
#endif
isReadyForUI = false; // Only used for UI display updates.
TERN_(SPINDLE_LASER_USE_PWM, ocr_off());
}
}
#if ENABLED(SPINDLE_CHANGE_DIR)

333
Marlin/src/feature/spindle_laser.h

@ -34,85 +34,98 @@
#include "../libs/buzzer.h"
#endif
#if ENABLED(LASER_POWER_INLINE)
#include "../module/planner.h"
#endif
// Inline laser power
#include "../module/planner.h"
#define PCT_TO_PWM(X) ((X) * 255 / 100)
#define PCT_TO_SERVO(X) ((X) * 180 / 100)
// Laser/Cutter operation mode
enum CutterMode : int8_t {
CUTTER_MODE_ERROR = -1,
CUTTER_MODE_STANDARD, // M3 power is applied directly and waits for planner moves to sync.
CUTTER_MODE_CONTINUOUS, // M3 or G1/2/3 move power is controlled within planner blocks, set with 'M3 I', cleared with 'M5 I'.
CUTTER_MODE_DYNAMIC // M4 laser power is proportional to the feed rate, set with 'M4 I', cleared with 'M5 I'.
};
class SpindleLaser {
public:
static const inline uint8_t pct_to_ocr(const_float_t pct) { return uint8_t(PCT_TO_PWM(pct)); }
static CutterMode cutter_mode;
// cpower = configured values (e.g., SPEED_POWER_MAX)
static constexpr uint8_t pct_to_ocr(const_float_t pct) { return uint8_t(PCT_TO_PWM(pct)); }
// cpower = configured values (e.g., SPEED_POWER_MAX)
// Convert configured power range to a percentage
static const inline uint8_t cpwr_to_pct(const cutter_cpower_t cpwr) {
constexpr cutter_cpower_t power_floor = TERN(CUTTER_POWER_RELATIVE, SPEED_POWER_MIN, 0),
power_range = SPEED_POWER_MAX - power_floor;
return cpwr ? round(100.0f * (cpwr - power_floor) / power_range) : 0;
static constexpr cutter_cpower_t power_floor = TERN(CUTTER_POWER_RELATIVE, SPEED_POWER_MIN, 0);
static constexpr uint8_t cpwr_to_pct(const cutter_cpower_t cpwr) {
return cpwr ? round(100.0f * (cpwr - power_floor) / (SPEED_POWER_MAX - power_floor)) : 0;
}
// Convert a cpower (e.g., SPEED_POWER_STARTUP) to unit power (upwr, upower),
// which can be PWM, Percent, Servo angle, or RPM (rel/abs).
static const inline cutter_power_t cpwr_to_upwr(const cutter_cpower_t cpwr) { // STARTUP power to Unit power
const cutter_power_t upwr = (
// Convert config defines from RPM to %, angle or PWM when in Spindle mode
// and convert from PERCENT to PWM when in Laser mode
static constexpr cutter_power_t cpwr_to_upwr(const cutter_cpower_t cpwr) { // STARTUP power to Unit power
return (
#if ENABLED(SPINDLE_FEATURE)
// Spindle configured values are in RPM
// Spindle configured define values are in RPM
#if CUTTER_UNIT_IS(RPM)
cpwr // to RPM
#elif CUTTER_UNIT_IS(PERCENT) // to PCT
cpwr_to_pct(cpwr)
#elif CUTTER_UNIT_IS(SERVO) // to SERVO angle
PCT_TO_SERVO(cpwr_to_pct(cpwr))
#else // to PWM
PCT_TO_PWM(cpwr_to_pct(cpwr))
cpwr // to same
#elif CUTTER_UNIT_IS(PERCENT)
cpwr_to_pct(cpwr) // to Percent
#elif CUTTER_UNIT_IS(SERVO)
PCT_TO_SERVO(cpwr_to_pct(cpwr)) // to SERVO angle
#else
PCT_TO_PWM(cpwr_to_pct(cpwr)) // to PWM
#endif
#else
// Laser configured values are in PCT
// Laser configured define values are in Percent
#if CUTTER_UNIT_IS(PWM255)
PCT_TO_PWM(cpwr)
PCT_TO_PWM(cpwr) // to PWM
#else
cpwr // to RPM/PCT
cpwr // to same
#endif
#endif
);
return upwr;
}
static const cutter_power_t mpower_min() { return cpwr_to_upwr(SPEED_POWER_MIN); }
static const cutter_power_t mpower_max() { return cpwr_to_upwr(SPEED_POWER_MAX); }
static constexpr cutter_power_t mpower_min() { return cpwr_to_upwr(SPEED_POWER_MIN); }
static constexpr cutter_power_t mpower_max() { return cpwr_to_upwr(SPEED_POWER_MAX); }
#if ENABLED(LASER_FEATURE)
static cutter_test_pulse_t testPulse; // Test fire Pulse ms value
static cutter_test_pulse_t testPulse; // (ms) Test fire pulse duration
static uint8_t last_block_power; // Track power changes for dynamic power
static feedRate_t feedrate_mm_m, last_feedrate_mm_m; // (mm/min) Track feedrate changes for dynamic power
static bool laser_feedrate_changed() {
const bool changed = last_feedrate_mm_m != feedrate_mm_m;
if (changed) last_feedrate_mm_m = feedrate_mm_m;
return changed;
}
#endif
static bool isReady; // Ready to apply power setting from the UI to OCR
static bool isReadyForUI; // Ready to apply power setting from the UI to OCR
static bool enable_state;
static uint8_t power,
last_power_applied; // Basic power state tracking
#if ENABLED(MARLIN_DEV_MODE)
static cutter_frequency_t frequency; // Set PWM frequency; range: 2K-50K
#endif
static cutter_frequency_t frequency; // Set PWM frequency; range: 2K-50K
static cutter_power_t menuPower, // Power as set via LCD menu in PWM, Percentage or RPM
unitPower; // Power as displayed status in PWM, Percentage or RPM
static void init();
#if ENABLED(MARLIN_DEV_MODE)
#if ENABLED(HAL_CAN_SET_PWM_FREQ) && SPINDLE_LASER_FREQUENCY
static void refresh_frequency() { hal.set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), frequency); }
#endif
// Modifying this function should update everywhere
static bool enabled(const cutter_power_t opwr) { return opwr > 0; }
static bool enabled() { return enabled(power); }
static bool enabled() { return enable_state; }
static void apply_power(const uint8_t inpow);
FORCE_INLINE static void refresh() { apply_power(power); }
FORCE_INLINE static void set_power(const uint8_t upwr) { power = upwr; refresh(); }
#if ENABLED(SPINDLE_LASER_USE_PWM)
@ -123,7 +136,6 @@ public:
public:
static void set_ocr(const uint8_t ocr);
static void ocr_set_power(const uint8_t ocr) { power = ocr; set_ocr(ocr); }
static void ocr_off();
/**
@ -141,78 +153,76 @@ public:
);
}
/**
* Correct power to configured range
*/
static cutter_power_t power_to_range(const cutter_power_t pwr) {
return power_to_range(pwr, _CUTTER_POWER(CUTTER_POWER_UNIT));
}
static cutter_power_t power_to_range(const cutter_power_t pwr, const uint8_t pwrUnit) {
static constexpr float
min_pct = TERN(CUTTER_POWER_RELATIVE, 0, TERN(SPINDLE_FEATURE, round(100.0f * (SPEED_POWER_MIN) / (SPEED_POWER_MAX)), SPEED_POWER_MIN)),
max_pct = TERN(SPINDLE_FEATURE, 100, SPEED_POWER_MAX);
if (pwr <= 0) return 0;
cutter_power_t upwr;
switch (pwrUnit) {
case _CUTTER_POWER_PWM255:
upwr = cutter_power_t(
(pwr < pct_to_ocr(min_pct)) ? pct_to_ocr(min_pct) // Use minimum if set below
: (pwr > pct_to_ocr(max_pct)) ? pct_to_ocr(max_pct) // Use maximum if set above
: pwr
);
break;
case _CUTTER_POWER_PERCENT:
upwr = cutter_power_t(
(pwr < min_pct) ? min_pct // Use minimum if set below
: (pwr > max_pct) ? max_pct // Use maximum if set above
: pwr // PCT
);
break;
case _CUTTER_POWER_RPM:
upwr = cutter_power_t(
(pwr < SPEED_POWER_MIN) ? SPEED_POWER_MIN // Use minimum if set below
: (pwr > SPEED_POWER_MAX) ? SPEED_POWER_MAX // Use maximum if set above
: pwr // Calculate OCR value
);
break;
default: break;
}
return upwr;
}
#endif // SPINDLE_LASER_USE_PWM
/**
* Enable/Disable spindle/laser
* @param enable true = enable; false = disable
* Correct power to configured range
*/
static void set_enabled(const bool enable) {
uint8_t value = 0;
if (enable) {
#if ENABLED(SPINDLE_LASER_USE_PWM)
if (power)
value = power;
else if (unitPower)
value = upower_to_ocr(cpwr_to_upwr(SPEED_POWER_STARTUP));
#else
value = 255;
#endif
static cutter_power_t power_to_range(const cutter_power_t pwr, const uint8_t pwrUnit=_CUTTER_POWER(CUTTER_POWER_UNIT)) {
static constexpr float
min_pct = TERN(CUTTER_POWER_RELATIVE, 0, TERN(SPINDLE_FEATURE, round(100.0f * (SPEED_POWER_MIN) / (SPEED_POWER_MAX)), SPEED_POWER_MIN)),
max_pct = TERN(SPINDLE_FEATURE, 100, SPEED_POWER_MAX);
if (pwr <= 0) return 0;
cutter_power_t upwr;
switch (pwrUnit) {
case _CUTTER_POWER_PWM255: { // PWM
const uint8_t pmin = pct_to_ocr(min_pct), pmax = pct_to_ocr(max_pct);
upwr = cutter_power_t(constrain(pwr, pmin, pmax));
} break;
case _CUTTER_POWER_PERCENT: // Percent
upwr = cutter_power_t(constrain(pwr, min_pct, max_pct));
break;
case _CUTTER_POWER_RPM: // Calculate OCR value
upwr = cutter_power_t(constrain(pwr, SPEED_POWER_MIN, SPEED_POWER_MAX));
break;
default: break;
}
set_power(value);
return upwr;
}
static void disable() { isReady = false; set_enabled(false); }
/**
* Wait for spindle to spin up or spin down
* Enable Laser or Spindle output.
* It's important to prevent changing the power output value during inline cutter operation.
* Inline power is adjusted in the planner to support LASER_TRAP_POWER and CUTTER_MODE_DYNAMIC mode.
*
* This method accepts one of the following control states:
*
* - For CUTTER_MODE_STANDARD the cutter power is either full on/off or ocr-based and it will apply
* SPEED_POWER_STARTUP if no value is assigned.
*
* @param on true = state to on; false = state to off.
* - For CUTTER_MODE_CONTINUOUS inline and power remains where last set and the cutter output enable flag is set.
*
* - CUTTER_MODE_DYNAMIC is also inline-based and it just sets the enable output flag.
*
* - For CUTTER_MODE_ERROR set the output enable_state flag directly and set power to 0 for any mode.
* This mode allows a global power shutdown action to occur.
*/
static void power_delay(const bool on) {
#if DISABLED(LASER_POWER_INLINE)
safe_delay(on ? SPINDLE_LASER_POWERUP_DELAY : SPINDLE_LASER_POWERDOWN_DELAY);
static void set_enabled(const bool enable) {
switch (cutter_mode) {
case CUTTER_MODE_STANDARD:
apply_power(enable ? TERN(SPINDLE_LASER_USE_PWM, (power ?: (unitPower ? upower_to_ocr(cpwr_to_upwr(SPEED_POWER_STARTUP)) : 0)), 255) : 0);
break;
case CUTTER_MODE_CONTINUOUS:
TERN_(LASER_FEATURE, set_inline_enabled(enable));
break;
case CUTTER_MODE_DYNAMIC:
TERN_(LASER_FEATURE, set_inline_enabled(enable));
break;
case CUTTER_MODE_ERROR: // Error mode, no enable and kill power.
enable_state = false;
apply_power(0);
}
#if SPINDLE_LASER_ENA_PIN
WRITE(SPINDLE_LASER_ENA_PIN, enable ? SPINDLE_LASER_ACTIVE_STATE : !SPINDLE_LASER_ACTIVE_STATE);
#endif
enable_state = enable;
}
static void disable() { isReadyForUI = false; set_enabled(false); }
// Wait for spindle/laser to startup or shutdown
static void power_delay(const bool on) {
safe_delay(on ? SPINDLE_LASER_POWERUP_DELAY : SPINDLE_LASER_POWERDOWN_DELAY);
}
#if ENABLED(SPINDLE_CHANGE_DIR)
@ -224,47 +234,60 @@ public:
#endif
#if ENABLED(AIR_EVACUATION)
static void air_evac_enable(); // Turn On Cutter Vacuum or Laser Blower motor
static void air_evac_disable(); // Turn Off Cutter Vacuum or Laser Blower motor
static void air_evac_toggle(); // Toggle Cutter Vacuum or Laser Blower motor
static bool air_evac_state() { // Get current state
static void air_evac_enable(); // Turn On Cutter Vacuum or Laser Blower motor
static void air_evac_disable(); // Turn Off Cutter Vacuum or Laser Blower motor
static void air_evac_toggle(); // Toggle Cutter Vacuum or Laser Blower motor
static bool air_evac_state() { // Get current state
return (READ(AIR_EVACUATION_PIN) == AIR_EVACUATION_ACTIVE);
}
#endif
#if ENABLED(AIR_ASSIST)
static void air_assist_enable(); // Turn on air assist
static void air_assist_disable(); // Turn off air assist
static void air_assist_toggle(); // Toggle air assist
static bool air_assist_state() { // Get current state
static void air_assist_enable(); // Turn on air assist
static void air_assist_disable(); // Turn off air assist
static void air_assist_toggle(); // Toggle air assist
static bool air_assist_state() { // Get current state
return (READ(AIR_ASSIST_PIN) == AIR_ASSIST_ACTIVE);
}
#endif
#if HAS_MARLINUI_MENU
static void enable_with_dir(const bool reverse) {
isReady = true;
const uint8_t ocr = TERN(SPINDLE_LASER_USE_PWM, upower_to_ocr(menuPower), 255);
if (menuPower)
power = ocr;
else
menuPower = cpwr_to_upwr(SPEED_POWER_STARTUP);
unitPower = menuPower;
set_reverse(reverse);
set_enabled(true);
}
FORCE_INLINE static void enable_forward() { enable_with_dir(false); }
FORCE_INLINE static void enable_reverse() { enable_with_dir(true); }
FORCE_INLINE static void enable_same_dir() { enable_with_dir(is_reverse()); }
#if ENABLED(SPINDLE_FEATURE)
static void enable_with_dir(const bool reverse) {
isReadyForUI = true;
const uint8_t ocr = TERN(SPINDLE_LASER_USE_PWM, upower_to_ocr(menuPower), 255);
if (menuPower)
power = ocr;
else
menuPower = cpwr_to_upwr(SPEED_POWER_STARTUP);
unitPower = menuPower;
set_reverse(reverse);
set_enabled(true);
}
FORCE_INLINE static void enable_forward() { enable_with_dir(false); }
FORCE_INLINE static void enable_reverse() { enable_with_dir(true); }
FORCE_INLINE static void enable_same_dir() { enable_with_dir(is_reverse()); }
#endif // SPINDLE_FEATURE
#if ENABLED(SPINDLE_LASER_USE_PWM)
static void update_from_mpower() {
if (isReady) power = upower_to_ocr(menuPower);
if (isReadyForUI) power = upower_to_ocr(menuPower);
unitPower = menuPower;
}
#endif
#if ENABLED(LASER_FEATURE)
// Toggle the laser on/off with menuPower. Apply SPEED_POWER_STARTUP if it was 0 on entry.
static void laser_menu_toggle(const bool state) {
set_enabled(state);
if (state) {
if (!menuPower) menuPower = cpwr_to_upwr(SPEED_POWER_STARTUP);
power = upower_to_ocr(menuPower);
apply_power(power);
}
}
/**
* Test fire the laser using the testPulse ms duration
* Also fires with any PWM power that was previous set
@ -272,74 +295,36 @@ public:
*/
static void test_fire_pulse() {
TERN_(HAS_BEEPER, buzzer.tone(30, 3000));
enable_forward(); // Turn Laser on (Spindle speak but same funct)
cutter_mode = CUTTER_MODE_STANDARD;// Menu needs standard mode.
laser_menu_toggle(true); // Laser On
delay(testPulse); // Delay for time set by user in pulse ms menu screen.
disable(); // Turn laser off
laser_menu_toggle(false); // Laser Off
}
#endif
#endif // LASER_FEATURE
#endif // HAS_MARLINUI_MENU
#if ENABLED(LASER_POWER_INLINE)
/**
* Inline power adds extra fields to the planner block
* to handle laser power and scale to movement speed.
*/
#if ENABLED(LASER_FEATURE)
// Force disengage planner power control
static void inline_disable() {
isReady = false;
unitPower = 0;
planner.laser_inline.status.isPlanned = false;
planner.laser_inline.status.isEnabled = false;
planner.laser_inline.power = 0;
// Dynamic mode rate calculation
static uint8_t calc_dynamic_power() {
if (feedrate_mm_m > 65535) return 255; // Too fast, go always on
uint16_t rate = uint16_t(feedrate_mm_m); // 16 bits from the G-code parser float input
rate >>= 8; // Take the G-code input e.g. F40000 and shift off the lower bits to get an OCR value from 1-255
return uint8_t(rate);
}
// Inline modes of all other functions; all enable planner inline power control
static void set_inline_enabled(const bool enable) {
if (enable)
inline_power(255);
else {
isReady = false;
unitPower = menuPower = 0;
planner.laser_inline.status.isPlanned = false;
TERN(SPINDLE_LASER_USE_PWM, inline_ocr_power, inline_power)(0);
}
}
static void set_inline_enabled(const bool enable) { planner.laser_inline.status.isEnabled = enable;}
// Set the power for subsequent movement blocks
static void inline_power(const cutter_power_t upwr) {
unitPower = menuPower = upwr;
#if ENABLED(SPINDLE_LASER_USE_PWM)
#if ENABLED(SPEED_POWER_RELATIVE) && !CUTTER_UNIT_IS(RPM) // relative mode does not turn laser off at 0, except for RPM
planner.laser_inline.status.isEnabled = true;
planner.laser_inline.power = upower_to_ocr(upwr);
isReady = true;
#else
inline_ocr_power(upower_to_ocr(upwr));
#endif
#else
planner.laser_inline.status.isEnabled = enabled(upwr);
planner.laser_inline.power = upwr;
isReady = enabled(upwr);
#endif
static void inline_power(const cutter_power_t cpwr) {
TERN(SPINDLE_LASER_USE_PWM, power = planner.laser_inline.power = cpwr, planner.laser_inline.power = cpwr > 0 ? 255 : 0);
}
static void inline_direction(const bool) { /* never */ }
#if ENABLED(SPINDLE_LASER_USE_PWM)
static void inline_ocr_power(const uint8_t ocrpwr) {
isReady = ocrpwr > 0;
planner.laser_inline.status.isEnabled = ocrpwr > 0;
planner.laser_inline.power = ocrpwr;
}
#endif
#endif // LASER_POWER_INLINE
#endif // LASER_FEATURE
static void kill() {
TERN_(LASER_POWER_INLINE, inline_disable());
disable();
}
static void kill() { disable(); }
};
extern SpindleLaser cutter;

6
Marlin/src/feature/spindle_laser_types.h

@ -74,12 +74,10 @@ typedef IF<(SPEED_POWER_MAX > 255), uint16_t, uint8_t>::type cutter_cpower_t;
#endif
#endif
typedef uint16_t cutter_frequency_t;
#if ENABLED(LASER_FEATURE)
typedef uint16_t cutter_test_pulse_t;
#define CUTTER_MENU_PULSE_TYPE uint16_3
#endif
#if ENABLED(MARLIN_DEV_MODE)
typedef uint16_t cutter_frequency_t;
#define CUTTER_MENU_FREQUENCY_TYPE uint16_5
#endif

29
Marlin/src/gcode/calibrate/G28.cpp

@ -55,11 +55,7 @@
#include "../../lcd/e3v2/proui/dwin.h"
#endif
#if HAS_L64XX // set L6470 absolute position registers to counts
#include "../../libs/L64XX/L64XX_Marlin.h"
#endif
#if ENABLED(LASER_MOVE_G28_OFF)
#if ENABLED(LASER_FEATURE)
#include "../../feature/spindle_laser.h"
#endif
@ -205,7 +201,12 @@ void GcodeSuite::G28() {
DEBUG_SECTION(log_G28, "G28", DEBUGGING(LEVELING));
if (DEBUGGING(LEVELING)) log_machine_info();
TERN_(LASER_MOVE_G28_OFF, cutter.set_inline_enabled(false)); // turn off laser
/*
* Set the laser power to false to stop the planner from processing the current power setting.
*/
#if ENABLED(LASER_FEATURE)
planner.laser_inline.status.isPowered = false;
#endif
#if ENABLED(DUAL_X_CARRIAGE)
bool IDEX_saved_duplication_state = extruder_duplication_enabled;
@ -596,20 +597,4 @@ void GcodeSuite::G28() {
TERN_(FULL_REPORT_TO_HOST_FEATURE, set_and_report_grblstate(old_grblstate));
#if HAS_L64XX
// Set L6470 absolute position registers to counts
// constexpr *might* move this to PROGMEM.
// If not, this will need a PROGMEM directive and an accessor.
#define _EN_ITEM(N) , E_AXIS
static constexpr AxisEnum L64XX_axis_xref[MAX_L64XX] = {
NUM_AXIS_LIST(X_AXIS, Y_AXIS, Z_AXIS, I_AXIS, J_AXIS, K_AXIS, U_AXIS, V_AXIS, W_AXIS),
X_AXIS, Y_AXIS, Z_AXIS, Z_AXIS, Z_AXIS
REPEAT(E_STEPPERS, _EN_ITEM)
};
#undef _EN_ITEM
for (uint8_t j = 1; j <= L64XX::chain[0]; j++) {
const uint8_t cv = L64XX::chain[j];
L64xxManager.set_param((L64XX_axis_t)cv, L6470_ABS_POS, stepper.position(L64XX_axis_xref[cv]));
}
#endif
}

134
Marlin/src/gcode/control/M3-M5.cpp

@ -31,17 +31,27 @@
/**
* Laser:
* M3 - Laser ON/Power (Ramped power)
* M4 - Laser ON/Power (Continuous power)
* M4 - Laser ON/Power (Ramped power)
* M5 - Set power output to 0 (leaving inline mode unchanged).
*
* M3I - Enable continuous inline power to be processed by the planner, with power
* calculated and set in the planner blocks, processed inline during stepping.
* Within inline mode M3 S-Values will set the power for the next moves e.g. G1 X10 Y10 powers on with the last S-Value.
* M3I must be set before using planner-synced M3 inline S-Values (LASER_POWER_SYNC).
*
* M4I - Set dynamic mode which calculates laser power OCR based on the current feedrate.
*
* M5I - Clear inline mode and set power to 0.
*
* Spindle:
* M3 - Spindle ON (Clockwise)
* M4 - Spindle ON (Counter-clockwise)
* M5 - Spindle OFF
*
* Parameters:
* S<power> - Set power. S0 will turn the spindle/laser off, except in relative mode.
* O<ocr> - Set power and OCR (oscillator count register)
* S<power> - Set power. S0 will turn the spindle/laser off.
*
* If no PWM pin is defined then M3/M4 just turns it on.
* If no PWM pin is defined then M3/M4 just turns it on or off.
*
* At least 12.8kHz (50Hz * 256) is needed for Spindle PWM.
* Hardware PWM is required on AVR. ISRs are too slow.
@ -70,77 +80,77 @@ void GcodeSuite::M3_M4(const bool is_M4) {
reset_stepper_timeout(); // Reset timeout to allow subsequent G-code to power the laser (imm.)
#endif
#if EITHER(SPINDLE_LASER_USE_PWM, SPINDLE_SERVO)
auto get_s_power = [] {
if (parser.seenval('S')) {
const float spwr = parser.value_float();
#if ENABLED(SPINDLE_SERVO)
cutter.unitPower = spwr;
#else
cutter.unitPower = TERN(SPINDLE_LASER_USE_PWM,
cutter.power_to_range(cutter_power_t(round(spwr))),
spwr > 0 ? 255 : 0);
#endif
}
else
cutter.unitPower = cutter.cpwr_to_upwr(SPEED_POWER_STARTUP);
return cutter.unitPower;
};
#endif
if (cutter.cutter_mode == CUTTER_MODE_STANDARD)
planner.synchronize(); // Wait for previous movement commands (G0/G1/G2/G3) to complete before changing power
#if ENABLED(LASER_POWER_INLINE)
if (parser.seen('I') == DISABLED(LASER_POWER_INLINE_INVERT)) {
// Laser power in inline mode
cutter.inline_direction(is_M4); // Should always be unused
#if ENABLED(SPINDLE_LASER_USE_PWM)
if (parser.seenval('O')) {
cutter.unitPower = cutter.power_to_range(parser.value_byte(), 0);
cutter.inline_ocr_power(cutter.unitPower); // The OCR is a value from 0 to 255 (uint8_t)
}
else
cutter.inline_power(cutter.upower_to_ocr(get_s_power()));
#else
cutter.set_inline_enabled(true);
#endif
return;
#if ENABLED(LASER_FEATURE)
if (parser.seen_test('I')) {
cutter.cutter_mode = is_M4 ? CUTTER_MODE_DYNAMIC : CUTTER_MODE_CONTINUOUS;
cutter.inline_power(0);
cutter.set_enabled(true);
}
// Non-inline, standard case
cutter.inline_disable(); // Prevent future blocks re-setting the power
#endif
planner.synchronize(); // Wait for previous movement commands (G0/G0/G2/G3) to complete before changing power
cutter.set_reverse(is_M4);
#if ENABLED(SPINDLE_LASER_USE_PWM)
if (parser.seenval('O')) {
cutter.unitPower = cutter.power_to_range(parser.value_byte(), 0);
cutter.ocr_set_power(cutter.unitPower); // The OCR is a value from 0 to 255 (uint8_t)
auto get_s_power = [] {
float u;
if (parser.seenval('S')) {
const float v = parser.value_float();
u = TERN(LASER_POWER_TRAP, v, cutter.power_to_range(v));
}
else
cutter.set_power(cutter.upower_to_ocr(get_s_power()));
#elif ENABLED(SPINDLE_SERVO)
cutter.set_power(get_s_power());
#else
else if (cutter.cutter_mode == CUTTER_MODE_STANDARD)
u = cutter.cpwr_to_upwr(SPEED_POWER_STARTUP);
cutter.menuPower = cutter.unitPower = u;
// PWM not implied, power converted to OCR from unit definition and on/off if not PWM.
cutter.power = TERN(SPINDLE_LASER_USE_PWM, cutter.upower_to_ocr(u), u > 0 ? 255 : 0);
return u;
};
if (cutter.cutter_mode == CUTTER_MODE_CONTINUOUS || cutter.cutter_mode == CUTTER_MODE_DYNAMIC) { // Laser power in inline mode
#if ENABLED(LASER_FEATURE)
planner.laser_inline.status.isPowered = true; // M3 or M4 is powered either way
get_s_power(); // Update cutter.power if seen
#if ENABLED(LASER_POWER_SYNC)
// With power sync we only set power so it does not effect queued inline power sets
planner.buffer_sync_block(BLOCK_BIT_LASER_PWR); // Send the flag, queueing inline power
#else
planner.synchronize();
cutter.inline_power(cutter.power);
#endif
#endif
}
else {
cutter.set_enabled(true);
#endif
cutter.menuPower = cutter.unitPower;
get_s_power();
cutter.apply_power(
#if ENABLED(SPINDLE_SERVO)
cutter.unitPower
#elif ENABLED(SPINDLE_LASER_USE_PWM)
cutter.upower_to_ocr(cutter.unitPower)
#else
cutter.unitPower > 0 ? 255 : 0
#endif
);
TERN_(SPINDLE_CHANGE_DIR, cutter.set_reverse(is_M4));
}
}
/**
* M5 - Cutter OFF (when moves are complete)
*/
void GcodeSuite::M5() {
#if ENABLED(LASER_POWER_INLINE)
if (parser.seen('I') == DISABLED(LASER_POWER_INLINE_INVERT)) {
cutter.set_inline_enabled(false); // Laser power in inline mode
return;
}
// Non-inline, standard case
cutter.inline_disable(); // Prevent future blocks re-setting the power
#endif
planner.synchronize();
cutter.set_enabled(false);
cutter.menuPower = cutter.unitPower;
cutter.power = 0;
cutter.apply_power(0); // M5 just kills power, leaving inline mode unchanged
if (cutter.cutter_mode != CUTTER_MODE_STANDARD) {
if (parser.seen_test('I')) {
TERN_(LASER_FEATURE, cutter.inline_power(cutter.power));
cutter.set_enabled(false); // Needs to happen while we are in inline mode to clear inline power.
cutter.cutter_mode = CUTTER_MODE_STANDARD; // Switch from inline to standard mode.
}
}
cutter.set_enabled(false); // Disable enable output setting
}
#endif // HAS_CUTTER

151
Marlin/src/gcode/feature/L6470/M122.cpp

@ -1,151 +0,0 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if HAS_L64XX
#include "../../gcode.h"
#include "../../../libs/L64XX/L64XX_Marlin.h"
#include "../../../module/stepper/indirection.h"
void echo_yes_no(const bool yes);
inline void L6470_say_status(const L64XX_axis_t axis) {
if (L64xxManager.spi_abort) return;
const L64XX_Marlin::L64XX_shadow_t &sh = L64xxManager.shadow;
L64xxManager.get_status(axis);
L64xxManager.say_axis(axis);
#if ENABLED(L6470_CHITCHAT)
char temp_buf[20];
sprintf_P(temp_buf, PSTR(" status: %4x "), sh.STATUS_AXIS_RAW);
SERIAL_ECHO(temp_buf);
print_bin(sh.STATUS_AXIS_RAW);
switch (sh.STATUS_AXIS_LAYOUT) {
case L6470_STATUS_LAYOUT: SERIAL_ECHOPGM(" L6470"); break;
case L6474_STATUS_LAYOUT: SERIAL_ECHOPGM(" L6474"); break;
case L6480_STATUS_LAYOUT: SERIAL_ECHOPGM(" L6480/powerSTEP01"); break;
}
#endif
SERIAL_ECHOPGM("\n...OUTPUT: ");
SERIAL_ECHOF(sh.STATUS_AXIS & STATUS_HIZ ? F("OFF") : F("ON "));
SERIAL_ECHOPGM(" BUSY: "); echo_yes_no((sh.STATUS_AXIS & STATUS_BUSY) == 0);
SERIAL_ECHOPGM(" DIR: ");
SERIAL_ECHOF((((sh.STATUS_AXIS & STATUS_DIR) >> 4) ^ L64xxManager.index_to_dir[axis]) ? F("FORWARD") : F("REVERSE"));
if (sh.STATUS_AXIS_LAYOUT == L6480_STATUS_LAYOUT) {
SERIAL_ECHOPGM(" Last Command: ");
if (sh.STATUS_AXIS & sh.STATUS_AXIS_WRONG_CMD) SERIAL_ECHOPGM("VALID");
else SERIAL_ECHOPGM("ERROR");
SERIAL_ECHOPGM("\n...THERMAL: ");
switch ((sh.STATUS_AXIS & (sh.STATUS_AXIS_TH_SD | sh.STATUS_AXIS_TH_WRN)) >> 11) {
case 0: SERIAL_ECHOPGM("DEVICE SHUTDOWN"); break;
case 1: SERIAL_ECHOPGM("BRIDGE SHUTDOWN"); break;
case 2: SERIAL_ECHOPGM("WARNING "); break;
case 3: SERIAL_ECHOPGM("OK "); break;
}
}
else {
SERIAL_ECHOPGM(" Last Command: ");
if (!(sh.STATUS_AXIS & sh.STATUS_AXIS_WRONG_CMD)) SERIAL_ECHOPGM("IN");
SERIAL_ECHOPGM("VALID ");
SERIAL_ECHOF(sh.STATUS_AXIS & sh.STATUS_AXIS_NOTPERF_CMD ? F("COMPLETED ") : F("Not PERFORMED"));
SERIAL_ECHOPGM("\n...THERMAL: ", !(sh.STATUS_AXIS & sh.STATUS_AXIS_TH_SD) ? "SHUTDOWN " : !(sh.STATUS_AXIS & sh.STATUS_AXIS_TH_WRN) ? "WARNING " : "OK ");
}
SERIAL_ECHOPGM(" OVERCURRENT:"); echo_yes_no((sh.STATUS_AXIS & sh.STATUS_AXIS_OCD) == 0);
if (sh.STATUS_AXIS_LAYOUT != L6474_STATUS_LAYOUT) {
SERIAL_ECHOPGM(" STALL:"); echo_yes_no((sh.STATUS_AXIS & sh.STATUS_AXIS_STEP_LOSS_A) == 0 || (sh.STATUS_AXIS & sh.STATUS_AXIS_STEP_LOSS_B) == 0);
SERIAL_ECHOPGM(" STEP-CLOCK MODE:"); echo_yes_no((sh.STATUS_AXIS & sh.STATUS_AXIS_SCK_MOD) != 0);
}
else {
SERIAL_ECHOPGM(" STALL: NA "
" STEP-CLOCK MODE: NA"
" UNDER VOLTAGE LOCKOUT: "); echo_yes_no((sh.STATUS_AXIS & sh.STATUS_AXIS_UVLO) == 0);
}
SERIAL_EOL();
}
/**
* M122: Debug L6470 drivers
*/
void GcodeSuite::M122() {
L64xxManager.pause_monitor(true); // Keep monitor_driver() from stealing status
L64xxManager.spi_active = true; // Tell set_directions() a series of SPI transfers is underway
//if (parser.seen('S'))
// tmc_set_report_interval(parser.value_bool());
//else
#if AXIS_IS_L64XX(X)
L6470_say_status(X);
#endif
#if AXIS_IS_L64XX(X2)
L6470_say_status(X2);
#endif
#if AXIS_IS_L64XX(Y)
L6470_say_status(Y);
#endif
#if AXIS_IS_L64XX(Y2)
L6470_say_status(Y2);
#endif
#if AXIS_IS_L64XX(Z)
L6470_say_status(Z);
#endif
#if AXIS_IS_L64XX(Z2)
L6470_say_status(Z2);
#endif
#if AXIS_IS_L64XX(Z3)
L6470_say_status(Z3);
#endif
#if AXIS_IS_L64XX(Z4)
L6470_say_status(Z4);
#endif
#if AXIS_IS_L64XX(E0)
L6470_say_status(E0);
#endif
#if AXIS_IS_L64XX(E1)
L6470_say_status(E1);
#endif
#if AXIS_IS_L64XX(E2)
L6470_say_status(E2);
#endif
#if AXIS_IS_L64XX(E3)
L6470_say_status(E3);
#endif
#if AXIS_IS_L64XX(E4)
L6470_say_status(E4);
#endif
#if AXIS_IS_L64XX(E5)
L6470_say_status(E5);
#endif
#if AXIS_IS_L64XX(E6)
L6470_say_status(E6);
#endif
#if AXIS_IS_L64XX(E7)
L6470_say_status(E7);
#endif
L64xxManager.spi_active = false; // done with all SPI transfers - clear handshake flags
L64xxManager.spi_abort = false;
L64xxManager.pause_monitor(false);
}
#endif // HAS_L64XX

417
Marlin/src/gcode/feature/L6470/M906.cpp

@ -1,417 +0,0 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if HAS_L64XX
#if AXIS_COLLISION('I')
#error "M906 parameter 'I' collision with axis name."
#endif
#include "../../gcode.h"
#include "../../../libs/L64XX/L64XX_Marlin.h"
#include "../../../module/stepper/indirection.h"
#include "../../../module/planner.h"
#define DEBUG_OUT ENABLED(L6470_CHITCHAT)
#include "../../../core/debug_out.h"
/**
* MACRO to fetch information on the items associated with current limiting
* and maximum voltage output.
*
* L6470 can be setup to shutdown if either current threshold is exceeded.
*
* L6470 output current can not be set directly. It is set indirectly by
* setting the maximum effective output voltage.
*
* Effective output voltage is set by PWM duty cycle.
*
* Maximum effective output voltage is affected by MANY variables. The main ones are:
* KVAL_HOLD
* KVAL_RUN
* KVAL_ACC
* KVAL_DEC
* Vs compensation (if enabled)
*/
void L64XX_report_current(L64XX &motor, const L64XX_axis_t axis) {
if (L64xxManager.spi_abort) return; // don't do anything if set_directions() has occurred
const L64XX_Marlin::L64XX_shadow_t &sh = L64xxManager.shadow;
const uint16_t status = L64xxManager.get_status(axis); //also populates shadow structure
const uint8_t OverCurrent_Threshold = uint8_t(motor.GetParam(L6470_OCD_TH));
auto say_axis_status = [](const L64XX_axis_t axis, const uint16_t status) {
L64xxManager.say_axis(axis);
#if ENABLED(L6470_CHITCHAT)
char tmp[10];
sprintf_P(tmp, PSTR("%4x "), status);
DEBUG_ECHOPGM(" status: ", tmp);
print_bin(status);
#else
UNUSED(status);
#endif
SERIAL_EOL();
};
char temp_buf[10];
switch (sh.STATUS_AXIS_LAYOUT) {
case L6470_STATUS_LAYOUT: // L6470
case L6480_STATUS_LAYOUT: { // L6480 & powerstep01
const uint16_t Stall_Threshold = (uint8_t)motor.GetParam(L6470_STALL_TH),
motor_status = (status & (STATUS_MOT_STATUS)) >> 5,
L6470_ADC_out = motor.GetParam(L6470_ADC_OUT),
L6470_ADC_out_limited = constrain(L6470_ADC_out, 8, 24);
const float comp_coef = 1600.0f / L6470_ADC_out_limited;
const uint16_t MicroSteps = _BV(motor.GetParam(L6470_STEP_MODE) & 0x07);
say_axis_status(axis, sh.STATUS_AXIS_RAW);
SERIAL_ECHOPGM("...OverCurrent Threshold: ");
sprintf_P(temp_buf, PSTR("%2d ("), OverCurrent_Threshold);
SERIAL_ECHO(temp_buf);
SERIAL_ECHO((OverCurrent_Threshold + 1) * motor.OCD_CURRENT_CONSTANT_INV);
SERIAL_ECHOPGM(" mA)");
SERIAL_ECHOPGM(" Stall Threshold: ");
sprintf_P(temp_buf, PSTR("%2d ("), Stall_Threshold);
SERIAL_ECHO(temp_buf);
SERIAL_ECHO((Stall_Threshold + 1) * motor.STALL_CURRENT_CONSTANT_INV);
SERIAL_ECHOPGM(" mA)");
SERIAL_ECHOPGM(" Motor Status: ");
switch (motor_status) {
case 0: SERIAL_ECHOPGM("stopped"); break;
case 1: SERIAL_ECHOPGM("accelerating"); break;
case 2: SERIAL_ECHOPGM("decelerating"); break;
case 3: SERIAL_ECHOPGM("at constant speed"); break;
}
SERIAL_EOL();
SERIAL_ECHOPGM("...MicroSteps: ", MicroSteps,
" ADC_OUT: ", L6470_ADC_out);
SERIAL_ECHOPGM(" Vs_compensation: ");
SERIAL_ECHOF((motor.GetParam(sh.L6470_AXIS_CONFIG) & CONFIG_EN_VSCOMP) ? F("ENABLED ") : F("DISABLED"));
SERIAL_ECHOLNPGM(" Compensation coefficient: ~", comp_coef * 0.01f);
SERIAL_ECHOPGM("...KVAL_HOLD: ", motor.GetParam(L6470_KVAL_HOLD),
" KVAL_RUN : ", motor.GetParam(L6470_KVAL_RUN),
" KVAL_ACC: ", motor.GetParam(L6470_KVAL_ACC),
" KVAL_DEC: ", motor.GetParam(L6470_KVAL_DEC),
" V motor max = ");
switch (motor_status) {
case 0: SERIAL_ECHO(motor.GetParam(L6470_KVAL_HOLD) * 100 / 256); SERIAL_ECHOPGM("% (KVAL_HOLD)"); break;
case 1: SERIAL_ECHO(motor.GetParam(L6470_KVAL_RUN) * 100 / 256); SERIAL_ECHOPGM("% (KVAL_RUN)"); break;
case 2: SERIAL_ECHO(motor.GetParam(L6470_KVAL_ACC) * 100 / 256); SERIAL_ECHOPGM("% (KVAL_ACC)"); break;
case 3: SERIAL_ECHO(motor.GetParam(L6470_KVAL_DEC) * 100 / 256); SERIAL_ECHOPGM("% (KVAL_HOLD)"); break;
}
SERIAL_EOL();
#if ENABLED(L6470_CHITCHAT)
DEBUG_ECHOPGM("...SLEW RATE: ");
switch (sh.STATUS_AXIS_LAYOUT) {
case L6470_STATUS_LAYOUT: {
switch ((motor.GetParam(sh.L6470_AXIS_CONFIG) & CONFIG_POW_SR) >> CONFIG_POW_SR_BIT) {
case 0: { DEBUG_ECHOLNPGM("320V/uS") ; break; }
case 1: { DEBUG_ECHOLNPGM("75V/uS") ; break; }
case 2: { DEBUG_ECHOLNPGM("110V/uS") ; break; }
case 3: { DEBUG_ECHOLNPGM("260V/uS") ; break; }
}
break;
}
case L6480_STATUS_LAYOUT: {
switch (motor.GetParam(L6470_GATECFG1) & CONFIG1_SR ) {
case CONFIG1_SR_220V_us: { DEBUG_ECHOLNPGM("220V/uS") ; break; }
case CONFIG1_SR_400V_us: { DEBUG_ECHOLNPGM("400V/uS") ; break; }
case CONFIG1_SR_520V_us: { DEBUG_ECHOLNPGM("520V/uS") ; break; }
case CONFIG1_SR_980V_us: { DEBUG_ECHOLNPGM("980V/uS") ; break; }
default: { DEBUG_ECHOLNPGM("unknown") ; break; }
}
}
}
#endif
SERIAL_EOL();
break;
}
case L6474_STATUS_LAYOUT: { // L6474
const uint16_t L6470_ADC_out = motor.GetParam(L6470_ADC_OUT) & 0x1F,
L6474_TVAL_val = motor.GetParam(L6474_TVAL) & 0x7F;
say_axis_status(axis, sh.STATUS_AXIS_RAW);
SERIAL_ECHOPGM("...OverCurrent Threshold: ");
sprintf_P(temp_buf, PSTR("%2d ("), OverCurrent_Threshold);
SERIAL_ECHO(temp_buf);
SERIAL_ECHO((OverCurrent_Threshold + 1) * motor.OCD_CURRENT_CONSTANT_INV);
SERIAL_ECHOPGM(" mA)");
SERIAL_ECHOPGM(" TVAL: ");
sprintf_P(temp_buf, PSTR("%2d ("), L6474_TVAL_val);
SERIAL_ECHO(temp_buf);
SERIAL_ECHO((L6474_TVAL_val + 1) * motor.STALL_CURRENT_CONSTANT_INV);
SERIAL_ECHOLNPGM(" mA) Motor Status: NA");
const uint16_t MicroSteps = _BV(motor.GetParam(L6470_STEP_MODE) & 0x07); //NOMORE(MicroSteps, 16);
SERIAL_ECHOPGM("...MicroSteps: ", MicroSteps,
" ADC_OUT: ", L6470_ADC_out);
SERIAL_ECHOLNPGM(" Vs_compensation: NA\n");
SERIAL_ECHOLNPGM("...KVAL_HOLD: NA"
" KVAL_RUN : NA"
" KVAL_ACC: NA"
" KVAL_DEC: NA"
" V motor max = NA");
#if ENABLED(L6470_CHITCHAT)
DEBUG_ECHOPGM("...SLEW RATE: ");
switch ((motor.GetParam(sh.L6470_AXIS_CONFIG) & CONFIG_POW_SR) >> CONFIG_POW_SR_BIT) {
case 0: DEBUG_ECHOLNPGM("320V/uS") ; break;
case 1: DEBUG_ECHOLNPGM("75V/uS") ; break;
case 2: DEBUG_ECHOLNPGM("110V/uS") ; break;
case 3: DEBUG_ECHOLNPGM("260V/uS") ; break;
default: DEBUG_ECHOLNPGM("slew rate: ", (motor.GetParam(sh.L6470_AXIS_CONFIG) & CONFIG_POW_SR) >> CONFIG_POW_SR_BIT); break;
}
#endif
SERIAL_EOL();
SERIAL_EOL();
break;
}
}
}
/**
* M906: report or set KVAL_HOLD which sets the maximum effective voltage provided by the
* PWMs to the steppers
*
* On L6474 this sets the TVAL register (same address).
*
* I - select which driver(s) to change on multi-driver axis
* (default) all drivers on the axis
* 0 - monitor only the first XYZ... driver
* 1 - monitor only X2, Y2, Z2
* 2 - monitor only Z3
* 3 - monitor only Z4
* Xxxx, Yxxx, Zxxx, Axxx, Bxxx, Cxxx, Uxxx, Vxxx, Wxxx, Exxx - axis to change (optional)
* L6474 - current in mA (4A max)
* All others - 0-255
*
* Sets KVAL_HOLD which affects the current being driven through the stepper.
*
* L6470 is used in the STEP-CLOCK mode. KVAL_HOLD is the only KVAL_xxx
* that affects the effective voltage seen by the stepper.
*/
void GcodeSuite::M906() {
L64xxManager.pause_monitor(true); // Keep monitor_driver() from stealing status
#define L6470_SET_KVAL_HOLD(Q) (AXIS_IS_L64XX(Q) ? stepper##Q.setTVALCurrent(value) : stepper##Q.SetParam(L6470_KVAL_HOLD, uint8_t(value)))
DEBUG_ECHOLNPGM("M906");
uint8_t report_current = true;
#if AXIS_IS_L64XX(X2) || AXIS_IS_L64XX(Y2) || AXIS_IS_L64XX(Z2) || AXIS_IS_L64XX(Z3) || AXIS_IS_L64XX(Z4)
const int8_t index = parser.byteval('I', -1);
#else
constexpr int8_t index = -1;
#endif
LOOP_LOGICAL_AXES(i) if (uint16_t value = parser.intval(AXIS_CHAR(i))) {
report_current = false;
if (planner.has_blocks_queued() || planner.cleaning_buffer_counter) {
SERIAL_ECHOLNPGM("Test aborted. Can't set KVAL_HOLD while steppers are moving.");
return;
}
switch (i) {
#if AXIS_IS_L64XX(X) || AXIS_IS_L64XX(X2)
case X_AXIS:
#if AXIS_IS_L64XX(X)
if (index < 0 || index == 0) L6470_SET_KVAL_HOLD(X);
#endif
#if AXIS_IS_L64XX(X2)
if (index < 0 || index == 1) L6470_SET_KVAL_HOLD(X2);
#endif
break;
#endif
#if AXIS_IS_L64XX(Y) || AXIS_IS_L64XX(Y2)
case Y_AXIS:
#if AXIS_IS_L64XX(Y)
if (index < 0 || index == 0) L6470_SET_KVAL_HOLD(Y);
#endif
#if AXIS_IS_L64XX(Y2)
if (index < 0 || index == 1) L6470_SET_KVAL_HOLD(Y2);
#endif
break;
#endif
#if AXIS_IS_L64XX(Z) || AXIS_IS_L64XX(Z2) || AXIS_IS_L64XX(Z3) || AXIS_IS_L64XX(Z4)
case Z_AXIS:
#if AXIS_IS_L64XX(Z)
if (index < 0 || index == 0) L6470_SET_KVAL_HOLD(Z);
#endif
#if AXIS_IS_L64XX(Z2)
if (index < 0 || index == 1) L6470_SET_KVAL_HOLD(Z2);
#endif
#if AXIS_IS_L64XX(Z3)
if (index < 0 || index == 2) L6470_SET_KVAL_HOLD(Z3);
#endif
#if AXIS_IS_L64XX(Z4)
if (index < 0 || index == 3) L6470_SET_KVAL_HOLD(Z4);
#endif
break;
#endif
#if AXIS_IS_L64XX(I)
case I_AXIS: L6470_SET_KVAL_HOLD(I); break;
#endif
#if AXIS_IS_L64XX(J)
case J_AXIS: L6470_SET_KVAL_HOLD(J); break;
#endif
#if AXIS_IS_L64XX(K)
case K_AXIS: L6470_SET_KVAL_HOLD(K); break;
#endif
#if AXIS_IS_L64XX(U)
case U_AXIS: L6470_SET_KVAL_HOLD(U); break;
#endif
#if AXIS_IS_L64XX(V)
case V_AXIS: L6470_SET_KVAL_HOLD(V); break;
#endif
#if AXIS_IS_L64XX(W)
case W_AXIS: L6470_SET_KVAL_HOLD(W); break;
#endif
#if AXIS_IS_L64XX(E0) || AXIS_IS_L64XX(E1) || AXIS_IS_L64XX(E2) || AXIS_IS_L64XX(E3) || AXIS_IS_L64XX(E4) || AXIS_IS_L64XX(E5) || AXIS_IS_L64XX(E6) || AXIS_IS_L64XX(E7)
case E_AXIS: {
const int8_t eindex = get_target_e_stepper_from_command(-2);
#if AXIS_IS_L64XX(E0)
if (eindex < 0 || eindex == 0) L6470_SET_KVAL_HOLD(E0);
#endif
#if AXIS_IS_L64XX(E1)
if (eindex < 0 || eindex == 1) L6470_SET_KVAL_HOLD(E1);
#endif
#if AXIS_IS_L64XX(E2)
if (eindex < 0 || eindex == 2) L6470_SET_KVAL_HOLD(E2);
#endif
#if AXIS_IS_L64XX(E3)
if (eindex < 0 || eindex == 3) L6470_SET_KVAL_HOLD(E3);
#endif
#if AXIS_IS_L64XX(E4)
if (eindex < 0 || eindex == 4) L6470_SET_KVAL_HOLD(E4);
#endif
#if AXIS_IS_L64XX(E5)
if (eindex < 0 || eindex == 5) L6470_SET_KVAL_HOLD(E5);
#endif
#if AXIS_IS_L64XX(E6)
if (eindex < 0 || eindex == 6) L6470_SET_KVAL_HOLD(E6);
#endif
#if AXIS_IS_L64XX(E7)
if (eindex < 0 || eindex == 7) L6470_SET_KVAL_HOLD(E7);
#endif
} break;
#endif
}
}
if (report_current) {
#define L64XX_REPORT_CURRENT(Q) L64XX_report_current(stepper##Q, Q)
L64xxManager.spi_active = true; // Tell set_directions() a series of SPI transfers is underway
#if AXIS_IS_L64XX(X)
L64XX_REPORT_CURRENT(X);
#endif
#if AXIS_IS_L64XX(X2)
L64XX_REPORT_CURRENT(X2);
#endif
#if AXIS_IS_L64XX(Y)
L64XX_REPORT_CURRENT(Y);
#endif
#if AXIS_IS_L64XX(Y2)
L64XX_REPORT_CURRENT(Y2);
#endif
#if AXIS_IS_L64XX(Z)
L64XX_REPORT_CURRENT(Z);
#endif
#if AXIS_IS_L64XX(Z2)
L64XX_REPORT_CURRENT(Z2);
#endif
#if AXIS_IS_L64XX(Z3)
L64XX_REPORT_CURRENT(Z3);
#endif
#if AXIS_IS_L64XX(Z4)
L64XX_REPORT_CURRENT(Z4);
#endif
#if AXIS_IS_L64XX(I)
L64XX_REPORT_CURRENT(I);
#endif
#if AXIS_IS_L64XX(J)
L64XX_REPORT_CURRENT(J);
#endif
#if AXIS_IS_L64XX(K)
L64XX_REPORT_CURRENT(K);
#endif
#if AXIS_IS_L64XX(U)
L64XX_REPORT_CURRENT(U);
#endif
#if AXIS_IS_L64XX(V)
L64XX_REPORT_CURRENT(V);
#endif
#if AXIS_IS_L64XX(W)
L64XX_REPORT_CURRENT(W);
#endif
#if AXIS_IS_L64XX(E0)
L64XX_REPORT_CURRENT(E0);
#endif
#if AXIS_IS_L64XX(E1)
L64XX_REPORT_CURRENT(E1);
#endif
#if AXIS_IS_L64XX(E2)
L64XX_REPORT_CURRENT(E2);
#endif
#if AXIS_IS_L64XX(E3)
L64XX_REPORT_CURRENT(E3);
#endif
#if AXIS_IS_L64XX(E4)
L64XX_REPORT_CURRENT(E4);
#endif
#if AXIS_IS_L64XX(E5)
L64XX_REPORT_CURRENT(E5);
#endif
#if AXIS_IS_L64XX(E6)
L64XX_REPORT_CURRENT(E6);
#endif
#if AXIS_IS_L64XX(E7)
L64XX_REPORT_CURRENT(E7);
#endif
L64xxManager.spi_active = false; // done with all SPI transfers - clear handshake flags
L64xxManager.spi_abort = false;
L64xxManager.pause_monitor(false);
}
}
#endif // HAS_L64XX

650
Marlin/src/gcode/feature/L6470/M916-M918.cpp

@ -1,650 +0,0 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
//
// NOTE: All tests assume each axis uses matching driver chips.
//
#include "../../../inc/MarlinConfig.h"
#if HAS_L64XX
#include "../../gcode.h"
#include "../../../module/stepper/indirection.h"
#include "../../../module/planner.h"
#include "../../../libs/L64XX/L64XX_Marlin.h"
#define DEBUG_OUT ENABLED(L6470_CHITCHAT)
#include "../../../core/debug_out.h"
/**
* M916: increase KVAL_HOLD until get thermal warning
* NOTE - on L6474 it is TVAL that is used
*
* J - select which driver(s) to monitor on multi-driver axis
* 0 - (default) monitor all drivers on the axis or E0
* 1 - monitor only X, Y, Z, E1
* 2 - monitor only X2, Y2, Z2, E2
* 3 - monitor only Z3, E3
* 4 - monitor only Z4, E4
*
* Xxxx, Yxxx, Zxxx, Exxx - axis to be monitored with displacement
* xxx (1-255) is distance moved on either side of current position
*
* F - feedrate
* optional - will use default max feedrate from configuration.h if not specified
*
* T - current (mA) setting for TVAL (0 - 4A in 31.25mA increments, rounds down) - L6474 only
* optional - will report current value from driver if not specified
*
* K - value for KVAL_HOLD (0 - 255) (ignored for L6474)
* optional - will report current value from driver if not specified
*
* D - time (in seconds) to run each setting of KVAL_HOLD/TVAL
* optional - defaults to zero (runs each setting once)
*/
/**
* This routine is also useful for determining the approximate KVAL_HOLD
* where the stepper stops losing steps. The sound will get noticeably quieter
* as it stops losing steps.
*/
void GcodeSuite::M916() {
DEBUG_ECHOLNPGM("M916");
L64xxManager.pause_monitor(true); // Keep monitor_driver() from stealing status
// Variables used by L64xxManager.get_user_input function - some may not be used
char axis_mon[3][3] = { {" "}, {" "}, {" "} }; // list of Axes to be monitored
L64XX_axis_t axis_index[3];
uint16_t axis_status[3];
uint8_t driver_count = 1;
float position_max;
float position_min;
float final_feedrate;
uint8_t kval_hold;
uint8_t OCD_TH_val = 0;
uint8_t STALL_TH_val = 0;
uint16_t over_current_threshold;
constexpr uint8_t over_current_flag = false; // M916 doesn't play with the overcurrent thresholds
#define DRIVER_TYPE_L6474(Q) AXIS_DRIVER_TYPE_##Q(L6474)
uint8_t j; // general purpose counter
if (L64xxManager.get_user_input(driver_count, axis_index, axis_mon, position_max, position_min, final_feedrate, kval_hold, over_current_flag, OCD_TH_val, STALL_TH_val, over_current_threshold))
return; // quit if invalid user input
DEBUG_ECHOLNPGM("feedrate = ", final_feedrate);
planner.synchronize(); // wait for all current movement commands to complete
const L64XX_Marlin::L64XX_shadow_t &sh = L64xxManager.shadow;
for (j = 0; j < driver_count; j++)
L64xxManager.get_status(axis_index[j]); // clear out any pre-existing error flags
char temp_axis_string[] = " ";
temp_axis_string[0] = axis_mon[0][0]; // need to have a string for use within sprintf format section
char gcode_string[80];
uint16_t status_composite = 0;
uint16_t M91x_counter = kval_hold;
uint16_t M91x_counter_max;
if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT) {
M91x_counter_max = 128; // TVAL is 7 bits
LIMIT(M91x_counter, 0U, 127U);
}
else
M91x_counter_max = 256; // KVAL_HOLD is 8 bits
uint8_t M91x_delay_s = parser.byteval('D'); // get delay in seconds
millis_t M91x_delay_ms = SEC_TO_MS(M91x_delay_s * 60);
millis_t M91x_delay_end;
DEBUG_ECHOLNPGM(".\n.");
do {
if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT)
DEBUG_ECHOLNPGM("TVAL current (mA) = ", (M91x_counter + 1) * sh.AXIS_STALL_CURRENT_CONSTANT_INV); // report TVAL current for this run
else
DEBUG_ECHOLNPGM("kval_hold = ", M91x_counter); // report KVAL_HOLD for this run
for (j = 0; j < driver_count; j++)
L64xxManager.set_param(axis_index[j], L6470_KVAL_HOLD, M91x_counter); //set KVAL_HOLD or TVAL (same register address)
M91x_delay_end = millis() + M91x_delay_ms;
do {
// turn the motor(s) both directions
sprintf_P(gcode_string, PSTR("G0 %s%03d F%03d"), temp_axis_string, uint16_t(position_min), uint16_t(final_feedrate));
process_subcommands_now(gcode_string);
sprintf_P(gcode_string, PSTR("G0 %s%03d F%03d"), temp_axis_string, uint16_t(position_max), uint16_t(final_feedrate));
process_subcommands_now(gcode_string);
// get the status after the motors have stopped
planner.synchronize();
status_composite = 0; // clear out the old bits
for (j = 0; j < driver_count; j++) {
axis_status[j] = (~L64xxManager.get_status(axis_index[j])) & sh.L6470_ERROR_MASK; // bits of interest are all active low
status_composite |= axis_status[j] ;
}
if (status_composite) break;
} while (millis() < M91x_delay_end);
if (status_composite) break;
M91x_counter++;
} while (!(status_composite & (sh.STATUS_AXIS_TH_WRN | sh.STATUS_AXIS_TH_SD)) && (M91x_counter < M91x_counter_max));
DEBUG_ECHOLNPGM(".");
#if ENABLED(L6470_CHITCHAT)
if (status_composite) {
L64xxManager.error_status_decode(status_composite, axis_index[0],
sh.STATUS_AXIS_TH_SD, sh.STATUS_AXIS_TH_WRN,
sh.STATUS_AXIS_STEP_LOSS_A, sh.STATUS_AXIS_STEP_LOSS_B,
sh.STATUS_AXIS_OCD, sh.STATUS_AXIS_LAYOUT);
DEBUG_ECHOLNPGM(".");
}
#endif
if ((status_composite & (sh.STATUS_AXIS_TH_WRN | sh.STATUS_AXIS_TH_SD)))
DEBUG_ECHOLNPGM(".\n.\nTest completed normally - Thermal warning/shutdown has occurred");
else if (status_composite)
DEBUG_ECHOLNPGM(".\n.\nTest completed abnormally - non-thermal error has occurred");
else
DEBUG_ECHOLNPGM(".\n.\nTest completed normally - Unable to get to thermal warning/shutdown");
L64xxManager.pause_monitor(false);
}
/**
* M917: Find minimum current thresholds
*
* Decrease OCD current until overcurrent error
* Increase OCD until overcurrent error goes away
* Decrease stall threshold until stall (not done on L6474)
* Increase stall until stall error goes away (not done on L6474)
*
* J - select which driver(s) to monitor on multi-driver axis
* 0 - (default) monitor all drivers on the axis or E0
* 1 - monitor only X, Y, Z, E1
* 2 - monitor only X2, Y2, Z2, E2
* Xxxx, Yxxx, Zxxx, Exxx - axis to be monitored with displacement
* xxx (1-255) is distance moved on either side of current position
*
* F - feedrate
* optional - will use default max feedrate from Configuration.h if not specified
*
* I - starting over-current threshold
* optional - will report current value from driver if not specified
* if there are multiple drivers on the axis then all will be set the same
*
* T - current (mA) setting for TVAL (0 - 4A in 31.25mA increments, rounds down) - L6474 only
* optional - will report current value from driver if not specified
*
* K - value for KVAL_HOLD (0 - 255) (ignored for L6474)
* optional - will report current value from driver if not specified
*/
void GcodeSuite::M917() {
DEBUG_ECHOLNPGM("M917");
L64xxManager.pause_monitor(true); // Keep monitor_driver() from stealing status
char axis_mon[3][3] = { {" "}, {" "}, {" "} }; // list of Axes to be monitored
L64XX_axis_t axis_index[3];
uint16_t axis_status[3];
uint8_t driver_count = 1;
float position_max;
float position_min;
float final_feedrate;
uint8_t kval_hold;
uint8_t OCD_TH_val = 0;
uint8_t STALL_TH_val = 0;
uint16_t over_current_threshold;
constexpr uint8_t over_current_flag = true;
uint8_t j; // general purpose counter
if (L64xxManager.get_user_input(driver_count, axis_index, axis_mon, position_max, position_min, final_feedrate, kval_hold, over_current_flag, OCD_TH_val, STALL_TH_val, over_current_threshold))
return; // quit if invalid user input
DEBUG_ECHOLNPGM("feedrate = ", final_feedrate);
planner.synchronize(); // wait for all current movement commands to complete
const L64XX_Marlin::L64XX_shadow_t &sh = L64xxManager.shadow;
for (j = 0; j < driver_count; j++)
L64xxManager.get_status(axis_index[j]); // clear error flags
char temp_axis_string[] = " ";
temp_axis_string[0] = axis_mon[0][0]; // need a sprintf format string
char gcode_string[80];
uint16_t status_composite = 0;
uint8_t test_phase = 0; // 0 - decreasing OCD - exit when OCD warning occurs (ignore STALL)
// 1 - increasing OCD - exit when OCD warning stops (ignore STALL)
// 2 - OCD finalized - decreasing STALL - exit when STALL warning happens
// 3 - OCD finalized - increasing STALL - exit when STALL warning stop
// 4 - all testing completed
DEBUG_ECHOPGM(".\n.\n.\nover_current threshold : ", (OCD_TH_val + 1) * 375); // first status display
DEBUG_ECHOPGM(" (OCD_TH: : ", OCD_TH_val);
if (sh.STATUS_AXIS_LAYOUT != L6474_STATUS_LAYOUT) {
DEBUG_ECHOPGM(") Stall threshold: ", (STALL_TH_val + 1) * 31.25);
DEBUG_ECHOPGM(" (STALL_TH: ", STALL_TH_val);
}
DEBUG_ECHOLNPGM(")");
do {
if (sh.STATUS_AXIS_LAYOUT != L6474_STATUS_LAYOUT) DEBUG_ECHOPGM("STALL threshold : ", (STALL_TH_val + 1) * 31.25);
DEBUG_ECHOLNPGM(" OCD threshold : ", (OCD_TH_val + 1) * 375);
sprintf_P(gcode_string, PSTR("G0 %s%03d F%03d"), temp_axis_string, uint16_t(position_min), uint16_t(final_feedrate));
process_subcommands_now(gcode_string);
sprintf_P(gcode_string, PSTR("G0 %s%03d F%03d"), temp_axis_string, uint16_t(position_max), uint16_t(final_feedrate));
process_subcommands_now(gcode_string);
planner.synchronize();
status_composite = 0; // clear out the old bits
for (j = 0; j < driver_count; j++) {
axis_status[j] = (~L64xxManager.get_status(axis_index[j])) & sh.L6470_ERROR_MASK; // bits of interest are all active low
status_composite |= axis_status[j];
}
if (status_composite && (status_composite & sh.STATUS_AXIS_UVLO)) {
DEBUG_ECHOLNPGM("Test aborted (Undervoltage lockout active)");
#if ENABLED(L6470_CHITCHAT)
for (j = 0; j < driver_count; j++) {
if (j) DEBUG_ECHOPGM("...");
L64xxManager.error_status_decode(axis_status[j], axis_index[j],
sh.STATUS_AXIS_TH_SD, sh.STATUS_AXIS_TH_WRN,
sh.STATUS_AXIS_STEP_LOSS_A, sh.STATUS_AXIS_STEP_LOSS_B,
sh.STATUS_AXIS_OCD, sh.STATUS_AXIS_LAYOUT);
}
#endif
return;
}
if (status_composite & (sh.STATUS_AXIS_TH_WRN | sh.STATUS_AXIS_TH_SD)) {
DEBUG_ECHOLNPGM("thermal problem - waiting for chip(s) to cool down ");
uint16_t status_composite_temp = 0;
uint8_t k = 0;
do {
k++;
if (!(k % 4)) {
kval_hold *= 0.95;
DEBUG_EOL();
DEBUG_ECHOLNPGM("Lowering KVAL_HOLD by about 5% to ", kval_hold);
for (j = 0; j < driver_count; j++)
L64xxManager.set_param(axis_index[j], L6470_KVAL_HOLD, kval_hold);
}
DEBUG_ECHOLNPGM(".");
reset_stepper_timeout(); // keep steppers powered
safe_delay(5000);
status_composite_temp = 0;
for (j = 0; j < driver_count; j++) {
axis_status[j] = (~L64xxManager.get_status(axis_index[j])) & sh.L6470_ERROR_MASK; // bits of interest are all active low
status_composite_temp |= axis_status[j];
}
}
while (status_composite_temp & (sh.STATUS_AXIS_TH_WRN | sh.STATUS_AXIS_TH_SD));
DEBUG_EOL();
}
if (status_composite & (sh.STATUS_AXIS_STEP_LOSS_A | sh.STATUS_AXIS_STEP_LOSS_B | sh.STATUS_AXIS_OCD)) {
switch (test_phase) {
case 0: {
if (status_composite & sh.STATUS_AXIS_OCD) {
// phase 0 with OCD warning - time to go to next phase
if (OCD_TH_val >= sh.AXIS_OCD_TH_MAX) {
OCD_TH_val = sh.AXIS_OCD_TH_MAX; // limit to max
test_phase = 2; // at highest value so skip phase 1
//DEBUG_ECHOLNPGM("LOGIC E0A OCD at highest - skip to 2");
DEBUG_ECHOLNPGM("OCD at highest - OCD finalized");
}
else {
OCD_TH_val++; // normal exit to next phase
test_phase = 1; // setup for first pass of phase 1
//DEBUG_ECHOLNPGM("LOGIC E0B - inc OCD & go to 1");
DEBUG_ECHOLNPGM("inc OCD");
}
}
else { // phase 0 without OCD warning - keep on decrementing if can
if (OCD_TH_val) {
OCD_TH_val--; // try lower value
//DEBUG_ECHOLNPGM("LOGIC E0C - dec OCD");
DEBUG_ECHOLNPGM("dec OCD");
}
else {
test_phase = 2; // at lowest value without warning so skip phase 1
//DEBUG_ECHOLNPGM("LOGIC E0D - OCD at latest - go to 2");
DEBUG_ECHOLNPGM("OCD finalized");
}
}
} break;
case 1: {
if (status_composite & sh.STATUS_AXIS_OCD) {
// phase 1 with OCD warning - increment if can
if (OCD_TH_val >= sh.AXIS_OCD_TH_MAX) {
OCD_TH_val = sh.AXIS_OCD_TH_MAX; // limit to max
test_phase = 2; // at highest value so go to next phase
//DEBUG_ECHOLNPGM("LOGIC E1A - OCD at max - go to 2");
DEBUG_ECHOLNPGM("OCD finalized");
}
else {
OCD_TH_val++; // try a higher value
//DEBUG_ECHOLNPGM("LOGIC E1B - inc OCD");
DEBUG_ECHOLNPGM("inc OCD");
}
}
else { // phase 1 without OCD warning - normal exit to phase 2
test_phase = 2;
//DEBUG_ECHOLNPGM("LOGIC E1C - no OCD warning - go to 1");
DEBUG_ECHOLNPGM("OCD finalized");
}
} break;
case 2: {
if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT) { // skip all STALL_TH steps if L6474
test_phase = 4;
break;
}
if (status_composite & (sh.STATUS_AXIS_STEP_LOSS_A | sh.STATUS_AXIS_STEP_LOSS_B)) {
// phase 2 with stall warning - time to go to next phase
if (STALL_TH_val >= 127) {
STALL_TH_val = 127; // limit to max
//DEBUG_ECHOLNPGM("LOGIC E2A - STALL warning, STALL at max, quit");
DEBUG_ECHOLNPGM("finished - STALL at maximum value but still have stall warning");
test_phase = 4;
}
else {
test_phase = 3; // normal exit to next phase (found failing value of STALL)
STALL_TH_val++; // setup for first pass of phase 3
//DEBUG_ECHOLNPGM("LOGIC E2B - INC - STALL warning, inc Stall, go to 3");
DEBUG_ECHOLNPGM("inc Stall");
}
}
else { // phase 2 without stall warning - decrement if can
if (STALL_TH_val) {
STALL_TH_val--; // try a lower value
//DEBUG_ECHOLNPGM("LOGIC E2C - no STALL, dec STALL");
DEBUG_ECHOLNPGM("dec STALL");
}
else {
DEBUG_ECHOLNPGM("finished - STALL at lowest value but still do NOT have stall warning");
test_phase = 4;
//DEBUG_ECHOLNPGM("LOGIC E2D - no STALL, at lowest so quit");
}
}
} break;
case 3: {
if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT) { // skip all STALL_TH steps if L6474
test_phase = 4;
break;
}
if (status_composite & (sh.STATUS_AXIS_STEP_LOSS_A | sh.STATUS_AXIS_STEP_LOSS_B)) {
// phase 3 with stall warning - increment if can
if (STALL_TH_val >= 127) {
STALL_TH_val = 127; // limit to max
DEBUG_ECHOLNPGM("finished - STALL at maximum value but still have stall warning");
test_phase = 4;
//DEBUG_ECHOLNPGM("LOGIC E3A - STALL, at max so quit");
}
else {
STALL_TH_val++; // still looking for passing value
//DEBUG_ECHOLNPGM("LOGIC E3B - STALL, inc stall");
DEBUG_ECHOLNPGM("inc stall");
}
}
else { //phase 3 without stall warning but have OCD warning
DEBUG_ECHOLNPGM("Hardware problem - OCD warning without STALL warning");
test_phase = 4;
//DEBUG_ECHOLNPGM("LOGIC E3C - not STALLED, hardware problem (quit)");
}
} break;
}
}
else {
switch (test_phase) {
case 0: { // phase 0 without OCD warning - keep on decrementing if can
if (OCD_TH_val) {
OCD_TH_val--; // try lower value
//DEBUG_ECHOLNPGM("LOGIC N0A - DEC OCD");
DEBUG_ECHOLNPGM("DEC OCD");
}
else {
test_phase = 2; // at lowest value without warning so skip phase 1
//DEBUG_ECHOLNPGM("LOGIC N0B - OCD at lowest (go to phase 2)");
DEBUG_ECHOLNPGM("OCD finalized");
}
} break;
case 1: //DEBUG_ECHOLNPGM("LOGIC N1 (go directly to 2)"); // phase 1 without OCD warning - drop directly to phase 2
DEBUG_ECHOLNPGM("OCD finalized");
case 2: { // phase 2 without stall warning - keep on decrementing if can
if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT) { // skip all STALL_TH steps if L6474
test_phase = 4;
break;
}
if (STALL_TH_val) {
STALL_TH_val--; // try a lower value (stay in phase 2)
//DEBUG_ECHOLNPGM("LOGIC N2B - dec STALL");
DEBUG_ECHOLNPGM("dec STALL");
}
else {
DEBUG_ECHOLNPGM("finished - STALL at lowest value but still no stall warning");
test_phase = 4;
//DEBUG_ECHOLNPGM("LOGIC N2C - STALL at lowest (quit)");
}
} break;
case 3: {
if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT) { // skip all STALL_TH steps if L6474
test_phase = 4;
break;
}
test_phase = 4;
//DEBUG_ECHOLNPGM("LOGIC N3 - finished!");
DEBUG_ECHOLNPGM("finished!");
} break; // phase 3 without any warnings - desired exit
} //
} // end of status checks
if (test_phase != 4) {
for (j = 0; j < driver_count; j++) { // update threshold(s)
L64xxManager.set_param(axis_index[j], L6470_OCD_TH, OCD_TH_val);
if (sh.STATUS_AXIS_LAYOUT != L6474_STATUS_LAYOUT) L64xxManager.set_param(axis_index[j], L6470_STALL_TH, STALL_TH_val);
if (L64xxManager.get_param(axis_index[j], L6470_OCD_TH) != OCD_TH_val) DEBUG_ECHOLNPGM("OCD mismatch");
if ((L64xxManager.get_param(axis_index[j], L6470_STALL_TH) != STALL_TH_val) && (sh.STATUS_AXIS_LAYOUT != L6474_STATUS_LAYOUT)) DEBUG_ECHOLNPGM("STALL mismatch");
}
}
} while (test_phase != 4);
DEBUG_ECHOLNPGM(".");
if (status_composite) {
#if ENABLED(L6470_CHITCHAT)
for (j = 0; j < driver_count; j++) {
if (j) DEBUG_ECHOPGM("...");
L64xxManager.error_status_decode(axis_status[j], axis_index[j],
sh.STATUS_AXIS_TH_SD, sh.STATUS_AXIS_TH_WRN,
sh.STATUS_AXIS_STEP_LOSS_A, sh.STATUS_AXIS_STEP_LOSS_B,
sh.STATUS_AXIS_OCD, sh.STATUS_AXIS_LAYOUT);
}
DEBUG_ECHOLNPGM(".");
#endif
DEBUG_ECHOLNPGM("Completed with errors");
}
else
DEBUG_ECHOLNPGM("Completed with no errors");
DEBUG_ECHOLNPGM(".");
L64xxManager.pause_monitor(false);
}
/**
* M918: increase speed until error or max feedrate achieved (as shown in configuration.h))
*
* J - select which driver(s) to monitor on multi-driver axis
* 0 - (default) monitor all drivers on the axis or E0
* 1 - monitor only X, Y, Z, E1
* 2 - monitor only X2, Y2, Z2, E2
* Xxxx, Yxxx, Zxxx, Exxx - axis to be monitored with displacement
* xxx (1-255) is distance moved on either side of current position
*
* I - over current threshold
* optional - will report current value from driver if not specified
*
* T - current (mA) setting for TVAL (0 - 4A in 31.25mA increments, rounds down) - L6474 only
* optional - will report current value from driver if not specified
*
* K - value for KVAL_HOLD (0 - 255) (ignored for L6474)
* optional - will report current value from driver if not specified
*
* M - value for microsteps (1 - 128) (optional)
* optional - will report current value from driver if not specified
*/
void GcodeSuite::M918() {
DEBUG_ECHOLNPGM("M918");
L64xxManager.pause_monitor(true); // Keep monitor_driver() from stealing status
char axis_mon[3][3] = { {" "}, {" "}, {" "} }; // list of Axes to be monitored
L64XX_axis_t axis_index[3];
uint16_t axis_status[3];
uint8_t driver_count = 1;
float position_max, position_min;
float final_feedrate;
uint8_t kval_hold;
uint8_t OCD_TH_val = 0;
uint8_t STALL_TH_val = 0;
uint16_t over_current_threshold;
constexpr uint8_t over_current_flag = true;
const L64XX_Marlin::L64XX_shadow_t &sh = L64xxManager.shadow;
uint8_t j; // general purpose counter
if (L64xxManager.get_user_input(driver_count, axis_index, axis_mon, position_max, position_min, final_feedrate, kval_hold, over_current_flag, OCD_TH_val, STALL_TH_val, over_current_threshold))
return; // quit if invalid user input
L64xxManager.get_status(axis_index[0]); // populate shadow array
uint8_t m_steps = parser.byteval('M');
if (m_steps != 0) {
LIMIT(m_steps, 1, sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT ? 16 : 128); // L6474
uint8_t stepVal;
for (stepVal = 0; stepVal < 8; stepVal++) { // convert to L64xx register value
if (m_steps == 1) break;
m_steps >>= 1;
}
if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT)
stepVal |= 0x98; // NO SYNC
else
stepVal |= (!SYNC_EN) | SYNC_SEL_1 | stepVal;
for (j = 0; j < driver_count; j++) {
L64xxManager.set_param(axis_index[j], dSPIN_HARD_HIZ, 0); // can't write STEP register if stepper being powered
// results in an extra NOOP being sent (data 00)
L64xxManager.set_param(axis_index[j], L6470_STEP_MODE, stepVal); // set microsteps
}
}
m_steps = L64xxManager.get_param(axis_index[0], L6470_STEP_MODE) & 0x07; // get microsteps
DEBUG_ECHOLNPGM("Microsteps = ", _BV(m_steps));
DEBUG_ECHOLNPGM("target (maximum) feedrate = ", final_feedrate);
const float feedrate_inc = final_feedrate / 10, // Start at 1/10 of max & go up by 1/10 per step
fr_limit = final_feedrate * 0.99f; // Rounding-safe comparison value
float current_feedrate = 0;
planner.synchronize(); // Wait for moves to complete
for (j = 0; j < driver_count; j++)
L64xxManager.get_status(axis_index[j]); // Clear error flags
char temp_axis_string[2] = " ";
temp_axis_string[0] = axis_mon[0][0]; // Need a sprintf format string
//temp_axis_string[1] = '\n';
char gcode_string[80];
uint16_t status_composite = 0;
DEBUG_ECHOLNPGM(".\n.\n."); // Make feedrate outputs easier to read
do {
current_feedrate += feedrate_inc;
DEBUG_ECHOLNPGM("...feedrate = ", current_feedrate);
sprintf_P(gcode_string, PSTR("G0 %s%03d F%03d"), temp_axis_string, uint16_t(position_min), uint16_t(current_feedrate));
process_subcommands_now(gcode_string);
sprintf_P(gcode_string, PSTR("G0 %s%03d F%03d"), temp_axis_string, uint16_t(position_max), uint16_t(current_feedrate));
process_subcommands_now(gcode_string);
planner.synchronize();
for (j = 0; j < driver_count; j++) {
axis_status[j] = (~L64xxManager.get_status(axis_index[j])) & 0x0800; // Bits of interest are all active LOW
status_composite |= axis_status[j];
}
if (status_composite) break; // Break on any error
} while (current_feedrate < fr_limit);
DEBUG_ECHOPGM("Completed with ");
if (status_composite) {
DEBUG_ECHOLNPGM("errors");
#if ENABLED(L6470_CHITCHAT)
for (j = 0; j < driver_count; j++) {
if (j) DEBUG_ECHOPGM("...");
L64xxManager.error_status_decode(axis_status[j], axis_index[j],
sh.STATUS_AXIS_TH_SD, sh.STATUS_AXIS_TH_WRN,
sh.STATUS_AXIS_STEP_LOSS_A, sh.STATUS_AXIS_STEP_LOSS_B,
sh.STATUS_AXIS_OCD, sh.STATUS_AXIS_LAYOUT);
}
#endif
}
else
DEBUG_ECHOLNPGM("no errors");
L64xxManager.pause_monitor(false);
}
#endif // HAS_L64XX

4
Marlin/src/gcode/feature/powerloss/M1000.cpp

@ -35,8 +35,6 @@
#include "../../../lcd/e3v2/creality/dwin.h"
#elif ENABLED(DWIN_LCD_PROUI)
#include "../../../lcd/e3v2/proui/dwin.h"
#elif ENABLED(DWIN_CREALITY_LCD_JYERSUI)
#include "../../../lcd/e3v2/jyersui/dwin.h" // Temporary fix until it can be better implemented
#endif
#define DEBUG_OUT ENABLED(DEBUG_POWER_LOSS_RECOVERY)
@ -71,8 +69,6 @@ void GcodeSuite::M1000() {
ui.goto_screen(menu_job_recovery);
#elif HAS_DWIN_E3V2_BASIC
recovery.dwin_flag = true;
#elif ENABLED(DWIN_CREALITY_LCD_JYERSUI) // Temporary fix until it can be better implemented
CrealityDWIN.Popup_Handler(Resume);
#elif ENABLED(EXTENSIBLE_UI)
ExtUI::onPowerLossResume();
#else

45
Marlin/src/gcode/gcode.cpp

@ -53,7 +53,7 @@ GcodeSuite gcode;
#include "../feature/cancel_object.h"
#endif
#if ENABLED(LASER_MOVE_POWER)
#if ENABLED(LASER_FEATURE)
#include "../feature/spindle_laser.h"
#endif
@ -214,8 +214,11 @@ void GcodeSuite::get_destination_from_command() {
recovery.save();
#endif
if (parser.floatval('F') > 0)
if (parser.floatval('F') > 0) {
feedrate_mm_s = parser.value_feedrate();
// Update the cutter feed rate for use by M4 I set inline moves.
TERN_(LASER_FEATURE, cutter.feedrate_mm_m = MMS_TO_MMM(feedrate_mm_s));
}
#if BOTH(PRINTCOUNTER, HAS_EXTRUDERS)
if (!DEBUGGING(DRYRUN) && !skip_move)
@ -227,15 +230,29 @@ void GcodeSuite::get_destination_from_command() {
M165();
#endif
#if ENABLED(LASER_MOVE_POWER)
// Set the laser power in the planner to configure this move
if (parser.seen('S')) {
const float spwr = parser.value_float();
cutter.inline_power(TERN(SPINDLE_LASER_USE_PWM, cutter.power_to_range(cutter_power_t(round(spwr))), spwr > 0 ? 255 : 0));
#if ENABLED(LASER_FEATURE)
if (cutter.cutter_mode == CUTTER_MODE_CONTINUOUS || cutter.cutter_mode == CUTTER_MODE_DYNAMIC) {
// Set the cutter power in the planner to configure this move
cutter.last_feedrate_mm_m = 0;
if (WITHIN(parser.codenum, 1, TERN(ARC_SUPPORT, 3, 1)) || TERN0(BEZIER_CURVE_SUPPORT, parser.codenum == 5)) {
planner.laser_inline.status.isPowered = true;
if (parser.seen('I')) cutter.set_enabled(true); // This is set for backward LightBurn compatibility.
if (parser.seen('S')) {
const float v = parser.value_float(),
u = TERN(LASER_POWER_TRAP, v, cutter.power_to_range(v));
cutter.menuPower = cutter.unitPower = u;
cutter.inline_power(TERN(SPINDLE_LASER_USE_PWM, cutter.upower_to_ocr(u), u > 0 ? 255 : 0));
}
}
else if (parser.codenum == 0) {
// For dynamic mode we need to flag isPowered off, dynamic power is calculated in the stepper based on feedrate.
if (cutter.cutter_mode == CUTTER_MODE_DYNAMIC) planner.laser_inline.status.isPowered = false;
cutter.inline_power(0); // This is planner-based so only set power and do not disable inline control flags.
}
}
else if (ENABLED(LASER_MOVE_G0_OFF) && parser.codenum == 0) // G0
cutter.set_inline_enabled(false);
#endif
else if (parser.codenum == 0)
cutter.apply_power(0);
#endif // LASER_FEATURE
}
/**
@ -1044,14 +1061,6 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) {
case 919: M919(); break; // M919: Set stepper Chopper Times
#endif
#if HAS_L64XX
case 122: M122(); break; // M122: Report status
case 906: M906(); break; // M906: Set or get motor drive level
case 916: M916(); break; // M916: L6470 tuning: Increase drive level until thermal warning
case 917: M917(); break; // M917: L6470 tuning: Find minimum current thresholds
case 918: M918(); break; // M918: L6470 tuning: Increase speed until max or error
#endif
#if HAS_MICROSTEPS
case 350: M350(); break; // M350: Set microstepping mode. Warning: Steps per unit remains unchanged. S code sets stepping mode for all drivers.
case 351: M351(); break; // M351: Toggle MS1 MS2 pins directly, S# determines MS1 or MS2, X# sets the pin high/low.

17
Marlin/src/gcode/gcode.h

@ -155,7 +155,7 @@
* M120 - Enable endstops detection.
* M121 - Disable endstops detection.
*
* M122 - Debug stepper (Requires at least one _DRIVER_TYPE defined as TMC2130/2160/5130/5160/2208/2209/2660 or L6470)
* M122 - Debug stepper (Requires at least one _DRIVER_TYPE defined as TMC2130/2160/5130/5160/2208/2209/2660)
* M123 - Report fan tachometers. (Requires En_FAN_TACHO_PIN) Optionally set auto-report interval. (Requires AUTO_REPORT_FANS)
* M125 - Save current position and move to filament change position. (Requires PARK_HEAD_ON_PAUSE)
*
@ -286,7 +286,7 @@
* M871 - Print/reset/clear first layer temperature offset values. (Requires PTC_PROBE, PTC_BED, or PTC_HOTEND)
* M876 - Handle Prompt Response. (Requires HOST_PROMPT_SUPPORT and not EMERGENCY_PARSER)
* M900 - Get or Set Linear Advance K-factor. (Requires LIN_ADVANCE)
* M906 - Set or get motor current in milliamps using axis codes XYZE, etc. Report values if no axis codes given. (Requires at least one _DRIVER_TYPE defined as TMC2130/2160/5130/5160/2208/2209/2660 or L6470)
* M906 - Set or get motor current in milliamps using axis codes XYZE, etc. Report values if no axis codes given. (Requires at least one _DRIVER_TYPE defined as TMC2130/2160/5130/5160/2208/2209/2660)
* M907 - Set digital trimpot motor current using axis codes. (Requires a board with digital trimpots)
* M908 - Control digital trimpot directly. (Requires HAS_MOTOR_CURRENT_DAC or DIGIPOTSS_PIN)
* M909 - Print digipot/DAC current value. (Requires HAS_MOTOR_CURRENT_DAC)
@ -295,9 +295,6 @@
* M912 - Clear stepper driver overtemperature pre-warn condition flag. (Requires at least one _DRIVER_TYPE defined as TMC2130/2160/5130/5160/2208/2209/2660)
* M913 - Set HYBRID_THRESHOLD speed. (Requires HYBRID_THRESHOLD)
* M914 - Set StallGuard sensitivity. (Requires SENSORLESS_HOMING or SENSORLESS_PROBING)
* M916 - L6470 tuning: Increase KVAL_HOLD until thermal warning. (Requires at least one _DRIVER_TYPE L6470)
* M917 - L6470 tuning: Find minimum current thresholds. (Requires at least one _DRIVER_TYPE L6470)
* M918 - L6470 tuning: Increase speed until max or error. (Requires at least one _DRIVER_TYPE L6470)
* M919 - Get or Set motor Chopper Times (time_off, hysteresis_end, hysteresis_start) using axis codes XYZE, etc. If no parameters are given, report. (Requires at least one _DRIVER_TYPE defined as TMC2130/2160/5130/5160/2208/2209/2660)
* M951 - Set Magnetic Parking Extruder parameters. (Requires MAGNETIC_PARKING_EXTRUDER)
* M3426 - Read MCP3426 ADC over I2C. (Requires HAS_MCP3426_ADC)
@ -886,7 +883,7 @@ private:
static void M250_report(const bool forReplay=true);
#endif
#if HAS_DISPLAY_SLEEP
#if HAS_GCODE_M255
static void M255();
static void M255_report(const bool forReplay=true);
#endif
@ -1163,14 +1160,6 @@ private:
static void M919();
#endif
#if HAS_L64XX
static void M122();
static void M906();
static void M916();
static void M917();
static void M918();
#endif
#if HAS_MOTOR_CURRENT_SPI || HAS_MOTOR_CURRENT_PWM || HAS_MOTOR_CURRENT_I2C || HAS_MOTOR_CURRENT_DAC
static void M907();
#if HAS_MOTOR_CURRENT_SPI || HAS_MOTOR_CURRENT_PWM

89
Marlin/src/gcode/host/M114.cpp

@ -28,12 +28,6 @@
#if ENABLED(M114_DETAIL)
#if HAS_L64XX
#include "../../libs/L64XX/L64XX_Marlin.h"
#define DEBUG_OUT ENABLED(L6470_CHITCHAT)
#include "../../core/debug_out.h"
#endif
void report_all_axis_pos(const xyze_pos_t &pos, const uint8_t n=LOGICAL_AXES, const uint8_t precision=3) {
char str[12];
LOOP_L_N(a, n) {
@ -84,89 +78,6 @@
planner.synchronize();
#if HAS_L64XX
char temp_buf[80];
int32_t temp;
//#define ABS_POS_SIGN_MASK 0b1111 1111 1110 0000 0000 0000 0000 0000
#define ABS_POS_SIGN_MASK 0b11111111111000000000000000000000
#define REPORT_ABSOLUTE_POS(Q) do{ \
L64xxManager.say_axis(Q, false); \
temp = L6470_GETPARAM(L6470_ABS_POS,Q); \
if (temp & ABS_POS_SIGN_MASK) temp |= ABS_POS_SIGN_MASK; \
sprintf_P(temp_buf, PSTR(":%8ld "), temp); \
DEBUG_ECHO(temp_buf); \
}while(0)
DEBUG_ECHOPGM("\nL6470:");
#if AXIS_IS_L64XX(X)
REPORT_ABSOLUTE_POS(X);
#endif
#if AXIS_IS_L64XX(X2)
REPORT_ABSOLUTE_POS(X2);
#endif
#if AXIS_IS_L64XX(Y)
REPORT_ABSOLUTE_POS(Y);
#endif
#if AXIS_IS_L64XX(Y2)
REPORT_ABSOLUTE_POS(Y2);
#endif
#if AXIS_IS_L64XX(Z)
REPORT_ABSOLUTE_POS(Z);
#endif
#if AXIS_IS_L64XX(Z2)
REPORT_ABSOLUTE_POS(Z2);
#endif
#if AXIS_IS_L64XX(Z3)
REPORT_ABSOLUTE_POS(Z3);
#endif
#if AXIS_IS_L64XX(Z4)
REPORT_ABSOLUTE_POS(Z4);
#endif
#if AXIS_IS_L64XX(I)
REPORT_ABSOLUTE_POS(I);
#endif
#if AXIS_IS_L64XX(J)
REPORT_ABSOLUTE_POS(J);
#endif
#if AXIS_IS_L64XX(K)
REPORT_ABSOLUTE_POS(K);
#endif
#if AXIS_IS_L64XX(U)
REPORT_ABSOLUTE_POS(U);
#endif
#if AXIS_IS_L64XX(V)
REPORT_ABSOLUTE_POS(V);
#endif
#if AXIS_IS_L64XX(W)
REPORT_ABSOLUTE_POS(W);
#endif
#if AXIS_IS_L64XX(E0)
REPORT_ABSOLUTE_POS(E0);
#endif
#if AXIS_IS_L64XX(E1)
REPORT_ABSOLUTE_POS(E1);
#endif
#if AXIS_IS_L64XX(E2)
REPORT_ABSOLUTE_POS(E2);
#endif
#if AXIS_IS_L64XX(E3)
REPORT_ABSOLUTE_POS(E3);
#endif
#if AXIS_IS_L64XX(E4)
REPORT_ABSOLUTE_POS(E4);
#endif
#if AXIS_IS_L64XX(E5)
REPORT_ABSOLUTE_POS(E5);
#endif
#if AXIS_IS_L64XX(E6)
REPORT_ABSOLUTE_POS(E6);
#endif
#if AXIS_IS_L64XX(E7)
REPORT_ABSOLUTE_POS(E7);
#endif
SERIAL_EOL();
#endif // HAS_L64XX
SERIAL_ECHOPGM("Stepper:");
LOOP_LOGICAL_AXES(i) {
SERIAL_ECHOPGM_P((PGM_P)pgm_read_ptr(&SP_AXIS_LBL[i]), stepper.position((AxisEnum)i));

2
Marlin/src/gcode/lcd/M255.cpp

@ -36,7 +36,7 @@ void GcodeSuite::M255() {
const int m = parser.value_int();
ui.sleep_timeout_minutes = constrain(m, SLEEP_TIMEOUT_MIN, SLEEP_TIMEOUT_MAX);
#else
const int s = parser.value_int() * 60;
const unsigned int s = parser.value_ushort() * 60;
ui.lcd_backlight_timeout = constrain(s, LCD_BKL_TIMEOUT_MIN, LCD_BKL_TIMEOUT_MAX);
#endif
}

28
Marlin/src/gcode/motion/G2_G3.cpp

@ -214,9 +214,12 @@ void plan_arc(
const uint16_t segments = nominal_segment_mm > (MAX_ARC_SEGMENT_MM) ? CEIL(flat_mm / (MAX_ARC_SEGMENT_MM)) :
nominal_segment_mm < (MIN_ARC_SEGMENT_MM) ? _MAX(1, FLOOR(flat_mm / (MIN_ARC_SEGMENT_MM))) :
nominal_segments;
const float segment_mm = flat_mm / segments;
// Add hints to help optimize the move
PlannerHints hints;
#if ENABLED(SCARA_FEEDRATE_SCALING)
const float inv_duration = (scaled_fr_mm_s / flat_mm) * segments;
hints.inv_duration = (scaled_fr_mm_s / flat_mm) * segments;
#endif
/**
@ -288,9 +291,19 @@ void plan_arc(
int8_t arc_recalc_count = N_ARC_CORRECTION;
#endif
// An arc can always complete within limits from a speed which...
// a) is <= any configured maximum speed,
// b) does not require centripetal force greater than any configured maximum acceleration,
// c) allows the print head to stop in the remining length of the curve within all configured maximum accelerations.
// The last has to be calculated every time through the loop.
const float limiting_accel = _MIN(planner.settings.max_acceleration_mm_per_s2[axis_p], planner.settings.max_acceleration_mm_per_s2[axis_q]),
limiting_speed = _MIN(planner.settings.max_feedrate_mm_s[axis_p], planner.settings.max_acceleration_mm_per_s2[axis_q]),
limiting_speed_sqr = _MIN(sq(limiting_speed), limiting_accel * radius);
float arc_mm_remaining = flat_mm;
for (uint16_t i = 1; i < segments; i++) { // Iterate (segments-1) times
thermalManager.manage_heater();
thermalManager.task();
const millis_t ms = millis();
if (ELAPSED(ms, next_idle_ms)) {
next_idle_ms = ms + 200UL;
@ -342,7 +355,13 @@ void plan_arc(
planner.apply_leveling(raw);
#endif
if (!planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, 0 OPTARG(SCARA_FEEDRATE_SCALING, inv_duration)))
// calculate safe speed for stopping by the end of the arc
arc_mm_remaining -= segment_mm;
hints.curve_radius = i > 1 ? radius : 0;
hints.safe_exit_speed_sqr = _MIN(limiting_speed_sqr, 2 * limiting_accel * arc_mm_remaining);
if (!planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, hints))
break;
}
}
@ -363,7 +382,8 @@ void plan_arc(
planner.apply_leveling(raw);
#endif
planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, 0 OPTARG(SCARA_FEEDRATE_SCALING, inv_duration));
hints.curve_radius = 0;
planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, hints);
#if ENABLED(AUTO_BED_LEVELING_UBL)
ARC_LIJKUVW_CODE(

2
Marlin/src/gcode/queue.cpp

@ -384,7 +384,7 @@ inline bool process_line_done(uint8_t &sis, char (&buff)[MAX_CMD_SIZE], int &ind
buff[ind] = '\0'; // Of course, I'm a Terminator.
const bool is_empty = (ind == 0); // An empty line?
if (is_empty)
thermalManager.manage_heater(); // Keep sensors satisfied
thermalManager.task(); // Keep sensors satisfied
else
ind = 0; // Start a new line
return is_empty; // Inform the caller

26
Marlin/src/inc/Conditionals_LCD.h

@ -99,7 +99,7 @@
#define IS_ULTIPANEL 1
#define STD_ENCODER_PULSES_PER_STEP 2
#elif ANY(miniVIKI, VIKI2, WYH_L12864, ELB_FULL_GRAPHIC_CONTROLLER, AZSMZ_12864)
#elif ANY(miniVIKI, VIKI2, WYH_L12864, ELB_FULL_GRAPHIC_CONTROLLER, AZSMZ_12864, EMOTION_TECH_LCD)
#define DOGLCD
#define IS_DOGM_12864 1
@ -116,6 +116,9 @@
#define IS_U8GLIB_LM6059_AF 1
#elif ENABLED(AZSMZ_12864)
#define IS_U8GLIB_ST7565_64128N 1
#elif ENABLED(EMOTION_TECH_LCD)
#define IS_U8GLIB_ST7565_64128N 1
#define ST7565_VOLTAGE_DIVIDER_VALUE 0x07
#endif
#elif ENABLED(OLED_PANEL_TINYBOY2)
@ -461,7 +464,7 @@
#define HAS_DGUS_LCD_CLASSIC 1
#endif
#if ANY(HAS_DGUS_LCD_CLASSIC, DGUS_LCD_UI_RELOADED)
#if EITHER(HAS_DGUS_LCD_CLASSIC, DGUS_LCD_UI_RELOADED)
#define HAS_DGUS_LCD 1
#endif
@ -474,8 +477,6 @@
// Aliases for LCD features
#if EITHER(DWIN_CREALITY_LCD, DWIN_LCD_PROUI)
#define HAS_DWIN_E3V2_BASIC 1
#endif
#if EITHER(HAS_DWIN_E3V2_BASIC, DWIN_CREALITY_LCD_JYERSUI)
#define HAS_DWIN_E3V2 1
#endif
#if ENABLED(DWIN_LCD_PROUI)
@ -505,7 +506,7 @@
#endif
#endif
#if ANY(HAS_WIRED_LCD, EXTENSIBLE_UI, DWIN_LCD_PROUI, DWIN_CREALITY_LCD_JYERSUI)
#if ANY(HAS_WIRED_LCD, EXTENSIBLE_UI, DWIN_LCD_PROUI)
#define HAS_DISPLAY 1
#endif
@ -525,7 +526,7 @@
#define HAS_MANUAL_MOVE_MENU 1
#endif
#if ANY(HAS_MARLINUI_U8GLIB, EXTENSIBLE_UI, HAS_MARLINUI_HD44780, IS_TFTGLCD_PANEL, IS_DWIN_MARLINUI, DWIN_CREALITY_LCD_JYERSUI)
#if ANY(HAS_MARLINUI_U8GLIB, EXTENSIBLE_UI, HAS_MARLINUI_HD44780, IS_TFTGLCD_PANEL, IS_DWIN_MARLINUI)
#define CAN_SHOW_REMAINING_TIME 1
#endif
@ -1046,9 +1047,12 @@
#endif
/**
* Set a flag for any type of bed probe, including the paper-test
* Set flags for any form of bed probe
*/
#if ANY(HAS_Z_SERVO_PROBE, FIX_MOUNTED_PROBE, NOZZLE_AS_PROBE, TOUCH_MI_PROBE, Z_PROBE_ALLEN_KEY, Z_PROBE_SLED, SOLENOID_PROBE, SENSORLESS_PROBING, RACK_AND_PINION_PROBE, MAGLEV4)
#if ANY(TOUCH_MI_PROBE, Z_PROBE_ALLEN_KEY, SOLENOID_PROBE, Z_PROBE_SLED, RACK_AND_PINION_PROBE, SENSORLESS_PROBING, MAGLEV4, MAG_MOUNTED_PROBE)
#define HAS_STOWABLE_PROBE 1
#endif
#if ANY(HAS_STOWABLE_PROBE, HAS_Z_SERVO_PROBE, FIX_MOUNTED_PROBE, NOZZLE_AS_PROBE)
#define HAS_BED_PROBE 1
#endif
@ -1206,13 +1210,13 @@
#if NONE(Z_MIN_PROBE_USES_Z_MIN_ENDSTOP_PIN, HAS_DELTA_SENSORLESS_PROBING)
#define USES_Z_MIN_PROBE_PIN 1
#endif
#if Z_HOME_TO_MIN && TERN1(USES_Z_MIN_PROBE_PIN, ENABLED(USE_PROBE_FOR_Z_HOMING))
#if Z_HOME_TO_MIN && (DISABLED(USES_Z_MIN_PROBE_PIN) || ENABLED(USE_PROBE_FOR_Z_HOMING))
#define HOMING_Z_WITH_PROBE 1
#endif
#ifndef Z_PROBE_LOW_POINT
#define Z_PROBE_LOW_POINT -5
#endif
#if ENABLED(Z_PROBE_ALLEN_KEY)
#if EITHER(Z_PROBE_ALLEN_KEY, MAG_MOUNTED_PROBE)
#define PROBE_TRIGGERED_WHEN_STOWED_TEST 1 // Extra test for Allen Key Probe
#endif
#if MULTIPLE_PROBING > 1
@ -1427,7 +1431,7 @@
#define TFT_DEFAULT_ORIENTATION 0
#define TFT_RES_480x272
#define TFT_INTERFACE_FSMC
#elif ANY(MKS_ROBIN_TFT_V1_1R, LONGER_LK_TFT28) // ILI9328 or R61505
#elif EITHER(MKS_ROBIN_TFT_V1_1R, LONGER_LK_TFT28) // ILI9328 or R61505
#define TFT_DEFAULT_ORIENTATION (TFT_EXCHANGE_XY | TFT_INVERT_X | TFT_INVERT_Y)
#define TFT_RES_320x240
#define TFT_INTERFACE_FSMC

18
Marlin/src/inc/Conditionals_adv.h

@ -79,6 +79,10 @@
#define SERVO_DELAY { 50 }
#endif
#if !HAS_STOWABLE_PROBE
#undef PROBE_DEPLOY_STOW_MENU
#endif
#if !HAS_EXTRUDERS
#define NO_VOLUMETRICS
#undef TEMP_SENSOR_0
@ -556,16 +560,6 @@
#endif
#endif
// Probe Temperature Compensation
#if !TEMP_SENSOR_PROBE
#undef PTC_PROBE
#endif
#if !TEMP_SENSOR_BED
#undef PTC_BED
#endif
#if !HAS_EXTRUDERS
#undef PTC_HOTEND
#endif
#if ANY(PTC_PROBE, PTC_BED, PTC_HOTEND)
#define HAS_PTC 1
#endif
@ -587,6 +581,10 @@
#define HAS_PRINT_PROGRESS 1
#endif
#if ANY(HAS_MARLINUI_MENU, ULTIPANEL_FEEDMULTIPLY, SOFT_RESET_ON_KILL)
#define HAS_ENCODER_ACTION 1
#endif
#if STATUS_MESSAGE_TIMEOUT_SEC > 0
#define HAS_STATUS_MESSAGE_TIMEOUT 1
#endif

46
Marlin/src/inc/Conditionals_post.h

@ -48,7 +48,7 @@
// Set additional flags to let HALs choose in their Conditionals_post.h
#if ANY(FLASH_EEPROM_EMULATION, SRAM_EEPROM_EMULATION, SDCARD_EEPROM_EMULATION, QSPI_EEPROM)
#define USE_EMULATED_EEPROM 1
#elif ANY(I2C_EEPROM, SPI_EEPROM)
#elif EITHER(I2C_EEPROM, SPI_EEPROM)
#define USE_WIRED_EEPROM 1
#elif ENABLED(IIC_BL24CXX_EEPROM)
// nothing
@ -1610,7 +1610,7 @@
#define HAS_X_MS_PINS 1
#endif
#if PIN_EXISTS(X2_ENABLE) || AXIS_IS_L64XX(X2) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(X2))
#if PIN_EXISTS(X2_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(X2))
#define HAS_X2_ENABLE 1
#endif
#if PIN_EXISTS(X2_DIR)
@ -1631,7 +1631,7 @@
#endif
#if HAS_Y_AXIS
#if PIN_EXISTS(Y_ENABLE) || AXIS_IS_L64XX(Y) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(Y))
#if PIN_EXISTS(Y_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(Y))
#define HAS_Y_ENABLE 1
#endif
#if PIN_EXISTS(Y_DIR)
@ -1644,7 +1644,7 @@
#define HAS_Y_MS_PINS 1
#endif
#if PIN_EXISTS(Y2_ENABLE) || AXIS_IS_L64XX(Y2) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(Y2))
#if PIN_EXISTS(Y2_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(Y2))
#define HAS_Y2_ENABLE 1
#endif
#if PIN_EXISTS(Y2_DIR)
@ -1664,7 +1664,7 @@
#endif
#if HAS_Z_AXIS
#if PIN_EXISTS(Z_ENABLE) || AXIS_IS_L64XX(Z) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(Z))
#if PIN_EXISTS(Z_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(Z))
#define HAS_Z_ENABLE 1
#endif
#if PIN_EXISTS(Z_DIR)
@ -1684,7 +1684,7 @@
#endif
#if NUM_Z_STEPPERS >= 2
#if PIN_EXISTS(Z2_ENABLE) || AXIS_IS_L64XX(Z2) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(Z2))
#if PIN_EXISTS(Z2_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(Z2))
#define HAS_Z2_ENABLE 1
#endif
#if PIN_EXISTS(Z2_DIR)
@ -1699,7 +1699,7 @@
#endif
#if NUM_Z_STEPPERS >= 3
#if PIN_EXISTS(Z3_ENABLE) || AXIS_IS_L64XX(Z3) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(Z3))
#if PIN_EXISTS(Z3_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(Z3))
#define HAS_Z3_ENABLE 1
#endif
#if PIN_EXISTS(Z3_DIR)
@ -1714,7 +1714,7 @@
#endif
#if NUM_Z_STEPPERS >= 4
#if PIN_EXISTS(Z4_ENABLE) || AXIS_IS_L64XX(Z4) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(Z4))
#if PIN_EXISTS(Z4_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(Z4))
#define HAS_Z4_ENABLE 1
#endif
#if PIN_EXISTS(Z4_DIR)
@ -1729,7 +1729,7 @@
#endif
#if HAS_I_AXIS
#if PIN_EXISTS(I_ENABLE) || AXIS_IS_L64XX(I) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(I))
#if PIN_EXISTS(I_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(I))
#define HAS_I_ENABLE 1
#endif
#if PIN_EXISTS(I_DIR)
@ -1749,7 +1749,7 @@
#endif
#if HAS_J_AXIS
#if PIN_EXISTS(J_ENABLE) || AXIS_IS_L64XX(J) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(J))
#if PIN_EXISTS(J_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(J))
#define HAS_J_ENABLE 1
#endif
#if PIN_EXISTS(J_DIR)
@ -1769,7 +1769,7 @@
#endif
#if HAS_K_AXIS
#if PIN_EXISTS(K_ENABLE) || AXIS_IS_L64XX(K) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(K))
#if PIN_EXISTS(K_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(K))
#define HAS_K_ENABLE 1
#endif
#if PIN_EXISTS(K_DIR)
@ -1789,7 +1789,7 @@
#endif
#if HAS_U_AXIS
#if PIN_EXISTS(U_ENABLE) || AXIS_IS_L64XX(U) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(U))
#if PIN_EXISTS(U_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(U))
#define HAS_U_ENABLE 1
#endif
#if PIN_EXISTS(U_DIR)
@ -1809,7 +1809,7 @@
#endif
#if HAS_V_AXIS
#if PIN_EXISTS(V_ENABLE) || AXIS_IS_L64XX(V) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(V))
#if PIN_EXISTS(V_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(V))
#define HAS_V_ENABLE 1
#endif
#if PIN_EXISTS(V_DIR)
@ -1829,7 +1829,7 @@
#endif
#if HAS_W_AXIS
#if PIN_EXISTS(W_ENABLE) || AXIS_IS_L64XX(W) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(W))
#if PIN_EXISTS(W_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(W))
#define HAS_W_ENABLE 1
#endif
#if PIN_EXISTS(W_DIR)
@ -1851,7 +1851,7 @@
// Extruder steppers and solenoids
#if HAS_EXTRUDERS
#if PIN_EXISTS(E0_ENABLE) || AXIS_IS_L64XX(E0) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E0))
#if PIN_EXISTS(E0_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E0))
#define HAS_E0_ENABLE 1
#endif
#if PIN_EXISTS(E0_DIR)
@ -1865,7 +1865,7 @@
#endif
#if E_STEPPERS > 1 || ENABLED(E_DUAL_STEPPER_DRIVERS)
#if PIN_EXISTS(E1_ENABLE) || AXIS_IS_L64XX(E1) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E1))
#if PIN_EXISTS(E1_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E1))
#define HAS_E1_ENABLE 1
#endif
#if PIN_EXISTS(E1_DIR)
@ -1880,7 +1880,7 @@
#endif
#if E_STEPPERS > 2
#if PIN_EXISTS(E2_ENABLE) || AXIS_IS_L64XX(E2) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E2))
#if PIN_EXISTS(E2_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E2))
#define HAS_E2_ENABLE 1
#endif
#if PIN_EXISTS(E2_DIR)
@ -1895,7 +1895,7 @@
#endif
#if E_STEPPERS > 3
#if PIN_EXISTS(E3_ENABLE) || AXIS_IS_L64XX(E3) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E3))
#if PIN_EXISTS(E3_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E3))
#define HAS_E3_ENABLE 1
#endif
#if PIN_EXISTS(E3_DIR)
@ -1910,7 +1910,7 @@
#endif
#if E_STEPPERS > 4
#if PIN_EXISTS(E4_ENABLE) || AXIS_IS_L64XX(E4) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E4))
#if PIN_EXISTS(E4_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E4))
#define HAS_E4_ENABLE 1
#endif
#if PIN_EXISTS(E4_DIR)
@ -1925,7 +1925,7 @@
#endif
#if E_STEPPERS > 5
#if PIN_EXISTS(E5_ENABLE) || AXIS_IS_L64XX(E5) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E5))
#if PIN_EXISTS(E5_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E5))
#define HAS_E5_ENABLE 1
#endif
#if PIN_EXISTS(E5_DIR)
@ -1940,7 +1940,7 @@
#endif
#if E_STEPPERS > 6
#if PIN_EXISTS(E6_ENABLE) || AXIS_IS_L64XX(E6) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E6))
#if PIN_EXISTS(E6_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E6))
#define HAS_E6_ENABLE 1
#endif
#if PIN_EXISTS(E6_DIR)
@ -1955,7 +1955,7 @@
#endif
#if E_STEPPERS > 7
#if PIN_EXISTS(E7_ENABLE) || AXIS_IS_L64XX(E7) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E7))
#if PIN_EXISTS(E7_ENABLE) || (ENABLED(SOFTWARE_DRIVER_ENABLE) && AXIS_IS_TMC(E7))
#define HAS_E7_ENABLE 1
#endif
#if PIN_EXISTS(E7_DIR)
@ -3428,7 +3428,7 @@
* Advanced Pause - Filament Change
*/
#if ENABLED(ADVANCED_PAUSE_FEATURE)
#if ANY(HAS_MARLINUI_MENU, EXTENSIBLE_UI, DWIN_LCD_PROUI, DWIN_CREALITY_LCD_JYERSUI) || BOTH(EMERGENCY_PARSER, HOST_PROMPT_SUPPORT)
#if ANY(HAS_MARLINUI_MENU, EXTENSIBLE_UI, DWIN_LCD_PROUI) || BOTH(EMERGENCY_PARSER, HOST_PROMPT_SUPPORT)
#define M600_PURGE_MORE_RESUMABLE 1
#endif
#ifndef FILAMENT_CHANGE_SLOW_LOAD_LENGTH

156
Marlin/src/inc/SanityCheck.h

@ -350,7 +350,7 @@
#elif defined(HAVE_TMC2208)
#error "HAVE_TMC2208 is now [AXIS]_DRIVER_TYPE TMC2208."
#elif defined(HAVE_L6470DRIVER)
#error "HAVE_L6470DRIVER is now [AXIS]_DRIVER_TYPE L6470."
#error "HAVE_L6470DRIVER is obsolete. L64xx stepper drivers are no longer supported in Marlin."
#elif defined(X_IS_TMC) || defined(X2_IS_TMC) || defined(Y_IS_TMC) || defined(Y2_IS_TMC) || defined(Z_IS_TMC) || defined(Z2_IS_TMC) || defined(Z3_IS_TMC) \
|| defined(E0_IS_TMC) || defined(E1_IS_TMC) || defined(E2_IS_TMC) || defined(E3_IS_TMC) || defined(E4_IS_TMC) || defined(E5_IS_TMC) || defined(E6_IS_TMC) || defined(E7_IS_TMC)
#error "[AXIS]_IS_TMC is now [AXIS]_DRIVER_TYPE TMC26X."
@ -363,9 +363,6 @@
#elif defined(X_IS_TMC2208) || defined(X2_IS_TMC2208) || defined(Y_IS_TMC2208) || defined(Y2_IS_TMC2208) || defined(Z_IS_TMC2208) || defined(Z2_IS_TMC2208) || defined(Z3_IS_TMC2208) \
|| defined(E0_IS_TMC2208) || defined(E1_IS_TMC2208) || defined(E2_IS_TMC2208) || defined(E3_IS_TMC2208) || defined(E4_IS_TMC2208) || defined(E5_IS_TMC2208) || defined(E6_IS_TMC2208) || defined(E7_IS_TMC2208)
#error "[AXIS]_IS_TMC2208 is now [AXIS]_DRIVER_TYPE TMC2208."
#elif defined(X_IS_L6470) || defined(X2_IS_L6470) || defined(Y_IS_L6470) || defined(Y2_IS_L6470) || defined(Z_IS_L6470) || defined(Z2_IS_L6470) || defined(Z3_IS_L6470) \
|| defined(E0_IS_L6470) || defined(E1_IS_L6470) || defined(E2_IS_L6470) || defined(E3_IS_L6470) || defined(E4_IS_L6470) || defined(E5_IS_L6470) || defined(E6_IS_L6470) || defined(E7_IS_L6470)
#error "[AXIS]_IS_L6470 is now [AXIS]_DRIVER_TYPE L6470."
#elif defined(AUTOMATIC_CURRENT_CONTROL)
#error "AUTOMATIC_CURRENT_CONTROL is now MONITOR_DRIVER_STATUS."
#elif defined(FILAMENT_CHANGE_LOAD_LENGTH)
@ -447,6 +444,16 @@
#error "SPINDLE_LASER_ACTIVE_HIGH is now SPINDLE_LASER_ACTIVE_STATE."
#elif defined(SPINDLE_LASER_ENABLE_INVERT)
#error "SPINDLE_LASER_ENABLE_INVERT is now SPINDLE_LASER_ACTIVE_STATE."
#elif defined(LASER_POWER_INLINE)
#error "LASER_POWER_INLINE is not required, inline mode is enabled with 'M3 I' and disabled with 'M5 I'."
#elif defined(LASER_POWER_INLINE_TRAPEZOID)
#error "LASER_POWER_INLINE_TRAPEZOID is now LASER_POWER_TRAP."
#elif defined(LASER_POWER_INLINE_TRAPEZOID_CONT)
#error "LASER_POWER_INLINE_TRAPEZOID_CONT is replaced with LASER_POWER_TRAP."
#elif defined(LASER_POWER_INLINE_TRAPEZOID_PER)
#error "LASER_POWER_INLINE_TRAPEZOID_CONT_PER replaced with LASER_POWER_TRAP."
#elif defined(LASER_POWER_INLINE_CONTINUOUS)
#error "LASER_POWER_INLINE_CONTINUOUS is not required, inline mode is enabled with 'M3 I' and disabled with 'M5 I'."
#elif defined(CUTTER_POWER_DISPLAY)
#error "CUTTER_POWER_DISPLAY is now CUTTER_POWER_UNIT."
#elif defined(CHAMBER_HEATER_PIN)
@ -595,6 +602,8 @@
#error "ARC_SUPPORT no longer uses ARC_SEGMENTS_PER_R."
#elif ENABLED(ARC_SUPPORT) && (!defined(MIN_ARC_SEGMENT_MM) || !defined(MAX_ARC_SEGMENT_MM))
#error "ARC_SUPPORT now requires MIN_ARC_SEGMENT_MM and MAX_ARC_SEGMENT_MM."
#elif defined(LASER_POWER_INLINE)
#error "LASER_POWER_INLINE is obsolete."
#elif defined(SPINDLE_LASER_PWM)
#error "SPINDLE_LASER_PWM (true) is now set with SPINDLE_LASER_USE_PWM (enabled)."
#elif ANY(IS_RAMPS_EEB, IS_RAMPS_EEF, IS_RAMPS_EFB, IS_RAMPS_EFF, IS_RAMPS_SF)
@ -635,6 +644,26 @@
#error "LEVEL_CENTER_TOO is now BED_TRAMMING_INCLUDE_CENTER."
#endif
// L64xx stepper drivers have been removed
#define _L6470 0x6470
#define _L6474 0x6474
#define _L6480 0x6480
#define _POWERSTEP01 0xF00D
#if HAS_DRIVER(L6470)
#error "L6470 stepper drivers are no longer supported in Marlin."
#elif HAS_DRIVER(L6474)
#error "L6474 stepper drivers are no longer supported in Marlin."
#elif HAS_DRIVER(L6480)
#error "L6480 stepper drivers are no longer supported in Marlin."
#elif HAS_DRIVER(POWERSTEP01)
#error "POWERSTEP01 stepper drivers are no longer supported in Marlin."
#endif
#undef _L6470
#undef _L6474
#undef _L6480
#undef _POWERSTEP01
// Check AXIS_RELATIVE_MODES
constexpr float arm[] = AXIS_RELATIVE_MODES;
static_assert(COUNT(arm) == LOGICAL_AXES, "AXIS_RELATIVE_MODES must contain " _LOGICAL_AXES_STR "elements.");
@ -654,34 +683,46 @@ static_assert(COUNT(arm) == LOGICAL_AXES, "AXIS_RELATIVE_MODES must contain " _L
#endif
#endif
#ifdef PTC_PROBE_START
constexpr auto _ptc_sample_start = PTC_PROBE_START;
constexpr decltype(_ptc_sample_start) _test_ptc_sample_start = 12.3f;
static_assert(_test_ptc_sample_start != 12.3f, "PTC_PROBE_START must be a whole number.");
#endif
#ifdef PTC_PROBE_RES
constexpr auto _ptc_sample_res = PTC_PROBE_RES;
constexpr decltype(_ptc_sample_res) _test_ptc_sample_res = 12.3f;
static_assert(_test_ptc_sample_res != 12.3f, "PTC_PROBE_RES must be a whole number.");
#endif
#ifdef PTC_BED_START
constexpr auto _btc_sample_start = PTC_BED_START;
constexpr decltype(_btc_sample_start) _test_btc_sample_start = 12.3f;
static_assert(_test_btc_sample_start != 12.3f, "PTC_BED_START must be a whole number.");
#endif
#ifdef PTC_BED_RES
constexpr auto _btc_sample_res = PTC_BED_RES;
constexpr decltype(_btc_sample_res) _test_btc_sample_res = 12.3f;
static_assert(_test_btc_sample_res != 12.3f, "PTC_BED_RES must be a whole number.");
#if ENABLED(PTC_PROBE)
#if !TEMP_SENSOR_PROBE
#error "PTC_PROBE requires a probe with a thermistor."
#endif
#ifdef PTC_PROBE_START
constexpr auto _ptc_sample_start = PTC_PROBE_START;
constexpr decltype(_ptc_sample_start) _test_ptc_sample_start = 12.3f;
static_assert(_test_ptc_sample_start != 12.3f, "PTC_PROBE_START must be a whole number.");
#endif
#ifdef PTC_PROBE_RES
constexpr auto _ptc_sample_res = PTC_PROBE_RES;
constexpr decltype(_ptc_sample_res) _test_ptc_sample_res = 12.3f;
static_assert(_test_ptc_sample_res != 12.3f, "PTC_PROBE_RES must be a whole number.");
#endif
#if ENABLED(PTC_BED) && defined(PTC_PROBE_TEMP)
constexpr auto _btc_probe_temp = PTC_PROBE_TEMP;
constexpr decltype(_btc_probe_temp) _test_btc_probe_temp = 12.3f;
static_assert(_test_btc_probe_temp != 12.3f, "PTC_PROBE_TEMP must be a whole number.");
#endif
#endif
#ifdef PTC_PROBE_TEMP
constexpr auto _btc_probe_temp = PTC_PROBE_TEMP;
constexpr decltype(_btc_probe_temp) _test_btc_probe_temp = 12.3f;
static_assert(_test_btc_probe_temp != 12.3f, "PTC_PROBE_TEMP must be a whole number.");
#if ENABLED(PTC_BED)
#if !TEMP_SENSOR_BED
#error "PTC_BED requires a bed with a thermistor."
#endif
#ifdef PTC_BED_START
constexpr auto _btc_sample_start = PTC_BED_START;
constexpr decltype(_btc_sample_start) _test_btc_sample_start = 12.3f;
static_assert(_test_btc_sample_start != 12.3f, "PTC_BED_START must be a whole number.");
#endif
#ifdef PTC_BED_RES
constexpr auto _btc_sample_res = PTC_BED_RES;
constexpr decltype(_btc_sample_res) _test_btc_sample_res = 12.3f;
static_assert(_test_btc_sample_res != 12.3f, "PTC_BED_RES must be a whole number.");
#endif
#endif
#if ENABLED(PTC_HOTEND)
#if EXTRUDERS != 1
#error "PTC_HOTEND only works with a single extruder."
#error "PTC_HOTEND requires a single extruder."
#endif
#ifdef PTC_HOTEND_START
constexpr auto _etc_sample_start = PTC_HOTEND_START;
@ -861,7 +902,7 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS
#error "PROGRESS_MSG_EXPIRE must be greater than or equal to 0."
#endif
#elif ENABLED(LCD_SET_PROGRESS_MANUALLY) && NONE(HAS_MARLINUI_U8GLIB, HAS_GRAPHICAL_TFT, HAS_MARLINUI_HD44780, EXTENSIBLE_UI, HAS_DWIN_E3V2, IS_DWIN_MARLINUI)
#error "LCD_SET_PROGRESS_MANUALLY requires LCD_PROGRESS_BAR, Character LCD, Graphical LCD, TFT, DWIN_CREALITY_LCD, DWIN_LCD_PROUI, DWIN_CREALITY_LCD_JYERSUI, DWIN_MARLINUI_*, OR EXTENSIBLE_UI."
#error "LCD_SET_PROGRESS_MANUALLY requires LCD_PROGRESS_BAR, Character LCD, Graphical LCD, TFT, DWIN_CREALITY_LCD, DWIN_LCD_PROUI, DWIN_MARLINUI_*, OR EXTENSIBLE_UI."
#endif
#if ENABLED(USE_M73_REMAINING_TIME) && DISABLED(LCD_SET_PROGRESS_MANUALLY)
@ -1615,8 +1656,8 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS
*/
#if 1 < 0 \
+ (DISABLED(BLTOUCH) && HAS_Z_SERVO_PROBE) \
+ COUNT_ENABLED(PROBE_MANUALLY, BLTOUCH, FIX_MOUNTED_PROBE, NOZZLE_AS_PROBE, TOUCH_MI_PROBE, SOLENOID_PROBE, Z_PROBE_ALLEN_KEY, Z_PROBE_SLED, RACK_AND_PINION_PROBE, SENSORLESS_PROBING, MAGLEV4)
#error "Please enable only one probe option: PROBE_MANUALLY, SENSORLESS_PROBING, BLTOUCH, FIX_MOUNTED_PROBE, NOZZLE_AS_PROBE, TOUCH_MI_PROBE, SOLENOID_PROBE, Z_PROBE_ALLEN_KEY, Z_PROBE_SLED, MAGLEV4, or Z Servo."
+ COUNT_ENABLED(PROBE_MANUALLY, BLTOUCH, FIX_MOUNTED_PROBE, NOZZLE_AS_PROBE, TOUCH_MI_PROBE, SOLENOID_PROBE, Z_PROBE_ALLEN_KEY, Z_PROBE_SLED, RACK_AND_PINION_PROBE, SENSORLESS_PROBING, MAGLEV4, MAG_MOUNTED_PROBE)
#error "Please enable only one probe option: PROBE_MANUALLY, SENSORLESS_PROBING, BLTOUCH, FIX_MOUNTED_PROBE, NOZZLE_AS_PROBE, TOUCH_MI_PROBE, SOLENOID_PROBE, Z_PROBE_ALLEN_KEY, Z_PROBE_SLED, MAGLEV4, MAG_MOUNTED_PROBE or Z Servo."
#endif
#if HAS_BED_PROBE
@ -1722,13 +1763,20 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS
#endif
#endif
/**
* Mag mounted probe requirements
*/
#if BOTH(MAG_MOUNTED_PROBE, USE_PROBE_FOR_Z_HOMING) && DISABLED(Z_SAFE_HOMING)
#error "MAG_MOUNTED_PROBE requires Z_SAFE_HOMING if it's being used to home Z."
#endif
/**
* MagLev V4 probe requirements
*/
#if ENABLED(MAGLEV4)
#if !PIN_EXISTS(MAGLEV_TRIGGER)
#error "MAGLEV4 requires MAGLEV_TRIGGER_PIN to be defined."
#elif DISABLED(Z_SAFE_HOMING)
#elif ENABLED(HOMING_Z_WITH_PROBE) && DISABLED(Z_SAFE_HOMING)
#error "MAGLEV4 requires Z_SAFE_HOMING."
#elif MAGLEV_TRIGGER_DELAY != 15
#error "MAGLEV_TRIGGER_DELAY should not be changed. Comment out this line to continue."
@ -2834,7 +2882,7 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS
+ COUNT_ENABLED(ANYCUBIC_LCD_I3MEGA, ANYCUBIC_LCD_CHIRON, ANYCUBIC_TFT35) \
+ COUNT_ENABLED(DGUS_LCD_UI_ORIGIN, DGUS_LCD_UI_FYSETC, DGUS_LCD_UI_HIPRECY, DGUS_LCD_UI_MKS, DGUS_LCD_UI_RELOADED) \
+ COUNT_ENABLED(ENDER2_STOCKDISPLAY, CR10_STOCKDISPLAY) \
+ COUNT_ENABLED(DWIN_CREALITY_LCD, DWIN_LCD_PROUI, DWIN_CREALITY_LCD_JYERSUI, DWIN_MARLINUI_PORTRAIT, DWIN_MARLINUI_LANDSCAPE) \
+ COUNT_ENABLED(DWIN_CREALITY_LCD, DWIN_LCD_PROUI, DWIN_MARLINUI_PORTRAIT, DWIN_MARLINUI_LANDSCAPE) \
+ COUNT_ENABLED(FYSETC_MINI_12864_X_X, FYSETC_MINI_12864_1_2, FYSETC_MINI_12864_2_0, FYSETC_GENERIC_12864_1_1) \
+ COUNT_ENABLED(LCD_SAINSMART_I2C_1602, LCD_SAINSMART_I2C_2004) \
+ COUNT_ENABLED(MKS_12864OLED, MKS_12864OLED_SSD1306) \
@ -3502,7 +3550,7 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS
/**
* TMC SPI Chaining
*/
#define IN_CHAIN(A) ((A##_CHAIN_POS > 0) && !HAS_L64XX)
#define IN_CHAIN(A) A##_CHAIN_POS > 0
#if IN_CHAIN(X ) || IN_CHAIN(Y ) || IN_CHAIN(Z ) || IN_CHAIN(X2) || IN_CHAIN(Y2) || IN_CHAIN(Z2) || IN_CHAIN(Z3) || IN_CHAIN(Z4) \
|| IN_CHAIN(E0) || IN_CHAIN(E1) || IN_CHAIN(E2) || IN_CHAIN(E3) || IN_CHAIN(E4) || IN_CHAIN(E5) || IN_CHAIN(E6) || IN_CHAIN(E7)
#define BAD_CHAIN(A) (IN_CHAIN(A) && !PIN_EXISTS(A##_CS))
@ -3567,13 +3615,6 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS
#endif
#undef IN_CHAIN
/**
* L64XX requirement
*/
#if HAS_L64XX && NUM_AXES > 3
#error "L64XX requires NUM_AXES <= 3. Homing with L64XX is not yet implemented for NUM_AXES > 3."
#endif
/**
* Digipot requirement
*/
@ -3841,37 +3882,26 @@ static_assert(_PLUS_TEST(4), "HOMING_FEEDRATE_MM_M values must be positive.");
#error "CUTTER_POWER_UNIT must be PWM255, PERCENT, RPM, or SERVO."
#endif
#if ENABLED(LASER_POWER_INLINE)
#if ENABLED(LASER_FEATURE)
#if ENABLED(SPINDLE_CHANGE_DIR)
#error "SPINDLE_CHANGE_DIR and LASER_POWER_INLINE are incompatible."
#elif ENABLED(LASER_MOVE_G0_OFF) && DISABLED(LASER_MOVE_POWER)
#error "LASER_MOVE_G0_OFF requires LASER_MOVE_POWER."
#error "SPINDLE_CHANGE_DIR and LASER_FEATURE are incompatible."
#elif ENABLED(LASER_MOVE_G0_OFF)
#error "LASER_MOVE_G0_OFF is no longer required, G0 and G28 cannot apply power."
#elif ENABLED(LASER_MOVE_G28_OFF)
#error "LASER_MOVE_G0_OFF is no longer required, G0 and G28 cannot apply power."
#elif ENABLED(LASER_MOVE_POWER)
#error "LASER_MOVE_POWER is no longer applicable."
#endif
#if ENABLED(LASER_POWER_INLINE_TRAPEZOID)
#if ENABLED(LASER_POWER_TRAP)
#if DISABLED(SPINDLE_LASER_USE_PWM)
#error "LASER_POWER_INLINE_TRAPEZOID requires SPINDLE_LASER_USE_PWM to function."
#elif ENABLED(S_CURVE_ACCELERATION)
//#ifndef LASER_POWER_INLINE_S_CURVE_ACCELERATION_WARN
// #define LASER_POWER_INLINE_S_CURVE_ACCELERATION_WARN
// #warning "Combining LASER_POWER_INLINE_TRAPEZOID with S_CURVE_ACCELERATION may result in unintended behavior."
//#endif
#error "LASER_POWER_TRAP requires SPINDLE_LASER_USE_PWM to function."
#endif
#endif
#if ENABLED(LASER_POWER_INLINE_INVERT)
//#ifndef LASER_POWER_INLINE_INVERT_WARN
// #define LASER_POWER_INLINE_INVERT_WARN
// #warning "Enabling LASER_POWER_INLINE_INVERT means that `M5` won't kill the laser immediately; use `M5 I` instead."
//#endif
#endif
#else
#if SPINDLE_LASER_POWERUP_DELAY < 1
#error "SPINDLE_LASER_POWERUP_DELAY must be greater than 0."
#elif SPINDLE_LASER_POWERDOWN_DELAY < 1
#error "SPINDLE_LASER_POWERDOWN_DELAY must be greater than 0."
#elif ENABLED(LASER_MOVE_POWER)
#error "LASER_MOVE_POWER requires LASER_POWER_INLINE."
#elif ANY(LASER_POWER_INLINE_TRAPEZOID, LASER_POWER_INLINE_INVERT, LASER_MOVE_G0_OFF, LASER_MOVE_POWER)
#error "Enabled an inline laser feature without inline laser power being enabled."
#endif
#endif
@ -3889,7 +3919,7 @@ static_assert(_PLUS_TEST(4), "HOMING_FEEDRATE_MM_M values must be positive.");
#error "SPINDLE_LASER_PWM_PIN not assigned to a PWM pin."
#elif !defined(SPINDLE_LASER_PWM_INVERT)
#error "SPINDLE_LASER_PWM_INVERT is required for (SPINDLE|LASER)_FEATURE."
#elif !(defined(SPEED_POWER_INTERCEPT) && defined(SPEED_POWER_MIN) && defined(SPEED_POWER_MAX) && defined(SPEED_POWER_STARTUP))
#elif !(defined(SPEED_POWER_MIN) && defined(SPEED_POWER_MAX) && defined(SPEED_POWER_STARTUP))
#error "SPINDLE_LASER_USE_PWM equation constant(s) missing."
#elif _PIN_CONFLICT(X_MIN)
#error "SPINDLE_LASER_USE_PWM pin conflicts with X_MIN_PIN."

2
Marlin/src/inc/Version.h

@ -52,7 +52,7 @@
* to alert users to major changes.
*/
#define MARLIN_HEX_VERSION 02010000
#define MARLIN_HEX_VERSION 02010100
#ifndef REQUIRED_CONFIGURATION_H_VERSION
#define REQUIRED_CONFIGURATION_H_VERSION MARLIN_HEX_VERSION
#endif

2
Marlin/src/lcd/dogm/status_screen_DOGM.cpp

@ -670,7 +670,7 @@ void MarlinUI::draw_status_screen() {
// Laser / Spindle
#if DO_DRAW_CUTTER
if (cutter.isReady && PAGE_CONTAINS(STATUS_CUTTER_TEXT_Y - INFO_FONT_ASCENT, STATUS_CUTTER_TEXT_Y - 1)) {
if (cutter.isReadyForUI && PAGE_CONTAINS(STATUS_CUTTER_TEXT_Y - INFO_FONT_ASCENT, STATUS_CUTTER_TEXT_Y - 1)) {
#if CUTTER_UNIT_IS(PERCENT)
lcd_put_u8str(STATUS_CUTTER_TEXT_X, STATUS_CUTTER_TEXT_Y, cutter_power2str(cutter.unitPower));
#elif CUTTER_UNIT_IS(RPM)

8
Marlin/src/lcd/dogm/u8g_dev_st7565_64128n_HAL.cpp

@ -74,7 +74,6 @@
#define ST7565_ON(N) ((N) ? 0xAF : 0xAE)
#define ST7565_OUT_MODE(N) ((N) ? 0xC8 : 0xC0)
#define ST7565_POWER_CONTROL(N) (0x28 | (N))
#define ST7565_V0_RATIO(N) (0x10 | ((N) & 0x7))
#define ST7565_V5_RATIO(N) (0x20 | ((N) & 0x7))
#define ST7565_CONTRAST(N) (0x81), (N)
@ -106,11 +105,14 @@ static const uint8_t u8g_dev_st7565_64128n_HAL_init_seq[] PROGMEM = {
ST7565_POWER_CONTROL(0x7), // power control: turn on voltage follower
U8G_ESC_DLY(50), // delay 50 ms
ST7565_V0_RATIO(0), // Set V0 voltage resistor ratio. Setting for controlling brightness of Displaytech 64128N
#ifdef ST7565_VOLTAGE_DIVIDER_VALUE
// Set V5 voltage resistor ratio. Affects brightness of Displaytech 64128N
ST7565_V5_RATIO(ST7565_VOLTAGE_DIVIDER_VALUE),
#endif
ST7565_INVERTED(0), // display normal, bit val 0: LCD pixel off.
ST7565_CONTRAST(0x1E), // Contrast value. Setting for controlling brightness of Displaytech 64128N
ST7565_CONTRAST(0x1E), // Contrast value for Displaytech 64128N
ST7565_ON(1), // display on

2
Marlin/src/lcd/e3v2/common/dwin_font.h

@ -21,6 +21,8 @@
*/
#pragma once
typedef uint8_t fontid_t;
/**
* 3-.0The font size, 0x00-0x09, corresponds to the font size below:
* 0x00=6*12 0x01=8*16 0x02=10*20 0x03=12*24 0x04=14*28

7
Marlin/src/lcd/e3v2/jyersui/README.md

@ -1,7 +0,0 @@
# DWIN for Creality Ender 3 v2
Marlin's Ender 3 v2 support requires the `DWIN_SET` included with the Ender 3 V2 [example configuration](https://github.com/MarlinFirmware/Configurations/tree/bugfix-2.1.x/config/examples/Creality/Ender-3%20V2).
## Easy Install
Copy the `DWIN_SET` folder onto a Micro-SD card and insert the card into the slot on the DWIN screen. Cycle the machine and wait for the screen to go from blue to orange. Turn the machine off and remove the SD card. When you turn on the machine the screen will display a "Creality" loading screen.

4848
Marlin/src/lcd/e3v2/jyersui/dwin.cpp

File diff suppressed because it is too large

245
Marlin/src/lcd/e3v2/jyersui/dwin.h

@ -1,245 +0,0 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2021 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 <https://www.gnu.org/licenses/>.
*
*/
#pragma once
/**
* lcd/e3v2/jyersui/dwin.h
*/
#include "dwin_lcd.h"
#include "../common/dwin_set.h"
#include "../common/dwin_font.h"
#include "../common/dwin_color.h"
#include "../common/encoder.h"
#include "../../../libs/BL24CXX.h"
#include "../../../inc/MarlinConfigPre.h"
//#define DWIN_CREALITY_LCD_CUSTOM_ICONS
enum processID : uint8_t {
Main, Print, Menu, Value, Option, File, Popup, Confirm, Wait
};
enum PopupID : uint8_t {
Pause, Stop, Resume, SaveLevel, ETemp, ConfFilChange, PurgeMore, MeshSlot,
Level, Home, MoveWait, Heating, FilLoad, FilChange, TempWarn, Runout, PIDWait, Resuming, ManualProbing,
FilInsert, HeaterTime, UserInput, LevelError, InvalidMesh, UI, Complete, Custom
};
enum menuID : uint8_t {
MainMenu,
Prepare,
Move,
HomeMenu,
ManualLevel,
ZOffset,
Preheat,
ChangeFilament,
MenuCustom,
Control,
TempMenu,
PID,
HotendPID,
BedPID,
#if HAS_PREHEAT
#define _PREHEAT_ID(N) Preheat##N,
REPEAT_1(PREHEAT_COUNT, _PREHEAT_ID)
#endif
Motion,
HomeOffsets,
MaxSpeed,
MaxAcceleration,
MaxJerk,
Steps,
Visual,
ColorSettings,
Advanced,
ProbeMenu,
Info,
Leveling,
LevelManual,
LevelView,
MeshViewer,
LevelSettings,
ManualMesh,
UBLMesh,
InfoMain,
Tune,
PreheatHotend
};
// Custom icons
#if ENABLED(DWIN_CREALITY_LCD_CUSTOM_ICONS)
// index of every custom icon should be >= CUSTOM_ICON_START
#define CUSTOM_ICON_START ICON_Checkbox_F
#define ICON_Checkbox_F 200
#define ICON_Checkbox_T 201
#define ICON_Fade 202
#define ICON_Mesh 203
#define ICON_Tilt 204
#define ICON_Brightness 205
#define ICON_AxisD 249
#define ICON_AxisBR 250
#define ICON_AxisTR 251
#define ICON_AxisBL 252
#define ICON_AxisTL 253
#define ICON_AxisC 254
#else
#define ICON_Fade ICON_Version
#define ICON_Mesh ICON_Version
#define ICON_Tilt ICON_Version
#define ICON_Brightness ICON_Version
#define ICON_AxisD ICON_Axis
#define ICON_AxisBR ICON_Axis
#define ICON_AxisTR ICON_Axis
#define ICON_AxisBL ICON_Axis
#define ICON_AxisTL ICON_Axis
#define ICON_AxisC ICON_Axis
#endif
enum colorID : uint8_t {
Default, White, Green, Cyan, Blue, Magenta, Red, Orange, Yellow, Brown, Black
};
#define Custom_Colors 10
#define Color_Aqua RGB(0x00,0x3F,0x1F)
#define Color_Light_White 0xBDD7
#define Color_Green RGB(0x00,0x3F,0x00)
#define Color_Light_Green 0x3460
#define Color_Cyan 0x07FF
#define Color_Light_Cyan 0x04F3
#define Color_Blue 0x015F
#define Color_Light_Blue 0x3A6A
#define Color_Magenta 0xF81F
#define Color_Light_Magenta 0x9813
#define Color_Light_Red 0x8800
#define Color_Orange 0xFA20
#define Color_Light_Orange 0xFBC0
#define Color_Light_Yellow 0x8BE0
#define Color_Brown 0xCC27
#define Color_Light_Brown 0x6204
#define Color_Black 0x0000
#define Color_Grey 0x18E3
#define Check_Color 0x4E5C // Check-box check color
#define Confirm_Color 0x34B9
#define Cancel_Color 0x3186
class CrealityDWINClass {
public:
static constexpr size_t eeprom_data_size = 48;
static struct EEPROM_Settings { // use bit fields to save space, max 48 bytes
bool time_format_textual : 1;
#if ENABLED(AUTO_BED_LEVELING_UBL)
uint8_t tilt_grid_size : 3;
#endif
uint16_t corner_pos : 10;
uint8_t cursor_color : 4;
uint8_t menu_split_line : 4;
uint8_t menu_top_bg : 4;
uint8_t menu_top_txt : 4;
uint8_t highlight_box : 4;
uint8_t progress_percent : 4;
uint8_t progress_time : 4;
uint8_t status_bar_text : 4;
uint8_t status_area_text : 4;
uint8_t coordinates_text : 4;
uint8_t coordinates_split_line : 4;
} eeprom_settings;
static constexpr const char * const color_names[11] = { "Default", "White", "Green", "Cyan", "Blue", "Magenta", "Red", "Orange", "Yellow", "Brown", "Black" };
static constexpr const char * const preheat_modes[3] = { "Both", "Hotend", "Bed" };
static void Clear_Screen(uint8_t e=3);
static void Draw_Float(float value, uint8_t row, bool selected=false, uint8_t minunit=10);
static void Draw_Option(uint8_t value, const char * const * options, uint8_t row, bool selected=false, bool color=false);
static uint16_t GetColor(uint8_t color, uint16_t original, bool light=false);
static void Draw_Checkbox(uint8_t row, bool value);
static void Draw_Title(const char * title);
static void Draw_Title(FSTR_P const title);
static void Draw_Menu_Item(uint8_t row, uint8_t icon=0, const char * const label1=nullptr, const char * const label2=nullptr, bool more=false, bool centered=false);
static void Draw_Menu_Item(uint8_t row, uint8_t icon=0, FSTR_P const flabel1=nullptr, FSTR_P const flabel2=nullptr, bool more=false, bool centered=false);
static void Draw_Menu(uint8_t menu, uint8_t select=0, uint8_t scroll=0);
static void Redraw_Menu(bool lastprocess=true, bool lastselection=false, bool lastmenu=false);
static void Redraw_Screen();
static void Main_Menu_Icons();
static void Draw_Main_Menu(uint8_t select=0);
static void Print_Screen_Icons();
static void Draw_Print_Screen();
static void Draw_Print_Filename(const bool reset=false);
static void Draw_Print_ProgressBar();
#if ENABLED(USE_M73_REMAINING_TIME)
static void Draw_Print_ProgressRemain();
#endif
static void Draw_Print_ProgressElapsed();
static void Draw_Print_confirm();
static void Draw_SD_Item(uint8_t item, uint8_t row);
static void Draw_SD_List(bool removed=false);
static void Draw_Status_Area(bool icons=false);
static void Draw_Popup(FSTR_P const line1, FSTR_P const line2, FSTR_P const line3, uint8_t mode, uint8_t icon=0);
static void Popup_Select();
static void Update_Status_Bar(bool refresh=false);
#if ENABLED(AUTO_BED_LEVELING_UBL)
static void Draw_Bed_Mesh(int16_t selected = -1, uint8_t gridline_width = 1, uint16_t padding_x = 8, uint16_t padding_y_top = 40 + 53 - 7);
static void Set_Mesh_Viewer_Status();
#endif
static FSTR_P Get_Menu_Title(uint8_t menu);
static uint8_t Get_Menu_Size(uint8_t menu);
static void Menu_Item_Handler(uint8_t menu, uint8_t item, bool draw=true);
static void Popup_Handler(PopupID popupid, bool option = false);
static void Confirm_Handler(PopupID popupid);
static void Main_Menu_Control();
static void Menu_Control();
static void Value_Control();
static void Option_Control();
static void File_Control();
static void Print_Screen_Control();
static void Popup_Control();
static void Confirm_Control();
static void Setup_Value(float value, float min, float max, float unit, uint8_t type);
static void Modify_Value(float &value, float min, float max, float unit, void (*f)()=nullptr);
static void Modify_Value(uint8_t &value, float min, float max, float unit, void (*f)()=nullptr);
static void Modify_Value(uint16_t &value, float min, float max, float unit, void (*f)()=nullptr);
static void Modify_Value(int16_t &value, float min, float max, float unit, void (*f)()=nullptr);
static void Modify_Value(uint32_t &value, float min, float max, float unit, void (*f)()=nullptr);
static void Modify_Value(int8_t &value, float min, float max, float unit, void (*f)()=nullptr);
static void Modify_Option(uint8_t value, const char * const * options, uint8_t max);
static void Update_Status(const char * const text);
static void Start_Print(bool sd);
static void Stop_Print();
static void Update();
static void State_Update();
static void Screen_Update();
static void AudioFeedback(const bool success=true);
static void Save_Settings(char *buff);
static void Load_Settings(const char *buff);
static void Reset_Settings();
};
extern CrealityDWINClass CrealityDWIN;

64
Marlin/src/lcd/e3v2/jyersui/dwin_lcd.cpp

@ -1,64 +0,0 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2021 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 <https://www.gnu.org/licenses/>.
*
*/
/********************************************************************************
* @file lcd/e3v2/jyersui/dwin_lcd.cpp
* @brief DWIN screen control functions
********************************************************************************/
#include "../../../inc/MarlinConfigPre.h"
#if ENABLED(DWIN_CREALITY_LCD_JYERSUI)
#include "dwin_lcd.h"
/*-------------------------------------- System variable function --------------------------------------*/
void DWIN_Startup() {}
/*---------------------------------------- Drawing functions ----------------------------------------*/
// Draw the degree (°) symbol
// Color: color
// x/y: Upper-left coordinate of the first pixel
void DWIN_Draw_DegreeSymbol(uint16_t Color, uint16_t x, uint16_t y) {
DWIN_Draw_Point(Color, 1, 1, x + 1, y);
DWIN_Draw_Point(Color, 1, 1, x + 2, y);
DWIN_Draw_Point(Color, 1, 1, x, y + 1);
DWIN_Draw_Point(Color, 1, 1, x + 3, y + 1);
DWIN_Draw_Point(Color, 1, 1, x, y + 2);
DWIN_Draw_Point(Color, 1, 1, x + 3, y + 2);
DWIN_Draw_Point(Color, 1, 1, x + 1, y + 3);
DWIN_Draw_Point(Color, 1, 1, x + 2, y + 3);
}
/*---------------------------------------- Picture related functions ----------------------------------------*/
// Draw an Icon
// libID: Icon library ID
// picID: Icon ID
// x/y: Upper-left point
void DWIN_ICON_Show(uint8_t libID, uint8_t picID, uint16_t x, uint16_t y) {
DWIN_ICON_Show(true, false, false, libID, picID, x, y);
}
#endif // DWIN_CREALITY_LCD_JYERSUI

2
Marlin/src/lcd/e3v2/marlinui/ui_common.cpp

@ -565,7 +565,7 @@ void MarlinUI::draw_status_message(const bool blink) {
#endif // AUTO_BED_LEVELING_UBL
#if ANY(BABYSTEP_ZPROBE_GFX_OVERLAY, MESH_EDIT_GFX_OVERLAY)
#if EITHER(BABYSTEP_ZPROBE_GFX_OVERLAY, MESH_EDIT_GFX_OVERLAY)
void MarlinUI::zoffset_overlay(const int8_t dir) {
const int rot_up = TERN(OVERLAY_GFX_REVERSE, ICON_RotateCCW, ICON_RotateCW),

82
Marlin/src/lcd/e3v2/proui/ubl_tools.cpp → Marlin/src/lcd/e3v2/proui/bedlevel_tools.cpp

@ -21,19 +21,20 @@
*/
/**
* UBL Tools and Mesh Viewer for Pro UI
* Version: 1.0.0
* Date: 2022/04/13
* Bed Level Tools for Pro UI
* Extended by: Miguel A. Risco-Castillo (MRISCOC)
* Version: 2.0.0
* Date: 2022/05/23
*
* Original Author: Henri-J-Norden
* Original Source: https://github.com/Jyers/Marlin/pull/126
* Based on the original work of: Henri-J-Norden
* https://github.com/Jyers/Marlin/pull/126
*/
#include "../../../inc/MarlinConfigPre.h"
#include "bedlevel_tools.h"
#if BOTH(DWIN_LCD_PROUI, AUTO_BED_LEVELING_UBL)
#if BOTH(DWIN_LCD_PROUI, HAS_LEVELING)
#include "ubl_tools.h"
#include "../../marlinui.h"
#include "../../../core/types.h"
#include "dwin.h"
@ -47,27 +48,29 @@
#include "../../../libs/least_squares_fit.h"
#include "../../../libs/vector_3.h"
UBLMeshToolsClass ubl_tools;
BedLevelToolsClass BedLevelTools;
#if ENABLED(USE_UBL_VIEWER)
bool UBLMeshToolsClass::viewer_asymmetric_range = false;
bool UBLMeshToolsClass::viewer_print_value = false;
#if USE_UBL_VIEWER
bool BedLevelToolsClass::viewer_asymmetric_range = false;
bool BedLevelToolsClass::viewer_print_value = false;
#endif
bool UBLMeshToolsClass::goto_mesh_value = false;
uint8_t UBLMeshToolsClass::tilt_grid = 1;
bool BedLevelToolsClass::goto_mesh_value = false;
uint8_t BedLevelToolsClass::mesh_x = 0;
uint8_t BedLevelToolsClass::mesh_y = 0;
uint8_t BedLevelToolsClass::tilt_grid = 1;
bool drawing_mesh = false;
char cmd[MAX_CMD_SIZE+16], str_1[16], str_2[16], str_3[16];
#if ENABLED(AUTO_BED_LEVELING_UBL)
void UBLMeshToolsClass::manual_value_update(const uint8_t mesh_x, const uint8_t mesh_y, bool undefined/*=false*/) {
void BedLevelToolsClass::manual_value_update(const uint8_t mesh_x, const uint8_t mesh_y, bool undefined/*=false*/) {
sprintf_P(cmd, PSTR("M421 I%i J%i Z%s %s"), mesh_x, mesh_y, dtostrf(current_position.z, 1, 3, str_1), undefined ? "N" : "");
gcode.process_subcommands_now(cmd);
planner.synchronize();
}
bool UBLMeshToolsClass::create_plane_from_mesh() {
bool BedLevelToolsClass::create_plane_from_mesh() {
struct linear_fit_data lsf_results;
incremental_LSF_reset(&lsf_results);
GRID_LOOP(x, y) {
@ -119,7 +122,7 @@ char cmd[MAX_CMD_SIZE+16], str_1[16], str_2[16], str_3[16];
#else
void UBLMeshToolsClass::manual_value_update(const uint8_t mesh_x, const uint8_t mesh_y) {
void BedLevelToolsClass::manual_value_update(const uint8_t mesh_x, const uint8_t mesh_y) {
sprintf_P(cmd, PSTR("G29 I%i J%i Z%s"), mesh_x, mesh_y, dtostrf(current_position.z, 1, 3, str_1));
gcode.process_subcommands_now(cmd);
planner.synchronize();
@ -127,7 +130,8 @@ char cmd[MAX_CMD_SIZE+16], str_1[16], str_2[16], str_3[16];
#endif
void UBLMeshToolsClass::manual_move(const uint8_t mesh_x, const uint8_t mesh_y, bool zmove/*=false*/) {
void BedLevelToolsClass::manual_move(const uint8_t mesh_x, const uint8_t mesh_y, bool zmove/*=false*/) {
gcode.process_subcommands_now(F("G28O"));
if (zmove) {
planner.synchronize();
current_position.z = goto_mesh_value ? bedlevel.z_values[mesh_x][mesh_y] : Z_CLEARANCE_BETWEEN_PROBES;
@ -149,8 +153,28 @@ void UBLMeshToolsClass::manual_move(const uint8_t mesh_x, const uint8_t mesh_y,
}
}
float UBLMeshToolsClass::get_max_value() {
float max = __FLT_MIN__;
void BedLevelToolsClass::MoveToXYZ() {
BedLevelTools.goto_mesh_value = true;
BedLevelTools.manual_move(BedLevelTools.mesh_x, BedLevelTools.mesh_y, false);
}
void BedLevelToolsClass::MoveToXY() {
BedLevelTools.goto_mesh_value = false;
BedLevelTools.manual_move(BedLevelTools.mesh_x, BedLevelTools.mesh_y, false);
}
void BedLevelToolsClass::MoveToZ() {
BedLevelTools.goto_mesh_value = true;
BedLevelTools.manual_move(BedLevelTools.mesh_x, BedLevelTools.mesh_y, true);
}
void BedLevelToolsClass::ProbeXY() {
sprintf_P(cmd, PSTR("G30X%sY%s"),
dtostrf(bedlevel.get_mesh_x(BedLevelTools.mesh_x), 1, 2, str_1),
dtostrf(bedlevel.get_mesh_y(BedLevelTools.mesh_y), 1, 2, str_2)
);
gcode.process_subcommands_now(cmd);
}
float BedLevelToolsClass::get_max_value() {
float max = __FLT_MAX__ * -1;
GRID_LOOP(x, y) {
if (!isnan(bedlevel.z_values[x][y]) && bedlevel.z_values[x][y] > max)
max = bedlevel.z_values[x][y];
@ -158,7 +182,7 @@ float UBLMeshToolsClass::get_max_value() {
return max;
}
float UBLMeshToolsClass::get_min_value() {
float BedLevelToolsClass::get_min_value() {
float min = __FLT_MAX__;
GRID_LOOP(x, y) {
if (!isnan(bedlevel.z_values[x][y]) && bedlevel.z_values[x][y] < min)
@ -167,19 +191,20 @@ float UBLMeshToolsClass::get_min_value() {
return min;
}
bool UBLMeshToolsClass::validate() {
float min = __FLT_MAX__, max = __FLT_MIN__;
bool BedLevelToolsClass::meshvalidate() {
float min = __FLT_MAX__, max = __FLT_MAX__ * -1;
GRID_LOOP(x, y) {
if (isnan(bedlevel.z_values[x][y])) return false;
if (bedlevel.z_values[x][y] < min) min = bedlevel.z_values[x][y];
if (bedlevel.z_values[x][y] > max) max = bedlevel.z_values[x][y];
}
return max <= UBL_Z_OFFSET_MAX && min >= UBL_Z_OFFSET_MIN;
return WITHIN(max, MESH_Z_OFFSET_MIN, MESH_Z_OFFSET_MAX);
}
#if ENABLED(USE_UBL_VIEWER)
void UBLMeshToolsClass::Draw_Bed_Mesh(int16_t selected /*= -1*/, uint8_t gridline_width /*= 1*/, uint16_t padding_x /*= 8*/, uint16_t padding_y_top /*= 40 + 53 - 7*/) {
#if USE_UBL_VIEWER
void BedLevelToolsClass::Draw_Bed_Mesh(int16_t selected /*= -1*/, uint8_t gridline_width /*= 1*/, uint16_t padding_x /*= 8*/, uint16_t padding_y_top /*= 40 + 53 - 7*/) {
drawing_mesh = true;
const uint16_t total_width_px = DWIN_WIDTH - padding_x - padding_x;
const uint16_t cell_width_px = total_width_px / (GRID_MAX_POINTS_X);
@ -237,7 +262,7 @@ bool UBLMeshToolsClass::validate() {
}
}
void UBLMeshToolsClass::Set_Mesh_Viewer_Status() { // TODO: draw gradient with values as a legend instead
void BedLevelToolsClass::Set_Mesh_Viewer_Status() { // TODO: draw gradient with values as a legend instead
float v_max = abs(get_max_value()), v_min = abs(get_min_value()), range = _MAX(v_min, v_max);
if (v_min > 3e+10F) v_min = 0.0000001;
if (v_max > 3e+10F) v_max = 0.0000001;
@ -255,6 +280,7 @@ bool UBLMeshToolsClass::validate() {
ui.set_status(msg);
drawing_mesh = false;
}
#endif
#endif // DWIN_LCD_PROUI && AUTO_BED_LEVELING_UBL
#endif // USE_UBL_VIEWER
#endif // DWIN_LCD_PROUI && HAS_LEVELING

46
Marlin/src/lcd/e3v2/proui/ubl_tools.h → Marlin/src/lcd/e3v2/proui/bedlevel_tools.h

@ -1,10 +1,9 @@
/**
* UBL Tools and Mesh Viewer for Pro UI
* Version: 1.0.0
* Date: 2022/04/13
/*
* Marlin 3D Printer Firmware
* Copyright (c) 2022 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Original Author: Henri-J-Norden (https://github.com/Henri-J-Norden)
* Original Source: https://github.com/Jyers/Marlin/pull/135
* 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
@ -20,22 +19,37 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
/**
* Bed Level Tools for Pro UI
* Extended by: Miguel A. Risco-Castillo (MRISCOC)
* Version: 2.0.0
* Date: 2022/05/23
*
* Based on the original work of: Henri-J-Norden
* https://github.com/Jyers/Marlin/pull/126
*/
#pragma once
#include "../../../inc/MarlinConfigPre.h"
//#define USE_UBL_VIEWER 1
#if ENABLED(AUTO_BED_LEVELING_UBL)
//#define USE_UBL_VIEWER 1
#endif
#define UBL_Z_OFFSET_MIN -3.0
#define UBL_Z_OFFSET_MAX 3.0
#define MESH_Z_OFFSET_MIN -3.0
#define MESH_Z_OFFSET_MAX 3.0
class UBLMeshToolsClass {
class BedLevelToolsClass {
public:
#if ENABLED(USE_UBL_VIEWER)
#if USE_UBL_VIEWER
static bool viewer_asymmetric_range;
static bool viewer_print_value;
#endif
static bool goto_mesh_value;
static uint8_t mesh_x;
static uint8_t mesh_y;
static uint8_t tilt_grid;
#if ENABLED(AUTO_BED_LEVELING_UBL)
@ -45,15 +59,19 @@ public:
static void manual_value_update(const uint8_t mesh_x, const uint8_t mesh_y);
#endif
static void manual_move(const uint8_t mesh_x, const uint8_t mesh_y, bool zmove=false);
static void MoveToXYZ();
static void MoveToXY();
static void MoveToZ();
static void ProbeXY();
static float get_max_value();
static float get_min_value();
static bool validate();
#if ENABLED(USE_UBL_VIEWER)
static bool meshvalidate();
#if USE_UBL_VIEWER
static void Draw_Bed_Mesh(int16_t selected = -1, uint8_t gridline_width = 1, uint16_t padding_x = 8, uint16_t padding_y_top = 40 + 53 - 7);
static void Set_Mesh_Viewer_Status();
#endif
};
extern UBLMeshToolsClass ubl_tools;
extern BedLevelToolsClass BedLevelTools;
void Goto_MeshViewer();

237
Marlin/src/lcd/e3v2/proui/dwin.cpp

@ -101,17 +101,18 @@
#if HAS_MESH || HAS_ONESTEP_LEVELING
#include "../../../feature/bedlevel/bedlevel.h"
#include "bedlevel_tools.h"
#endif
#if HAS_BED_PROBE
#include "../../../module/probe.h"
#endif
#ifdef BLTOUCH_HS_MODE
#if ENABLED(BLTOUCH)
#include "../../../feature/bltouch.h"
#endif
#if ANY(BABYSTEPPING, HAS_BED_PROBE, HAS_WORKSPACE_OFFSET)
#if EITHER(BABYSTEPPING, HAS_BED_PROBE)
#define HAS_ZOFFSET_ITEM 1
#if ENABLED(BABYSTEPPING)
#include "../../../feature/babystep.h"
@ -141,10 +142,6 @@
#include "meshviewer.h"
#endif
#if ENABLED(AUTO_BED_LEVELING_UBL)
#include "ubl_tools.h"
#endif
#if ENABLED(PRINTCOUNTER)
#include "printstats.h"
#endif
@ -157,16 +154,14 @@
#include "../../../feature/leds/leds.h"
#endif
#include <WString.h>
#include <stdio.h>
#include <string.h>
#if HAS_LOCKSCREEN
#include "lockscreen.h"
#endif
#ifndef MACHINE_SIZE
#define MACHINE_SIZE STRINGIFY(X_BED_SIZE) "x" STRINGIFY(Y_BED_SIZE) "x" STRINGIFY(Z_MAX_POS)
#endif
#include "lockscreen.h"
#define PAUSE_HEAT
#define MENU_CHAR_LIMIT 24
@ -251,6 +246,7 @@ constexpr float max_feedrate_edit_values[] =
{ 1000, 1000, 10, 50 }
#endif
;
constexpr float max_acceleration_edit_values[] =
#ifdef MAX_ACCEL_EDIT_VALUES
MAX_ACCEL_EDIT_VALUES
@ -258,6 +254,7 @@ constexpr float max_acceleration_edit_values[] =
{ 1000, 1000, 200, 2000 }
#endif
;
#if HAS_CLASSIC_JERK
constexpr float max_jerk_edit_values[] =
#ifdef MAX_JERK_EDIT_VALUES
@ -305,7 +302,9 @@ MenuClass *FilamentMenu = nullptr;
MenuClass *TemperatureMenu = nullptr;
MenuClass *MaxSpeedMenu = nullptr;
MenuClass *MaxAccelMenu = nullptr;
MenuClass *MaxJerkMenu = nullptr;
#if HAS_CLASSIC_JERK
MenuClass *MaxJerkMenu = nullptr;
#endif
MenuClass *StepsMenu = nullptr;
MenuClass *HotendPIDMenu = nullptr;
MenuClass *BedPIDMenu = nullptr;
@ -552,14 +551,9 @@ void Popup_window_PauseOrStop() {
#endif
// Draw status line
void DWIN_DrawStatusLine(const char *text) {
void DWIN_DrawStatusLine() {
DWIN_Draw_Rectangle(1, HMI_data.StatusBg_Color, 0, STATUS_Y, DWIN_WIDTH, STATUS_Y + 20);
if (text) DWINUI::Draw_CenteredString(HMI_data.StatusTxt_Color, STATUS_Y + 2, text);
}
void DWIN_DrawStatusLine(FSTR_P fstr) {
DWIN_Draw_Rectangle(1, HMI_data.StatusBg_Color, 0, STATUS_Y, DWIN_WIDTH, STATUS_Y + 20);
if (fstr) DWINUI::Draw_CenteredString(HMI_data.StatusTxt_Color, STATUS_Y + 2, fstr);
DWINUI::Draw_CenteredString(HMI_data.StatusTxt_Color, STATUS_Y + 2, ui.status_message);
}
// Clear & reset status line
@ -588,7 +582,7 @@ void DWIN_DrawStatusMessage() {
// If the string fits the status line do not scroll it
if (slen <= LCD_WIDTH) {
if (hash_changed) {
DWIN_DrawStatusLine(ui.status_message);
DWIN_DrawStatusLine();
hash_changed = false;
}
}
@ -620,7 +614,7 @@ void DWIN_DrawStatusMessage() {
if (hash_changed) {
ui.status_message[LCD_WIDTH] = 0;
DWIN_DrawStatusLine(ui.status_message);
DWIN_DrawStatusLine();
hash_changed = false;
}
@ -663,7 +657,7 @@ void ICON_ResumeOrPause() {
}
// Update filename on print
void DWIN_Print_Header(const char *text = nullptr) {
void DWIN_Print_Header(const char *text=nullptr) {
static char headertxt[31] = ""; // Print header text
if (text) {
const int8_t size = _MIN(30U, strlen_P(text));
@ -1099,7 +1093,7 @@ void DWIN_Draw_Dashboard() {
DWINUI::Draw_Int(DWIN_FONT_STAT, HMI_data.Indicator_Color, HMI_data.Background_Color, 3, 195 + 2 * STAT_CHR_W, 384, thermalManager.fan_speed[0]);
#endif
#if BOTH(BABYSTEPPING, HAS_BED_PROBE)
#if HAS_ZOFFSET_ITEM
DWINUI::Draw_Icon(planner.leveling_active ? ICON_SetZOffset : ICON_Zoffset, 187, 416);
#endif
@ -1370,13 +1364,15 @@ void Draw_Main_Area() {
case ESDiagProcess: Draw_EndStopDiag(); break;
#endif
case Popup: popupDraw(); break;
case Locked: lockScreen.draw(); break;
#if HAS_LOCKSCREEN
case Locked: lockScreen.draw(); break;
#endif
case Menu:
case SetInt:
case SetPInt:
case SetIntNoDraw:
case SetFloat:
case SetPFloat: ReDrawMenu(); break;
case SetPFloat: ReDrawMenu(true); break;
default: break;
}
}
@ -1567,7 +1563,9 @@ void DWIN_HandleScreen() {
case PrintProcess: HMI_Printing(); break;
case Popup: HMI_Popup(); break;
case Leveling: break;
case Locked: HMI_LockScreen(); break;
#if HAS_LOCKSCREEN
case Locked: HMI_LockScreen(); break;
#endif
case PrintDone:
TERN_(HAS_ESDIAG, case ESDiagProcess:)
case WaitResponse: HMI_WaitForUser(); break;
@ -1769,7 +1767,7 @@ void DWIN_Print_Aborted() {
Goto_PrintDone();
}
// Progress Bar update
// Progress and remaining time update
void DWIN_M73() {
if (parser.seenval('P')) {
_percent_done = parser.value_byte();
@ -1843,17 +1841,12 @@ void DWIN_CopySettingsFrom(const char * const buff) {
TERN_(PREVENT_COLD_EXTRUSION, ApplyExtMinT());
feedrate_percentage = 100;
TERN_(BAUD_RATE_GCODE, HMI_SetBaudRate());
#if BOTH(CASE_LIGHT_MENU, CASELIGHT_USES_BRIGHTNESS)
// Apply Case light brightness
caselight.brightness = HMI_data.CaseLight_Brightness;
caselight.update_brightness();
#endif
#if BOTH(LED_CONTROL_MENU, HAS_COLOR_LEDS)
leds.set_color(
(HMI_data.LED_Color >> 16) & 0xFF,
(HMI_data.LED_Color >> 8) & 0xFF,
(HMI_data.LED_Color >> 0) & 0xFF
OPTARG(HAS_WHITE_LED, (HMI_data.LED_Color >> 24) & 0xFF)
HMI_data.Led_Color.r,
HMI_data.Led_Color.g,
HMI_data.Led_Color.b
OPTARG(HAS_WHITE_LED, HMI_data.Led_Color.w)
);
leds.update();
#endif
@ -1880,7 +1873,7 @@ void DWIN_InitScreen() {
index_file = MROWS;
hash_changed = true;
last_E = 0;
DWIN_DrawStatusLine(FSTR_P(nullptr));
DWIN_DrawStatusLine();
DWIN_Draw_Dashboard();
Goto_Main_Menu();
}
@ -1935,7 +1928,7 @@ void DWIN_RedrawScreen() {
case PAUSE_MESSAGE_PARKING: DWIN_Popup_Pause(GET_TEXT_F(MSG_PAUSE_PRINT_PARKING)); break; // M125
case PAUSE_MESSAGE_CHANGING: DWIN_Popup_Pause(GET_TEXT_F(MSG_FILAMENT_CHANGE_INIT)); break; // pause_print (M125, M600)
case PAUSE_MESSAGE_WAITING: DWIN_Popup_Pause(GET_TEXT_F(MSG_ADVANCED_PAUSE_WAITING), BTN_Continue); break;
case PAUSE_MESSAGE_INSERT: DWIN_Popup_Continue(ICON_BLTouch, GET_TEXT_F(MSG_ADVANCED_PAUSE), GET_TEXT_F(MSG_FILAMENT_CHANGE_INSERT)); break;
case PAUSE_MESSAGE_INSERT: DWIN_Popup_Pause(GET_TEXT_F(MSG_FILAMENT_CHANGE_INSERT), BTN_Continue); break;
case PAUSE_MESSAGE_LOAD: DWIN_Popup_Pause(GET_TEXT_F(MSG_FILAMENT_CHANGE_LOAD)); break;
case PAUSE_MESSAGE_UNLOAD: DWIN_Popup_Pause(GET_TEXT_F(MSG_FILAMENT_CHANGE_UNLOAD)); break; // Unload of pause and Unload of M702
case PAUSE_MESSAGE_PURGE:
@ -1988,28 +1981,32 @@ void DWIN_RedrawScreen() {
}
#endif // HAS_MESH
void DWIN_LockScreen() {
if (checkkey != Locked) {
lockScreen.rprocess = checkkey;
checkkey = Locked;
lockScreen.init();
#if HAS_LOCKSCREEN
void DWIN_LockScreen() {
if (checkkey != Locked) {
lockScreen.rprocess = checkkey;
checkkey = Locked;
lockScreen.init();
}
}
}
void DWIN_UnLockScreen() {
if (checkkey == Locked) {
checkkey = lockScreen.rprocess;
Draw_Main_Area();
void DWIN_UnLockScreen() {
if (checkkey == Locked) {
checkkey = lockScreen.rprocess;
Draw_Main_Area();
}
}
}
void HMI_LockScreen() {
EncoderState encoder_diffState = get_encoder_state();
if (encoder_diffState == ENCODER_DIFF_NO) return;
lockScreen.onEncoder(encoder_diffState);
if (lockScreen.isUnlocked()) DWIN_UnLockScreen();
}
void HMI_LockScreen() {
EncoderState encoder_diffState = get_encoder_state();
if (encoder_diffState == ENCODER_DIFF_NO) return;
lockScreen.onEncoder(encoder_diffState);
if (lockScreen.isUnlocked()) DWIN_UnLockScreen();
}
#endif // HAS_LOCKSCREEN
#if HAS_GCODE_PREVIEW
@ -2051,7 +2048,8 @@ void HMI_LockScreen() {
#if ENABLED(EEPROM_SETTINGS)
void WriteEeprom() {
DWIN_DrawStatusLine(GET_TEXT_F(MSG_STORE_EEPROM));
ui.set_status(GET_TEXT_F(MSG_STORE_EEPROM));
DWIN_DrawStatusLine();
DWIN_UpdateLCD();
DONE_BUZZ(settings.save());
}
@ -2106,11 +2104,13 @@ void HomeX() { queue.inject(F("G28X")); }
void HomeY() { queue.inject(F("G28Y")); }
void HomeZ() { queue.inject(F("G28Z")); }
void SetHome() {
#if HAS_HOME_OFFSET
// Apply workspace offset, making the current position 0,0,0
void SetHome() {
queue.inject(F("G92X0Y0Z0"));
DONE_BUZZ(true);
}
}
#endif
#if HAS_ZOFFSET_ITEM
@ -2132,22 +2132,25 @@ void SetHome() {
void SetMoveZto0() {
#if ENABLED(Z_SAFE_HOMING)
char cmd[54], str_1[5], str_2[5];
sprintf_P(cmd, PSTR("G28XYO\nG28Z\nG0X%sY%sF5000\nM420S0\nG0Z0F300\nM400"),
sprintf_P(cmd, PSTR("G28XYO\nG28Z\nG0X%sY%sF5000\nG0Z0F300\nM400"),
dtostrf(Z_SAFE_HOMING_X_POINT, 1, 1, str_1),
dtostrf(Z_SAFE_HOMING_Y_POINT, 1, 1, str_2)
);
gcode.process_subcommands_now(cmd);
#else
gcode.process_subcommands_now(F("G28O\nM420S0\nG0Z0F300\nM400"));
set_bed_leveling_enabled(false);
gcode.process_subcommands_now(F("G28O\nG0Z0F300\nM400"));
#endif
ui.reset_status();
DONE_BUZZ(true);
}
void HomeZandDisable() {
SetMoveZto0();
DisableMotors();
}
#if !HAS_BED_PROBE
void HomeZandDisable() {
SetMoveZto0();
DisableMotors();
}
#endif
#endif // HAS_ZOFFSET_ITEM
@ -2264,7 +2267,11 @@ void SetPID(celsius_t t, heater_id_t h) {
}
#endif
#if HAS_COLOR_LEDS
void ApplyLEDColor() { HMI_data.LED_Color = TERN0(HAS_WHITE_LED, (leds.color.w << 24)) | (leds.color.r << 16) | (leds.color.g << 8) | leds.color.b; }
void ApplyLEDColor() {
HMI_data.Led_Color = LEDColor(
TERN(HAS_WHITE_LED, { 0, 0, 0, leds.color.w }, { leds.color.r, leds.color.g, leds.color.b })
);
}
void LiveLEDColor(uint8_t *color) { *color = MenuData.Value; leds.update(); }
void LiveLEDColorR() { LiveLEDColor(&leds.color.r); }
void LiveLEDColorG() { LiveLEDColor(&leds.color.g); }
@ -2840,9 +2847,6 @@ void onDrawGetColorItem(MenuItemClass* menuitem, int8_t line) {
DWIN_Draw_HLine(HMI_data.SplitLine_Color, 16, MYPOS(line + 1), 240);
}
#if HAS_FILAMENT_SENSOR
void onDrawRunoutEnable(MenuItemClass* menuitem, int8_t line) { onDrawChkbMenu(menuitem, line, runout.enabled); }
#endif
void onDrawPIDi(MenuItemClass* menuitem, int8_t line) { onDrawFloatMenu(menuitem, line, 2, unscalePID_i(*(float*)static_cast<MenuItemPtrClass*>(menuitem)->value)); }
void onDrawPIDd(MenuItemClass* menuitem, int8_t line) { onDrawFloatMenu(menuitem, line, 2, unscalePID_d(*(float*)static_cast<MenuItemPtrClass*>(menuitem)->value)); }
@ -3249,7 +3253,9 @@ void Draw_AdvancedSettings_Menu() {
MENU_ITEM_F(ICON_PrintStats, MSG_INFO_STATS_MENU, onDrawSubMenu, Goto_PrintStats);
MENU_ITEM_F(ICON_PrintStatsReset, MSG_INFO_PRINT_COUNT_RESET, onDrawSubMenu, PrintStats.Reset);
#endif
MENU_ITEM_F(ICON_Lock, MSG_LOCKSCREEN, onDrawMenuItem, DWIN_LockScreen);
#if HAS_LOCKSCREEN
MENU_ITEM_F(ICON_Lock, MSG_LOCKSCREEN, onDrawMenuItem, DWIN_LockScreen);
#endif
}
ui.reset_status(true);
UpdateMenu(AdvancedSettings);
@ -3295,12 +3301,15 @@ void Draw_Move_Menu() {
EDIT_ITEM_F(ICON_ProbeOffsetX, MSG_ZPROBE_XOFFSET, onDrawPFloatMenu, SetProbeOffsetX, &probe.offset.x);
EDIT_ITEM_F(ICON_ProbeOffsetY, MSG_ZPROBE_YOFFSET, onDrawPFloatMenu, SetProbeOffsetY, &probe.offset.y);
EDIT_ITEM_F(ICON_ProbeOffsetZ, MSG_ZPROBE_ZOFFSET, onDrawPFloat2Menu, SetProbeOffsetZ, &probe.offset.z);
#ifdef BLTOUCH_HS_MODE
EDIT_ITEM_F(ICON_HSMode, MSG_ENABLE_HS_MODE, onDrawChkbMenu, SetHSMode, &bltouch.high_speed_mode);
#if ENABLED(BLTOUCH)
MENU_ITEM_F(ICON_ProbeStow, MSG_MANUAL_STOW, onDrawMenuItem, ProbeStow);
MENU_ITEM_F(ICON_ProbeDeploy, MSG_MANUAL_DEPLOY, onDrawMenuItem, ProbeDeploy);
MENU_ITEM_F(ICON_BltouchReset, MSG_BLTOUCH_RESET, onDrawMenuItem, bltouch._reset);
#ifdef BLTOUCH_HS_MODE
EDIT_ITEM_F(ICON_HSMode, MSG_ENABLE_HS_MODE, onDrawChkbMenu, SetHSMode, &bltouch.high_speed_mode);
#endif
#endif
MENU_ITEM_F(ICON_ProbeTest, MSG_M48_TEST, onDrawMenuItem, ProbeTest);
MENU_ITEM_F(ICON_ProbeStow, MSG_MANUAL_STOW, onDrawMenuItem, ProbeStow);
MENU_ITEM_F(ICON_ProbeDeploy, MSG_MANUAL_DEPLOY, onDrawMenuItem, ProbeDeploy);
}
UpdateMenu(ProbeSetMenu);
}
@ -3384,14 +3393,24 @@ void Draw_GetColor_Menu() {
#endif
#if ENABLED(LED_CONTROL_MENU)
void Draw_LedControl_Menu() {
checkkey = Menu;
if (SetMenu(LedControlMenu, GET_TEXT_F(MSG_LED_CONTROL), 6)) {
BACK_ITEM(Draw_Control_Menu);
#if !BOTH(CASE_LIGHT_MENU, CASE_LIGHT_USE_NEOPIXEL)
EDIT_ITEM_F(ICON_LedControl, MSG_LEDS, onDrawChkbMenu, SetLedStatus, &leds.lights_on);
#endif
#if HAS_COLOR_LEDS
void Draw_LedControl_Menu() {
checkkey = Menu;
if (SetMenu(LedControlMenu, GET_TEXT_F(MSG_LED_CONTROL), 6)) {
BACK_ITEM(Draw_Control_Menu);
#if !BOTH(CASE_LIGHT_MENU, CASE_LIGHT_USE_NEOPIXEL)
EDIT_ITEM_F(ICON_LedControl, MSG_LEDS, onDrawChkbMenu, SetLedStatus, &leds.lights_on);
#endif
#if HAS_COLOR_LEDS
#if ENABLED(LED_COLOR_PRESETS)
MENU_ITEM_F(ICON_LedControl, MSG_SET_LEDS_WHITE, onDrawMenuItem, leds.set_white);
MENU_ITEM_F(ICON_LedControl, MSG_SET_LEDS_RED, onDrawMenuItem, leds.set_red);
MENU_ITEM_F(ICON_LedControl, MSG_SET_LEDS_ORANGE, onDrawMenuItem, leds.set_orange);
MENU_ITEM_F(ICON_LedControl, MSG_SET_LEDS_YELLOW, onDrawMenuItem, leds.set_yellow);
MENU_ITEM_F(ICON_LedControl, MSG_SET_LEDS_GREEN, onDrawMenuItem, leds.set_green);
MENU_ITEM_F(ICON_LedControl, MSG_SET_LEDS_BLUE, onDrawMenuItem, leds.set_blue);
MENU_ITEM_F(ICON_LedControl, MSG_SET_LEDS_INDIGO, onDrawMenuItem, leds.set_indigo);
MENU_ITEM_F(ICON_LedControl, MSG_SET_LEDS_VIOLET, onDrawMenuItem, leds.set_violet);
#else
EDIT_ITEM_F(ICON_LedControl, MSG_COLORS_RED, onDrawPInt8Menu, SetLEDColorR, &leds.color.r);
EDIT_ITEM_F(ICON_LedControl, MSG_COLORS_GREEN, onDrawPInt8Menu, SetLEDColorG, &leds.color.g);
EDIT_ITEM_F(ICON_LedControl, MSG_COLORS_BLUE, onDrawPInt8Menu, SetLEDColorB, &leds.color.b);
@ -3399,9 +3418,10 @@ void Draw_GetColor_Menu() {
EDIT_ITEM_F(ICON_LedControl, MSG_COLORS_WHITE, onDrawPInt8Menu, SetLedColorW, &leds.color.w);
#endif
#endif
}
UpdateMenu(LedControlMenu);
#endif
}
UpdateMenu(LedControlMenu);
}
#endif
void Draw_Tune_Menu() {
@ -3432,7 +3452,9 @@ void Draw_Tune_Menu() {
#if ENABLED(FWRETRACT)
MENU_ITEM_F(ICON_FWRetract, MSG_FWRETRACT, onDrawSubMenu, Draw_FWRetract_Menu);
#endif
MENU_ITEM_F(ICON_Lock, MSG_LOCKSCREEN, onDrawMenuItem, DWIN_LockScreen);
#if HAS_LOCKSCREEN
MENU_ITEM_F(ICON_Lock, MSG_LOCKSCREEN, onDrawMenuItem, DWIN_LockScreen);
#endif
#if HAS_LCD_BRIGHTNESS
EDIT_ITEM_F(ICON_Brightness, MSG_BRIGHTNESS, onDrawPInt8Menu, SetBrightness, &ui.brightness);
MENU_ITEM_F(ICON_Brightness, MSG_BRIGHTNESS_OFF, onDrawMenuItem, TurnOffBacklight);
@ -3750,15 +3772,14 @@ void Draw_Steps_Menu() {
#endif
#if ENABLED(MESH_EDIT_MENU)
uint8_t mesh_x = 0, mesh_y = 0;
#define Z_OFFSET_MIN -3
#define Z_OFFSET_MAX 3
void LiveEditMesh() { ((MenuItemPtrClass*)EditZValueItem)->value = &bedlevel.z_values[HMI_value.Select ? mesh_x : MenuData.Value][HMI_value.Select ? MenuData.Value : mesh_y]; EditZValueItem->redraw(); }
void ApplyEditMeshX() { mesh_x = MenuData.Value; }
void SetEditMeshX() { HMI_value.Select = 0; SetIntOnClick(0, GRID_MAX_POINTS_X - 1, mesh_x, ApplyEditMeshX, LiveEditMesh); }
void ApplyEditMeshY() { mesh_y = MenuData.Value; }
void SetEditMeshY() { HMI_value.Select = 1; SetIntOnClick(0, GRID_MAX_POINTS_Y - 1, mesh_y, ApplyEditMeshY, LiveEditMesh); }
void LiveEditMesh() { ((MenuItemPtrClass*)EditZValueItem)->value = &bedlevel.z_values[HMI_value.Select ? BedLevelTools.mesh_x : MenuData.Value][HMI_value.Select ? MenuData.Value : BedLevelTools.mesh_y]; EditZValueItem->redraw(); }
void ApplyEditMeshX() { BedLevelTools.mesh_x = MenuData.Value; }
void SetEditMeshX() { HMI_value.Select = 0; SetIntOnClick(0, GRID_MAX_POINTS_X - 1, BedLevelTools.mesh_x, ApplyEditMeshX, LiveEditMesh); }
void ApplyEditMeshY() { BedLevelTools.mesh_y = MenuData.Value; }
void SetEditMeshY() { HMI_value.Select = 1; SetIntOnClick(0, GRID_MAX_POINTS_Y - 1, BedLevelTools.mesh_y, ApplyEditMeshY, LiveEditMesh); }
void SetEditZValue() { SetPFloatOnClick(Z_OFFSET_MIN, Z_OFFSET_MAX, 3); }
#endif
#endif
@ -3772,14 +3793,14 @@ void Draw_Steps_Menu() {
onDrawIntMenu(menuitem, line, bedlevel.storage_slot);
}
void ApplyUBLTiltGrid() { ubl_tools.tilt_grid = MenuData.Value; }
void SetUBLTiltGrid() { SetIntOnClick(1, 3, ubl_tools.tilt_grid, ApplyUBLTiltGrid); }
void ApplyUBLTiltGrid() { BedLevelTools.tilt_grid = MenuData.Value; }
void SetUBLTiltGrid() { SetIntOnClick(1, 3, BedLevelTools.tilt_grid, ApplyUBLTiltGrid); }
void UBLTiltMesh() {
if (bedlevel.storage_slot < 0) bedlevel.storage_slot = 0;
char buf[15];
if (ubl_tools.tilt_grid > 1) {
sprintf_P(buf, PSTR("G28O\nG29 J%i"), ubl_tools.tilt_grid);
if (BedLevelTools.tilt_grid > 1) {
sprintf_P(buf, PSTR("G28O\nG29 J%i"), BedLevelTools.tilt_grid);
gcode.process_subcommands_now(buf);
}
else
@ -3788,16 +3809,10 @@ void Draw_Steps_Menu() {
}
void UBLSmartFillMesh() {
bedlevel.smart_fill_mesh();
LOOP_L_N(x, GRID_MAX_POINTS_Y) bedlevel.smart_fill_mesh();
LCD_MESSAGE(MSG_UBL_MESH_FILLED);
}
bool UBLValidMesh() {
const bool valid = ubl_tools.validate();
if (!valid) bedlevel.invalidate();
return valid;
}
void UBLSaveMesh() {
if (bedlevel.storage_slot < 0) bedlevel.storage_slot = 0;
settings.store_mesh(bedlevel.storage_slot);
@ -3808,14 +3823,6 @@ void Draw_Steps_Menu() {
void UBLLoadMesh() {
if (bedlevel.storage_slot < 0) bedlevel.storage_slot = 0;
settings.load_mesh(bedlevel.storage_slot);
if (UBLValidMesh()) {
ui.status_printf(0, GET_TEXT_F(MSG_MESH_LOADED), bedlevel.storage_slot);
DONE_BUZZ(true);
}
else {
LCD_MESSAGE_F("Invalid Mesh Loaded");
DONE_BUZZ(false);
}
}
#endif // AUTO_BED_LEVELING_UBL
@ -3837,7 +3844,7 @@ void Draw_Steps_Menu() {
EDIT_ITEM_F(ICON_UBLActive, MSG_UBL_STORAGE_SLOT, onDrawUBLSlot, SetUBLSlot, &bedlevel.storage_slot);
MENU_ITEM_F(ICON_UBLActive, MSG_UBL_SAVE_MESH, onDrawMenuItem, UBLSaveMesh);
MENU_ITEM_F(ICON_UBLActive, MSG_UBL_LOAD_MESH, onDrawMenuItem, UBLLoadMesh);
EDIT_ITEM_F(ICON_UBLActive, MSG_UBL_TILTING_GRID, onDrawPInt8Menu, SetUBLTiltGrid, &ubl_tools.tilt_grid);
EDIT_ITEM_F(ICON_UBLActive, MSG_UBL_TILTING_GRID, onDrawPInt8Menu, SetUBLTiltGrid, &BedLevelTools.tilt_grid);
MENU_ITEM_F(ICON_UBLActive, MSG_UBL_TILT_MESH, onDrawMenuItem, UBLTiltMesh);
MENU_ITEM_F(ICON_UBLActive, MSG_UBL_SMART_FILLIN, onDrawMenuItem, UBLSmartFillMesh);
#endif
@ -3851,13 +3858,15 @@ void Draw_Steps_Menu() {
#if ENABLED(MESH_EDIT_MENU)
void Draw_EditMesh_Menu() {
if (!leveling_is_valid()) { LCD_MESSAGE(MSG_UBL_MESH_INVALID); return; }
set_bed_leveling_enabled(false);
checkkey = Menu;
if (SetMenu(EditMeshMenu, GET_TEXT_F(MSG_EDIT_MESH), 4)) {
mesh_x = mesh_y = 0;
BedLevelTools.mesh_x = BedLevelTools.mesh_y = 0;
BACK_ITEM(Draw_MeshSet_Menu);
EDIT_ITEM_F(ICON_UBLActive, MSG_MESH_X, onDrawPInt8Menu, SetEditMeshX, &mesh_x);
EDIT_ITEM_F(ICON_UBLActive, MSG_MESH_Y, onDrawPInt8Menu, SetEditMeshY, &mesh_y);
EditZValueItem = EDIT_ITEM_F(ICON_UBLActive, MSG_MESH_EDIT_Z, onDrawPFloat3Menu, SetEditZValue, &bedlevel.z_values[mesh_x][mesh_y]);
EDIT_ITEM_F(ICON_UBLActive, MSG_MESH_X, onDrawPInt8Menu, SetEditMeshX,&BedLevelTools.mesh_x);
EDIT_ITEM_F(ICON_UBLActive, MSG_MESH_Y, onDrawPInt8Menu, SetEditMeshY,&BedLevelTools.mesh_y);
EditZValueItem = EDIT_ITEM_F(ICON_UBLActive, MSG_MESH_EDIT_Z, onDrawPFloat3Menu, SetEditZValue, &bedlevel.z_values[BedLevelTools.mesh_x][BedLevelTools.mesh_y]);
}
UpdateMenu(EditMeshMenu);
}

11
Marlin/src/lcd/e3v2/proui/dwin.h

@ -160,8 +160,7 @@ void Goto_PowerLossRecovery();
void Goto_ConfirmToPrint();
void DWIN_Draw_Dashboard(const bool with_update); // Status Area
void Draw_Main_Area(); // Redraw main area
void DWIN_DrawStatusLine(const char *text); // Draw simple status text
void DWIN_DrawStatusLine(FSTR_P fstr);
void DWIN_DrawStatusLine(); // Draw simple status text
void DWIN_RedrawDash(); // Redraw Dash and Status line
void DWIN_RedrawScreen(); // Redraw all screen elements
void HMI_MainMenu(); // Main process screen
@ -210,9 +209,11 @@ void DWIN_RebootScreen();
#endif
// Utility and extensions
void DWIN_LockScreen();
void DWIN_UnLockScreen();
void HMI_LockScreen();
#if HAS_LOCKSCREEN
void DWIN_LockScreen();
void DWIN_UnLockScreen();
void HMI_LockScreen();
#endif
#if HAS_MESH
void DWIN_MeshViewer();
#endif

24
Marlin/src/lcd/e3v2/proui/dwin_defines.h

@ -28,23 +28,21 @@
* Date: 2022/02/28
*/
#define HAS_GCODE_PREVIEW 1
#define HAS_PIDPLOT 1
#define HAS_ESDIAG 1
#define HAS_LOCKSCREEN 1
//#define DEBUG_DWIN 1
//#define NEED_HEX_PRINT 1
#include "../../../inc/MarlinConfigPre.h"
#include "../common/dwin_color.h"
#include <stddef.h>
#define HAS_ESDIAG 1
#define HAS_PIDPLOT 1
#define HAS_GCODE_PREVIEW 1
#if defined(__STM32F1__) || defined(STM32F1)
#define DASH_REDRAW 1
#endif
#include "../common/dwin_color.h"
#if ENABLED(LED_CONTROL_MENU)
#include "../../../feature/leds/leds.h"
#endif
#define Def_Background_Color RGB( 1, 12, 8)
#define Def_Cursor_color RGB(20, 49, 31)
@ -65,14 +63,8 @@
#define Def_Indicator_Color Color_White
#define Def_Coordinate_Color Color_White
#define Def_Button_Color RGB( 0, 23, 16)
#define HAS_ESDIAG 1
#if BOTH(LED_CONTROL_MENU, HAS_COLOR_LEDS)
#define Def_Leds_Color 0xFFFFFFFF
#endif
#if ENABLED(CASELIGHT_USES_BRIGHTNESS)
#define Def_CaseLight_Brightness 255
#define Def_Leds_Color LEDColorWhite()
#endif
typedef struct {
@ -115,12 +107,12 @@ typedef struct {
bool Baud115K = false;
#endif
bool FullManualTramming = false;
// Led
#if ENABLED(MESH_BED_LEVELING)
float ManualZOffset = 0;
#endif
// Led
#if BOTH(LED_CONTROL_MENU, HAS_COLOR_LEDS)
uint32_t LED_Color = Def_Leds_Color;
LEDColor Led_Color = Def_Leds_Color;
#endif
} HMI_data_t;

60
Marlin/src/lcd/e3v2/proui/dwinui.cpp

@ -23,8 +23,8 @@
/**
* DWIN Enhanced implementation for PRO UI
* Author: Miguel A. Risco-Castillo (MRISCOC)
* Version: 3.17.1
* Date: 2022/04/12
* Version: 3.18.1
* Date: 2022/07/05
*/
#include "../../../inc/MarlinConfigPre.h"
@ -44,7 +44,7 @@ uint16_t DWINUI::pencolor = Color_White;
uint16_t DWINUI::textcolor = Def_Text_Color;
uint16_t DWINUI::backcolor = Def_Background_Color;
uint16_t DWINUI::buttoncolor = Def_Button_Color;
uint8_t DWINUI::font = font8x16;
uint8_t DWINUI::fontid = font8x16;
FSTR_P const DWINUI::Author = F(STRING_CONFIG_H_AUTHOR);
void (*DWINUI::onTitleDraw)(TitleClass* title) = nullptr;
@ -62,17 +62,15 @@ void DWINUI::init() {
textcolor = Def_Text_Color;
backcolor = Def_Background_Color;
buttoncolor = Def_Button_Color;
font = font8x16;
fontid = font8x16;
}
// Set text/number font
void DWINUI::setFont(uint8_t cfont) {
font = cfont;
}
void DWINUI::setFont(fontid_t fid) { fontid = fid; }
// Get font character width
uint8_t DWINUI::fontWidth(uint8_t cfont) {
switch (cfont) {
uint8_t DWINUI::fontWidth(fontid_t fid) {
switch (fid) {
case font6x12 : return 6;
case font8x16 : return 8;
case font10x20: return 10;
@ -88,8 +86,8 @@ uint8_t DWINUI::fontWidth(uint8_t cfont) {
}
// Get font character height
uint8_t DWINUI::fontHeight(uint8_t cfont) {
switch (cfont) {
uint8_t DWINUI::fontHeight(fontid_t fid) {
switch (fid) {
case font6x12 : return 12;
case font8x16 : return 16;
case font10x20: return 20;
@ -105,14 +103,10 @@ uint8_t DWINUI::fontHeight(uint8_t cfont) {
}
// Get screen x coordinates from text column
uint16_t DWINUI::ColToX(uint8_t col) {
return col * fontWidth(font);
}
uint16_t DWINUI::ColToX(uint8_t col) { return col * fontWidth(fontid); }
// Get screen y coordinates from text row
uint16_t DWINUI::RowToY(uint8_t row) {
return row * fontHeight(font);
}
uint16_t DWINUI::RowToY(uint8_t row) { return row * fontHeight(fontid); }
// Set text/number color
void DWINUI::SetColors(uint16_t fgcolor, uint16_t bgcolor, uint16_t alcolor) {
@ -152,9 +146,9 @@ void DWINUI::MoveBy(xy_int_t point) {
}
// Draw a Centered string using arbitrary x1 and x2 margins
void DWINUI::Draw_CenteredString(bool bShow, uint8_t size, uint16_t color, uint16_t bColor, uint16_t x1, uint16_t x2, uint16_t y, const char * const string) {
const uint16_t x = _MAX(0U, x2 + x1 - strlen_P(string) * fontWidth(size)) / 2 - 1;
DWIN_Draw_String(bShow, size, color, bColor, x, y, string);
void DWINUI::Draw_CenteredString(bool bShow, fontid_t fid, uint16_t color, uint16_t bColor, uint16_t x1, uint16_t x2, uint16_t y, const char * const string) {
const uint16_t x = _MAX(0U, x2 + x1 - strlen_P(string) * fontWidth(fid)) / 2 - 1;
DWIN_Draw_String(bShow, fid, color, bColor, x, y, string);
}
// Draw a char
@ -164,13 +158,13 @@ void DWINUI::Draw_CenteredString(bool bShow, uint8_t size, uint16_t color, uint1
// c: ASCII code of char
void DWINUI::Draw_Char(uint16_t color, uint16_t x, uint16_t y, const char c) {
const char string[2] = { c, 0};
DWIN_Draw_String(false, font, color, backcolor, x, y, string, 1);
DWIN_Draw_String(false, fontid, color, backcolor, x, y, string, 1);
}
// Draw a char at cursor position and increment cursor
void DWINUI::Draw_Char(uint16_t color, const char c) {
Draw_Char(color, cursor.x, cursor.y, c);
MoveBy(fontWidth(font), 0);
MoveBy(fontWidth(fontid), 0);
}
// Draw a string at cursor position
@ -178,49 +172,49 @@ void DWINUI::Draw_Char(uint16_t color, const char c) {
// *string: The string
// rlimit: For draw less chars than string length use rlimit
void DWINUI::Draw_String(const char * const string, uint16_t rlimit) {
DWIN_Draw_String(false, font, textcolor, backcolor, cursor.x, cursor.y, string, rlimit);
MoveBy(strlen(string) * fontWidth(font), 0);
DWIN_Draw_String(false, fontid, textcolor, backcolor, cursor.x, cursor.y, string, rlimit);
MoveBy(strlen(string) * fontWidth(fontid), 0);
}
void DWINUI::Draw_String(uint16_t color, const char * const string, uint16_t rlimit) {
DWIN_Draw_String(false, font, color, backcolor, cursor.x, cursor.y, string, rlimit);
MoveBy(strlen(string) * fontWidth(font), 0);
DWIN_Draw_String(false, fontid, color, backcolor, cursor.x, cursor.y, string, rlimit);
MoveBy(strlen(string) * fontWidth(fontid), 0);
}
// Draw a numeric integer value
// bShow: true=display background color; false=don't display background color
// signedMode: 1=signed; 0=unsigned
// size: Font size
// fid: Font ID
// color: Character color
// bColor: Background color
// iNum: Number of digits
// x/y: Upper-left coordinate
// value: Integer value
void DWINUI::Draw_Int(uint8_t bShow, bool signedMode, uint8_t size, uint16_t color, uint16_t bColor, uint8_t iNum, uint16_t x, uint16_t y, int32_t value) {
void DWINUI::Draw_Int(uint8_t bShow, bool signedMode, fontid_t fid, uint16_t color, uint16_t bColor, uint8_t iNum, uint16_t x, uint16_t y, int32_t value) {
char nstr[10];
sprintf_P(nstr, PSTR("%*li"), (signedMode ? iNum + 1 : iNum), value);
DWIN_Draw_String(bShow, size, color, bColor, x, y, nstr);
DWIN_Draw_String(bShow, fid, color, bColor, x, y, nstr);
}
// Draw a numeric float value
// bShow: true=display background color; false=don't display background color
// signedMode: 1=signed; 0=unsigned
// size: Font size
// fid: Font ID
// color: Character color
// bColor: Background color
// iNum: Number of digits
// fNum: Number of decimal digits
// x/y: Upper-left coordinate
// value: float value
void DWINUI::Draw_Float(uint8_t bShow, bool signedMode, uint8_t size, uint16_t color, uint16_t bColor, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value) {
void DWINUI::Draw_Float(uint8_t bShow, bool signedMode, fontid_t fid, uint16_t color, uint16_t bColor, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value) {
char nstr[10];
DWIN_Draw_String(bShow, size, color, bColor, x, y, dtostrf(value, iNum + (signedMode ? 2:1) + fNum, fNum, nstr));
DWIN_Draw_String(bShow, fid, color, bColor, x, y, dtostrf(value, iNum + (signedMode ? 2:1) + fNum, fNum, nstr));
}
// ------------------------- Buttons ------------------------------//
void DWINUI::Draw_Button(uint16_t color, uint16_t bcolor, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, const char * const caption) {
DWIN_Draw_Rectangle(1, bcolor, x1, y1, x2, y2);
Draw_CenteredString(0, font, color, bcolor, x1, x2, (y2 + y1 - fontHeight())/2, caption);
Draw_CenteredString(0, fontid, color, bcolor, x1, x2, (y2 + y1 - fontHeight())/2, caption);
}
void DWINUI::Draw_Button(uint8_t id, uint16_t x, uint16_t y) {

157
Marlin/src/lcd/e3v2/proui/dwinui.h

@ -24,8 +24,8 @@
/**
* DWIN Enhanced implementation for PRO UI
* Author: Miguel A. Risco-Castillo (MRISCOC)
* Version: 3.17.1
* Date: 2022/04/12
* Version: 3.18.1
* Date: 2022/07/05
*/
#include "dwin_lcd.h"
@ -39,6 +39,7 @@
#define ICON_BedSizeY ICON_PrintSize
#define ICON_BedTramming ICON_SetHome
#define ICON_Binary ICON_Contact
#define ICON_BltouchReset ICON_StockConfiguration
#define ICON_Brightness ICON_Motion
#define ICON_Cancel ICON_StockConfiguration
#define ICON_CustomPreheat ICON_SetEndTemp
@ -145,7 +146,7 @@
#define DWIN_FONT_HEAD font10x20
#define DWIN_FONT_ALERT font10x20
#define STATUS_Y 354
#define LCD_WIDTH (DWIN_WIDTH / 8) // only if the default font is font8x16
#define LCD_WIDTH (DWIN_WIDTH / 8) // only if the default fontid is font8x16
// Minimum unit (0.1) : multiple (10)
#define UNITFDIGITS 1
@ -156,7 +157,7 @@ constexpr uint8_t TITLE_HEIGHT = 30, // Title bar heig
TROWS = (STATUS_Y - TITLE_HEIGHT) / MLINE, // Total rows
MROWS = TROWS - 1, // Other-than-Back
ICOX = 26, // Menu item icon X position
LBLX = 60, // Menu item label X position
LBLX = 55, // Menu item label X position
VALX = 210, // Menu item value X position
MENU_CHR_W = 8, MENU_CHR_H = 16, // Menu font 8x16
STAT_CHR_W = 10;
@ -196,7 +197,7 @@ namespace DWINUI {
extern uint16_t textcolor;
extern uint16_t backcolor;
extern uint16_t buttoncolor;
extern uint8_t font;
extern fontid_t fontid;
extern FSTR_P const Author;
extern void (*onTitleDraw)(TitleClass* title);
@ -205,15 +206,15 @@ namespace DWINUI {
void init();
// Set text/number font
void setFont(uint8_t cfont);
void setFont(fontid_t cfont);
// Get font character width
uint8_t fontWidth(uint8_t cfont);
inline uint8_t fontWidth() { return fontWidth(font); };
uint8_t fontWidth(fontid_t cfont);
inline uint8_t fontWidth() { return fontWidth(fontid); };
// Get font character height
uint8_t fontHeight(uint8_t cfont);
inline uint8_t fontHeight() { return fontHeight(font); };
uint8_t fontHeight(fontid_t cfont);
inline uint8_t fontHeight() { return fontHeight(fontid); };
// Get screen x coordinates from text column
uint16_t ColToX(uint8_t col);
@ -278,108 +279,108 @@ namespace DWINUI {
// Draw a numeric integer value
// bShow: true=display background color; false=don't display background color
// signedMode: 1=signed; 0=unsigned
// size: Font size
// fid: Font ID
// color: Character color
// bColor: Background color
// iNum: Number of digits
// x/y: Upper-left coordinate
// value: Integer value
void Draw_Int(uint8_t bShow, bool signedMode, uint8_t size, uint16_t color, uint16_t bColor, uint8_t iNum, uint16_t x, uint16_t y, int32_t value);
void Draw_Int(uint8_t bShow, bool signedMode, fontid_t fid, uint16_t color, uint16_t bColor, uint8_t iNum, uint16_t x, uint16_t y, int32_t value);
// Draw a positive integer
inline void Draw_Int(uint8_t bShow, uint8_t size, uint16_t color, uint16_t bColor, uint8_t iNum, uint16_t x, uint16_t y, long value) {
Draw_Int(bShow, 0, size, color, bColor, iNum, x, y, value);
inline void Draw_Int(uint8_t bShow, fontid_t fid, uint16_t color, uint16_t bColor, uint8_t iNum, uint16_t x, uint16_t y, long value) {
Draw_Int(bShow, 0, fid, color, bColor, iNum, x, y, value);
}
inline void Draw_Int(uint8_t iNum, long value) {
Draw_Int(false, 0, font, textcolor, backcolor, iNum, cursor.x, cursor.y, value);
MoveBy(iNum * fontWidth(font), 0);
Draw_Int(false, 0, fontid, textcolor, backcolor, iNum, cursor.x, cursor.y, value);
MoveBy(iNum * fontWidth(fontid), 0);
}
inline void Draw_Int(uint8_t iNum, uint16_t x, uint16_t y, long value) {
Draw_Int(false, 0, font, textcolor, backcolor, iNum, x, y, value);
Draw_Int(false, 0, fontid, textcolor, backcolor, iNum, x, y, value);
}
inline void Draw_Int(uint16_t color, uint8_t iNum, uint16_t x, uint16_t y, long value) {
Draw_Int(false, 0, font, color, backcolor, iNum, x, y, value);
Draw_Int(false, 0, fontid, color, backcolor, iNum, x, y, value);
}
inline void Draw_Int(uint16_t color, uint16_t bColor, uint8_t iNum, uint16_t x, uint16_t y, long value) {
Draw_Int(true, 0, font, color, bColor, iNum, x, y, value);
Draw_Int(true, 0, fontid, color, bColor, iNum, x, y, value);
}
inline void Draw_Int(uint8_t size, uint16_t color, uint16_t bColor, uint8_t iNum, uint16_t x, uint16_t y, long value) {
Draw_Int(true, 0, size, color, bColor, iNum, x, y, value);
inline void Draw_Int(fontid_t fid, uint16_t color, uint16_t bColor, uint8_t iNum, uint16_t x, uint16_t y, long value) {
Draw_Int(true, 0, fid, color, bColor, iNum, x, y, value);
}
// Draw a signed integer
inline void Draw_Signed_Int(uint8_t bShow, uint8_t size, uint16_t color, uint16_t bColor, uint8_t iNum, uint16_t x, uint16_t y, long value) {
Draw_Int(bShow, 1, size, color, bColor, iNum, x, y, value);
inline void Draw_Signed_Int(uint8_t bShow, fontid_t fid, uint16_t color, uint16_t bColor, uint8_t iNum, uint16_t x, uint16_t y, long value) {
Draw_Int(bShow, 1, fid, color, bColor, iNum, x, y, value);
}
inline void Draw_Signed_Int(uint8_t iNum, long value) {
Draw_Int(false, 1, font, textcolor, backcolor, iNum, cursor.x, cursor.y, value);
MoveBy(iNum * fontWidth(font), 0);
Draw_Int(false, 1, fontid, textcolor, backcolor, iNum, cursor.x, cursor.y, value);
MoveBy(iNum * fontWidth(fontid), 0);
}
inline void Draw_Signed_Int(uint8_t iNum, uint16_t x, uint16_t y, long value) {
Draw_Int(false, 1, font, textcolor, backcolor, iNum, x, y, value);
Draw_Int(false, 1, fontid, textcolor, backcolor, iNum, x, y, value);
}
inline void Draw_Signed_Int(uint16_t color, uint8_t iNum, uint16_t x, uint16_t y, long value) {
Draw_Int(false, 1, font, color, backcolor, iNum, x, y, value);
Draw_Int(false, 1, fontid, color, backcolor, iNum, x, y, value);
}
inline void Draw_Signed_Int(uint16_t color, uint16_t bColor, uint8_t iNum, uint16_t x, uint16_t y, long value) {
Draw_Int(true, 1, font, color, bColor, iNum, x, y, value);
Draw_Int(true, 1, fontid, color, bColor, iNum, x, y, value);
}
inline void Draw_Signed_Int(uint8_t size, uint16_t color, uint16_t bColor, uint8_t iNum, uint16_t x, uint16_t y, long value) {
Draw_Int(true, 1, size, color, bColor, iNum, x, y, value);
inline void Draw_Signed_Int(fontid_t fid, uint16_t color, uint16_t bColor, uint8_t iNum, uint16_t x, uint16_t y, long value) {
Draw_Int(true, 1, fid, color, bColor, iNum, x, y, value);
}
// Draw a numeric float value
// bShow: true=display background color; false=don't display background color
// signedMode: 1=signed; 0=unsigned
// size: Font size
// fid: Font ID
// color: Character color
// bColor: Background color
// iNum: Number of digits
// fNum: Number of decimal digits
// x/y: Upper-left coordinate
// value: float value
void Draw_Float(uint8_t bShow, bool signedMode, uint8_t size, uint16_t color, uint16_t bColor, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value);
void Draw_Float(uint8_t bShow, bool signedMode, fontid_t fid, uint16_t color, uint16_t bColor, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value);
// Draw a positive floating point number
inline void Draw_Float(uint8_t bShow, uint8_t size, uint16_t color, uint16_t bColor, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value) {
Draw_Float(bShow, 0, size, color, bColor, iNum, fNum, x, y, value);
inline void Draw_Float(uint8_t bShow, fontid_t fid, uint16_t color, uint16_t bColor, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value) {
Draw_Float(bShow, 0, fid, color, bColor, iNum, fNum, x, y, value);
}
inline void Draw_Float(uint8_t iNum, uint8_t fNum, float value) {
Draw_Float(false, 0, font, textcolor, backcolor, iNum, fNum, cursor.x, cursor.y, value);
MoveBy((iNum + fNum + 1) * fontWidth(font), 0);
Draw_Float(false, 0, fontid, textcolor, backcolor, iNum, fNum, cursor.x, cursor.y, value);
MoveBy((iNum + fNum + 1) * fontWidth(fontid), 0);
}
inline void Draw_Float(uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value) {
Draw_Float(false, 0, font, textcolor, backcolor, iNum, fNum, x, y, value);
Draw_Float(false, 0, fontid, textcolor, backcolor, iNum, fNum, x, y, value);
}
inline void Draw_Float(uint8_t size, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value) {
Draw_Float(false, 0, size, textcolor, backcolor, iNum, fNum, x, y, value);
inline void Draw_Float(fontid_t fid, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value) {
Draw_Float(false, 0, fid, textcolor, backcolor, iNum, fNum, x, y, value);
}
inline void Draw_Float(uint16_t color, uint16_t bColor, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value) {
Draw_Float(true, 0, font, color, bColor, iNum, fNum, x, y, value);
Draw_Float(true, 0, fontid, color, bColor, iNum, fNum, x, y, value);
}
inline void Draw_Float(uint8_t size, uint16_t color, uint16_t bColor, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value) {
Draw_Float(true, 0, size, color, bColor, iNum, fNum, x, y, value);
inline void Draw_Float(fontid_t fid, uint16_t color, uint16_t bColor, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value) {
Draw_Float(true, 0, fid, color, bColor, iNum, fNum, x, y, value);
}
// Draw a signed floating point number
inline void Draw_Signed_Float(uint8_t bShow, uint8_t size, uint16_t color, uint16_t bColor, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value) {
Draw_Float(bShow, 1, size, color, bColor, iNum, fNum, x, y, value);
inline void Draw_Signed_Float(uint8_t bShow, fontid_t fid, uint16_t color, uint16_t bColor, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value) {
Draw_Float(bShow, 1, fid, color, bColor, iNum, fNum, x, y, value);
}
inline void Draw_Signed_Float(uint8_t iNum, uint8_t fNum, float value) {
Draw_Float(false, 1, font, textcolor, backcolor, iNum, fNum, cursor.x, cursor.y, value);
MoveBy((iNum + fNum + 1) * fontWidth(font), 0);
Draw_Float(false, 1, fontid, textcolor, backcolor, iNum, fNum, cursor.x, cursor.y, value);
MoveBy((iNum + fNum + 1) * fontWidth(fontid), 0);
}
inline void Draw_Signed_Float(uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value) {
Draw_Float(false, 1, font, textcolor, backcolor, iNum, fNum, x, y, value);
Draw_Float(false, 1, fontid, textcolor, backcolor, iNum, fNum, x, y, value);
}
inline void Draw_Signed_Float(uint8_t size, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value) {
Draw_Float(false, 1, size, textcolor, backcolor, iNum, fNum, x, y, value);
inline void Draw_Signed_Float(fontid_t fid, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value) {
Draw_Float(false, 1, fid, textcolor, backcolor, iNum, fNum, x, y, value);
}
inline void Draw_Signed_Float(uint16_t color, uint16_t bColor, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value) {
Draw_Float(true, 1, font, color, bColor, iNum, fNum, x, y, value);
Draw_Float(true, 1, fontid, color, bColor, iNum, fNum, x, y, value);
}
inline void Draw_Signed_Float(uint8_t size, uint16_t color, uint16_t bColor, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value) {
Draw_Float(true, 1, size, color, bColor, iNum, fNum, x, y, value);
inline void Draw_Signed_Float(fontid_t fid, uint16_t color, uint16_t bColor, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value) {
Draw_Float(true, 1, fid, color, bColor, iNum, fNum, x, y, value);
}
// Draw a char
@ -407,70 +408,70 @@ namespace DWINUI {
}
// Draw a string
// size: Font size
// fid: Font ID
// color: Character color
// bColor: Background color
// x/y: Upper-left coordinate of the string
// *string: The string
inline void Draw_String(uint16_t x, uint16_t y, const char * const string) {
DWIN_Draw_String(false, font, textcolor, backcolor, x, y, string);
DWIN_Draw_String(false, fontid, textcolor, backcolor, x, y, string);
}
inline void Draw_String(uint16_t x, uint16_t y, FSTR_P title) {
DWIN_Draw_String(false, font, textcolor, backcolor, x, y, FTOP(title));
DWIN_Draw_String(false, fontid, textcolor, backcolor, x, y, FTOP(title));
}
inline void Draw_String(uint16_t color, uint16_t x, uint16_t y, const char * const string) {
DWIN_Draw_String(false, font, color, backcolor, x, y, string);
DWIN_Draw_String(false, fontid, color, backcolor, x, y, string);
}
inline void Draw_String(uint16_t color, uint16_t x, uint16_t y, FSTR_P title) {
DWIN_Draw_String(false, font, color, backcolor, x, y, title);
DWIN_Draw_String(false, fontid, color, backcolor, x, y, title);
}
inline void Draw_String(uint16_t color, uint16_t bgcolor, uint16_t x, uint16_t y, const char * const string) {
DWIN_Draw_String(true, font, color, bgcolor, x, y, string);
DWIN_Draw_String(true, fontid, color, bgcolor, x, y, string);
}
inline void Draw_String(uint16_t color, uint16_t bgcolor, uint16_t x, uint16_t y, FSTR_P title) {
DWIN_Draw_String(true, font, color, bgcolor, x, y, title);
DWIN_Draw_String(true, fontid, color, bgcolor, x, y, title);
}
inline void Draw_String(uint8_t size, uint16_t color, uint16_t bgcolor, uint16_t x, uint16_t y, const char * const string) {
DWIN_Draw_String(true, size, color, bgcolor, x, y, string);
inline void Draw_String(fontid_t fid, uint16_t color, uint16_t bgcolor, uint16_t x, uint16_t y, const char * const string) {
DWIN_Draw_String(true, fid, color, bgcolor, x, y, string);
}
inline void Draw_String(uint8_t size, uint16_t color, uint16_t bgcolor, uint16_t x, uint16_t y, FSTR_P title) {
DWIN_Draw_String(true, size, color, bgcolor, x, y, title);
inline void Draw_String(fontid_t fid, uint16_t color, uint16_t bgcolor, uint16_t x, uint16_t y, FSTR_P title) {
DWIN_Draw_String(true, fid, color, bgcolor, x, y, title);
}
// Draw a centered string using DWIN_WIDTH
// bShow: true=display background color; false=don't display background color
// size: Font size
// fid: Font ID
// color: Character color
// bColor: Background color
// y: Upper coordinate of the string
// *string: The string
void Draw_CenteredString(bool bShow, uint8_t size, uint16_t color, uint16_t bColor, uint16_t x1, uint16_t x2, uint16_t y, const char * const string);
inline void Draw_CenteredString(bool bShow, uint8_t size, uint16_t color, uint16_t bColor, uint16_t y, const char * const string) {
Draw_CenteredString(bShow, size, color, bColor, 0, DWIN_WIDTH, y, string);
void Draw_CenteredString(bool bShow, fontid_t fid, uint16_t color, uint16_t bColor, uint16_t x1, uint16_t x2, uint16_t y, const char * const string);
inline void Draw_CenteredString(bool bShow, fontid_t fid, uint16_t color, uint16_t bColor, uint16_t y, const char * const string) {
Draw_CenteredString(bShow, fid, color, bColor, 0, DWIN_WIDTH, y, string);
}
inline void Draw_CenteredString(bool bShow, uint8_t size, uint16_t color, uint16_t bColor, uint16_t y, FSTR_P string) {
Draw_CenteredString(bShow, size, color, bColor, y, FTOP(string));
inline void Draw_CenteredString(bool bShow, fontid_t fid, uint16_t color, uint16_t bColor, uint16_t y, FSTR_P string) {
Draw_CenteredString(bShow, fid, color, bColor, y, FTOP(string));
}
inline void Draw_CenteredString(uint16_t color, uint16_t bcolor, uint16_t y, const char * const string) {
Draw_CenteredString(true, font, color, bcolor, y, string);
Draw_CenteredString(true, fontid, color, bcolor, y, string);
}
inline void Draw_CenteredString(uint8_t size, uint16_t color, uint16_t y, const char * const string) {
Draw_CenteredString(false, size, color, backcolor, y, string);
inline void Draw_CenteredString(fontid_t fid, uint16_t color, uint16_t y, const char * const string) {
Draw_CenteredString(false, fid, color, backcolor, y, string);
}
inline void Draw_CenteredString(uint8_t size, uint16_t color, uint16_t y, FSTR_P title) {
Draw_CenteredString(false, size, color, backcolor, y, title);
inline void Draw_CenteredString(fontid_t fid, uint16_t color, uint16_t y, FSTR_P title) {
Draw_CenteredString(false, fid, color, backcolor, y, title);
}
inline void Draw_CenteredString(uint16_t color, uint16_t y, const char * const string) {
Draw_CenteredString(false, font, color, backcolor, y, string);
Draw_CenteredString(false, fontid, color, backcolor, y, string);
}
inline void Draw_CenteredString(uint16_t color, uint16_t y, FSTR_P title) {
Draw_CenteredString(false, font, color, backcolor, y, title);
Draw_CenteredString(false, fontid, color, backcolor, y, title);
}
inline void Draw_CenteredString(uint16_t y, const char * const string) {
Draw_CenteredString(false, font, textcolor, backcolor, y, string);
Draw_CenteredString(false, fontid, textcolor, backcolor, y, string);
}
inline void Draw_CenteredString(uint16_t y, FSTR_P title) {
Draw_CenteredString(false, font, textcolor, backcolor, y, title);
Draw_CenteredString(false, fontid, textcolor, backcolor, y, title);
}
// Draw a box

8
Marlin/src/lcd/e3v2/proui/lockscreen.cpp

@ -31,8 +31,10 @@
#if ENABLED(DWIN_LCD_PROUI)
#include "../../../core/types.h"
#include "dwin_lcd.h"
#include "dwin_defines.h"
#if HAS_LOCKSCREEN
#include "dwinui.h"
#include "dwin.h"
#include "lockscreen.h"
@ -73,4 +75,6 @@ void LockScreenClass::onEncoder(EncoderState encoder_diffState) {
DWIN_UpdateLCD();
}
#endif // HAS_LOCKSCREEN
#endif // DWIN_LCD_PROUI

26
Marlin/src/lcd/e3v2/proui/menus.cpp

@ -23,8 +23,8 @@
/**
* Menu functions for ProUI
* Author: Miguel A. Risco-Castillo
* Version: 1.4.1
* Date: 2022/04/14
* Version: 1.5.1
* Date: 2022/05/23
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
@ -166,6 +166,17 @@ void onDrawChkbMenu(MenuItemClass* menuitem, int8_t line) {
onDrawChkbMenu(menuitem, line, val);
}
void DrawItemEdit() {
switch (checkkey) {
case SetIntNoDraw: if (MenuData.LiveUpdate) MenuData.LiveUpdate(); break;
case SetInt:
case SetPInt: DWINUI::Draw_Signed_Int(HMI_data.Text_Color, HMI_data.Selected_Color, 4 , VALX, MBASE(CurrentMenu->line()) - 1, MenuData.Value); break;
case SetFloat:
case SetPFloat: DWINUI::Draw_Signed_Float(HMI_data.Text_Color, HMI_data.Selected_Color, 3, MenuData.dp, VALX - MenuData.dp * DWINUI::fontWidth(DWIN_FONT_MENU), MBASE(CurrentMenu->line()), MenuData.Value / POW(10, MenuData.dp)); break;
default: break;
}
}
//-----------------------------------------------------------------------------
// On click functions
//-----------------------------------------------------------------------------
@ -307,7 +318,7 @@ int8_t HMI_GetInt(const int32_t lo, const int32_t hi) {
return 2;
}
LIMIT(MenuData.Value, lo, hi);
DWINUI::Draw_Signed_Int(HMI_data.Text_Color, HMI_data.Selected_Color, 4 , VALX, MBASE(CurrentMenu->line()) - 1, MenuData.Value);
DrawItemEdit();
return 1;
}
return 0;
@ -361,7 +372,7 @@ int8_t HMI_GetFloat(uint8_t dp, int32_t lo, int32_t hi) {
return 2;
}
LIMIT(MenuData.Value, lo, hi);
DWINUI::Draw_Signed_Float(HMI_data.Text_Color, HMI_data.Selected_Color, 3, dp, VALX - dp * DWINUI::fontWidth(DWIN_FONT_MENU), MBASE(CurrentMenu->line()), MenuData.Value / POW(10, dp));
DrawItemEdit();
return 1;
}
return 0;
@ -469,7 +480,7 @@ void MenuItemClass::SetFrame(uint8_t id, uint16_t x1, uint16_t y1, uint16_t x2,
}
void MenuItemClass::draw(int8_t line) {
if (line < 0 || line >= TROWS) return;
if (!WITHIN(line, 0, TROWS - 1)) return;
if (onDraw != nullptr) (*onDraw)(this, line);
};
@ -547,6 +558,9 @@ void UpdateMenu(MenuClass* &menu) {
menu->draw();
}
void ReDrawMenu() { if (CurrentMenu && checkkey==Menu) CurrentMenu->draw(); }
void ReDrawMenu(const bool force/*=false*/) {
if (CurrentMenu && (force || checkkey == Menu)) CurrentMenu->draw();
if (force) DrawItemEdit();
}
#endif // DWIN_LCD_PROUI

6
Marlin/src/lcd/e3v2/proui/menus.h

@ -23,8 +23,8 @@
/**
* Menu functions for ProUI
* Author: Miguel A. Risco-Castillo
* Version: 1.4.1
* Date: 2022/04/14
* Version: 1.5.1
* Date: 2022/05/23
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
@ -168,7 +168,7 @@ bool SetMenu(MenuClass* &menu, FSTR_P title, int8_t totalitems);
void UpdateMenu(MenuClass* &menu);
//Redraw the current Menu if it is valid
void ReDrawMenu();
void ReDrawMenu(const bool force=false);
// Clear MenuItems array and free MenuItems elements
void MenuItemsClear();

15
Marlin/src/lcd/e3v2/proui/meshviewer.cpp

@ -31,8 +31,6 @@
#if BOTH(DWIN_LCD_PROUI, HAS_MESH)
#include "meshviewer.h"
#include "../../../core/types.h"
#include "../../marlinui.h"
#include "dwin_lcd.h"
@ -40,9 +38,10 @@
#include "dwin.h"
#include "dwin_popup.h"
#include "../../../feature/bedlevel/bedlevel.h"
#include "meshviewer.h"
#if ENABLED(AUTO_BED_LEVELING_UBL)
#include "ubl_tools.h"
#include "bedlevel_tools.h"
#endif
MeshViewerClass MeshViewer;
@ -112,10 +111,10 @@ void MeshViewerClass::DrawMesh(bed_mesh_t zval, const uint8_t sizex, const uint8
void MeshViewerClass::Draw(bool withsave /*= false*/) {
Title.ShowCaption(GET_TEXT_F(MSG_MESH_VIEWER));
#if ENABLED(USE_UBL_VIEWER)
#if USE_UBL_VIEWER
DWINUI::ClearMainArea();
ubl_tools.viewer_print_value = true;
ubl_tools.Draw_Bed_Mesh(-1, 1, 8, 10 + TITLE_HEIGHT);
BedLevelTools.viewer_print_value = true;
BedLevelTools.Draw_Bed_Mesh(-1, 1, 8, 10 + TITLE_HEIGHT);
#else
DrawMesh(bedlevel.z_values, GRID_MAX_POINTS_X, GRID_MAX_POINTS_Y);
#endif
@ -127,8 +126,8 @@ void MeshViewerClass::Draw(bool withsave /*= false*/) {
else
DWINUI::Draw_Button(BTN_Continue, 86, 305);
#if ENABLED(USE_UBL_VIEWER)
ubl_tools.Set_Mesh_Viewer_Status();
#if USE_UBL_VIEWER
BedLevelTools.Set_Mesh_Viewer_Status();
#else
char str_1[6], str_2[6] = "";
ui.status_printf(0, F("Mesh minZ: %s, maxZ: %s"),

3
Marlin/src/lcd/e3v2/proui/meshviewer.h

@ -21,9 +21,6 @@
*/
#pragma once
#include "../../../core/types.h"
#include "../../../feature/bedlevel/bedlevel.h"
/**
* Mesh Viewer for PRO UI
* Author: Miguel A. Risco-Castillo (MRISCOC)

9
Marlin/src/lcd/e3v2/proui/plot.cpp

@ -46,13 +46,16 @@
#ifdef DWIN_LCD_PROUI
#include "dwin_defines.h"
#if HAS_PIDPLOT
#include "plot.h"
#include "../../../core/types.h"
#include "../../marlinui.h"
#include "dwin_lcd.h"
#include "dwinui.h"
#include "dwin_popup.h"
#include "dwin.h"
#define Plot_Bg_Color RGB( 1, 12, 8)
@ -71,7 +74,7 @@ void PlotClass::Draw(const frame_rect_t frame, const float max, const float ref)
y2 = frame.y + frame.h - 1;
r = round((y2) - ref * scale);
DWINUI::Draw_Box(1, Plot_Bg_Color, frame);
for (uint8_t i = 1; i < 4; i++) if (i*50 < frame.w) DWIN_Draw_VLine(Line_Color, i*50 + frame.x, frame.y, frame.h);
for (uint8_t i = 1; i < 4; i++) if (i * 50 < frame.w) DWIN_Draw_VLine(Line_Color, i * 50 + frame.x, frame.y, frame.h);
DWINUI::Draw_Box(0, Color_White, DWINUI::ExtendFrame(frame, 1));
DWIN_Draw_HLine(Color_Red, frame.x, r, frame.w);
}
@ -91,4 +94,6 @@ void PlotClass::Update(const float value) {
grphpoints++;
}
#endif // HAS_PIDPLOT
#endif // DWIN_LCD_PROUI

2
Marlin/src/lcd/extui/ftdi_eve_touch_ui/ftdi_eve_lib/basic/commands.cpp

@ -1208,7 +1208,7 @@ void CLCD::default_display_orientation() {
+ ENABLED(TOUCH_UI_INVERTED) * 1
);
cmd.execute();
#elif ANY(TOUCH_UI_PORTRAIT, TOUCH_UI_MIRRORED)
#elif EITHER(TOUCH_UI_PORTRAIT, TOUCH_UI_MIRRORED)
#error "PORTRAIT or MIRRORED orientation not supported on the FT800."
#elif ENABLED(TOUCH_UI_INVERTED)
mem_write_32(REG::ROTATE, 1);

16
Marlin/src/lcd/extui/ftdi_eve_touch_ui/theme/colors.h

@ -37,14 +37,14 @@ namespace Theme {
#else
// Use linear accent colors
#if ANY(TOUCH_UI_ROYAL_THEME, TOUCH_UI_FROZEN_THEME)
// Dark blue accent colors
constexpr int accent_hue = 216;
constexpr float accent_sat = 0.7;
#if EITHER(TOUCH_UI_ROYAL_THEME, TOUCH_UI_FROZEN_THEME)
// Dark blue accent colors
constexpr int accent_hue = 216;
constexpr float accent_sat = 0.7;
#else
// Green accent colors
constexpr int accent_hue = 68;
constexpr float accent_sat = 0.68;
// Green accent colors
constexpr int accent_hue = 68;
constexpr float accent_sat = 0.68;
#endif
// Shades of accent color
@ -88,7 +88,7 @@ namespace Theme {
constexpr uint32_t bed_mesh_lines_rgb = 0xFFFFFF;
constexpr uint32_t bed_mesh_shadow_rgb = 0x444444;
#elif ANY(TOUCH_UI_COCOA_THEME, TOUCH_UI_FROZEN_THEME)
#elif EITHER(TOUCH_UI_COCOA_THEME, TOUCH_UI_FROZEN_THEME)
constexpr uint32_t theme_darkest = accent_color_1;
constexpr uint32_t theme_dark = accent_color_4;

4
Marlin/src/lcd/extui/mks_ui/draw_ui.h

@ -236,9 +236,9 @@ typedef struct UI_Config_Struct {
eStepMax = 10;
// Extruder speed (mm/s)
uint8_t extruSpeed;
static constexpr uint8_t eSpeedH = 1,
static constexpr uint8_t eSpeedH = 20,
eSpeedN = 10,
eSpeedL = 20;
eSpeedL = 1;
uint8_t print_state;
uint8_t stepPrintSpeed;
uint8_t waitEndMoves;

2
Marlin/src/lcd/extui/ui_api.cpp

@ -169,7 +169,7 @@ namespace ExtUI {
}
void yield() {
if (!flags.printer_killed) thermalManager.manage_heater();
if (!flags.printer_killed) thermalManager.task();
}
void enableHeater(const extruder_t extruder) {

2
Marlin/src/lcd/extui/ui_api.h

@ -199,7 +199,7 @@ namespace ExtUI {
#endif
inline void simulateUserClick() {
#if ANY(HAS_MARLINUI_MENU, EXTENSIBLE_UI, DWIN_CREALITY_LCD_JYERSUI)
#if EITHER(HAS_MARLINUI_MENU, EXTENSIBLE_UI)
ui.lcd_clicked = true;
#endif
}

1
Marlin/src/lcd/language/language_en.h

@ -254,6 +254,7 @@ namespace Language_en {
LSTR MSG_UBL_SMART_FILLIN = _UxGT("Smart Fill-in");
LSTR MSG_UBL_FILLIN_MESH = _UxGT("Fill-in Mesh");
LSTR MSG_UBL_MESH_FILLED = _UxGT("Missing Points Filled");
LSTR MSG_UBL_MESH_INVALID = _UxGT("Invalid Mesh");
LSTR MSG_UBL_INVALIDATE_ALL = _UxGT("Invalidate All");
LSTR MSG_UBL_INVALIDATE_CLOSEST = _UxGT("Invalidate Closest");
LSTR MSG_UBL_FINE_TUNE_ALL = _UxGT("Fine Tune All");

5
Marlin/src/lcd/marlinui.cpp

@ -49,8 +49,6 @@ MarlinUI ui;
#include "e3v2/creality/dwin.h"
#elif ENABLED(DWIN_LCD_PROUI)
#include "e3v2/proui/dwin.h"
#elif ENABLED(DWIN_CREALITY_LCD_JYERSUI)
#include "e3v2/jyersui/dwin.h"
#endif
#if ENABLED(LCD_PROGRESS_BAR) && !IS_TFTGLCD_PANEL
@ -161,7 +159,7 @@ constexpr uint8_t epps = ENCODER_PULSES_PER_STEP;
bool MarlinUI::lcd_clicked;
#endif
#if EITHER(HAS_WIRED_LCD, DWIN_CREALITY_LCD_JYERSUI)
#if HAS_WIRED_LCD
bool MarlinUI::get_blink() {
static uint8_t blink = 0;
@ -1575,7 +1573,6 @@ void MarlinUI::init() {
TERN_(EXTENSIBLE_UI, ExtUI::onStatusChanged(status_message));
TERN_(DWIN_CREALITY_LCD, DWIN_StatusChanged(status_message));
TERN_(DWIN_LCD_PROUI, DWIN_CheckStatusMessage());
TERN_(DWIN_CREALITY_LCD_JYERSUI, CrealityDWIN.Update_Status(status_message));
}
#if ENABLED(STATUS_MESSAGE_SCROLLING)

18
Marlin/src/lcd/marlinui.h

@ -32,10 +32,6 @@
#include "tft_io/touch_calibration.h"
#endif
#if ANY(HAS_MARLINUI_MENU, ULTIPANEL_FEEDMULTIPLY, SOFT_RESET_ON_KILL)
#define HAS_ENCODER_ACTION 1
#endif
#if E_MANUAL > 1
#define MULTI_E_MANUAL 1
#endif
@ -87,11 +83,9 @@ typedef bool (*statusResetFunc_t)();
#endif // HAS_MARLINUI_MENU
#endif // HAS_WIRED_LCD
#if EITHER(HAS_WIRED_LCD, DWIN_CREALITY_LCD_JYERSUI)
#define LCD_UPDATE_INTERVAL TERN(HAS_TOUCH_BUTTONS, 50, 100)
#endif
#endif // HAS_WIRED_LCD
#if HAS_MARLINUI_U8GLIB
enum MarlinFont : uint8_t {
@ -273,8 +267,8 @@ public:
#endif
#if LCD_BACKLIGHT_TIMEOUT
#define LCD_BKL_TIMEOUT_MIN 1
#define LCD_BKL_TIMEOUT_MAX (60*60*18) // 18 hours max within uint16_t
#define LCD_BKL_TIMEOUT_MIN 1u
#define LCD_BKL_TIMEOUT_MAX UINT16_MAX // Slightly more than 18 hours
static uint16_t lcd_backlight_timeout;
static millis_t backlight_off_ms;
static void refresh_backlight_timeout();
@ -393,7 +387,7 @@ public:
static void poweroff();
#endif
#if EITHER(HAS_WIRED_LCD, DWIN_CREALITY_LCD_JYERSUI)
#if HAS_WIRED_LCD
static bool get_blink();
#endif
@ -630,7 +624,7 @@ public:
static bool use_click() { return false; }
#endif
#if ENABLED(ADVANCED_PAUSE_FEATURE) && ANY(HAS_MARLINUI_MENU, EXTENSIBLE_UI, DWIN_LCD_PROUI, DWIN_CREALITY_LCD_JYERSUI)
#if ENABLED(ADVANCED_PAUSE_FEATURE) && ANY(HAS_MARLINUI_MENU, EXTENSIBLE_UI, DWIN_LCD_PROUI)
static void pause_show_message(const PauseMessage message, const PauseMode mode=PAUSE_MODE_SAME, const uint8_t extruder=active_extruder);
#else
static void _pause_show_message() {}

5
Marlin/src/lcd/menu/menu_item.h

@ -27,6 +27,10 @@
#include "../../inc/MarlinConfigPre.h"
#if ENABLED(LASER_SYNCHRONOUS_M106_M107)
#include "../../module/planner.h"
#endif
void lcd_move_z();
////////////////////////////////////////////
@ -538,6 +542,7 @@ class MenuItem_bool : public MenuEditItemBase {
inline void on_fan_update() {
thermalManager.set_fan_speed(MenuItemBase::itemIndex, editable.uint8);
TERN_(LASER_SYNCHRONOUS_M106_M107, planner.buffer_sync_block(BLOCK_BIT_SYNC_FANS));
}
#if ENABLED(EXTRA_FAN_SPEED)

8
Marlin/src/lcd/menu/menu_motion.cpp

@ -348,6 +348,14 @@ void menu_motion() {
GCODES_ITEM(MSG_AUTO_Z_ALIGN, F("G34"));
#endif
//
// Probe Deploy/Stow
//
#if ENABLED(PROBE_DEPLOY_STOW_MENU)
GCODES_ITEM(MSG_MANUAL_DEPLOY, F("M401"));
GCODES_ITEM(MSG_MANUAL_STOW, F("M402"));
#endif
//
// Assisted Bed Tramming
//

18
Marlin/src/lcd/menu/menu_spindle_laser.cpp

@ -33,7 +33,7 @@
#include "../../feature/spindle_laser.h"
void menu_spindle_laser() {
bool is_enabled = cutter.enabled() && cutter.isReady;
bool is_enabled = cutter.enabled();
#if ENABLED(SPINDLE_CHANGE_DIR)
bool is_rev = cutter.is_reverse();
#endif
@ -49,7 +49,13 @@
#endif
editable.state = is_enabled;
EDIT_ITEM(bool, MSG_CUTTER(TOGGLE), &is_enabled, []{ if (editable.state) cutter.disable(); else cutter.enable_same_dir(); });
EDIT_ITEM(bool, MSG_CUTTER(TOGGLE), &is_enabled, []{
#if ENABLED(SPINDLE_FEATURE)
if (editable.state) cutter.disable(); else cutter.enable_same_dir();
#else
cutter.laser_menu_toggle(!editable.state);
#endif
});
#if ENABLED(AIR_EVACUATION)
bool evac_state = cutter.air_evac_state();
@ -72,12 +78,10 @@
// Setup and fire a test pulse using the current PWM power level for for a duration of test_pulse_min to test_pulse_max ms.
EDIT_ITEM_FAST(CUTTER_MENU_PULSE_TYPE, MSG_LASER_PULSE_MS, &cutter.testPulse, LASER_TEST_PULSE_MIN, LASER_TEST_PULSE_MAX);
ACTION_ITEM(MSG_LASER_FIRE_PULSE, cutter.test_fire_pulse);
#if ENABLED(HAL_CAN_SET_PWM_FREQ) && SPINDLE_LASER_FREQUENCY
EDIT_ITEM_FAST(CUTTER_MENU_FREQUENCY_TYPE, MSG_CUTTER_FREQUENCY, &cutter.frequency, 2000, 80000, cutter.refresh_frequency);
#endif
#endif
#if BOTH(MARLIN_DEV_MODE, HAL_CAN_SET_PWM_FREQ) && SPINDLE_LASER_FREQUENCY
EDIT_ITEM_FAST(CUTTER_MENU_FREQUENCY_TYPE, MSG_CUTTER_FREQUENCY, &cutter.frequency, 2000, 80000, cutter.refresh_frequency);
#endif
END_MENU();
}

998
Marlin/src/libs/L64XX/L64XX_Marlin.cpp

@ -1,998 +0,0 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
/**
* The monitor_driver routines are a close copy of the TMC code
*/
#include "../../inc/MarlinConfig.h"
#if HAS_L64XX
#include "L64XX_Marlin.h"
L64XX_Marlin L64xxManager;
#include "../../module/stepper/indirection.h"
#include "../../gcode/gcode.h"
#include "../../module/planner.h"
#include "../../HAL/shared/Delay.h"
static const char NUM_AXIS_LIST(
str_X[] PROGMEM = "X ", str_Y[] PROGMEM = "Y ", str_Z[] PROGMEM = "Z ",
str_I[] PROGMEM = STR_I " ", str_J[] PROGMEM = STR_J " ", str_K[] PROGMEM = STR_K " "
),
str_X2[] PROGMEM = "X2", str_Y2[] PROGMEM = "Y2",
str_Z2[] PROGMEM = "Z2", str_Z3[] PROGMEM = "Z3", str_Z4[] PROGMEM = "Z4",
LIST_N(EXTRUDERS,
str_E0[] PROGMEM = "E0", str_E1[] PROGMEM = "E1",
str_E2[] PROGMEM = "E2", str_E3[] PROGMEM = "E3",
str_E4[] PROGMEM = "E4", str_E5[] PROGMEM = "E5",
str_E6[] PROGMEM = "E6", str_E7[] PROGMEM = "E7"
)
;
#define _EN_ITEM(N) , str_E##N
PGM_P const L64XX_Marlin::index_to_axis[] PROGMEM = {
NUM_AXIS_LIST(str_X, str_Y, str_Z, str_I, str_J, str_K),
str_X2, str_Y2, str_Z2, str_Z3, str_Z4
REPEAT(E_STEPPERS, _EN_ITEM)
};
#undef _EN_ITEM
#define DEBUG_OUT ENABLED(L6470_CHITCHAT)
#include "../../core/debug_out.h"
void echo_yes_no(const bool yes) { DEBUG_ECHOPGM_P(yes ? PSTR(" YES") : PSTR(" NO ")); UNUSED(yes); }
uint8_t L64XX_Marlin::dir_commands[MAX_L64XX]; // array to hold direction command for each driver
#define _EN_ITEM(N) , ENABLED(INVERT_E##N##_DIR)
const uint8_t L64XX_Marlin::index_to_dir[MAX_L64XX] = {
NUM_AXIS_LIST(ENABLED(INVERT_X_DIR), ENABLED(INVERT_Y_DIR), ENABLED(INVERT_Z_DIR), ENABLED(INVERT_I_DIR), ENABLED(INVERT_J_DIR), ENABLED(INVERT_K_DIR), ENABLED(INVERT_U_DIR), ENABLED(INVERT_V_DIR), ENABLED(INVERT_W_DIR))
, ENABLED(INVERT_X_DIR) ^ BOTH(HAS_DUAL_X_STEPPERS, INVERT_X2_VS_X_DIR) // X2
, ENABLED(INVERT_Y_DIR) ^ BOTH(HAS_DUAL_Y_STEPPERS, INVERT_Y2_VS_Y_DIR) // Y2
, ENABLED(INVERT_Z_DIR) ^ ENABLED(INVERT_Z2_VS_Z_DIR) // Z2
, ENABLED(INVERT_Z_DIR) ^ ENABLED(INVERT_Z3_VS_Z_DIR) // Z3
, ENABLED(INVERT_Z_DIR) ^ ENABLED(INVERT_Z4_VS_Z_DIR) // Z4
REPEAT(E_STEPPERS, _EN_ITEM)
};
#undef _EN_ITEM
volatile uint8_t L64XX_Marlin::spi_abort = false;
uint8_t L64XX_Marlin::spi_active = false;
L64XX_Marlin::L64XX_shadow_t L64XX_Marlin::shadow;
//uint32_t UVLO_ADC = 0x0400; // ADC undervoltage event
void L6470_populate_chain_array() {
#define _L6470_INIT_SPI(Q) do{ stepper##Q.set_chain_info(Q, Q##_CHAIN_POS); }while(0)
#if AXIS_IS_L64XX(X)
_L6470_INIT_SPI(X);
#endif
#if AXIS_IS_L64XX(X2)
_L6470_INIT_SPI(X2);
#endif
#if AXIS_IS_L64XX(Y)
_L6470_INIT_SPI(Y);
#endif
#if AXIS_IS_L64XX(Y2)
_L6470_INIT_SPI(Y2);
#endif
#if AXIS_IS_L64XX(Z)
_L6470_INIT_SPI(Z);
#endif
#if AXIS_IS_L64XX(Z2)
_L6470_INIT_SPI(Z2);
#endif
#if AXIS_IS_L64XX(Z3)
_L6470_INIT_SPI(Z3);
#endif
#if AXIS_IS_L64XX(Z4)
_L6470_INIT_SPI(Z4);
#endif
#if AXIS_IS_L64XX(E0)
_L6470_INIT_SPI(E0);
#endif
#if AXIS_IS_L64XX(E1)
_L6470_INIT_SPI(E1);
#endif
#if AXIS_IS_L64XX(E2)
_L6470_INIT_SPI(E2);
#endif
#if AXIS_IS_L64XX(E3)
_L6470_INIT_SPI(E3);
#endif
#if AXIS_IS_L64XX(E4)
_L6470_INIT_SPI(E4);
#endif
#if AXIS_IS_L64XX(E5)
_L6470_INIT_SPI(E5);
#endif
#if AXIS_IS_L64XX(E6)
_L6470_INIT_SPI(E6);
#endif
#if AXIS_IS_L64XX(E7)
_L6470_INIT_SPI(E7);
#endif
}
/**
* Some status bit positions & definitions differ per driver.
* Copy info to known locations to simplfy check/display logic.
* 1. Copy stepper status
* 2. Copy status bit definitions
* 3. Copy status layout
* 4. Make all error bits active low (as needed)
*/
uint16_t L64XX_Marlin::get_stepper_status(L64XX &st) {
shadow.STATUS_AXIS_RAW = st.getStatus();
shadow.STATUS_AXIS = shadow.STATUS_AXIS_RAW;
shadow.STATUS_AXIS_LAYOUT = st.L6470_status_layout;
shadow.AXIS_OCD_TH_MAX = st.OCD_TH_MAX;
shadow.AXIS_STALL_TH_MAX = st.STALL_TH_MAX;
shadow.AXIS_OCD_CURRENT_CONSTANT_INV = st.OCD_CURRENT_CONSTANT_INV;
shadow.AXIS_STALL_CURRENT_CONSTANT_INV = st.STALL_CURRENT_CONSTANT_INV;
shadow.L6470_AXIS_CONFIG = st.L64XX_CONFIG;
shadow.L6470_AXIS_STATUS = st.L64XX_STATUS;
shadow.STATUS_AXIS_OCD = st.STATUS_OCD;
shadow.STATUS_AXIS_SCK_MOD = st.STATUS_SCK_MOD;
shadow.STATUS_AXIS_STEP_LOSS_A = st.STATUS_STEP_LOSS_A;
shadow.STATUS_AXIS_STEP_LOSS_B = st.STATUS_STEP_LOSS_B;
shadow.STATUS_AXIS_TH_SD = st.STATUS_TH_SD;
shadow.STATUS_AXIS_TH_WRN = st.STATUS_TH_WRN;
shadow.STATUS_AXIS_UVLO = st.STATUS_UVLO;
shadow.STATUS_AXIS_WRONG_CMD = st.STATUS_WRONG_CMD;
shadow.STATUS_AXIS_CMD_ERR = st.STATUS_CMD_ERR;
shadow.STATUS_AXIS_NOTPERF_CMD = st.STATUS_NOTPERF_CMD;
switch (shadow.STATUS_AXIS_LAYOUT) {
case L6470_STATUS_LAYOUT: { // L6470
shadow.L6470_ERROR_MASK = shadow.STATUS_AXIS_UVLO | shadow.STATUS_AXIS_TH_WRN | shadow.STATUS_AXIS_TH_SD | shadow.STATUS_AXIS_OCD | shadow.STATUS_AXIS_STEP_LOSS_A | shadow.STATUS_AXIS_STEP_LOSS_B;
shadow.STATUS_AXIS ^= (shadow.STATUS_AXIS_WRONG_CMD | shadow.STATUS_AXIS_NOTPERF_CMD); // invert just error bits that are active high
break;
}
case L6474_STATUS_LAYOUT: { // L6474
shadow.L6470_ERROR_MASK = shadow.STATUS_AXIS_UVLO | shadow.STATUS_AXIS_TH_WRN | shadow.STATUS_AXIS_TH_SD | shadow.STATUS_AXIS_OCD ;
shadow.STATUS_AXIS ^= (shadow.STATUS_AXIS_WRONG_CMD | shadow.STATUS_AXIS_NOTPERF_CMD); // invert just error bits that are active high
break;
}
case L6480_STATUS_LAYOUT: { // L6480 & powerSTEP01
shadow.L6470_ERROR_MASK = shadow.STATUS_AXIS_UVLO | shadow.STATUS_AXIS_TH_WRN | shadow.STATUS_AXIS_TH_SD | shadow.STATUS_AXIS_OCD | shadow.STATUS_AXIS_STEP_LOSS_A | shadow.STATUS_AXIS_STEP_LOSS_B;
shadow.STATUS_AXIS ^= (shadow.STATUS_AXIS_CMD_ERR | shadow.STATUS_AXIS_TH_WRN | shadow.STATUS_AXIS_TH_SD); // invert just error bits that are active high
break;
}
}
return shadow.STATUS_AXIS;
}
void L64XX_Marlin::init() { // Set up SPI and then init chips
ENABLE_RESET_L64XX_CHIPS(LOW); // hardware reset of drivers
DELAY_US(100);
ENABLE_RESET_L64XX_CHIPS(HIGH);
DELAY_US(1000); // need about 650µs for the chip(s) to fully start up
L6470_populate_chain_array(); // Set up array to control where in the SPI transfer sequence a particular stepper's data goes
spi_init(); // Since L64XX SPI pins are unset we must init SPI here
init_to_defaults(); // init the chips
}
uint16_t L64XX_Marlin::get_status(const L64XX_axis_t axis) {
#define STATUS_L6470(Q) get_stepper_status(stepper##Q)
switch (axis) {
default: break;
#if AXIS_IS_L64XX(X)
case X : return STATUS_L6470(X);
#endif
#if AXIS_IS_L64XX(Y)
case Y : return STATUS_L6470(Y);
#endif
#if AXIS_IS_L64XX(Z)
case Z : return STATUS_L6470(Z);
#endif
#if AXIS_IS_L64XX(X2)
case X2: return STATUS_L6470(X2);
#endif
#if AXIS_IS_L64XX(Y2)
case Y2: return STATUS_L6470(Y2);
#endif
#if AXIS_IS_L64XX(Z2)
case Z2: return STATUS_L6470(Z2);
#endif
#if AXIS_IS_L64XX(Z3)
case Z3: return STATUS_L6470(Z3);
#endif
#if AXIS_IS_L64XX(Z4)
case Z4: return STATUS_L6470(Z4);
#endif
#if AXIS_IS_L64XX(E0)
case E0: return STATUS_L6470(E0);
#endif
#if AXIS_IS_L64XX(E1)
case E1: return STATUS_L6470(E1);
#endif
#if AXIS_IS_L64XX(E2)
case E2: return STATUS_L6470(E2);
#endif
#if AXIS_IS_L64XX(E3)
case E3: return STATUS_L6470(E3);
#endif
#if AXIS_IS_L64XX(E4)
case E4: return STATUS_L6470(E4);
#endif
#if AXIS_IS_L64XX(E5)
case E5: return STATUS_L6470(E5);
#endif
#if AXIS_IS_L64XX(E6)
case E6: return STATUS_L6470(E6);
#endif
#if AXIS_IS_L64XX(E7)
case E7: return STATUS_L6470(E7);
#endif
}
return 0; // Not needed but kills a compiler warning
}
uint32_t L64XX_Marlin::get_param(const L64XX_axis_t axis, const uint8_t param) {
#define GET_L6470_PARAM(Q) L6470_GETPARAM(param, Q)
switch (axis) {
default: break;
#if AXIS_IS_L64XX(X)
case X : return GET_L6470_PARAM(X);
#endif
#if AXIS_IS_L64XX(Y)
case Y : return GET_L6470_PARAM(Y);
#endif
#if AXIS_IS_L64XX(Z)
case Z : return GET_L6470_PARAM(Z);
#endif
#if AXIS_IS_L64XX(X2)
case X2: return GET_L6470_PARAM(X2);
#endif
#if AXIS_IS_L64XX(Y2)
case Y2: return GET_L6470_PARAM(Y2);
#endif
#if AXIS_IS_L64XX(Z2)
case Z2: return GET_L6470_PARAM(Z2);
#endif
#if AXIS_IS_L64XX(Z3)
case Z3: return GET_L6470_PARAM(Z3);
#endif
#if AXIS_IS_L64XX(Z4)
case Z4: return GET_L6470_PARAM(Z4);
#endif
#if AXIS_IS_L64XX(E0)
case E0: return GET_L6470_PARAM(E0);
#endif
#if AXIS_IS_L64XX(E1)
case E1: return GET_L6470_PARAM(E1);
#endif
#if AXIS_IS_L64XX(E2)
case E2: return GET_L6470_PARAM(E2);
#endif
#if AXIS_IS_L64XX(E3)
case E3: return GET_L6470_PARAM(E3);
#endif
#if AXIS_IS_L64XX(E4)
case E4: return GET_L6470_PARAM(E4);
#endif
#if AXIS_IS_L64XX(E5)
case E5: return GET_L6470_PARAM(E5);
#endif
#if AXIS_IS_L64XX(E6)
case E6: return GET_L6470_PARAM(E6);
#endif
#if AXIS_IS_L64XX(E7)
case E7: return GET_L6470_PARAM(E7);
#endif
}
return 0; // not needed but kills a compiler warning
}
void L64XX_Marlin::set_param(const L64XX_axis_t axis, const uint8_t param, const uint32_t value) {
#define SET_L6470_PARAM(Q) stepper##Q.SetParam(param, value)
switch (axis) {
default: break;
#if AXIS_IS_L64XX(X)
case X : SET_L6470_PARAM(X); break;
#endif
#if AXIS_IS_L64XX(Y)
case Y : SET_L6470_PARAM(Y); break;
#endif
#if AXIS_IS_L64XX(Z)
case Z : SET_L6470_PARAM(Z); break;
#endif
#if AXIS_IS_L64XX(I)
case I : SET_L6470_PARAM(I); break;
#endif
#if AXIS_IS_L64XX(J)
case J : SET_L6470_PARAM(J); break;
#endif
#if AXIS_IS_L64XX(K)
case K : SET_L6470_PARAM(K); break;
#endif
#if AXIS_IS_L64XX(X2)
case X2: SET_L6470_PARAM(X2); break;
#endif
#if AXIS_IS_L64XX(Y2)
case Y2: SET_L6470_PARAM(Y2); break;
#endif
#if AXIS_IS_L64XX(Z2)
case Z2: SET_L6470_PARAM(Z2); break;
#endif
#if AXIS_IS_L64XX(Z3)
case Z3: SET_L6470_PARAM(Z3); break;
#endif
#if AXIS_IS_L64XX(Z4)
case Z4: SET_L6470_PARAM(Z4); break;
#endif
#if AXIS_IS_L64XX(E0)
case E0: SET_L6470_PARAM(E0); break;
#endif
#if AXIS_IS_L64XX(E1)
case E1: SET_L6470_PARAM(E1); break;
#endif
#if AXIS_IS_L64XX(E2)
case E2: SET_L6470_PARAM(E2); break;
#endif
#if AXIS_IS_L64XX(E3)
case E3: SET_L6470_PARAM(E3); break;
#endif
#if AXIS_IS_L64XX(E4)
case E4: SET_L6470_PARAM(E4); break;
#endif
#if AXIS_IS_L64XX(E5)
case E5: SET_L6470_PARAM(E5); break;
#endif
#if AXIS_IS_L64XX(E6)
case E6: SET_L6470_PARAM(E6); break;
#endif
#if AXIS_IS_L64XX(E7)
case E7: SET_L6470_PARAM(E7); break;
#endif
}
}
inline void echo_min_max(const char a, const_float_t min, const_float_t max) {
DEBUG_CHAR(' '); DEBUG_CHAR(a);
DEBUG_ECHOLNPGM(" min = ", min, " max = ", max);
}
inline void echo_oct_used(const_float_t oct, const uint8_t stall) {
DEBUG_ECHOPGM("over_current_threshold used : ", oct);
DEBUG_ECHOPGM_P(stall ? PSTR(" (Stall") : PSTR(" (OCD"));
DEBUG_ECHOLNPGM(" threshold)");
}
inline void err_out_of_bounds() { DEBUG_ECHOLNPGM("Test aborted - motion out of bounds"); }
uint8_t L64XX_Marlin::get_user_input(uint8_t &driver_count, L64XX_axis_t axis_index[3], char axis_mon[3][3],
float &position_max, float &position_min, float &final_feedrate, uint8_t &kval_hold,
uint8_t over_current_flag, uint8_t &OCD_TH_val, uint8_t &STALL_TH_val, uint16_t &over_current_threshold
) {
// Return TRUE if the calling routine needs to abort/kill
uint16_t displacement = 0; // " = 0" to eliminate compiler warning
uint8_t j; // general purpose counter
if (!all_axes_homed()) {
DEBUG_ECHOLNPGM("Test aborted - home all before running this command");
return true;
}
uint8_t found_displacement = false;
LOOP_LOGICAL_AXES(i) if (uint16_t _displacement = parser.intval(AXIS_CHAR(i))) {
found_displacement = true;
displacement = _displacement;
const uint8_t axis_offset = parser.byteval('J');
axis_mon[0][0] = AXIS_CHAR(i); // Axis first character, one of XYZ...E
const bool single_or_e = axis_offset >= 2 || axis_mon[0][0] == 'E',
one_or_more = !single_or_e && axis_offset == 0;
uint8_t driver_count_local = 0; // Can't use "driver_count" directly as a subscript because it's passed by reference
if (single_or_e) // Single axis, E0, or E1
axis_mon[0][1] = axis_offset + '0'; // Index given by 'J' parameter
if (single_or_e || one_or_more) {
for (j = 0; j < MAX_L64XX; j++) { // Count up the drivers on this axis
PGM_P str = (PGM_P)pgm_read_ptr(&index_to_axis[j]); // Get a PGM_P from progmem
const char c = pgm_read_byte(str); // Get a char from progmem
if (axis_mon[0][0] == c) { // For each stepper on this axis...
char *mon = axis_mon[driver_count_local];
*mon++ = c; // Copy the 3 letter axis name
*mon++ = pgm_read_byte(&str[1]); // to the axis_mon array
*mon = pgm_read_byte(&str[2]);
axis_index[driver_count_local] = (L64XX_axis_t)j; // And store the L64XX axis index
driver_count_local++;
}
}
if (one_or_more) driver_count = driver_count_local;
}
break; // only take first axis found
}
if (!found_displacement) {
DEBUG_ECHOLNPGM("Test aborted - AXIS with displacement is required");
return true;
}
//
// Position calcs & checks
//
const float LOGICAL_AXIS_LIST(
E_center = current_position.e,
X_center = LOGICAL_X_POSITION(current_position.x),
Y_center = LOGICAL_Y_POSITION(current_position.y),
Z_center = LOGICAL_Z_POSITION(current_position.z),
I_center = LOGICAL_I_POSITION(current_position.i),
J_center = LOGICAL_J_POSITION(current_position.j),
K_center = LOGICAL_K_POSITION(current_position.k)
);
switch (axis_mon[0][0]) {
default: position_max = position_min = 0; break;
case 'X': {
position_min = X_center - displacement;
position_max = X_center + displacement;
echo_min_max('X', position_min, position_max);
if (TERN0(HAS_ENDSTOPS, position_min < (X_MIN_POS) || position_max > (X_MAX_POS))) {
err_out_of_bounds();
return true;
}
} break;
#if HAS_Y_AXIS
case 'Y': {
position_min = Y_center - displacement;
position_max = Y_center + displacement;
echo_min_max('Y', position_min, position_max);
if (TERN0(HAS_ENDSTOPS, position_min < (Y_MIN_POS) || position_max > (Y_MAX_POS))) {
err_out_of_bounds();
return true;
}
} break;
#endif
#if HAS_Z_AXIS
case 'Z': {
position_min = Z_center - displacement;
position_max = Z_center + displacement;
echo_min_max('Z', position_min, position_max);
if (TERN0(HAS_ENDSTOPS, position_min < (Z_MIN_POS) || position_max > (Z_MAX_POS))) {
err_out_of_bounds();
return true;
}
} break;
#endif
#if HAS_I_AXIS
case AXIS4_NAME: {
position_min = I_center - displacement;
position_max = I_center + displacement;
echo_min_max(AXIS4_NAME, position_min, position_max);
if (TERN0(HAS_ENDSTOPS, position_min < (I_MIN_POS) || position_max > (I_MAX_POS))) {
err_out_of_bounds();
return true;
}
} break;
#endif
#if HAS_J_AXIS
case AXIS5_NAME: {
position_min = J_center - displacement;
position_max = J_center + displacement;
echo_min_max(AXIS5_NAME, position_min, position_max);
if (TERN1(HAS_ENDSTOPS, position_min < (J_MIN_POS) || position_max > (J_MAX_POS))) {
err_out_of_bounds();
return true;
}
} break;
#endif
#if HAS_K_AXIS
case AXIS6_NAME: {
position_min = K_center - displacement;
position_max = K_center + displacement;
echo_min_max(AXIS6_NAME, position_min, position_max);
if (TERN2(HAS_ENDSTOPS, position_min < (K_MIN_POS) || position_max > (K_MAX_POS))) {
err_out_of_bounds();
return true;
}
} break;
#endif
#if HAS_EXTRUDERS
case 'E': {
position_min = E_center - displacement;
position_max = E_center + displacement;
echo_min_max('E', position_min, position_max);
} break;
#endif
}
//
// Work on the drivers
//
LOOP_L_N(k, driver_count) {
uint8_t not_found = true;
for (j = 1; j <= L64XX::chain[0]; j++) {
PGM_P const str = (PGM_P)pgm_read_ptr(&index_to_axis[L64XX::chain[j]]);
if (pgm_read_byte(&str[0]) == axis_mon[k][0] && pgm_read_byte(&str[1]) == axis_mon[k][1]) { // See if a L6470 driver
not_found = false;
break;
}
}
if (not_found) {
driver_count = k;
axis_mon[k][0] = ' '; // mark this entry invalid
break;
}
}
if (driver_count == 0) {
DEBUG_ECHOLNPGM("Test aborted - not a L6470 axis");
return true;
}
DEBUG_ECHOPGM("Monitoring:");
for (j = 0; j < driver_count; j++) DEBUG_ECHOPGM(" ", axis_mon[j]);
DEBUG_EOL();
// now have a list of driver(s) to monitor
//
// TVAL & kVAL_HOLD checks & settings
//
const L64XX_shadow_t &sh = shadow;
get_status(axis_index[0]); // populate shadow array
if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT) { // L6474 - use TVAL
uint16_t TVAL_current = parser.ushortval('T');
if (TVAL_current) {
uint8_t TVAL_count = (TVAL_current / sh.AXIS_STALL_CURRENT_CONSTANT_INV) - 1;
LIMIT(TVAL_count, 0, sh.AXIS_STALL_TH_MAX);
for (j = 0; j < driver_count; j++)
set_param(axis_index[j], L6474_TVAL, TVAL_count);
}
// only print the tval from one of the drivers
kval_hold = get_param(axis_index[0], L6474_TVAL);
DEBUG_ECHOLNPGM("TVAL current (mA) = ", (kval_hold + 1) * sh.AXIS_STALL_CURRENT_CONSTANT_INV);
}
else {
kval_hold = parser.byteval('K');
if (kval_hold) {
DEBUG_ECHOLNPGM("kval_hold = ", kval_hold);
for (j = 0; j < driver_count; j++)
set_param(axis_index[j], L6470_KVAL_HOLD, kval_hold);
}
else {
// only print the KVAL_HOLD from one of the drivers
kval_hold = get_param(axis_index[0], L6470_KVAL_HOLD);
DEBUG_ECHOLNPGM("KVAL_HOLD = ", kval_hold);
}
}
//
// Overcurrent checks & settings
//
if (over_current_flag) {
uint8_t OCD_TH_val_local = 0, // compiler thinks OCD_TH_val is unused if use it directly
STALL_TH_val_local = 0; // just in case ...
over_current_threshold = parser.intval('I');
if (over_current_threshold) {
OCD_TH_val_local = over_current_threshold/375;
LIMIT(OCD_TH_val_local, 0, 15);
STALL_TH_val_local = over_current_threshold/31.25;
LIMIT(STALL_TH_val_local, 0, 127);
uint16_t OCD_TH_actual = (OCD_TH_val_local + 1) * 375,
STALL_TH_actual = (STALL_TH_val_local + 1) * 31.25;
if (OCD_TH_actual < STALL_TH_actual) {
OCD_TH_val_local++;
OCD_TH_actual = (OCD_TH_val_local + 1) * 375;
}
DEBUG_ECHOLNPGM("over_current_threshold specified: ", over_current_threshold);
if (!(sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT)) echo_oct_used((STALL_TH_val_local + 1) * 31.25, true);
echo_oct_used((OCD_TH_val_local + 1) * 375, false);
#define SET_OVER_CURRENT(Q) do { stepper##Q.SetParam(L6470_STALL_TH, STALL_TH_val_local); stepper##Q.SetParam(L6470_OCD_TH, OCD_TH_val_local);} while (0)
for (j = 0; j < driver_count; j++) {
set_param(axis_index[j], L6470_STALL_TH, STALL_TH_val_local);
set_param(axis_index[j], L6470_OCD_TH, OCD_TH_val_local);
}
}
else {
// only get & print the OVER_CURRENT values from one of the drivers
STALL_TH_val_local = get_param(axis_index[0], L6470_STALL_TH);
OCD_TH_val_local = get_param(axis_index[0], L6470_OCD_TH);
if (!(sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT)) echo_oct_used((STALL_TH_val_local + 1) * 31.25, true);
echo_oct_used((OCD_TH_val_local + 1) * 375, false);
} // over_current_threshold
for (j = 0; j < driver_count; j++) { // set all drivers on axis the same
set_param(axis_index[j], L6470_STALL_TH, STALL_TH_val_local);
set_param(axis_index[j], L6470_OCD_TH, OCD_TH_val_local);
}
OCD_TH_val = OCD_TH_val_local; // force compiler to update the main routine's copy
STALL_TH_val = STALL_TH_val_local; // force compiler to update the main routine's copy
} // end of overcurrent
//
// Feedrate
//
final_feedrate = parser.floatval('F');
if (final_feedrate == 0) {
static constexpr float default_max_feedrate[] = DEFAULT_MAX_FEEDRATE;
const uint8_t num_feedrates = COUNT(default_max_feedrate);
for (j = 0; j < num_feedrates; j++) {
if (AXIS_CHAR(j) == axis_mon[0][0]) {
final_feedrate = default_max_feedrate[j];
break;
}
}
if (j == 3 && num_feedrates > 4) { // have more than one extruder feedrate
uint8_t extruder_num = axis_mon[0][1] - '0';
if (j <= num_feedrates - extruder_num) // have a feedrate specifically for this extruder
final_feedrate = default_max_feedrate[j + extruder_num];
else
final_feedrate = default_max_feedrate[3]; // use E0 feedrate for this extruder
}
final_feedrate *= 60; // convert to mm/minute
} // end of feedrate
return false; // FALSE indicates no user input problems
}
void L64XX_Marlin::say_axis(const L64XX_axis_t axis, const uint8_t label/*=true*/) {
if (label) SERIAL_ECHOPGM("AXIS:");
const char * const str = L64xxManager.index_to_axis[axis];
SERIAL_CHAR(' ', str[0], str[1], ' ');
}
#if ENABLED(L6470_CHITCHAT)
// Assumes status bits have been inverted
void L64XX_Marlin::error_status_decode(const uint16_t status, const L64XX_axis_t axis,
const uint16_t _status_axis_th_sd, const uint16_t _status_axis_th_wrn,
const uint16_t _status_axis_step_loss_a, const uint16_t _status_axis_step_loss_b,
const uint16_t _status_axis_ocd, const uint8_t _status_axis_layout
) {
say_axis(axis);
DEBUG_ECHOPGM(" THERMAL: ");
DEBUG_ECHOPGM_P((status & _status_axis_th_sd) ? PSTR("SHUTDOWN") : (status & _status_axis_th_wrn) ? PSTR("WARNING ") : PSTR("OK "));
DEBUG_ECHOPGM(" OVERCURRENT: ");
echo_yes_no((status & _status_axis_ocd) != 0);
if (!(_status_axis_layout == L6474_STATUS_LAYOUT)) { // L6474 doesn't have these bits
DEBUG_ECHOPGM(" STALL: ");
echo_yes_no((status & (_status_axis_step_loss_a | _status_axis_step_loss_b)) != 0);
}
DEBUG_EOL();
}
#endif
//////////////////////////////////////////////////////////////////////////////////////////////////
////
//// MONITOR_L6470_DRIVER_STATUS routines
////
//////////////////////////////////////////////////////////////////////////////////////////////////
#if ENABLED(MONITOR_L6470_DRIVER_STATUS)
bool L64XX_Marlin::monitor_paused = false; // Flag to skip monitor during M122, M906, M916, M917, M918, etc.
struct L6470_driver_data {
L64XX_axis_t driver_index;
uint32_t driver_status;
uint8_t is_otw;
uint8_t otw_counter;
uint8_t is_ot;
uint8_t is_hi_Z;
uint8_t com_counter;
};
L6470_driver_data driver_L6470_data[] = {
#if AXIS_IS_L64XX(X)
{ X, 0, 0, 0, 0, 0, 0 },
#endif
#if AXIS_IS_L64XX(Y)
{ Y, 0, 0, 0, 0, 0, 0 },
#endif
#if AXIS_IS_L64XX(Z)
{ Z, 0, 0, 0, 0, 0, 0 },
#endif
#if AXIS_IS_L64XX(I)
{ I, 0, 0, 0, 0, 0, 0 },
#endif
#if AXIS_IS_L64XX(J)
{ J, 0, 0, 0, 0, 0, 0 },
#endif
#if AXIS_IS_L64XX(K)
{ K, 0, 0, 0, 0, 0, 0 },
#endif
#if AXIS_IS_L64XX(X2)
{ X2, 0, 0, 0, 0, 0, 0 },
#endif
#if AXIS_IS_L64XX(Y2)
{ Y2, 0, 0, 0, 0, 0, 0 },
#endif
#if AXIS_IS_L64XX(Z2)
{ Z2, 0, 0, 0, 0, 0, 0 },
#endif
#if AXIS_IS_L64XX(Z3)
{ Z3, 0, 0, 0, 0, 0, 0 },
#endif
#if AXIS_IS_L64XX(Z4)
{ Z4, 0, 0, 0, 0, 0, 0 },
#endif
#if AXIS_IS_L64XX(E0)
{ E0, 0, 0, 0, 0, 0, 0 },
#endif
#if AXIS_IS_L64XX(E1)
{ E1, 0, 0, 0, 0, 0, 0 },
#endif
#if AXIS_IS_L64XX(E2)
{ E2, 0, 0, 0, 0, 0, 0 },
#endif
#if AXIS_IS_L64XX(E3)
{ E3, 0, 0, 0, 0, 0, 0 },
#endif
#if AXIS_IS_L64XX(E4)
{ E4, 0, 0, 0, 0, 0, 0 },
#endif
#if AXIS_IS_L64XX(E5)
{ E5, 0, 0, 0, 0, 0, 0 }
#endif
#if AXIS_IS_L64XX(E6)
{ E6, 0, 0, 0, 0, 0, 0 }
#endif
#if AXIS_IS_L64XX(E7)
{ E7, 0, 0, 0, 0, 0, 0 }
#endif
};
void L64XX_Marlin::append_stepper_err(char* &p, const uint8_t stepper_index, const char * const err/*=nullptr*/) {
PGM_P const str = (PGM_P)pgm_read_ptr(&index_to_axis[stepper_index]);
p += sprintf_P(p, PSTR("Stepper %c%c "), pgm_read_byte(&str[0]), pgm_read_byte(&str[1]));
if (err) p += sprintf_P(p, err);
}
void L64XX_Marlin::monitor_update(L64XX_axis_t stepper_index) {
if (spi_abort) return; // don't do anything if set_directions() has occurred
const L64XX_shadow_t &sh = shadow;
get_status(stepper_index); // get stepper status and details
uint16_t status = sh.STATUS_AXIS;
uint8_t kval_hold, tval;
char temp_buf[120], *p = temp_buf;
uint8_t j;
for (j = 0; j < L64XX::chain[0]; j++) // find the table for this stepper
if (driver_L6470_data[j].driver_index == stepper_index) break;
driver_L6470_data[j].driver_status = status;
uint16_t _status = ~status; // all error bits are active low
if (status == 0 || status == 0xFFFF) { // com problem
if (driver_L6470_data[j].com_counter == 0) { // warn user when it first happens
driver_L6470_data[j].com_counter++;
append_stepper_err(p, stepper_index, PSTR(" - communications lost\n"));
DEBUG_ECHO(temp_buf);
}
else {
driver_L6470_data[j].com_counter++;
if (driver_L6470_data[j].com_counter > 240) { // remind of com problem about every 2 minutes
driver_L6470_data[j].com_counter = 1;
append_stepper_err(p, stepper_index, PSTR(" - still no communications\n"));
DEBUG_ECHO(temp_buf);
}
}
}
else {
if (driver_L6470_data[j].com_counter) { // comms re-established
driver_L6470_data[j].com_counter = 0;
append_stepper_err(p, stepper_index, PSTR(" - communications re-established\n.. setting all drivers to default values\n"));
DEBUG_ECHO(temp_buf);
init_to_defaults();
}
else {
// no com problems - do the usual checks
if (_status & sh.L6470_ERROR_MASK) {
append_stepper_err(p, stepper_index);
if (status & STATUS_HIZ) { // The driver has shut down. HiZ is active high
driver_L6470_data[j].is_hi_Z = true;
p += sprintf_P(p, PSTR("%cIS SHUT DOWN"), ' ');
//if (_status & sh.STATUS_AXIS_TH_SD) { // strange - TH_SD never seems to go active, must be implied by the HiZ and TH_WRN
if (_status & sh.STATUS_AXIS_TH_WRN) { // over current shutdown
p += sprintf_P(p, PSTR("%cdue to over temperature"), ' ');
driver_L6470_data[j].is_ot = true;
if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT) { // L6474
tval = get_param(stepper_index, L6474_TVAL) - 2 * KVAL_HOLD_STEP_DOWN;
set_param(stepper_index, L6474_TVAL, tval); // reduce TVAL
p += sprintf_P(p, PSTR(" - TVAL reduced by %d to %d mA"), uint16_t (2 * KVAL_HOLD_STEP_DOWN * sh.AXIS_STALL_CURRENT_CONSTANT_INV), uint16_t ((tval + 1) * sh.AXIS_STALL_CURRENT_CONSTANT_INV)); // let user know
}
else {
kval_hold = get_param(stepper_index, L6470_KVAL_HOLD) - 2 * KVAL_HOLD_STEP_DOWN;
set_param(stepper_index, L6470_KVAL_HOLD, kval_hold); // reduce KVAL_HOLD
p += sprintf_P(p, PSTR(" - KVAL_HOLD reduced by %d to %d"), 2 * KVAL_HOLD_STEP_DOWN, kval_hold); // let user know
}
}
else
driver_L6470_data[j].is_ot = false;
}
else {
driver_L6470_data[j].is_hi_Z = false;
if (_status & sh.STATUS_AXIS_TH_WRN) { // have an over temperature warning
driver_L6470_data[j].is_otw = true;
driver_L6470_data[j].otw_counter++;
kval_hold = get_param(stepper_index, L6470_KVAL_HOLD);
if (driver_L6470_data[j].otw_counter > 4) { // otw present for 2 - 2.5 seconds, reduce KVAL_HOLD
driver_L6470_data[j].otw_counter = 0;
driver_L6470_data[j].is_otw = true;
if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT) { // L6474
tval = get_param(stepper_index, L6474_TVAL) - KVAL_HOLD_STEP_DOWN;
set_param(stepper_index, L6474_TVAL, tval); // reduce TVAL
p += sprintf_P(p, PSTR(" - TVAL reduced by %d to %d mA"), uint16_t (KVAL_HOLD_STEP_DOWN * sh.AXIS_STALL_CURRENT_CONSTANT_INV), uint16_t ((tval + 1) * sh.AXIS_STALL_CURRENT_CONSTANT_INV)); // let user know
}
else {
kval_hold = get_param(stepper_index, L6470_KVAL_HOLD) - KVAL_HOLD_STEP_DOWN;
set_param(stepper_index, L6470_KVAL_HOLD, kval_hold); // reduce KVAL_HOLD
p += sprintf_P(p, PSTR(" - KVAL_HOLD reduced by %d to %d"), KVAL_HOLD_STEP_DOWN, kval_hold); // let user know
}
}
else if (driver_L6470_data[j].otw_counter)
p += sprintf_P(p, PSTR("%c- thermal warning"), ' '); // warn user
}
}
#if ENABLED(L6470_STOP_ON_ERROR)
if (_status & (sh.STATUS_AXIS_UVLO | sh.STATUS_AXIS_TH_WRN | sh.STATUS_AXIS_TH_SD))
kill(temp_buf);
#endif
#if ENABLED(L6470_CHITCHAT)
if (_status & sh.STATUS_AXIS_OCD)
p += sprintf_P(p, PSTR("%c over current"), ' ');
if (_status & (sh.STATUS_AXIS_STEP_LOSS_A | sh.STATUS_AXIS_STEP_LOSS_B))
p += sprintf_P(p, PSTR("%c stall"), ' ');
if (_status & sh.STATUS_AXIS_UVLO)
p += sprintf_P(p, PSTR("%c under voltage lock out"), ' ');
p += sprintf_P(p, PSTR("%c\n"), ' ');
#endif
DEBUG_ECHOLN(temp_buf); // print the error message
}
else {
driver_L6470_data[j].is_ot = false;
driver_L6470_data[j].otw_counter = 0; //clear out warning indicators
driver_L6470_data[j].is_otw = false;
} // end usual checks
} // comms established but have errors
} // comms re-established
} // end monitor_update()
void L64XX_Marlin::monitor_driver() {
static millis_t next_cOT = 0;
if (ELAPSED(millis(), next_cOT)) {
next_cOT = millis() + 500;
if (!monitor_paused) { // Skip during M122, M906, M916, M917 or M918 (could steal status result from test)
spi_active = true; // Tell set_directions() a series of SPI transfers is underway
#if AXIS_IS_L64XX(X)
monitor_update(X);
#endif
#if AXIS_IS_L64XX(Y)
monitor_update(Y);
#endif
#if AXIS_IS_L64XX(Z)
monitor_update(Z);
#endif
#if AXIS_IS_L64XX(I)
monitor_update(I);
#endif
#if AXIS_IS_L64XX(J)
monitor_update(J);
#endif
#if AXIS_IS_L64XX(K)
monitor_update(K);
#endif
#if AXIS_IS_L64XX(X2)
monitor_update(X2);
#endif
#if AXIS_IS_L64XX(Y2)
monitor_update(Y2);
#endif
#if AXIS_IS_L64XX(Z2)
monitor_update(Z2);
#endif
#if AXIS_IS_L64XX(Z3)
monitor_update(Z3);
#endif
#if AXIS_IS_L64XX(Z4)
monitor_update(Z4);
#endif
#if AXIS_IS_L64XX(E0)
monitor_update(E0);
#endif
#if AXIS_IS_L64XX(E1)
monitor_update(E1);
#endif
#if AXIS_IS_L64XX(E2)
monitor_update(E2);
#endif
#if AXIS_IS_L64XX(E3)
monitor_update(E3);
#endif
#if AXIS_IS_L64XX(E4)
monitor_update(E4);
#endif
#if AXIS_IS_L64XX(E5)
monitor_update(E5);
#endif
#if AXIS_IS_L64XX(E6)
monitor_update(E6);
#endif
#if AXIS_IS_L64XX(E7)
monitor_update(E7);
#endif
if (TERN0(L6470_DEBUG, report_L6470_status)) DEBUG_EOL();
spi_active = false; // done with all SPI transfers - clear handshake flags
spi_abort = false;
}
}
}
#endif // MONITOR_L6470_DRIVER_STATUS
#endif // HAS_L64XX

141
Marlin/src/libs/L64XX/L64XX_Marlin.h

@ -1,141 +0,0 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#pragma once
#include "../../inc/MarlinConfig.h"
#include <L6470.h>
#if !(L6470_LIBRARY_VERSION >= 0x000800)
#error 'L6470_LIBRARY_VERSION 0x000800 or later required'
#endif
#define L6470_GETPARAM(P,Q) stepper##Q.GetParam(P)
#define dSPIN_STEP_CLOCK 0x58
#define dSPIN_STEP_CLOCK_FWD dSPIN_STEP_CLOCK
#define dSPIN_STEP_CLOCK_REV dSPIN_STEP_CLOCK+1
#define HAS_L64XX_EXTRUDER (AXIS_IS_L64XX(E0) || AXIS_IS_L64XX(E1) || AXIS_IS_L64XX(E2) || AXIS_IS_L64XX(E3) || AXIS_IS_L64XX(E4) || AXIS_IS_L64XX(E5) || AXIS_IS_L64XX(E6) || AXIS_IS_L64XX(E7))
#define _EN_ITEM(N) , E##N
enum L64XX_axis_t : uint8_t { MAIN_AXIS_NAMES, X2, Y2, Z2, Z3, Z4 REPEAT(E_STEPPERS, _EN_ITEM), MAX_L64XX };
#undef _EN_ITEM
class L64XX_Marlin : public L64XXHelper {
public:
static PGM_P const index_to_axis[MAX_L64XX];
static const uint8_t index_to_dir[MAX_L64XX];
static uint8_t dir_commands[MAX_L64XX];
// Flags to guarantee graceful switch if stepper interrupts L6470 SPI transfer
static volatile uint8_t spi_abort;
static uint8_t spi_active;
L64XX_Marlin() {}
static void init();
static void init_to_defaults();
static uint16_t get_stepper_status(L64XX &st);
static uint16_t get_status(const L64XX_axis_t axis);
static uint32_t get_param(const L64XX_axis_t axis, const uint8_t param);
static void set_param(const L64XX_axis_t axis, const uint8_t param, const uint32_t value);
//static void send_command(const L64XX_axis_t axis, uint8_t command);
static uint8_t get_user_input(uint8_t &driver_count, L64XX_axis_t axis_index[3], char axis_mon[3][3],
float &position_max, float &position_min, float &final_feedrate, uint8_t &kval_hold,
uint8_t over_current_flag, uint8_t &OCD_TH_val, uint8_t &STALL_TH_val, uint16_t &over_current_threshold);
static void transfer(uint8_t L6470_buf[], const uint8_t length);
static void say_axis(const L64XX_axis_t axis, const uint8_t label=true);
#if ENABLED(L6470_CHITCHAT)
static void error_status_decode(
const uint16_t status, const L64XX_axis_t axis,
const uint16_t _status_axis_th_sd, const uint16_t _status_axis_th_wrn,
const uint16_t _status_axis_step_loss_a, const uint16_t _status_axis_step_loss_b,
const uint16_t _status_axis_ocd, const uint8_t _status_axis_layout
);
#else
FORCE_INLINE static void error_status_decode(
const uint16_t, const L64XX_axis_t,
const uint16_t, const uint16_t,
const uint16_t, const uint16_t,
const uint16_t, const uint8_t
){}
#endif
// ~40 bytes SRAM to simplify status decode routines
typedef struct {
uint8_t STATUS_AXIS_LAYOUT; // Copy of L6470_status_layout
uint8_t AXIS_OCD_TH_MAX; // Size of OCD_TH field
uint8_t AXIS_STALL_TH_MAX; // Size of STALL_TH field
float AXIS_OCD_CURRENT_CONSTANT_INV; // mA per count
float AXIS_STALL_CURRENT_CONSTANT_INV; // mA per count
uint8_t L6470_AXIS_CONFIG, // Address of the CONFIG register
L6470_AXIS_STATUS; // Address of the STATUS register
uint16_t L6470_ERROR_MASK, // STATUS_UVLO | STATUS_TH_WRN | STATUS_TH_SD | STATUS_OCD | STATUS_STEP_LOSS_A | STATUS_STEP_LOSS_B
L6474_ERROR_MASK, // STATUS_UVLO | STATUS_TH_WRN | STATUS_TH_SD | STATUS_OCD
STATUS_AXIS_RAW, // Copy of status register contents
STATUS_AXIS, // Copy of status register contents but with all error bits active low
STATUS_AXIS_OCD, // Overcurrent detected bit position
STATUS_AXIS_SCK_MOD, // Step clock mode is active bit position
STATUS_AXIS_STEP_LOSS_A, // Stall detected on A bridge bit position
STATUS_AXIS_STEP_LOSS_B, // Stall detected on B bridge bit position
STATUS_AXIS_TH_SD, // Thermal shutdown bit position
STATUS_AXIS_TH_WRN, // Thermal warning bit position
STATUS_AXIS_UVLO, // Undervoltage lockout is active bit position
STATUS_AXIS_WRONG_CMD, // Last command not valid bit position
STATUS_AXIS_CMD_ERR, // Command error bit position
STATUS_AXIS_NOTPERF_CMD; // Last command not performed bit position
} L64XX_shadow_t;
static L64XX_shadow_t shadow;
#if ENABLED(MONITOR_L6470_DRIVER_STATUS)
static bool monitor_paused;
static void pause_monitor(const bool p) { monitor_paused = p; }
static void monitor_update(L64XX_axis_t stepper_index);
static void monitor_driver();
#else
static void pause_monitor(const bool) {}
#endif
//protected:
// L64XXHelper methods
static void spi_init();
static uint8_t transfer_single(uint8_t data, int16_t ss_pin);
static uint8_t transfer_chain(uint8_t data, int16_t ss_pin, uint8_t chain_position);
private:
static void append_stepper_err(char* &p, const uint8_t stepper_index, const char * const err=nullptr);
};
void echo_yes_no(const bool yes);
extern L64XX_Marlin L64xxManager;

98
Marlin/src/libs/L64XX/README.md

@ -1,98 +0,0 @@
### L64XX Stepper Driver
*Arduino-L6470* library revision 0.8.0 or above is required.
This software can be used with the L6470, L6474, L6480 and the powerSTEP01 (collectively referred to as "L64xx" from now on). Different drivers can be mixed within a system.
These devices use voltage PWMs to drive the stepper phases. On the L6474 the phase current is controlled by the `TVAL` register. On all the other drivers the phase current is indirectly controlled via the `KVAL_HOLD` register which scales the PWM duty cycle.
This software assumes that all drivers are in one SPI daisy chain.
### Hardware Setup
- MOSI from controller tied to SDI on the first device
- SDO of the first device is tied to SDI of the next device
- SDO of the last device is tied to MISO of the controller
- All devices share the same `SCK_PIN` and `SS_PIN` pins. The user must supply a macro to control the `RESET_PIN`(s).
- Each L6470 passes the data it saw on its SDI to its neighbor on the **NEXT** SPI cycle (8 bit delay).
- Each L6470 acts on the **last** SPI data it saw when the `SS_PIN` **goes high**.
The L6474 uses the standard STEP DIR interface. Phase currents are changed in response to step pulses. The direction is set by the DIR pin. Instead of an ENA pin, stepper power is controlled with SPI commands.
The other drivers operate in `STEP_CLOCK` mode. In this mode the Direction / Enable functions are done with SPI commands and the phase currents are changed in response to STEP pulses.
### Hardware / Software Interaction
Except for the L6474, powering up a stepper and setting the direction are done by the same command. You can't do one without the other.
**All** directions are set **every time** a new block is popped off the queue by the stepper ISR.
When setting direction, SPI transfers are minimized by using arrays and a specialized SPI method. *Arduino-L6470* library calls are not used. For N L64xx drivers, this results in N bytes transferred. If library calls were used then N<sup>2</sup> bytes would be sent.
### Power-up (Reset) Sequence
- Stepper objects are instantiated before the `setup()` entry point is reached.
- In `setup()` (before stepper drivers are initialized) the `L6470_init()` method is called to do the following:
- If present, pulse the hardware reset pin.
- Populate the `L6470_chain` array, which maps positions in the SPI stream to commands/data for L64XX stepper drivers.
- Initialize the L64XX Software SPI pin states.
- Initialize L64XX drivers. They may be reset later by a call to `L6470_init_to_defaults()`.
The steppers are **NOT** powered up (enabled) during this sequence.
### `L6470_chain` array
This array is used by all routines that transmit SPI data. For a chain with N devices, the array contains:
Index|Value
-----|-----
0|Number of drivers in chain
1|Axis index of the first device in the chain (closest to MOSI)
...|
N|Axis index of the last device chain (closest to MISO)
### Set Direction and Enable
The `DIR_WRITE` macros for the L64xx drivers are written so that the standard X, Y, Z and extruder logic used by the `set_directions()` routine is not altered. These macros write the correct forward/reverse command to the corresponding location in the array `L6470_dir_commands`. On the L6474 the array the command used just enables the stepper because direction is set by the DIR pin.
At the end of the `set_directions()` routine, the array `L6470_chain` is used to grab the corresponding direction/enable commands out of the array `L6470_dir_commands` and put them in the correct sequence in the array `L6470_buf`. Array `L6470_buf` is then passed to the **`void`** `L6470_Transfer` function which actually sends the data to the devices.
### Utilities, etc.
The **absolute position** registers should accurately reflect Marlin’s stepper position counts. They are set to zero during initialization. `G28` sets them to the Marlin counts for the corresponding axis after homing. NOTE: These registers are often the negative of the Marlin counts. This is because the Marlin counts reflect the logical direction while the registers reflect the stepper direction. The register contents are displayed via the `M114 D` command.
The `L6470_monitor` feature reads the status of each device every half second. It will report if there are any error conditions present or if communications has been lost/restored. The `KVAL_HOLD` value is reduced every 2 – 2.5 seconds if the thermal warning or thermal shutdown conditions are present.
**M122** displays the settings of most of the bits in the status register plus a couple of other items.
**M906** can be used to set the `KVAL_HOLD` register (`TVAL` on L6474) one driver at a time. If a setting is not included with the command then the contents of the registers that affect the phase current/voltage are displayed.
**M916, M917 & M918**
These utilities are used to tune the system. They can get you in the ballpark for acceptable jerk, acceleration, top speed and `KVAL_HOLD` settings (`TVAL` on L6474). In general they seem to provide an overly optimistic `KVAL_HOLD` (`TVAL`) setting because of the lag between setting `KVAL_HOLD` (`TVAL`) and the driver reaching final temperature. Enabling the `L6470_monitor` feature during prints will provide the **final useful setting**.
The amount of power needed to move the stepper without skipping steps increases as jerk, acceleration, top speed, and micro-steps increase. The power dissipated by the driver increases as the power to the stepper increases. The net result is a balancing act between jerk, acceleration, top speed, micro-steps, and power dissipated by the driver.
**M916** - Increases `KVAL_HOLD` (`TVAL`) while moving one axis until a thermal warning is generated. This routine is also useful for determining the approximate `KVAL_HOLD` (`TVAL`) where the stepper stops losing steps. The sound will get noticeably quieter as it stops losing steps.
**M917** - Find minimum current thresholds. This is accomplished by doing the following steps while moving an axis:
1. Decrease OCD current until overcurrent error.
2. Increase OCD until overcurrent error goes away.
3. Decrease stall threshold until stall error (not available on the L6474).
4. Increase stall until stall error goes away (not available on the L6474).
**M918** - Increase speed until error or max feedrate achieved.

2
Marlin/src/libs/buzzer.cpp

@ -48,7 +48,7 @@ void Buzzer::tone(const uint16_t duration, const uint16_t frequency/*=0*/) {
if (!ui.sound_on) return;
while (buffer.isFull()) {
tick();
thermalManager.manage_heater();
thermalManager.task();
}
tone_t tone = { duration, frequency };
buffer.enqueue(tone);

2
Marlin/src/libs/nozzle.cpp

@ -161,7 +161,7 @@ Nozzle nozzle;
void Nozzle::clean(const uint8_t &pattern, const uint8_t &strokes, const_float_t radius, const uint8_t &objects, const uint8_t cleans) {
xyz_pos_t start[HOTENDS] = NOZZLE_CLEAN_START_POINT, end[HOTENDS] = NOZZLE_CLEAN_END_POINT, middle[HOTENDS] = NOZZLE_CLEAN_CIRCLE_MIDDLE;
const uint8_t arrPos = ANY(SINGLENOZZLE, MIXING_EXTRUDER) ? 0 : active_extruder;
const uint8_t arrPos = EITHER(SINGLENOZZLE, MIXING_EXTRUDER) ? 0 : active_extruder;
#if NOZZLE_CLEAN_MIN_TEMP > 20
if (thermalManager.degTargetHotend(arrPos) < NOZZLE_CLEAN_MIN_TEMP) {

4
Marlin/src/module/delta.cpp

@ -60,10 +60,6 @@ xy_float_t delta_tower[ABC];
abc_float_t delta_diagonal_rod_2_tower;
float delta_clip_start_height = Z_MAX_POS;
abc_float_t delta_diagonal_rod_trim;
#if HAS_DELTA_SENSORLESS_PROBING
abc_float_t offset_sensorless_adj{0};
float largest_sensorless_adj = 0;
#endif
float delta_safe_distance_from_top();

4
Marlin/src/module/delta.h

@ -38,10 +38,6 @@ extern xy_float_t delta_tower[ABC];
extern abc_float_t delta_diagonal_rod_2_tower;
extern float delta_clip_start_height;
extern abc_float_t delta_diagonal_rod_trim;
#if HAS_DELTA_SENSORLESS_PROBING
extern abc_float_t offset_sensorless_adj;
extern float largest_sensorless_adj;
#endif
/**
* Recalculate factors used for delta kinematics whenever

34
Marlin/src/module/motion.cpp

@ -966,7 +966,7 @@ FORCE_INLINE void segment_idle(millis_t &next_idle_ms) {
next_idle_ms = ms + 200UL;
return idle();
}
thermalManager.manage_heater(); // Returns immediately on most calls
thermalManager.task(); // Returns immediately on most calls
}
#if IS_KINEMATIC
@ -1041,19 +1041,18 @@ FORCE_INLINE void segment_idle(millis_t &next_idle_ms) {
NOLESS(segments, 1U);
// The approximate length of each segment
const float inv_segments = 1.0f / float(segments),
cartesian_segment_mm = cartesian_mm * inv_segments;
const float inv_segments = 1.0f / float(segments);
const xyze_float_t segment_distance = diff * inv_segments;
#if ENABLED(SCARA_FEEDRATE_SCALING)
const float inv_duration = scaled_fr_mm_s / cartesian_segment_mm;
#endif
// Add hints to help optimize the move
PlannerHints hints(cartesian_mm * inv_segments);
TERN_(SCARA_FEEDRATE_SCALING, hints.inv_duration = scaled_fr_mm_s / hints.millimeters);
/*
SERIAL_ECHOPGM("mm=", cartesian_mm);
SERIAL_ECHOPGM(" seconds=", seconds);
SERIAL_ECHOPGM(" segments=", segments);
SERIAL_ECHOPGM(" segment_mm=", cartesian_segment_mm);
SERIAL_ECHOPGM(" segment_mm=", hints.millimeters);
SERIAL_EOL();
//*/
@ -1065,11 +1064,12 @@ FORCE_INLINE void segment_idle(millis_t &next_idle_ms) {
while (--segments) {
segment_idle(next_idle_ms);
raw += segment_distance;
if (!planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, cartesian_segment_mm OPTARG(SCARA_FEEDRATE_SCALING, inv_duration))) break;
if (!planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, hints))
break;
}
// Ensure last segment arrives at target location.
planner.buffer_line(destination, scaled_fr_mm_s, active_extruder, cartesian_segment_mm OPTARG(SCARA_FEEDRATE_SCALING, inv_duration));
planner.buffer_line(destination, scaled_fr_mm_s, active_extruder, hints);
return false; // caller will update current_position
}
@ -1108,17 +1108,16 @@ FORCE_INLINE void segment_idle(millis_t &next_idle_ms) {
NOLESS(segments, 1U);
// The approximate length of each segment
const float inv_segments = 1.0f / float(segments),
cartesian_segment_mm = cartesian_mm * inv_segments;
const float inv_segments = 1.0f / float(segments);
const xyze_float_t segment_distance = diff * inv_segments;
#if ENABLED(SCARA_FEEDRATE_SCALING)
const float inv_duration = scaled_fr_mm_s / cartesian_segment_mm;
#endif
// Add hints to help optimize the move
PlannerHints hints(cartesian_mm * inv_segments);
TERN_(SCARA_FEEDRATE_SCALING, hints.inv_duration = scaled_fr_mm_s / hints.millimeters);
//SERIAL_ECHOPGM("mm=", cartesian_mm);
//SERIAL_ECHOLNPGM(" segments=", segments);
//SERIAL_ECHOLNPGM(" segment_mm=", cartesian_segment_mm);
//SERIAL_ECHOLNPGM(" segment_mm=", hints.millimeters);
// Get the raw current position as starting point
xyze_pos_t raw = current_position;
@ -1128,12 +1127,13 @@ FORCE_INLINE void segment_idle(millis_t &next_idle_ms) {
while (--segments) {
segment_idle(next_idle_ms);
raw += segment_distance;
if (!planner.buffer_line(raw, fr_mm_s, active_extruder, cartesian_segment_mm OPTARG(SCARA_FEEDRATE_SCALING, inv_duration))) break;
if (!planner.buffer_line(raw, fr_mm_s, active_extruder, hints))
break;
}
// Since segment_distance is only approximate,
// the final move must be to the exact destination.
planner.buffer_line(destination, fr_mm_s, active_extruder, cartesian_segment_mm OPTARG(SCARA_FEEDRATE_SCALING, inv_duration));
planner.buffer_line(destination, fr_mm_s, active_extruder, hints);
}
#endif // SEGMENT_LEVELED_MOVES && !AUTO_BED_LEVELING_UBL

636
Marlin/src/module/planner.cpp

@ -128,8 +128,13 @@ uint8_t Planner::delay_before_delivering; // This counter delays delivery
planner_settings_t Planner::settings; // Initialized by settings.load()
#if ENABLED(LASER_POWER_INLINE)
/**
* Set up inline block variables
* Set laser_power_floor based on SPEED_POWER_MIN to pevent a zero power output state with LASER_POWER_TRAP
*/
#if ENABLED(LASER_FEATURE)
laser_state_t Planner::laser_inline; // Current state for blocks
const uint8_t laser_power_floor = cutter.pct_to_ocr(SPEED_POWER_MIN);
#endif
uint32_t Planner::max_acceleration_steps_per_s2[DISTINCT_AXES]; // (steps/s^2) Derived from mm_per_s2
@ -799,6 +804,7 @@ void Planner::calculate_trapezoid_for_block(block_t * const block, const_float_t
if (plateau_steps < 0) {
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);
decelerate_steps = block->step_event_count - accelerate_steps;
plateau_steps = 0;
#if ENABLED(S_CURVE_ACCELERATION)
@ -822,7 +828,7 @@ void Planner::calculate_trapezoid_for_block(block_t * const block, const_float_t
// Store new block parameters
block->accelerate_until = accelerate_steps;
block->decelerate_after = accelerate_steps + plateau_steps;
block->decelerate_after = block->step_event_count - decelerate_steps;
block->initial_rate = initial_rate;
#if ENABLED(S_CURVE_ACCELERATION)
block->acceleration_time = acceleration_time;
@ -833,46 +839,54 @@ void Planner::calculate_trapezoid_for_block(block_t * const block, const_float_t
#endif
block->final_rate = final_rate;
/**
* Laser trapezoid calculations
*
* Approximate the trapezoid with the laser, incrementing the power every `entry_per` while accelerating
* and decrementing it every `exit_power_per` while decelerating, thus ensuring power is related to feedrate.
*
* LASER_POWER_INLINE_TRAPEZOID_CONT doesn't need this as it continuously approximates
*
* Note this may behave unreliably when running with S_CURVE_ACCELERATION
*/
#if ENABLED(LASER_POWER_INLINE_TRAPEZOID)
if (block->laser.power > 0) { // No need to care if power == 0
const uint8_t entry_power = block->laser.power * entry_factor; // Power on block entry
#if DISABLED(LASER_POWER_INLINE_TRAPEZOID_CONT)
// Speedup power
const uint8_t entry_power_diff = block->laser.power - entry_power;
if (entry_power_diff) {
block->laser.entry_per = accelerate_steps / entry_power_diff;
block->laser.power_entry = entry_power;
}
else {
block->laser.entry_per = 0;
block->laser.power_entry = block->laser.power;
}
// Slowdown power
const uint8_t exit_power = block->laser.power * exit_factor, // Power on block entry
exit_power_diff = block->laser.power - exit_power;
if (exit_power_diff) {
block->laser.exit_per = (block->step_event_count - block->decelerate_after) / exit_power_diff;
block->laser.power_exit = exit_power;
#if ENABLED(LASER_POWER_TRAP)
/**
* Laser Trapezoid Calculations
*
* Approximate the trapezoid with the laser, incrementing the power every `trap_ramp_entry_incr`
* steps while accelerating, and decrementing the power every `trap_ramp_exit_decr` while decelerating,
* to keep power proportional to feedrate. Laser power trap will reduce the initial power to no less
* than the laser_power_floor value. Based on the number of calculated accel/decel steps the power is
* distributed over the trapezoid entry- and exit-ramp steps.
*
* trap_ramp_active_pwr - The active power is initially set at a reduced level factor of initial
* power / accel steps and will be additively incremented using a trap_ramp_entry_incr value for each
* accel step processed later in the stepper code. The trap_ramp_exit_decr value is calculated as
* power / decel steps and is also adjusted to no less than the power floor.
*
* If the power == 0 the inline mode variables need to be set to zero to prevent stepper processing.
* The method allows for simpler non-powered moves like G0 or G28.
*
* Laser Trap Power works for all Jerk and Curve modes; however Arc-based moves will have issues since
* the segments are usually too small.
*/
if (cutter.cutter_mode == CUTTER_MODE_CONTINUOUS) {
if (planner.laser_inline.status.isPowered && planner.laser_inline.status.isEnabled) {
if (block->laser.power > 0) {
NOLESS(block->laser.power, laser_power_floor);
block->laser.trap_ramp_active_pwr = (block->laser.power - laser_power_floor) * (initial_rate / float(block->nominal_rate)) + laser_power_floor;
block->laser.trap_ramp_entry_incr = (block->laser.power - block->laser.trap_ramp_active_pwr) / accelerate_steps;
float laser_pwr = block->laser.power * (final_rate / float(block->nominal_rate));
NOLESS(laser_pwr, laser_power_floor);
block->laser.trap_ramp_exit_decr = (block->laser.power - laser_pwr) / decelerate_steps;
#if ENABLED(DEBUG_LASER_TRAP)
SERIAL_ECHO_MSG("lp:",block->laser.power);
SERIAL_ECHO_MSG("as:",accelerate_steps);
SERIAL_ECHO_MSG("ds:",decelerate_steps);
SERIAL_ECHO_MSG("p.trap:",block->laser.trap_ramp_active_pwr);
SERIAL_ECHO_MSG("p.incr:",block->laser.trap_ramp_entry_incr);
SERIAL_ECHO_MSG("p.decr:",block->laser.trap_ramp_exit_decr);
#endif
}
else {
block->laser.exit_per = 0;
block->laser.power_exit = block->laser.power;
block->laser.trap_ramp_active_pwr = 0;
block->laser.trap_ramp_entry_incr = 0;
block->laser.trap_ramp_exit_decr = 0;
}
#else
block->laser.power_entry = entry_power;
#endif
}
}
#endif
#endif // LASER_POWER_TRAP
}
/* PLANNER SPEED DEFINITION
@ -925,20 +939,30 @@ void Planner::calculate_trapezoid_for_block(block_t * const block, const_float_t
this block can never be less than block_buffer_tail and will always be pushed forward and maintain
this requirement when encountered by the Planner::release_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.
NOTE: Since the planner only computes on what's in the planner buffer, some motions with many short
segments (e.g., complex curves) may seem to move slowly. 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:
- Maximize the machine acceleration. The planner will be able to compute higher velocity profiles
within the same combined distance.
- 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.
- 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.
- Use G2/G3 arcs instead of many short segments. Arcs inform the planner of a safe exit speed at the
end of the last segment, which alleviates this problem.
*/
// 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) {
void Planner::reverse_pass_kernel(block_t * const current, const block_t * const next
OPTARG(HINTS_SAFE_EXIT_SPEED, const_float_t safe_exit_speed_sqr)
) {
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
@ -958,9 +982,10 @@ void Planner::reverse_pass_kernel(block_t * const current, const block_t * const
// 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.
const float new_entry_speed_sqr = current->flag.nominal_length
? max_entry_speed_sqr
: _MIN(max_entry_speed_sqr, max_allowable_speed_sqr(-current->acceleration, next ? next->entry_speed_sqr : sq(float(MINIMUM_PLANNER_SPEED)), current->millimeters));
const float next_entry_speed_sqr = next ? next->entry_speed_sqr : _MAX(TERN0(HINTS_SAFE_EXIT_SPEED, safe_exit_speed_sqr), sq(float(MINIMUM_PLANNER_SPEED))),
new_entry_speed_sqr = current->flag.nominal_length
? max_entry_speed_sqr
: _MIN(max_entry_speed_sqr, max_allowable_speed_sqr(-current->acceleration, next_entry_speed_sqr, current->millimeters));
if (current->entry_speed_sqr != new_entry_speed_sqr) {
// Need to recalculate the block speed - Mark it now, so the stepper
@ -989,7 +1014,7 @@ void Planner::reverse_pass_kernel(block_t * const current, const block_t * const
* recalculate() needs to go over the current plan twice.
* Once in reverse and once forward. This implements the reverse pass.
*/
void Planner::reverse_pass() {
void Planner::reverse_pass(TERN_(HINTS_SAFE_EXIT_SPEED, const_float_t safe_exit_speed_sqr)) {
// Initialize block index to the last block in the planner buffer.
uint8_t block_index = prev_block_index(block_buffer_head);
@ -1013,7 +1038,7 @@ void Planner::reverse_pass() {
// Only process movement blocks
if (current->is_move()) {
reverse_pass_kernel(current, next);
reverse_pass_kernel(current, next OPTARG(HINTS_SAFE_EXIT_SPEED, safe_exit_speed_sqr));
next = current;
}
@ -1126,14 +1151,13 @@ void Planner::forward_pass() {
* according to the entry_factor for each junction. Must be called by
* recalculate() after updating the blocks.
*/
void Planner::recalculate_trapezoids() {
void Planner::recalculate_trapezoids(TERN_(HINTS_SAFE_EXIT_SPEED, const_float_t safe_exit_speed_sqr)) {
// The tail may be changed by the ISR so get a local copy.
uint8_t block_index = block_buffer_tail,
head_block_index = block_buffer_head;
// Since there could be non-move blocks in the head of the queue, and the
// Since 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 to the first move block.
// specially handled), scan backwards to the first non-SYNC block.
while (head_block_index != block_index) {
// Go back (head always point to the first free block)
@ -1200,10 +1224,12 @@ void Planner::recalculate_trapezoids() {
block_index = next_block_index(block_index);
}
// Last/newest block in buffer. Exit speed is set with MINIMUM_PLANNER_SPEED. Always recalculated.
if (next) {
// Last/newest block in buffer. Always recalculated.
if (block) {
// Exit speed is set with MINIMUM_PLANNER_SPEED unless some code higher up knows better.
next_entry_speed = _MAX(TERN0(HINTS_SAFE_EXIT_SPEED, SQRT(safe_exit_speed_sqr)), float(MINIMUM_PLANNER_SPEED));
// Mark the last block as RECALCULATE, to prevent the Stepper ISR running it.
// Mark the next(last) block as RECALCULATE, to prevent the Stepper ISR running it.
// As the last block is always recalculated here, there is a chance the block isn't
// marked as RECALCULATE yet. That's the reason for the following line.
block->flag.recalculate = true;
@ -1214,33 +1240,33 @@ void Planner::recalculate_trapezoids() {
if (!stepper.is_block_busy(block)) {
// Block is not BUSY, we won the race against the Stepper ISR:
const float next_nominal_speed = SQRT(next->nominal_speed_sqr),
nomr = 1.0f / next_nominal_speed;
calculate_trapezoid_for_block(next, next_entry_speed * nomr, float(MINIMUM_PLANNER_SPEED) * nomr);
const float current_nominal_speed = SQRT(block->nominal_speed_sqr),
nomr = 1.0f / current_nominal_speed;
calculate_trapezoid_for_block(block, current_entry_speed * nomr, next_entry_speed * nomr);
#if ENABLED(LIN_ADVANCE)
if (next->use_advance_lead) {
const float comp = next->e_D_ratio * extruder_advance_K[active_extruder] * settings.axis_steps_per_mm[E_AXIS];
next->max_adv_steps = next_nominal_speed * comp;
next->final_adv_steps = (MINIMUM_PLANNER_SPEED) * comp;
if (block->use_advance_lead) {
const float comp = block->e_D_ratio * extruder_advance_K[active_extruder] * settings.axis_steps_per_mm[E_AXIS];
block->max_adv_steps = current_nominal_speed * comp;
block->final_adv_steps = next_entry_speed * comp;
}
#endif
}
// Reset next only to ensure its trapezoid is computed - The stepper is free to use
// Reset block to ensure its trapezoid is computed - The stepper is free to use
// the block from now on.
next->flag.recalculate = false;
block->flag.recalculate = false;
}
}
void Planner::recalculate() {
void Planner::recalculate(TERN_(HINTS_SAFE_EXIT_SPEED, const_float_t safe_exit_speed_sqr)) {
// 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();
reverse_pass(TERN_(HINTS_SAFE_EXIT_SPEED, safe_exit_speed_sqr));
forward_pass();
}
recalculate_trapezoids();
recalculate_trapezoids(TERN_(HINTS_SAFE_EXIT_SPEED, safe_exit_speed_sqr));
}
/**
@ -1295,7 +1321,7 @@ void Planner::recalculate() {
#endif // HAS_FAN
/**
* Maintain fans, paste extruder pressure,
* Maintain fans, paste extruder pressure, spindle/laser power
*/
void Planner::check_axes_activity() {
@ -1359,7 +1385,7 @@ void Planner::check_axes_activity() {
}
else {
TERN_(HAS_CUTTER, cutter.refresh());
TERN_(HAS_CUTTER, if (cutter.cutter_mode == CUTTER_MODE_STANDARD) cutter.refresh());
#if HAS_TAIL_FAN_SPEED
FANS_LOOP(i) {
@ -1459,7 +1485,7 @@ void Planner::check_axes_activity() {
for (uint8_t b = block_buffer_tail; b != block_buffer_head; b = next_block_index(b)) {
const block_t * const block = &block_buffer[b];
if (NUM_AXIS_GANG(block->steps.x, || block->steps.y, || block->steps.z, || block->steps.i, || block->steps.j, || block->steps.k, || block->steps.u, || block->steps.v, || block->steps.w)) {
const float se = (float)block->steps.e / block->step_event_count * SQRT(block->nominal_speed_sqr); // mm/sec
const float se = (float)block->steps.e / block->step_event_count * SQRT(block->nominal_speed_sqr); // mm/sec;
NOLESS(high, se);
}
}
@ -1766,22 +1792,21 @@ float Planner::get_axis_position_mm(const AxisEnum axis) {
void Planner::synchronize() { while (busy()) idle(); }
/**
* Planner::_buffer_steps
* @brief Add a new linear movement to the planner queue (in terms of steps).
*
* Add a new linear movement to the planner queue (in terms of steps).
*
* target - target position in steps units
* target_float - target position in direct (mm, degrees) units. optional
* fr_mm_s - (target) speed of the move
* extruder - target extruder
* millimeters - the length of the movement, if known
* @param target Target position in steps units
* @param target_float Target position in direct (mm, degrees) units.
* @param cart_dist_mm The pre-calculated move lengths for all axes, in mm
* @param fr_mm_s (target) speed of the move
* @param extruder target extruder
* @param hints parameters to aid planner calculations
*
* Returns true if movement was properly queued, false otherwise (if cleaning)
* @return true if movement was properly queued, false otherwise (if cleaning)
*/
bool Planner::_buffer_steps(const xyze_long_t &target
OPTARG(HAS_POSITION_FLOAT, const xyze_pos_t &target_float)
OPTARG(HAS_DIST_MM_ARG, const xyze_float_t &cart_dist_mm)
, feedRate_t fr_mm_s, const uint8_t extruder, const_float_t millimeters/*=0.0*/
, feedRate_t fr_mm_s, const uint8_t extruder, const PlannerHints &hints
) {
// Wait for the next available block
@ -1797,7 +1822,7 @@ bool Planner::_buffer_steps(const xyze_long_t &target
if (!_populate_block(block, target
OPTARG(HAS_POSITION_FLOAT, target_float)
OPTARG(HAS_DIST_MM_ARG, cart_dist_mm)
, fr_mm_s, extruder, millimeters
, fr_mm_s, extruder, hints
)
) {
// Movement was not queued, probably because it was too short.
@ -1819,7 +1844,7 @@ bool Planner::_buffer_steps(const xyze_long_t &target
block_buffer_head = next_buffer_head;
// Recalculate and optimize trapezoidal speed profiles
recalculate();
recalculate(TERN_(HINTS_SAFE_EXIT_SPEED, hints.safe_exit_speed_sqr));
// Movement successfully queued!
return true;
@ -1837,8 +1862,7 @@ bool Planner::_buffer_steps(const xyze_long_t &target
* @param cart_dist_mm The pre-calculated move lengths for all axes, in mm
* @param fr_mm_s (target) speed of the move
* @param extruder target extruder
* @param millimeters A pre-calculated linear distance for the move, in mm,
* or 0.0 to have the distance calculated here.
* @param hints parameters to aid planner calculations
*
* @return true if movement is acceptable, false otherwise
*/
@ -1847,7 +1871,7 @@ bool Planner::_populate_block(
const abce_long_t &target
OPTARG(HAS_POSITION_FLOAT, const xyze_pos_t &target_float)
OPTARG(HAS_DIST_MM_ARG, const xyze_float_t &cart_dist_mm)
, feedRate_t fr_mm_s, const uint8_t extruder, const_float_t millimeters/*=0.0*/
, feedRate_t fr_mm_s, const uint8_t extruder, const PlannerHints &hints
) {
int32_t LOGICAL_AXIS_LIST(
de = target.e - position.e,
@ -1863,8 +1887,36 @@ bool Planner::_populate_block(
);
/* <-- add a slash to enable
#define _ALINE(A) " " STR_##A ":", target[_AXIS(A)], " (", int32_t(target[_AXIS(A)] - position[_AXIS(A)]), " steps)"
SERIAL_ECHOLNPGM(" _populate_block FR:", fr_mm_s, LOGICAL_AXIS_MAP(_ALINE));
SERIAL_ECHOLNPGM(
" _populate_block FR:", fr_mm_s,
" A:", target.a, " (", da, " steps)"
#if HAS_Y_AXIS
" B:", target.b, " (", db, " steps)"
#endif
#if HAS_Z_AXIS
" C:", target.c, " (", dc, " steps)"
#endif
#if HAS_I_AXIS
" " STR_I ":", target.i, " (", di, " steps)"
#endif
#if HAS_J_AXIS
" " STR_J ":", target.j, " (", dj, " steps)"
#endif
#if HAS_K_AXIS
" " STR_K ":", target.k, " (", dk, " steps)"
#endif
#if HAS_U_AXIS
" " STR_U ":", target.u, " (", du, " steps)"
#endif
#if HAS_V_AXIS
" " STR_V ":", target.v, " (", dv, " steps)"
#endif
#if HAS_W_AXIS
" " STR_W ":", target.w, " (", dw, " steps)"
#if HAS_EXTRUDERS
" E:", target.e, " (", de, " steps)"
#endif
);
//*/
#if EITHER(PREVENT_COLD_EXTRUSION, PREVENT_LENGTHY_EXTRUDE)
@ -1962,11 +2014,34 @@ bool Planner::_populate_block(
// Set direction bits
block->direction_bits = dm;
// Update block laser power
#if ENABLED(LASER_POWER_INLINE)
laser_inline.status.isPlanned = true;
block->laser.status = laser_inline.status;
block->laser.power = laser_inline.power;
/**
* Update block laser power
* For standard mode get the cutter.power value for processing, since it's
* only set by apply_power().
*/
#if HAS_CUTTER
switch (cutter.cutter_mode) {
default: break;
case CUTTER_MODE_STANDARD: block->cutter_power = cutter.power; break;
#if ENABLED(LASER_FEATURE)
/**
* For inline mode get the laser_inline variables, including power and status.
* Dynamic mode only needs to update if the feedrate has changed, since it's
* calculated from the current feedrate and power level.
*/
case CUTTER_MODE_CONTINUOUS:
block->laser.power = laser_inline.power;
block->laser.status = laser_inline.status;
break;
case CUTTER_MODE_DYNAMIC:
if (cutter.laser_feedrate_changed()) // Only process changes in rate
block->laser.power = laser_inline.power = cutter.calc_dynamic_power();
break;
#endif
}
#endif
// Number of steps for each axis
@ -2028,9 +2103,9 @@ bool Planner::_populate_block(
#endif
#elif ENABLED(MARKFORGED_XY)
steps_dist_mm.a = (da - db) * mm_per_step[A_AXIS];
steps_dist_mm.b = db * mm_per_step[B_AXIS];
steps_dist_mm.b = db * mm_per_step[B_AXIS];
#elif ENABLED(MARKFORGED_YX)
steps_dist_mm.a = da * mm_per_step[A_AXIS];
steps_dist_mm.a = da * mm_per_step[A_AXIS];
steps_dist_mm.b = (db - da) * mm_per_step[B_AXIS];
#else
XYZ_CODE(
@ -2072,25 +2147,16 @@ bool Planner::_populate_block(
block->millimeters = TERN0(HAS_EXTRUDERS, ABS(steps_dist_mm.e));
}
else {
if (millimeters)
block->millimeters = millimeters;
if (hints.millimeters)
block->millimeters = hints.millimeters;
else {
/**
* Distance for interpretation of feedrate in accordance with LinuxCNC (the successor of
* NIST RS274NGC interpreter - version 3) and its default CANON_XYZ feed reference mode.
*
* Assume:
* - X, Y, Z are the primary linear axes;
* - U, V, W are secondary linear axes;
* - A, B, C are rotational axes.
*
* Then:
* - dX, dY, dZ are the displacements of the primary linear axes;
* - dU, dV, dW are the displacements of linear axes;
* - dA, dB, dC are the displacements of rotational axes.
*
* The time it takes to execute move command with feedrate F is t = D/F,
* where D is the total distance, calculated as follows:
* Distance for interpretation of feedrate in accordance with LinuxCNC (the successor of NIST
* RS274NGC interpreter - version 3) and its default CANON_XYZ feed reference mode.
* Assume that X, Y, Z are the primary linear axes and U, V, W are secondary linear axes and A, B, C are
* rotational axes. Then dX, dY, dZ are the displacements of the primary linear axes and dU, dV, dW are the displacements of linear axes and
* dA, dB, dC are the displacements of rotational axes.
* The time it takes to execute move command with feedrate F is t = D/F, where D is the total distance, calculated as follows:
* D^2 = dX^2 + dY^2 + dZ^2
* if D^2 == 0 (none of XYZ move but any secondary linear axes move, whether other axes are moved or not):
* D^2 = dU^2 + dV^2 + dW^2
@ -2099,9 +2165,8 @@ bool Planner::_populate_block(
*/
float distance_sqr = (
#if ENABLED(ARTICULATED_ROBOT_ARM)
// For articulated robots, interpreting feedrate like LinuxCNC would require inverse kinematics. As a workaround,
// assume that motors sit on a mutually-orthogonal axes and we can think of distance as magnitude of an n-vector
// in an n-dimensional Euclidian space.
// For articulated robots, interpreting feedrate like LinuxCNC would require inverse kinematics. As a workaround, pretend that motors sit on n mutually orthogonal
// axes and assume that we could think of distance as magnitude of an n-vector in an n-dimensional Euclidian space.
NUM_AXIS_GANG(
sq(steps_dist_mm.x), + sq(steps_dist_mm.y), + sq(steps_dist_mm.z),
+ sq(steps_dist_mm.i), + sq(steps_dist_mm.j), + sq(steps_dist_mm.k),
@ -2121,7 +2186,7 @@ bool Planner::_populate_block(
#elif CORE_IS_YZ
XYZ_GANG(sq(steps_dist_mm.x), + sq(steps_dist_mm.head.y), + sq(steps_dist_mm.head.z))
#else
XYZ_GANG(sq(steps_dist_mm.x), + sq(steps_dist_mm.y), + sq(steps_dist_mm.z))
XYZ_GANG(sq(steps_dist_mm.x), + sq(steps_dist_mm.y), + sq(steps_dist_mm.z))
#endif
);
@ -2154,9 +2219,9 @@ bool Planner::_populate_block(
/**
* At this point at least one of the axes has more steps than
* MIN_STEPS_PER_SEGMENT, ensuring the segment won't get dropped
* as zero-length. It's important to not apply corrections to blocks
* that would get dropped!
* MIN_STEPS_PER_SEGMENT, ensuring the segment won't get dropped as
* zero-length. It's important to not apply corrections
* to blocks that would get dropped!
*
* A correction function is permitted to add steps to an axis, it
* should *never* remove steps!
@ -2177,7 +2242,6 @@ bool Planner::_populate_block(
TERN_(MIXING_EXTRUDER, mixer.populate_block(block->b_color));
TERN_(HAS_CUTTER, block->cutter_power = cutter.power);
#if HAS_FAN
FANS_LOOP(i) block->fan_speed[i] = thermalManager.fan_speed[i];
@ -2428,7 +2492,7 @@ bool Planner::_populate_block(
if (speed_factor < 1.0f) {
current_speed *= speed_factor;
block->nominal_rate *= speed_factor;
block->nominal_speed_sqr *= sq(speed_factor);
block->nominal_speed_sqr = block->nominal_speed_sqr * sq(speed_factor);
}
// Compute and limit the acceleration rate for the trapezoid generator.
@ -2505,29 +2569,17 @@ bool Planner::_populate_block(
if (block->step_event_count <= acceleration_long_cutoff) {
LOGICAL_AXIS_CODE(
LIMIT_ACCEL_LONG(E_AXIS, E_INDEX_N(extruder)),
LIMIT_ACCEL_LONG(A_AXIS, 0),
LIMIT_ACCEL_LONG(B_AXIS, 0),
LIMIT_ACCEL_LONG(C_AXIS, 0),
LIMIT_ACCEL_LONG(I_AXIS, 0),
LIMIT_ACCEL_LONG(J_AXIS, 0),
LIMIT_ACCEL_LONG(K_AXIS, 0),
LIMIT_ACCEL_LONG(U_AXIS, 0),
LIMIT_ACCEL_LONG(V_AXIS, 0),
LIMIT_ACCEL_LONG(W_AXIS, 0)
LIMIT_ACCEL_LONG(A_AXIS, 0), LIMIT_ACCEL_LONG(B_AXIS, 0), LIMIT_ACCEL_LONG(C_AXIS, 0),
LIMIT_ACCEL_LONG(I_AXIS, 0), LIMIT_ACCEL_LONG(J_AXIS, 0), LIMIT_ACCEL_LONG(K_AXIS, 0),
LIMIT_ACCEL_LONG(U_AXIS, 0), LIMIT_ACCEL_LONG(V_AXIS, 0), LIMIT_ACCEL_LONG(W_AXIS, 0)
);
}
else {
LOGICAL_AXIS_CODE(
LIMIT_ACCEL_FLOAT(E_AXIS, E_INDEX_N(extruder)),
LIMIT_ACCEL_FLOAT(A_AXIS, 0),
LIMIT_ACCEL_FLOAT(B_AXIS, 0),
LIMIT_ACCEL_FLOAT(C_AXIS, 0),
LIMIT_ACCEL_FLOAT(I_AXIS, 0),
LIMIT_ACCEL_FLOAT(J_AXIS, 0),
LIMIT_ACCEL_FLOAT(K_AXIS, 0),
LIMIT_ACCEL_FLOAT(U_AXIS, 0),
LIMIT_ACCEL_FLOAT(V_AXIS, 0),
LIMIT_ACCEL_FLOAT(W_AXIS, 0)
LIMIT_ACCEL_FLOAT(A_AXIS, 0), LIMIT_ACCEL_FLOAT(B_AXIS, 0), LIMIT_ACCEL_FLOAT(C_AXIS, 0),
LIMIT_ACCEL_FLOAT(I_AXIS, 0), LIMIT_ACCEL_FLOAT(J_AXIS, 0), LIMIT_ACCEL_FLOAT(K_AXIS, 0),
LIMIT_ACCEL_FLOAT(U_AXIS, 0), LIMIT_ACCEL_FLOAT(V_AXIS, 0), LIMIT_ACCEL_FLOAT(W_AXIS, 0)
);
}
}
@ -2592,7 +2644,10 @@ bool Planner::_populate_block(
#if HAS_DIST_MM_ARG
cart_dist_mm
#else
LOGICAL_AXIS_ARRAY(steps_dist_mm.e, steps_dist_mm.x, steps_dist_mm.y, steps_dist_mm.z, steps_dist_mm.i, steps_dist_mm.j, steps_dist_mm.k, steps_dist_mm.u, steps_dist_mm.v, steps_dist_mm.w)
LOGICAL_AXIS_ARRAY(steps_dist_mm.e,
steps_dist_mm.x, steps_dist_mm.y, steps_dist_mm.z,
steps_dist_mm.i, steps_dist_mm.j, steps_dist_mm.k,
steps_dist_mm.u, steps_dist_mm.v, steps_dist_mm.w)
#endif
;
@ -2613,7 +2668,7 @@ bool Planner::_populate_block(
// NOTE: Max junction velocity is computed without sin() or acos() by trig half angle identity.
float junction_cos_theta = LOGICAL_AXIS_GANG(
+ (-prev_unit_vec.e * unit_vec.e),
(-prev_unit_vec.x * unit_vec.x),
+ (-prev_unit_vec.x * unit_vec.x),
+ (-prev_unit_vec.y * unit_vec.y),
+ (-prev_unit_vec.z * unit_vec.z),
+ (-prev_unit_vec.i * unit_vec.i),
@ -2636,99 +2691,104 @@ bool Planner::_populate_block(
const float junction_acceleration = limit_value_by_axis_maximum(block->acceleration, junction_unit_vec);
NOLESS(junction_cos_theta, -0.999999f); // Check for numerical round-off to avoid divide by zero.
const float sin_theta_d2 = SQRT(0.5f * (1.0f - junction_cos_theta)); // Trig half angle identity. Always positive.
vmax_junction_sqr = junction_acceleration * junction_deviation_mm * sin_theta_d2 / (1.0f - sin_theta_d2);
#if ENABLED(JD_HANDLE_SMALL_SEGMENTS)
// For small moves with >135° junction (octagon) find speed for approximate arc
if (block->millimeters < 1 && junction_cos_theta < -0.7071067812f) {
#if ENABLED(JD_USE_MATH_ACOS)
#error "TODO: Inline maths with the MCU / FPU."
#elif ENABLED(JD_USE_LOOKUP_TABLE)
// Fast acos approximation (max. error +-0.01 rads)
// Based on LUT table and linear interpolation
/**
* // Generate the JD Lookup Table
* constexpr float c = 1.00751495f; // Correction factor to center error around 0
* for (int i = 0; i < jd_lut_count - 1; ++i) {
* const float x0 = (sq(i) - 1) / sq(i),
* y0 = acos(x0) * (i == 0 ? 1 : c),
* x1 = i < jd_lut_count - 1 ? 0.5 * x0 + 0.5 : 0.999999f,
* y1 = acos(x1) * (i < jd_lut_count - 1 ? c : 1);
* jd_lut_k[i] = (y0 - y1) / (x0 - x1);
* jd_lut_b[i] = (y1 * x0 - y0 * x1) / (x0 - x1);
* }
*
* // Compute correction factor (Set c to 1.0f first!)
* float min = INFINITY, max = -min;
* for (float t = 0; t <= 1; t += 0.0003f) {
* const float e = acos(t) / approx(t);
* if (isfinite(e)) {
* if (e < min) min = e;
* if (e > max) max = e;
* }
* }
* fprintf(stderr, "%.9gf, ", (min + max) / 2);
*/
static constexpr int16_t jd_lut_count = 16;
static constexpr uint16_t jd_lut_tll = _BV(jd_lut_count - 1);
static constexpr int16_t jd_lut_tll0 = __builtin_clz(jd_lut_tll) + 1; // i.e., 16 - jd_lut_count + 1
static constexpr float jd_lut_k[jd_lut_count] PROGMEM = {
-1.03145837f, -1.30760646f, -1.75205851f, -2.41705704f,
-3.37769222f, -4.74888992f, -6.69649887f, -9.45661736f,
-13.3640480f, -18.8928222f, -26.7136841f, -37.7754593f,
-53.4201813f, -75.5458374f, -106.836761f, -218.532821f };
static constexpr float jd_lut_b[jd_lut_count] PROGMEM = {
1.57079637f, 1.70887053f, 2.04220939f, 2.62408352f,
3.52467871f, 4.85302639f, 6.77020454f, 9.50875854f,
13.4009285f, 18.9188995f, 26.7321243f, 37.7885055f,
53.4293975f, 75.5523529f, 106.841369f, 218.534011f };
const float neg = junction_cos_theta < 0 ? -1 : 1,
t = neg * junction_cos_theta;
const int16_t idx = (t < 0.00000003f) ? 0 : __builtin_clz(uint16_t((1.0f - t) * jd_lut_tll)) - jd_lut_tll0;
float junction_theta = t * pgm_read_float(&jd_lut_k[idx]) + pgm_read_float(&jd_lut_b[idx]);
if (neg > 0) junction_theta = RADIANS(180) - junction_theta; // acos(-t)
#else
// Fast acos(-t) approximation (max. error +-0.033rad = 1.89°)
// Based on MinMax polynomial published by W. Randolph Franklin, see
// https://wrf.ecse.rpi.edu/Research/Short_Notes/arcsin/onlyelem.html
// acos( t) = pi / 2 - asin(x)
// acos(-t) = pi - acos(t) ... pi / 2 + asin(x)
const float neg = junction_cos_theta < 0 ? -1 : 1,
t = neg * junction_cos_theta,
asinx = 0.032843707f
+ t * (-1.451838349f
+ t * ( 29.66153956f
+ t * (-131.1123477f
+ t * ( 262.8130562f
+ t * (-242.7199627f
+ t * ( 84.31466202f ) ))))),
junction_theta = RADIANS(90) + neg * asinx; // acos(-t)
// NOTE: junction_theta bottoms out at 0.033 which avoids divide by 0.
#endif
const float limit_sqr = (block->millimeters * junction_acceleration) / junction_theta;
NOMORE(vmax_junction_sqr, limit_sqr);
}
#endif // JD_HANDLE_SMALL_SEGMENTS
if (TERN0(HINTS_CURVE_RADIUS, hints.curve_radius)) {
TERN_(HINTS_CURVE_RADIUS, vmax_junction_sqr = junction_acceleration * hints.curve_radius);
}
else {
NOLESS(junction_cos_theta, -0.999999f); // Check for numerical round-off to avoid divide by zero.
const float sin_theta_d2 = SQRT(0.5f * (1.0f - junction_cos_theta)); // Trig half angle identity. Always positive.
vmax_junction_sqr = junction_acceleration * junction_deviation_mm * sin_theta_d2 / (1.0f - sin_theta_d2);
#if ENABLED(JD_HANDLE_SMALL_SEGMENTS)
// For small moves with >135° junction (octagon) find speed for approximate arc
if (block->millimeters < 1 && junction_cos_theta < -0.7071067812f) {
#if ENABLED(JD_USE_MATH_ACOS)
#error "TODO: Inline maths with the MCU / FPU."
#elif ENABLED(JD_USE_LOOKUP_TABLE)
// Fast acos approximation (max. error +-0.01 rads)
// Based on LUT table and linear interpolation
/**
* // Generate the JD Lookup Table
* constexpr float c = 1.00751495f; // Correction factor to center error around 0
* for (int i = 0; i < jd_lut_count - 1; ++i) {
* const float x0 = (sq(i) - 1) / sq(i),
* y0 = acos(x0) * (i == 0 ? 1 : c),
* x1 = i < jd_lut_count - 1 ? 0.5 * x0 + 0.5 : 0.999999f,
* y1 = acos(x1) * (i < jd_lut_count - 1 ? c : 1);
* jd_lut_k[i] = (y0 - y1) / (x0 - x1);
* jd_lut_b[i] = (y1 * x0 - y0 * x1) / (x0 - x1);
* }
*
* // Compute correction factor (Set c to 1.0f first!)
* float min = INFINITY, max = -min;
* for (float t = 0; t <= 1; t += 0.0003f) {
* const float e = acos(t) / approx(t);
* if (isfinite(e)) {
* if (e < min) min = e;
* if (e > max) max = e;
* }
* }
* fprintf(stderr, "%.9gf, ", (min + max) / 2);
*/
static constexpr int16_t jd_lut_count = 16;
static constexpr uint16_t jd_lut_tll = _BV(jd_lut_count - 1);
static constexpr int16_t jd_lut_tll0 = __builtin_clz(jd_lut_tll) + 1; // i.e., 16 - jd_lut_count + 1
static constexpr float jd_lut_k[jd_lut_count] PROGMEM = {
-1.03145837f, -1.30760646f, -1.75205851f, -2.41705704f,
-3.37769222f, -4.74888992f, -6.69649887f, -9.45661736f,
-13.3640480f, -18.8928222f, -26.7136841f, -37.7754593f,
-53.4201813f, -75.5458374f, -106.836761f, -218.532821f };
static constexpr float jd_lut_b[jd_lut_count] PROGMEM = {
1.57079637f, 1.70887053f, 2.04220939f, 2.62408352f,
3.52467871f, 4.85302639f, 6.77020454f, 9.50875854f,
13.4009285f, 18.9188995f, 26.7321243f, 37.7885055f,
53.4293975f, 75.5523529f, 106.841369f, 218.534011f };
const float neg = junction_cos_theta < 0 ? -1 : 1,
t = neg * junction_cos_theta;
const int16_t idx = (t < 0.00000003f) ? 0 : __builtin_clz(uint16_t((1.0f - t) * jd_lut_tll)) - jd_lut_tll0;
float junction_theta = t * pgm_read_float(&jd_lut_k[idx]) + pgm_read_float(&jd_lut_b[idx]);
if (neg > 0) junction_theta = RADIANS(180) - junction_theta; // acos(-t)
#else
// Fast acos(-t) approximation (max. error +-0.033rad = 1.89°)
// Based on MinMax polynomial published by W. Randolph Franklin, see
// https://wrf.ecse.rpi.edu/Research/Short_Notes/arcsin/onlyelem.html
// acos( t) = pi / 2 - asin(x)
// acos(-t) = pi - acos(t) ... pi / 2 + asin(x)
const float neg = junction_cos_theta < 0 ? -1 : 1,
t = neg * junction_cos_theta,
asinx = 0.032843707f
+ t * (-1.451838349f
+ t * ( 29.66153956f
+ t * (-131.1123477f
+ t * ( 262.8130562f
+ t * (-242.7199627f
+ t * ( 84.31466202f ) ))))),
junction_theta = RADIANS(90) + neg * asinx; // acos(-t)
// NOTE: junction_theta bottoms out at 0.033 which avoids divide by 0.
#endif
const float limit_sqr = (block->millimeters * junction_acceleration) / junction_theta;
NOMORE(vmax_junction_sqr, limit_sqr);
}
#endif // JD_HANDLE_SMALL_SEGMENTS
}
}
// Get the lowest speed
@ -2888,14 +2948,12 @@ bool Planner::_populate_block(
} // _populate_block()
/**
* Planner::buffer_sync_block
* Add a block to the buffer that just updates the position,
* or in case of LASER_SYNCHRONOUS_M106_M107 the fan PWM
* @brief Add a block to the buffer that just updates the position
* Supports LASER_SYNCHRONOUS_M106_M107 and LASER_POWER_SYNC power sync block buffer queueing.
*
* @param sync_flag The sync flag to set, determining the type of sync the block will do
*/
void Planner::buffer_sync_block(TERN_(LASER_SYNCHRONOUS_M106_M107, const BlockFlagBit sync_flag/*=BLOCK_BIT_SYNC_POSITION*/)) {
#if DISABLED(LASER_SYNCHRONOUS_M106_M107)
constexpr BlockFlagBit sync_flag = BLOCK_BIT_SYNC_POSITION;
#endif
void Planner::buffer_sync_block(const BlockFlagBit sync_flag/*=BLOCK_BIT_SYNC_POSITION*/) {
// Wait for the next available block
uint8_t next_buffer_head;
@ -2903,18 +2961,22 @@ void Planner::buffer_sync_block(TERN_(LASER_SYNCHRONOUS_M106_M107, const BlockFl
// Clear block
block->reset();
block->flag.apply(sync_flag);
block->position = position;
#if ENABLED(BACKLASH_COMPENSATION)
LOOP_NUM_AXES(axis) block->position[axis] += backlash.get_applied_steps((AxisEnum)axis);
#endif
#if BOTH(HAS_FAN, LASER_SYNCHRONOUS_M106_M107)
FANS_LOOP(i) block->fan_speed[i] = thermalManager.fan_speed[i];
#endif
/**
* M3-based power setting can be processed inline with a laser power sync block.
* During active moves cutter.power is processed immediately, otherwise on the next move.
*/
TERN_(LASER_POWER_SYNC, block->laser.power = cutter.power);
// 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
@ -2931,22 +2993,24 @@ void Planner::buffer_sync_block(TERN_(LASER_SYNCHRONOUS_M106_M107, const BlockFl
} // buffer_sync_block()
/**
* Planner::buffer_segment
*
* Add a new linear movement to the buffer in axis units.
* @brief Add a single linear movement
*
* Leveling and kinematics should be applied ahead of calling this.
* @description Add a new linear movement to the buffer in axis units.
* Leveling and kinematics should be applied before calling this.
*
* a,b,c,e - target positions in mm and/or degrees
* fr_mm_s - (target) speed of the move
* extruder - target extruder
* millimeters - the length of the movement, if known
* @param abce Target position in mm and/or degrees
* @param cart_dist_mm The pre-calculated move lengths for all axes, in mm
* @param fr_mm_s (target) speed of the move
* @param extruder optional target extruder (otherwise active_extruder)
* @param hints optional parameters to aid planner calculations
*
* Return 'false' if no segment was queued due to cleaning, cold extrusion, full queue, etc.
* @return false if no segment was queued due to cleaning, cold extrusion, full queue, etc.
*/
bool Planner::buffer_segment(const abce_pos_t &abce
OPTARG(HAS_DIST_MM_ARG, const xyze_float_t &cart_dist_mm)
, const_feedRate_t fr_mm_s, const uint8_t extruder/*=active_extruder*/, const_float_t millimeters/*=0.0*/
, const_feedRate_t fr_mm_s
, const uint8_t extruder/*=active_extruder*/
, const PlannerHints &hints/*=PlannerHints()*/
) {
// If we are cleaning, do not accept queuing of movements
@ -3052,7 +3116,7 @@ bool Planner::buffer_segment(const abce_pos_t &abce
if (!_buffer_steps(target
OPTARG(HAS_POSITION_FLOAT, target_float)
OPTARG(HAS_DIST_MM_ARG, cart_dist_mm)
, fr_mm_s, extruder, millimeters
, fr_mm_s, extruder, hints
)) return false;
stepper.wake_up();
@ -3066,12 +3130,12 @@ bool Planner::buffer_segment(const abce_pos_t &abce
*
* cart - target position in mm or degrees
* fr_mm_s - (target) speed of the move (mm/s)
* extruder - target extruder
* millimeters - the length of the movement, if known
* inv_duration - the reciprocal if the duration of the movement, if known (kinematic only if feeedrate scaling is enabled)
* extruder - optional target extruder (otherwise active_extruder)
* hints - optional parameters to aid planner calculations
*/
bool Planner::buffer_line(const xyze_pos_t &cart, const_feedRate_t fr_mm_s, const uint8_t extruder/*=active_extruder*/, const float millimeters/*=0.0*/
OPTARG(SCARA_FEEDRATE_SCALING, const_float_t inv_duration/*=0.0*/)
bool Planner::buffer_line(const xyze_pos_t &cart, const_feedRate_t fr_mm_s
, const uint8_t extruder/*=active_extruder*/
, const PlannerHints &hints/*=PlannerHints()*/
) {
xyze_pos_t machine = cart;
TERN_(HAS_POSITION_MODIFIERS, apply_modifiers(machine));
@ -3093,41 +3157,35 @@ bool Planner::buffer_line(const xyze_pos_t &cart, const_feedRate_t fr_mm_s, cons
);
#endif
const float mm = millimeters ?: (cart_dist_mm.x || cart_dist_mm.y) ? cart_dist_mm.magnitude() : TERN0(HAS_Z_AXIS, ABS(cart_dist_mm.z));
// Cartesian XYZ to kinematic ABC, stored in global 'delta'
inverse_kinematics(machine);
PlannerHints ph = hints;
if (!hints.millimeters)
ph.millimeters = (cart_dist_mm.x || cart_dist_mm.y) ? cart_dist_mm.magnitude() : TERN0(HAS_Z_AXIS, ABS(cart_dist_mm.z));
#if ENABLED(SCARA_FEEDRATE_SCALING)
// For SCARA scale the feed rate from mm/s to degrees/s
// For SCARA scale the feedrate from mm/s to degrees/s
// i.e., Complete the angular vector in the given time.
const float duration_recip = inv_duration ?: fr_mm_s / mm;
const float duration_recip = hints.inv_duration ?: fr_mm_s / ph.millimeters;
const xyz_pos_t diff = delta - position_float;
const feedRate_t feedrate = diff.magnitude() * duration_recip;
#else
const feedRate_t feedrate = fr_mm_s;
#endif
TERN_(HAS_EXTRUDERS, delta.e = machine.e);
if (buffer_segment(delta OPTARG(HAS_DIST_MM_ARG, cart_dist_mm), feedrate, extruder, mm)) {
if (buffer_segment(delta OPTARG(HAS_DIST_MM_ARG, cart_dist_mm), feedrate, extruder, ph)) {
position_cart = cart;
return true;
}
return false;
#else
return buffer_segment(machine, fr_mm_s, extruder, millimeters);
return buffer_segment(machine, fr_mm_s, extruder, hints);
#endif
} // buffer_line()
#if ENABLED(DIRECT_STEPPING)
/**
* @brief Add a direct stepping page block to the buffer
* and wake up the Stepper ISR to process it.
*
* @param page_idx Page index provided by G6 I<index>
* @param extruder The extruder to use in the move
* @param num_steps Number of steps to process in the ISR
*/
void Planner::buffer_page(const page_idx_t page_idx, const uint8_t extruder, const uint16_t num_steps) {
if (!last_page_step_rate) {
kill(GET_TEXT_F(MSG_BAD_PAGE_SPEED));
@ -3212,7 +3270,7 @@ void Planner::set_machine_position_mm(const abce_pos_t &abce) {
if (has_blocks_queued()) {
//previous_nominal_speed_sqr = 0.0; // Reset planner junction speeds. Assume start from rest.
//previous_speed.reset();
buffer_sync_block();
buffer_sync_block(BLOCK_BIT_SYNC_POSITION);
}
else {
#if ENABLED(BACKLASH_COMPENSATION)
@ -3225,12 +3283,6 @@ void Planner::set_machine_position_mm(const abce_pos_t &abce) {
}
}
/**
* @brief Set the Planner position in mm
* @details Set the Planner position from a native machine position in mm
*
* @param xyze A native (Cartesian) machine position
*/
void Planner::set_position_mm(const xyze_pos_t &xyze) {
xyze_pos_t machine = xyze;
TERN_(HAS_POSITION_MODIFIERS, apply_modifiers(machine, true));
@ -3259,20 +3311,14 @@ void Planner::set_position_mm(const xyze_pos_t &xyze) {
TERN_(IS_KINEMATIC, TERN_(HAS_EXTRUDERS, position_cart.e = e));
if (has_blocks_queued())
buffer_sync_block();
buffer_sync_block(BLOCK_BIT_SYNC_POSITION);
else
stepper.set_axis_position(E_AXIS, position.e);
}
#endif
/**
* @brief Recalculate the steps/s^2 acceleration rates, based on the mm/s^2
* @details Update planner movement factors after a change to certain settings:
* - max_acceleration_steps_per_s2 from settings max_acceleration_mm_per_s2 * axis_steps_per_mm (M201, M92)
* - acceleration_long_cutoff based on the largest max_acceleration_steps_per_s2 (M201)
* - max_e_jerk for all extruders based on junction_deviation_mm (M205 J)
*/
// Recalculate the steps/s^2 acceleration rates, based on the mm/s^2
void Planner::refresh_acceleration_rates() {
uint32_t highest_rate = 1;
LOOP_DISTINCT_AXES(i) {
@ -3285,8 +3331,8 @@ void Planner::refresh_acceleration_rates() {
}
/**
* @brief Recalculate 'position' and 'mm_per_step'.
* @details Required whenever settings.axis_steps_per_mm changes!
* Recalculate 'position' and 'mm_per_step'.
* Must be called whenever settings.axis_steps_per_mm changes!
*/
void Planner::refresh_positioning() {
LOOP_DISTINCT_AXES(i) mm_per_step[i] = 1.0f / settings.axis_steps_per_mm[i];

151
Marlin/src/module/planner.h

@ -89,30 +89,6 @@
#define HAS_DIST_MM_ARG 1
#endif
#if ENABLED(LASER_POWER_INLINE)
typedef struct {
bool isPlanned:1;
bool isEnabled:1;
bool dir:1;
bool Reserved:6;
} power_status_t;
typedef struct {
power_status_t status; // See planner settings for meaning
uint8_t power; // Ditto; When in trapezoid mode this is nominal power
#if ENABLED(LASER_POWER_INLINE_TRAPEZOID)
uint8_t power_entry; // Entry power for the laser
#if DISABLED(LASER_POWER_INLINE_TRAPEZOID_CONT)
uint8_t power_exit; // Exit power for the laser
uint32_t entry_per, // Steps per power increment (to avoid floats in stepper calcs)
exit_per; // Steps per power decrement
#endif
#endif
} block_laser_t;
#endif
/**
* Planner block flags as boolean bit fields
*/
@ -132,14 +108,14 @@ enum BlockFlagBit {
BLOCK_BIT_SYNC_POSITION
// Direct stepping page
#if ENABLED(DIRECT_STEPPING)
, BLOCK_BIT_PAGE
#endif
OPTARG(DIRECT_STEPPING, BLOCK_BIT_PAGE)
// Sync the fan speeds from the block
#if ENABLED(LASER_SYNCHRONOUS_M106_M107)
, BLOCK_BIT_SYNC_FANS
#endif
OPTARG(LASER_SYNCHRONOUS_M106_M107, BLOCK_BIT_SYNC_FANS)
// Sync laser power from a queued block
OPTARG(LASER_POWER_SYNC, BLOCK_BIT_LASER_PWR)
};
/**
@ -165,6 +141,10 @@ typedef struct {
#if ENABLED(LASER_SYNCHRONOUS_M106_M107)
bool sync_fans:1;
#endif
#if ENABLED(LASER_POWER_SYNC)
bool sync_laser_pwr:1;
#endif
};
};
@ -176,19 +156,45 @@ typedef struct {
} block_flags_t;
#if ENABLED(LASER_FEATURE)
typedef struct {
bool isEnabled:1; // Set to engage the inline laser power output.
bool dir:1;
bool isPowered:1; // Set on any parsed G1, G2, G3, or G5 powered move, cleared on G0 and G28.
bool isSyncPower:1; // Set on a M3 sync based set laser power, used to determine active trap power
bool Reserved:4;
} power_status_t;
typedef struct {
power_status_t status; // See planner settings for meaning
uint8_t power; // Ditto; When in trapezoid mode this is nominal power
#if ENABLED(LASER_POWER_TRAP)
float trap_ramp_active_pwr; // Laser power level during active trapezoid smoothing
float trap_ramp_entry_incr; // Acceleration per step laser power increment (trap entry)
float trap_ramp_exit_decr; // Deceleration per step laser power decrement (trap exit)
#endif
} block_laser_t;
#endif
/**
* A single entry in the planner buffer, used to set up and
* track a coordinated linear motion for one or more axes.
* struct block_t
*
* A single entry in the planner buffer.
* Tracks linear movement over multiple axes.
*
* The "nominal" values are as-specified by G-code, and
* may never actually be reached due to acceleration limits.
*/
typedef struct block_t {
typedef struct PlannerBlock {
volatile block_flags_t flag; // Block flags
volatile bool is_fan_sync() { return TERN0(LASER_SYNCHRONOUS_M106_M107, flag.sync_fans); }
volatile bool is_sync() { return flag.sync_position || is_fan_sync(); }
volatile bool is_pwr_sync() { return TERN0(LASER_POWER_SYNC, flag.sync_laser_pwr); }
volatile bool is_sync() { return flag.sync_position || is_fan_sync() || is_pwr_sync(); }
volatile bool is_page() { return TERN0(DIRECT_STEPPING, flag.page); }
volatile bool is_move() { return !(is_sync() || is_page()); }
@ -270,7 +276,7 @@ typedef struct block_t {
xyze_pos_t start_position;
#endif
#if ENABLED(LASER_POWER_INLINE)
#if ENABLED(LASER_FEATURE)
block_laser_t laser;
#endif
@ -284,7 +290,7 @@ typedef struct block_t {
#define BLOCK_MOD(n) ((n)&(BLOCK_BUFFER_SIZE-1))
#if ENABLED(LASER_POWER_INLINE)
#if ENABLED(LASER_FEATURE)
typedef struct {
/**
* Laser status flags
@ -293,11 +299,10 @@ typedef struct block_t {
/**
* Laser power: 0 or 255 in case of PWM-less laser,
* or the OCR (oscillator count register) value;
*
* Using OCR instead of raw power, because it avoids
* floating point operations during the move loop.
*/
uint8_t power;
volatile uint8_t power;
} laser_state_t;
#endif
@ -346,6 +351,30 @@ typedef struct {
typedef IF<(BLOCK_BUFFER_SIZE > 64), uint16_t, uint8_t>::type last_move_t;
#endif
#if ENABLED(ARC_SUPPORT)
#define HINTS_CURVE_RADIUS
#define HINTS_SAFE_EXIT_SPEED
#endif
struct PlannerHints {
float millimeters = 0.0; // Move Length, if known, else 0.
#if ENABLED(SCARA_FEEDRATE_SCALING)
float inv_duration = 0.0; // Reciprocal of the move duration, if known
#endif
#if ENABLED(HINTS_CURVE_RADIUS)
float curve_radius = 0.0; // Radius of curvature of the motion path - to calculate cornering speed
#else
static constexpr float curve_radius = 0.0;
#endif
#if ENABLED(HINTS_SAFE_EXIT_SPEED)
float safe_exit_speed_sqr = 0.0; // Square of the speed considered "safe" at the end of the segment
// i.e., at or below the exit speed of the segment that the planner
// would calculate if it knew the as-yet-unbuffered path
#endif
PlannerHints(const_float_t mm=0.0f) : millimeters(mm) {}
};
class Planner {
public:
@ -399,7 +428,7 @@ class Planner {
static planner_settings_t settings;
#if ENABLED(LASER_POWER_INLINE)
#if ENABLED(LASER_FEATURE)
static laser_state_t laser_inline;
#endif
@ -749,14 +778,14 @@ class Planner {
* 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
* hints - parameters to aid planner calculations
*
* Returns true if movement was buffered, false otherwise
*/
static bool _buffer_steps(const xyze_long_t &target
OPTARG(HAS_POSITION_FLOAT, const xyze_pos_t &target_float)
OPTARG(HAS_DIST_MM_ARG, const xyze_float_t &cart_dist_mm)
, feedRate_t fr_mm_s, const uint8_t extruder, const_float_t millimeters=0.0
, feedRate_t fr_mm_s, const uint8_t extruder, const PlannerHints &hints
);
/**
@ -771,25 +800,23 @@ class Planner {
* @param cart_dist_mm The pre-calculated move lengths for all axes, in mm
* @param fr_mm_s (target) speed of the move
* @param extruder target extruder
* @param millimeters A pre-calculated linear distance for the move, in mm,
* or 0.0 to have the distance calculated here.
* @param hints parameters to aid planner calculations
*
* @return true if movement is acceptable, false otherwise
*/
static bool _populate_block(block_t * const block, const xyze_long_t &target
OPTARG(HAS_POSITION_FLOAT, const xyze_pos_t &target_float)
OPTARG(HAS_DIST_MM_ARG, const xyze_float_t &cart_dist_mm)
, feedRate_t fr_mm_s, const uint8_t extruder, const_float_t millimeters=0.0
, feedRate_t fr_mm_s, const uint8_t extruder, const PlannerHints &hints
);
/**
* Planner::buffer_sync_block
* Add a block to the buffer that just updates the position or in
* case of LASER_SYNCHRONOUS_M106_M107 the fan pwm
* Add a block to the buffer that just updates the position
* @param sync_flag sets a condition bit to process additional items
* such as sync fan pwm or sync M3/M4 laser power into a queued block
*/
static void buffer_sync_block(
TERN_(LASER_SYNCHRONOUS_M106_M107, const BlockFlagBit flag=BLOCK_BIT_SYNC_POSITION)
);
static void buffer_sync_block(const BlockFlagBit flag=BLOCK_BIT_SYNC_POSITION);
#if IS_KINEMATIC
private:
@ -807,12 +834,14 @@ class Planner {
*
* a,b,c,e - target positions in mm and/or degrees
* fr_mm_s - (target) speed of the move
* extruder - target extruder
* millimeters - the length of the movement, if known
* extruder - optional target extruder (otherwise active_extruder)
* hints - optional parameters to aid planner calculations
*/
static bool buffer_segment(const abce_pos_t &abce
OPTARG(HAS_DIST_MM_ARG, const xyze_float_t &cart_dist_mm)
, const_feedRate_t fr_mm_s, const uint8_t extruder=active_extruder, const_float_t millimeters=0.0
, const_feedRate_t fr_mm_s
, const uint8_t extruder=active_extruder
, const PlannerHints &hints=PlannerHints()
);
public:
@ -824,12 +853,12 @@ class Planner {
*
* cart - target position in mm or degrees
* fr_mm_s - (target) speed of the move (mm/s)
* extruder - target extruder
* millimeters - the length of the movement, if known
* inv_duration - the reciprocal if the duration of the movement, if known (kinematic only if feeedrate scaling is enabled)
* extruder - optional target extruder (otherwise active_extruder)
* hints - optional parameters to aid planner calculations
*/
static bool buffer_line(const xyze_pos_t &cart, const_feedRate_t fr_mm_s, const uint8_t extruder=active_extruder, const float millimeters=0.0
OPTARG(SCARA_FEEDRATE_SCALING, const_float_t inv_duration=0.0)
static bool buffer_line(const xyze_pos_t &cart, const_feedRate_t fr_mm_s
, const uint8_t extruder=active_extruder
, const PlannerHints &hints=PlannerHints()
);
#if ENABLED(DIRECT_STEPPING)
@ -1022,15 +1051,15 @@ class Planner {
static void calculate_trapezoid_for_block(block_t * const block, const_float_t entry_factor, const_float_t exit_factor);
static void reverse_pass_kernel(block_t * const current, const block_t * const next);
static void reverse_pass_kernel(block_t * const current, const block_t * const next OPTARG(ARC_SUPPORT, const_float_t safe_exit_speed_sqr));
static void forward_pass_kernel(const block_t * const previous, block_t * const current, uint8_t block_index);
static void reverse_pass();
static void reverse_pass(TERN_(ARC_SUPPORT, const_float_t safe_exit_speed_sqr));
static void forward_pass();
static void recalculate_trapezoids();
static void recalculate_trapezoids(TERN_(ARC_SUPPORT, const_float_t safe_exit_speed_sqr));
static void recalculate();
static void recalculate(TERN_(ARC_SUPPORT, const_float_t safe_exit_speed_sqr));
#if HAS_JUNCTION_DEVIATION

9
Marlin/src/module/planner_bezier.cpp

@ -121,9 +121,12 @@ void cubic_b_spline(
millis_t next_idle_ms = millis() + 200UL;
// Hints to help optimize the move
PlannerHints hints;
for (float t = 0; t < 1;) {
thermalManager.manage_heater();
thermalManager.task();
millis_t now = millis();
if (ELAPSED(now, next_idle_ms)) {
next_idle_ms = now + 200UL;
@ -177,7 +180,7 @@ void cubic_b_spline(
}
*/
step = new_t - t;
hints.millimeters = new_t - t;
t = new_t;
// Compute and send new position
@ -203,7 +206,7 @@ void cubic_b_spline(
const xyze_pos_t &pos = bez_target;
#endif
if (!planner.buffer_line(pos, scaled_fr_mm_s, active_extruder, step))
if (!planner.buffer_line(pos, scaled_fr_mm_s, active_extruder, hints))
break;
}
}

139
Marlin/src/module/probe.cpp

@ -48,7 +48,12 @@
#include "delta.h"
#endif
#if ANY(HAS_QUIET_PROBING, USE_SENSORLESS)
#if ENABLED(SENSORLESS_PROBING)
abc_float_t offset_sensorless_adj{0};
float largest_sensorless_adj = 0;
#endif
#if EITHER(HAS_QUIET_PROBING, USE_SENSORLESS)
#include "stepper/indirection.h"
#if BOTH(HAS_QUIET_PROBING, PROBING_ESTEPPERS_OFF)
#include "stepper.h"
@ -259,7 +264,57 @@ xyz_pos_t Probe::offset; // Initialized by settings.load()
#endif
}
#endif // Z_PROBE_ALLEN_KEY
#elif ENABLED(MAG_MOUNTED_PROBE)
typedef struct { float fr_mm_min; xyz_pos_t where; } mag_probe_move_t;
inline void run_deploy_moves_script() {
#ifdef MAG_MOUNTED_DEPLOY_1
constexpr mag_probe_move_t deploy_1 = MAG_MOUNTED_DEPLOY_1;
do_blocking_move_to(deploy_1.where, MMM_TO_MMS(deploy_1.fr_mm_min));
#endif
#ifdef MAG_MOUNTED_DEPLOY_2
constexpr mag_probe_move_t deploy_2 = MAG_MOUNTED_DEPLOY_2;
do_blocking_move_to(deploy_2.where, MMM_TO_MMS(deploy_2.fr_mm_min));
#endif
#ifdef MAG_MOUNTED_DEPLOY_3
constexpr mag_probe_move_t deploy_3 = MAG_MOUNTED_DEPLOY_3;
do_blocking_move_to(deploy_3.where, MMM_TO_MMS(deploy_3.fr_mm_min));
#endif
#ifdef MAG_MOUNTED_DEPLOY_4
constexpr mag_probe_move_t deploy_4 = MAG_MOUNTED_DEPLOY_4;
do_blocking_move_to(deploy_4.where, MMM_TO_MMS(deploy_4.fr_mm_min));
#endif
#ifdef MAG_MOUNTED_DEPLOY_5
constexpr mag_probe_move_t deploy_5 = MAG_MOUNTED_DEPLOY_5;
do_blocking_move_to(deploy_5.where, MMM_TO_MMS(deploy_5.fr_mm_min));
#endif
}
inline void run_stow_moves_script() {
#ifdef MAG_MOUNTED_STOW_1
constexpr mag_probe_move_t stow_1 = MAG_MOUNTED_STOW_1;
do_blocking_move_to(stow_1.where, MMM_TO_MMS(stow_1.fr_mm_min));
#endif
#ifdef MAG_MOUNTED_STOW_2
constexpr mag_probe_move_t stow_2 = MAG_MOUNTED_STOW_2;
do_blocking_move_to(stow_2.where, MMM_TO_MMS(stow_2.fr_mm_min));
#endif
#ifdef MAG_MOUNTED_STOW_3
constexpr mag_probe_move_t stow_3 = MAG_MOUNTED_STOW_3;
do_blocking_move_to(stow_3.where, MMM_TO_MMS(stow_3.fr_mm_min));
#endif
#ifdef MAG_MOUNTED_STOW_4
constexpr mag_probe_move_t stow_4 = MAG_MOUNTED_STOW_4;
do_blocking_move_to(stow_4.where, MMM_TO_MMS(stow_4.fr_mm_min));
#endif
#ifdef MAG_MOUNTED_STOW_5
constexpr mag_probe_move_t stow_5 = MAG_MOUNTED_STOW_5;
do_blocking_move_to(stow_5.where, MMM_TO_MMS(stow_5.fr_mm_min));
#endif
}
#endif // MAG_MOUNTED_PROBE
#if HAS_QUIET_PROBING
@ -345,7 +400,7 @@ FORCE_INLINE void probe_specific_action(const bool deploy) {
servo[Z_PROBE_SERVO_NR].move(servo_angles[Z_PROBE_SERVO_NR][deploy ? 0 : 1]);
#elif EITHER(TOUCH_MI_PROBE, Z_PROBE_ALLEN_KEY)
#elif ANY(TOUCH_MI_PROBE, Z_PROBE_ALLEN_KEY, MAG_MOUNTED_PROBE)
deploy ? run_deploy_moves_script() : run_stow_moves_script();
@ -867,76 +922,38 @@ float Probe::probe_at_point(const_float_t rx, const_float_t ry, const ProbePtRai
#endif // HAS_Z_SERVO_PROBE
#if USE_SENSORLESS
sensorless_t stealth_states { false };
/**
* Disable stealthChop if used. Enable diag1 pin on driver.
*/
void Probe::enable_stallguard_diag1() {
#if ENABLED(SENSORLESS_PROBING)
#if HAS_DELTA_SENSORLESS_PROBING
stealth_states.x = tmc_enable_stallguard(stepperX);
stealth_states.y = tmc_enable_stallguard(stepperY);
#endif
stealth_states.z = tmc_enable_stallguard(stepperZ);
endstops.enable(true);
#endif
}
/**
* Re-enable stealthChop if used. Disable diag1 pin on driver.
*/
void Probe::disable_stallguard_diag1() {
#if ENABLED(SENSORLESS_PROBING)
endstops.not_homing();
#if HAS_DELTA_SENSORLESS_PROBING
tmc_disable_stallguard(stepperX, stealth_states.x);
tmc_disable_stallguard(stepperY, stealth_states.y);
#endif
tmc_disable_stallguard(stepperZ, stealth_states.z);
#endif
}
#if HAS_DELTA_SENSORLESS_PROBING
/**
* Set the sensorless Z offset
*/
void Probe::set_offset_sensorless_adj(const_float_t sz) {
#if ENABLED(SENSORLESS_PROBING)
DEBUG_SECTION(pso, "Probe::set_offset_sensorless_adj", true);
#if HAS_DELTA_SENSORLESS_PROBING
if (test_sensitivity.x) offset_sensorless_adj.a = sz;
if (test_sensitivity.y) offset_sensorless_adj.b = sz;
#endif
if (test_sensitivity.z) offset_sensorless_adj.c = sz;
#endif
DEBUG_SECTION(pso, "Probe::set_offset_sensorless_adj", true);
if (test_sensitivity.x) offset_sensorless_adj.a = sz;
if (test_sensitivity.y) offset_sensorless_adj.b = sz;
if (test_sensitivity.z) offset_sensorless_adj.c = sz;
}
/**
* Refresh largest_sensorless_adj based on triggered endstops
*/
void Probe::refresh_largest_sensorless_adj() {
#if ENABLED(SENSORLESS_PROBING)
DEBUG_SECTION(rso, "Probe::refresh_largest_sensorless_adj", true);
largest_sensorless_adj = -3; // A reference away from any real probe height
#if HAS_DELTA_SENSORLESS_PROBING
if (TEST(endstops.state(), X_MAX)) {
NOLESS(largest_sensorless_adj, offset_sensorless_adj.a);
DEBUG_ECHOLNPGM("Endstop_X: ", largest_sensorless_adj, " TowerX");
}
if (TEST(endstops.state(), Y_MAX)) {
NOLESS(largest_sensorless_adj, offset_sensorless_adj.b);
DEBUG_ECHOLNPGM("Endstop_Y: ", largest_sensorless_adj, " TowerY");
}
#endif
if (TEST(endstops.state(), Z_MAX)) {
NOLESS(largest_sensorless_adj, offset_sensorless_adj.c);
DEBUG_ECHOLNPGM("Endstop_Z: ", largest_sensorless_adj, " TowerZ");
}
#endif
DEBUG_SECTION(rso, "Probe::refresh_largest_sensorless_adj", true);
largest_sensorless_adj = -3; // A reference away from any real probe height
if (TEST(endstops.state(), X_MAX)) {
NOLESS(largest_sensorless_adj, offset_sensorless_adj.a);
DEBUG_ECHOLNPGM("Endstop_X: ", largest_sensorless_adj, " TowerX");
}
if (TEST(endstops.state(), Y_MAX)) {
NOLESS(largest_sensorless_adj, offset_sensorless_adj.b);
DEBUG_ECHOLNPGM("Endstop_Y: ", largest_sensorless_adj, " TowerY");
}
if (TEST(endstops.state(), Z_MAX)) {
NOLESS(largest_sensorless_adj, offset_sensorless_adj.c);
DEBUG_ECHOLNPGM("Endstop_Z: ", largest_sensorless_adj, " TowerZ");
}
}
#endif // SENSORLESS_PROBING || SENSORLESS_HOMING
#endif
#endif // HAS_BED_PROBE

12
Marlin/src/module/probe.h

@ -62,16 +62,16 @@
#endif
#endif
#if ENABLED(SENSORLESS_PROBING)
extern abc_float_t offset_sensorless_adj;
#endif
class Probe {
public:
#if ENABLED(SENSORLESS_PROBING)
typedef struct {
#if HAS_DELTA_SENSORLESS_PROBING
bool x:1, y:1, z:1;
#else
bool z;
#endif
} sense_bool_t;
static sense_bool_t test_sensitivity;
#endif
@ -302,9 +302,7 @@ public:
#endif
// Basic functions for Sensorless Homing and Probing
#if USE_SENSORLESS
static void enable_stallguard_diag1();
static void disable_stallguard_diag1();
#if HAS_DELTA_SENSORLESS_PROBING
static void set_offset_sensorless_adj(const_float_t sz);
static void refresh_largest_sensorless_adj();
#endif

35
Marlin/src/module/settings.cpp

@ -76,8 +76,7 @@
#include "../lcd/extui/ui_api.h"
#elif ENABLED(DWIN_LCD_PROUI)
#include "../lcd/e3v2/proui/dwin.h"
#elif ENABLED(DWIN_CREALITY_LCD_JYERSUI)
#include "../lcd/e3v2/jyersui/dwin.h"
#include "../lcd/e3v2/proui/bedlevel_tools.h"
#endif
#if ENABLED(HOST_PROMPT_SUPPORT)
@ -502,8 +501,6 @@ typedef struct SettingsDataStruct {
//
#if ENABLED(DWIN_LCD_PROUI)
uint8_t dwin_data[eeprom_data_size];
#elif ENABLED(DWIN_CREALITY_LCD_JYERSUI)
uint8_t dwin_settings[CrealityDWIN.eeprom_data_size];
#endif
//
@ -1522,15 +1519,6 @@ void MarlinSettings::postprocess() {
}
#endif
#if ENABLED(DWIN_CREALITY_LCD_JYERSUI)
{
_FIELD_TEST(dwin_settings);
char dwin_settings[CrealityDWIN.eeprom_data_size] = { 0 };
CrealityDWIN.Save_Settings(dwin_settings);
EEPROM_WRITE(dwin_settings);
}
#endif
//
// Case Light Brightness
//
@ -2495,13 +2483,6 @@ void MarlinSettings::postprocess() {
EEPROM_READ(dwin_data);
if (!validating) DWIN_CopySettingsFrom(dwin_data);
}
#elif ENABLED(DWIN_CREALITY_LCD_JYERSUI)
{
const char dwin_settings[CrealityDWIN.eeprom_data_size] = { 0 };
_FIELD_TEST(dwin_settings);
EEPROM_READ(dwin_settings);
if (!validating) CrealityDWIN.Load_Settings(dwin_settings);
}
#endif
//
@ -2776,7 +2757,7 @@ void MarlinSettings::postprocess() {
#endif
persistentStore.access_start();
const uint16_t status = persistentStore.read_data(pos, dest, MESH_STORE_SIZE, &crc);
uint16_t status = persistentStore.read_data(pos, dest, MESH_STORE_SIZE, &crc);
persistentStore.access_finish();
#if ENABLED(OPTIMIZED_MESH_STORAGE)
@ -2789,6 +2770,16 @@ void MarlinSettings::postprocess() {
bedlevel.set_mesh_from_store(z_mesh_store, bedlevel.z_values);
#endif
#if ENABLED(DWIN_LCD_PROUI)
status = !BedLevelTools.meshvalidate();
if (status) {
bedlevel.invalidate();
LCD_MESSAGE(MSG_UBL_MESH_INVALID);
}
else
ui.status_printf(0, GET_TEXT_F(MSG_MESH_LOADED), bedlevel.storage_slot);
#endif
if (status) SERIAL_ECHOLNPGM("?Unable to load mesh data.");
else DEBUG_ECHOLNPGM("Mesh loaded from slot ", slot);
@ -2929,8 +2920,6 @@ void MarlinSettings::reset() {
#endif
#endif
TERN_(DWIN_CREALITY_LCD_JYERSUI, CrealityDWIN.Reset_Settings());
//
// Case Light Brightness
//

272
Marlin/src/module/stepper.cpp

@ -117,12 +117,6 @@ Stepper stepper; // Singleton
#include "../feature/runout.h"
#endif
#if HAS_L64XX
#include "../libs/L64XX/L64XX_Marlin.h"
uint8_t L6470_buf[MAX_L64XX + 1]; // chip command sequence - element 0 not used
bool L64XX_OK_to_power_up = false; // flag to keep L64xx steppers powered down after a reset or power up
#endif
#if ENABLED(AUTO_POWER_CONTROL)
#include "../feature/power.h"
#endif
@ -253,20 +247,6 @@ xyz_long_t Stepper::endstops_trigsteps;
xyze_long_t Stepper::count_position{0};
xyze_int8_t Stepper::count_direction{0};
#if ENABLED(LASER_POWER_INLINE_TRAPEZOID)
Stepper::stepper_laser_t Stepper::laser_trap = {
.enabled = false,
.cur_power = 0,
.cruise_set = false,
#if DISABLED(LASER_POWER_INLINE_TRAPEZOID_CONT)
.last_step_count = 0,
.acc_step_count = 0
#else
.till_update = 0
#endif
};
#endif
#define MINDIR(A) (count_direction[_AXIS(A)] < 0)
#define MAXDIR(A) (count_direction[_AXIS(A)] > 0)
@ -632,27 +612,6 @@ void Stepper::set_directions() {
#endif
#endif // !LIN_ADVANCE
#if HAS_L64XX
if (L64XX_OK_to_power_up) { // OK to send the direction commands (which powers up the L64XX steppers)
if (L64xxManager.spi_active) {
L64xxManager.spi_abort = true; // Interrupted SPI transfer needs to shut down gracefully
for (uint8_t j = 1; j <= L64XX::chain[0]; j++)
L6470_buf[j] = dSPIN_NOP; // Fill buffer with NOOPs
L64xxManager.transfer(L6470_buf, L64XX::chain[0]); // Send enough NOOPs to complete any command
L64xxManager.transfer(L6470_buf, L64XX::chain[0]);
L64xxManager.transfer(L6470_buf, L64XX::chain[0]);
}
// L64xxManager.dir_commands[] is an array that holds direction command for each stepper
// Scan command array, copy matches into L64xxManager.transfer
for (uint8_t j = 1; j <= L64XX::chain[0]; j++)
L6470_buf[j] = L64xxManager.dir_commands[L64XX::chain[j]];
L64xxManager.transfer(L6470_buf, L64XX::chain[0]); // send the command stream to the drivers
}
#endif
DIR_WAIT_AFTER();
}
@ -1964,7 +1923,6 @@ uint32_t Stepper::block_phase_isr() {
// If there is a current block
if (current_block) {
// If current block is finished, reset pointer and finalize state
if (step_events_completed >= step_event_count) {
#if ENABLED(DIRECT_STEPPING)
@ -2017,32 +1975,29 @@ uint32_t Stepper::block_phase_isr() {
else if (LA_steps) nextAdvanceISR = 0;
#endif
// Update laser - Accelerating
#if ENABLED(LASER_POWER_INLINE_TRAPEZOID)
if (laser_trap.enabled) {
#if DISABLED(LASER_POWER_INLINE_TRAPEZOID_CONT)
if (current_block->laser.entry_per) {
laser_trap.acc_step_count -= step_events_completed - laser_trap.last_step_count;
laser_trap.last_step_count = step_events_completed;
// Should be faster than a divide, since this should trip just once
if (laser_trap.acc_step_count < 0) {
while (laser_trap.acc_step_count < 0) {
laser_trap.acc_step_count += current_block->laser.entry_per;
if (laser_trap.cur_power < current_block->laser.power) laser_trap.cur_power++;
}
cutter.ocr_set_power(laser_trap.cur_power);
}
}
#else
if (laser_trap.till_update)
laser_trap.till_update--;
else {
laser_trap.till_update = LASER_POWER_INLINE_TRAPEZOID_CONT_PER;
laser_trap.cur_power = (current_block->laser.power * acc_step_rate) / current_block->nominal_rate;
cutter.ocr_set_power(laser_trap.cur_power); // Cycle efficiency is irrelevant it the last line was many cycles
/**
* Adjust Laser Power - Accelerating
*
* isPowered - True when a move is powered.
* isEnabled - laser power is active.
*
* Laser power variables are calulated and stored in this block by the planner code.
* trap_ramp_active_pwr - the active power in this block across accel or decel trap steps.
* trap_ramp_entry_incr - holds the precalculated value to increase the current power per accel step.
*
* Apply the starting active power and then increase power per step by the trap_ramp_entry_incr value if positive.
*/
#if ENABLED(LASER_POWER_TRAP)
if (cutter.cutter_mode == CUTTER_MODE_CONTINUOUS) {
if (planner.laser_inline.status.isPowered && planner.laser_inline.status.isEnabled) {
if (current_block->laser.trap_ramp_entry_incr > 0) {
cutter.apply_power(current_block->laser.trap_ramp_active_pwr);
current_block->laser.trap_ramp_active_pwr += current_block->laser.trap_ramp_entry_incr;
}
#endif
}
// Not a powered move.
else cutter.apply_power(0);
}
#endif
}
@ -2051,6 +2006,7 @@ uint32_t Stepper::block_phase_isr() {
uint32_t step_rate;
#if ENABLED(S_CURVE_ACCELERATION)
// If this is the 1st time we process the 2nd half of the trapezoid...
if (!bezier_2nd_half) {
// Initialize the Bézier speed curve
@ -2065,8 +2021,8 @@ uint32_t Stepper::block_phase_isr() {
? _eval_bezier_curve(deceleration_time)
: current_block->final_rate;
}
#else
#else
// Using the old trapezoidal control
step_rate = STEP_MULTIPLY(deceleration_time, current_block->acceleration_rate);
if (step_rate < acc_step_rate) { // Still decelerating?
@ -2075,9 +2031,8 @@ uint32_t Stepper::block_phase_isr() {
}
else
step_rate = current_block->final_rate;
#endif
// step_rate is in steps/second
#endif
// step_rate to timer interval and steps per stepper isr
interval = calc_timer_interval(step_rate, &steps_per_isr);
@ -2094,37 +2049,25 @@ uint32_t Stepper::block_phase_isr() {
else if (LA_steps) nextAdvanceISR = 0;
#endif // LIN_ADVANCE
// Update laser - Decelerating
#if ENABLED(LASER_POWER_INLINE_TRAPEZOID)
if (laser_trap.enabled) {
#if DISABLED(LASER_POWER_INLINE_TRAPEZOID_CONT)
if (current_block->laser.exit_per) {
laser_trap.acc_step_count -= step_events_completed - laser_trap.last_step_count;
laser_trap.last_step_count = step_events_completed;
// Should be faster than a divide, since this should trip just once
if (laser_trap.acc_step_count < 0) {
while (laser_trap.acc_step_count < 0) {
laser_trap.acc_step_count += current_block->laser.exit_per;
if (laser_trap.cur_power > current_block->laser.power_exit) laser_trap.cur_power--;
}
cutter.ocr_set_power(laser_trap.cur_power);
}
}
#else
if (laser_trap.till_update)
laser_trap.till_update--;
else {
laser_trap.till_update = LASER_POWER_INLINE_TRAPEZOID_CONT_PER;
laser_trap.cur_power = (current_block->laser.power * step_rate) / current_block->nominal_rate;
cutter.ocr_set_power(laser_trap.cur_power); // Cycle efficiency isn't relevant when the last line was many cycles
/*
* Adjust Laser Power - Decelerating
* trap_ramp_entry_decr - holds the precalculated value to decrease the current power per decel step.
*/
#if ENABLED(LASER_POWER_TRAP)
if (cutter.cutter_mode == CUTTER_MODE_CONTINUOUS) {
if (planner.laser_inline.status.isPowered && planner.laser_inline.status.isEnabled) {
if (current_block->laser.trap_ramp_exit_decr > 0) {
current_block->laser.trap_ramp_active_pwr -= current_block->laser.trap_ramp_exit_decr;
cutter.apply_power(current_block->laser.trap_ramp_active_pwr);
}
#endif
// Not a powered move.
else cutter.apply_power(0);
}
}
#endif
}
// Must be in cruise phase otherwise
else {
else { // Must be in cruise phase otherwise
#if ENABLED(LIN_ADVANCE)
// If there are any esteps, fire the next advance_isr "now"
@ -2139,24 +2082,49 @@ uint32_t Stepper::block_phase_isr() {
// The timer interval is just the nominal value for the nominal speed
interval = ticks_nominal;
}
// Update laser - Cruising
#if ENABLED(LASER_POWER_INLINE_TRAPEZOID)
if (laser_trap.enabled) {
if (!laser_trap.cruise_set) {
laser_trap.cur_power = current_block->laser.power;
cutter.ocr_set_power(laser_trap.cur_power);
laser_trap.cruise_set = true;
/**
* Adjust Laser Power - Cruise
* power - direct or floor adjusted active laser power.
*/
#if ENABLED(LASER_POWER_TRAP)
if (cutter.cutter_mode == CUTTER_MODE_CONTINUOUS) {
if (step_events_completed + 1 == accelerate_until) {
if (planner.laser_inline.status.isPowered && planner.laser_inline.status.isEnabled) {
if (current_block->laser.trap_ramp_entry_incr > 0) {
current_block->laser.trap_ramp_active_pwr = current_block->laser.power;
cutter.apply_power(current_block->laser.power);
}
}
#if ENABLED(LASER_POWER_INLINE_TRAPEZOID_CONT)
laser_trap.till_update = LASER_POWER_INLINE_TRAPEZOID_CONT_PER;
#else
laser_trap.last_step_count = step_events_completed;
#endif
// Not a powered move.
else cutter.apply_power(0);
}
#endif
}
}
#endif
}
#if ENABLED(LASER_FEATURE)
/**
* CUTTER_MODE_DYNAMIC is experimental and developing.
* Super-fast method to dynamically adjust the laser power OCR value based on the input feedrate in mm-per-minute.
* TODO: Set up Min/Max OCR offsets to allow tuning and scaling of various lasers.
* TODO: Integrate accel/decel +-rate into the dynamic laser power calc.
*/
if (cutter.cutter_mode == CUTTER_MODE_DYNAMIC
&& planner.laser_inline.status.isPowered // isPowered flag set on any parsed G1, G2, G3, or G5 move; cleared on any others.
&& cutter.last_block_power != current_block->laser.power // Prevent constant update without change
) {
cutter.apply_power(current_block->laser.power);
cutter.last_block_power = current_block->laser.power;
}
#endif
}
else { // !current_block
#if ENABLED(LASER_FEATURE)
if (cutter.cutter_mode == CUTTER_MODE_DYNAMIC)
cutter.apply_power(0); // No movement in dynamic mode so turn Laser off
#endif
}
// If there is no current block at this point, attempt to pop one from the buffer
@ -2169,11 +2137,18 @@ uint32_t Stepper::block_phase_isr() {
// Sync block? Sync the stepper counts or fan speeds and return
while (current_block->is_sync()) {
if (current_block->is_fan_sync()) {
TERN_(LASER_SYNCHRONOUS_M106_M107, planner.sync_fan_speeds(current_block->fan_speed));
}
else
_set_position(current_block->position);
#if ENABLED(LASER_POWER_SYNC)
if (cutter.cutter_mode == CUTTER_MODE_CONTINUOUS) {
if (current_block->is_pwr_sync()) {
planner.laser_inline.status.isSyncPower = true;
cutter.apply_power(current_block->laser.power);
}
}
#endif
TERN_(LASER_SYNCHRONOUS_M106_M107, if (current_block->is_fan_sync()) planner.sync_fan_speeds(current_block->fan_speed));
if (!(current_block->is_fan_sync() || current_block->is_pwr_sync())) _set_position(current_block->position);
discard_current_block();
@ -2183,8 +2158,10 @@ uint32_t Stepper::block_phase_isr() {
}
// For non-inline cutter, grossly apply power
#if ENABLED(LASER_FEATURE) && DISABLED(LASER_POWER_INLINE)
cutter.apply_power(current_block->cutter_power);
#if HAS_CUTTER
if (cutter.cutter_mode == CUTTER_MODE_STANDARD) {
cutter.apply_power(current_block->cutter_power);
}
#endif
#if ENABLED(POWER_LOSS_RECOVERY)
@ -2347,46 +2324,30 @@ uint32_t Stepper::block_phase_isr() {
else LA_isr_rate = LA_ADV_NEVER;
#endif
if ( ENABLED(HAS_L64XX) // Always set direction for L64xx (Also enables the chips)
|| ENABLED(DUAL_X_CARRIAGE) // TODO: Find out why this fixes "jittery" small circles
if ( ENABLED(DUAL_X_CARRIAGE) // TODO: Find out why this fixes "jittery" small circles
|| current_block->direction_bits != last_direction_bits
|| TERN(MIXING_EXTRUDER, false, stepper_extruder != last_moved_extruder)
) {
E_TERN_(last_moved_extruder = stepper_extruder);
TERN_(HAS_L64XX, L64XX_OK_to_power_up = true);
set_directions(current_block->direction_bits);
}
#if ENABLED(LASER_POWER_INLINE)
const power_status_t stat = current_block->laser.status;
#if ENABLED(LASER_POWER_INLINE_TRAPEZOID)
laser_trap.enabled = stat.isPlanned && stat.isEnabled;
laser_trap.cur_power = current_block->laser.power_entry; // RESET STATE
laser_trap.cruise_set = false;
#if DISABLED(LASER_POWER_INLINE_TRAPEZOID_CONT)
laser_trap.last_step_count = 0;
laser_trap.acc_step_count = current_block->laser.entry_per / 2;
#else
laser_trap.till_update = 0;
#endif
// Always have PWM in this case
if (stat.isPlanned) { // Planner controls the laser
cutter.ocr_set_power(
stat.isEnabled ? laser_trap.cur_power : 0 // ON with power or OFF
);
}
#else
if (stat.isPlanned) { // Planner controls the laser
#if ENABLED(SPINDLE_LASER_USE_PWM)
cutter.ocr_set_power(
stat.isEnabled ? current_block->laser.power : 0 // ON with power or OFF
);
#if ENABLED(LASER_FEATURE)
if (cutter.cutter_mode == CUTTER_MODE_CONTINUOUS) { // Planner controls the laser
if (planner.laser_inline.status.isSyncPower)
// If the previous block was a M3 sync power then skip the trap power init otherwise it will 0 the sync power.
planner.laser_inline.status.isSyncPower = false; // Clear the flag to process subsequent trap calc's.
else if (current_block->laser.status.isEnabled) {
#if ENABLED(LASER_POWER_TRAP)
TERN_(DEBUG_LASER_TRAP, SERIAL_ECHO_MSG("InitTrapPwr:",current_block->laser.trap_ramp_active_pwr));
cutter.apply_power(current_block->laser.status.isPowered ? current_block->laser.trap_ramp_active_pwr : 0);
#else
cutter.set_enabled(stat.isEnabled);
TERN_(DEBUG_CUTTER_POWER, SERIAL_ECHO_MSG("InlinePwr:",current_block->laser.power));
cutter.apply_power(current_block->laser.status.isPowered ? current_block->laser.power : 0);
#endif
}
#endif
#endif // LASER_POWER_INLINE
}
#endif // LASER_FEATURE
// If the endstop is already pressed, endstop interrupts won't invoke
// endstop_triggered and the move will grind. So check here for a
@ -2416,21 +2377,6 @@ uint32_t Stepper::block_phase_isr() {
// Calculate the initial timer interval
interval = calc_timer_interval(current_block->initial_rate, &steps_per_isr);
}
#if ENABLED(LASER_POWER_INLINE_CONTINUOUS)
else { // No new block found; so apply inline laser parameters
// This should mean ending file with 'M5 I' will stop the laser; thus the inline flag isn't needed
const power_status_t stat = planner.laser_inline.status;
if (stat.isPlanned) { // Planner controls the laser
#if ENABLED(SPINDLE_LASER_USE_PWM)
cutter.ocr_set_power(
stat.isEnabled ? planner.laser_inline.power : 0 // ON with power or OFF
);
#else
cutter.set_enabled(stat.isEnabled);
#endif
}
}
#endif
}
// Return the interval to wait

19
Marlin/src/module/stepper.h

@ -444,25 +444,6 @@ class Stepper {
// Current stepper motor directions (+1 or -1)
static xyze_int8_t count_direction;
#if ENABLED(LASER_POWER_INLINE_TRAPEZOID)
typedef struct {
bool enabled; // Trapezoid needed flag (i.e., laser on, planner in control)
uint8_t cur_power; // Current laser power
bool cruise_set; // Power set up for cruising?
#if ENABLED(LASER_POWER_INLINE_TRAPEZOID_CONT)
uint16_t till_update; // Countdown to the next update
#else
uint32_t last_step_count, // Step count from the last update
acc_step_count; // Bresenham counter for laser accel/decel
#endif
} stepper_laser_t;
static stepper_laser_t laser_trap;
#endif
public:
// Initialize stepper hardware
static void init();

264
Marlin/src/module/stepper/L64xx.cpp

@ -1,264 +0,0 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
/**
* stepper/L64xx.cpp
* Stepper driver indirection for L64XX drivers
*/
#include "../../inc/MarlinConfig.h"
#if HAS_L64XX
#include "L64xx.h"
#if AXIS_IS_L64XX(X)
L64XX_CLASS(X) stepperX(L6470_CHAIN_SS_PIN);
#endif
#if AXIS_IS_L64XX(X2)
L64XX_CLASS(X2) stepperX2(L6470_CHAIN_SS_PIN);
#endif
#if AXIS_IS_L64XX(Y)
L64XX_CLASS(Y) stepperY(L6470_CHAIN_SS_PIN);
#endif
#if AXIS_IS_L64XX(Y2)
L64XX_CLASS(Y2) stepperY2(L6470_CHAIN_SS_PIN);
#endif
#if AXIS_IS_L64XX(Z)
L64XX_CLASS(Z) stepperZ(L6470_CHAIN_SS_PIN);
#endif
#if AXIS_IS_L64XX(Z2)
L64XX_CLASS(Z2) stepperZ2(L6470_CHAIN_SS_PIN);
#endif
#if AXIS_IS_L64XX(Z3)
L64XX_CLASS(Z3) stepperZ3(L6470_CHAIN_SS_PIN);
#endif
#if AXIS_IS_L64XX(Z4)
L64XX_CLASS(Z4) stepperZ4(L6470_CHAIN_SS_PIN);
#endif
#if AXIS_IS_L64XX(I)
L64XX_CLASS(I) stepperI(L6470_CHAIN_SS_PIN);
#endif
#if AXIS_IS_L64XX(J)
L64XX_CLASS(J) stepperJ(L6470_CHAIN_SS_PIN);
#endif
#if AXIS_IS_L64XX(K)
L64XX_CLASS(K) stepperK(L6470_CHAIN_SS_PIN);
#endif
#if AXIS_IS_L64XX(U)
L64XX_CLASS(u) stepperU(L6470_CHAIN_SS_PIN);
#endif
#if AXIS_IS_L64XX(V)
L64XX_CLASS(v) stepperV(L6470_CHAIN_SS_PIN);
#endif
#if AXIS_IS_L64XX(W)
L64XX_CLASS(w) stepperW(L6470_CHAIN_SS_PIN);
#endif
#if AXIS_IS_L64XX(E0)
L64XX_CLASS(E0) stepperE0(L6470_CHAIN_SS_PIN);
#endif
#if AXIS_IS_L64XX(E1)
L64XX_CLASS(E1) stepperE1(L6470_CHAIN_SS_PIN);
#endif
#if AXIS_IS_L64XX(E2)
L64XX_CLASS(E2) stepperE2(L6470_CHAIN_SS_PIN);
#endif
#if AXIS_IS_L64XX(E3)
L64XX_CLASS(E3) stepperE3(L6470_CHAIN_SS_PIN);
#endif
#if AXIS_IS_L64XX(E4)
L64XX_CLASS(E4) stepperE4(L6470_CHAIN_SS_PIN);
#endif
#if AXIS_IS_L64XX(E5)
L64XX_CLASS(E5) stepperE5(L6470_CHAIN_SS_PIN);
#endif
#if AXIS_IS_L64XX(E6)
L64XX_CLASS(E6) stepperE6(L6470_CHAIN_SS_PIN);
#endif
#if AXIS_IS_L64XX(E7)
L64XX_CLASS(E7) stepperE7(L6470_CHAIN_SS_PIN);
#endif
// Not using L64XX class init method because it
// briefly sends power to the steppers
inline void L6470_init_chip(L64XX &st, const int ms, const int oc, const int sc, const int mv, const int slew_rate) {
st.set_handlers(L64xxManager.spi_init, L64xxManager.transfer_single, L64xxManager.transfer_chain); // specify which external SPI routines to use
switch (st.L6470_status_layout) {
case L6470_STATUS_LAYOUT: {
st.resetDev();
st.softFree();
st.SetParam(st.L64XX_CONFIG, CONFIG_PWM_DIV_1 | CONFIG_PWM_MUL_2 | CONFIG_OC_SD_DISABLE | CONFIG_VS_COMP_DISABLE | CONFIG_SW_HARD_STOP | CONFIG_INT_16MHZ);
st.SetParam(L6470_KVAL_RUN, 0xFF);
st.SetParam(L6470_KVAL_ACC, 0xFF);
st.SetParam(L6470_KVAL_DEC, 0xFF);
st.setMicroSteps(ms);
st.setOverCurrent(oc);
st.setStallCurrent(sc);
st.SetParam(L6470_KVAL_HOLD, mv);
st.SetParam(L6470_ABS_POS, 0);
uint32_t config_temp = st.GetParam(st.L64XX_CONFIG);
config_temp &= ~CONFIG_POW_SR;
switch (slew_rate) {
case 0: st.SetParam(st.L64XX_CONFIG, config_temp | CONFIG_SR_75V_us); break;
default:
case 1: st.SetParam(st.L64XX_CONFIG, config_temp | CONFIG_SR_110V_us); break;
case 3:
case 2: st.SetParam(st.L64XX_CONFIG, config_temp | CONFIG_SR_260V_us); break;
}
st.getStatus();
st.getStatus();
break;
}
case L6474_STATUS_LAYOUT: {
st.free();
//st.SetParam(st.L64XX_CONFIG, CONFIG_PWM_DIV_1 | CONFIG_PWM_MUL_2 | CONFIG_OC_SD_DISABLE | CONFIG_VS_COMP_DISABLE | CONFIG_SW_HARD_STOP | CONFIG_INT_16MHZ);
//st.SetParam(L6474_TVAL, 0xFF);
st.setMicroSteps(ms);
st.setOverCurrent(oc);
st.setTVALCurrent(sc);
st.SetParam(L6470_ABS_POS, 0);
uint32_t config_temp = st.GetParam(st.L64XX_CONFIG);
config_temp &= ~CONFIG_POW_SR & ~CONFIG_EN_TQREG; // clear out slew rate and set current to be controlled by TVAL register
switch (slew_rate) {
case 0: st.SetParam(st.L64XX_CONFIG, config_temp | CONFIG_SR_75V_us); break;
default:
case 1: st.SetParam(st.L64XX_CONFIG, config_temp | CONFIG_SR_110V_us); break;
case 3:
case 2: st.SetParam(st.L64XX_CONFIG, config_temp | CONFIG_SR_260V_us); break;
//case 0: st.SetParam(st.L64XX_CONFIG, 0x2E88 | CONFIG_EN_TQREG | CONFIG_SR_75V_us); break;
//default:
//case 1: st.SetParam(st.L64XX_CONFIG, 0x2E88 | CONFIG_EN_TQREG | CONFIG_SR_110V_us); break;
//case 3:
//case 2: st.SetParam(st.L64XX_CONFIG, 0x2E88 | CONFIG_EN_TQREG | CONFIG_SR_260V_us); break;
//case 0: st.SetParam(st.L64XX_CONFIG, 0x2E88 ); break;
//default:
//case 1: st.SetParam(st.L64XX_CONFIG, 0x2E88 ); break;
//case 3:
//case 2: st.SetParam(st.L64XX_CONFIG, 0x2E88 ); break;
}
st.getStatus();
st.getStatus();
break;
}
case L6480_STATUS_LAYOUT: {
st.resetDev();
st.softFree();
st.SetParam(st.L64XX_CONFIG, CONFIG_PWM_DIV_1 | CONFIG_PWM_MUL_2 | CONFIG_OC_SD_DISABLE | CONFIG_VS_COMP_DISABLE | CONFIG_SW_HARD_STOP | CONFIG_INT_16MHZ);
st.SetParam(L6470_KVAL_RUN, 0xFF);
st.SetParam(L6470_KVAL_ACC, 0xFF);
st.SetParam(L6470_KVAL_DEC, 0xFF);
st.setMicroSteps(ms);
st.setOverCurrent(oc);
st.setStallCurrent(sc);
st.SetParam(+-L6470_KVAL_HOLD, mv);
st.SetParam(L6470_ABS_POS, 0);
st.SetParam(st.L64XX_CONFIG,(st.GetParam(st.L64XX_CONFIG) | PWR_VCC_7_5V));
st.getStatus(); // must clear out status bits before can set slew rate
st.getStatus();
switch (slew_rate) {
case 0: st.SetParam(L6470_GATECFG1, CONFIG1_SR_220V_us); st.SetParam(L6470_GATECFG2, CONFIG2_SR_220V_us); break;
default:
case 1: st.SetParam(L6470_GATECFG1, CONFIG1_SR_400V_us); st.SetParam(L6470_GATECFG2, CONFIG2_SR_400V_us); break;
case 2: st.SetParam(L6470_GATECFG1, CONFIG1_SR_520V_us); st.SetParam(L6470_GATECFG2, CONFIG2_SR_520V_us); break;
case 3: st.SetParam(L6470_GATECFG1, CONFIG1_SR_980V_us); st.SetParam(L6470_GATECFG2, CONFIG2_SR_980V_us); break;
}
break;
}
}
}
#define L6470_INIT_CHIP(Q) L6470_init_chip(stepper##Q, Q##_MICROSTEPS, Q##_OVERCURRENT, Q##_STALLCURRENT, Q##_MAX_VOLTAGE, Q##_SLEW_RATE)
void L64XX_Marlin::init_to_defaults() {
#if AXIS_IS_L64XX(X)
L6470_INIT_CHIP(X);
#endif
#if AXIS_IS_L64XX(X2)
L6470_INIT_CHIP(X2);
#endif
#if AXIS_IS_L64XX(Y)
L6470_INIT_CHIP(Y);
#endif
#if AXIS_IS_L64XX(Y2)
L6470_INIT_CHIP(Y2);
#endif
#if AXIS_IS_L64XX(Z)
L6470_INIT_CHIP(Z);
#endif
#if AXIS_IS_L64XX(Z2)
L6470_INIT_CHIP(Z2);
#endif
#if AXIS_IS_L64XX(Z3)
L6470_INIT_CHIP(Z3);
#endif
#if AXIS_IS_L64XX(Z4)
L6470_INIT_CHIP(Z4);
#endif
#if AXIS_IS_L64XX(I)
L6470_INIT_CHIP(I);
#endif
#if AXIS_IS_L64XX(J)
L6470_INIT_CHIP(J);
#endif
#if AXIS_IS_L64XX(K)
L6470_INIT_CHIP(K);
#endif
#if AXIS_IS_L64XX(U)
L6470_INIT_CHIP(U);
#endif
#if AXIS_IS_L64XX(V)
L6470_INIT_CHIP(V);
#endif
#if AXIS_IS_L64XX(W)
L6470_INIT_CHIP(W);
#endif
#if AXIS_IS_L64XX(E0)
L6470_INIT_CHIP(E0);
#endif
#if AXIS_IS_L64XX(E1)
L6470_INIT_CHIP(E1);
#endif
#if AXIS_IS_L64XX(E2)
L6470_INIT_CHIP(E2);
#endif
#if AXIS_IS_L64XX(E3)
L6470_INIT_CHIP(E3);
#endif
#if AXIS_IS_L64XX(E4)
L6470_INIT_CHIP(E4);
#endif
#if AXIS_IS_L64XX(E5)
L6470_INIT_CHIP(E5);
#endif
#if AXIS_IS_L64XX(E6)
L6470_INIT_CHIP(E6);
#endif
#if AXIS_IS_L64XX(E7)
L6470_INIT_CHIP(E7);
#endif
}
#endif // HAS_L64XX

490
Marlin/src/module/stepper/L64xx.h

@ -1,490 +0,0 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#pragma once
/**
* stepper/L64xx.h
* Stepper driver indirection for L64XX drivers
*/
#include "../../inc/MarlinConfig.h"
#include "../../libs/L64XX/L64XX_Marlin.h"
// Convert option names to L64XX classes
#define CLASS_L6470 L6470
#define CLASS_L6474 L6474
#define CLASS_POWERSTEP01 powerSTEP01
#define __L64XX_CLASS(TYPE) CLASS_##TYPE
#define _L64XX_CLASS(TYPE) __L64XX_CLASS(TYPE)
#define L64XX_CLASS(ST) _L64XX_CLASS(ST##_DRIVER_TYPE)
#define L6474_DIR_WRITE(A,STATE) do{ L64xxManager.dir_commands[A] = dSPIN_L6474_ENABLE; WRITE(A##_DIR_PIN, STATE); }while(0)
#define L64XX_DIR_WRITE(A,STATE) do{ L64xxManager.dir_commands[A] = (STATE) ? dSPIN_STEP_CLOCK_REV : dSPIN_STEP_CLOCK_FWD; }while(0)
// X Stepper
#if AXIS_IS_L64XX(X)
extern L64XX_CLASS(X) stepperX;
#define X_ENABLE_INIT() NOOP
#define X_ENABLE_WRITE(STATE) (STATE ? stepperX.hardStop() : stepperX.free())
#define X_ENABLE_READ() (stepperX.getStatus() & STATUS_HIZ)
#if AXIS_DRIVER_TYPE_X(L6474)
#define X_DIR_INIT() SET_OUTPUT(X_DIR_PIN)
#define X_DIR_WRITE(STATE) L6474_DIR_WRITE(X, STATE)
#define X_DIR_READ() READ(X_DIR_PIN)
#else
#define X_DIR_INIT() NOOP
#define X_DIR_WRITE(STATE) L64XX_DIR_WRITE(X, STATE)
#define X_DIR_READ() (stepper##X.getStatus() & STATUS_DIR);
#if AXIS_DRIVER_TYPE_X(L6470)
#define DISABLE_STEPPER_X() stepperX.free()
#endif
#endif
#endif
// Y Stepper
#if AXIS_IS_L64XX(Y)
extern L64XX_CLASS(Y) stepperY;
#define Y_ENABLE_INIT() NOOP
#define Y_ENABLE_WRITE(STATE) (STATE ? stepperY.hardStop() : stepperY.free())
#define Y_ENABLE_READ() (stepperY.getStatus() & STATUS_HIZ)
#if AXIS_DRIVER_TYPE_Y(L6474)
#define Y_DIR_INIT() SET_OUTPUT(Y_DIR_PIN)
#define Y_DIR_WRITE(STATE) L6474_DIR_WRITE(Y, STATE)
#define Y_DIR_READ() READ(Y_DIR_PIN)
#else
#define Y_DIR_INIT() NOOP
#define Y_DIR_WRITE(STATE) L64XX_DIR_WRITE(Y, STATE)
#define Y_DIR_READ() (stepper##Y.getStatus() & STATUS_DIR);
#if AXIS_DRIVER_TYPE_Y(L6470)
#define DISABLE_STEPPER_Y() stepperY.free()
#endif
#endif
#endif
// Z Stepper
#if AXIS_IS_L64XX(Z)
extern L64XX_CLASS(Z) stepperZ;
#define Z_ENABLE_INIT() NOOP
#define Z_ENABLE_WRITE(STATE) (STATE ? stepperZ.hardStop() : stepperZ.free())
#define Z_ENABLE_READ() (stepperZ.getStatus() & STATUS_HIZ)
#if AXIS_DRIVER_TYPE_Z(L6474)
#define Z_DIR_INIT() SET_OUTPUT(Z_DIR_PIN)
#define Z_DIR_WRITE(STATE) L6474_DIR_WRITE(Z, STATE)
#define Z_DIR_READ() READ(Z_DIR_PIN)
#else
#define Z_DIR_INIT() NOOP
#define Z_DIR_WRITE(STATE) L64XX_DIR_WRITE(Z, STATE)
#define Z_DIR_READ() (stepper##Z.getStatus() & STATUS_DIR);
#if AXIS_DRIVER_TYPE_Z(L6470)
#define DISABLE_STEPPER_Z() stepperZ.free()
#endif
#endif
#endif
// X2 Stepper
#if HAS_X2_ENABLE && AXIS_IS_L64XX(X2)
extern L64XX_CLASS(X2) stepperX2;
#define X2_ENABLE_INIT() NOOP
#define X2_ENABLE_WRITE(STATE) (STATE ? stepperX2.hardStop() : stepperX2.free())
#define X2_ENABLE_READ() (stepperX2.getStatus() & STATUS_HIZ)
#if AXIS_DRIVER_TYPE_X2(L6474)
#define X2_DIR_INIT() SET_OUTPUT(X2_DIR_PIN)
#define X2_DIR_WRITE(STATE) L6474_DIR_WRITE(X2, STATE)
#define X2_DIR_READ() READ(X2_DIR_PIN)
#else
#define X2_DIR_INIT() NOOP
#define X2_DIR_WRITE(STATE) L64XX_DIR_WRITE(X2, STATE)
#define X2_DIR_READ() (stepper##X2.getStatus() & STATUS_DIR);
#endif
#endif
#if AXIS_DRIVER_TYPE_X2(L6470)
#define DISABLE_STEPPER_X2() stepperX2.free()
#endif
// Y2 Stepper
#if HAS_Y2_ENABLE && AXIS_IS_L64XX(Y2)
extern L64XX_CLASS(Y2) stepperY2;
#define Y2_ENABLE_INIT() NOOP
#define Y2_ENABLE_WRITE(STATE) (STATE ? stepperY2.hardStop() : stepperY2.free())
#define Y2_ENABLE_READ() (stepperY2.getStatus() & STATUS_HIZ)
#if AXIS_DRIVER_TYPE_Y2(L6474)
#define Y2_DIR_INIT() SET_OUTPUT(Y2_DIR_PIN)
#define Y2_DIR_WRITE(STATE) L6474_DIR_WRITE(Y2, STATE)
#define Y2_DIR_READ() READ(Y2_DIR_PIN)
#else
#define Y2_DIR_INIT() NOOP
#define Y2_DIR_WRITE(STATE) L64XX_DIR_WRITE(Y2, STATE)
#define Y2_DIR_READ() (stepper##Y2.getStatus() & STATUS_DIR);
#endif
#endif
#if AXIS_DRIVER_TYPE_Y2(L6470)
#define DISABLE_STEPPER_Y2() stepperY2.free()
#endif
// Z2 Stepper
#if HAS_Z2_ENABLE && AXIS_IS_L64XX(Z2)
extern L64XX_CLASS(Z2) stepperZ2;
#define Z2_ENABLE_INIT() NOOP
#define Z2_ENABLE_WRITE(STATE) (STATE ? stepperZ2.hardStop() : stepperZ2.free())
#define Z2_ENABLE_READ() (stepperZ2.getStatus() & STATUS_HIZ)
#if AXIS_DRIVER_TYPE_Z2(L6474)
#define Z2_DIR_INIT() SET_OUTPUT(Z2_DIR_PIN)
#define Z2_DIR_WRITE(STATE) L6474_DIR_WRITE(Z2, STATE)
#define Z2_DIR_READ() READ(Z2_DIR_PIN)
#else
#define Z2_DIR_INIT() NOOP
#define Z2_DIR_WRITE(STATE) L64XX_DIR_WRITE(Z2, STATE)
#define Z2_DIR_READ() (stepper##Z2.getStatus() & STATUS_DIR);
#endif
#endif
#if AXIS_DRIVER_TYPE_Z2(L6470)
#define DISABLE_STEPPER_Z2() stepperZ2.free()
#endif
// Z3 Stepper
#if HAS_Z3_ENABLE && AXIS_IS_L64XX(Z3)
extern L64XX_CLASS(Z3) stepperZ3;
#define Z3_ENABLE_INIT() NOOP
#define Z3_ENABLE_WRITE(STATE) (STATE ? stepperZ3.hardStop() : stepperZ3.free())
#define Z3_ENABLE_READ() (stepperZ3.getStatus() & STATUS_HIZ)
#if AXIS_DRIVER_TYPE_Z3(L6474)
#define Z3_DIR_INIT() SET_OUTPUT(Z3_DIR_PIN)
#define Z3_DIR_WRITE(STATE) L6474_DIR_WRITE(Z3, STATE)
#define Z3_DIR_READ() READ(Z3_DIR_PIN)
#else
#define Z3_DIR_INIT() NOOP
#define Z3_DIR_WRITE(STATE) L64XX_DIR_WRITE(Z3, STATE)
#define Z3_DIR_READ() (stepper##Z3.getStatus() & STATUS_DIR);
#endif
#endif
#if AXIS_DRIVER_TYPE_Z3(L6470)
#define DISABLE_STEPPER_Z3() stepperZ3.free()
#endif
// Z4 Stepper
#if HAS_Z4_ENABLE && AXIS_IS_L64XX(Z4)
extern L64XX_CLASS(Z4) stepperZ4;
#define Z4_ENABLE_INIT() NOOP
#define Z4_ENABLE_WRITE(STATE) (STATE ? stepperZ4.hardStop() : stepperZ4.free())
#define Z4_ENABLE_READ() (stepperZ4.getStatus() & STATUS_HIZ)
#if AXIS_DRIVER_TYPE_Z4(L6474)
#define Z4_DIR_INIT() SET_OUTPUT(Z4_DIR_PIN)
#define Z4_DIR_WRITE(STATE) L6474_DIR_WRITE(Z4, STATE)
#define Z4_DIR_READ() READ(Z4_DIR_PIN)
#else
#define Z4_DIR_INIT() NOOP
#define Z4_DIR_WRITE(STATE) L64XX_DIR_WRITE(Z4, STATE)
#define Z4_DIR_READ() (stepper##Z4.getStatus() & STATUS_DIR);
#endif
#endif
#if AXIS_DRIVER_TYPE_Z4(L6470)
#define DISABLE_STEPPER_Z4() stepperZ4.free()
#endif
// I Stepper
#if AXIS_IS_L64XX(I)
extern L64XX_CLASS(I) stepperI;
#define I_ENABLE_INIT() NOOP
#define I_ENABLE_WRITE(STATE) (STATE ? stepperI.hardStop() : stepperI.free())
#define I_ENABLE_READ() (stepperI.getStatus() & STATUS_HIZ)
#if AXIS_DRIVER_TYPE_I(L6474)
#define I_DIR_INIT() SET_OUTPUT(I_DIR_PIN)
#define I_DIR_WRITE(STATE) L6474_DIR_WRITE(I, STATE)
#define I_DIR_READ() READ(I_DIR_PIN)
#else
#define I_DIR_INIT() NOOP
#define I_DIR_WRITE(STATE) L64XX_DIR_WRITE(I, STATE)
#define I_DIR_READ() (stepper##I.getStatus() & STATUS_DIR);
#if AXIS_DRIVER_TYPE_I(L6470)
#define DISABLE_STEPPER_I() stepperI.free()
#endif
#endif
#endif
// J Stepper
#if AXIS_IS_L64XX(J)
extern L64XX_CLASS(J) stepperJ;
#define J_ENABLE_INIT() NOOP
#define J_ENABLE_WRITE(STATE) (STATE ? stepperJ.hardStop() : stepperJ.free())
#define J_ENABLE_READ() (stepperJ.getStatus() & STATUS_HIZ)
#if AXIS_DRIVER_TYPE_J(L6474)
#define J_DIR_INIT() SET_OUTPUT(J_DIR_PIN)
#define J_DIR_WRITE(STATE) L6474_DIR_WRITE(J, STATE)
#define J_DIR_READ() READ(J_DIR_PIN)
#else
#define J_DIR_INIT() NOOP
#define J_DIR_WRITE(STATE) L64XX_DIR_WRITE(J, STATE)
#define J_DIR_READ() (stepper##J.getStatus() & STATUS_DIR);
#if AXIS_DRIVER_TYPE_J(L6470)
#define DISABLE_STEPPER_J() stepperJ.free()
#endif
#endif
#endif
// K Stepper
#if AXIS_IS_L64XX(K)
extern L64XX_CLASS(K) stepperK;
#define K_ENABLE_INIT() NOOP
#define K_ENABLE_WRITE(STATE) (STATE ? stepperK.hardStop() : stepperK.free())
#define K_ENABLE_READ() (stepperK.getStatus() & STATUS_HIZ)
#if AXIS_DRIVER_TYPE_K(L6474)
#define K_DIR_INIT() SET_OUTPUT(K_DIR_PIN)
#define K_DIR_WRITE(STATE) L6474_DIR_WRITE(K, STATE)
#define K_DIR_READ() READ(K_DIR_PIN)
#else
#define K_DIR_INIT() NOOP
#define K_DIR_WRITE(STATE) L64XX_DIR_WRITE(K, STATE)
#define K_DIR_READ() (stepper##K.getStatus() & STATUS_DIR);
#if AXIS_DRIVER_TYPE_K(L6470)
#define DISABLE_STEPPER_K() stepperK.free()
#endif
#endif
#endif
// U Stepper
#if HAS_U_AXIS
#if AXIS_IS_L64XX(U)
extern L64XX_CLASS(U) stepperU;
#define U_ENABLE_INIT() NOOP
#define U_ENABLE_WRITE(STATE) (STATE ? stepperU.hardStop() : stepperU.free())
#define U_ENABLE_READ() (stepperU.getStatus() & STATUS_HIZ)
#if AXIS_DRIVER_TYPE_U(L6474)
#define U_DIR_INIT() SET_OUTPUT(U_DIR_PIN)
#define U_DIR_WRITE(STATE) L6474_DIR_WRITE(U, STATE)
#define U_DIR_READ() READ(U_DIR_PIN)
#else
#define U_DIR_INIT() NOOP
#define U_DIR_WRITE(STATE) L64XX_DIR_WRITE(U, STATE)
#define U_DIR_READ() (stepper##U.getStatus() & STATUS_DIR);
#if AXIS_DRIVER_TYPE_U(L6470)
#define DISABLE_STEPPER_U() stepperU.free()
#endif
#endif
#endif
#endif
// V Stepper
#if HAS_V_AXIS
#if AXIS_IS_L64XX(V)
extern L64XX_CLASS(V) stepperV;
#define V_ENABLE_INIT() NOOP
#define V_ENABLE_WRITE(STATE) (STATE ? stepperV.hardStop() : stepperV.free())
#define V_ENABLE_READ() (stepperV.getStatus() & STATUS_HIZ)
#if AXIS_DRIVER_TYPE_V(L6474)
#define V_DIR_INIT() SET_OUTPUT(V_DIR_PIN)
#define V_DIR_WRITE(STATE) L6474_DIR_WRITE(V, STATE)
#define V_DIR_READ() READ(V_DIR_PIN)
#else
#define V_DIR_INIT() NOOP
#define V_DIR_WRITE(STATE) L64XX_DIR_WRITE(V, STATE)
#define V_DIR_READ() (stepper##V.getStatus() & STATUS_DIR);
#if AXIS_DRIVER_TYPE_V(L6470)
#define DISABLE_STEPPER_V() stepperV.free()
#endif
#endif
#endif
#endif
// W Stepper
#if HAS_W_AXIS
#if AXIS_IS_L64XX(W)
extern L64XX_CLASS(w) stepperW;
#define W_ENABLE_INIT() NOOP
#define W_ENABLE_WRITE(STATE) (STATE ? stepperW.hardStop() : stepperW.free())
#define W_ENABLE_READ() (stepperW.getStatus() & STATUS_HIZ)
#if AXIS_DRIVER_TYPE_W(L6474)
#define W_DIR_INIT() SET_OUTPUT(W_DIR_PIN)
#define W_DIR_WRITE(STATE) L6474_DIR_WRITE(W, STATE)
#define W_DIR_READ() READ(W_DIR_PIN)
#else
#define W_DIR_INIT() NOOP
#define W_DIR_WRITE(STATE) L64XX_DIR_WRITE(W, STATE)
#define W_DIR_READ() (stepper##W.getStatus() & STATUS_DIR);
#if AXIS_DRIVER_TYPE_W(L6470)
#define DISABLE_STEPPER_W() stepperW.free()
#endif
#endif
#endif
#endif
// E0 Stepper
#if AXIS_IS_L64XX(E0)
extern L64XX_CLASS(E0) stepperE0;
#define E0_ENABLE_INIT() NOOP
#define E0_ENABLE_WRITE(STATE) (STATE ? stepperE0.hardStop() : stepperE0.free())
#define E0_ENABLE_READ() (stepperE0.getStatus() & STATUS_HIZ)
#if AXIS_DRIVER_TYPE_E0(L6474)
#define E0_DIR_INIT() SET_OUTPUT(E0_DIR_PIN)
#define E0_DIR_WRITE(STATE) L6474_DIR_WRITE(E0, STATE)
#define E0_DIR_READ() READ(E0_DIR_PIN)
#else
#define E0_DIR_INIT() NOOP
#define E0_DIR_WRITE(STATE) L64XX_DIR_WRITE(E0, STATE)
#define E0_DIR_READ() (stepper##E0.getStatus() & STATUS_DIR);
#if AXIS_DRIVER_TYPE_E0(L6470)
#define DISABLE_STEPPER_E0() do{ stepperE0.free(); }while(0)
#endif
#endif
#endif
// E1 Stepper
#if AXIS_IS_L64XX(E1)
extern L64XX_CLASS(E1) stepperE1;
#define E1_ENABLE_INIT() NOOP
#define E1_ENABLE_WRITE(STATE) (STATE ? stepperE1.hardStop() : stepperE1.free())
#define E1_ENABLE_READ() (stepperE1.getStatus() & STATUS_HIZ)
#if AXIS_DRIVER_TYPE_E1(L6474)
#define E1_DIR_INIT() SET_OUTPUT(E1_DIR_PIN)
#define E1_DIR_WRITE(STATE) L6474_DIR_WRITE(E1, STATE)
#define E1_DIR_READ() READ(E1_DIR_PIN)
#else
#define E1_DIR_INIT() NOOP
#define E1_DIR_WRITE(STATE) L64XX_DIR_WRITE(E1, STATE)
#define E1_DIR_READ() (stepper##E1.getStatus() & STATUS_DIR);
#if AXIS_DRIVER_TYPE_E1(L6470)
#define DISABLE_STEPPER_E1() do{ stepperE1.free(); }while(0)
#endif
#endif
#endif
// E2 Stepper
#if AXIS_IS_L64XX(E2)
extern L64XX_CLASS(E2) stepperE2;
#define E2_ENABLE_INIT() NOOP
#define E2_ENABLE_WRITE(STATE) (STATE ? stepperE2.hardStop() : stepperE2.free())
#define E2_ENABLE_READ() (stepperE2.getStatus() & STATUS_HIZ)
#if AXIS_DRIVER_TYPE_E2(L6474)
#define E2_DIR_INIT() SET_OUTPUT(E2_DIR_PIN)
#define E2_DIR_WRITE(STATE) L6474_DIR_WRITE(E2, STATE)
#define E2_DIR_READ() READ(E2_DIR_PIN)
#else
#define E2_DIR_INIT() NOOP
#define E2_DIR_WRITE(STATE) L64XX_DIR_WRITE(E2, STATE)
#define E2_DIR_READ() (stepper##E2.getStatus() & STATUS_DIR);
#if AXIS_DRIVER_TYPE_E2(L6470)
#define DISABLE_STEPPER_E2() do{ stepperE2.free(); }while(0)
#endif
#endif
#endif
// E3 Stepper
#if AXIS_IS_L64XX(E3)
extern L64XX_CLASS(E3) stepperE3;
#define E3_ENABLE_INIT() NOOP
#define E3_ENABLE_WRITE(STATE) (STATE ? stepperE3.hardStop() : stepperE3.free())
#define E3_ENABLE_READ() (stepperE3.getStatus() & STATUS_HIZ)
#if AXIS_DRIVER_TYPE_E3(L6474)
#define E3_DIR_INIT() SET_OUTPUT(E3_DIR_PIN)
#define E3_DIR_WRITE(STATE) L6474_DIR_WRITE(E3, STATE)
#define E3_DIR_READ() READ(E3_DIR_PIN)
#else
#define E3_DIR_INIT() NOOP
#define E3_DIR_WRITE(STATE) L64XX_DIR_WRITE(E3, STATE)
#define E3_DIR_READ() (stepper##E3.getStatus() & STATUS_DIR);
#endif
#endif
// E4 Stepper
#if AXIS_IS_L64XX(E4)
extern L64XX_CLASS(E4) stepperE4;
#define E4_ENABLE_INIT() NOOP
#define E4_ENABLE_WRITE(STATE) (STATE ? stepperE4.hardStop() : stepperE4.free())
#define E4_ENABLE_READ() (stepperE4.getStatus() & STATUS_HIZ)
#if AXIS_DRIVER_TYPE_E4(L6474)
#define E4_DIR_INIT() SET_OUTPUT(E4_DIR_PIN)
#define E4_DIR_WRITE(STATE) L6474_DIR_WRITE(E4, STATE)
#define E4_DIR_READ() READ(E4_DIR_PIN)
#else
#define E4_DIR_INIT() NOOP
#define E4_DIR_WRITE(STATE) L64XX_DIR_WRITE(E4, STATE)
#define E4_DIR_READ() (stepper##E4.getStatus() & STATUS_DIR);
#if AXIS_DRIVER_TYPE_E4(L6470)
#define DISABLE_STEPPER_E4() do{ stepperE4.free(); }while(0)
#endif
#endif
#endif
// E5 Stepper
#if AXIS_IS_L64XX(E5)
extern L64XX_CLASS(E5) stepperE5;
#define E5_ENABLE_INIT() NOOP
#define E5_ENABLE_WRITE(STATE) (STATE ? stepperE5.hardStop() : stepperE5.free())
#define E5_ENABLE_READ() (stepperE5.getStatus() & STATUS_HIZ)
#if AXIS_DRIVER_TYPE_E5(L6474)
#define E5_DIR_INIT() SET_OUTPUT(E5_DIR_PIN)
#define E5_DIR_WRITE(STATE) L6474_DIR_WRITE(E5, STATE)
#define E5_DIR_READ() READ(E5_DIR_PIN)
#else
#define E5_DIR_INIT() NOOP
#define E5_DIR_WRITE(STATE) L64XX_DIR_WRITE(E5, STATE)
#define E5_DIR_READ() (stepper##E5.getStatus() & STATUS_DIR);
#if AXIS_DRIVER_TYPE_E5(L6470)
#define DISABLE_STEPPER_E5() do{ stepperE5.free(); }while(0)
#endif
#endif
#endif
// E6 Stepper
#if AXIS_IS_L64XX(E6)
extern L64XX_CLASS(E6) stepperE6;
#define E6_ENABLE_INIT() NOOP
#define E6_ENABLE_WRITE(STATE) (STATE ? stepperE6.hardStop() : stepperE6.free())
#define E6_ENABLE_READ() (stepperE6.getStatus() & STATUS_HIZ)
#if AXIS_DRIVER_TYPE_E6(L6474)
#define E6_DIR_INIT() SET_OUTPUT(E6_DIR_PIN)
#define E6_DIR_WRITE(STATE) L6474_DIR_WRITE(E6, STATE)
#define E6_DIR_READ() READ(E6_DIR_PIN)
#else
#define E6_DIR_INIT() NOOP
#define E6_DIR_WRITE(STATE) L64XX_DIR_WRITE(E6, STATE)
#define E6_DIR_READ() (stepper##E6.getStatus() & STATUS_DIR);
#if AXIS_DRIVER_TYPE_E6(L6470)
#define DISABLE_STEPPER_E6() do{ stepperE6.free(); }while(0)
#endif
#endif
#endif
// E7 Stepper
#if AXIS_IS_L64XX(E7)
extern L64XX_CLASS(E7) stepperE7;
#define E7_ENABLE_INIT() NOOP
#define E7_ENABLE_WRITE(STATE) (STATE ? stepperE7.hardStop() : stepperE7.free())
#define E7_ENABLE_READ() (stepperE7.getStatus() & STATUS_HIZ)
#if AXIS_DRIVER_TYPE_E7(L6474)
#define E7_DIR_INIT() SET_OUTPUT(E7_DIR_PIN)
#define E7_DIR_WRITE(STATE) L6474_DIR_WRITE(E7, STATE)
#define E7_DIR_READ() READ(E7_DIR_PIN)
#else
#define E7_DIR_INIT() NOOP
#define E7_DIR_WRITE(STATE) L64XX_DIR_WRITE(E7, STATE)
#define E7_DIR_READ() (stepper##E7.getStatus() & STATUS_DIR);
#if AXIS_DRIVER_TYPE_E7(L6470)
#define DISABLE_STEPPER_E7() do{ stepperE7.free(); }while(0)
#endif
#endif
#endif

1
Marlin/src/module/stepper/indirection.cpp

@ -38,7 +38,6 @@ void restore_stepper_drivers() {
void reset_stepper_drivers() {
TERN_(HAS_TMC26X, tmc26x_init_to_defaults());
TERN_(HAS_L64XX, L64xxManager.init_to_defaults());
TERN_(HAS_TRINAMIC_CONFIG, reset_trinamic_drivers());
}

4
Marlin/src/module/stepper/indirection.h

@ -32,10 +32,6 @@
#include "../../inc/MarlinConfig.h"
#if HAS_L64XX
#include "L64xx.h"
#endif
#if HAS_TMC26X
#include "TMC26X.h"
#endif

477
Marlin/src/module/temperature.cpp

@ -904,7 +904,7 @@ volatile bool Temperature::raw_temps_ready = false;
temp_hotend[active_extruder].target = 0.0f;
temp_hotend[active_extruder].soft_pwm_amount = 0;
#if HAS_FAN
set_fan_speed(ANY(MPC_FAN_0_ALL_HOTENDS, MPC_FAN_0_ACTIVE_HOTEND) ? 0 : active_extruder, 0);
set_fan_speed(EITHER(MPC_FAN_0_ALL_HOTENDS, MPC_FAN_0_ACTIVE_HOTEND) ? 0 : active_extruder, 0);
planner.sync_fan_speeds(fan_speed);
#endif
@ -922,7 +922,7 @@ volatile bool Temperature::raw_temps_ready = false;
disable_all_heaters();
#if HAS_FAN
zero_fan_speeds();
set_fan_speed(ANY(MPC_FAN_0_ALL_HOTENDS, MPC_FAN_0_ACTIVE_HOTEND) ? 0 : active_extruder, 255);
set_fan_speed(EITHER(MPC_FAN_0_ALL_HOTENDS, MPC_FAN_0_ACTIVE_HOTEND) ? 0 : active_extruder, 255);
planner.sync_fan_speeds(fan_speed);
#endif
const xyz_pos_t tuningpos = MPC_TUNING_POS;
@ -949,7 +949,7 @@ volatile bool Temperature::raw_temps_ready = false;
}
#if HAS_FAN
set_fan_speed(ANY(MPC_FAN_0_ALL_HOTENDS, MPC_FAN_0_ACTIVE_HOTEND) ? 0 : active_extruder, 0);
set_fan_speed(EITHER(MPC_FAN_0_ALL_HOTENDS, MPC_FAN_0_ACTIVE_HOTEND) ? 0 : active_extruder, 0);
planner.sync_fan_speeds(fan_speed);
#endif
@ -1031,7 +1031,7 @@ volatile bool Temperature::raw_temps_ready = false;
total_energy_fan0 += constants.heater_power * hotend.soft_pwm_amount / 127 * MPC_dT + (last_temp - current_temp) * constants.block_heat_capacity;
#if HAS_FAN
else if (ELAPSED(ms, test_end_ms) && !fan0_done) {
set_fan_speed(ANY(MPC_FAN_0_ALL_HOTENDS, MPC_FAN_0_ACTIVE_HOTEND) ? 0 : active_extruder, 255);
set_fan_speed(EITHER(MPC_FAN_0_ALL_HOTENDS, MPC_FAN_0_ACTIVE_HOTEND) ? 0 : active_extruder, 255);
planner.sync_fan_speeds(fan_speed);
settle_end_ms = ms + settle_time;
test_end_ms = settle_end_ms + test_duration;
@ -1318,104 +1318,101 @@ void Temperature::min_temp_error(const heater_id_t heater_id) {
}
#if ANY(PID_DEBUG, PID_BED_DEBUG, PID_CHAMBER_DEBUG)
bool Temperature::pid_debug_flag; // = 0
#define HAS_PID_DEBUG 1
bool Temperature::pid_debug_flag; // = false
#endif
#if HAS_HOTEND
#if HAS_PID_HEATING
float Temperature::get_pid_output_hotend(const uint8_t E_NAME) {
const uint8_t ee = HOTEND_INDEX;
#if ENABLED(PIDTEMP)
#if DISABLED(PID_OPENLOOP)
static hotend_pid_t work_pid[HOTENDS];
static float temp_iState[HOTENDS] = { 0 },
temp_dState[HOTENDS] = { 0 };
static Flags<HOTENDS> pid_reset;
const float pid_error = temp_hotend[ee].target - temp_hotend[ee].celsius;
float pid_output;
if (temp_hotend[ee].target == 0
|| pid_error < -(PID_FUNCTIONAL_RANGE)
|| TERN0(HEATER_IDLE_HANDLER, heater_idle[ee].timed_out)
) {
pid_output = 0;
pid_reset.set(ee);
template<typename TT, int MIN_POW, int MAX_POW>
class PIDRunner {
public:
TT &tempinfo;
__typeof__(TT::pid) work_pid{0};
float temp_iState = 0, temp_dState = 0;
bool pid_reset = true;
PIDRunner(TT &t) : tempinfo(t) { }
float get_pid_output() {
#if ENABLED(PID_OPENLOOP)
return constrain(tempinfo.target, 0, MAX_POW);
#else // !PID_OPENLOOP
const float pid_error = tempinfo.target - tempinfo.celsius;
if (!tempinfo.target || pid_error < -(PID_FUNCTIONAL_RANGE)) {
pid_reset = true;
return 0;
}
else if (pid_error > PID_FUNCTIONAL_RANGE) {
pid_output = PID_MAX;
pid_reset.set(ee);
pid_reset = true;
return MAX_POW;
}
else {
if (pid_reset[ee]) {
temp_iState[ee] = 0.0;
work_pid[ee].Kd = 0.0;
pid_reset.clear(ee);
}
work_pid[ee].Kd = work_pid[ee].Kd + PID_K2 * (PID_PARAM(Kd, ee) * (temp_dState[ee] - temp_hotend[ee].celsius) - work_pid[ee].Kd);
const float max_power_over_i_gain = float(PID_MAX) / PID_PARAM(Ki, ee) - float(MIN_POWER);
temp_iState[ee] = constrain(temp_iState[ee] + pid_error, 0, max_power_over_i_gain);
work_pid[ee].Kp = PID_PARAM(Kp, ee) * pid_error;
work_pid[ee].Ki = PID_PARAM(Ki, ee) * temp_iState[ee];
if (pid_reset) {
pid_reset = false;
temp_iState = 0.0;
work_pid.Kd = 0.0;
}
pid_output = work_pid[ee].Kp + work_pid[ee].Ki + work_pid[ee].Kd + float(MIN_POWER);
const float max_power_over_i_gain = float(MAX_POW) / tempinfo.pid.Ki - float(MIN_POW);
temp_iState = constrain(temp_iState + pid_error, 0, max_power_over_i_gain);
#if ENABLED(PID_EXTRUSION_SCALING)
#if HOTENDS == 1
constexpr bool this_hotend = true;
#else
const bool this_hotend = (ee == active_extruder);
#endif
work_pid[ee].Kc = 0;
if (this_hotend) {
const long e_position = stepper.position(E_AXIS);
if (e_position > pes_e_position) {
lpq[lpq_ptr] = e_position - pes_e_position;
pes_e_position = e_position;
}
else
lpq[lpq_ptr] = 0;
work_pid.Kp = tempinfo.pid.Kp * pid_error;
work_pid.Ki = tempinfo.pid.Ki * temp_iState;
work_pid.Kd = work_pid.Kd + PID_K2 * (tempinfo.pid.Kd * (temp_dState - tempinfo.celsius) - work_pid.Kd);
if (++lpq_ptr >= lpq_len) lpq_ptr = 0;
work_pid[ee].Kc = (lpq[lpq_ptr] * planner.mm_per_step[E_AXIS]) * PID_PARAM(Kc, ee);
pid_output += work_pid[ee].Kc;
}
#endif // PID_EXTRUSION_SCALING
#if ENABLED(PID_FAN_SCALING)
if (fan_speed[active_extruder] > PID_FAN_SCALING_MIN_SPEED) {
work_pid[ee].Kf = PID_PARAM(Kf, ee) + (PID_FAN_SCALING_LIN_FACTOR) * fan_speed[active_extruder];
pid_output += work_pid[ee].Kf;
}
//pid_output -= work_pid[ee].Ki;
//pid_output += work_pid[ee].Ki * work_pid[ee].Kf
#endif // PID_FAN_SCALING
LIMIT(pid_output, 0, PID_MAX);
}
temp_dState[ee] = temp_hotend[ee].celsius;
temp_dState = tempinfo.celsius;
#else // PID_OPENLOOP
return constrain(work_pid.Kp + work_pid.Ki + work_pid.Kd + float(MIN_POW), 0, MAX_POW);
const float pid_output = constrain(temp_hotend[ee].target, 0, PID_MAX);
#endif // !PID_OPENLOOP
}
#endif // PID_OPENLOOP
FORCE_INLINE void debug(const_celsius_float_t c, const_float_t pid_out, FSTR_P const name=nullptr, const int8_t index=-1) {
if (TERN0(HAS_PID_DEBUG, thermalManager.pid_debug_flag)) {
SERIAL_ECHO_START();
if (name) SERIAL_ECHOLNF(name);
if (index >= 0) SERIAL_ECHO(index);
SERIAL_ECHOLNPGM(
STR_PID_DEBUG_INPUT, c,
STR_PID_DEBUG_OUTPUT, pid_out
#if DISABLED(PID_OPENLOOP)
, "pTerm", work_pid.Kp, "iTerm", work_pid.Ki, "dTerm", work_pid.Kd
#endif
);
}
}
};
#endif // HAS_PID_HEATING
#if HAS_HOTEND
float Temperature::get_pid_output_hotend(const uint8_t E_NAME) {
const uint8_t ee = HOTEND_INDEX;
#if ENABLED(PIDTEMP)
typedef PIDRunner<hotend_info_t, 0, PID_MAX> PIDRunnerHotend;
static PIDRunnerHotend hotend_pid[HOTENDS] = {
#define _HOTENDPID(E) temp_hotend[E],
REPEAT(HOTENDS, _HOTENDPID)
};
const float pid_output = hotend_pid[ee].get_pid_output();
#if ENABLED(PID_DEBUG)
if (ee == active_extruder && pid_debug_flag) {
SERIAL_ECHO_MSG(STR_PID_DEBUG, ee, STR_PID_DEBUG_INPUT, temp_hotend[ee].celsius, STR_PID_DEBUG_OUTPUT, pid_output
#if DISABLED(PID_OPENLOOP)
, STR_PID_DEBUG_PTERM, work_pid[ee].Kp
, STR_PID_DEBUG_ITERM, work_pid[ee].Ki
, STR_PID_DEBUG_DTERM, work_pid[ee].Kd
#if ENABLED(PID_EXTRUSION_SCALING)
, STR_PID_DEBUG_CTERM, work_pid[ee].Kc
#endif
#endif
);
}
if (ee == active_extruder)
hotend_pid[ee].debug(temp_hotend[ee].celsius, pid_output, F("E"), ee);
#endif
#elif ENABLED(MPCTEMP)
MPCHeaterInfo &hotend = temp_hotend[ee];
MPC_t &constants = hotend.constants;
@ -1433,7 +1430,7 @@ void Temperature::min_temp_error(const heater_id_t heater_id) {
float ambient_xfer_coeff = constants.ambient_xfer_coeff_fan0;
#if ENABLED(MPC_INCLUDE_FAN)
const uint8_t fan_index = ANY(MPC_FAN_0_ACTIVE_HOTEND, MPC_FAN_0_ALL_HOTENDS) ? 0 : ee;
const uint8_t fan_index = EITHER(MPC_FAN_0_ACTIVE_HOTEND, MPC_FAN_0_ALL_HOTENDS) ? 0 : ee;
const float fan_fraction = TERN_(MPC_FAN_0_ACTIVE_HOTEND, !this_hotend ? 0.0f : ) fan_speed[fan_index] * RECIPROCAL(255);
ambient_xfer_coeff += fan_fraction * constants.fan255_adjustment;
#endif
@ -1497,7 +1494,7 @@ void Temperature::min_temp_error(const heater_id_t heater_id) {
#else // No PID or MPC enabled
const bool is_idling = TERN0(HEATER_IDLE_HANDLER, heater_idle[ee].timed_out);
const float pid_output = (!is_idling && temp_hotend[ee].celsius < temp_hotend[ee].target) ? BANG_MAX : 0;
const float pid_output = (!is_idling && temp_hotend[ee].is_below_target()) ? BANG_MAX : 0;
#endif
@ -1509,61 +1506,9 @@ void Temperature::min_temp_error(const heater_id_t heater_id) {
#if ENABLED(PIDTEMPBED)
float Temperature::get_pid_output_bed() {
#if DISABLED(PID_OPENLOOP)
static PID_t work_pid{0};
static float temp_iState = 0, temp_dState = 0;
static bool pid_reset = true;
float pid_output = 0;
const float max_power_over_i_gain = float(MAX_BED_POWER) / temp_bed.pid.Ki - float(MIN_BED_POWER),
pid_error = temp_bed.target - temp_bed.celsius;
if (!temp_bed.target || pid_error < -(PID_FUNCTIONAL_RANGE)) {
pid_output = 0;
pid_reset = true;
}
else if (pid_error > PID_FUNCTIONAL_RANGE) {
pid_output = MAX_BED_POWER;
pid_reset = true;
}
else {
if (pid_reset) {
temp_iState = 0.0;
work_pid.Kd = 0.0;
pid_reset = false;
}
temp_iState = constrain(temp_iState + pid_error, 0, max_power_over_i_gain);
work_pid.Kp = temp_bed.pid.Kp * pid_error;
work_pid.Ki = temp_bed.pid.Ki * temp_iState;
work_pid.Kd = work_pid.Kd + PID_K2 * (temp_bed.pid.Kd * (temp_dState - temp_bed.celsius) - work_pid.Kd);
temp_dState = temp_bed.celsius;
pid_output = constrain(work_pid.Kp + work_pid.Ki + work_pid.Kd + float(MIN_BED_POWER), 0, MAX_BED_POWER);
}
#else // PID_OPENLOOP
const float pid_output = constrain(temp_bed.target, 0, MAX_BED_POWER);
#endif // PID_OPENLOOP
#if ENABLED(PID_BED_DEBUG)
if (pid_debug_flag) {
SERIAL_ECHO_MSG(
" PID_BED_DEBUG : Input ", temp_bed.celsius, " Output ", pid_output
#if DISABLED(PID_OPENLOOP)
, STR_PID_DEBUG_PTERM, work_pid.Kp
, STR_PID_DEBUG_ITERM, work_pid.Ki
, STR_PID_DEBUG_DTERM, work_pid.Kd
#endif
);
}
#endif
static PIDRunner<bed_info_t, MIN_BED_POWER, MAX_BED_POWER> bed_pid(temp_bed);
const float pid_output = bed_pid.get_pid_output();
TERN_(PID_BED_DEBUG, bed_pid.debug(temp_bed.celsius, pid_output, F("(Bed)")));
return pid_output;
}
@ -1572,114 +1517,17 @@ void Temperature::min_temp_error(const heater_id_t heater_id) {
#if ENABLED(PIDTEMPCHAMBER)
float Temperature::get_pid_output_chamber() {
#if DISABLED(PID_OPENLOOP)
static PID_t work_pid{0};
static float temp_iState = 0, temp_dState = 0;
static bool pid_reset = true;
float pid_output = 0;
const float max_power_over_i_gain = float(MAX_CHAMBER_POWER) / temp_chamber.pid.Ki - float(MIN_CHAMBER_POWER),
pid_error = temp_chamber.target - temp_chamber.celsius;
if (!temp_chamber.target || pid_error < -(PID_FUNCTIONAL_RANGE)) {
pid_output = 0;
pid_reset = true;
}
else if (pid_error > PID_FUNCTIONAL_RANGE) {
pid_output = MAX_CHAMBER_POWER;
pid_reset = true;
}
else {
if (pid_reset) {
temp_iState = 0.0;
work_pid.Kd = 0.0;
pid_reset = false;
}
temp_iState = constrain(temp_iState + pid_error, 0, max_power_over_i_gain);
work_pid.Kp = temp_chamber.pid.Kp * pid_error;
work_pid.Ki = temp_chamber.pid.Ki * temp_iState;
work_pid.Kd = work_pid.Kd + PID_K2 * (temp_chamber.pid.Kd * (temp_dState - temp_chamber.celsius) - work_pid.Kd);
temp_dState = temp_chamber.celsius;
pid_output = constrain(work_pid.Kp + work_pid.Ki + work_pid.Kd + float(MIN_CHAMBER_POWER), 0, MAX_CHAMBER_POWER);
}
#else // PID_OPENLOOP
const float pid_output = constrain(temp_chamber.target, 0, MAX_CHAMBER_POWER);
#endif // PID_OPENLOOP
#if ENABLED(PID_CHAMBER_DEBUG)
{
SERIAL_ECHO_MSG(
" PID_CHAMBER_DEBUG : Input ", temp_chamber.celsius, " Output ", pid_output
#if DISABLED(PID_OPENLOOP)
, STR_PID_DEBUG_PTERM, work_pid.Kp
, STR_PID_DEBUG_ITERM, work_pid.Ki
, STR_PID_DEBUG_DTERM, work_pid.Kd
#endif
);
}
#endif
static PIDRunner<chamber_info_t, MIN_CHAMBER_POWER, MAX_CHAMBER_POWER> chamber_pid(temp_chamber);
const float pid_output = chamber_pid.get_pid_output();
TERN_(PID_CHAMBER_DEBUG, chamber_pid.debug(temp_chamber.celsius, pid_output, F("(Chamber)")));
return pid_output;
}
#endif // PIDTEMPCHAMBER
/**
* Manage heating activities for extruder hot-ends and a heated bed
* - Acquire updated temperature readings
* - Also resets the watchdog timer
* - Invoke thermal runaway protection
* - Manage extruder auto-fan
* - Apply filament width to the extrusion rate (may move)
* - Update the heated bed PID output value
*/
void Temperature::manage_heater() {
if (marlin_state == MF_INITIALIZING) return hal.watchdog_refresh(); // If Marlin isn't started, at least reset the watchdog!
static bool no_reentry = false; // Prevent recursion
if (no_reentry) return;
REMEMBER(mh, no_reentry, true);
#if ENABLED(EMERGENCY_PARSER)
if (emergency_parser.killed_by_M112) kill(FPSTR(M112_KILL_STR), nullptr, true);
if (emergency_parser.quickstop_by_M410) {
emergency_parser.quickstop_by_M410 = false; // quickstop_stepper may call idle so clear this now!
quickstop_stepper();
}
#endif
if (!updateTemperaturesIfReady()) return; // Will also reset the watchdog if temperatures are ready
#if DISABLED(IGNORE_THERMOCOUPLE_ERRORS)
#if TEMP_SENSOR_0_IS_MAX_TC
if (degHotend(0) > _MIN(HEATER_0_MAXTEMP, TEMP_SENSOR_0_MAX_TC_TMAX - 1.0)) max_temp_error(H_E0);
if (degHotend(0) < _MAX(HEATER_0_MINTEMP, TEMP_SENSOR_0_MAX_TC_TMIN + .01)) min_temp_error(H_E0);
#endif
#if TEMP_SENSOR_1_IS_MAX_TC
if (degHotend(1) > _MIN(HEATER_1_MAXTEMP, TEMP_SENSOR_1_MAX_TC_TMAX - 1.0)) max_temp_error(H_E1);
if (degHotend(1) < _MAX(HEATER_1_MINTEMP, TEMP_SENSOR_1_MAX_TC_TMIN + .01)) min_temp_error(H_E1);
#endif
#if TEMP_SENSOR_REDUNDANT_IS_MAX_TC
if (degRedundant() > TEMP_SENSOR_REDUNDANT_MAX_TC_TMAX - 1.0) max_temp_error(H_REDUNDANT);
if (degRedundant() < TEMP_SENSOR_REDUNDANT_MAX_TC_TMIN + .01) min_temp_error(H_REDUNDANT);
#endif
#else
#warning "Safety Alert! Disable IGNORE_THERMOCOUPLE_ERRORS for the final build!"
#endif
millis_t ms = millis();
#if HAS_HOTEND
#if HAS_HOTEND
void Temperature::manage_hotends(const millis_t &ms) {
HOTEND_LOOP() {
#if ENABLED(THERMAL_PROTECTION_HOTENDS)
if (degHotend(e) > temp_range[e].maxtemp) max_temp_error((heater_id_t)e);
@ -1707,25 +1555,13 @@ void Temperature::manage_heater() {
#endif
} // HOTEND_LOOP
}
#endif // HAS_HOTEND
#if HAS_TEMP_REDUNDANT
// Make sure measured temperatures are close together
if (ABS(degRedundantTarget() - degRedundant()) > TEMP_SENSOR_REDUNDANT_MAX_DIFF)
_temp_error((heater_id_t)HEATER_ID(TEMP_SENSOR_REDUNDANT_TARGET), F(STR_REDUNDANCY), GET_TEXT_F(MSG_ERR_REDUNDANT_TEMP));
#endif
// Manage extruder auto fans and/or read fan tachometers
TERN_(HAS_FAN_LOGIC, manage_extruder_fans(ms));
#endif // HAS_HOTEND
/**
* Dynamically set the volumetric multiplier based
* on the delayed Filament Width measurement.
*/
TERN_(FILAMENT_WIDTH_SENSOR, filwidth.update_volumetric());
#if HAS_HEATED_BED
#if HAS_HEATED_BED
void Temperature::manage_heated_bed(const millis_t &ms) {
#if ENABLED(THERMAL_PROTECTION_BED)
if (degBed() > BED_MAXTEMP) max_temp_error(H_BED);
@ -1770,9 +1606,7 @@ void Temperature::manage_heater() {
#if HEATER_IDLE_HANDLER
if (heater_idle[IDLE_INDEX_BED].timed_out) {
temp_bed.soft_pwm_amount = 0;
#if DISABLED(PIDTEMPBED)
WRITE_HEATER_BED(LOW);
#endif
if (DISABLED(PIDTEMPBED)) WRITE_HEATER_BED(LOW);
}
else
#endif
@ -1785,10 +1619,10 @@ void Temperature::manage_heater() {
#if ENABLED(BED_LIMIT_SWITCHING)
if (temp_bed.celsius >= temp_bed.target + BED_HYSTERESIS)
temp_bed.soft_pwm_amount = 0;
else if (temp_bed.celsius <= temp_bed.target - (BED_HYSTERESIS))
else if (temp_bed.is_below_target(-(BED_HYSTERESIS) + 1))
temp_bed.soft_pwm_amount = MAX_BED_POWER >> 1;
#else // !PIDTEMPBED && !BED_LIMIT_SWITCHING
temp_bed.soft_pwm_amount = temp_bed.celsius < temp_bed.target ? MAX_BED_POWER >> 1 : 0;
temp_bed.soft_pwm_amount = temp_bed.is_below_target() ? MAX_BED_POWER >> 1 : 0;
#endif
}
else {
@ -1799,10 +1633,13 @@ void Temperature::manage_heater() {
}
} while (false);
}
#endif // HAS_HEATED_BED
#endif // HAS_HEATED_BED
#if HAS_HEATED_CHAMBER
#if HAS_HEATED_CHAMBER
void Temperature::manage_heated_chamber(const millis_t &ms) {
#ifndef CHAMBER_CHECK_INTERVAL
#define CHAMBER_CHECK_INTERVAL 1000UL
@ -1897,17 +1734,17 @@ void Temperature::manage_heater() {
if (flag_chamber_excess_heat) {
temp_chamber.soft_pwm_amount = 0;
#if ENABLED(CHAMBER_VENT)
if (!flag_chamber_off) servo[CHAMBER_VENT_SERVO_NR].move(temp_chamber.celsius <= temp_chamber.target ? 0 : 90);
if (!flag_chamber_off) servo[CHAMBER_VENT_SERVO_NR].move(temp_chamber.is_below_target() ? 0 : 90);
#endif
}
else {
#if ENABLED(CHAMBER_LIMIT_SWITCHING)
if (temp_chamber.celsius >= temp_chamber.target + TEMP_CHAMBER_HYSTERESIS)
temp_chamber.soft_pwm_amount = 0;
else if (temp_chamber.celsius <= temp_chamber.target - (TEMP_CHAMBER_HYSTERESIS))
else if (temp_chamber.is_below_target(-(TEMP_CHAMBER_HYSTERESIS) + 1))
temp_chamber.soft_pwm_amount = (MAX_CHAMBER_POWER) >> 1;
#else
temp_chamber.soft_pwm_amount = temp_chamber.celsius < temp_chamber.target ? (MAX_CHAMBER_POWER) >> 1 : 0;
temp_chamber.soft_pwm_amount = temp_chamber.is_below_target() ? (MAX_CHAMBER_POWER) >> 1 : 0;
#endif
#if ENABLED(CHAMBER_VENT)
if (!flag_chamber_off) servo[CHAMBER_VENT_SERVO_NR].move(0);
@ -1923,10 +1760,13 @@ void Temperature::manage_heater() {
tr_state_machine[RUNAWAY_IND_CHAMBER].run(temp_chamber.celsius, temp_chamber.target, H_CHAMBER, THERMAL_PROTECTION_CHAMBER_PERIOD, THERMAL_PROTECTION_CHAMBER_HYSTERESIS);
#endif
#endif
}
#endif // HAS_HEATED_CHAMBER
#endif // HAS_HEATED_CHAMBER
#if HAS_COOLER
#if HAS_COOLER
void Temperature::manage_cooler(const millis_t &ms) {
#ifndef COOLER_CHECK_INTERVAL
#define COOLER_CHECK_INTERVAL 2000UL
@ -1984,15 +1824,90 @@ void Temperature::manage_heater() {
#if ENABLED(THERMAL_PROTECTION_COOLER)
tr_state_machine[RUNAWAY_IND_COOLER].run(temp_cooler.celsius, temp_cooler.target, H_COOLER, THERMAL_PROTECTION_COOLER_PERIOD, THERMAL_PROTECTION_COOLER_HYSTERESIS);
#endif
}
#endif // HAS_COOLER
#endif // HAS_COOLER
/**
* Manage heating activities for extruder hot-ends and a heated bed
* - Acquire updated temperature readings
* - Also resets the watchdog timer
* - Invoke thermal runaway protection
* - Manage extruder auto-fan
* - Apply filament width to the extrusion rate (may move)
* - Update the heated bed PID output value
*/
void Temperature::task() {
if (marlin_state == MF_INITIALIZING) return hal.watchdog_refresh(); // If Marlin isn't started, at least reset the watchdog!
static bool no_reentry = false; // Prevent recursion
if (no_reentry) return;
REMEMBER(mh, no_reentry, true);
#if ENABLED(EMERGENCY_PARSER)
if (emergency_parser.killed_by_M112) kill(FPSTR(M112_KILL_STR), nullptr, true);
if (emergency_parser.quickstop_by_M410) {
emergency_parser.quickstop_by_M410 = false; // quickstop_stepper may call idle so clear this now!
quickstop_stepper();
}
#endif
if (!updateTemperaturesIfReady()) return; // Will also reset the watchdog if temperatures are ready
#if DISABLED(IGNORE_THERMOCOUPLE_ERRORS)
#if TEMP_SENSOR_0_IS_MAX_TC
if (degHotend(0) > _MIN(HEATER_0_MAXTEMP, TEMP_SENSOR_0_MAX_TC_TMAX - 1.0)) max_temp_error(H_E0);
if (degHotend(0) < _MAX(HEATER_0_MINTEMP, TEMP_SENSOR_0_MAX_TC_TMIN + .01)) min_temp_error(H_E0);
#endif
#if TEMP_SENSOR_1_IS_MAX_TC
if (degHotend(1) > _MIN(HEATER_1_MAXTEMP, TEMP_SENSOR_1_MAX_TC_TMAX - 1.0)) max_temp_error(H_E1);
if (degHotend(1) < _MAX(HEATER_1_MINTEMP, TEMP_SENSOR_1_MAX_TC_TMIN + .01)) min_temp_error(H_E1);
#endif
#if TEMP_SENSOR_REDUNDANT_IS_MAX_TC
if (degRedundant() > TEMP_SENSOR_REDUNDANT_MAX_TC_TMAX - 1.0) max_temp_error(H_REDUNDANT);
if (degRedundant() < TEMP_SENSOR_REDUNDANT_MAX_TC_TMIN + .01) min_temp_error(H_REDUNDANT);
#endif
#else
#warning "Safety Alert! Disable IGNORE_THERMOCOUPLE_ERRORS for the final build!"
#endif
const millis_t ms = millis();
// Handle Hotend Temp Errors, Heating Watch, etc.
TERN_(HAS_HOTEND, manage_hotends(ms));
#if HAS_TEMP_REDUNDANT
// Make sure measured temperatures are close together
if (ABS(degRedundantTarget() - degRedundant()) > TEMP_SENSOR_REDUNDANT_MAX_DIFF)
_temp_error((heater_id_t)HEATER_ID(TEMP_SENSOR_REDUNDANT_TARGET), F(STR_REDUNDANCY), GET_TEXT_F(MSG_ERR_REDUNDANT_TEMP));
#endif
// Manage extruder auto fans and/or read fan tachometers
TERN_(HAS_FAN_LOGIC, manage_extruder_fans(ms));
/**
* Dynamically set the volumetric multiplier based
* on the delayed Filament Width measurement.
*/
TERN_(FILAMENT_WIDTH_SENSOR, filwidth.update_volumetric());
// Handle Bed Temp Errors, Heating Watch, etc.
TERN_(HAS_HEATED_BED, manage_heated_bed(ms));
// Handle Heated Chamber Temp Errors, Heating Watch, etc.
TERN_(HAS_HEATED_CHAMBER, manage_heated_chamber(ms));
// Handle Cooler Temp Errors, Cooling Watch, etc.
TERN_(HAS_COOLER, manage_cooler(ms));
#if ENABLED(LASER_COOLANT_FLOW_METER)
cooler.flowmeter_task(ms);
#if ENABLED(FLOWMETER_SAFETY)
if (cutter.enabled() && cooler.check_flow_too_low()) {
if (cooler.check_flow_too_low()) {
TERN_(HAS_DISPLAY, if (cutter.enabled()) ui.flow_fault());
cutter.disable();
TERN_(HAS_DISPLAY, ui.flow_fault());
cutter.cutter_mode = CUTTER_MODE_ERROR; // Immediately kill stepper inline power output
}
#endif
#endif
@ -2479,7 +2394,7 @@ void Temperature::updateTemperaturesFromRawValues() {
/**
* Initialize the temperature manager
*
* The manager is implemented by periodic calls to manage_heater()
* The manager is implemented by periodic calls to task()
*
* - Init (and disable) SPI thermocouples like MAX6675 and MAX31865
* - Disable RUMBA JTAG to accommodate a thermocouple extension
@ -3111,7 +3026,7 @@ void Temperature::disable_all_heaters() {
static millis_t next_max_tc_ms[MAX_TC_COUNT] = { 0 };
// Return last-read value between readings
millis_t ms = millis();
const millis_t ms = millis();
if (PENDING(ms, next_max_tc_ms[hindex]))
return THERMO_TEMP(hindex);
@ -3419,16 +3334,18 @@ void Temperature::isr() {
_PWM_MOD(COOLER, soft_pwm_cooler, temp_cooler);
#endif
#if BOTH(USE_CONTROLLER_FAN, FAN_SOFT_PWM)
WRITE(CONTROLLER_FAN_PIN, soft_pwm_controller.add(pwm_mask, soft_pwm_controller_speed));
#endif
#if ENABLED(FAN_SOFT_PWM)
#if ENABLED(USE_CONTROLLER_FAN)
WRITE(CONTROLLER_FAN_PIN, soft_pwm_controller.add(pwm_mask, soft_pwm_controller_speed));
#endif
#define _FAN_PWM(N) do{ \
uint8_t &spcf = soft_pwm_count_fan[N]; \
spcf = (spcf & pwm_mask) + (soft_pwm_amount_fan[N] >> 1); \
WRITE_FAN(N, spcf > pwm_mask ? HIGH : LOW); \
}while(0)
#if HAS_FAN0
_FAN_PWM(0);
#endif

16
Marlin/src/module/temperature.h

@ -232,6 +232,7 @@ public:
typedef struct HeaterInfo : public TempInfo {
celsius_t target;
uint8_t soft_pwm_amount;
bool is_below_target(const celsius_t offs=0) const { return (celsius < (target + offs)); }
} heater_info_t;
// A heater with PID stabilization
@ -715,9 +716,9 @@ class Temperature {
static void readings_ready();
/**
* Call periodically to manage heaters
* Call periodically to manage heaters and keep the watchdog fed
*/
static void manage_heater() __O2; // __O2 added to work around a compiler error
static void task();
/**
* Preheating hotends
@ -807,6 +808,8 @@ class Temperature {
#endif
}
static void manage_hotends(const millis_t &ms);
#endif // HAS_HOTEND
#if HAS_HEATED_BED
@ -819,6 +822,9 @@ class Temperature {
static celsius_t degTargetBed() { return temp_bed.target; }
static bool isHeatingBed() { return temp_bed.target > temp_bed.celsius; }
static bool isCoolingBed() { return temp_bed.target < temp_bed.celsius; }
static bool degBedNear(const celsius_t temp) {
return ABS(wholeDegBed() - temp) < (TEMP_BED_HYSTERESIS);
}
// Start watching the Bed to make sure it's really heating up
static void start_watching_bed() { TERN_(WATCH_BED, watch_bed.restart(degBed(), degTargetBed())); }
@ -835,9 +841,7 @@ class Temperature {
static void wait_for_bed_heating();
static bool degBedNear(const celsius_t temp) {
return ABS(wholeDegBed() - temp) < (TEMP_BED_HYSTERESIS);
}
static void manage_heated_bed(const millis_t &ms);
#endif // HAS_HEATED_BED
@ -863,6 +867,7 @@ class Temperature {
static bool isHeatingChamber() { return temp_chamber.target > temp_chamber.celsius; }
static bool isCoolingChamber() { return temp_chamber.target < temp_chamber.celsius; }
static bool wait_for_chamber(const bool no_wait_for_cooling=true);
static void manage_heated_chamber(const millis_t &ms);
#endif
#endif
@ -886,6 +891,7 @@ class Temperature {
static bool isLaserHeating() { return temp_cooler.target > temp_cooler.celsius; }
static bool isLaserCooling() { return temp_cooler.target < temp_cooler.celsius; }
static bool wait_for_cooler(const bool no_wait_for_cooling=true);
static void manage_cooler(const millis_t &ms);
#endif
#endif

2
Marlin/src/pins/linux/pins_RAMPS_LINUX.h

@ -582,7 +582,7 @@
#define LCD_SDSS SDSS
#define SD_DETECT_PIN 49
#elif ANY(VIKI2, miniVIKI)
#elif EITHER(VIKI2, miniVIKI)
#define DOGLCD_CS 45
#define DOGLCD_A0 44

2
Marlin/src/pins/lpc1768/pins_MKS_SBASE.h

@ -238,7 +238,7 @@
#define LCD_SDSS P0_28 // EXP2.4
#define LCD_PINS_ENABLE P0_18 // EXP1.3
#define LCD_PINS_D4 P0_15 // EXP1.5
#if ANY(VIKI2, miniVIKI)
#if EITHER(VIKI2, miniVIKI)
#define DOGLCD_SCK SD_SCK_PIN
#define DOGLCD_MOSI SD_MOSI_PIN
#endif

2
Marlin/src/pins/lpc1768/pins_RAMPS_RE_ARM.h

@ -351,7 +351,7 @@
//#define SHIFT_EN_PIN P1_22 // (41) J5-4 & AUX-4
#endif
#if ANY(VIKI2, miniVIKI)
#if EITHER(VIKI2, miniVIKI)
#define DOGLCD_CS P0_16 // (16)
#define DOGLCD_A0 P2_06 // (59) J3-8 & AUX-2
#define DOGLCD_SCK SD_SCK_PIN

2
Marlin/src/pins/lpc1769/pins_AZTEEG_X5_GT.h

@ -107,7 +107,7 @@
// Display
//
#if ANY(VIKI2, miniVIKI)
#if EITHER(VIKI2, miniVIKI)
#define BEEPER_PIN P1_31
#define DOGLCD_A0 P2_06
#define DOGLCD_CS P0_16

2
Marlin/src/pins/lpc1769/pins_AZTEEG_X5_MINI.h

@ -154,7 +154,7 @@
//#define SHIFT_EN_PIN P1_22 // (41) J5-4 & AUX-4
#endif
#if ANY(VIKI2, miniVIKI)
#if EITHER(VIKI2, miniVIKI)
#define BEEPER_PIN P1_30 // (37) may change if cable changes
#define DOGLCD_CS P0_26 // (63) J5-3 & AUX-2
#define DOGLCD_SCK SD_SCK_PIN

2
Marlin/src/pins/lpc1769/pins_SMOOTHIEBOARD.h

@ -91,7 +91,7 @@
//
// LCD / Controller
//
#if ANY(VIKI2, miniVIKI)
#if EITHER(VIKI2, miniVIKI)
#define BEEPER_PIN P1_31
#define DOGLCD_A0 P2_11

36
Marlin/src/pins/pins.h

@ -567,6 +567,8 @@
#include "stm32f1/pins_CREALITY_V4.h" // STM32F1 env:STM32F103RE_creality env:STM32F103RE_creality_xfer env:STM32F103RC_creality env:STM32F103RC_creality_xfer env:STM32F103RE_creality_maple
#elif MB(CREALITY_V4210)
#include "stm32f1/pins_CREALITY_V4210.h" // STM32F1 env:STM32F103RE_creality env:STM32F103RE_creality_xfer env:STM32F103RC_creality env:STM32F103RC_creality_xfer env:STM32F103RE_creality_maple
#elif MB(CREALITY_V425)
#include "stm32f1/pins_CREALITY_V425.h" // STM32F1 env:STM32F103RE_creality env:STM32F103RE_creality_xfer env:STM32F103RC_creality env:STM32F103RC_creality_xfer env:STM32F103RE_creality_maple
#elif MB(CREALITY_V422)
#include "stm32f1/pins_CREALITY_V422.h" // STM32F1 env:STM32F103RE_creality env:STM32F103RE_creality_xfer env:STM32F103RC_creality env:STM32F103RC_creality_xfer env:STM32F103RE_creality_maple
#elif MB(CREALITY_V423)
@ -629,8 +631,6 @@
#include "stm32f4/pins_RUMBA32_BTT.h" // STM32F4 env:rumba32
#elif MB(BLACK_STM32F407VE)
#include "stm32f4/pins_BLACK_STM32F407VE.h" // STM32F4 env:STM32F407VE_black
#elif MB(STEVAL_3DP001V1)
#include "stm32f4/pins_STEVAL_3DP001V1.h" // STM32F4 env:STM32F401VE_STEVAL
#elif MB(BTT_SKR_PRO_V1_1)
#include "stm32f4/pins_BTT_SKR_PRO_V1_1.h" // STM32F4 env:BIGTREE_SKR_PRO env:BIGTREE_SKR_PRO_usb_flash_drive
#elif MB(BTT_SKR_PRO_V1_2)
@ -678,15 +678,17 @@
#elif MB(MKS_ROBIN_NANO_V3_1)
#include "stm32f4/pins_MKS_ROBIN_NANO_V3.h" // STM32F4 env:mks_robin_nano_v3_1 env:mks_robin_nano_v3_1_usb_flash_drive env:mks_robin_nano_v3_1_usb_flash_drive_msc
#elif MB(ANET_ET4)
#include "stm32f4/pins_ANET_ET4.h" // STM32F4 env:Anet_ET4_OpenBLT
#include "stm32f4/pins_ANET_ET4.h" // STM32F4 env:Anet_ET4_no_bootloader env:Anet_ET4_OpenBLT
#elif MB(ANET_ET4P)
#include "stm32f4/pins_ANET_ET4P.h" // STM32F4 env:Anet_ET4_OpenBLT
#include "stm32f4/pins_ANET_ET4P.h" // STM32F4 env:Anet_ET4_no_bootloader env:Anet_ET4_OpenBLT
#elif MB(FYSETC_CHEETAH_V20)
#include "stm32f4/pins_FYSETC_CHEETAH_V20.h" // STM32F4 env:FYSETC_CHEETAH_V20
#elif MB(MKS_MONSTER8)
#include "stm32f4/pins_MKS_MONSTER8.h" // STM32F4 env:mks_monster8 env:mks_monster8_usb_flash_drive env:mks_monster8_usb_flash_drive_msc
#elif MB(MKS_MONSTER8_V1)
#include "stm32f4/pins_MKS_MONSTER8_V1.h" // STM32F4 env:mks_monster8 env:mks_monster8_usb_flash_drive env:mks_monster8_usb_flash_drive_msc
#elif MB(MKS_MONSTER8_V2)
#include "stm32f4/pins_MKS_MONSTER8_V2.h" // STM32F4 env:mks_monster8 env:mks_monster8_usb_flash_drive env:mks_monster8_usb_flash_drive_msc
#elif MB(TH3D_EZBOARD_V2)
#include "stm32f4/pins_TH3D_EZBOARD_V2.h" // STM32F4 env:TH3D_EZBoard_V2
#include "stm32f4/pins_TH3D_EZBOARD_V2.h" // STM32F4 env:TH3D_EZBoard_V2_no_bootloader env:TH3D_EZBoard_V2_OpenBLT
#elif MB(OPULO_LUMEN_REV3)
#include "stm32f4/pins_OPULO_LUMEN_REV3.h" // STM32F4 env:Opulo_Lumen_REV3
#elif MB(MKS_ROBIN_NANO_V1_3_F4)
@ -706,8 +708,10 @@
#include "stm32f7/pins_REMRAM_V1.h" // STM32F7 env:REMRAM_V1
#elif MB(NUCLEO_F767ZI)
#include "stm32f7/pins_NUCLEO_F767ZI.h" // STM32F7 env:NUCLEO_F767ZI
#elif MB(BTT_SKR_SE_BX)
#include "stm32h7/pins_BTT_SKR_SE_BX.h" // STM32H7 env:BTT_SKR_SE_BX
#elif MB(BTT_SKR_SE_BX_V2)
#include "stm32h7/pins_BTT_SKR_SE_BX_V2.h" // STM32H7 env:BTT_SKR_SE_BX
#elif MB(BTT_SKR_SE_BX_V3)
#include "stm32h7/pins_BTT_SKR_SE_BX_V3.h" // STM32H7 env:BTT_SKR_SE_BX
#elif MB(BTT_SKR_V3_0)
#include "stm32h7/pins_BTT_SKR_V3_0.h" // STM32H7 env:STM32H743Vx_btt
#elif MB(BTT_SKR_V3_0_EZ)
@ -782,6 +786,7 @@
#define BOARD_STM32F103R 99906
#define BOARD_ESP32 99907
#define BOARD_STEVAL 99908
#define BOARD_STEVAL_3DP001V1 99908
#define BOARD_BIGTREE_SKR_V1_1 99909
#define BOARD_BIGTREE_SKR_V1_3 99910
#define BOARD_BIGTREE_SKR_V1_4 99911
@ -797,6 +802,8 @@
#define BOARD_RAMPS_LONGER3D_LK4PRO 99921
#define BOARD_BTT_SKR_V2_0 99922
#define BOARD_TH3D_EZBOARD_LITE_V2 99923
#define BOARD_BTT_SKR_SE_BX 99924
#define BOARD_MKS_MONSTER8 99925
#if MB(MKS_13)
#error "BOARD_MKS_13 has been renamed BOARD_MKS_GEN_13. Please update your configuration."
@ -835,7 +842,7 @@
#elif MOTHERBOARD == BOARD_ESP32
#error "BOARD_ESP32 has been renamed BOARD_ESPRESSIF_ESP32. Please update your configuration."
#elif MB(STEVAL)
#error "BOARD_STEVAL has been renamed BOARD_STEVAL_3DP001V1. Please update your configuration."
#error "BOARD_STEVAL_3DP001V1 (BOARD_STEVAL) is no longer supported in Marlin."
#elif MB(RUMBA32)
#error "BOARD_RUMBA32 is now BOARD_RUMBA32_MKS or BOARD_RUMBA32_V1_0. Please update your configuration."
#elif MB(RUMBA32_AUS3D)
@ -848,6 +855,10 @@
#error "BOARD_BTT_SKR_V2_0 is now BOARD_BTT_SKR_V2_0_REV_A or BOARD_BTT_SKR_V2_0_REV_B. See https://bit.ly/3t5d9JQ for more information. Please update your configuration."
#elif MB(TH3D_EZBOARD_LITE_V2)
#error "BOARD_TH3D_EZBOARD_LITE_V2 is now BOARD_TH3D_EZBOARD_V2. Please update your configuration."
#elif MB(BTT_SKR_SE_BX)
#error "BOARD_BTT_SKR_SE_BX is now BOARD_BTT_SKR_SE_BX_V2 or BOARD_BTT_SKR_SE_BX_V3. Please update your configuration."
#elif MB(MKS_MONSTER8)
#error "BOARD_MKS_MONSTER8 is now BOARD_MKS_MONSTER8_V1 or BOARD_MKS_MONSTER8_V2. Please update your configuration."
#elif defined(MOTHERBOARD)
#error "Unknown MOTHERBOARD value set in Configuration.h."
#else
@ -863,7 +874,7 @@
#undef BOARD_STM32F103R
#undef BOARD_ESP32
#undef BOARD_STEVAL
#undef BOARD_BIGTREE_SKR_MINI_E3
#undef BOARD_STEVAL_3DP001V1
#undef BOARD_BIGTREE_SKR_V1_1
#undef BOARD_BIGTREE_SKR_V1_3
#undef BOARD_BIGTREE_SKR_V1_4
@ -871,6 +882,7 @@
#undef BOARD_BIGTREE_BTT002_V1_0
#undef BOARD_BIGTREE_SKR_PRO_V1_1
#undef BOARD_BIGTREE_SKR_MINI_V1_1
#undef BOARD_BIGTREE_SKR_MINI_E3
#undef BOARD_BIGTREE_SKR_E3_DIP
#undef BOARD_RUMBA32
#undef BOARD_RUMBA32_AUS3D
@ -878,6 +890,8 @@
#undef BOARD_RAMPS_LONGER3D_LK4PRO
#undef BOARD_BTT_SKR_V2_0
#undef BOARD_TH3D_EZBOARD_LITE_V2
#undef BOARD_BTT_SKR_SE_BX
#undef BOARD_MKS_MONSTER8
#endif

15
Marlin/src/pins/pinsDebug_list.h

@ -1826,21 +1826,6 @@
#if PIN_EXISTS(K_SERIAL_RX)
REPORT_NAME_DIGITAL(__LINE__, K_SERIAL_RX_PIN)
#endif
#if PIN_EXISTS(L6470_CHAIN_SCK)
REPORT_NAME_DIGITAL(__LINE__, L6470_CHAIN_SCK_PIN)
#endif
#if PIN_EXISTS(L6470_CHAIN_MISO)
REPORT_NAME_DIGITAL(__LINE__, L6470_CHAIN_MISO_PIN)
#endif
#if PIN_EXISTS(L6470_CHAIN_MOSI)
REPORT_NAME_DIGITAL(__LINE__, L6470_CHAIN_MOSI_PIN)
#endif
#if PIN_EXISTS(L6470_CHAIN_SS)
REPORT_NAME_DIGITAL(__LINE__, L6470_CHAIN_SS_PIN)
#endif
#if PIN_EXISTS(L6470_RESET_CHAIN)
REPORT_NAME_DIGITAL(__LINE__, L6470_RESET_CHAIN_PIN)
#endif
#if PIN_EXISTS(FET_SAFETY)
REPORT_NAME_DIGITAL(__LINE__, FET_SAFETY_PIN)
#endif

2
Marlin/src/pins/rambo/pins_RAMBO.h

@ -208,7 +208,7 @@
#define LCD_PINS_D6 74
#define LCD_PINS_D7 75
#if ANY(VIKI2, miniVIKI)
#if EITHER(VIKI2, miniVIKI)
#define BEEPER_PIN 44
// NB: Panucatt's Viki 2.0 wiring diagram (v1.2) indicates that the
// beeper/buzzer is connected to pin 33; however, the pin used in the

2
Marlin/src/pins/rambo/pins_SCOOVO_X9H.h

@ -143,7 +143,7 @@
#define HOME_PIN BTN_HOME
#if ANY(VIKI2, miniVIKI)
#if EITHER(VIKI2, miniVIKI)
#define BEEPER_PIN 44
// Pins for DOGM SPI LCD Support
#define DOGLCD_A0 70

2
Marlin/src/pins/ramps/pins_AZTEEG_X3.h

@ -51,7 +51,7 @@
#undef STAT_LED_RED_PIN
#undef STAT_LED_BLUE_PIN
#if ANY(VIKI2, miniVIKI)
#if EITHER(VIKI2, miniVIKI)
#undef DOGLCD_A0
#undef DOGLCD_CS

4
Marlin/src/pins/ramps/pins_AZTEEG_X3_PRO.h

@ -142,7 +142,7 @@
#undef BEEPER_PIN
#define BEEPER_PIN 33
#if ANY(VIKI2, miniVIKI)
#if EITHER(VIKI2, miniVIKI)
#undef SD_DETECT_PIN
#define SD_DETECT_PIN 49 // For easy adapter board
#undef BEEPER_PIN
@ -169,7 +169,7 @@
#undef SPINDLE_DIR_PIN
#if HAS_CUTTER // EXP2 header
#if ANY(VIKI2, miniVIKI)
#if EITHER(VIKI2, miniVIKI)
#define BTN_EN2 31 // Pin 7 needed for Spindle PWM
#endif
#define SPINDLE_LASER_PWM_PIN 7 // Hardware PWM

2
Marlin/src/pins/ramps/pins_MKS_GEN_13.h

@ -60,7 +60,7 @@
//
// LCD / Controller
//
#if ANY(VIKI2, miniVIKI)
#if EITHER(VIKI2, miniVIKI)
/**
* VIKI2 Has two groups of wires with...
*

2
Marlin/src/pins/ramps/pins_RAMPS.h

@ -728,7 +728,7 @@
#define LCD_SDSS SDSS
#define SD_DETECT_PIN EXP2_04_PIN
#elif ANY(VIKI2, miniVIKI)
#elif EITHER(VIKI2, miniVIKI)
#define DOGLCD_CS AUX4_05_PIN
#define DOGLCD_A0 AUX2_07_PIN

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save