diff --git a/Marlin/src/MarlinCore.cpp b/Marlin/src/MarlinCore.cpp index 5c4f5a4413..06e0d228e0 100644 --- a/Marlin/src/MarlinCore.cpp +++ b/Marlin/src/MarlinCore.cpp @@ -498,12 +498,7 @@ inline void manage_inactivity(const bool ignore_stepper_queue=false) { if (ENABLED(DISABLE_INACTIVE_Z)) DISABLE_AXIS_Z(); if (ENABLED(DISABLE_INACTIVE_E)) disable_e_steppers(); - #if BOTH(HAS_LCD_MENU, AUTO_BED_LEVELING_UBL) - if (ubl.lcd_map_control) { - ubl.lcd_map_control = false; - ui.defer_status_screen(false); - } - #endif + TERN_(AUTO_BED_LEVELING_UBL, ubl.steppers_were_disabled()); } } else diff --git a/Marlin/src/feature/bedlevel/ubl/ubl.cpp b/Marlin/src/feature/bedlevel/ubl/ubl.cpp index e08debf1d3..a93f5218d3 100644 --- a/Marlin/src/feature/bedlevel/ubl/ubl.cpp +++ b/Marlin/src/feature/bedlevel/ubl/ubl.cpp @@ -84,11 +84,7 @@ _GRIDPOS(Y, 12), _GRIDPOS(Y, 13), _GRIDPOS(Y, 14), _GRIDPOS(Y, 15) ); - #if HAS_LCD_MENU - bool unified_bed_leveling::lcd_map_control = false; - #endif - - volatile int unified_bed_leveling::encoder_diff; + volatile int16_t unified_bed_leveling::encoder_diff; unified_bed_leveling::unified_bed_leveling() { reset(); diff --git a/Marlin/src/feature/bedlevel/ubl/ubl.h b/Marlin/src/feature/bedlevel/ubl/ubl.h index 9e227f02d9..3455416a62 100644 --- a/Marlin/src/feature/bedlevel/ubl/ubl.h +++ b/Marlin/src/feature/bedlevel/ubl/ubl.h @@ -111,9 +111,12 @@ class unified_bed_leveling { #if HAS_LCD_MENU static bool lcd_map_control; + static void steppers_were_disabled(); + #else + static inline void steppers_were_disabled() {} #endif - static volatile int encoder_diff; // Volatile because it's changed at interrupt time. + static volatile int16_t encoder_diff; // Volatile because buttons may changed it at interrupt time unified_bed_leveling(); diff --git a/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp b/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp index a3d45169bc..da3477078a 100644 --- a/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp +++ b/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp @@ -54,7 +54,18 @@ #define UBL_G29_P31 #if HAS_LCD_MENU - void _lcd_ubl_output_map_lcd(); + + bool unified_bed_leveling::lcd_map_control = false; + + void unified_bed_leveling::steppers_were_disabled() { + if (lcd_map_control) { + lcd_map_control = false; + ui.defer_status_screen(false); + } + } + + void ubl_map_screen(); + #endif #define SIZE_OF_LITTLE_RAISE 1 @@ -789,11 +800,11 @@ bool click_and_hold(const clickFunc_t func=nullptr) { if (ui.button_pressed()) { - ui.quick_feedback(false); // Preserve button state for click-and-hold + ui.quick_feedback(false); // Preserve button state for click-and-hold const millis_t nxt = millis() + 1500UL; - while (ui.button_pressed()) { // Loop while the encoder is pressed. Uses hardware flag! - idle(); // idle, of course - if (ELAPSED(millis(), nxt)) { // After 1.5 seconds + while (ui.button_pressed()) { // Loop while the encoder is pressed. Uses hardware flag! + idle(); // idle, of course + if (ELAPSED(millis(), nxt)) { // After 1.5 seconds ui.quick_feedback(); if (func) (*func)(); ui.wait_for_release(); @@ -995,9 +1006,9 @@ lcd_mesh_edit_setup(new_z); do { + idle(); new_z = lcd_mesh_edit(); TERN_(UBL_MESH_EDIT_MOVES_Z, do_blocking_move_to_z(h_offset + new_z)); // Move the nozzle as the point is edited - idle(); SERIAL_FLUSH(); // Prevent host M105 buffer overrun. } while (!ui.button_pressed()); @@ -1022,7 +1033,7 @@ SERIAL_ECHOLNPGM("Done Editing Mesh"); if (lcd_map_control) - ui.goto_screen(_lcd_ubl_output_map_lcd); + ui.goto_screen(ubl_map_screen); else ui.return_to_status(); } diff --git a/Marlin/src/feature/touch/xpt2046.cpp b/Marlin/src/feature/touch/xpt2046.cpp index 9c84e4d95c..879d88bab3 100644 --- a/Marlin/src/feature/touch/xpt2046.cpp +++ b/Marlin/src/feature/touch/xpt2046.cpp @@ -81,7 +81,6 @@ #endif XPT2046 touch; -extern int8_t encoderDiff; void XPT2046::init() { SET_INPUT(TOUCH_MISO_PIN); diff --git a/Marlin/src/gcode/control/M17_M18_M84.cpp b/Marlin/src/gcode/control/M17_M18_M84.cpp index 55a219917c..842f301d92 100644 --- a/Marlin/src/gcode/control/M17_M18_M84.cpp +++ b/Marlin/src/gcode/control/M17_M18_M84.cpp @@ -64,11 +64,6 @@ void GcodeSuite::M18_M84() { else planner.finish_and_disable(); - #if BOTH(HAS_LCD_MENU, AUTO_BED_LEVELING_UBL) - if (ubl.lcd_map_control) { - ubl.lcd_map_control = false; - ui.defer_status_screen(false); - } - #endif + TERN_(AUTO_BED_LEVELING_UBL, ubl.steppers_were_disabled()); } } diff --git a/Marlin/src/lcd/menu/menu.cpp b/Marlin/src/lcd/menu/menu.cpp index 05491bfeea..7c384f1618 100644 --- a/Marlin/src/lcd/menu/menu.cpp +++ b/Marlin/src/lcd/menu/menu.cpp @@ -230,7 +230,7 @@ void MarlinUI::goto_screen(screenFunc_t screen, const uint16_t encoder/*=0*/, co screen = TERN(BABYSTEP_ZPROBE_OFFSET, lcd_babystep_zoffset, lcd_babystep_z); else { #if ENABLED(MOVE_Z_WHEN_IDLE) - move_menu_scale = MOVE_Z_IDLE_MULTIPLICATOR; + ui.manual_move.menu_scale = MOVE_Z_IDLE_MULTIPLICATOR; screen = lcd_move_z; #endif } diff --git a/Marlin/src/lcd/menu/menu_delta_calibrate.cpp b/Marlin/src/lcd/menu/menu_delta_calibrate.cpp index 381606bd14..7c25ca0afc 100644 --- a/Marlin/src/lcd/menu/menu_delta_calibrate.cpp +++ b/Marlin/src/lcd/menu/menu_delta_calibrate.cpp @@ -46,7 +46,7 @@ void _man_probe_pt(const xy_pos_t &xy) { do_blocking_move_to_xy_z(xy, Z_CLEARANCE_BETWEEN_PROBES); ui.wait_for_move = false; ui.synchronize(); - move_menu_scale = _MAX(PROBE_MANUALLY_STEP, MIN_STEPS_PER_SEGMENT / float(DEFAULT_XYZ_STEPS_PER_UNIT)); + ui.manual_move.menu_scale = _MAX(PROBE_MANUALLY_STEP, MIN_STEPS_PER_SEGMENT / float(DEFAULT_XYZ_STEPS_PER_UNIT)); ui.goto_screen(lcd_move_z); } } diff --git a/Marlin/src/lcd/menu/menu_motion.cpp b/Marlin/src/lcd/menu/menu_motion.cpp index 58594dd013..ff99358a8b 100644 --- a/Marlin/src/lcd/menu/menu_motion.cpp +++ b/Marlin/src/lcd/menu/menu_motion.cpp @@ -50,28 +50,13 @@ float manual_move_e_origin = 0; #endif -// -// Tell ui.update() to start a move to current_position" after a short delay. -// -inline void manual_move_to_current(AxisEnum axis - #if MULTI_MANUAL - , const int8_t eindex=-1 - #endif -) { - #if MULTI_MANUAL - if (axis == E_AXIS) ui.manual_move_e_index = eindex >= 0 ? eindex : active_extruder; - #endif - ui.manual_move_start_time = millis() + (move_menu_scale < 0.99f ? 0UL : 250UL); // delay for bigger moves - ui.manual_move_axis = (int8_t)axis; -} - // // "Motion" > "Move Axis" submenu // static void _lcd_move_xyz(PGM_P const name, const AxisEnum axis) { if (ui.use_click()) return ui.goto_previous_screen_no_defer(); - if (ui.encoderPosition && !ui.processing_manual_move) { + if (ui.encoderPosition && !ui.manual_move.processing) { // Start with no limits to movement float min = current_position[axis] - 1000, @@ -105,13 +90,13 @@ static void _lcd_move_xyz(PGM_P const name, const AxisEnum axis) { #endif // Get the new position - const float diff = float(int32_t(ui.encoderPosition)) * move_menu_scale; + const float diff = float(int32_t(ui.encoderPosition)) * ui.manual_move.menu_scale; #if IS_KINEMATIC - ui.manual_move_offset += diff; + ui.manual_move.offset += diff; if (int32_t(ui.encoderPosition) < 0) - NOLESS(ui.manual_move_offset, min - current_position[axis]); + NOLESS(ui.manual_move.offset, min - current_position[axis]); else - NOMORE(ui.manual_move_offset, max - current_position[axis]); + NOMORE(ui.manual_move.offset, max - current_position[axis]); #else current_position[axis] += diff; if (int32_t(ui.encoderPosition) < 0) @@ -120,16 +105,16 @@ static void _lcd_move_xyz(PGM_P const name, const AxisEnum axis) { NOMORE(current_position[axis], max); #endif - manual_move_to_current(axis); + ui.manual_move.soon(axis); ui.refresh(LCDVIEW_REDRAW_NOW); } ui.encoderPosition = 0; if (ui.should_draw()) { const float pos = NATIVE_TO_LOGICAL( - ui.processing_manual_move ? destination[axis] : current_position[axis] + TERN0(IS_KINEMATIC, ui.manual_move_offset), + ui.manual_move.processing ? destination[axis] : current_position[axis] + TERN0(IS_KINEMATIC, ui.manual_move.offset), axis ); - MenuEditItemBase::draw_edit_screen(name, move_menu_scale >= 0.1f ? ftostr41sign(pos) : ftostr43sign(pos)); + MenuEditItemBase::draw_edit_screen(name, ui.manual_move.menu_scale >= 0.1f ? ftostr41sign(pos) : ftostr43sign(pos)); } } void lcd_move_x() { _lcd_move_xyz(GET_TEXT(MSG_MOVE_X), X_AXIS); } @@ -141,10 +126,10 @@ void lcd_move_z() { _lcd_move_xyz(GET_TEXT(MSG_MOVE_Z), Z_AXIS); } static void lcd_move_e(TERN_(MULTI_MANUAL, const int8_t eindex=-1)) { if (ui.use_click()) return ui.goto_previous_screen_no_defer(); if (ui.encoderPosition) { - if (!ui.processing_manual_move) { - const float diff = float(int32_t(ui.encoderPosition)) * move_menu_scale; - TERN(IS_KINEMATIC, ui.manual_move_offset, current_position.e) += diff; - manual_move_to_current(E_AXIS + if (!ui.manual_move.processing) { + const float diff = float(int32_t(ui.encoderPosition)) * ui.manual_move.menu_scale; + TERN(IS_KINEMATIC, ui.manual_move.offset, current_position.e) += diff; + ui.manual_move.soon(E_AXIS #if MULTI_MANUAL , eindex #endif @@ -160,7 +145,7 @@ void lcd_move_z() { _lcd_move_xyz(GET_TEXT(MSG_MOVE_Z), Z_AXIS); } MenuEditItemBase::draw_edit_screen( GET_TEXT(TERN(MULTI_MANUAL, MSG_MOVE_EN, MSG_MOVE_E)), ftostr41sign(current_position.e - + TERN0(IS_KINEMATIC, ui.manual_move_offset) + + TERN0(IS_KINEMATIC, ui.manual_move.offset) - TERN0(MANUAL_E_MOVES_RELATIVE, manual_move_e_origin) ) ); @@ -181,7 +166,7 @@ screenFunc_t _manual_move_func_ptr; void _goto_manual_move(const float scale) { ui.defer_status_screen(); - move_menu_scale = scale; + ui.manual_move.menu_scale = scale; ui.goto_screen(_manual_move_func_ptr); } diff --git a/Marlin/src/lcd/menu/menu_ubl.cpp b/Marlin/src/lcd/menu/menu_ubl.cpp index 58cf13a11c..3722b53cb9 100644 --- a/Marlin/src/lcd/menu/menu_ubl.cpp +++ b/Marlin/src/lcd/menu/menu_ubl.cpp @@ -49,46 +49,36 @@ static int8_t x_plot = 0, y_plot = 0; // May be negative during move static int16_t custom_bed_temp = 50; #endif -float mesh_edit_value, mesh_edit_accumulator; // We round mesh_edit_value to 2.5 decimal places. So we keep a - // separate value that doesn't lose precision. -static int16_t ubl_encoderPosition = 0; +float mesh_edit_accumulator; // Rounded to 2.5 decimal places on use + +inline float rounded_mesh_value() { + const int32_t rounded = int32_t(mesh_edit_accumulator * 1000); + return float(rounded - (rounded % 5L)) / 1000; +} static void _lcd_mesh_fine_tune(PGM_P const msg) { ui.defer_status_screen(); if (ubl.encoder_diff) { - ubl_encoderPosition = (ubl.encoder_diff > 0) ? 1 : -1; + mesh_edit_accumulator += ubl.encoder_diff > 0 ? 0.005f : -0.005f; ubl.encoder_diff = 0; - - mesh_edit_accumulator += float(ubl_encoderPosition) * 0.005f * 0.5f; - mesh_edit_value = mesh_edit_accumulator; - ui.encoderPosition = 0; ui.refresh(LCDVIEW_CALL_REDRAW_NEXT); - - const int32_t rounded = (int32_t)(mesh_edit_value * 1000); - mesh_edit_value = float(rounded - (rounded % 5L)) / 1000; } if (ui.should_draw()) { - MenuEditItemBase::draw_edit_screen(msg, ftostr43sign(mesh_edit_value)); - TERN_(MESH_EDIT_GFX_OVERLAY, _lcd_zoffset_overlay_gfx(mesh_edit_value)); + const float rounded_f = rounded_mesh_value(); + MenuEditItemBase::draw_edit_screen(msg, ftostr43sign(rounded_f)); + TERN_(MESH_EDIT_GFX_OVERLAY, _lcd_zoffset_overlay_gfx(rounded_f)); } } -void lcd_limbo() { - ui.currentScreen = []{}; - ui.defer_status_screen(); -} - -float lcd_mesh_edit() { - lcd_limbo(); - ui.refresh(LCDVIEW_CALL_REDRAW_NEXT); - _lcd_mesh_fine_tune(GET_TEXT(MSG_MESH_EDITOR)); - return mesh_edit_value; -} +// +// Called external to the menu system to acquire the result of an edit. +// +float lcd_mesh_edit() { return rounded_mesh_value(); } void lcd_mesh_edit_setup(const float &initial) { - mesh_edit_value = mesh_edit_accumulator = initial; - lcd_limbo(); + mesh_edit_accumulator = initial; + ui.goto_screen([]{ _lcd_mesh_fine_tune(GET_TEXT(MSG_MESH_EDIT_Z)); }); } void _lcd_z_offset_edit() { @@ -97,11 +87,11 @@ void _lcd_z_offset_edit() { float lcd_z_offset_edit() { ui.goto_screen(_lcd_z_offset_edit); - return mesh_edit_value; + return rounded_mesh_value(); } void lcd_z_offset_edit_setup(const float &initial) { - mesh_edit_value = mesh_edit_accumulator = initial; + mesh_edit_accumulator = initial; ui.goto_screen(_lcd_z_offset_edit); } @@ -390,24 +380,10 @@ void _lcd_ubl_storage_mesh() { END_MENU(); } -/** - * UBL LCD "radar" map homing - */ -void _lcd_ubl_output_map_lcd(); - -void _lcd_ubl_map_homing() { - ui.defer_status_screen(); - _lcd_draw_homing(); - if (all_axes_homed()) { - ubl.lcd_map_control = true; // Return to the map screen - ui.goto_screen(_lcd_ubl_output_map_lcd); - } -} - /** * UBL LCD "radar" map point editing */ -void _lcd_ubl_map_lcd_edit_cmd() { +void _lcd_ubl_map_edit_cmd() { char ubl_lcd_gcode[50], str[10], str2[10]; dtostrf(ubl.mesh_index_to_xpos(x_plot), 0, 2, str); dtostrf(ubl.mesh_index_to_ypos(y_plot), 0, 2, str2); @@ -419,85 +395,122 @@ void _lcd_ubl_map_lcd_edit_cmd() { * UBL LCD Map Movement */ void ubl_map_move_to_xy() { - const feedRate_t fr_mm_s = MMM_TO_MMS(XY_PROBE_SPEED); - - destination = current_position; // sync destination at the start #if ENABLED(DELTA) - if (current_position.z > delta_clip_start_height) { + if (current_position.z > delta_clip_start_height) { // Make sure the delta has fully free motion + destination = current_position; destination.z = delta_clip_start_height; - prepare_internal_move_to_destination(fr_mm_s); + prepare_internal_fast_move_to_destination(homing_feedrate(Z_AXIS)); // Set current_position from destination } #endif - destination.set(ubl.mesh_index_to_xpos(x_plot), ubl.mesh_index_to_ypos(y_plot)); - prepare_internal_move_to_destination(fr_mm_s); + // Set the nozzle position to the mesh point + current_position.set(ubl.mesh_index_to_xpos(x_plot), ubl.mesh_index_to_ypos(y_plot)); + + // Use the built-in manual move handler + ui.manual_move.soon(ALL_AXES); +} + +inline int32_t grid_index(const uint8_t x, const uint8_t y) { + return (GRID_MAX_POINTS_X) * y + x; } /** * UBL LCD "radar" map */ -void _lcd_ubl_output_map_lcd() { +void ubl_map_screen() { + // static millis_t next_move = 0; + // const millis_t ms = millis(); - static int16_t step_scaler = 0; + uint8_t x, y; - if (ui.use_click()) return _lcd_ubl_map_lcd_edit_cmd(); + if (ui.first_page) { - if (ui.encoderPosition) { - step_scaler += int32_t(ui.encoderPosition); - x_plot += step_scaler / (ENCODER_STEPS_PER_MENU_ITEM); - ui.encoderPosition = 0; - ui.refresh(LCDVIEW_REDRAW_NOW); - } + // On click send "G29 P4 ..." to edit the Z value + if (ui.use_click()) { + _lcd_ubl_map_edit_cmd(); + return; + } - #define KEEP_LOOPING ENABLED(IS_KINEMATIC) // Loop until a valid point is found - - do { - // Encoder to the right (++) - if (x_plot >= GRID_MAX_POINTS_X) { x_plot = 0; y_plot++; } - if (y_plot >= GRID_MAX_POINTS_Y) y_plot = 0; - - // Encoder to the left (--) - if (x_plot < 0) { x_plot = GRID_MAX_POINTS_X - 1; y_plot--; } - if (y_plot < 0) y_plot = GRID_MAX_POINTS_Y - 1; + ui.defer_status_screen(); #if IS_KINEMATIC - const xy_pos_t xy = { ubl.mesh_index_to_xpos(x_plot), ubl.mesh_index_to_ypos(y_plot) }; - if (position_is_reachable(xy)) break; // Found a valid point - x_plot += (step_scaler < 0) ? -1 : 1; + // Index of the mesh point upon entry + const uint32_t old_pos_index = grid_index(x_plot, y_plot); + // Direction from new (unconstrained) encoder value + const int8_t step_dir = int32_t(ui.encoderPosition) < old_pos_index ? -1 : 1; #endif - } while(KEEP_LOOPING); + do { + // Now, keep the encoder position within range + if (int32_t(ui.encoderPosition) < 0) ui.encoderPosition = GRID_MAX_POINTS - 1; + if (int32_t(ui.encoderPosition) > GRID_MAX_POINTS - 1) ui.encoderPosition = 0; - // Determine number of points to edit - #if IS_KINEMATIC - n_edit_pts = 9; //TODO: Delta accessible edit points - #else - const bool xc = WITHIN(x_plot, 1, GRID_MAX_POINTS_X - 2), - yc = WITHIN(y_plot, 1, GRID_MAX_POINTS_Y - 2); - n_edit_pts = yc ? (xc ? 9 : 6) : (xc ? 6 : 4); // Corners - #endif + // Draw the grid point based on the encoder + x = ui.encoderPosition % (GRID_MAX_POINTS_X); + y = ui.encoderPosition / (GRID_MAX_POINTS_X); - // Cleanup - if (ABS(step_scaler) >= ENCODER_STEPS_PER_MENU_ITEM) step_scaler = 0; + // Validate if needed + #if IS_KINEMATIC + const xy_pos_t xy = { ubl.mesh_index_to_xpos(x), ubl.mesh_index_to_ypos(y) }; + if (position_is_reachable(xy)) break; // Found a valid point + ui.encoderPosition += step_dir; // Test the next point + #endif + } while(ENABLED(IS_KINEMATIC)); - if (ui.should_draw()) { - ui.ubl_plot(x_plot, y_plot); - if (!planner.movesplanned()) - ubl_map_move_to_xy(); // Move to new location + // Determine number of points to edit + #if IS_KINEMATIC + n_edit_pts = 9; // TODO: Delta accessible edit points + #else + const bool xc = WITHIN(x, 1, GRID_MAX_POINTS_X - 2), + yc = WITHIN(y, 1, GRID_MAX_POINTS_Y - 2); + n_edit_pts = yc ? (xc ? 9 : 6) : (xc ? 6 : 4); // Corners + #endif + + // Refresh is also set by encoder movement + //if (int32_t(ui.encoderPosition) != grid_index(x, y)) + // ui.refresh(LCDVIEW_CALL_REDRAW_NEXT); + } + + // Draw the grid point based on the encoder + x = ui.encoderPosition % (GRID_MAX_POINTS_X); + y = ui.encoderPosition / (GRID_MAX_POINTS_X); + + if (ui.should_draw()) ui.ubl_plot(x, y); + + // Add a move if needed to match the grid point + if (x != x_plot || y != y_plot) { + x_plot = x; y_plot = y; // The move is always posted, so update the grid point now + ubl_map_move_to_xy(); // Sets up a "manual move" + ui.refresh(LCDVIEW_CALL_REDRAW_NEXT); // Clean up a half drawn box + } +} + +/** + * UBL LCD "radar" map homing + */ +void _ubl_map_screen_homing() { + ui.defer_status_screen(); + _lcd_draw_homing(); + if (all_axes_homed()) { + ubl.lcd_map_control = true; // Return to the map screen after editing Z + ui.goto_screen(ubl_map_screen, grid_index(x_plot, y_plot)); // Pre-set the encoder value + ui.manual_move.menu_scale = 0; // Immediate move + ubl_map_move_to_xy(); // Move to current mesh point + ui.manual_move.menu_scale = 1; // Delayed moves } } /** * UBL Homing before LCD map */ -void _lcd_ubl_output_map_lcd_cmd() { +void _ubl_goto_map_screen() { + if (planner.movesplanned()) return; // The ACTION_ITEM will do nothing if (!all_axes_known()) { set_all_unhomed(); queue.inject_P(G28_STR); } - if (planner.movesplanned()) return; - ui.goto_screen(_lcd_ubl_map_homing); + ui.goto_screen(_ubl_map_screen_homing); // Go to the "Homing" screen } /** @@ -591,7 +604,7 @@ void _lcd_ubl_level_bed() { #if ENABLED(G26_MESH_VALIDATION) SUBMENU(MSG_UBL_STEP_BY_STEP_MENU, _lcd_ubl_step_by_step); #endif - ACTION_ITEM(MSG_UBL_MESH_EDIT, _lcd_ubl_output_map_lcd_cmd); + ACTION_ITEM(MSG_UBL_MESH_EDIT, _ubl_goto_map_screen); SUBMENU(MSG_UBL_STORAGE_MESH_MENU, _lcd_ubl_storage_mesh); SUBMENU(MSG_UBL_OUTPUT_MAP, _lcd_ubl_output_map); SUBMENU(MSG_UBL_TOOLS, _menu_ubl_tools); diff --git a/Marlin/src/lcd/ultralcd.cpp b/Marlin/src/lcd/ultralcd.cpp index 74924e1dd1..d2033018a6 100644 --- a/Marlin/src/lcd/ultralcd.cpp +++ b/Marlin/src/lcd/ultralcd.cpp @@ -211,7 +211,6 @@ millis_t MarlinUI::next_button_update_ms; // = 0 #endif bool MarlinUI::lcd_clicked; - float move_menu_scale; bool MarlinUI::use_click() { const bool click = lcd_clicked; @@ -388,7 +387,7 @@ bool MarlinUI::get_blink() { void lcd_move_z(); void _reprapworld_keypad_move(const AxisEnum axis, const int16_t dir) { - move_menu_scale = REPRAPWORLD_KEYPAD_MOVE_STEP; + ui.manual_move.menu_scale = REPRAPWORLD_KEYPAD_MOVE_STEP; ui.encoderPosition = dir; switch (axis) { case X_AXIS: lcd_move_x(); break; @@ -637,51 +636,65 @@ void MarlinUI::quick_feedback(const bool clear_buttons/*=true*/) { #if HAS_LCD_MENU - int8_t MarlinUI::manual_move_axis = (int8_t)NO_AXIS; - millis_t MarlinUI::manual_move_start_time = 0; + ManualMove MarlinUI::manual_move{}; - #if IS_KINEMATIC - bool MarlinUI::processing_manual_move = false; - float MarlinUI::manual_move_offset = 0; - #endif - - #if MULTI_MANUAL - int8_t MarlinUI::manual_move_e_index = 0; - #endif + millis_t ManualMove::start_time = 0; + float ManualMove::menu_scale = 1; + TERN_(IS_KINEMATIC, float ManualMove::offset = 0); + TERN_(IS_KINEMATIC, bool ManualMove::processing = false); + TERN_(MULTI_MANUAL, int8_t ManualMove::e_index = 0); + uint8_t ManualMove::axis = (uint8_t)NO_AXIS; /** - * If the most recent manual move hasn't been fed to the planner yet, - * and the planner can accept one, send a move immediately. + * If a manual move has been posted and its time has arrived, and if the planner + * has a space for it, then add a linear move to current_position the planner. + * + * If any manual move needs to be interrupted, make sure to force a manual move + * by setting manual_move.start_time to millis() after updating current_position. + * + * To post a manual move: + * - Update current_position to the new place you want to go. + * - Set manual_move.axis to an axis like X_AXIS. Use ALL_AXES for diagonal moves. + * - Set manual_move.start_time to a point in the future (in ms) when the move should be done. + * + * For kinematic machines: + * - Set manual_move.offset to modify one axis and post the move. + * This is used to achieve more rapid stepping on kinematic machines. + * + * Currently used by the _lcd_move_xyz function in menu_motion.cpp + * and the ubl_map_move_to_xy funtion in menu_ubl.cpp. */ - void MarlinUI::manage_manual_move() { + void ManualMove::task() { - if (processing_manual_move) return; + if (processing) return; // Prevent re-entry from idle() calls - if (manual_move_axis != (int8_t)NO_AXIS && ELAPSED(millis(), manual_move_start_time) && !planner.is_full()) { + // Add a manual move to the queue? + if (axis != (uint8_t)NO_AXIS && ELAPSED(millis(), start_time) && !planner.is_full()) { + + const feedRate_t fr_mm_s = (uint8_t(axis) <= E_AXIS) ? manual_feedrate_mm_s[axis] : XY_PROBE_FEEDRATE_MM_S; - const feedRate_t fr_mm_s = manual_feedrate_mm_s[manual_move_axis]; #if IS_KINEMATIC #if EXTRUDERS > 1 const int8_t old_extruder = active_extruder; - if (manual_move_axis == E_AXIS) active_extruder = manual_move_e_index; + if (axis == E_AXIS) active_extruder = e_index; #endif - // Set movement on a single axis + // Apply a linear offset to a single axis destination = current_position; - destination[manual_move_axis] += manual_move_offset; + if (axis <= XYZE) destination[axis] += offset; // Reset for the next move - manual_move_offset = 0; - manual_move_axis = (int8_t)NO_AXIS; + offset = 0; + axis = (uint8_t)NO_AXIS; // DELTA and SCARA machines use segmented moves, which could fill the planner during the call to // move_to_destination. This will cause idle() to be called, which can then call this function while the - // previous invocation is being blocked. Modifications to manual_move_offset shouldn't be made while - // processing_manual_move is true or the planner will get out of sync. - processing_manual_move = true; + // previous invocation is being blocked. Modifications to offset shouldn't be made while + // processing is true or the planner will get out of sync. + processing = true; prepare_internal_move_to_destination(fr_mm_s); // will set current_position from destination - processing_manual_move = false; + processing = false; #if EXTRUDERS > 1 active_extruder = old_extruder; @@ -689,15 +702,47 @@ void MarlinUI::quick_feedback(const bool clear_buttons/*=true*/) { #else - planner.buffer_line(current_position, fr_mm_s, manual_move_axis == E_AXIS ? manual_move_e_index : active_extruder); - manual_move_axis = (int8_t)NO_AXIS; + // For Cartesian / Core motion simply move to the current_position + planner.buffer_line(current_position, fr_mm_s, axis == E_AXIS ? e_index : active_extruder); + + //SERIAL_ECHOLNPAIR("Add planner.move with Axis ", int(axis), " at FR ", fr_mm_s); + + axis = (uint8_t)NO_AXIS; #endif } } + // + // Tell ui.update() to start a move to current_position after a short delay. + // + void ManualMove::soon(AxisEnum move_axis + #if MULTI_MANUAL + , const int8_t eindex/*=-1*/ + #endif + ) { + #if MULTI_MANUAL + if (move_axis == E_AXIS) e_index = eindex >= 0 ? eindex : active_extruder; + #endif + start_time = millis() + (menu_scale < 0.99f ? 0UL : 250UL); // delay for bigger moves + axis = (uint8_t)move_axis; + //SERIAL_ECHOLNPAIR("Post Move with Axis ", int(axis), " soon."); + } + #endif // HAS_LCD_MENU +#if ENABLED(AUTO_BED_LEVELING_UBL) + + void MarlinUI::external_encoder() { + if (external_control && encoderDiff) { + ubl.encoder_diff += encoderDiff; // Encoder for UBL G29 mesh editing + encoderDiff = 0; // Hide encoder events from the screen handler + refresh(LCDVIEW_REDRAW_NOW); // ...but keep the refresh. + } + } + +#endif + /** * Update the LCD, read encoder buttons, etc. * - Read button states @@ -753,7 +798,7 @@ void MarlinUI::update() { #if HAS_LCD_MENU // Handle any queued Move Axis motion - manage_manual_move(); + manual_move.task(); // Update button states for button_pressed(), etc. // If the state changes the next update may be delayed 300-500ms. @@ -776,7 +821,7 @@ void MarlinUI::update() { if (ELAPSED(ms, next_button_update_ms)) { encoderDiff = (ENCODER_STEPS_PER_MENU_ITEM) * (ENCODER_PULSES_PER_STEP) * encoderDirection; if (touch_buttons & EN_A) encoderDiff *= -1; - TERN_(AUTO_BED_LEVELING_UBL, if (external_control) ubl.encoder_diff = encoderDiff); + TERN_(AUTO_BED_LEVELING_UBL, external_encoder()); next_button_update_ms = ms + repeat_delay; // Assume the repeat delay if (!wait_for_unclick) { next_button_update_ms += 250; // Longer delay on first press @@ -1196,10 +1241,7 @@ void MarlinUI::update() { case encrot2: ENCODER_SPIN(encrot1, encrot3); break; case encrot3: ENCODER_SPIN(encrot2, encrot0); break; } - if (external_control) { - TERN_(AUTO_BED_LEVELING_UBL, ubl.encoder_diff = encoderDiff); // Make encoder rotation available to UBL G29 mesh editing. - encoderDiff = 0; // Hide the encoder event from the current screen handler. - } + TERN_(AUTO_BED_LEVELING_UBL, external_encoder()); lastEncoderBits = enc; } diff --git a/Marlin/src/lcd/ultralcd.h b/Marlin/src/lcd/ultralcd.h index 7641643409..6ebd43a37d 100644 --- a/Marlin/src/lcd/ultralcd.h +++ b/Marlin/src/lcd/ultralcd.h @@ -99,9 +99,6 @@ typedef void (*screenFunc_t)(); typedef void (*menuAction_t)(); - // Manual Movement - extern float move_menu_scale; - #if ENABLED(ADVANCED_PAUSE_FEATURE) void lcd_pause_show_message(const PauseMessage message, const PauseMode mode=PAUSE_MODE_SAME, @@ -264,6 +261,35 @@ } preheat_t; #endif +#if HAS_LCD_MENU + + // Manual Movement class + class ManualMove { + public: + static millis_t start_time; + static float menu_scale; + TERN_(IS_KINEMATIC, static float offset); + #if IS_KINEMATIC + static bool processing; + #else + static bool constexpr processing = false; + #endif + #if MULTI_MANUAL + static int8_t e_index; + #else + static int8_t constexpr e_index = 0; + #endif + static uint8_t axis; + static void task(); + static void soon(AxisEnum axis + #if MULTI_MANUAL + , const int8_t eindex=-1 + #endif + ); + }; + +#endif + //////////////////////////////////////////// //////////// MarlinUI Singleton //////////// //////////////////////////////////////////// @@ -494,29 +520,14 @@ public: static void enable_encoder_multiplier(const bool onoff); #endif - static int8_t manual_move_axis; - static millis_t manual_move_start_time; - - #if IS_KINEMATIC - static float manual_move_offset; - static bool processing_manual_move; - #else - static constexpr bool processing_manual_move = false; - #endif - - #if E_MANUAL > 1 - static int8_t manual_move_e_index; - #else - static constexpr int8_t manual_move_e_index = 0; - #endif + // Manual Movement + static ManualMove manual_move; // Select Screen (modal NO/YES style dialog) static bool selection; static void set_selection(const bool sel) { selection = sel; } static bool update_selection(); - static void manage_manual_move(); - static bool lcd_clicked; static bool use_click(); @@ -609,6 +620,9 @@ public: static bool external_control; FORCE_INLINE static void capture() { external_control = true; } FORCE_INLINE static void release() { external_control = false; } + #if ENABLED(AUTO_BED_LEVELING_UBL) + static void external_encoder(); + #endif #else static constexpr bool external_control = false; #endif diff --git a/Marlin/src/module/motion.cpp b/Marlin/src/module/motion.cpp index 7fba6f8ea3..1994280a2c 100644 --- a/Marlin/src/module/motion.cpp +++ b/Marlin/src/module/motion.cpp @@ -1316,16 +1316,18 @@ void do_homing_move(const AxisEnum axis, const float distance, const feedRate_t current_position[axis] = distance; line_to_current_position(real_fr_mm_s); #else + // Get the ABC or XYZ positions in mm abce_pos_t target = planner.get_axis_positions_mm(); - target[axis] = 0; - planner.set_machine_position_mm(target); - target[axis] = distance; + + target[axis] = 0; // Set the single homing axis to 0 + planner.set_machine_position_mm(target); // Update the machine position #if HAS_DIST_MM_ARG const xyze_float_t cart_dist_mm{0}; #endif // Set delta/cartesian axes directly + target[axis] = distance; // The move will be towards the endstop planner.buffer_segment(target #if HAS_DIST_MM_ARG , cart_dist_mm diff --git a/Marlin/src/module/planner.cpp b/Marlin/src/module/planner.cpp index 1beb63c1a4..8352c2b3a6 100644 --- a/Marlin/src/module/planner.cpp +++ b/Marlin/src/module/planner.cpp @@ -1654,7 +1654,7 @@ void Planner::synchronize() { * extruder - target extruder * millimeters - the length of the movement, if known * - * Returns true if movement was properly queued, false otherwise + * Returns true if movement was properly queued, false otherwise (if cleaning) */ bool Planner::_buffer_steps(const xyze_long_t &target #if HAS_POSITION_FLOAT @@ -2637,6 +2637,8 @@ void Planner::buffer_sync_block() { * fr_mm_s - (target) speed of the move * extruder - target extruder * millimeters - the length of the movement, if known + * + * Return 'false' if no segment was queued due to cleaning, cold extrusion, full queue, etc. */ bool Planner::buffer_segment(const float &a, const float &b, const float &c, const float &e #if HAS_DIST_MM_ARG @@ -2706,7 +2708,7 @@ bool Planner::buffer_segment(const float &a, const float &b, const float &c, con SERIAL_ECHOLNPGM(")"); //*/ - // Queue the movement + // Queue the movement. Return 'false' if the move was not queued. if (!_buffer_steps(target #if HAS_POSITION_FLOAT , target_float