update code base to Marlin 2.0.9.2

This commit is contained in:
Stefan Kalscheuer
2021-10-03 18:57:12 +02:00
parent b9d7ba838e
commit 7077da3591
2617 changed files with 332093 additions and 103438 deletions

1219
Marlin/src/gcode/bedlevel/G26.cpp Executable file → Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,169 @@
/**
* 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 ENABLED(ASSISTED_TRAMMING)
#include "../gcode.h"
#include "../../module/planner.h"
#include "../../module/probe.h"
#include "../../feature/bedlevel/bedlevel.h"
#if HAS_MULTI_HOTEND
#include "../../module/tool_change.h"
#endif
#define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
#include "../../core/debug_out.h"
//
// Define tramming point names.
//
#include "../../feature/tramming.h"
/**
* G35: Read bed corners to help adjust bed screws
*
* S<screw_thread>
*
* Screw thread: 30 - Clockwise M3
* 31 - Counter-Clockwise M3
* 40 - Clockwise M4
* 41 - Counter-Clockwise M4
* 50 - Clockwise M5
* 51 - Counter-Clockwise M5
**/
void GcodeSuite::G35() {
DEBUG_SECTION(log_G35, "G35", DEBUGGING(LEVELING));
if (DEBUGGING(LEVELING)) log_machine_info();
float z_measured[G35_PROBE_COUNT] = { 0 };
const uint8_t screw_thread = parser.byteval('S', TRAMMING_SCREW_THREAD);
if (!WITHIN(screw_thread, 30, 51) || screw_thread % 10 > 1) {
SERIAL_ECHOLNPGM("?(S)crew thread must be 30, 31, 40, 41, 50, or 51.");
return;
}
// Wait for planner moves to finish!
planner.synchronize();
// Disable the leveling matrix before auto-aligning
#if HAS_LEVELING
#if ENABLED(RESTORE_LEVELING_AFTER_G35)
const bool leveling_was_active = planner.leveling_active;
#endif
set_bed_leveling_enabled(false);
#endif
#if ENABLED(CNC_WORKSPACE_PLANES)
workspace_plane = PLANE_XY;
#endif
// Always home with tool 0 active
#if HAS_MULTI_HOTEND
const uint8_t old_tool_index = active_extruder;
tool_change(0, true);
#endif
// Disable duplication mode on homing
TERN_(HAS_DUPLICATION_MODE, set_duplication_enabled(false));
// Home only Z axis when X and Y is trusted, otherwise all axes, if needed before this procedure
if (!all_axes_trusted()) process_subcommands_now_P(PSTR("G28Z"));
bool err_break = false;
// Probe all positions
LOOP_L_N(i, G35_PROBE_COUNT) {
// In BLTOUCH HS mode, the probe travels in a deployed state.
// Users of G35 might have a badly misaligned bed, so raise Z by the
// length of the deployed pin (BLTOUCH stroke < 7mm)
do_blocking_move_to_z(SUM_TERN(BLTOUCH_HS_MODE, Z_CLEARANCE_BETWEEN_PROBES, 7));
const float z_probed_height = probe.probe_at_point(tramming_points[i], PROBE_PT_RAISE, 0, true);
if (isnan(z_probed_height)) {
SERIAL_ECHOPGM("G35 failed at point ", i + 1, " (");
SERIAL_ECHOPGM_P((char *)pgm_read_ptr(&tramming_point_name[i]));
SERIAL_CHAR(')');
SERIAL_ECHOLNPGM_P(SP_X_STR, tramming_points[i].x, SP_Y_STR, tramming_points[i].y);
err_break = true;
break;
}
if (DEBUGGING(LEVELING)) {
DEBUG_ECHOPGM("Probing point ", i + 1, " (");
DEBUG_ECHOPGM_P((char *)pgm_read_ptr(&tramming_point_name[i]));
DEBUG_CHAR(')');
DEBUG_ECHOLNPGM_P(SP_X_STR, tramming_points[i].x, SP_Y_STR, tramming_points[i].y, SP_Z_STR, z_probed_height);
}
z_measured[i] = z_probed_height;
}
if (!err_break) {
const float threads_factor[] = { 0.5, 0.7, 0.8 };
// Calculate adjusts
LOOP_S_L_N(i, 1, G35_PROBE_COUNT) {
const float diff = z_measured[0] - z_measured[i],
adjust = ABS(diff) < 0.001f ? 0 : diff / threads_factor[(screw_thread - 30) / 10];
const int full_turns = trunc(adjust);
const float decimal_part = adjust - float(full_turns);
const int minutes = trunc(decimal_part * 60.0f);
SERIAL_ECHOPGM("Turn ");
SERIAL_ECHOPGM_P((char *)pgm_read_ptr(&tramming_point_name[i]));
SERIAL_ECHOPGM(" ", (screw_thread & 1) == (adjust > 0) ? "CCW" : "CW", " by ", ABS(full_turns), " turns");
if (minutes) SERIAL_ECHOPGM(" and ", ABS(minutes), " minutes");
if (ENABLED(REPORT_TRAMMING_MM)) SERIAL_ECHOPGM(" (", -diff, "mm)");
SERIAL_EOL();
}
}
else
SERIAL_ECHOLNPGM("G35 aborted.");
// Restore the active tool after homing
#if HAS_MULTI_HOTEND
tool_change(old_tool_index, DISABLED(PARKING_EXTRUDER)); // Fetch previous toolhead if not PARKING_EXTRUDER
#endif
#if BOTH(HAS_LEVELING, RESTORE_LEVELING_AFTER_G35)
set_bed_leveling_enabled(leveling_was_active);
#endif
// Stow the probe, as the last call to probe.probe_at_point(...) left
// the probe deployed if it was successful.
probe.stow();
move_to_tramming_wait_pos();
// After this operation the Z position needs correction
set_axis_never_homed(Z_AXIS);
}
#endif // ASSISTED_TRAMMING

2
Marlin/src/gcode/bedlevel/G42.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/

80
Marlin/src/gcode/bedlevel/M420.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -30,7 +30,7 @@
#include "../../module/probe.h"
#if ENABLED(EEPROM_SETTINGS)
#include "../../module/configuration_store.h"
#include "../../module/settings.h"
#endif
#if ENABLED(EXTENSIBLE_UI)
@@ -68,30 +68,28 @@ void GcodeSuite::M420() {
y_min = probe.min_y(), y_max = probe.max_y();
#if ENABLED(AUTO_BED_LEVELING_BILINEAR)
bilinear_start.set(x_min, y_min);
bilinear_grid_spacing.set((x_max - x_min) / (GRID_MAX_POINTS_X - 1),
(y_max - y_min) / (GRID_MAX_POINTS_Y - 1));
bilinear_grid_spacing.set((x_max - x_min) / (GRID_MAX_CELLS_X),
(y_max - y_min) / (GRID_MAX_CELLS_Y));
#endif
GRID_LOOP(x, y) {
Z_VALUES(x, y) = 0.001 * random(-200, 200);
#if ENABLED(EXTENSIBLE_UI)
ExtUI::onMeshUpdate(x, y, Z_VALUES(x, y));
#endif
TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(x, y, Z_VALUES(x, y)));
}
SERIAL_ECHOPGM("Simulated " STRINGIFY(GRID_MAX_POINTS_X) "x" STRINGIFY(GRID_MAX_POINTS_Y) " mesh ");
SERIAL_ECHOPAIR(" (", x_min);
SERIAL_ECHOPGM(" (", x_min);
SERIAL_CHAR(','); SERIAL_ECHO(y_min);
SERIAL_ECHOPAIR(")-(", x_max);
SERIAL_ECHOPGM(")-(", x_max);
SERIAL_CHAR(','); SERIAL_ECHO(y_max);
SERIAL_ECHOLNPGM(")");
}
#endif
xyz_pos_t oldpos = current_position;
// If disabling leveling do it right away
// (Don't disable for just M420 or M420 V)
if (seen_S && !to_enable) set_bed_leveling_enabled(false);
xyz_pos_t oldpos = current_position;
#if ENABLED(AUTO_BED_LEVELING_UBL)
// L to load a mesh from the EEPROM
@@ -110,7 +108,7 @@ void GcodeSuite::M420() {
if (!WITHIN(storage_slot, 0, a - 1)) {
SERIAL_ECHOLNPGM("?Invalid storage slot.");
SERIAL_ECHOLNPAIR("?Use 0 to ", a - 1);
SERIAL_ECHOLNPGM("?Use 0 to ", a - 1);
return;
}
@@ -130,12 +128,12 @@ void GcodeSuite::M420() {
ubl.display_map(parser.byteval('T'));
SERIAL_ECHOPGM("Mesh is ");
if (!ubl.mesh_is_valid()) SERIAL_ECHOPGM("in");
SERIAL_ECHOLNPAIR("valid\nStorage slot: ", ubl.storage_slot);
SERIAL_ECHOLNPGM("valid\nStorage slot: ", ubl.storage_slot);
}
#endif // AUTO_BED_LEVELING_UBL
const bool seenV = parser.seen('V');
const bool seenV = parser.seen_test('V');
#if HAS_MESH
@@ -155,22 +153,19 @@ void GcodeSuite::M420() {
// Get the sum and average of all mesh values
float mesh_sum = 0;
for (uint8_t x = GRID_MAX_POINTS_X; x--;)
for (uint8_t y = GRID_MAX_POINTS_Y; y--;)
mesh_sum += Z_VALUES(x, y);
GRID_LOOP(x, y) mesh_sum += Z_VALUES(x, y);
const float zmean = mesh_sum / float(GRID_MAX_POINTS);
#else
#else // midrange
// Find the low and high mesh values
// Find the low and high mesh values.
float lo_val = 100, hi_val = -100;
for (uint8_t x = GRID_MAX_POINTS_X; x--;)
for (uint8_t y = GRID_MAX_POINTS_Y; y--;) {
const float z = Z_VALUES(x, y);
NOMORE(lo_val, z);
NOLESS(hi_val, z);
}
// Take the mean of the lowest and highest
GRID_LOOP(x, y) {
const float z = Z_VALUES(x, y);
NOMORE(lo_val, z);
NOLESS(hi_val, z);
}
// Get the midrange plus C value. (The median may be better.)
const float zmean = (lo_val + hi_val) / 2.0 + cval;
#endif
@@ -179,16 +174,11 @@ void GcodeSuite::M420() {
if (!NEAR_ZERO(zmean)) {
set_bed_leveling_enabled(false);
// Subtract the mean from all values
for (uint8_t x = GRID_MAX_POINTS_X; x--;)
for (uint8_t y = GRID_MAX_POINTS_Y; y--;) {
Z_VALUES(x, y) -= zmean;
#if ENABLED(EXTENSIBLE_UI)
ExtUI::onMeshUpdate(x, y, Z_VALUES(x, y));
#endif
}
#if ENABLED(ABL_BILINEAR_SUBDIVISION)
bed_level_virt_interpolate();
#endif
GRID_LOOP(x, y) {
Z_VALUES(x, y) -= zmean;
TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(x, y, Z_VALUES(x, y)));
}
TERN_(ABL_BILINEAR_SUBDIVISION, bed_level_virt_interpolate());
}
#endif
@@ -210,9 +200,7 @@ void GcodeSuite::M420() {
if (leveling_is_valid()) {
#if ENABLED(AUTO_BED_LEVELING_BILINEAR)
print_bilinear_leveling_grid();
#if ENABLED(ABL_BILINEAR_SUBDIVISION)
print_bilinear_leveling_grid_virt();
#endif
TERN_(ABL_BILINEAR_SUBDIVISION, print_bilinear_leveling_grid_virt());
#elif ENABLED(MESH_BED_LEVELING)
SERIAL_ECHOLNPGM("Mesh Bed Level data:");
mbl.report_mesh();
@@ -254,4 +242,18 @@ void GcodeSuite::M420() {
report_current_position();
}
void GcodeSuite::M420_report(const bool forReplay/*=true*/) {
report_heading_etc(forReplay, PSTR(
TERN(MESH_BED_LEVELING, "Mesh Bed Leveling", TERN(AUTO_BED_LEVELING_UBL, "Unified Bed Leveling", "Auto Bed Leveling"))
));
SERIAL_ECHOPGM_P(
PSTR(" M420 S"), planner.leveling_active
#if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
, SP_Z_STR, LINEAR_UNIT(planner.z_fade_height)
#endif
, PSTR(" ; Leveling ")
);
serialprintln_onoff(planner.leveling_active);
}
#endif // HAS_LEVELING

693
Marlin/src/gcode/bedlevel/abl/G29.cpp Executable file → Normal file

File diff suppressed because it is too large Load Diff

43
Marlin/src/gcode/bedlevel/abl/M421.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -36,32 +36,39 @@
#endif
/**
* M421: Set a single Mesh Bed Leveling Z coordinate
* M421: Set one or more Mesh Bed Leveling Z coordinates
*
* Usage:
* M421 I<xindex> J<yindex> Z<linear>
* M421 I<xindex> J<yindex> Q<offset>
*
* - If I is omitted, set the entire row
* - If J is omitted, set the entire column
* - If both I and J are omitted, set all
*/
void GcodeSuite::M421() {
int8_t ix = parser.intval('I', -1), iy = parser.intval('J', -1);
const bool hasI = ix >= 0,
hasJ = iy >= 0,
hasZ = parser.seen('Z'),
hasQ = !hasZ && parser.seen('Q');
const bool hasZ = parser.seenval('Z'),
hasQ = !hasZ && parser.seenval('Q');
if (!hasI || !hasJ || !(hasZ || hasQ))
SERIAL_ERROR_MSG(STR_ERR_M421_PARAMETERS);
else if (!WITHIN(ix, 0, GRID_MAX_POINTS_X - 1) || !WITHIN(iy, 0, GRID_MAX_POINTS_Y - 1))
SERIAL_ERROR_MSG(STR_ERR_MESH_XY);
else {
z_values[ix][iy] = parser.value_linear_units() + (hasQ ? z_values[ix][iy] : 0);
#if ENABLED(ABL_BILINEAR_SUBDIVISION)
bed_level_virt_interpolate();
#endif
#if ENABLED(EXTENSIBLE_UI)
ExtUI::onMeshUpdate(ix, iy, z_values[ix][iy]);
#endif
if (hasZ || hasQ) {
if (WITHIN(ix, -1, GRID_MAX_POINTS_X - 1) && WITHIN(iy, -1, GRID_MAX_POINTS_Y - 1)) {
const float zval = parser.value_linear_units();
uint8_t sx = ix >= 0 ? ix : 0, ex = ix >= 0 ? ix : GRID_MAX_POINTS_X - 1,
sy = iy >= 0 ? iy : 0, ey = iy >= 0 ? iy : GRID_MAX_POINTS_Y - 1;
LOOP_S_LE_N(x, sx, ex) {
LOOP_S_LE_N(y, sy, ey) {
z_values[x][y] = zval + (hasQ ? z_values[x][y] : 0);
TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(x, y, z_values[x][y]));
}
}
TERN_(ABL_BILINEAR_SUBDIVISION, bed_level_virt_interpolate());
}
else
SERIAL_ERROR_MSG(STR_ERR_MESH_XY);
}
else
SERIAL_ERROR_MSG(STR_ERR_M421_PARAMETERS);
}
#endif // AUTO_BED_LEVELING_BILINEAR

91
Marlin/src/gcode/bedlevel/mbl/G29.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -34,14 +34,19 @@
#include "../../queue.h"
#include "../../../libs/buzzer.h"
#include "../../../lcd/ultralcd.h"
#include "../../../lcd/marlinui.h"
#include "../../../module/motion.h"
#include "../../../module/stepper.h"
#if ENABLED(EXTENSIBLE_UI)
#include "../../../lcd/extui/ui_api.h"
#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED)
#include "../../../lcd/e3v2/enhanced/dwin.h"
#endif
#define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
#include "../../../core/debug_out.h"
// Save 130 bytes with non-duplication of PSTR
inline void echo_not_entered(const char c) { SERIAL_CHAR(c); SERIAL_ECHOLNPGM(" not entered."); }
@@ -57,14 +62,22 @@ inline void echo_not_entered(const char c) { SERIAL_CHAR(c); SERIAL_ECHOLNPGM("
* S3 In Jn Zn.nn Manually modify a single point
* S4 Zn.nn Set z offset. Positive away from bed, negative closer to bed.
* S5 Reset and disable mesh
*
*/
void GcodeSuite::G29() {
DEBUG_SECTION(log_G29, "G29", true);
// G29 Q is also available if debugging
#if ENABLED(DEBUG_LEVELING_FEATURE)
const bool seenQ = parser.seen_test('Q');
if (seenQ || DEBUGGING(LEVELING)) {
log_machine_info();
if (seenQ) return;
}
#endif
TERN_(FULL_REPORT_TO_HOST_FEATURE, set_and_report_grblstate(M_PROBE));
static int mbl_probe_index = -1;
#if HAS_SOFTWARE_ENDSTOPS
static bool saved_soft_endstops_state;
#endif
MeshLevelingState state = (MeshLevelingState)parser.byteval('S', (int8_t)MeshReport);
if (!WITHIN(state, 0, 5)) {
@@ -73,6 +86,7 @@ void GcodeSuite::G29() {
}
int8_t ix, iy;
ix = iy = 0;
switch (state) {
case MeshReport:
@@ -89,7 +103,8 @@ void GcodeSuite::G29() {
mbl.reset();
mbl_probe_index = 0;
if (!ui.wait_for_move) {
queue.inject_P(PSTR("G28\nG29 S2"));
queue.inject_P(parser.seen_test('N') ? PSTR("G28" TERN(CAN_SET_LEVELING_AFTER_G28, "L0", "") "\nG29S2") : PSTR("G29S2"));
TERN_(EXTENSIBLE_UI, ExtUI::onMeshLevelingStart());
return;
}
state = MeshNext;
@@ -101,40 +116,43 @@ void GcodeSuite::G29() {
}
// For each G29 S2...
if (mbl_probe_index == 0) {
#if HAS_SOFTWARE_ENDSTOPS
// For the initial G29 S2 save software endstop state
saved_soft_endstops_state = soft_endstops_enabled;
#endif
// Move close to the bed before the first point
do_blocking_move_to_z(0);
do_blocking_move_to_z(0.4f
#ifdef MANUAL_PROBE_START_Z
+ (MANUAL_PROBE_START_Z) - 0.4f
#endif
);
}
else {
// Save Z for the previous mesh position
mbl.set_zigzag_z(mbl_probe_index - 1, current_position.z);
#if HAS_SOFTWARE_ENDSTOPS
soft_endstops_enabled = saved_soft_endstops_state;
#endif
TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(ix, iy, current_position.z));
SET_SOFT_ENDSTOP_LOOSE(false);
}
// If there's another point to sample, move there with optional lift.
if (mbl_probe_index < GRID_MAX_POINTS) {
#if HAS_SOFTWARE_ENDSTOPS
// Disable software endstops to allow manual adjustment
// If G29 is not completed, they will not be re-enabled
soft_endstops_enabled = false;
#endif
if (mbl_probe_index < (GRID_MAX_POINTS)) {
// Disable software endstops to allow manual adjustment
// If G29 is left hanging without completion they won't be re-enabled!
SET_SOFT_ENDSTOP_LOOSE(true);
mbl.zigzag(mbl_probe_index++, ix, iy);
_manual_goto_xy({ mbl.index_to_xpos[ix], mbl.index_to_ypos[iy] });
}
else {
// One last "return to the bed" (as originally coded) at completion
current_position.z = MANUAL_PROBE_HEIGHT;
// Move to the after probing position
current_position.z = (
#ifdef Z_AFTER_PROBING
Z_AFTER_PROBING
#else
Z_CLEARANCE_BETWEEN_MANUAL_PROBES
#endif
);
line_to_current_position();
planner.synchronize();
// After recording the last point, activate home and activate
mbl_probe_index = -1;
SERIAL_ECHOLNPGM("Mesh probing done.");
TERN_(HAS_STATUS_MESSAGE, ui.set_status(GET_TEXT(MSG_MESH_DONE)));
BUZZ(100, 659);
BUZZ(100, 698);
@@ -147,18 +165,15 @@ void GcodeSuite::G29() {
planner.synchronize();
#endif
#if ENABLED(LCD_BED_LEVELING)
ui.wait_for_move = false;
#endif
TERN_(LCD_BED_LEVELING, ui.wait_for_move = false);
}
break;
case MeshSet:
if (parser.seenval('I')) {
ix = parser.value_int();
if (!WITHIN(ix, 0, GRID_MAX_POINTS_X - 1)) {
SERIAL_ECHOPAIR("I out of range (0-", int(GRID_MAX_POINTS_X - 1));
SERIAL_ECHOLNPGM(")");
if (!WITHIN(ix, 0, (GRID_MAX_POINTS_X) - 1)) {
SERIAL_ECHOLNPGM("I out of range (0-", (GRID_MAX_POINTS_X) - 1, ")");
return;
}
}
@@ -167,9 +182,8 @@ void GcodeSuite::G29() {
if (parser.seenval('J')) {
iy = parser.value_int();
if (!WITHIN(iy, 0, GRID_MAX_POINTS_Y - 1)) {
SERIAL_ECHOPAIR("J out of range (0-", int(GRID_MAX_POINTS_Y - 1));
SERIAL_ECHOLNPGM(")");
if (!WITHIN(iy, 0, (GRID_MAX_POINTS_Y) - 1)) {
SERIAL_ECHOLNPGM("J out of range (0-", (GRID_MAX_POINTS_Y) - 1, ")");
return;
}
}
@@ -178,9 +192,8 @@ void GcodeSuite::G29() {
if (parser.seenval('Z')) {
mbl.z_values[ix][iy] = parser.value_linear_units();
#if ENABLED(EXTENSIBLE_UI)
ExtUI::onMeshUpdate(ix, iy, mbl.z_values[ix][iy]);
#endif
TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(ix, iy, mbl.z_values[ix][iy]));
TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_MeshUpdate(ix, iy, mbl.z_values[ix][iy]));
}
else
return echo_not_entered('Z');
@@ -200,11 +213,13 @@ void GcodeSuite::G29() {
} // switch(state)
if (state == MeshNext) {
SERIAL_ECHOPAIR("MBL G29 point ", _MIN(mbl_probe_index, GRID_MAX_POINTS));
SERIAL_ECHOLNPAIR(" of ", int(GRID_MAX_POINTS));
SERIAL_ECHOLNPGM("MBL G29 point ", _MIN(mbl_probe_index, GRID_MAX_POINTS), " of ", GRID_MAX_POINTS);
if (mbl_probe_index > 0) TERN_(HAS_STATUS_MESSAGE, ui.status_printf_P(0, PSTR(S_FMT " %i/%i"), GET_TEXT(MSG_PROBING_POINT), _MIN(mbl_probe_index, GRID_MAX_POINTS), int(GRID_MAX_POINTS)));
}
report_current_position();
TERN_(FULL_REPORT_TO_HOST_FEATURE, set_and_report_grblstate(M_IDLE));
}
#endif // MESH_BED_LEVELING

2
Marlin/src/gcode/bedlevel/mbl/M421.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/

15
Marlin/src/gcode/bedlevel/ubl/G29.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -31,6 +31,17 @@
#include "../../gcode.h"
#include "../../../feature/bedlevel/bedlevel.h"
void GcodeSuite::G29() { ubl.G29(); }
#if ENABLED(FULL_REPORT_TO_HOST_FEATURE)
#include "../../../module/motion.h"
#endif
void GcodeSuite::G29() {
TERN_(FULL_REPORT_TO_HOST_FEATURE, set_and_report_grblstate(M_PROBE));
ubl.G29();
TERN_(FULL_REPORT_TO_HOST_FEATURE, set_and_report_grblstate(M_IDLE));
}
#endif // AUTO_BED_LEVELING_UBL

34
Marlin/src/gcode/bedlevel/ubl/M421.cpp Executable file → Normal file
View File

@@ -16,12 +16,12 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
/**
* unified.cpp - Unified Bed Leveling
* M421.cpp - Unified Bed Leveling
*/
#include "../../../inc/MarlinConfig.h"
@@ -33,39 +33,43 @@
#if ENABLED(EXTENSIBLE_UI)
#include "../../../lcd/extui/ui_api.h"
#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED)
#include "../../../lcd/e3v2/enhanced/dwin.h"
#endif
/**
* M421: Set a single Mesh Bed Leveling Z coordinate
*
* Usage:
* M421 I<xindex> J<yindex> Z<linear>
* M421 I<xindex> J<yindex> Q<offset>
* M421 I<xindex> J<yindex> N
* M421 C Z<linear>
* M421 C Q<offset>
* M421 I<xindex> J<yindex> Z<linear> : Set the Mesh Point IJ to the Z value
* M421 I<xindex> J<yindex> Q<offset> : Add the Q value to the Mesh Point IJ
* M421 I<xindex> J<yindex> N : Set the Mesh Point IJ to NAN (not set)
* M421 C Z<linear> : Set the closest Mesh Point to the Z value
* M421 C Q<offset> : Add the Q value to the closest Mesh Point
*/
void GcodeSuite::M421() {
xy_int8_t ij = { int8_t(parser.intval('I', -1)), int8_t(parser.intval('J', -1)) };
const bool hasI = ij.x >= 0,
hasJ = ij.y >= 0,
hasC = parser.seen('C'),
hasN = parser.seen('N'),
hasC = parser.seen_test('C'),
hasN = parser.seen_test('N'),
hasZ = parser.seen('Z'),
hasQ = !hasZ && parser.seen('Q');
if (hasC) ij = ubl.find_closest_mesh_point_of_type(REAL, current_position);
if (hasC) ij = ubl.find_closest_mesh_point_of_type(CLOSEST, current_position);
// Test for bad parameter combinations
if (int(hasC) + int(hasI && hasJ) != 1 || !(hasZ || hasQ || hasN))
SERIAL_ERROR_MSG(STR_ERR_M421_PARAMETERS);
// Test for I J out of range
else if (!WITHIN(ij.x, 0, GRID_MAX_POINTS_X - 1) || !WITHIN(ij.y, 0, GRID_MAX_POINTS_Y - 1))
SERIAL_ERROR_MSG(STR_ERR_MESH_XY);
else {
float &zval = ubl.z_values[ij.x][ij.y];
zval = hasN ? NAN : parser.value_linear_units() + (hasQ ? zval : 0);
#if ENABLED(EXTENSIBLE_UI)
ExtUI::onMeshUpdate(ij.x, ij.y, zval);
#endif
float &zval = ubl.z_values[ij.x][ij.y]; // Altering this Mesh Point
zval = hasN ? NAN : parser.value_linear_units() + (hasQ ? zval : 0); // N=NAN, Z=NEWVAL, or Q=ADDVAL
TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(ij.x, ij.y, zval)); // Ping ExtUI in case it's showing the mesh
TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_MeshUpdate(ij.x, ij.y, zval));
}
}

370
Marlin/src/gcode/calibrate/G28.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -27,7 +27,7 @@
#include "../../module/stepper.h"
#include "../../module/endstops.h"
#if HOTENDS > 1
#if HAS_MULTI_HOTEND
#include "../../module/tool_change.h"
#endif
@@ -45,12 +45,24 @@
#include "../../feature/bltouch.h"
#endif
#include "../../lcd/ultralcd.h"
#include "../../lcd/marlinui.h"
#if ENABLED(EXTENSIBLE_UI)
#include "../../lcd/extui/ui_api.h"
#elif ENABLED(DWIN_CREALITY_LCD)
#include "../../lcd/e3v2/creality/dwin.h"
#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED)
#include "../../lcd/e3v2/enhanced/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)
#include "../../feature/spindle_laser.h"
#endif
#define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
#include "../../core/debug_out.h"
@@ -62,7 +74,7 @@
current_position.set(0.0, 0.0);
sync_plan_position();
const int x_axis_home_dir = x_home_dir(active_extruder);
const int x_axis_home_dir = TOOL_X_HOME_DIR(active_extruder);
const float mlx = max_length(X_AXIS),
mly = max_length(Y_AXIS),
@@ -85,13 +97,13 @@
};
#endif
do_blocking_move_to_xy(1.5 * mlx * x_axis_home_dir, 1.5 * mly * home_dir(Y_AXIS), fr_mm_s);
do_blocking_move_to_xy(1.5 * mlx * x_axis_home_dir, 1.5 * mly * Y_HOME_DIR, fr_mm_s);
endstops.validate_homing_move();
current_position.set(0.0, 0.0);
#if ENABLED(SENSORLESS_HOMING)
#if ENABLED(SENSORLESS_HOMING) && DISABLED(ENDSTOPS_ALWAYS_ON_DEFAULT)
tmc_disable_stallguard(stepperX, stealth_states.x);
tmc_disable_stallguard(stepperY, stealth_states.y);
#if AXIS_HAS_STALLGUARD(X2)
@@ -108,15 +120,10 @@
#if ENABLED(Z_SAFE_HOMING)
inline void home_z_safely() {
DEBUG_SECTION(log_G28, "home_z_safely", DEBUGGING(LEVELING));
// Disallow Z homing if X or Y are unknown
if (!TEST(axis_known_position, X_AXIS) || !TEST(axis_known_position, Y_AXIS)) {
LCD_MESSAGEPGM(MSG_ERR_Z_HOMING);
SERIAL_ECHO_MSG(STR_ERR_Z_HOMING_SER);
return;
}
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("home_z_safely >>>");
// Disallow Z homing if X or Y homing is needed
if (homing_needed_error(_BV(X_AXIS) | _BV(Y_AXIS))) return;
sync_plan_position();
@@ -124,24 +131,26 @@
* Move the Z probe (or just the nozzle) to the safe homing point
* (Z is already at the right height)
*/
destination.set(safe_homing_xy, current_position.z);
#if HOMING_Z_WITH_PROBE
destination -= probe.offset_xy;
constexpr xy_float_t safe_homing_xy = { Z_SAFE_HOMING_X_POINT, Z_SAFE_HOMING_Y_POINT };
#if HAS_HOME_OFFSET
xy_float_t okay_homing_xy = safe_homing_xy;
okay_homing_xy -= home_offset;
#else
constexpr xy_float_t okay_homing_xy = safe_homing_xy;
#endif
destination.set(okay_homing_xy, current_position.z);
TERN_(HOMING_Z_WITH_PROBE, destination -= probe.offset_xy);
if (position_is_reachable(destination)) {
if (DEBUGGING(LEVELING)) DEBUG_POS("home_z_safely", destination);
// This causes the carriage on Dual X to unpark
#if ENABLED(DUAL_X_CARRIAGE)
active_extruder_parked = false;
#endif
// Free the active extruder for movement
TERN_(DUAL_X_CARRIAGE, idex_set_parked(false));
#if ENABLED(SENSORLESS_HOMING)
safe_delay(500); // Short delay needed to settle
#endif
TERN_(SENSORLESS_HOMING, safe_delay(500)); // Short delay needed to settle
do_blocking_move_to_xy(destination);
homeaxis(Z_AXIS);
@@ -150,34 +159,34 @@
LCD_MESSAGEPGM(MSG_ZPROBE_OUT);
SERIAL_ECHO_MSG(STR_ZPROBE_OUT_SER);
}
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("<<< home_z_safely");
}
#endif // Z_SAFE_HOMING
#if ENABLED(IMPROVE_HOMING_RELIABILITY)
slow_homing_t begin_slow_homing() {
slow_homing_t slow_homing{0};
slow_homing.acceleration.set(planner.settings.max_acceleration_mm_per_s2[X_AXIS],
planner.settings.max_acceleration_mm_per_s2[Y_AXIS]);
motion_state_t begin_slow_homing() {
motion_state_t motion_state{0};
motion_state.acceleration.set(planner.settings.max_acceleration_mm_per_s2[X_AXIS],
planner.settings.max_acceleration_mm_per_s2[Y_AXIS]
OPTARG(DELTA, planner.settings.max_acceleration_mm_per_s2[Z_AXIS])
);
planner.settings.max_acceleration_mm_per_s2[X_AXIS] = 100;
planner.settings.max_acceleration_mm_per_s2[Y_AXIS] = 100;
TERN_(DELTA, planner.settings.max_acceleration_mm_per_s2[Z_AXIS] = 100);
#if HAS_CLASSIC_JERK
slow_homing.jerk_xy = planner.max_jerk;
planner.max_jerk.set(0, 0);
motion_state.jerk_state = planner.max_jerk;
planner.max_jerk.set(0, 0 OPTARG(DELTA, 0));
#endif
planner.reset_acceleration_rates();
return slow_homing;
return motion_state;
}
void end_slow_homing(const slow_homing_t &slow_homing) {
planner.settings.max_acceleration_mm_per_s2[X_AXIS] = slow_homing.acceleration.x;
planner.settings.max_acceleration_mm_per_s2[Y_AXIS] = slow_homing.acceleration.y;
#if HAS_CLASSIC_JERK
planner.max_jerk = slow_homing.jerk_xy;
#endif
void end_slow_homing(const motion_state_t &motion_state) {
planner.settings.max_acceleration_mm_per_s2[X_AXIS] = motion_state.acceleration.x;
planner.settings.max_acceleration_mm_per_s2[Y_AXIS] = motion_state.acceleration.y;
TERN_(DELTA, planner.settings.max_acceleration_mm_per_s2[Z_AXIS] = motion_state.acceleration.z);
TERN_(HAS_CLASSIC_JERK, planner.max_jerk = motion_state.jerk_state);
planner.reset_acceleration_rates();
}
@@ -191,22 +200,23 @@
* None Home to all axes with no parameters.
* With QUICK_HOME enabled XY will home together, then Z.
*
* O Home only if position is unknown
*
* Rn Raise by n mm/inches before homing
* L<bool> Force leveling state ON (if possible) or OFF after homing (Requires RESTORE_LEVELING_AFTER_G28 or ENABLE_LEVELING_AFTER_G28)
* O Home only if the position is not known and trusted
* R<linear> Raise by n mm/inches before homing
*
* Cartesian/SCARA parameters
*
* X Home to the X endstop
* Y Home to the Y endstop
* Z Home to the Z endstop
*
*/
void GcodeSuite::G28() {
if (DEBUGGING(LEVELING)) {
DEBUG_ECHOLNPGM(">>> G28");
log_machine_info();
}
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
TERN_(FULL_REPORT_TO_HOST_FEATURE, set_and_report_grblstate(M_HOMING));
#if ENABLED(DUAL_X_CARRIAGE)
bool IDEX_saved_duplication_state = extruder_duplication_enabled;
@@ -214,49 +224,53 @@ void GcodeSuite::G28() {
#endif
#if ENABLED(MARLIN_DEV_MODE)
if (parser.seen('S')) {
LOOP_XYZ(a) set_axis_is_at_home((AxisEnum)a);
if (parser.seen_test('S')) {
LOOP_LINEAR_AXES(a) set_axis_is_at_home((AxisEnum)a);
sync_plan_position();
SERIAL_ECHOLNPGM("Simulated Homing");
report_current_position();
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("<<< G28");
return;
}
#endif
// Home (O)nly if position is unknown
if (!homing_needed() && parser.boolval('O')) {
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> homing not needed, skip\n<<< G28");
if (!axes_should_home() && parser.seen_test('O')) {
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> homing not needed, skip");
return;
}
// Wait for planner moves to finish!
planner.synchronize();
TERN_(HAS_DWIN_E3V2_BASIC, DWIN_StartHoming());
TERN_(EXTENSIBLE_UI, ExtUI::onHomingStart());
planner.synchronize(); // Wait for planner moves to finish!
SET_SOFT_ENDSTOP_LOOSE(false); // Reset a leftover 'loose' motion state
// Disable the leveling matrix before homing
#if HAS_LEVELING
// Cancel the active G29 session
#if ENABLED(PROBE_MANUALLY)
g29_in_progress = false;
#endif
#if ENABLED(RESTORE_LEVELING_AFTER_G28)
const bool leveling_was_active = planner.leveling_active;
#endif
set_bed_leveling_enabled(false);
#if CAN_SET_LEVELING_AFTER_G28
const bool leveling_restore_state = parser.boolval('L', TERN1(RESTORE_LEVELING_AFTER_G28, planner.leveling_active));
#endif
#if ENABLED(CNC_WORKSPACE_PLANES)
workspace_plane = PLANE_XY;
#endif
// Cancel any prior G29 session
TERN_(PROBE_MANUALLY, g29_in_progress = false);
// Disable leveling before homing
TERN_(HAS_LEVELING, set_bed_leveling_enabled(false));
// Reset to the XY plane
TERN_(CNC_WORKSPACE_PLANES, workspace_plane = PLANE_XY);
// Count this command as movement / activity
reset_stepper_timeout();
#define HAS_CURRENT_HOME(N) (defined(N##_CURRENT_HOME) && N##_CURRENT_HOME != N##_CURRENT)
#define HAS_HOMING_CURRENT (HAS_CURRENT_HOME(X) || HAS_CURRENT_HOME(X2) || HAS_CURRENT_HOME(Y) || HAS_CURRENT_HOME(Y2))
#if HAS_CURRENT_HOME(X) || HAS_CURRENT_HOME(X2) || HAS_CURRENT_HOME(Y) || HAS_CURRENT_HOME(Y2) || (ENABLED(DELTA) && HAS_CURRENT_HOME(Z))
#define HAS_HOMING_CURRENT 1
#endif
#if HAS_HOMING_CURRENT
auto debug_current = [](PGM_P const s, const int16_t a, const int16_t b){
serialprintPGM(s); DEBUG_ECHOLNPAIR(" current: ", a, " -> ", b);
auto debug_current = [](PGM_P const s, const int16_t a, const int16_t b) {
DEBUG_ECHOPGM_P(s); DEBUG_ECHOLNPGM(" current: ", a, " -> ", b);
};
#if HAS_CURRENT_HOME(X)
const int16_t tmc_save_current_X = stepperX.getMilliamps();
@@ -278,23 +292,30 @@ void GcodeSuite::G28() {
stepperY2.rms_current(Y2_CURRENT_HOME);
if (DEBUGGING(LEVELING)) debug_current(PSTR("Y2"), tmc_save_current_Y2, Y2_CURRENT_HOME);
#endif
#if HAS_CURRENT_HOME(Z) && ENABLED(DELTA)
const int16_t tmc_save_current_Z = stepperZ.getMilliamps();
stepperZ.rms_current(Z_CURRENT_HOME);
if (DEBUGGING(LEVELING)) debug_current(PSTR("Z"), tmc_save_current_Z, Z_CURRENT_HOME);
#endif
#endif
#if ENABLED(IMPROVE_HOMING_RELIABILITY)
slow_homing_t slow_homing = begin_slow_homing();
motion_state_t saved_motion_state = begin_slow_homing();
#endif
// Always home with tool 0 active
#if HOTENDS > 1
#if HAS_MULTI_HOTEND
#if DISABLED(DELTA) || ENABLED(DELTA_HOME_TO_SAFE_ZONE)
const uint8_t old_tool_index = active_extruder;
#endif
// PARKING_EXTRUDER homing requires different handling of movement / solenoid activation, depending on the side of homing
#if ENABLED(PARKING_EXTRUDER)
const bool pe_final_change_must_unpark = parking_extruder_unpark_after_homing(old_tool_index, X_HOME_DIR + 1 == old_tool_index * 2);
#endif
tool_change(0, true);
#endif
#if HAS_DUPLICATION_MODE
extruder_duplication_enabled = false;
#endif
TERN_(HAS_DUPLICATION_MODE, set_duplication_enabled(false));
remember_feedrate_scaling_off();
@@ -306,46 +327,60 @@ void GcodeSuite::G28() {
home_delta();
#if ENABLED(IMPROVE_HOMING_RELIABILITY)
end_slow_homing(slow_homing);
TERN_(IMPROVE_HOMING_RELIABILITY, end_slow_homing(saved_motion_state));
#elif ENABLED(AXEL_TPARA)
constexpr bool doZ = true; // for NANODLP_Z_SYNC if your DLP is on a TPARA
home_TPARA();
#else
#define _UNSAFE(A) (homeZ && TERN0(Z_SAFE_HOMING, axes_should_home(_BV(A##_AXIS))))
const bool homeZ = TERN0(HAS_Z_AXIS, parser.seen_test('Z')),
LINEAR_AXIS_LIST( // Other axes should be homed before Z safe-homing
needX = _UNSAFE(X), needY = _UNSAFE(Y), needZ = false, // UNUSED
needI = _UNSAFE(I), needJ = _UNSAFE(J), needK = _UNSAFE(K)
),
LINEAR_AXIS_LIST( // Home each axis if needed or flagged
homeX = needX || parser.seen_test('X'),
homeY = needY || parser.seen_test('Y'),
homeZZ = homeZ,
homeI = needI || parser.seen_test(AXIS4_NAME), homeJ = needJ || parser.seen_test(AXIS5_NAME), homeK = needK || parser.seen_test(AXIS6_NAME),
),
home_all = LINEAR_AXIS_GANG( // Home-all if all or none are flagged
homeX == homeX, && homeY == homeX, && homeZ == homeX,
&& homeI == homeX, && homeJ == homeX, && homeK == homeX
),
LINEAR_AXIS_LIST(
doX = home_all || homeX, doY = home_all || homeY, doZ = home_all || homeZ,
doI = home_all || homeI, doJ = home_all || homeJ, doK = home_all || homeK
);
#if HAS_Z_AXIS
UNUSED(needZ); UNUSED(homeZZ);
#else
constexpr bool doZ = false;
#endif
#else // NOT DELTA
TERN_(HOME_Z_FIRST, if (doZ) homeaxis(Z_AXIS));
const bool homeX = parser.seen('X'), homeY = parser.seen('Y'), homeZ = parser.seen('Z'),
home_all = homeX == homeY && homeX == homeZ, // All or None
doX = home_all || homeX, doY = home_all || homeY, doZ = home_all || homeZ;
const float z_homing_height = parser.seenval('R') ? parser.value_linear_units() : Z_HOMING_HEIGHT;
destination = current_position;
#if Z_HOME_DIR > 0 // If homing away from BED do Z first
if (doZ) homeaxis(Z_AXIS);
#endif
const float z_homing_height =
(DISABLED(UNKNOWN_Z_NO_RAISE) || TEST(axis_known_position, Z_AXIS))
? (parser.seenval('R') ? parser.value_linear_units() : Z_HOMING_HEIGHT)
: 0;
if (z_homing_height && (doX || doY)) {
if (z_homing_height && (LINEAR_AXIS_GANG(doX, || doY, || TERN0(Z_SAFE_HOMING, doZ), || doI, || doJ, || doK))) {
// Raise Z before homing any other axes and z is not already high enough (never lower z)
destination.z = z_homing_height + (TEST(axis_known_position, Z_AXIS) ? 0.0f : current_position.z);
if (destination.z > current_position.z) {
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("Raise Z (before homing) to ", destination.z);
do_blocking_move_to_z(destination.z);
}
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Raise Z (before homing) by ", z_homing_height);
do_z_clearance(z_homing_height);
TERN_(BLTOUCH, bltouch.init());
}
#if ENABLED(QUICK_HOME)
if (doX && doY) quick_home_xy();
#endif
// Diagonal move first if both are homing
TERN_(QUICK_HOME, if (doX && doY) quick_home_xy());
// Home Y (before X)
if (ENABLED(HOME_Y_BEFORE_X) && (doY || (ENABLED(CODEPENDENT_XY_HOMING) && doX)))
if (ENABLED(HOME_Y_BEFORE_X) && (doY || TERN0(CODEPENDENT_XY_HOMING, doX)))
homeaxis(Y_AXIS);
// Home X
@@ -358,16 +393,14 @@ void GcodeSuite::G28() {
homeaxis(X_AXIS);
// Remember this extruder's position for later tool change
inactive_extruder_x_pos = current_position.x;
inactive_extruder_x = current_position.x;
// Home the 1st (left) extruder
active_extruder = 0;
homeaxis(X_AXIS);
// Consider the active extruder to be parked
raised_parked_position = current_position;
delayed_move_time = 0;
active_extruder_parked = true;
// Consider the active extruder to be in its "parked" position
idex_set_parked();
#else
@@ -380,40 +413,34 @@ void GcodeSuite::G28() {
if (DISABLED(HOME_Y_BEFORE_X) && doY)
homeaxis(Y_AXIS);
#if ENABLED(IMPROVE_HOMING_RELIABILITY)
end_slow_homing(slow_homing);
#endif
TERN_(IMPROVE_HOMING_RELIABILITY, end_slow_homing(saved_motion_state));
// Home Z last if homing towards the bed
#if Z_HOME_DIR < 0
#if HAS_Z_AXIS && DISABLED(HOME_Z_FIRST)
if (doZ) {
#if ENABLED(BLTOUCH)
bltouch.init();
#endif
#if ENABLED(Z_SAFE_HOMING)
home_z_safely();
#else
homeaxis(Z_AXIS);
#if EITHER(Z_MULTI_ENDSTOPS, Z_STEPPER_AUTO_ALIGN)
stepper.set_all_z_lock(false);
stepper.set_separate_multi_axis(false);
#endif
#if HOMING_Z_WITH_PROBE && defined(Z_AFTER_PROBING)
#if Z_AFTER_HOMING > Z_AFTER_PROBING
do_blocking_move_to_z(Z_AFTER_HOMING);
#else
probe.move_z_after_probing();
#endif
#elif defined(Z_AFTER_HOMING)
do_blocking_move_to_z(Z_AFTER_HOMING);
#endif
TERN(Z_SAFE_HOMING, home_z_safely(), homeaxis(Z_AXIS));
probe.move_z_after_homing();
}
#endif
} // doZ
#endif // Z_HOME_DIR < 0
#if LINEAR_AXES >= 4
if (doI) homeaxis(I_AXIS);
#endif
#if LINEAR_AXES >= 5
if (doJ) homeaxis(J_AXIS);
#endif
#if LINEAR_AXES >= 6
if (doK) homeaxis(K_AXIS);
#endif
sync_plan_position();
#endif // !DELTA (G28)
#endif
/**
* Preserve DXC mode across a G28 for IDEX printers in DXC_DUPLICATION_MODE.
@@ -423,34 +450,28 @@ void GcodeSuite::G28() {
*/
#if ENABLED(DUAL_X_CARRIAGE)
if (dxc_is_duplicating()) {
if (idex_is_duplicating()) {
#if ENABLED(IMPROVE_HOMING_RELIABILITY)
slow_homing = begin_slow_homing();
#endif
TERN_(IMPROVE_HOMING_RELIABILITY, saved_motion_state = begin_slow_homing());
// Always home the 2nd (right) extruder first
active_extruder = 1;
homeaxis(X_AXIS);
// Remember this extruder's position for later tool change
inactive_extruder_x_pos = current_position.x;
inactive_extruder_x = current_position.x;
// Home the 1st (left) extruder
active_extruder = 0;
homeaxis(X_AXIS);
// Consider the active extruder to be parked
raised_parked_position = current_position;
delayed_move_time = 0;
active_extruder_parked = true;
extruder_duplication_enabled = IDEX_saved_duplication_state;
dual_x_carriage_mode = IDEX_saved_mode;
stepper.set_directions();
idex_set_parked();
#if ENABLED(IMPROVE_HOMING_RELIABILITY)
end_slow_homing(slow_homing);
#endif
dual_x_carriage_mode = IDEX_saved_mode;
set_duplication_enabled(IDEX_saved_duplication_state);
TERN_(IMPROVE_HOMING_RELIABILITY, end_slow_homing(saved_motion_state));
}
#endif // DUAL_X_CARRIAGE
@@ -458,24 +479,18 @@ void GcodeSuite::G28() {
endstops.not_homing();
// Clear endstop state for polled stallGuard endstops
#if ENABLED(SPI_ENDSTOPS)
endstops.clear_endstop_state();
#endif
TERN_(SPI_ENDSTOPS, endstops.clear_endstop_state());
#if BOTH(DELTA, DELTA_HOME_TO_SAFE_ZONE)
// move to a height where we can use the full xy-area
do_blocking_move_to_z(delta_clip_start_height);
#endif
// Move to a height where we can use the full xy-area
TERN_(DELTA_HOME_TO_SAFE_ZONE, do_blocking_move_to_z(delta_clip_start_height));
#if ENABLED(RESTORE_LEVELING_AFTER_G28)
set_bed_leveling_enabled(leveling_was_active);
#endif
TERN_(CAN_SET_LEVELING_AFTER_G28, if (leveling_restore_state) set_bed_leveling_enabled());
restore_feedrate_and_scaling();
// Restore the active tool after homing
#if HOTENDS > 1 && (DISABLED(DELTA) || ENABLED(DELTA_HOME_TO_SAFE_ZONE))
tool_change(old_tool_index, NONE(PARKING_EXTRUDER, DUAL_X_CARRIAGE)); // Do move if one of these
#if HAS_MULTI_HOTEND && (DISABLED(DELTA) || ENABLED(DELTA_HOME_TO_SAFE_ZONE))
tool_change(old_tool_index, TERN(PARKING_EXTRUDER, !pe_final_change_must_unpark, DISABLED(DUAL_X_CARRIAGE))); // Do move if one of these
#endif
#if HAS_HOMING_CURRENT
@@ -492,26 +507,43 @@ void GcodeSuite::G28() {
#if HAS_CURRENT_HOME(Y2)
stepperY2.rms_current(tmc_save_current_Y2);
#endif
#endif
#if HAS_CURRENT_HOME(Z) && ENABLED(DELTA)
stepperZ.rms_current(tmc_save_current_Z);
#endif
#if HAS_CURRENT_HOME(I)
stepperI.rms_current(tmc_save_current_I);
#endif
#if HAS_CURRENT_HOME(J)
stepperJ.rms_current(tmc_save_current_J);
#endif
#if HAS_CURRENT_HOME(K)
stepperK.rms_current(tmc_save_current_K);
#endif
#endif // HAS_HOMING_CURRENT
ui.refresh();
TERN_(HAS_DWIN_E3V2_BASIC, DWIN_CompletedHoming());
TERN_(EXTENSIBLE_UI, ExtUI::onHomingComplete());
report_current_position();
if (ENABLED(NANODLP_Z_SYNC) && (doZ || ENABLED(NANODLP_ALL_AXIS)))
SERIAL_ECHOLNPGM(STR_Z_MOVE_COMP);
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("<<< G28");
TERN_(FULL_REPORT_TO_HOST_FEATURE, set_and_report_grblstate(M_IDLE));
#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] = {
X_AXIS, Y_AXIS, Z_AXIS,
X_AXIS, Y_AXIS, Z_AXIS, Z_AXIS,
E_AXIS, E_AXIS, E_AXIS, E_AXIS, E_AXIS, E_AXIS
LINEAR_AXIS_LIST(X_AXIS, Y_AXIS, Z_AXIS, I_AXIS, J_AXIS, K_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]));

152
Marlin/src/gcode/calibrate/G33.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -29,13 +29,13 @@
#include "../../module/motion.h"
#include "../../module/stepper.h"
#include "../../module/endstops.h"
#include "../../lcd/ultralcd.h"
#include "../../lcd/marlinui.h"
#if HAS_BED_PROBE
#include "../../module/probe.h"
#endif
#if HOTENDS > 1
#if HAS_MULTI_HOTEND
#include "../../module/tool_change.h"
#endif
@@ -63,25 +63,24 @@ enum CalEnum : char { // the 7 main calibration points -
#define LOOP_CAL_RAD(VAR) LOOP_CAL_PT(VAR, __A, _7P_STEP)
#define LOOP_CAL_ACT(VAR, _4P, _OP) LOOP_CAL_PT(VAR, _OP ? _AB : __A, _4P ? _4P_STEP : _7P_STEP)
#if HOTENDS > 1
#if HAS_MULTI_HOTEND
const uint8_t old_tool_index = active_extruder;
#define AC_CLEANUP() ac_cleanup(old_tool_index)
#else
#define AC_CLEANUP() ac_cleanup()
#endif
float lcd_probe_pt(const xy_pos_t &xy);
float dcr;
void ac_home() {
endstops.enable(true);
TERN_(SENSORLESS_HOMING, probe.set_homing_current(true));
home_delta();
TERN_(SENSORLESS_HOMING, probe.set_homing_current(false));
endstops.not_homing();
}
void ac_setup(const bool reset_bed) {
#if HOTENDS > 1
tool_change(0, true);
#endif
TERN_(HAS_MULTI_HOTEND, tool_change(0, true));
planner.synchronize();
remember_feedrate_scaling_off();
@@ -91,26 +90,16 @@ void ac_setup(const bool reset_bed) {
#endif
}
void ac_cleanup(
#if HOTENDS > 1
const uint8_t old_tool_index
#endif
) {
#if ENABLED(DELTA_HOME_TO_SAFE_ZONE)
do_blocking_move_to_z(delta_clip_start_height);
#endif
#if HAS_BED_PROBE
probe.stow();
#endif
void ac_cleanup(TERN_(HAS_MULTI_HOTEND, const uint8_t old_tool_index)) {
TERN_(DELTA_HOME_TO_SAFE_ZONE, do_blocking_move_to_z(delta_clip_start_height));
TERN_(HAS_BED_PROBE, probe.stow());
restore_feedrate_and_scaling();
#if HOTENDS > 1
tool_change(old_tool_index, true);
#endif
TERN_(HAS_MULTI_HOTEND, tool_change(old_tool_index, true));
}
void print_signed_float(PGM_P const prefix, const float &f) {
void print_signed_float(PGM_P const prefix, const_float_t f) {
SERIAL_ECHOPGM(" ");
serialprintPGM(prefix);
SERIAL_ECHOPGM_P(prefix);
SERIAL_CHAR(':');
if (f >= 0) SERIAL_CHAR('+');
SERIAL_ECHO_F(f, 2);
@@ -120,14 +109,14 @@ void print_signed_float(PGM_P const prefix, const float &f) {
* - Print the delta settings
*/
static void print_calibration_settings(const bool end_stops, const bool tower_angles) {
SERIAL_ECHOPAIR(".Height:", delta_height);
SERIAL_ECHOPGM(".Height:", delta_height);
if (end_stops) {
print_signed_float(PSTR("Ex"), delta_endstop_adj.a);
print_signed_float(PSTR("Ey"), delta_endstop_adj.b);
print_signed_float(PSTR("Ez"), delta_endstop_adj.c);
}
if (end_stops && tower_angles) {
SERIAL_ECHOPAIR(" Radius:", delta_radius);
SERIAL_ECHOPGM(" Radius:", delta_radius);
SERIAL_EOL();
SERIAL_CHAR('.');
SERIAL_ECHO_SP(13);
@@ -138,7 +127,7 @@ static void print_calibration_settings(const bool end_stops, const bool tower_an
print_signed_float(PSTR("Tz"), delta_tower_angle_trim.c);
}
if ((!end_stops && tower_angles) || (end_stops && !tower_angles)) { // XOR
SERIAL_ECHOPAIR(" Radius:", delta_radius);
SERIAL_ECHOPGM(" Radius:", delta_radius);
}
SERIAL_EOL();
}
@@ -188,9 +177,9 @@ static float std_dev_points(float z_pt[NPP + 1], const bool _0p_cal, const bool
/**
* - Probe a point
*/
static float calibration_probe(const xy_pos_t &xy, const bool stow) {
static float calibration_probe(const xy_pos_t &xy, const bool stow, const bool probe_at_offset) {
#if HAS_BED_PROBE
return probe.probe_at_point(xy, stow ? PROBE_PT_STOW : PROBE_PT_RAISE, 0, true, false);
return probe.probe_at_point(xy, stow ? PROBE_PT_STOW : PROBE_PT_RAISE, 0, true, probe_at_offset);
#else
UNUSED(stow);
return lcd_probe_pt(xy);
@@ -200,7 +189,7 @@ static float calibration_probe(const xy_pos_t &xy, const bool stow) {
/**
* - Probe a grid
*/
static bool probe_calibration_points(float z_pt[NPP + 1], const int8_t probe_points, const bool towers_set, const bool stow_after_each) {
static bool probe_calibration_points(float z_pt[NPP + 1], const int8_t probe_points, const bool towers_set, const bool stow_after_each, const bool probe_at_offset) {
const bool _0p_calibration = probe_points == 0,
_1p_calibration = probe_points == 1 || probe_points == -1,
_4p_calibration = probe_points == 2,
@@ -222,11 +211,9 @@ static bool probe_calibration_points(float z_pt[NPP + 1], const int8_t probe_poi
if (!_0p_calibration) {
const float dcr = delta_calibration_radius();
if (!_7p_no_intermediates && !_7p_4_intermediates && !_7p_11_intermediates) { // probe the center
const xy_pos_t center{0};
z_pt[CEN] += calibration_probe(center, stow_after_each);
z_pt[CEN] += calibration_probe(center, stow_after_each, probe_at_offset);
if (isnan(z_pt[CEN])) return false;
}
@@ -237,7 +224,7 @@ static bool probe_calibration_points(float z_pt[NPP + 1], const int8_t probe_poi
const float a = RADIANS(210 + (360 / NPP) * (rad - 1)),
r = dcr * 0.1;
const xy_pos_t vec = { cos(a), sin(a) };
z_pt[CEN] += calibration_probe(vec * r, stow_after_each);
z_pt[CEN] += calibration_probe(vec * r, stow_after_each, probe_at_offset);
if (isnan(z_pt[CEN])) return false;
}
z_pt[CEN] /= float(_7p_2_intermediates ? 7 : probe_points);
@@ -262,7 +249,7 @@ static bool probe_calibration_points(float z_pt[NPP + 1], const int8_t probe_poi
r = dcr * (1 - 0.1 * (zig_zag ? offset - circle : circle)),
interpol = FMOD(rad, 1);
const xy_pos_t vec = { cos(a), sin(a) };
const float z_temp = calibration_probe(vec * r, stow_after_each);
const float z_temp = calibration_probe(vec * r, stow_after_each, probe_at_offset);
if (isnan(z_temp)) return false;
// split probe point to neighbouring calibration points
z_pt[uint8_t(LROUND(rad - interpol + NPP - 1)) % NPP + 1] += z_temp * sq(cos(RADIANS(interpol * 90)));
@@ -289,7 +276,6 @@ static bool probe_calibration_points(float z_pt[NPP + 1], const int8_t probe_poi
static void reverse_kinematics_probe_points(float z_pt[NPP + 1], abc_float_t mm_at_pt_axis[NPP + 1]) {
xyz_pos_t pos{0};
const float dcr = delta_calibration_radius();
LOOP_CAL_ALL(rad) {
const float a = RADIANS(210 + (360 / NPP) * (rad - 1)),
r = (rad == CEN ? 0.0f : dcr);
@@ -300,7 +286,7 @@ static void reverse_kinematics_probe_points(float z_pt[NPP + 1], abc_float_t mm_
}
static void forward_kinematics_probe_points(abc_float_t mm_at_pt_axis[NPP + 1], float z_pt[NPP + 1]) {
const float r_quot = delta_calibration_radius() / delta_radius;
const float r_quot = dcr / delta_radius;
#define ZPP(N,I,A) (((1.0f + r_quot * (N)) / 3.0f) * mm_at_pt_axis[I].A)
#define Z00(I, A) ZPP( 0, I, A)
@@ -341,7 +327,7 @@ static void calc_kinematics_diff_probe_points(float z_pt[NPP + 1], abc_float_t d
}
static float auto_tune_h() {
const float r_quot = delta_calibration_radius() / delta_radius;
const float r_quot = dcr / delta_radius;
return RECIPROCAL(r_quot / (2.0f / 3.0f)); // (2/3)/CR
}
@@ -362,7 +348,7 @@ static float auto_tune_a() {
abc_float_t delta_e = { 0.0f }, delta_t = { 0.0f };
delta_t.reset();
LOOP_XYZ(axis) {
LOOP_LINEAR_AXES(axis) {
delta_t[axis] = diff;
calc_kinematics_diff_probe_points(z_pt, delta_e, delta_r, delta_t);
delta_t[axis] = 0;
@@ -386,6 +372,8 @@ static float auto_tune_a() {
* P3 Probe all positions: center, towers and opposite towers. Calibrate all.
* P4-P10 Probe all positions at different intermediate locations and average them.
*
* Rn.nn override default calibration Radius
*
* T Don't calibrate tower angle corrections
*
* Cn.nn Calibration precision; when omitted calibrates to maximum precision
@@ -399,16 +387,46 @@ static float auto_tune_a() {
* V3 Report settings and probe results
*
* E Engage the probe for each point
*
* O Probe at offset points (this is wrong but it seems to work)
*
* With SENSORLESS_PROBING:
* Use these flags to calibrate stall sensitivity: (e.g., `G33 P1 Y Z` to calibrate X only.)
* X Don't activate stallguard on X.
* Y Don't activate stallguard on Y.
* Z Don't activate stallguard on Z.
*/
void GcodeSuite::G33() {
TERN_(FULL_REPORT_TO_HOST_FEATURE, set_and_report_grblstate(M_PROBE));
const int8_t probe_points = parser.intval('P', DELTA_CALIBRATION_DEFAULT_POINTS);
if (!WITHIN(probe_points, 0, 10)) {
SERIAL_ECHOLNPGM("?(P)oints implausible (0-10).");
return;
}
const bool towers_set = !parser.seen('T');
const bool probe_at_offset = TERN0(HAS_PROBE_XY_OFFSET, parser.boolval('O')),
towers_set = !parser.seen_test('T');
float max_dcr = dcr = DELTA_PRINTABLE_RADIUS;
#if HAS_PROBE_XY_OFFSET
// For offset probes the calibration radius is set to a safe but non-optimal value
dcr -= HYPOT(probe.offset_xy.x, probe.offset_xy.y);
if (probe_at_offset) {
// With probe positions both probe and nozzle need to be within the printable area
max_dcr = dcr;
}
// else with nozzle positions there is a risk of the probe being outside the bed
// but as long the nozzle stays within the printable area there is no risk of
// the effector crashing into the towers.
#endif
if (parser.seenval('R')) dcr = parser.value_float();
if (!WITHIN(dcr, 0, max_dcr)) {
SERIAL_ECHOLNPGM("?calibration (R)adius implausible.");
return;
}
const float calibration_precision = parser.floatval('C', 0.0f);
if (calibration_precision < 0) {
@@ -428,7 +446,13 @@ void GcodeSuite::G33() {
return;
}
const bool stow_after_each = parser.seen('E');
const bool stow_after_each = parser.seen_test('E');
#if HAS_DELTA_SENSORLESS_PROBING
probe.test_sensitivity.x = !parser.seen_test('X');
TERN_(HAS_Y_AXIS, probe.test_sensitivity.y = !parser.seen_test('Y'));
TERN_(HAS_Z_AXIS, probe.test_sensitivity.z = !parser.seen_test('Z'));
#endif
const bool _0p_calibration = probe_points == 0,
_1p_calibration = probe_points == 1 || probe_points == -1,
@@ -452,21 +476,9 @@ void GcodeSuite::G33() {
SERIAL_ECHOLNPGM("G33 Auto Calibrate");
const float dcr = delta_calibration_radius();
if (!_1p_calibration && !_0p_calibration) { // test if the outer radius is reachable
LOOP_CAL_RAD(axis) {
const float a = RADIANS(210 + (360 / NPP) * (axis - 1));
if (!position_is_reachable(cos(a) * dcr, sin(a) * dcr)) {
SERIAL_ECHOLNPGM("?Bed calibration radius implausible.");
return;
}
}
}
// Report settings
PGM_P checkingac = PSTR("Checking... AC");
serialprintPGM(checkingac);
PGM_P const checkingac = PSTR("Checking... AC");
SERIAL_ECHOPGM_P(checkingac);
if (verbose_level == 0) SERIAL_ECHOPGM(" (DRY-RUN)");
SERIAL_EOL();
ui.set_status_P(checkingac);
@@ -486,9 +498,9 @@ void GcodeSuite::G33() {
// Probe the points
zero_std_dev_old = zero_std_dev;
if (!probe_calibration_points(z_at_pt, probe_points, towers_set, stow_after_each)) {
if (!probe_calibration_points(z_at_pt, probe_points, towers_set, stow_after_each, probe_at_offset)) {
SERIAL_ECHOLNPGM("Correct delta settings with M665 and M666");
return AC_CLEANUP();
return ac_cleanup(TERN_(HAS_MULTI_HOTEND, old_tool_index));
}
zero_std_dev = std_dev_points(z_at_pt, _0p_calibration, _1p_calibration, _4p_calibration, _4p_opposite_points);
@@ -525,11 +537,11 @@ void GcodeSuite::G33() {
#define Z0(I) ZP(0, I)
// calculate factors
if (_7p_9_center) calibration_radius_factor = 0.9f;
if (_7p_9_center) dcr *= 0.9f;
h_factor = auto_tune_h();
r_factor = auto_tune_r();
a_factor = auto_tune_a();
calibration_radius_factor = 1.0f;
dcr /= 0.9f;
switch (probe_points) {
case 0:
@@ -538,7 +550,7 @@ void GcodeSuite::G33() {
case 1:
test_precision = 0.0f; // forced end
LOOP_XYZ(axis) e_delta[axis] = +Z4(CEN);
LOOP_LINEAR_AXES(axis) e_delta[axis] = +Z4(CEN);
break;
case 2:
@@ -586,21 +598,21 @@ void GcodeSuite::G33() {
// Normalize angles to least-squares
if (_angle_results) {
float a_sum = 0.0f;
LOOP_XYZ(axis) a_sum += delta_tower_angle_trim[axis];
LOOP_XYZ(axis) delta_tower_angle_trim[axis] -= a_sum / 3.0f;
LOOP_LINEAR_AXES(axis) a_sum += delta_tower_angle_trim[axis];
LOOP_LINEAR_AXES(axis) delta_tower_angle_trim[axis] -= a_sum / 3.0f;
}
// adjust delta_height and endstops by the max amount
const float z_temp = _MAX(delta_endstop_adj.a, delta_endstop_adj.b, delta_endstop_adj.c);
delta_height -= z_temp;
LOOP_XYZ(axis) delta_endstop_adj[axis] -= z_temp;
LOOP_LINEAR_AXES(axis) delta_endstop_adj[axis] -= z_temp;
}
recalc_delta_settings();
NOMORE(zero_std_dev_min, zero_std_dev);
// print report
if (verbose_level == 3)
if (verbose_level == 3 || verbose_level == 0)
print_calibration_results(z_at_pt, _tower_results, _opposite_results);
if (verbose_level != 0) { // !dry run
@@ -641,8 +653,8 @@ void GcodeSuite::G33() {
}
}
else { // dry run
PGM_P enddryrun = PSTR("End DRY-RUN");
serialprintPGM(enddryrun);
PGM_P const enddryrun = PSTR("End DRY-RUN");
SERIAL_ECHOPGM_P(enddryrun);
SERIAL_ECHO_SP(35);
SERIAL_ECHOLNPAIR_F("std dev:", zero_std_dev, 3);
@@ -659,7 +671,9 @@ void GcodeSuite::G33() {
}
while (((zero_std_dev < test_precision && iterations < 31) || iterations <= force_iterations) && zero_std_dev > calibration_precision);
AC_CLEANUP();
ac_cleanup(TERN_(HAS_MULTI_HOTEND, old_tool_index));
TERN_(FULL_REPORT_TO_HOST_FEATURE, set_and_report_grblstate(M_IDLE));
}
#endif // DELTA_AUTO_CALIBRATION

View File

@@ -0,0 +1,157 @@
/**
* 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/MarlinConfigPre.h"
#if ENABLED(MECHANICAL_GANTRY_CALIBRATION)
#include "../gcode.h"
#include "../../module/motion.h"
#include "../../module/stepper.h"
#include "../../module/endstops.h"
#if HAS_LEVELING
#include "../../feature/bedlevel/bedlevel.h"
#endif
#define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
#include "../../core/debug_out.h"
void GcodeSuite::G34() {
// Home before the alignment procedure
home_if_needed();
TERN_(HAS_LEVELING, TEMPORARY_BED_LEVELING_STATE(false));
SET_SOFT_ENDSTOP_LOOSE(true);
TemporaryGlobalEndstopsState unlock_z(false);
#ifdef GANTRY_CALIBRATION_COMMANDS_PRE
gcode.process_subcommands_now_P(PSTR(GANTRY_CALIBRATION_COMMANDS_PRE));
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Sub Commands Processed");
#endif
#ifdef GANTRY_CALIBRATION_SAFE_POSITION
// Move XY to safe position
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Parking XY");
const xy_pos_t safe_pos = GANTRY_CALIBRATION_SAFE_POSITION;
do_blocking_move_to(safe_pos, MMM_TO_MMS(GANTRY_CALIBRATION_XY_PARK_FEEDRATE));
#endif
const float move_distance = parser.intval('Z', GANTRY_CALIBRATION_EXTRA_HEIGHT),
zbase = ENABLED(GANTRY_CALIBRATION_TO_MIN) ? Z_MIN_POS : Z_MAX_POS,
zpounce = zbase - move_distance, zgrind = zbase + move_distance;
// Move Z to pounce position
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Setting Z Pounce");
do_blocking_move_to_z(zpounce, homing_feedrate(Z_AXIS));
// Store current motor settings, then apply reduced value
#define _REDUCE_CURRENT ANY(HAS_MOTOR_CURRENT_SPI, HAS_MOTOR_CURRENT_PWM, HAS_MOTOR_CURRENT_DAC, HAS_MOTOR_CURRENT_I2C, HAS_TRINAMIC_CONFIG)
#if _REDUCE_CURRENT
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Reducing Current");
#endif
#if HAS_MOTOR_CURRENT_SPI
const uint16_t target_current = parser.intval('S', GANTRY_CALIBRATION_CURRENT);
const uint32_t previous_current = stepper.motor_current_setting[Z_AXIS];
stepper.set_digipot_current(Z_AXIS, target_current);
#elif HAS_MOTOR_CURRENT_PWM
const uint16_t target_current = parser.intval('S', GANTRY_CALIBRATION_CURRENT);
const uint32_t previous_current = stepper.motor_current_setting[Z_AXIS];
stepper.set_digipot_current(1, target_current);
#elif HAS_MOTOR_CURRENT_DAC
const float target_current = parser.floatval('S', GANTRY_CALIBRATION_CURRENT);
const float previous_current = dac_amps(Z_AXIS, target_current);
stepper_dac.set_current_value(Z_AXIS, target_current);
#elif HAS_MOTOR_CURRENT_I2C
const uint16_t target_current = parser.intval('S', GANTRY_CALIBRATION_CURRENT);
previous_current = dac_amps(Z_AXIS);
digipot_i2c.set_current(Z_AXIS, target_current)
#elif HAS_TRINAMIC_CONFIG
const uint16_t target_current = parser.intval('S', GANTRY_CALIBRATION_CURRENT);
static uint16_t previous_current_arr[NUM_Z_STEPPER_DRIVERS];
#if AXIS_IS_TMC(Z)
previous_current_arr[0] = stepperZ.getMilliamps();
stepperZ.rms_current(target_current);
#endif
#if AXIS_IS_TMC(Z2)
previous_current_arr[1] = stepperZ2.getMilliamps();
stepperZ2.rms_current(target_current);
#endif
#if AXIS_IS_TMC(Z3)
previous_current_arr[2] = stepperZ3.getMilliamps();
stepperZ3.rms_current(target_current);
#endif
#if AXIS_IS_TMC(Z4)
previous_current_arr[3] = stepperZ4.getMilliamps();
stepperZ4.rms_current(target_current);
#endif
#endif
// Do Final Z move to adjust
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Final Z Move");
do_blocking_move_to_z(zgrind, MMM_TO_MMS(GANTRY_CALIBRATION_FEEDRATE));
// Back off end plate, back to normal motion range
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Z Backoff");
do_blocking_move_to_z(zpounce, MMM_TO_MMS(GANTRY_CALIBRATION_FEEDRATE));
#if _REDUCE_CURRENT
// Reset current to original values
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Restore Current");
#endif
#if HAS_MOTOR_CURRENT_SPI
stepper.set_digipot_current(Z_AXIS, previous_current);
#elif HAS_MOTOR_CURRENT_PWM
stepper.set_digipot_current(1, previous_current);
#elif HAS_MOTOR_CURRENT_DAC
stepper_dac.set_current_value(Z_AXIS, previous_current);
#elif HAS_MOTOR_CURRENT_I2C
digipot_i2c.set_current(Z_AXIS, previous_current)
#elif HAS_TRINAMIC_CONFIG
#if AXIS_IS_TMC(Z)
stepperZ.rms_current(previous_current_arr[0]);
#endif
#if AXIS_IS_TMC(Z2)
stepperZ2.rms_current(previous_current_arr[1]);
#endif
#if AXIS_IS_TMC(Z3)
stepperZ3.rms_current(previous_current_arr[2]);
#endif
#if AXIS_IS_TMC(Z4)
stepperZ4.rms_current(previous_current_arr[3]);
#endif
#endif
#ifdef GANTRY_CALIBRATION_COMMANDS_POST
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Running Post Commands");
gcode.process_subcommands_now_P(PSTR(GANTRY_CALIBRATION_COMMANDS_POST));
#endif
SET_SOFT_ENDSTOP_LOOSE(false);
}
#endif // MECHANICAL_GANTRY_CALIBRATION

772
Marlin/src/gcode/calibrate/G34_M422.cpp Executable file → Normal file
View File

@@ -16,389 +16,447 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#include "../../inc/MarlinConfigPre.h"
#if ENABLED(Z_STEPPER_AUTO_ALIGN)
#if EITHER(Z_MULTI_ENDSTOPS, Z_STEPPER_AUTO_ALIGN)
#include "../../feature/z_stepper_align.h"
#include "../gcode.h"
#include "../../module/planner.h"
#include "../../module/stepper.h"
#include "../../module/motion.h"
#include "../../module/stepper.h"
#include "../../module/planner.h"
#include "../../module/probe.h"
#if HOTENDS > 1
#include "../../module/tool_change.h"
#endif
#include "../../lcd/marlinui.h" // for LCD_MESSAGEPGM
#if HAS_LEVELING
#include "../../feature/bedlevel/bedlevel.h"
#endif
#if HAS_MULTI_HOTEND
#include "../../module/tool_change.h"
#endif
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
#include "../../libs/least_squares_fit.h"
#include "../../libs/least_squares_fit.h"
#endif
#define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
#include "../../core/debug_out.h"
inline void set_all_z_lock(const bool lock) {
stepper.set_z_lock(lock);
stepper.set_z2_lock(lock);
#if NUM_Z_STEPPER_DRIVERS >= 3
stepper.set_z3_lock(lock);
#if NUM_Z_STEPPER_DRIVERS >= 4
stepper.set_z4_lock(lock);
#endif
#if NUM_Z_STEPPER_DRIVERS >= 3
#define TRIPLE_Z 1
#if NUM_Z_STEPPER_DRIVERS >= 4
#define QUAD_Z 1
#endif
}
#endif
/**
* G34: Z-Stepper automatic alignment
*
* I<iterations>
* T<accuracy>
* A<amplification>
* R<recalculate> points based on current probe offsets
* Manual stepper lock controls (reset by G28):
* L Unlock all steppers
* Z<1-4> Z stepper to lock / unlock
* S<state> 0=UNLOCKED 1=LOCKED. If omitted, assume LOCKED.
*
* Examples:
* G34 Z1 ; Lock Z1
* G34 L Z2 ; Unlock all, then lock Z2
* G34 Z2 S0 ; Unlock Z2
*
* With Z_STEPPER_AUTO_ALIGN:
* I<iterations> Number of tests. If omitted, Z_STEPPER_ALIGN_ITERATIONS.
* T<accuracy> Target Accuracy factor. If omitted, Z_STEPPER_ALIGN_ACC.
* A<amplification> Provide an Amplification value. If omitted, Z_STEPPER_ALIGN_AMP.
* R Flag to recalculate points based on current probe offsets
*/
void GcodeSuite::G34() {
if (DEBUGGING(LEVELING)) {
DEBUG_ECHOLNPGM(">>> G34");
log_machine_info();
DEBUG_SECTION(log_G34, "G34", DEBUGGING(LEVELING));
if (DEBUGGING(LEVELING)) log_machine_info();
planner.synchronize(); // Prevent damage
const bool seenL = parser.seen('L');
if (seenL) stepper.set_all_z_lock(false);
const bool seenZ = parser.seenval('Z');
if (seenZ) {
const bool state = parser.boolval('S', true);
switch (parser.intval('Z')) {
case 1: stepper.set_z1_lock(state); break;
case 2: stepper.set_z2_lock(state); break;
#if TRIPLE_Z
case 3: stepper.set_z3_lock(state); break;
#if QUAD_Z
case 4: stepper.set_z4_lock(state); break;
#endif
#endif
}
}
do { // break out on error
if (seenL || seenZ) {
stepper.set_separate_multi_axis(seenZ);
return;
}
#if NUM_Z_STEPPER_DRIVERS == 4
SERIAL_ECHOLNPGM("Alignment for 4 steppers is Experimental!");
#elif NUM_Z_STEPPER_DRIVERS > 4
SERIAL_ECHOLNPGM("Alignment not supported for over 4 steppers");
break;
#endif
#if ENABLED(Z_STEPPER_AUTO_ALIGN)
do { // break out on error
const int8_t z_auto_align_iterations = parser.intval('I', Z_STEPPER_ALIGN_ITERATIONS);
if (!WITHIN(z_auto_align_iterations, 1, 30)) {
SERIAL_ECHOLNPGM("?(I)teration out of bounds (1-30).");
break;
}
const int8_t z_auto_align_iterations = parser.intval('I', Z_STEPPER_ALIGN_ITERATIONS);
if (!WITHIN(z_auto_align_iterations, 1, 30)) {
SERIAL_ECHOLNPGM("?(I)teration out of bounds (1-30).");
break;
}
const float z_auto_align_accuracy = parser.floatval('T', Z_STEPPER_ALIGN_ACC);
if (!WITHIN(z_auto_align_accuracy, 0.01f, 1.0f)) {
SERIAL_ECHOLNPGM("?(T)arget accuracy out of bounds (0.01-1.0).");
break;
}
const float z_auto_align_accuracy = parser.floatval('T', Z_STEPPER_ALIGN_ACC);
if (!WITHIN(z_auto_align_accuracy, 0.01f, 1.0f)) {
SERIAL_ECHOLNPGM("?(T)arget accuracy out of bounds (0.01-1.0).");
break;
}
const float z_auto_align_amplification =
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
Z_STEPPER_ALIGN_AMP;
#else
parser.floatval('A', Z_STEPPER_ALIGN_AMP);
if (!WITHIN(ABS(z_auto_align_amplification), 0.5f, 2.0f)) {
SERIAL_ECHOLNPGM("?(A)mplification out of bounds (0.5-2.0).");
break;
}
#endif
const float z_auto_align_amplification = TERN(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS, Z_STEPPER_ALIGN_AMP, parser.floatval('A', Z_STEPPER_ALIGN_AMP));
if (!WITHIN(ABS(z_auto_align_amplification), 0.5f, 2.0f)) {
SERIAL_ECHOLNPGM("?(A)mplification out of bounds (0.5-2.0).");
break;
}
if (parser.seen('R')) z_stepper_align.reset_to_default();
if (parser.seen('R')) z_stepper_align.reset_to_default();
const ProbePtRaise raise_after = parser.boolval('E') ? PROBE_PT_STOW : PROBE_PT_RAISE;
const ProbePtRaise raise_after = parser.boolval('E') ? PROBE_PT_STOW : PROBE_PT_RAISE;
// Wait for planner moves to finish!
planner.synchronize();
// Disable the leveling matrix before auto-aligning
#if HAS_LEVELING
#if ENABLED(RESTORE_LEVELING_AFTER_G34)
const bool leveling_was_active = planner.leveling_active;
#endif
set_bed_leveling_enabled(false);
#endif
#if ENABLED(CNC_WORKSPACE_PLANES)
workspace_plane = PLANE_XY;
#endif
// Always home with tool 0 active
#if HOTENDS > 1
const uint8_t old_tool_index = active_extruder;
tool_change(0, true);
#endif
#if HAS_DUPLICATION_MODE
extruder_duplication_enabled = false;
#endif
#if BOTH(BLTOUCH, BLTOUCH_HS_MODE)
// In BLTOUCH HS mode, the probe travels in a deployed state.
// Users of G34 might have a badly misaligned bed, so raise Z by the
// length of the deployed pin (BLTOUCH stroke < 7mm)
#define Z_BASIC_CLEARANCE Z_CLEARANCE_BETWEEN_PROBES + 7.0f
#else
#define Z_BASIC_CLEARANCE Z_CLEARANCE_BETWEEN_PROBES
#endif
// Compute a worst-case clearance height to probe from. After the first
// iteration this will be re-calculated based on the actual bed position
auto magnitude2 = [&](const uint8_t i, const uint8_t j) {
const xy_pos_t diff = z_stepper_align.xy[i] - z_stepper_align.xy[j];
return HYPOT2(diff.x, diff.y);
};
float z_probe = Z_BASIC_CLEARANCE + (G34_MAX_GRADE) * 0.01f * SQRT(
#if NUM_Z_STEPPER_DRIVERS == 3
_MAX(magnitude2(0, 1), magnitude2(1, 2), magnitude2(2, 0))
#elif NUM_Z_STEPPER_DRIVERS == 4
_MAX(magnitude2(0, 1), magnitude2(1, 2), magnitude2(2, 3),
magnitude2(3, 0), magnitude2(0, 2), magnitude2(1, 3))
#else
magnitude2(0, 1)
#endif
);
// Home before the alignment procedure
if (!all_axes_known()) home_all_axes();
// Move the Z coordinate realm towards the positive - dirty trick
current_position.z += z_probe * 0.5f;
sync_plan_position();
// Now, the Z origin lies below the build plate. That allows to probe deeper, before run_z_probe throws an error.
// This hack is un-done at the end of G34 - either by re-homing, or by using the probed heights of the last iteration.
#if DISABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
float last_z_align_move[NUM_Z_STEPPER_DRIVERS] = ARRAY_N(NUM_Z_STEPPER_DRIVERS, 10000.0f, 10000.0f, 10000.0f, 10000.0f);
#else
float last_z_align_level_indicator = 10000.0f;
#endif
float z_measured[NUM_Z_STEPPER_DRIVERS] = { 0 },
z_maxdiff = 0.0f,
amplification = z_auto_align_amplification;
// These are needed after the for-loop
uint8_t iteration;
bool err_break = false;
float z_measured_min;
#if DISABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
bool adjustment_reverse = false;
#endif
// 'iteration' is declared above and is also used after the for-loop.
// *not* the same as LOOP_L_N(iteration, z_auto_align_iterations)
for (iteration = 0; iteration < z_auto_align_iterations; ++iteration) {
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> probing all positions.");
SERIAL_ECHOLNPAIR("\nITERATION: ", int(iteration + 1));
// Initialize minimum value
z_measured_min = 100000.0f;
float z_measured_max = -100000.0f;
// Probe all positions (one per Z-Stepper)
LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS) {
// iteration odd/even --> downward / upward stepper sequence
const uint8_t iprobe = (iteration & 1) ? NUM_Z_STEPPER_DRIVERS - 1 - i : i;
// Safe clearance even on an incline
if ((iteration == 0 || i > 0) && z_probe > current_position.z) do_blocking_move_to_z(z_probe);
if (DEBUGGING(LEVELING))
DEBUG_ECHOLNPAIR_P(PSTR("Probing X"), z_stepper_align.xy[iprobe].x, SP_Y_STR, z_stepper_align.xy[iprobe].y);
// Probe a Z height for each stepper.
// Probing sanity check is disabled, as it would trigger even in normal cases because
// current_position.z has been manually altered in the "dirty trick" above.
const float z_probed_height = probe.probe_at_point(z_stepper_align.xy[iprobe], raise_after, 0, true, false);
if (isnan(z_probed_height)) {
SERIAL_ECHOLNPGM("Probing failed.");
err_break = true;
break;
}
// Add height to each value, to provide a more useful target height for
// the next iteration of probing. This allows adjustments to be made away from the bed.
z_measured[iprobe] = z_probed_height + Z_CLEARANCE_BETWEEN_PROBES;
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("> Z", int(iprobe + 1), " measured position is ", z_measured[iprobe]);
// Remember the minimum measurement to calculate the correction later on
z_measured_min = _MIN(z_measured_min, z_measured[iprobe]);
z_measured_max = _MAX(z_measured_max, z_measured[iprobe]);
} // for (i)
if (err_break) break;
// Adapt the next probe clearance height based on the new measurements.
// Safe_height = lowest distance to bed (= highest measurement) plus highest measured misalignment.
z_maxdiff = z_measured_max - z_measured_min;
z_probe = Z_BASIC_CLEARANCE + z_measured_max + z_maxdiff;
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
// Replace the initial values in z_measured with calculated heights at
// each stepper position. This allows the adjustment algorithm to be
// shared between both possible probing mechanisms.
// This must be done after the next z_probe height is calculated, so that
// the height is calculated from actual print area positions, and not
// extrapolated motor movements.
// Compute the least-squares fit for all probed points.
// Calculate the Z position of each stepper and store it in z_measured.
// This allows the actual adjustment logic to be shared by both algorithms.
linear_fit_data lfd;
incremental_LSF_reset(&lfd);
LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS) {
SERIAL_ECHOLNPAIR("PROBEPT_", int(i), ": ", z_measured[i]);
incremental_LSF(&lfd, z_stepper_align.xy[i], z_measured[i]);
}
finish_incremental_LSF(&lfd);
z_measured_min = 100000.0f;
LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS) {
z_measured[i] = -(lfd.A * z_stepper_align.stepper_xy[i].x + lfd.B * z_stepper_align.stepper_xy[i].y + lfd.D);
z_measured_min = _MIN(z_measured_min, z_measured[i]);
}
SERIAL_ECHOLNPAIR("CALCULATED STEPPER POSITIONS: Z1=", z_measured[0], " Z2=", z_measured[1], " Z3=", z_measured[2]);
#endif
SERIAL_ECHOLNPAIR("\n"
"DIFFERENCE Z1-Z2=", ABS(z_measured[0] - z_measured[1])
#if NUM_Z_STEPPER_DRIVERS == 3
, " Z2-Z3=", ABS(z_measured[1] - z_measured[2])
, " Z3-Z1=", ABS(z_measured[2] - z_measured[0])
// Disable the leveling matrix before auto-aligning
#if HAS_LEVELING
#if ENABLED(RESTORE_LEVELING_AFTER_G34)
const bool leveling_was_active = planner.leveling_active;
#endif
);
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
// Check if the applied corrections go in the correct direction.
// Calculate the sum of the absolute deviations from the mean of the probe measurements.
// Compare to the last iteration to ensure it's getting better.
// Calculate mean value as a reference
float z_measured_mean = 0.0f;
LOOP_L_N(zstepper, NUM_Z_STEPPER_DRIVERS) z_measured_mean += z_measured[zstepper];
z_measured_mean /= NUM_Z_STEPPER_DRIVERS;
// Calculate the sum of the absolute deviations from the mean value
float z_align_level_indicator = 0.0f;
LOOP_L_N(zstepper, NUM_Z_STEPPER_DRIVERS)
z_align_level_indicator += ABS(z_measured[zstepper] - z_measured_mean);
// If it's getting worse, stop and throw an error
if (last_z_align_level_indicator < z_align_level_indicator * 0.7f) {
SERIAL_ECHOLNPGM("Decreasing accuracy detected.");
err_break = true;
break;
}
last_z_align_level_indicator = z_align_level_indicator;
set_bed_leveling_enabled(false);
#endif
// The following correction actions are to be enabled for select Z-steppers only
stepper.set_separate_multi_axis(true);
TERN_(CNC_WORKSPACE_PLANES, workspace_plane = PLANE_XY);
bool success_break = true;
// Correct the individual stepper offsets
LOOP_L_N(zstepper, NUM_Z_STEPPER_DRIVERS) {
// Calculate current stepper move
float z_align_move = z_measured[zstepper] - z_measured_min;
const float z_align_abs = ABS(z_align_move);
// Always home with tool 0 active
#if HAS_MULTI_HOTEND
const uint8_t old_tool_index = active_extruder;
tool_change(0, true);
#endif
#if DISABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
// Optimize one iteration's correction based on the first measurements
if (z_align_abs) amplification = (iteration == 1) ? _MIN(last_z_align_move[zstepper] / z_align_abs, 2.0f) : z_auto_align_amplification;
TERN_(HAS_DUPLICATION_MODE, set_duplication_enabled(false));
// Check for less accuracy compared to last move
if (last_z_align_move[zstepper] < z_align_abs * 0.7f) {
SERIAL_ECHOLNPGM("Decreasing accuracy detected.");
adjustment_reverse = !adjustment_reverse;
// In BLTOUCH HS mode, the probe travels in a deployed state.
// Users of G34 might have a badly misaligned bed, so raise Z by the
// length of the deployed pin (BLTOUCH stroke < 7mm)
#define Z_BASIC_CLEARANCE (Z_CLEARANCE_BETWEEN_PROBES + 7.0f * BOTH(BLTOUCH, BLTOUCH_HS_MODE))
// Compute a worst-case clearance height to probe from. After the first
// iteration this will be re-calculated based on the actual bed position
auto magnitude2 = [&](const uint8_t i, const uint8_t j) {
const xy_pos_t diff = z_stepper_align.xy[i] - z_stepper_align.xy[j];
return HYPOT2(diff.x, diff.y);
};
float z_probe = Z_BASIC_CLEARANCE + (G34_MAX_GRADE) * 0.01f * SQRT(_MAX(0, magnitude2(0, 1)
#if TRIPLE_Z
, magnitude2(2, 1), magnitude2(2, 0)
#if QUAD_Z
, magnitude2(3, 2), magnitude2(3, 1), magnitude2(3, 0)
#endif
#endif
));
// Home before the alignment procedure
home_if_needed();
// Move the Z coordinate realm towards the positive - dirty trick
current_position.z += z_probe * 0.5f;
sync_plan_position();
// Now, the Z origin lies below the build plate. That allows to probe deeper, before run_z_probe throws an error.
// This hack is un-done at the end of G34 - either by re-homing, or by using the probed heights of the last iteration.
#if DISABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
float last_z_align_move[NUM_Z_STEPPER_DRIVERS] = ARRAY_N_1(NUM_Z_STEPPER_DRIVERS, 10000.0f);
#else
float last_z_align_level_indicator = 10000.0f;
#endif
float z_measured[NUM_Z_STEPPER_DRIVERS] = { 0 },
z_maxdiff = 0.0f,
amplification = z_auto_align_amplification;
#if DISABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
bool adjustment_reverse = false;
#endif
#if HAS_STATUS_MESSAGE
PGM_P const msg_iteration = GET_TEXT(MSG_ITERATION);
const uint8_t iter_str_len = strlen_P(msg_iteration);
#endif
// Final z and iteration values will be used after breaking the loop
float z_measured_min;
uint8_t iteration = 0;
bool err_break = false; // To break out of nested loops
while (iteration < z_auto_align_iterations) {
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> probing all positions.");
const int iter = iteration + 1;
SERIAL_ECHOLNPGM("\nG34 Iteration: ", iter);
#if HAS_STATUS_MESSAGE
char str[iter_str_len + 2 + 1];
sprintf_P(str, msg_iteration, iter);
ui.set_status(str);
#endif
// Initialize minimum value
z_measured_min = 100000.0f;
float z_measured_max = -100000.0f;
// Probe all positions (one per Z-Stepper)
LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS) {
// iteration odd/even --> downward / upward stepper sequence
const uint8_t iprobe = (iteration & 1) ? NUM_Z_STEPPER_DRIVERS - 1 - i : i;
// Safe clearance even on an incline
if ((iteration == 0 || i > 0) && z_probe > current_position.z) do_blocking_move_to_z(z_probe);
if (DEBUGGING(LEVELING))
DEBUG_ECHOLNPGM_P(PSTR("Probing X"), z_stepper_align.xy[iprobe].x, SP_Y_STR, z_stepper_align.xy[iprobe].y);
// Probe a Z height for each stepper.
// Probing sanity check is disabled, as it would trigger even in normal cases because
// current_position.z has been manually altered in the "dirty trick" above.
const float z_probed_height = probe.probe_at_point(z_stepper_align.xy[iprobe], raise_after, 0, true, false);
if (isnan(z_probed_height)) {
SERIAL_ECHOLNPGM("Probing failed");
LCD_MESSAGEPGM(MSG_LCD_PROBING_FAILED);
err_break = true;
break;
}
// Remember the alignment for the next iteration
last_z_align_move[zstepper] = z_align_abs;
// Add height to each value, to provide a more useful target height for
// the next iteration of probing. This allows adjustments to be made away from the bed.
z_measured[iprobe] = z_probed_height + Z_CLEARANCE_BETWEEN_PROBES;
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> Z", iprobe + 1, " measured position is ", z_measured[iprobe]);
// Remember the minimum measurement to calculate the correction later on
z_measured_min = _MIN(z_measured_min, z_measured[iprobe]);
z_measured_max = _MAX(z_measured_max, z_measured[iprobe]);
} // for (i)
if (err_break) break;
// Adapt the next probe clearance height based on the new measurements.
// Safe_height = lowest distance to bed (= highest measurement) plus highest measured misalignment.
z_maxdiff = z_measured_max - z_measured_min;
z_probe = Z_BASIC_CLEARANCE + z_measured_max + z_maxdiff;
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
// Replace the initial values in z_measured with calculated heights at
// each stepper position. This allows the adjustment algorithm to be
// shared between both possible probing mechanisms.
// This must be done after the next z_probe height is calculated, so that
// the height is calculated from actual print area positions, and not
// extrapolated motor movements.
// Compute the least-squares fit for all probed points.
// Calculate the Z position of each stepper and store it in z_measured.
// This allows the actual adjustment logic to be shared by both algorithms.
linear_fit_data lfd;
incremental_LSF_reset(&lfd);
LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS) {
SERIAL_ECHOLNPGM("PROBEPT_", i, ": ", z_measured[i]);
incremental_LSF(&lfd, z_stepper_align.xy[i], z_measured[i]);
}
finish_incremental_LSF(&lfd);
z_measured_min = 100000.0f;
LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS) {
z_measured[i] = -(lfd.A * z_stepper_align.stepper_xy[i].x + lfd.B * z_stepper_align.stepper_xy[i].y + lfd.D);
z_measured_min = _MIN(z_measured_min, z_measured[i]);
}
SERIAL_ECHOLNPGM(
LIST_N(DOUBLE(NUM_Z_STEPPER_DRIVERS),
"Calculated Z1=", z_measured[0],
" Z2=", z_measured[1],
" Z3=", z_measured[2],
" Z4=", z_measured[3]
)
);
#endif
// Stop early if all measured points achieve accuracy target
if (z_align_abs > z_auto_align_accuracy) success_break = false;
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("> Z", int(zstepper + 1), " corrected by ", z_align_move);
// Lock all steppers except one
set_all_z_lock(true);
switch (zstepper) {
case 0: stepper.set_z_lock(false); break;
case 1: stepper.set_z2_lock(false); break;
#if NUM_Z_STEPPER_DRIVERS >= 3
case 2: stepper.set_z3_lock(false); break;
SERIAL_ECHOLNPGM("\n"
"Z2-Z1=", ABS(z_measured[1] - z_measured[0])
#if TRIPLE_Z
, " Z3-Z2=", ABS(z_measured[2] - z_measured[1])
, " Z3-Z1=", ABS(z_measured[2] - z_measured[0])
#if QUAD_Z
, " Z4-Z3=", ABS(z_measured[3] - z_measured[2])
, " Z4-Z2=", ABS(z_measured[3] - z_measured[1])
, " Z4-Z1=", ABS(z_measured[3] - z_measured[0])
#endif
#endif
#if NUM_Z_STEPPER_DRIVERS == 4
case 3: stepper.set_z4_lock(false); break;
);
#if HAS_STATUS_MESSAGE
char fstr1[10];
char msg[6 + (6 + 5) * NUM_Z_STEPPER_DRIVERS + 1]
#if TRIPLE_Z
, fstr2[10], fstr3[10]
#if QUAD_Z
, fstr4[10], fstr5[10], fstr6[10]
#endif
#endif
;
sprintf_P(msg,
PSTR("1:2=%s" TERN_(TRIPLE_Z, " 3-2=%s 3-1=%s") TERN_(QUAD_Z, " 4-3=%s 4-2=%s 4-1=%s")),
dtostrf(ABS(z_measured[1] - z_measured[0]), 1, 3, fstr1)
OPTARG(TRIPLE_Z,
dtostrf(ABS(z_measured[2] - z_measured[1]), 1, 3, fstr2),
dtostrf(ABS(z_measured[2] - z_measured[0]), 1, 3, fstr3))
OPTARG(QUAD_Z,
dtostrf(ABS(z_measured[3] - z_measured[2]), 1, 3, fstr4),
dtostrf(ABS(z_measured[3] - z_measured[1]), 1, 3, fstr5),
dtostrf(ABS(z_measured[3] - z_measured[0]), 1, 3, fstr6))
);
ui.set_status(msg);
#endif
auto decreasing_accuracy = [](const_float_t v1, const_float_t v2) {
if (v1 < v2 * 0.7f) {
SERIAL_ECHOLNPGM("Decreasing Accuracy Detected.");
LCD_MESSAGEPGM(MSG_DECREASING_ACCURACY);
return true;
}
return false;
};
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
// Check if the applied corrections go in the correct direction.
// Calculate the sum of the absolute deviations from the mean of the probe measurements.
// Compare to the last iteration to ensure it's getting better.
// Calculate mean value as a reference
float z_measured_mean = 0.0f;
LOOP_L_N(zstepper, NUM_Z_STEPPER_DRIVERS) z_measured_mean += z_measured[zstepper];
z_measured_mean /= NUM_Z_STEPPER_DRIVERS;
// Calculate the sum of the absolute deviations from the mean value
float z_align_level_indicator = 0.0f;
LOOP_L_N(zstepper, NUM_Z_STEPPER_DRIVERS)
z_align_level_indicator += ABS(z_measured[zstepper] - z_measured_mean);
// If it's getting worse, stop and throw an error
err_break = decreasing_accuracy(last_z_align_level_indicator, z_align_level_indicator);
if (err_break) break;
last_z_align_level_indicator = z_align_level_indicator;
#endif
// The following correction actions are to be enabled for select Z-steppers only
stepper.set_separate_multi_axis(true);
bool success_break = true;
// Correct the individual stepper offsets
LOOP_L_N(zstepper, NUM_Z_STEPPER_DRIVERS) {
// Calculate current stepper move
float z_align_move = z_measured[zstepper] - z_measured_min;
const float z_align_abs = ABS(z_align_move);
#if DISABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
// Optimize one iteration's correction based on the first measurements
if (z_align_abs) amplification = (iteration == 1) ? _MIN(last_z_align_move[zstepper] / z_align_abs, 2.0f) : z_auto_align_amplification;
// Check for less accuracy compared to last move
if (decreasing_accuracy(last_z_align_move[zstepper], z_align_abs)) {
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> Z", zstepper + 1, " last_z_align_move = ", last_z_align_move[zstepper]);
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> Z", zstepper + 1, " z_align_abs = ", z_align_abs);
adjustment_reverse = !adjustment_reverse;
}
// Remember the alignment for the next iteration, but only if steppers move,
// otherwise it would be just zero (in case this stepper was at z_measured_min already)
if (z_align_abs > 0) last_z_align_move[zstepper] = z_align_abs;
#endif
// Stop early if all measured points achieve accuracy target
if (z_align_abs > z_auto_align_accuracy) success_break = false;
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> Z", zstepper + 1, " corrected by ", z_align_move);
// Lock all steppers except one
stepper.set_all_z_lock(true, zstepper);
#if DISABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
// Decreasing accuracy was detected so move was inverted.
// Will match reversed Z steppers on dual steppers. Triple will need more work to map.
if (adjustment_reverse) {
z_align_move = -z_align_move;
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> Z", zstepper + 1, " correction reversed to ", z_align_move);
}
#endif
// Do a move to correct part of the misalignment for the current stepper
do_blocking_move_to_z(amplification * z_align_move + current_position.z);
} // for (zstepper)
// Back to normal stepper operations
stepper.set_all_z_lock(false);
stepper.set_separate_multi_axis(false);
if (err_break) break;
if (success_break) {
SERIAL_ECHOLNPGM("Target accuracy achieved.");
LCD_MESSAGEPGM(MSG_ACCURACY_ACHIEVED);
break;
}
#if DISABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
// Decreasing accuracy was detected so move was inverted.
// Will match reversed Z steppers on dual steppers. Triple will need more work to map.
if (adjustment_reverse)
z_align_move = -z_align_move;
#endif
iteration++;
} // while (iteration < z_auto_align_iterations)
// Do a move to correct part of the misalignment for the current stepper
do_blocking_move_to_z(amplification * z_align_move + current_position.z);
} // for (zstepper)
if (err_break)
SERIAL_ECHOLNPGM("G34 aborted.");
else {
SERIAL_ECHOLNPGM("Did ", iteration + (iteration != z_auto_align_iterations), " of ", z_auto_align_iterations);
SERIAL_ECHOLNPAIR_F("Accuracy: ", z_maxdiff);
}
// Back to normal stepper operations
set_all_z_lock(false);
stepper.set_separate_multi_axis(false);
// Stow the probe because the last call to probe.probe_at_point(...)
// leaves the probe deployed when it's successful.
IF_DISABLED(TOUCH_MI_PROBE, probe.stow());
if (err_break) break;
#if ENABLED(HOME_AFTER_G34)
// After this operation the z position needs correction
set_axis_never_homed(Z_AXIS);
// Home Z after the alignment procedure
process_subcommands_now_P(PSTR("G28Z"));
#else
// Use the probed height from the last iteration to determine the Z height.
// z_measured_min is used, because all steppers are aligned to z_measured_min.
// Ideally, this would be equal to the 'z_probe * 0.5f' which was added earlier.
current_position.z -= z_measured_min - (float)Z_CLEARANCE_BETWEEN_PROBES;
sync_plan_position();
#endif
if (success_break) { SERIAL_ECHOLNPGM("Target accuracy achieved."); break; }
// Restore the active tool after homing
TERN_(HAS_MULTI_HOTEND, tool_change(old_tool_index, DISABLED(PARKING_EXTRUDER))); // Fetch previous tool for parking extruder
} // for (iteration)
#if BOTH(HAS_LEVELING, RESTORE_LEVELING_AFTER_G34)
set_bed_leveling_enabled(leveling_was_active);
#endif
if (err_break)
SERIAL_ECHOLNPGM("G34 aborted.");
else {
SERIAL_ECHOLNPAIR("Did ", int(iteration + (iteration != z_auto_align_iterations)), " of ", int(z_auto_align_iterations));
SERIAL_ECHOLNPAIR_F("Accuracy: ", z_maxdiff);
}
// Stow the probe, as the last call to probe.probe_at_point(...) left
// the probe deployed if it was successful.
probe.stow();
#if ENABLED(HOME_AFTER_G34)
// After this operation the z position needs correction
set_axis_not_trusted(Z_AXIS);
// Home Z after the alignment procedure
process_subcommands_now_P(PSTR("G28Z"));
#else
// Use the probed height from the last iteration to determine the Z height.
// z_measured_min is used, because all steppers are aligned to z_measured_min.
// Ideally, this would be equal to the 'z_probe * 0.5f' which was added earlier.
current_position.z -= z_measured_min - (float)Z_CLEARANCE_BETWEEN_PROBES;
sync_plan_position();
#endif
// Restore the active tool after homing
#if HOTENDS > 1
tool_change(old_tool_index, DISABLED(PARKING_EXTRUDER)); // Fetch previous tool for parking extruder
#endif
#if HAS_LEVELING && ENABLED(RESTORE_LEVELING_AFTER_G34)
set_bed_leveling_enabled(leveling_was_active);
#endif
}while(0);
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("<<< G34");
}while(0);
#endif // Z_STEPPER_AUTO_ALIGN
}
#endif // Z_MULTI_ENDSTOPS || Z_STEPPER_AUTO_ALIGN
#if ENABLED(Z_STEPPER_AUTO_ALIGN)
/**
* M422: Set a Z-Stepper automatic alignment XY point.
* Use repeatedly to set multiple points.
@@ -417,49 +475,27 @@ void GcodeSuite::G34() {
*/
void GcodeSuite::M422() {
if (!parser.seen_any()) return M422_report();
if (parser.seen('R')) {
z_stepper_align.reset_to_default();
return;
}
if (!parser.seen_any()) {
LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS)
SERIAL_ECHOLNPAIR_P(PSTR("M422 S"), int(i + 1), SP_X_STR, z_stepper_align.xy[i].x, SP_Y_STR, z_stepper_align.xy[i].y);
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS)
SERIAL_ECHOLNPAIR_P(PSTR("M422 W"), int(i + 1), SP_X_STR, z_stepper_align.stepper_xy[i].x, SP_Y_STR, z_stepper_align.stepper_xy[i].y);
#endif
const bool is_probe_point = parser.seen('S');
if (TERN0(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS, is_probe_point && parser.seen('W'))) {
SERIAL_ECHOLNPGM("?(S) and (W) may not be combined.");
return;
}
const bool is_probe_point = parser.seen('S');
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
if (is_probe_point && parser.seen('W')) {
SERIAL_ECHOLNPGM("?(S) and (W) may not be combined.");
return;
}
#endif
xy_pos_t *pos_dest = (
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
!is_probe_point ? z_stepper_align.stepper_xy :
#endif
TERN_(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS, !is_probe_point ? z_stepper_align.stepper_xy :)
z_stepper_align.xy
);
if (!is_probe_point
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
&& !parser.seen('W')
#endif
) {
SERIAL_ECHOLNPGM(
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
"?(S) or (W) is required."
#else
"?(S) is required."
#endif
);
if (!is_probe_point && TERN1(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS, !parser.seen('W'))) {
SERIAL_ECHOLNPGM("?(S)" TERN_(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS, " or (W)") " is required.");
return;
}
@@ -468,7 +504,7 @@ void GcodeSuite::M422() {
if (is_probe_point) {
position_index = parser.intval('S') - 1;
if (!WITHIN(position_index, 0, int8_t(NUM_Z_STEPPER_DRIVERS) - 1)) {
SERIAL_ECHOLNPGM("?(S) Z-ProbePosition index invalid.");
SERIAL_ECHOLNPGM("?(S) Probe-position index invalid.");
return;
}
}
@@ -476,7 +512,7 @@ void GcodeSuite::M422() {
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
position_index = parser.intval('W') - 1;
if (!WITHIN(position_index, 0, NUM_Z_STEPPER_DRIVERS - 1)) {
SERIAL_ECHOLNPGM("?(W) Z-Stepper index invalid.");
SERIAL_ECHOLNPGM("?(W) Z-stepper index invalid.");
return;
}
#endif
@@ -501,4 +537,26 @@ void GcodeSuite::M422() {
pos_dest[position_index] = pos;
}
void GcodeSuite::M422_report(const bool forReplay/*=true*/) {
report_heading(forReplay, PSTR(STR_Z_AUTO_ALIGN));
LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS) {
report_echo_start(forReplay);
SERIAL_ECHOLNPGM_P(
PSTR(" M422 S"), i + 1,
SP_X_STR, z_stepper_align.xy[i].x,
SP_Y_STR, z_stepper_align.xy[i].y
);
}
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS) {
report_echo_start(forReplay);
SERIAL_ECHOLNPGM_P(
PSTR(" M422 W"), i + 1,
SP_X_STR, z_stepper_align.stepper_xy[i].x,
SP_Y_STR, z_stepper_align.stepper_xy[i].y
);
}
#endif
}
#endif // Z_STEPPER_AUTO_ALIGN

362
Marlin/src/gcode/calibrate/G425.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -30,7 +30,7 @@
#include "../../feature/backlash.h"
#endif
#include "../../lcd/ultralcd.h"
#include "../../lcd/marlinui.h"
#include "../../module/motion.h"
#include "../../module/planner.h"
#include "../../module/tool_change.h"
@@ -51,7 +51,6 @@
#undef CALIBRATION_MEASURE_AT_TOP_EDGES
#endif
/**
* G425 backs away from the calibration object by various distances
* depending on the confidence level:
@@ -74,11 +73,23 @@
#if BOTH(CALIBRATION_MEASURE_LEFT, CALIBRATION_MEASURE_RIGHT)
#define HAS_X_CENTER 1
#endif
#if BOTH(CALIBRATION_MEASURE_FRONT, CALIBRATION_MEASURE_BACK)
#if HAS_Y_AXIS && BOTH(CALIBRATION_MEASURE_FRONT, CALIBRATION_MEASURE_BACK)
#define HAS_Y_CENTER 1
#endif
#if LINEAR_AXES >= 4 && BOTH(CALIBRATION_MEASURE_IMIN, CALIBRATION_MEASURE_IMAX)
#define HAS_I_CENTER 1
#endif
#if LINEAR_AXES >= 5 && BOTH(CALIBRATION_MEASURE_JMIN, CALIBRATION_MEASURE_JMAX)
#define HAS_J_CENTER 1
#endif
#if LINEAR_AXES >= 6 && BOTH(CALIBRATION_MEASURE_KMIN, CALIBRATION_MEASURE_KMAX)
#define HAS_K_CENTER 1
#endif
enum side_t : uint8_t { TOP, RIGHT, FRONT, LEFT, BACK, NUM_SIDES };
enum side_t : uint8_t {
TOP, RIGHT, FRONT, LEFT, BACK, NUM_SIDES,
LIST_N(DOUBLE(SUB3(LINEAR_AXES)), IMINIMUM, IMAXIMUM, JMINIMUM, JMAXIMUM, KMINIMUM, KMAXIMUM)
};
static constexpr xyz_pos_t true_center CALIBRATION_OBJECT_CENTER;
static constexpr xyz_float_t dimensions CALIBRATION_OBJECT_DIMENSIONS;
@@ -93,8 +104,6 @@ struct measurements_t {
xy_float_t nozzle_outer_dimension = nod;
};
#define TEMPORARY_SOFT_ENDSTOP_STATE(enable) REMEMBER(tes, soft_endstops_enabled, enable);
#if ENABLED(BACKLASH_GCODE)
#define TEMPORARY_BACKLASH_CORRECTION(value) REMEMBER(tbst, backlash.correction, value)
#else
@@ -108,7 +117,7 @@ struct measurements_t {
#endif
inline void calibration_move() {
do_blocking_move_to(current_position, MMM_TO_MMS(CALIBRATION_FEEDRATE_TRAVEL));
do_blocking_move_to((xyz_pos_t)current_position, MMM_TO_MMS(CALIBRATION_FEEDRATE_TRAVEL));
}
/**
@@ -127,7 +136,7 @@ inline void park_above_object(measurements_t &m, const float uncertainty) {
calibration_move();
}
#if HOTENDS > 1
#if HAS_MULTI_HOTEND
inline void set_nozzle(measurements_t &m, const uint8_t extruder) {
if (extruder != active_extruder) {
park_above_object(m, CALIBRATION_MEASUREMENT_UNKNOWN);
@@ -146,14 +155,16 @@ inline void park_above_object(measurements_t &m, const float uncertainty) {
#endif
#if !PIN_EXISTS(CALIBRATION)
#include "../../module/probe.h"
#endif
inline bool read_calibration_pin() {
return (
#if PIN_EXISTS(CALIBRATION)
READ(CALIBRATION_PIN) != CALIBRATION_PIN_INVERTING
#elif HAS_CUSTOM_PROBE_PIN
READ(Z_MIN_PROBE_PIN) != Z_MIN_PROBE_ENDSTOP_INVERTING
#else
READ(Z_MIN_PIN) != Z_MIN_ENDSTOP_INVERTING
PROBE_TRIGGERED()
#endif
);
}
@@ -175,7 +186,7 @@ float measuring_movement(const AxisEnum axis, const int dir, const bool stop_sta
destination = current_position;
for (float travel = 0; travel < limit; travel += step) {
destination[axis] += dir * step;
do_blocking_move_to(destination, mms);
do_blocking_move_to((xyz_pos_t)destination, mms);
planner.synchronize();
if (read_calibration_pin() == stop_state) break;
}
@@ -195,18 +206,22 @@ float measuring_movement(const AxisEnum axis, const int dir, const bool stop_sta
inline float measure(const AxisEnum axis, const int dir, const bool stop_state, float * const backlash_ptr, const float uncertainty) {
const bool fast = uncertainty == CALIBRATION_MEASUREMENT_UNKNOWN;
// Save position
destination = current_position;
const float start_pos = destination[axis];
// Save the current position of the specified axis
const float start_pos = current_position[axis];
// Take a measurement. Only the specified axis will be affected.
const float measured_pos = measuring_movement(axis, dir, stop_state, fast);
// Measure backlash
if (backlash_ptr && !fast) {
const float release_pos = measuring_movement(axis, -dir, !stop_state, fast);
*backlash_ptr = ABS(release_pos - measured_pos);
}
// Return to starting position
// Move back to the starting position
destination = current_position;
destination[axis] = start_pos;
do_blocking_move_to(destination, MMM_TO_MMS(CALIBRATION_FEEDRATE_TRAVEL));
do_blocking_move_to((xyz_pos_t)destination, MMM_TO_MMS(CALIBRATION_FEEDRATE_TRAVEL));
return measured_pos;
}
@@ -227,7 +242,15 @@ inline void probe_side(measurements_t &m, const float uncertainty, const side_t
park_above_object(m, uncertainty);
switch (side) {
#if AXIS_CAN_CALIBRATE(Z)
#if AXIS_CAN_CALIBRATE(X)
case RIGHT: dir = -1;
case LEFT: axis = X_AXIS; break;
#endif
#if LINEAR_AXES >= 2 && AXIS_CAN_CALIBRATE(Y)
case BACK: dir = -1;
case FRONT: axis = Y_AXIS; break;
#endif
#if HAS_Z_AXIS && AXIS_CAN_CALIBRATE(Z)
case TOP: {
const float measurement = measure(Z_AXIS, -1, true, &m.backlash[TOP], uncertainty);
m.obj_center.z = measurement - dimensions.z / 2;
@@ -235,13 +258,17 @@ inline void probe_side(measurements_t &m, const float uncertainty, const side_t
return;
}
#endif
#if AXIS_CAN_CALIBRATE(X)
case LEFT: axis = X_AXIS; break;
case RIGHT: axis = X_AXIS; dir = -1; break;
#if LINEAR_AXES >= 4 && AXIS_CAN_CALIBRATE(I)
case IMINIMUM: dir = -1;
case IMAXIMUM: axis = I_AXIS; break;
#endif
#if AXIS_CAN_CALIBRATE(Y)
case FRONT: axis = Y_AXIS; break;
case BACK: axis = Y_AXIS; dir = -1; break;
#if LINEAR_AXES >= 5 && AXIS_CAN_CALIBRATE(J)
case JMINIMUM: dir = -1;
case JMAXIMUM: axis = J_AXIS; break;
#endif
#if LINEAR_AXES >= 6 && AXIS_CAN_CALIBRATE(K)
case KMINIMUM: dir = -1;
case KMAXIMUM: axis = K_AXIS; break;
#endif
default: return;
}
@@ -256,7 +283,7 @@ inline void probe_side(measurements_t &m, const float uncertainty, const side_t
#endif
}
if (AXIS_CAN_CALIBRATE(X) && axis == X_AXIS || AXIS_CAN_CALIBRATE(Y) && axis == Y_AXIS) {
if ((AXIS_CAN_CALIBRATE(X) && axis == X_AXIS) || (AXIS_CAN_CALIBRATE(Y) && axis == Y_AXIS)) {
// Move to safe distance to the side of the calibration object
current_position[axis] = m.obj_center[axis] + (-dir) * (dimensions[axis] / 2 + m.nozzle_outer_dimension[axis] / 2 + uncertainty);
calibration_move();
@@ -286,72 +313,86 @@ inline void probe_sides(measurements_t &m, const float uncertainty) {
probe_side(m, uncertainty, TOP);
#endif
#if ENABLED(CALIBRATION_MEASURE_RIGHT)
probe_side(m, uncertainty, RIGHT, probe_top_at_edge);
#endif
#if ENABLED(CALIBRATION_MEASURE_FRONT)
probe_side(m, uncertainty, FRONT, probe_top_at_edge);
#endif
#if ENABLED(CALIBRATION_MEASURE_LEFT)
probe_side(m, uncertainty, LEFT, probe_top_at_edge);
#endif
#if ENABLED(CALIBRATION_MEASURE_BACK)
probe_side(m, uncertainty, BACK, probe_top_at_edge);
#endif
TERN_(CALIBRATION_MEASURE_RIGHT, probe_side(m, uncertainty, RIGHT, probe_top_at_edge));
TERN_(CALIBRATION_MEASURE_FRONT, probe_side(m, uncertainty, FRONT, probe_top_at_edge));
TERN_(CALIBRATION_MEASURE_LEFT, probe_side(m, uncertainty, LEFT, probe_top_at_edge));
TERN_(CALIBRATION_MEASURE_BACK, probe_side(m, uncertainty, BACK, probe_top_at_edge));
TERN_(CALIBRATION_MEASURE_IMIN, probe_side(m, uncertainty, IMINIMUM, probe_top_at_edge));
TERN_(CALIBRATION_MEASURE_IMAX, probe_side(m, uncertainty, IMAXIMUM, probe_top_at_edge));
TERN_(CALIBRATION_MEASURE_JMIN, probe_side(m, uncertainty, JMINIMUM, probe_top_at_edge));
TERN_(CALIBRATION_MEASURE_JMAX, probe_side(m, uncertainty, JMAXIMUM, probe_top_at_edge));
TERN_(CALIBRATION_MEASURE_KMIN, probe_side(m, uncertainty, KMINIMUM, probe_top_at_edge));
TERN_(CALIBRATION_MEASURE_KMAX, probe_side(m, uncertainty, KMAXIMUM, probe_top_at_edge));
// Compute the measured center of the calibration object.
#if HAS_X_CENTER
m.obj_center.x = (m.obj_side[LEFT] + m.obj_side[RIGHT]) / 2;
#endif
#if HAS_Y_CENTER
m.obj_center.y = (m.obj_side[FRONT] + m.obj_side[BACK]) / 2;
#endif
TERN_(HAS_X_CENTER, m.obj_center.x = (m.obj_side[LEFT] + m.obj_side[RIGHT]) / 2);
TERN_(HAS_Y_CENTER, m.obj_center.y = (m.obj_side[FRONT] + m.obj_side[BACK]) / 2);
TERN_(HAS_I_CENTER, m.obj_center.i = (m.obj_side[IMINIMUM] + m.obj_side[IMAXIMUM]) / 2);
TERN_(HAS_J_CENTER, m.obj_center.j = (m.obj_side[JMINIMUM] + m.obj_side[JMAXIMUM]) / 2);
TERN_(HAS_K_CENTER, m.obj_center.k = (m.obj_side[KMINIMUM] + m.obj_side[KMAXIMUM]) / 2);
// Compute the outside diameter of the nozzle at the height
// at which it makes contact with the calibration object
#if HAS_X_CENTER
m.nozzle_outer_dimension.x = m.obj_side[RIGHT] - m.obj_side[LEFT] - dimensions.x;
#endif
#if HAS_Y_CENTER
m.nozzle_outer_dimension.y = m.obj_side[BACK] - m.obj_side[FRONT] - dimensions.y;
#endif
TERN_(HAS_X_CENTER, m.nozzle_outer_dimension.x = m.obj_side[RIGHT] - m.obj_side[LEFT] - dimensions.x);
TERN_(HAS_Y_CENTER, m.nozzle_outer_dimension.y = m.obj_side[BACK] - m.obj_side[FRONT] - dimensions.y);
park_above_object(m, uncertainty);
// The difference between the known and the measured location
// of the calibration object is the positional error
m.pos_error.x = (0
#if HAS_X_CENTER
+ true_center.x - m.obj_center.x
#endif
LINEAR_AXIS_CODE(
m.pos_error.x = TERN0(HAS_X_CENTER, true_center.x - m.obj_center.x),
m.pos_error.y = TERN0(HAS_Y_CENTER, true_center.y - m.obj_center.y),
m.pos_error.z = true_center.z - m.obj_center.z,
m.pos_error.i = TERN0(HAS_I_CENTER, true_center.i - m.obj_center.i),
m.pos_error.j = TERN0(HAS_J_CENTER, true_center.j - m.obj_center.j),
m.pos_error.k = TERN0(HAS_K_CENTER, true_center.k - m.obj_center.k)
);
m.pos_error.y = (0
#if HAS_Y_CENTER
+ true_center.y - m.obj_center.y
#endif
);
m.pos_error.z = true_center.z - m.obj_center.z;
}
#if ENABLED(CALIBRATION_REPORTING)
inline void report_measured_faces(const measurements_t &m) {
SERIAL_ECHOLNPGM("Sides:");
#if AXIS_CAN_CALIBRATE(Z)
SERIAL_ECHOLNPAIR(" Top: ", m.obj_side[TOP]);
#if HAS_Z_AXIS && AXIS_CAN_CALIBRATE(Z)
SERIAL_ECHOLNPGM(" Top: ", m.obj_side[TOP]);
#endif
#if ENABLED(CALIBRATION_MEASURE_LEFT)
SERIAL_ECHOLNPAIR(" Left: ", m.obj_side[LEFT]);
SERIAL_ECHOLNPGM(" Left: ", m.obj_side[LEFT]);
#endif
#if ENABLED(CALIBRATION_MEASURE_RIGHT)
SERIAL_ECHOLNPAIR(" Right: ", m.obj_side[RIGHT]);
SERIAL_ECHOLNPGM(" Right: ", m.obj_side[RIGHT]);
#endif
#if ENABLED(CALIBRATION_MEASURE_FRONT)
SERIAL_ECHOLNPAIR(" Front: ", m.obj_side[FRONT]);
#if HAS_Y_AXIS
#if ENABLED(CALIBRATION_MEASURE_FRONT)
SERIAL_ECHOLNPGM(" Front: ", m.obj_side[FRONT]);
#endif
#if ENABLED(CALIBRATION_MEASURE_BACK)
SERIAL_ECHOLNPGM(" Back: ", m.obj_side[BACK]);
#endif
#endif
#if ENABLED(CALIBRATION_MEASURE_BACK)
SERIAL_ECHOLNPAIR(" Back: ", m.obj_side[BACK]);
#if LINEAR_AXES >= 4
#if ENABLED(CALIBRATION_MEASURE_IMIN)
SERIAL_ECHOLNPGM(" " STR_I_MIN ": ", m.obj_side[IMINIMUM]);
#endif
#if ENABLED(CALIBRATION_MEASURE_IMAX)
SERIAL_ECHOLNPGM(" " STR_I_MAX ": ", m.obj_side[IMAXIMUM]);
#endif
#endif
#if LINEAR_AXES >= 5
#if ENABLED(CALIBRATION_MEASURE_JMIN)
SERIAL_ECHOLNPGM(" " STR_J_MIN ": ", m.obj_side[JMINIMUM]);
#endif
#if ENABLED(CALIBRATION_MEASURE_JMAX)
SERIAL_ECHOLNPGM(" " STR_J_MAX ": ", m.obj_side[JMAXIMUM]);
#endif
#endif
#if LINEAR_AXES >= 6
#if ENABLED(CALIBRATION_MEASURE_KMIN)
SERIAL_ECHOLNPGM(" " STR_K_MIN ": ", m.obj_side[KMINIMUM]);
#endif
#if ENABLED(CALIBRATION_MEASURE_KMAX)
SERIAL_ECHOLNPGM(" " STR_K_MAX ": ", m.obj_side[KMAXIMUM]);
#endif
#endif
SERIAL_EOL();
}
@@ -359,12 +400,21 @@ inline void probe_sides(measurements_t &m, const float uncertainty) {
inline void report_measured_center(const measurements_t &m) {
SERIAL_ECHOLNPGM("Center:");
#if HAS_X_CENTER
SERIAL_ECHOLNPAIR_P(SP_X_STR, m.obj_center.x);
SERIAL_ECHOLNPGM_P(SP_X_STR, m.obj_center.x);
#endif
#if HAS_Y_CENTER
SERIAL_ECHOLNPAIR_P(SP_Y_STR, m.obj_center.y);
SERIAL_ECHOLNPGM_P(SP_Y_STR, m.obj_center.y);
#endif
SERIAL_ECHOLNPGM_P(SP_Z_STR, m.obj_center.z);
#if HAS_I_CENTER
SERIAL_ECHOLNPGM_P(SP_I_STR, m.obj_center.i);
#endif
#if HAS_J_CENTER
SERIAL_ECHOLNPGM_P(SP_J_STR, m.obj_center.j);
#endif
#if HAS_K_CENTER
SERIAL_ECHOLNPGM_P(SP_K_STR, m.obj_center.k);
#endif
SERIAL_ECHOLNPAIR_P(SP_Z_STR, m.obj_center.z);
SERIAL_EOL();
}
@@ -372,53 +422,85 @@ inline void probe_sides(measurements_t &m, const float uncertainty) {
SERIAL_ECHOLNPGM("Backlash:");
#if AXIS_CAN_CALIBRATE(X)
#if ENABLED(CALIBRATION_MEASURE_LEFT)
SERIAL_ECHOLNPAIR(" Left: ", m.backlash[LEFT]);
SERIAL_ECHOLNPGM(" Left: ", m.backlash[LEFT]);
#endif
#if ENABLED(CALIBRATION_MEASURE_RIGHT)
SERIAL_ECHOLNPAIR(" Right: ", m.backlash[RIGHT]);
SERIAL_ECHOLNPGM(" Right: ", m.backlash[RIGHT]);
#endif
#endif
#if AXIS_CAN_CALIBRATE(Y)
#if HAS_Y_AXIS && AXIS_CAN_CALIBRATE(Y)
#if ENABLED(CALIBRATION_MEASURE_FRONT)
SERIAL_ECHOLNPAIR(" Front: ", m.backlash[FRONT]);
SERIAL_ECHOLNPGM(" Front: ", m.backlash[FRONT]);
#endif
#if ENABLED(CALIBRATION_MEASURE_BACK)
SERIAL_ECHOLNPAIR(" Back: ", m.backlash[BACK]);
SERIAL_ECHOLNPGM(" Back: ", m.backlash[BACK]);
#endif
#endif
#if AXIS_CAN_CALIBRATE(Z)
SERIAL_ECHOLNPAIR(" Top: ", m.backlash[TOP]);
#if HAS_Z_AXIS && AXIS_CAN_CALIBRATE(Z)
SERIAL_ECHOLNPGM(" Top: ", m.backlash[TOP]);
#endif
#if LINEAR_AXES >= 4 && AXIS_CAN_CALIBRATE(I)
#if ENABLED(CALIBRATION_MEASURE_IMIN)
SERIAL_ECHOLNPGM(" " STR_I_MIN ": ", m.backlash[IMINIMUM]);
#endif
#if ENABLED(CALIBRATION_MEASURE_IMAX)
SERIAL_ECHOLNPGM(" " STR_I_MAX ": ", m.backlash[IMAXIMUM]);
#endif
#endif
#if LINEAR_AXES >= 5 && AXIS_CAN_CALIBRATE(J)
#if ENABLED(CALIBRATION_MEASURE_JMIN)
SERIAL_ECHOLNPGM(" " STR_J_MIN ": ", m.backlash[JMINIMUM]);
#endif
#if ENABLED(CALIBRATION_MEASURE_JMAX)
SERIAL_ECHOLNPGM(" " STR_J_MAX ": ", m.backlash[JMAXIMUM]);
#endif
#endif
#if LINEAR_AXES >= 6 && AXIS_CAN_CALIBRATE(K)
#if ENABLED(CALIBRATION_MEASURE_KMIN)
SERIAL_ECHOLNPGM(" " STR_K_MIN ": ", m.backlash[KMINIMUM]);
#endif
#if ENABLED(CALIBRATION_MEASURE_KMAX)
SERIAL_ECHOLNPGM(" " STR_K_MAX ": ", m.backlash[KMAXIMUM]);
#endif
#endif
SERIAL_EOL();
}
inline void report_measured_positional_error(const measurements_t &m) {
SERIAL_CHAR('T');
SERIAL_ECHO(int(active_extruder));
SERIAL_ECHO(active_extruder);
SERIAL_ECHOLNPGM(" Positional Error:");
#if HAS_X_CENTER
SERIAL_ECHOLNPAIR_P(SP_X_STR, m.pos_error.x);
#if HAS_X_CENTER && AXIS_CAN_CALIBRATE(X)
SERIAL_ECHOLNPGM_P(SP_X_STR, m.pos_error.x);
#endif
#if HAS_Y_CENTER
SERIAL_ECHOLNPAIR_P(SP_Y_STR, m.pos_error.y);
#if HAS_Y_CENTER && AXIS_CAN_CALIBRATE(Y)
SERIAL_ECHOLNPGM_P(SP_Y_STR, m.pos_error.y);
#endif
#if HAS_Z_AXIS && AXIS_CAN_CALIBRATE(Z)
SERIAL_ECHOLNPGM_P(SP_Z_STR, m.pos_error.z);
#endif
#if HAS_I_CENTER && AXIS_CAN_CALIBRATE(I)
SERIAL_ECHOLNPGM_P(SP_I_STR, m.pos_error.i);
#endif
#if HAS_J_CENTER && AXIS_CAN_CALIBRATE(J)
SERIAL_ECHOLNPGM_P(SP_J_STR, m.pos_error.j);
#endif
#if HAS_K_CENTER && AXIS_CAN_CALIBRATE(K)
SERIAL_ECHOLNPGM_P(SP_Z_STR, m.pos_error.z);
#endif
if (AXIS_CAN_CALIBRATE(Z)) SERIAL_ECHOLNPAIR_P(SP_Z_STR, m.pos_error.z);
SERIAL_EOL();
}
inline void report_measured_nozzle_dimensions(const measurements_t &m) {
SERIAL_ECHOLNPGM("Nozzle Tip Outer Dimensions:");
#if HAS_X_CENTER || HAS_Y_CENTER
#if HAS_X_CENTER
SERIAL_ECHOLNPAIR_P(SP_X_STR, m.nozzle_outer_dimension.x);
#endif
#if HAS_Y_CENTER
SERIAL_ECHOLNPAIR_P(SP_Y_STR, m.nozzle_outer_dimension.y);
#endif
#else
UNUSED(m);
#if HAS_X_CENTER
SERIAL_ECHOLNPGM_P(SP_X_STR, m.nozzle_outer_dimension.x);
#endif
#if HAS_Y_CENTER
SERIAL_ECHOLNPGM_P(SP_Y_STR, m.nozzle_outer_dimension.y);
#endif
SERIAL_EOL();
UNUSED(m);
}
#if HAS_HOTEND_OFFSET
@@ -427,7 +509,7 @@ inline void probe_sides(measurements_t &m, const float uncertainty) {
//
inline void report_hotend_offsets() {
LOOP_S_L_N(e, 1, HOTENDS)
SERIAL_ECHOLNPAIR_P(PSTR("T"), int(e), PSTR(" Hotend Offset X"), hotend_offset[e].x, SP_Y_STR, hotend_offset[e].y, SP_Z_STR, hotend_offset[e].z);
SERIAL_ECHOLNPGM_P(PSTR("T"), e, PSTR(" Hotend Offset X"), hotend_offset[e].x, SP_Y_STR, hotend_offset[e].y, SP_Z_STR, hotend_offset[e].z);
}
#endif
@@ -467,8 +549,33 @@ inline void calibrate_backlash(measurements_t &m, const float uncertainty) {
backlash.distance_mm.y = m.backlash[BACK];
#endif
if (AXIS_CAN_CALIBRATE(Z)) backlash.distance_mm.z = m.backlash[TOP];
#endif
TERN_(HAS_Z_AXIS, if (AXIS_CAN_CALIBRATE(Z)) backlash.distance_mm.z = m.backlash[TOP]);
#if HAS_I_CENTER
backlash.distance_mm.i = (m.backlash[IMINIMUM] + m.backlash[IMAXIMUM]) / 2;
#elif ENABLED(CALIBRATION_MEASURE_IMIN)
backlash.distance_mm.i = m.backlash[IMINIMUM];
#elif ENABLED(CALIBRATION_MEASURE_IMAX)
backlash.distance_mm.i = m.backlash[IMAXIMUM];
#endif
#if HAS_J_CENTER
backlash.distance_mm.j = (m.backlash[JMINIMUM] + m.backlash[JMAXIMUM]) / 2;
#elif ENABLED(CALIBRATION_MEASURE_JMIN)
backlash.distance_mm.j = m.backlash[JMINIMUM];
#elif ENABLED(CALIBRATION_MEASURE_JMAX)
backlash.distance_mm.j = m.backlash[JMAXIMUM];
#endif
#if HAS_K_CENTER
backlash.distance_mm.k = (m.backlash[KMINIMUM] + m.backlash[KMAXIMUM]) / 2;
#elif ENABLED(CALIBRATION_MEASURE_KMIN)
backlash.distance_mm.k = m.backlash[KMINIMUM];
#elif ENABLED(CALIBRATION_MEASURE_KMAX)
backlash.distance_mm.k = m.backlash[KMAXIMUM];
#endif
#endif // BACKLASH_GCODE
}
#if ENABLED(BACKLASH_GCODE)
@@ -478,7 +585,10 @@ inline void calibrate_backlash(measurements_t &m, const float uncertainty) {
// New scope for TEMPORARY_BACKLASH_CORRECTION
TEMPORARY_BACKLASH_CORRECTION(all_on);
TEMPORARY_BACKLASH_SMOOTHING(0.0f);
const xyz_float_t move = { AXIS_CAN_CALIBRATE(X) * 3, AXIS_CAN_CALIBRATE(Y) * 3, AXIS_CAN_CALIBRATE(Z) * 3 };
const xyz_float_t move = LINEAR_AXIS_ARRAY(
AXIS_CAN_CALIBRATE(X) * 3, AXIS_CAN_CALIBRATE(Y) * 3, AXIS_CAN_CALIBRATE(Z) * 3,
AXIS_CAN_CALIBRATE(I) * 3, AXIS_CAN_CALIBRATE(J) * 3, AXIS_CAN_CALIBRATE(K) * 3
);
current_position += move; calibration_move();
current_position -= move; calibration_move();
}
@@ -506,11 +616,7 @@ inline void calibrate_toolhead(measurements_t &m, const float uncertainty, const
TEMPORARY_BACKLASH_CORRECTION(all_on);
TEMPORARY_BACKLASH_SMOOTHING(0.0f);
#if HOTENDS > 1
set_nozzle(m, extruder);
#else
UNUSED(extruder);
#endif
TERN(HAS_MULTI_HOTEND, set_nozzle(m, extruder), UNUSED(extruder));
probe_sides(m, uncertainty);
@@ -529,6 +635,10 @@ inline void calibrate_toolhead(measurements_t &m, const float uncertainty, const
if (ENABLED(HAS_Y_CENTER) && AXIS_CAN_CALIBRATE(Y)) update_measurements(m, Y_AXIS);
if (AXIS_CAN_CALIBRATE(Z)) update_measurements(m, Z_AXIS);
TERN_(HAS_I_CENTER, update_measurements(m, I_AXIS));
TERN_(HAS_J_CENTER, update_measurements(m, J_AXIS));
TERN_(HAS_K_CENTER, update_measurements(m, K_AXIS));
sync_plan_position();
}
@@ -545,13 +655,9 @@ inline void calibrate_all_toolheads(measurements_t &m, const float uncertainty)
HOTEND_LOOP() calibrate_toolhead(m, uncertainty, e);
#if HAS_HOTEND_OFFSET
normalize_hotend_offsets();
#endif
TERN_(HAS_HOTEND_OFFSET, normalize_hotend_offsets());
#if HOTENDS > 1
set_nozzle(m, 0);
#endif
TERN_(HAS_MULTI_HOTEND, set_nozzle(m, 0));
}
/**
@@ -568,9 +674,7 @@ inline void calibrate_all_toolheads(measurements_t &m, const float uncertainty)
inline void calibrate_all() {
measurements_t m;
#if HAS_HOTEND_OFFSET
reset_hotend_offsets();
#endif
TERN_(HAS_HOTEND_OFFSET, reset_hotend_offsets());
TEMPORARY_BACKLASH_CORRECTION(all_on);
TEMPORARY_BACKLASH_SMOOTHING(0.0f);
@@ -578,12 +682,10 @@ inline void calibrate_all() {
// Do a fast and rough calibration of the toolheads
calibrate_all_toolheads(m, CALIBRATION_MEASUREMENT_UNKNOWN);
#if ENABLED(BACKLASH_GCODE)
calibrate_backlash(m, CALIBRATION_MEASUREMENT_UNCERTAIN);
#endif
TERN_(BACKLASH_GCODE, calibrate_backlash(m, CALIBRATION_MEASUREMENT_UNCERTAIN));
// Cycle the toolheads so the servos settle into their "natural" positions
#if HOTENDS > 1
#if HAS_MULTI_HOTEND
HOTEND_LOOP() set_nozzle(m, e);
#endif
@@ -605,19 +707,23 @@ inline void calibrate_all() {
* no args - Perform entire calibration sequence (backlash + position on all toolheads)
*/
void GcodeSuite::G425() {
TEMPORARY_SOFT_ENDSTOP_STATE(false);
TEMPORARY_BED_LEVELING_STATE(false);
if (axis_unhomed_error()) return;
#ifdef CALIBRATION_SCRIPT_PRE
GcodeSuite::process_subcommands_now_P(PSTR(CALIBRATION_SCRIPT_PRE));
#endif
if (homing_needed_error()) return;
TEMPORARY_BED_LEVELING_STATE(false);
SET_SOFT_ENDSTOP_LOOSE(true);
measurements_t m;
const float uncertainty = parser.floatval('U', CALIBRATION_MEASUREMENT_UNCERTAIN);
float uncertainty = parser.seenval('U') ? parser.value_float() : CALIBRATION_MEASUREMENT_UNCERTAIN;
if (parser.seen('B'))
if (parser.seen_test('B'))
calibrate_backlash(m, uncertainty);
else if (parser.seen('T'))
calibrate_toolhead(m, uncertainty, parser.has_value() ? parser.value_int() : active_extruder);
else if (parser.seen_test('T'))
calibrate_toolhead(m, uncertainty, parser.intval('T', active_extruder));
#if ENABLED(CALIBRATION_REPORTING)
else if (parser.seen('V')) {
probe_sides(m, uncertainty);
@@ -635,6 +741,12 @@ void GcodeSuite::G425() {
#endif
else
calibrate_all();
SET_SOFT_ENDSTOP_LOOSE(false);
#ifdef CALIBRATION_SCRIPT_POST
GcodeSuite::process_subcommands_now_P(PSTR(CALIBRATION_SCRIPT_POST));
#endif
}
#endif // CALIBRATION_GCODE

View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -36,6 +36,7 @@
#include "../../module/temperature.h"
#include "../../module/probe.h"
#include "../../feature/probe_temp_comp.h"
#include "../../lcd/marlinui.h"
/**
* G76: calibrate probe and/or bed temperature offsets
@@ -45,8 +46,8 @@
* - When calibrating bed, probe temperature is held constant.
* Compensation values are deltas to first probe measurement at bed temp. = 60°C.
* - The hotend will not be heated at any time.
* - On my Prusa MK3S clone I put a piece of paper between the probe and the hotend
* so the hotend fan would not cool my probe constantly. Alternativly you could just
* - On my Průša MK3S clone I put a piece of paper between the probe and the hotend
* so the hotend fan would not cool my probe constantly. Alternatively you could just
* make sure the fan is not running while running the calibration process.
*
* Probe calibration:
@@ -80,6 +81,12 @@
* - `B` - Run bed temperature calibration.
* - `P` - Run probe temperature calibration.
*/
static void say_waiting_for() { SERIAL_ECHOPGM("Waiting for "); }
static void say_waiting_for_probe_heating() { say_waiting_for(); SERIAL_ECHOLNPGM("probe heating."); }
static void say_successfully_calibrated() { SERIAL_ECHOPGM("Successfully calibrated"); }
static void say_failed_to_calibrate() { SERIAL_ECHOPGM("!Failed to calibrate"); }
void GcodeSuite::G76() {
// Check if heated bed is available and z-homing is done with probe
#if TEMP_SENSOR_BED == 0 || !(HOMING_Z_WITH_PROBE)
@@ -96,20 +103,26 @@ void GcodeSuite::G76() {
return (timeout && ELAPSED(ms, timeout));
};
auto wait_for_temps = [&](const float tb, const float tp, millis_t &ntr, const millis_t timeout=0) {
SERIAL_ECHOLNPGM("Waiting for bed and probe temperature.");
while (fabs(thermalManager.degBed() - tb) > 0.1f || thermalManager.degProbe() > tp)
auto wait_for_temps = [&](const celsius_t tb, const celsius_t tp, millis_t &ntr, const millis_t timeout=0) {
say_waiting_for(); SERIAL_ECHOLNPGM("bed and probe temperature.");
while (thermalManager.wholeDegBed() != tb || thermalManager.wholeDegProbe() > tp)
if (report_temps(ntr, timeout)) return true;
return false;
};
auto g76_probe = [](const xy_pos_t &xypos) {
do_blocking_move_to_z(5.0); // Raise nozzle before probing
const float measured_z = probe.probe_at_point(xypos, PROBE_PT_NONE, 0, false); // verbose=0, probe_relative=false
auto g76_probe = [](const TempSensorID sid, celsius_t &targ, const xy_pos_t &nozpos) {
do_z_clearance(5.0); // Raise nozzle before probing
const float measured_z = probe.probe_at_point(nozpos, PROBE_PT_STOW, 0, false); // verbose=0, probe_relative=false
if (isnan(measured_z))
SERIAL_ECHOLNPGM("!Received NAN. Aborting.");
else
else {
SERIAL_ECHOLNPAIR_F("Measured: ", measured_z);
if (targ == cali_info_init[sid].start_temp)
temp_comp.prepare_new_calibration(measured_z);
else
temp_comp.push_back_new_measurement(sid, measured_z);
targ += cali_info_init[sid].temp_res;
}
return measured_z;
};
@@ -125,8 +138,9 @@ void GcodeSuite::G76() {
// Synchronize with planner
planner.synchronize();
const xyz_pos_t parkpos = { temp_comp.park_point_x, temp_comp.park_point_y, temp_comp.park_point_z };
const xy_pos_t ppos = { temp_comp.measure_point_x, temp_comp.measure_point_y };
const xyz_pos_t parkpos = temp_comp.park_point,
probe_pos_xyz = xyz_pos_t(temp_comp.measure_point) + xyz_pos_t({ 0.0f, 0.0f, PTC_PROBE_HEATING_OFFSET }),
noz_pos_xyz = probe_pos_xyz - probe.offset_xy; // Nozzle position based on probe position
if (do_bed_cal || do_probe_cal) {
// Ensure park position is reachable
@@ -135,7 +149,7 @@ void GcodeSuite::G76() {
SERIAL_ECHOLNPGM("!Park");
else {
// Ensure probe position is reachable
reachable = probe.can_reach(ppos);
reachable = probe.can_reach(probe_pos_xyz);
if (!reachable) SERIAL_ECHOLNPGM("!Probe");
}
@@ -144,14 +158,11 @@ void GcodeSuite::G76() {
return;
}
process_subcommands_now_P(PSTR("G28"));
process_subcommands_now_P(G28_STR);
}
remember_feedrate_scaling_off();
// Nozzle position based on probe position
const xy_pos_t noz_pos = ppos - probe.offset_xy;
/******************************************
* Calibrate bed temperature offsets
******************************************/
@@ -159,63 +170,60 @@ void GcodeSuite::G76() {
// Report temperatures every second and handle heating timeouts
millis_t next_temp_report = millis() + 1000;
auto report_targets = [&](const celsius_t tb, const celsius_t tp) {
SERIAL_ECHOLNPGM("Target Bed:", tb, " Probe:", tp);
};
if (do_bed_cal) {
uint16_t target_bed = temp_comp.cali_info_init[TSI_BED].start_temp,
target_probe = temp_comp.bed_calib_probe_temp;
celsius_t target_bed = cali_info_init[TSI_BED].start_temp,
target_probe = temp_comp.bed_calib_probe_temp;
SERIAL_ECHOLNPGM("Waiting for cooling.");
while (thermalManager.degBed() > target_bed || thermalManager.degProbe() > target_probe)
say_waiting_for(); SERIAL_ECHOLNPGM(" cooling.");
while (thermalManager.wholeDegBed() > target_bed || thermalManager.wholeDegProbe() > target_probe)
report_temps(next_temp_report);
// Disable leveling so it won't mess with us
#if HAS_LEVELING
set_bed_leveling_enabled(false);
#endif
TERN_(HAS_LEVELING, set_bed_leveling_enabled(false));
for (;;) {
thermalManager.setTargetBed(target_bed);
SERIAL_ECHOLNPAIR("Target Bed:", target_bed, " Probe:", target_probe);
report_targets(target_bed, target_probe);
// Park nozzle
do_blocking_move_to(parkpos);
// Wait for heatbed to reach target temp and probe to cool below target temp
if (wait_for_temps(target_bed, target_probe, next_temp_report, millis() + 900UL * 1000UL)) {
if (wait_for_temps(target_bed, target_probe, next_temp_report, millis() + MIN_TO_MS(15))) {
SERIAL_ECHOLNPGM("!Bed heating timeout.");
break;
}
// Move the nozzle to the probing point and wait for the probe to reach target temp
do_blocking_move_to_xy(noz_pos);
SERIAL_ECHOLNPGM("Waiting for probe heating.");
while (thermalManager.degProbe() < target_probe)
do_blocking_move_to(noz_pos_xyz);
say_waiting_for_probe_heating();
SERIAL_EOL();
while (thermalManager.wholeDegProbe() < target_probe)
report_temps(next_temp_report);
const float measured_z = g76_probe(noz_pos);
if (isnan(measured_z)) break;
if (target_bed == temp_comp.cali_info_init[TSI_BED].start_temp)
temp_comp.prepare_new_calibration(measured_z);
else
temp_comp.push_back_new_measurement(TSI_BED, measured_z);
target_bed += temp_comp.cali_info_init[TSI_BED].temp_res;
if (target_bed > temp_comp.max_bed_temp) break;
const float measured_z = g76_probe(TSI_BED, target_bed, noz_pos_xyz);
if (isnan(measured_z) || target_bed > (BED_MAX_TARGET)) break;
}
SERIAL_ECHOLNPAIR("Retrieved measurements: ", temp_comp.get_index());
if (temp_comp.finish_calibration(TSI_BED))
SERIAL_ECHOLNPGM("Successfully calibrated bed.");
else
SERIAL_ECHOLNPGM("!Failed to calibrate bed. Values reset.");
SERIAL_ECHOLNPGM("Retrieved measurements: ", temp_comp.get_index());
if (temp_comp.finish_calibration(TSI_BED)) {
say_successfully_calibrated();
SERIAL_ECHOLNPGM(" bed.");
}
else {
say_failed_to_calibrate();
SERIAL_ECHOLNPGM(" bed. Values reset.");
}
// Cleanup
thermalManager.setTargetBed(0);
#if HAS_LEVELING
set_bed_leveling_enabled(true);
#endif
TERN_(HAS_LEVELING, set_bed_leveling_enabled(true));
} // do_bed_cal
/********************************************
@@ -228,26 +236,27 @@ void GcodeSuite::G76() {
do_blocking_move_to(parkpos);
// Initialize temperatures
const uint16_t target_bed = temp_comp.probe_calib_bed_temp;
const celsius_t target_bed = temp_comp.probe_calib_bed_temp;
thermalManager.setTargetBed(target_bed);
uint16_t target_probe = temp_comp.cali_info_init[TSI_PROBE].start_temp;
celsius_t target_probe = cali_info_init[TSI_PROBE].start_temp;
report_targets(target_bed, target_probe);
// Wait for heatbed to reach target temp and probe to cool below target temp
wait_for_temps(target_bed, target_probe, next_temp_report);
// Disable leveling so it won't mess with us
#if HAS_LEVELING
set_bed_leveling_enabled(false);
#endif
TERN_(HAS_LEVELING, set_bed_leveling_enabled(false));
bool timeout = false;
for (;;) {
// Move probe to probing point and wait for it to reach target temperature
do_blocking_move_to_xy(noz_pos);
do_blocking_move_to(noz_pos_xyz);
SERIAL_ECHOLNPAIR("Waiting for probe heating. Bed:", target_bed, " Probe:", target_probe);
const millis_t probe_timeout_ms = millis() + 900UL * 1000UL;
say_waiting_for_probe_heating();
SERIAL_ECHOLNPGM(" Bed:", target_bed, " Probe:", target_probe);
const millis_t probe_timeout_ms = millis() + SEC_TO_MS(900UL);
while (thermalManager.degProbe() < target_probe) {
if (report_temps(next_temp_report, probe_timeout_ms)) {
SERIAL_ECHOLNPGM("!Probe heating timed out.");
@@ -257,30 +266,20 @@ void GcodeSuite::G76() {
}
if (timeout) break;
const float measured_z = g76_probe(noz_pos);
if (isnan(measured_z)) break;
if (target_probe == temp_comp.cali_info_init[TSI_PROBE].start_temp)
temp_comp.prepare_new_calibration(measured_z);
else
temp_comp.push_back_new_measurement(TSI_PROBE, measured_z);
target_probe += temp_comp.cali_info_init[TSI_PROBE].temp_res;
if (target_probe > temp_comp.cali_info_init[TSI_PROBE].end_temp) break;
const float measured_z = g76_probe(TSI_PROBE, target_probe, noz_pos_xyz);
if (isnan(measured_z) || target_probe > cali_info_init[TSI_PROBE].end_temp) break;
}
SERIAL_ECHOLNPAIR("Retrieved measurements: ", temp_comp.get_index());
SERIAL_ECHOLNPGM("Retrieved measurements: ", temp_comp.get_index());
if (temp_comp.finish_calibration(TSI_PROBE))
SERIAL_ECHOPGM("Successfully calibrated");
say_successfully_calibrated();
else
SERIAL_ECHOPGM("!Failed to calibrate");
say_failed_to_calibrate();
SERIAL_ECHOLNPGM(" probe.");
// Cleanup
thermalManager.setTargetBed(0);
#if HAS_LEVELING
set_bed_leveling_enabled(true);
#endif
TERN_(HAS_LEVELING, set_bed_leveling_enabled(true));
SERIAL_ECHOLNPGM("Final compensation values:");
temp_comp.print_offsets();
@@ -316,17 +315,17 @@ void GcodeSuite::M871() {
}
else if (parser.seen("BPE")) {
if (!parser.seenval('V')) return;
const int16_t val = parser.value_int();
const int16_t offset_val = parser.value_int();
if (!parser.seenval('I')) return;
const int16_t idx = parser.value_int();
const TempSensorID mod = (parser.seen('B') ? TSI_BED :
#if ENABLED(USE_TEMP_EXT_COMPENSATION)
parser.seen('E') ? TSI_EXT :
#endif
TSI_PROBE
#if ENABLED(USE_TEMP_EXT_COMPENSATION)
parser.seen('E') ? TSI_EXT :
#endif
TSI_PROBE
);
if (idx > 0 && temp_comp.set_offset(mod, idx - 1, val))
SERIAL_ECHOLNPAIR("Set value: ", val);
if (idx > 0 && temp_comp.set_offset(mod, idx - 1, offset_val))
SERIAL_ECHOLNPGM("Set value: ", offset_val);
else
SERIAL_ECHOLNPGM("!Invalid index. Failed to set value (note: value at index 0 is constant).");
@@ -335,4 +334,25 @@ void GcodeSuite::M871() {
temp_comp.print_offsets();
}
/**
* M192: Wait for probe temperature sensor to reach a target
*
* Select only one of these flags:
* R - Wait for heating or cooling
* S - Wait only for heating
*/
void GcodeSuite::M192() {
if (DEBUGGING(DRYRUN)) return;
const bool no_wait_for_cooling = parser.seenval('S');
if (!no_wait_for_cooling && ! parser.seenval('R')) {
SERIAL_ERROR_MSG("No target temperature set.");
return;
}
const celsius_t target_temp = parser.value_celsius();
ui.set_status_P(thermalManager.isProbeBelowTemp(target_temp) ? GET_TEXT(MSG_PROBE_HEATING) : GET_TEXT(MSG_PROBE_COOLING));
thermalManager.wait_for_probe(target_temp, no_wait_for_cooling);
}
#endif // PROBE_TEMP_COMPENSATION

115
Marlin/src/gcode/calibrate/M100.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -26,7 +26,7 @@
#include "../gcode.h"
#include "../queue.h"
#include "../../libs/hex_print_routines.h"
#include "../../libs/hex_print.h"
#include "../../MarlinCore.h" // for idle()
@@ -51,7 +51,7 @@
* Also, there are two support functions that can be called from a developer's C code.
*
* uint16_t check_for_free_memory_corruption(PGM_P const free_memory_start);
* void M100_dump_routine(PGM_P const title, const char * const start, const char * const end);
* void M100_dump_routine(PGM_P const title, const char * const start, const uintptr_t size);
*
* Initial version by Roxy-3D
*/
@@ -60,7 +60,7 @@
#define TEST_BYTE ((char) 0xE5)
#if defined(__AVR__) || IS_32BIT_TEENSY
#if EITHER(__AVR__, IS_32BIT_TEENSY)
extern char __bss_end;
char *end_bss = &__bss_end,
@@ -116,13 +116,18 @@
// Utility functions
//
// Location of a variable on its stack frame. Returns a value above
// the stack (once the function returns to the caller).
char* top_of_stack() {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wreturn-local-addr"
// Location of a variable in its stack frame.
// The returned address will be above the stack (after it returns).
char *top_of_stack() {
char x;
return &x + 1; // x is pulled on return;
}
#pragma GCC diagnostic pop
// Count the number of test bytes at the specified location.
inline int32_t count_test_bytes(const char * const start_free_memory) {
for (uint32_t i = 0; i < 32000; i++)
@@ -146,13 +151,13 @@ inline int32_t count_test_bytes(const char * const start_free_memory) {
* the block. If so, it may indicate memory corruption due to a bad pointer.
* Unexpected bytes are flagged in the right column.
*/
inline void dump_free_memory(char *start_free_memory, char *end_free_memory) {
void dump_free_memory(char *start_free_memory, char *end_free_memory) {
//
// Start and end the dump on a nice 16 byte boundary
// (even though the values are not 16-byte aligned).
//
start_free_memory = (char*)(ptr_int_t(uint32_t(start_free_memory) & ~0xFUL)); // Align to 16-byte boundary
end_free_memory = (char*)(ptr_int_t(uint32_t(end_free_memory) | 0xFUL)); // Align end_free_memory to the 15th byte (at or above end_free_memory)
start_free_memory = (char*)(uintptr_t(uint32_t(start_free_memory) & ~0xFUL)); // Align to 16-byte boundary
end_free_memory = (char*)(uintptr_t(uint32_t(end_free_memory) | 0xFUL)); // Align end_free_memory to the 15th byte (at or above end_free_memory)
// Dump command main loop
while (start_free_memory < end_free_memory) {
@@ -177,42 +182,42 @@ inline int32_t count_test_bytes(const char * const start_free_memory) {
}
}
void M100_dump_routine(PGM_P const title, const char * const start, const char * const end) {
serialprintPGM(title);
SERIAL_EOL();
void M100_dump_routine(PGM_P const title, const char * const start, const uintptr_t size) {
SERIAL_ECHOLNPGM_P(title);
//
// Round the start and end locations to produce full lines of output
//
const char * const end = start + size - 1;
dump_free_memory(
(char*)(ptr_int_t(uint32_t(start) & ~0xFUL)), // Align to 16-byte boundary
(char*)(ptr_int_t(uint32_t(end) | 0xFUL)) // Align end_free_memory to the 15th byte (at or above end_free_memory)
(char*)(uintptr_t(uint32_t(start) & ~0xFUL)), // Align to 16-byte boundary
(char*)(uintptr_t(uint32_t(end) | 0xFUL)) // Align end_free_memory to the 15th byte (at or above end_free_memory)
);
}
#endif // M100_FREE_MEMORY_DUMPER
inline int check_for_free_memory_corruption(PGM_P const title) {
serialprintPGM(title);
SERIAL_ECHOPGM_P(title);
char *start_free_memory = free_memory_start, *end_free_memory = free_memory_end;
int n = end_free_memory - start_free_memory;
SERIAL_ECHOPAIR("\nfmc() n=", n);
SERIAL_ECHOPAIR("\nfree_memory_start=", hex_address(free_memory_start));
SERIAL_ECHOLNPAIR(" end_free_memory=", hex_address(end_free_memory));
SERIAL_ECHOLNPGM("\nfmc() n=", n,
"\nfree_memory_start=", hex_address(free_memory_start),
" end=", hex_address(end_free_memory));
if (end_free_memory < start_free_memory) {
SERIAL_ECHOPGM(" end_free_memory < Heap ");
// SET_INPUT_PULLUP(63); // if the developer has a switch wired up to their controller board
// safe_delay(5); // this code can be enabled to pause the display as soon as the
// while ( READ(63)) // malfunction is detected. It is currently defaulting to a switch
// idle(); // being on pin-63 which is unassigend and available on most controller
// safe_delay(20); // boards.
// while ( !READ(63))
// idle();
//SET_INPUT_PULLUP(63); // if the developer has a switch wired up to their controller board
//safe_delay(5); // this code can be enabled to pause the display as soon as the
//while ( READ(63)) // malfunction is detected. It is currently defaulting to a switch
// idle(); // being on pin-63 which is unassigend and available on most controller
//safe_delay(20); // boards.
//while ( !READ(63))
// idle();
serial_delay(20);
#if ENABLED(M100_FREE_MEMORY_DUMPER)
M100_dump_routine(PSTR(" Memory corruption detected with end_free_memory<Heap\n"), (const char*)0x1B80, (const char*)0x21FF);
M100_dump_routine(PSTR(" Memory corruption detected with end_free_memory<Heap\n"), (const char*)0x1B80, 0x0680);
#endif
}
@@ -222,17 +227,15 @@ inline int check_for_free_memory_corruption(PGM_P const title) {
if (start_free_memory[i] == TEST_BYTE) {
int32_t j = count_test_bytes(start_free_memory + i);
if (j > 8) {
// SERIAL_ECHOPAIR("Found ", j);
// SERIAL_ECHOLNPAIR(" bytes free at ", hex_address(start_free_memory + i));
//SERIAL_ECHOPGM("Found ", j);
//SERIAL_ECHOLNPGM(" bytes free at ", hex_address(start_free_memory + i));
i += j;
block_cnt++;
SERIAL_ECHOPAIR(" (", block_cnt);
SERIAL_ECHOPAIR(") found=", j);
SERIAL_ECHOLNPGM(" ");
SERIAL_ECHOLNPGM(" (", block_cnt, ") found=", j);
}
}
}
SERIAL_ECHOPAIR(" block_found=", block_cnt);
SERIAL_ECHOPGM(" block_found=", block_cnt);
if (block_cnt != 1)
SERIAL_ECHOLNPGM("\nMemory Corruption detected in free memory area.");
@@ -264,8 +267,7 @@ inline void free_memory_pool_report(char * const start_free_memory, const int32_
if (*addr == TEST_BYTE) {
const int32_t j = count_test_bytes(addr);
if (j > 8) {
SERIAL_ECHOPAIR("Found ", j);
SERIAL_ECHOLNPAIR(" bytes free at ", hex_address(addr));
SERIAL_ECHOLNPGM("Found ", j, " bytes free at ", hex_address(addr));
if (j > max_cnt) {
max_cnt = j;
max_addr = addr;
@@ -275,12 +277,11 @@ inline void free_memory_pool_report(char * const start_free_memory, const int32_
}
}
}
if (block_cnt > 1) {
SERIAL_ECHOLNPGM("\nMemory Corruption detected in free memory area.");
SERIAL_ECHOPAIR("\nLargest free block is ", max_cnt);
SERIAL_ECHOLNPAIR(" bytes at ", hex_address(max_addr));
}
SERIAL_ECHOLNPAIR("check_for_free_memory_corruption() = ", check_for_free_memory_corruption(PSTR("M100 F ")));
if (block_cnt > 1) SERIAL_ECHOLNPGM(
"\nMemory Corruption detected in free memory area."
"\nLargest free block is ", max_cnt, " bytes at ", hex_address(max_addr)
);
SERIAL_ECHOLNPGM("check_for_free_memory_corruption() = ", check_for_free_memory_corruption(PSTR("M100 F ")));
}
#if ENABLED(M100_FREE_MEMORY_CORRUPTOR)
@@ -289,16 +290,16 @@ inline void free_memory_pool_report(char * const start_free_memory, const int32_
* Corrupt <num> locations in the free memory pool and report the corrupt addresses.
* This is useful to check the correctness of the M100 D and the M100 F commands.
*/
inline void corrupt_free_memory(char *start_free_memory, const uint32_t size) {
inline void corrupt_free_memory(char *start_free_memory, const uintptr_t size) {
start_free_memory += 8;
const uint32_t near_top = top_of_stack() - start_free_memory - 250, // -250 to avoid interrupt activity that's altered the stack.
j = near_top / (size + 1);
SERIAL_ECHOLNPGM("Corrupting free memory block.\n");
SERIAL_ECHOLNPGM("Corrupting free memory block.");
for (uint32_t i = 1; i <= size; i++) {
char * const addr = start_free_memory + i * j;
*addr = i;
SERIAL_ECHOPAIR("\nCorrupting address: ", hex_address(addr));
SERIAL_ECHOPGM("\nCorrupting address: ", hex_address(addr));
}
SERIAL_EOL();
}
@@ -317,8 +318,8 @@ inline void init_free_memory(char *start_free_memory, int32_t size) {
return;
}
start_free_memory += 8; // move a few bytes away from the heap just because we don't want
// to be altering memory that close to it.
start_free_memory += 8; // move a few bytes away from the heap just because we
// don't want to be altering memory that close to it.
memset(start_free_memory, TEST_BYTE, size);
SERIAL_ECHO(size);
@@ -326,8 +327,8 @@ inline void init_free_memory(char *start_free_memory, int32_t size) {
for (int32_t i = 0; i < size; i++) {
if (start_free_memory[i] != TEST_BYTE) {
SERIAL_ECHOPAIR("? address : ", hex_address(start_free_memory + i));
SERIAL_ECHOLNPAIR("=", hex_byte(start_free_memory[i]));
SERIAL_ECHOPGM("? address : ", hex_address(start_free_memory + i));
SERIAL_ECHOLNPGM("=", hex_byte(start_free_memory[i]));
SERIAL_EOL();
}
}
@@ -337,16 +338,16 @@ inline void init_free_memory(char *start_free_memory, int32_t size) {
* M100: Free Memory Check
*/
void GcodeSuite::M100() {
char *sp = top_of_stack();
if (!free_memory_end) free_memory_end = sp - MEMORY_END_CORRECTION;
SERIAL_ECHOPAIR("\nbss_end : ", hex_address(end_bss));
if (heaplimit) SERIAL_ECHOPAIR("\n__heaplimit : ", hex_address(heaplimit));
SERIAL_ECHOPAIR("\nfree_memory_start : ", hex_address(free_memory_start));
if (stacklimit) SERIAL_ECHOPAIR("\n__stacklimit : ", hex_address(stacklimit));
SERIAL_ECHOPAIR("\nfree_memory_end : ", hex_address(free_memory_end));
if (MEMORY_END_CORRECTION) SERIAL_ECHOPAIR("\nMEMORY_END_CORRECTION: ", MEMORY_END_CORRECTION);
SERIAL_ECHOLNPAIR("\nStack Pointer : ", hex_address(sp));
SERIAL_ECHOPGM("\nbss_end : ", hex_address(end_bss));
if (heaplimit) SERIAL_ECHOPGM("\n__heaplimit : ", hex_address(heaplimit));
SERIAL_ECHOPGM("\nfree_memory_start : ", hex_address(free_memory_start));
if (stacklimit) SERIAL_ECHOPGM("\n__stacklimit : ", hex_address(stacklimit));
SERIAL_ECHOPGM("\nfree_memory_end : ", hex_address(free_memory_end));
if (MEMORY_END_CORRECTION)
SERIAL_ECHOPGM("\nMEMORY_END_CORRECTION : ", MEMORY_END_CORRECTION);
SERIAL_ECHOLNPGM("\nStack Pointer : ", hex_address(sp));
// Always init on the first invocation of M100
static bool m100_not_initialized = true;
@@ -364,10 +365,8 @@ void GcodeSuite::M100() {
return free_memory_pool_report(free_memory_start, free_memory_end - free_memory_start);
#if ENABLED(M100_FREE_MEMORY_CORRUPTOR)
if (parser.seen('C'))
return corrupt_free_memory(free_memory_start, parser.value_int());
#endif
}

8
Marlin/src/gcode/calibrate/M12.cpp Executable file → Normal file
View File

@@ -16,9 +16,10 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfigPre.h"
#if ENABLED(EXTERNAL_CLOSED_LOOP_CONTROLLER)
@@ -28,9 +29,12 @@
#include "../../feature/closedloop.h"
void GcodeSuite::M12() {
planner.synchronize();
if (parser.seenval('S'))
set_closedloop(parser.value_int()); // Force a CLC set
closedloop.set(parser.value_int()); // Force a CLC set
}
#endif

50
Marlin/src/gcode/calibrate/M425.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -46,8 +46,22 @@
void GcodeSuite::M425() {
bool noArgs = true;
LOOP_XYZ(a) {
if (CAN_CALIBRATE(a) && parser.seen(XYZ_CHAR(a))) {
auto axis_can_calibrate = [](const uint8_t a) {
switch (a) {
default: return false;
LINEAR_AXIS_CODE(
case X_AXIS: return AXIS_CAN_CALIBRATE(X),
case Y_AXIS: return AXIS_CAN_CALIBRATE(Y),
case Z_AXIS: return AXIS_CAN_CALIBRATE(Z),
case I_AXIS: return AXIS_CAN_CALIBRATE(I),
case J_AXIS: return AXIS_CAN_CALIBRATE(J),
case K_AXIS: return AXIS_CAN_CALIBRATE(K),
);
}
};
LOOP_LINEAR_AXES(a) {
if (axis_can_calibrate(a) && parser.seen(AXIS_CHAR(a))) {
planner.synchronize();
backlash.distance_mm[a] = parser.has_value() ? parser.value_linear_units() : backlash.get_measurement(AxisEnum(a));
noArgs = false;
@@ -72,23 +86,23 @@ void GcodeSuite::M425() {
SERIAL_ECHOPGM("Backlash Correction ");
if (!backlash.correction) SERIAL_ECHOPGM("in");
SERIAL_ECHOLNPGM("active:");
SERIAL_ECHOLNPAIR(" Correction Amount/Fade-out: F", backlash.get_correction(), " (F1.0 = full, F0.0 = none)");
SERIAL_ECHOLNPGM(" Correction Amount/Fade-out: F", backlash.get_correction(), " (F1.0 = full, F0.0 = none)");
SERIAL_ECHOPGM(" Backlash Distance (mm): ");
LOOP_XYZ(a) if (CAN_CALIBRATE(a)) {
SERIAL_CHAR(' ', XYZ_CHAR(a));
LOOP_LINEAR_AXES(a) if (axis_can_calibrate(a)) {
SERIAL_CHAR(' ', AXIS_CHAR(a));
SERIAL_ECHO(backlash.distance_mm[a]);
SERIAL_EOL();
}
#ifdef BACKLASH_SMOOTHING_MM
SERIAL_ECHOLNPAIR(" Smoothing (mm): S", backlash.smoothing_mm);
SERIAL_ECHOLNPGM(" Smoothing (mm): S", backlash.smoothing_mm);
#endif
#if ENABLED(MEASURE_BACKLASH_WHEN_PROBING)
SERIAL_ECHOPGM(" Average measured backlash (mm):");
if (backlash.has_any_measurement()) {
LOOP_XYZ(a) if (CAN_CALIBRATE(a) && backlash.has_measurement(AxisEnum(a))) {
SERIAL_CHAR(' ', XYZ_CHAR(a));
LOOP_LINEAR_AXES(a) if (axis_can_calibrate(a) && backlash.has_measurement(AxisEnum(a))) {
SERIAL_CHAR(' ', AXIS_CHAR(a));
SERIAL_ECHO(backlash.get_measurement(AxisEnum(a)));
}
}
@@ -99,4 +113,22 @@ void GcodeSuite::M425() {
}
}
void GcodeSuite::M425_report(const bool forReplay/*=true*/) {
report_heading_etc(forReplay, PSTR(STR_BACKLASH_COMPENSATION));
SERIAL_ECHOLNPGM_P(
PSTR(" M425 F"), backlash.get_correction()
#ifdef BACKLASH_SMOOTHING_MM
, PSTR(" S"), LINEAR_UNIT(backlash.smoothing_mm)
#endif
, LIST_N(DOUBLE(LINEAR_AXES),
SP_X_STR, LINEAR_UNIT(backlash.distance_mm.x),
SP_Y_STR, LINEAR_UNIT(backlash.distance_mm.y),
SP_Z_STR, LINEAR_UNIT(backlash.distance_mm.z),
SP_I_STR, LINEAR_UNIT(backlash.distance_mm.i),
SP_J_STR, LINEAR_UNIT(backlash.distance_mm.j),
SP_K_STR, LINEAR_UNIT(backlash.distance_mm.k)
)
);
}
#endif // BACKLASH_GCODE

190
Marlin/src/gcode/calibrate/M48.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -27,13 +27,10 @@
#include "../gcode.h"
#include "../../module/motion.h"
#include "../../module/probe.h"
#include "../../lcd/marlinui.h"
#include "../../feature/bedlevel/bedlevel.h"
#if HAS_SPI_LCD
#include "../../lcd/ultralcd.h"
#endif
#if HAS_LEVELING
#include "../../module/planner.h"
#endif
@@ -54,11 +51,9 @@
* This function requires the machine to be homed before invocation.
*/
extern const char SP_Y_STR[];
void GcodeSuite::M48() {
if (axis_unhomed_error()) return;
if (homing_needed_error()) return;
const int8_t verbose_level = parser.byteval('V', 1);
if (!WITHIN(verbose_level, 0, 4)) {
@@ -77,61 +72,85 @@ void GcodeSuite::M48() {
const ProbePtRaise raise_after = parser.boolval('E') ? PROBE_PT_STOW : PROBE_PT_RAISE;
xy_float_t next_pos = current_position;
const xy_pos_t probe_pos = {
parser.linearval('X', next_pos.x + probe.offset_xy.x), // If no X use the probe's current X position
parser.linearval('Y', next_pos.y + probe.offset_xy.y) // If no Y, ditto
// Test at the current position by default, overridden by X and Y
const xy_pos_t test_position = {
parser.linearval('X', current_position.x + probe.offset_xy.x), // If no X use the probe's current X position
parser.linearval('Y', current_position.y + probe.offset_xy.y) // If no Y, ditto
};
if (!probe.can_reach(probe_pos)) {
if (!probe.can_reach(test_position)) {
ui.set_status_P(GET_TEXT(MSG_M48_OUT_OF_BOUNDS), 99);
SERIAL_ECHOLNPGM("? (X,Y) out of bounds.");
return;
}
// Get the number of leg moves per test-point
bool seen_L = parser.seen('L');
uint8_t n_legs = seen_L ? parser.value_byte() : 0;
if (n_legs > 15) {
SERIAL_ECHOLNPGM("?Number of legs in movement not plausible (0-15).");
SERIAL_ECHOLNPGM("?Legs of movement implausible (0-15).");
return;
}
if (n_legs == 1) n_legs = 2;
// Schizoid motion as an optional stress-test
const bool schizoid_flag = parser.boolval('S');
if (schizoid_flag && !seen_L) n_legs = 7;
/**
* Now get everything to the specified probe point So we can safely do a
* probe to get us close to the bed. If the Z-Axis is far from the bed,
* we don't want to use that as a starting point for each probe.
*/
if (verbose_level > 2)
SERIAL_ECHOLNPGM("Positioning the probe...");
// Disable bed level correction in M48 because we want the raw data when we probe
// Always disable Bed Level correction before probing...
#if HAS_LEVELING
const bool was_enabled = planner.leveling_active;
set_bed_leveling_enabled(false);
#endif
// Work with reasonable feedrates
remember_feedrate_scaling_off();
float mean = 0.0, sigma = 0.0, min = 99999.9, max = -99999.9, sample_set[n_samples];
// Working variables
float mean = 0.0, // The average of all points so far, used to calculate deviation
sigma = 0.0, // Standard deviation of all points so far
min = 99999.9, // Smallest value sampled so far
max = -99999.9, // Largest value sampled so far
sample_set[n_samples]; // Storage for sampled values
auto dev_report = [](const bool verbose, const_float_t mean, const_float_t sigma, const_float_t min, const_float_t max, const bool final=false) {
if (verbose) {
SERIAL_ECHOPAIR_F("Mean: ", mean, 6);
if (!final) SERIAL_ECHOPAIR_F(" Sigma: ", sigma, 6);
SERIAL_ECHOPAIR_F(" Min: ", min, 3);
SERIAL_ECHOPAIR_F(" Max: ", max, 3);
SERIAL_ECHOPAIR_F(" Range: ", max-min, 3);
if (final) SERIAL_EOL();
}
if (final) {
SERIAL_ECHOLNPAIR_F("Standard Deviation: ", sigma, 6);
SERIAL_EOL();
}
};
// Move to the first point, deploy, and probe
const float t = probe.probe_at_point(probe_pos, raise_after, verbose_level);
const float t = probe.probe_at_point(test_position, raise_after, verbose_level);
bool probing_good = !isnan(t);
if (probing_good) {
randomSeed(millis());
float sample_sum = 0.0;
LOOP_L_N(n, n_samples) {
#if HAS_SPI_LCD
#if HAS_STATUS_MESSAGE
// Display M48 progress in the status bar
ui.status_printf_P(0, PSTR(S_FMT ": %d/%d"), GET_TEXT(MSG_M48_POINT), int(n + 1), int(n_samples));
#endif
// When there are "legs" of movement move around the point before probing
if (n_legs) {
// Pick a random direction, starting angle, and radius
const int dir = (random(0, 10) > 5.0) ? -1 : 1; // clockwise or counter clockwise
float angle = random(0, 360);
const float radius = random(
@@ -142,97 +161,91 @@ void GcodeSuite::M48() {
int(5), int(0.125 * _MIN(X_BED_SIZE, Y_BED_SIZE))
#endif
);
if (verbose_level > 3) {
SERIAL_ECHOPAIR("Start radius:", radius, " angle:", angle, " dir:");
SERIAL_ECHOPGM("Start radius:", radius, " angle:", angle, " dir:");
if (dir > 0) SERIAL_CHAR('C');
SERIAL_ECHOLNPGM("CW");
}
// Move from leg to leg in rapid succession
LOOP_L_N(l, n_legs - 1) {
float delta_angle;
// Move some distance around the perimeter
float delta_angle;
if (schizoid_flag) {
// The points of a 5 point star are 72 degrees apart. We need to
// skip a point and go to the next one on the star.
// The points of a 5 point star are 72 degrees apart.
// Skip a point and go to the next one on the star.
delta_angle = dir * 2.0 * 72.0;
}
else {
// If we do this line, we are just trying to move further
// around the circle.
delta_angle = dir * (float) random(25, 45);
// Just move further along the perimeter.
delta_angle = dir * (float)random(25, 45);
}
angle += delta_angle;
while (angle > 360.0) angle -= 360.0; // We probably do not need to keep the angle between 0 and 2*PI, but the
// Arduino documentation says the trig functions should not be given values
while (angle < 0.0) angle += 360.0; // outside of this range. It looks like they behave correctly with
// numbers outside of the range, but just to be safe we clamp them.
const xy_pos_t noz_pos = probe_pos - probe.offset_xy;
next_pos.set(noz_pos.x + cos(RADIANS(angle)) * radius,
noz_pos.y + sin(RADIANS(angle)) * radius);
// Trig functions work without clamping, but just to be safe...
while (angle > 360.0) angle -= 360.0;
while (angle < 0.0) angle += 360.0;
#if DISABLED(DELTA)
LIMIT(next_pos.x, X_MIN_POS, X_MAX_POS);
LIMIT(next_pos.y, Y_MIN_POS, Y_MAX_POS);
#else
// If we have gone out too far, we can do a simple fix and scale the numbers
// back in closer to the origin.
// Choose the next position as an offset to chosen test position
const xy_pos_t noz_pos = test_position - probe.offset_xy;
xy_pos_t next_pos = {
noz_pos.x + float(cos(RADIANS(angle))) * radius,
noz_pos.y + float(sin(RADIANS(angle))) * radius
};
#if ENABLED(DELTA)
// If the probe can't reach the point on a round bed...
// Simply scale the numbers to bring them closer to origin.
while (!probe.can_reach(next_pos)) {
next_pos *= 0.8f;
if (verbose_level > 3)
SERIAL_ECHOLNPAIR_P(PSTR("Moving inward: X"), next_pos.x, SP_Y_STR, next_pos.y);
SERIAL_ECHOLNPGM_P(PSTR("Moving inward: X"), next_pos.x, SP_Y_STR, next_pos.y);
}
#elif HAS_ENDSTOPS
// For a rectangular bed just keep the probe in bounds
LIMIT(next_pos.x, X_MIN_POS, X_MAX_POS);
LIMIT(next_pos.y, Y_MIN_POS, Y_MAX_POS);
#endif
if (verbose_level > 3)
SERIAL_ECHOLNPAIR_P(PSTR("Going to: X"), next_pos.x, SP_Y_STR, next_pos.y);
SERIAL_ECHOLNPGM_P(PSTR("Going to: X"), next_pos.x, SP_Y_STR, next_pos.y);
do_blocking_move_to_xy(next_pos);
} // n_legs loop
} // n_legs
// Probe a single point
sample_set[n] = probe.probe_at_point(probe_pos, raise_after, 0);
const float pz = probe.probe_at_point(test_position, raise_after, 0);
// Break the loop if the probe fails
probing_good = !isnan(sample_set[n]);
probing_good = !isnan(pz);
if (!probing_good) break;
/**
* Get the current mean for the data points we have so far
*/
float sum = 0.0;
LOOP_LE_N(j, n) sum += sample_set[j];
mean = sum / (n + 1);
// Store the new sample
sample_set[n] = pz;
NOMORE(min, sample_set[n]);
NOLESS(max, sample_set[n]);
// Keep track of the largest and smallest samples
NOMORE(min, pz);
NOLESS(max, pz);
/**
* Now, use that mean to calculate the standard deviation for the
* data points we have so far
*/
sum = 0.0;
LOOP_LE_N(j, n)
sum += sq(sample_set[j] - mean);
// Get the mean value of all samples thus far
sample_sum += pz;
mean = sample_sum / (n + 1);
sigma = SQRT(sum / (n + 1));
if (verbose_level > 0) {
if (verbose_level > 1) {
SERIAL_ECHO(n + 1);
SERIAL_ECHOPAIR(" of ", int(n_samples));
SERIAL_ECHOPAIR_F(": z: ", sample_set[n], 3);
if (verbose_level > 2) {
SERIAL_ECHOPAIR_F(" mean: ", mean, 4);
SERIAL_ECHOPAIR_F(" sigma: ", sigma, 6);
SERIAL_ECHOPAIR_F(" min: ", min, 3);
SERIAL_ECHOPAIR_F(" max: ", max, 3);
SERIAL_ECHOPAIR_F(" range: ", max-min, 3);
}
SERIAL_EOL();
}
// Calculate the standard deviation so far.
// The value after the last sample will be the final output.
float dev_sum = 0.0;
LOOP_LE_N(j, n) dev_sum += sq(sample_set[j] - mean);
sigma = SQRT(dev_sum / (n + 1));
if (verbose_level > 1) {
SERIAL_ECHO(n + 1);
SERIAL_ECHOPGM(" of ", n_samples);
SERIAL_ECHOPAIR_F(": z: ", pz, 3);
SERIAL_CHAR(' ');
dev_report(verbose_level > 2, mean, sigma, min, max);
SERIAL_EOL();
}
} // n_samples loop
@@ -242,18 +255,9 @@ void GcodeSuite::M48() {
if (probing_good) {
SERIAL_ECHOLNPGM("Finished!");
dev_report(verbose_level > 0, mean, sigma, min, max, true);
if (verbose_level > 0) {
SERIAL_ECHOPAIR_F("Mean: ", mean, 6);
SERIAL_ECHOPAIR_F(" Min: ", min, 3);
SERIAL_ECHOPAIR_F(" Max: ", max, 3);
SERIAL_ECHOLNPAIR_F(" Range: ", max-min, 3);
}
SERIAL_ECHOLNPAIR_F("Standard Deviation: ", sigma, 6);
SERIAL_EOL();
#if HAS_SPI_LCD
#if HAS_STATUS_MESSAGE
// Display M48 results in the status bar
char sigma_str[8];
ui.status_printf_P(0, PSTR(S_FMT ": %s"), GET_TEXT(MSG_M48_DEVIATION), dtostrf(sigma, 2, 6, sigma_str));
@@ -263,9 +267,7 @@ void GcodeSuite::M48() {
restore_feedrate_and_scaling();
// Re-enable bed level correction if it had been on
#if HAS_LEVELING
set_bed_leveling_enabled(was_enabled);
#endif
TERN_(HAS_LEVELING, set_bed_leveling_enabled(was_enabled));
report_current_position();
}

85
Marlin/src/gcode/calibrate/M665.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -30,6 +30,7 @@
#if ENABLED(DELTA)
#include "../../module/delta.h"
/**
* M665: Set delta configurations
*
@@ -38,20 +39,44 @@
* R = delta radius
* S = segments per second
* X = Alpha (Tower 1) angle trim
* Y = Beta (Tower 2) angle trim
* Y = Beta (Tower 2) angle trim
* Z = Gamma (Tower 3) angle trim
* A = Alpha (Tower 1) diagonal rod trim
* B = Beta (Tower 2) diagonal rod trim
* C = Gamma (Tower 3) diagonal rod trim
*/
void GcodeSuite::M665() {
if (parser.seen('H')) delta_height = parser.value_linear_units();
if (parser.seen('L')) delta_diagonal_rod = parser.value_linear_units();
if (parser.seen('R')) delta_radius = parser.value_linear_units();
if (parser.seen('S')) delta_segments_per_second = parser.value_float();
if (parser.seen('X')) delta_tower_angle_trim.a = parser.value_float();
if (parser.seen('Y')) delta_tower_angle_trim.b = parser.value_float();
if (parser.seen('Z')) delta_tower_angle_trim.c = parser.value_float();
if (!parser.seen_any()) return M665_report();
if (parser.seenval('H')) delta_height = parser.value_linear_units();
if (parser.seenval('L')) delta_diagonal_rod = parser.value_linear_units();
if (parser.seenval('R')) delta_radius = parser.value_linear_units();
if (parser.seenval('S')) segments_per_second = parser.value_float();
if (parser.seenval('X')) delta_tower_angle_trim.a = parser.value_float();
if (parser.seenval('Y')) delta_tower_angle_trim.b = parser.value_float();
if (parser.seenval('Z')) delta_tower_angle_trim.c = parser.value_float();
if (parser.seenval('A')) delta_diagonal_rod_trim.a = parser.value_float();
if (parser.seenval('B')) delta_diagonal_rod_trim.b = parser.value_float();
if (parser.seenval('C')) delta_diagonal_rod_trim.c = parser.value_float();
recalc_delta_settings();
}
void GcodeSuite::M665_report(const bool forReplay/*=true*/) {
report_heading_etc(forReplay, PSTR(STR_DELTA_SETTINGS));
SERIAL_ECHOLNPGM_P(
PSTR(" M665 L"), LINEAR_UNIT(delta_diagonal_rod)
, PSTR(" R"), LINEAR_UNIT(delta_radius)
, PSTR(" H"), LINEAR_UNIT(delta_height)
, PSTR(" S"), segments_per_second
, SP_X_STR, LINEAR_UNIT(delta_tower_angle_trim.a)
, SP_Y_STR, LINEAR_UNIT(delta_tower_angle_trim.b)
, SP_Z_STR, LINEAR_UNIT(delta_tower_angle_trim.c)
, PSTR(" A"), LINEAR_UNIT(delta_diagonal_rod_trim.a)
, PSTR(" B"), LINEAR_UNIT(delta_diagonal_rod_trim.b)
, PSTR(" C"), LINEAR_UNIT(delta_diagonal_rod_trim.c)
);
}
#elif IS_SCARA
#include "../../module/scara.h"
@@ -62,6 +87,9 @@
* Parameters:
*
* S[segments-per-second] - Segments-per-second
*
* Without NO_WORKSPACE_OFFSETS:
*
* P[theta-psi-offset] - Theta-Psi offset, added to the shoulder (A/X) angle
* T[theta-offset] - Theta offset, added to the elbow (B/Y) angle
* Z[z-offset] - Z offset, added to Z
@@ -70,7 +98,9 @@
* B, T, and Y are all aliases for the elbow angle
*/
void GcodeSuite::M665() {
if (parser.seenval('S')) delta_segments_per_second = parser.value_float();
if (!parser.seen_any()) return M665_report();
if (parser.seenval('S')) segments_per_second = parser.value_float();
#if HAS_SCARA_OFFSET
@@ -101,6 +131,41 @@
#endif // HAS_SCARA_OFFSET
}
void GcodeSuite::M665_report(const bool forReplay/*=true*/) {
report_heading_etc(forReplay, PSTR(STR_SCARA_SETTINGS " (" STR_S_SEG_PER_SEC TERN_(HAS_SCARA_OFFSET, " " STR_SCARA_P_T_Z) ")"));
SERIAL_ECHOLNPGM_P(
PSTR(" M665 S"), segments_per_second
#if HAS_SCARA_OFFSET
, SP_P_STR, scara_home_offset.a
, SP_T_STR, scara_home_offset.b
, SP_Z_STR, LINEAR_UNIT(scara_home_offset.z)
#endif
);
}
#elif ENABLED(POLARGRAPH)
#include "../../module/polargraph.h"
/**
* M665: Set POLARGRAPH settings
*
* Parameters:
*
* S[segments-per-second] - Segments-per-second
*/
void GcodeSuite::M665() {
if (parser.seenval('S'))
segments_per_second = parser.value_float();
else
M665_report();
}
void GcodeSuite::M665_report(const bool forReplay/*=true*/) {
report_heading_etc(forReplay, PSTR(STR_POLARGRAPH_SETTINGS " (" STR_S_SEG_PER_SEC ")"));
SERIAL_ECHOLNPGM(" M665 S", segments_per_second);
}
#endif
#endif // IS_KINEMATIC

95
Marlin/src/gcode/calibrate/M666.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -27,31 +27,49 @@
#include "../gcode.h"
#if ENABLED(DELTA)
#include "../../module/delta.h"
#include "../../module/motion.h"
#else
#include "../../module/endstops.h"
#endif
#define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
#include "../../core/debug_out.h"
#define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
#include "../../core/debug_out.h"
#if ENABLED(DELTA)
/**
* M666: Set delta endstop adjustment
*/
void GcodeSuite::M666() {
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM(">>> M666");
LOOP_XYZ(i) {
if (parser.seen(XYZ_CHAR(i))) {
DEBUG_SECTION(log_M666, "M666", DEBUGGING(LEVELING));
bool is_err = false, is_set = false;
LOOP_LINEAR_AXES(i) {
if (parser.seen(AXIS_CHAR(i))) {
is_set = true;
const float v = parser.value_linear_units();
if (v * Z_HOME_DIR <= 0) delta_endstop_adj[i] = v;
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("delta_endstop_adj[", XYZ_CHAR(i), "] = ", delta_endstop_adj[i]);
if (v > 0)
is_err = true;
else {
delta_endstop_adj[i] = v;
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("delta_endstop_adj[", AS_CHAR(AXIS_CHAR(i)), "] = ", v);
}
}
}
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("<<< M666");
if (is_err) SERIAL_ECHOLNPGM("?M666 offsets must be <= 0");
if (!is_set) M666_report();
}
#elif HAS_EXTRA_ENDSTOPS
void GcodeSuite::M666_report(const bool forReplay/*=true*/) {
report_heading_etc(forReplay, PSTR(STR_ENDSTOP_ADJUSTMENT));
SERIAL_ECHOLNPGM_P(
PSTR(" M666 X"), LINEAR_UNIT(delta_endstop_adj.a)
, SP_Y_STR, LINEAR_UNIT(delta_endstop_adj.b)
, SP_Z_STR, LINEAR_UNIT(delta_endstop_adj.c)
);
}
#include "../../module/endstops.h"
#else
/**
* M666: Set Dual Endstops offsets for X, Y, and/or Z.
@@ -64,6 +82,8 @@
* Set All: M666 Z<offset>
*/
void GcodeSuite::M666() {
if (!parser.seen_any()) return M666_report();
#if ENABLED(X_DUAL_ENDSTOPS)
if (parser.seenval('X')) endstops.x2_endstop_adj = parser.value_linear_units();
#endif
@@ -72,33 +92,40 @@
#endif
#if ENABLED(Z_MULTI_ENDSTOPS)
if (parser.seenval('Z')) {
#if NUM_Z_STEPPER_DRIVERS >= 3
const float z_adj = parser.value_linear_units();
const int ind = parser.intval('S');
if (!ind || ind == 2) endstops.z2_endstop_adj = z_adj;
if (!ind || ind == 3) endstops.z3_endstop_adj = z_adj;
#if NUM_Z_STEPPER_DRIVERS >= 4
if (!ind || ind == 4) endstops.z4_endstop_adj = z_adj;
#endif
const float z_adj = parser.value_linear_units();
#if NUM_Z_STEPPER_DRIVERS == 2
endstops.z2_endstop_adj = z_adj;
#else
endstops.z2_endstop_adj = parser.value_linear_units();
const int ind = parser.intval('S');
#define _SET_ZADJ(N) if (!ind || ind == N) endstops.z##N##_endstop_adj = z_adj;
REPEAT_S(2, INCREMENT(NUM_Z_STEPPER_DRIVERS), _SET_ZADJ)
#endif
}
#endif
if (!parser.seen("XYZ")) {
SERIAL_ECHOPGM("Dual Endstop Adjustment (mm): ");
#if ENABLED(X_DUAL_ENDSTOPS)
SERIAL_ECHOPAIR(" X2:", endstops.x2_endstop_adj);
}
void GcodeSuite::M666_report(const bool forReplay/*=true*/) {
report_heading_etc(forReplay, PSTR(STR_ENDSTOP_ADJUSTMENT));
SERIAL_ECHOPGM(" M666");
#if ENABLED(X_DUAL_ENDSTOPS)
SERIAL_ECHOLNPGM_P(SP_X_STR, LINEAR_UNIT(endstops.x2_endstop_adj));
#endif
#if ENABLED(Y_DUAL_ENDSTOPS)
SERIAL_ECHOLNPGM_P(SP_Y_STR, LINEAR_UNIT(endstops.y2_endstop_adj));
#endif
#if ENABLED(Z_MULTI_ENDSTOPS)
#if NUM_Z_STEPPER_DRIVERS >= 3
SERIAL_ECHOPGM(" S2 Z", LINEAR_UNIT(endstops.z3_endstop_adj));
report_echo_start(forReplay);
SERIAL_ECHOPGM(" M666 S3 Z", LINEAR_UNIT(endstops.z3_endstop_adj));
#if NUM_Z_STEPPER_DRIVERS >= 4
report_echo_start(forReplay);
SERIAL_ECHOPGM(" M666 S4 Z", LINEAR_UNIT(endstops.z4_endstop_adj));
#endif
#else
SERIAL_ECHOLNPGM_P(SP_Z_STR, LINEAR_UNIT(endstops.z2_endstop_adj));
#endif
#if ENABLED(Y_DUAL_ENDSTOPS)
SERIAL_ECHOPAIR(" Y2:", endstops.y2_endstop_adj);
#endif
#if ENABLED(Z_MULTI_ENDSTOPS)
#define _ECHO_ZADJ(N) SERIAL_ECHOPAIR(" Z" STRINGIFY(N) ":", endstops.z##N##_endstop_adj);
REPEAT_S(2, INCREMENT(NUM_Z_STEPPER_DRIVERS), _ECHO_ZADJ)
#endif
SERIAL_EOL();
}
#endif
}
#endif // HAS_EXTRA_ENDSTOPS

38
Marlin/src/gcode/calibrate/M852.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -36,10 +36,11 @@
* K[yz_factor] - New YZ skew factor
*/
void GcodeSuite::M852() {
uint8_t ijk = 0, badval = 0, setval = 0;
if (!parser.seen("SIJK")) return M852_report();
if (parser.seen('I') || parser.seen('S')) {
++ijk;
uint8_t badval = 0, setval = 0;
if (parser.seenval('I') || parser.seenval('S')) {
const float value = parser.value_linear_units();
if (WITHIN(value, SKEW_FACTOR_MIN, SKEW_FACTOR_MAX)) {
if (planner.skew_factor.xy != value) {
@@ -53,8 +54,7 @@ void GcodeSuite::M852() {
#if ENABLED(SKEW_CORRECTION_FOR_Z)
if (parser.seen('J')) {
++ijk;
if (parser.seenval('J')) {
const float value = parser.value_linear_units();
if (WITHIN(value, SKEW_FACTOR_MIN, SKEW_FACTOR_MAX)) {
if (planner.skew_factor.xz != value) {
@@ -66,8 +66,7 @@ void GcodeSuite::M852() {
++badval;
}
if (parser.seen('K')) {
++ijk;
if (parser.seenval('K')) {
const float value = parser.value_linear_units();
if (WITHIN(value, SKEW_FACTOR_MIN, SKEW_FACTOR_MAX)) {
if (planner.skew_factor.yz != value) {
@@ -86,21 +85,22 @@ void GcodeSuite::M852() {
// When skew is changed the current position changes
if (setval) {
set_current_from_steppers_for_axis(ALL_AXES);
set_current_from_steppers_for_axis(ALL_AXES_ENUM);
sync_plan_position();
report_current_position();
}
}
if (!ijk) {
SERIAL_ECHO_START();
serialprintPGM(GET_TEXT(MSG_SKEW_FACTOR));
SERIAL_ECHOPAIR_F(" XY: ", planner.skew_factor.xy, 6);
#if ENABLED(SKEW_CORRECTION_FOR_Z)
SERIAL_ECHOPAIR_F(" XZ: ", planner.skew_factor.xz, 6);
SERIAL_ECHOPAIR_F(" YZ: ", planner.skew_factor.yz, 6);
#endif
SERIAL_EOL();
}
void GcodeSuite::M852_report(const bool forReplay/*=true*/) {
report_heading_etc(forReplay, PSTR(STR_SKEW_FACTOR));
SERIAL_ECHOPAIR_F(" M851 I", planner.skew_factor.xy, 6);
#if ENABLED(SKEW_CORRECTION_FOR_Z)
SERIAL_ECHOPAIR_F(" J", planner.skew_factor.xz, 6);
SERIAL_ECHOPAIR_F(" K", planner.skew_factor.yz, 6);
SERIAL_ECHOLNPGM(" ; XY, XZ, YZ");
#else
SERIAL_ECHOLNPGM(" ; XY");
#endif
}
#endif // SKEW_CORRECTION_GCODE

247
Marlin/src/gcode/config/M200-M205.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -30,24 +30,81 @@
* M200: Set filament diameter and set E axis units to cubic units
*
* T<extruder> - Optional extruder number. Current extruder if omitted.
* D<linear> - Diameter of the filament. Use "D0" to switch back to linear units on the E axis.
* D<linear> - Set filament diameter and enable. D0 disables volumetric.
* S<bool> - Turn volumetric ON or OFF.
*
* With VOLUMETRIC_EXTRUDER_LIMIT:
*
* L<float> - Volumetric extruder limit (in mm^3/sec). L0 disables the limit.
*/
void GcodeSuite::M200() {
if (!parser.seen("DST" TERN_(VOLUMETRIC_EXTRUDER_LIMIT, "L")))
return M200_report();
const int8_t target_extruder = get_target_extruder_from_command();
if (target_extruder < 0) return;
if (parser.seen('D')) {
// setting any extruder filament size disables volumetric on the assumption that
// slicers either generate in extruder values as cubic mm or as as filament feeds
// for all extruders
bool vol_enable = parser.volumetric_enabled,
can_enable = true;
if (parser.seenval('D')) {
const float dval = parser.value_linear_units();
if ( (parser.volumetric_enabled = (dval != 0)) )
if (dval) { // Set filament size for volumetric calculation
planner.set_filament_size(target_extruder, dval);
vol_enable = true; // Dn = enable for compatibility
}
else
can_enable = false; // D0 = disable for compatibility
}
// Enable or disable with S1 / S0
parser.volumetric_enabled = can_enable && parser.boolval('S', vol_enable);
#if ENABLED(VOLUMETRIC_EXTRUDER_LIMIT)
if (parser.seenval('L')) {
// Set volumetric limit (in mm^3/sec)
const float lval = parser.value_float();
if (WITHIN(lval, 0, 20))
planner.set_volumetric_extruder_limit(target_extruder, lval);
else
SERIAL_ECHOLNPGM("?L value out of range (0-20).");
}
#endif
planner.calculate_volumetric_multipliers();
}
void GcodeSuite::M200_report(const bool forReplay/*=true*/) {
if (!forReplay) {
report_heading(forReplay, PSTR(STR_FILAMENT_SETTINGS), false);
if (!parser.volumetric_enabled) SERIAL_ECHOPGM(" (Disabled):");
SERIAL_EOL();
report_echo_start(forReplay);
}
#if EXTRUDERS == 1
{
SERIAL_ECHOLNPGM(
" M200 S", parser.volumetric_enabled, " D", LINEAR_UNIT(planner.filament_size[0])
#if ENABLED(VOLUMETRIC_EXTRUDER_LIMIT)
, " L", LINEAR_UNIT(planner.volumetric_extruder_limit[0])
#endif
);
}
#else
SERIAL_ECHOLNPGM(" M200 S", parser.volumetric_enabled);
LOOP_L_N(i, EXTRUDERS) {
report_echo_start(forReplay);
SERIAL_ECHOLNPGM(
" M200 T", i, " D", LINEAR_UNIT(planner.filament_size[i])
#if ENABLED(VOLUMETRIC_EXTRUDER_LIMIT)
, " L", LINEAR_UNIT(planner.volumetric_extruder_limit[i])
#endif
);
}
#endif
}
#endif // !NO_VOLUMETRICS
/**
@@ -56,35 +113,96 @@
* With multiple extruders use T to specify which one.
*/
void GcodeSuite::M201() {
if (!parser.seen("T" LOGICAL_AXES_STRING))
return M201_report();
const int8_t target_extruder = get_target_extruder_from_command();
if (target_extruder < 0) return;
LOOP_XYZE(i) {
if (parser.seen(axis_codes[i])) {
const uint8_t a = (i == E_AXIS ? uint8_t(E_AXIS_N(target_extruder)) : i);
#ifdef XY_FREQUENCY_LIMIT
if (parser.seenval('F')) planner.set_frequency_limit(parser.value_byte());
if (parser.seenval('G')) planner.xy_freq_min_speed_factor = constrain(parser.value_float(), 1, 100) / 100;
#endif
LOOP_LOGICAL_AXES(i) {
if (parser.seenval(axis_codes[i])) {
const uint8_t a = TERN(HAS_EXTRUDERS, (i == E_AXIS ? uint8_t(E_AXIS_N(target_extruder)) : i), i);
planner.set_max_acceleration(a, parser.value_axis_units((AxisEnum)a));
}
}
}
void GcodeSuite::M201_report(const bool forReplay/*=true*/) {
report_heading_etc(forReplay, PSTR(STR_MAX_ACCELERATION));
SERIAL_ECHOLNPGM_P(
LIST_N(DOUBLE(LINEAR_AXES),
PSTR(" M201 X"), LINEAR_UNIT(planner.settings.max_acceleration_mm_per_s2[X_AXIS]),
SP_Y_STR, LINEAR_UNIT(planner.settings.max_acceleration_mm_per_s2[Y_AXIS]),
SP_Z_STR, LINEAR_UNIT(planner.settings.max_acceleration_mm_per_s2[Z_AXIS]),
SP_I_STR, LINEAR_UNIT(planner.settings.max_acceleration_mm_per_s2[I_AXIS]),
SP_J_STR, LINEAR_UNIT(planner.settings.max_acceleration_mm_per_s2[J_AXIS]),
SP_K_STR, LINEAR_UNIT(planner.settings.max_acceleration_mm_per_s2[K_AXIS])
)
#if HAS_EXTRUDERS && DISABLED(DISTINCT_E_FACTORS)
, SP_E_STR, VOLUMETRIC_UNIT(planner.settings.max_acceleration_mm_per_s2[E_AXIS])
#endif
);
#if ENABLED(DISTINCT_E_FACTORS)
LOOP_L_N(i, E_STEPPERS) {
report_echo_start(forReplay);
SERIAL_ECHOLNPGM_P(
PSTR(" M201 T"), i
, SP_E_STR, VOLUMETRIC_UNIT(planner.settings.max_acceleration_mm_per_s2[E_AXIS_N(i)])
);
}
#endif
}
/**
* M203: Set maximum feedrate that your machine can sustain (M203 X200 Y200 Z300 E10000) in units/sec
*
* With multiple extruders use T to specify which one.
*/
void GcodeSuite::M203() {
if (!parser.seen("T" LOGICAL_AXES_STRING))
return M203_report();
const int8_t target_extruder = get_target_extruder_from_command();
if (target_extruder < 0) return;
LOOP_XYZE(i)
if (parser.seen(axis_codes[i])) {
const uint8_t a = (i == E_AXIS ? uint8_t(E_AXIS_N(target_extruder)) : i);
LOOP_LOGICAL_AXES(i)
if (parser.seenval(axis_codes[i])) {
const uint8_t a = TERN(HAS_EXTRUDERS, (i == E_AXIS ? uint8_t(E_AXIS_N(target_extruder)) : i), i);
planner.set_max_feedrate(a, parser.value_axis_units((AxisEnum)a));
}
}
void GcodeSuite::M203_report(const bool forReplay/*=true*/) {
report_heading_etc(forReplay, PSTR(STR_MAX_FEEDRATES));
SERIAL_ECHOLNPGM_P(
LIST_N(DOUBLE(LINEAR_AXES),
PSTR(" M203 X"), LINEAR_UNIT(planner.settings.max_feedrate_mm_s[X_AXIS]),
SP_Y_STR, LINEAR_UNIT(planner.settings.max_feedrate_mm_s[Y_AXIS]),
SP_Z_STR, LINEAR_UNIT(planner.settings.max_feedrate_mm_s[Z_AXIS]),
SP_I_STR, LINEAR_UNIT(planner.settings.max_feedrate_mm_s[I_AXIS]),
SP_J_STR, LINEAR_UNIT(planner.settings.max_feedrate_mm_s[J_AXIS]),
SP_K_STR, LINEAR_UNIT(planner.settings.max_feedrate_mm_s[K_AXIS])
)
#if HAS_EXTRUDERS && DISABLED(DISTINCT_E_FACTORS)
, SP_E_STR, VOLUMETRIC_UNIT(planner.settings.max_feedrate_mm_s[E_AXIS])
#endif
);
#if ENABLED(DISTINCT_E_FACTORS)
LOOP_L_N(i, E_STEPPERS) {
SERIAL_ECHO_START();
SERIAL_ECHOLNPGM_P(
PSTR(" M203 T"), i
, SP_E_STR, VOLUMETRIC_UNIT(planner.settings.max_feedrate_mm_s[E_AXIS_N(i)])
);
}
#endif
}
/**
* M204: Set Accelerations in units/sec^2 (M204 P1200 R3000 T3000)
*
@@ -93,11 +211,8 @@ void GcodeSuite::M203() {
* T = Travel (non printing) moves
*/
void GcodeSuite::M204() {
if (!parser.seen("PRST")) {
SERIAL_ECHOPAIR("Acceleration: P", planner.settings.acceleration);
SERIAL_ECHOPAIR(" R", planner.settings.retract_acceleration);
SERIAL_ECHOLNPAIR_P(SP_T_STR, planner.settings.travel_acceleration);
}
if (!parser.seen("PRST"))
return M204_report();
else {
//planner.synchronize();
// 'S' for legacy compatibility. Should NOT BE USED for new development
@@ -108,6 +223,15 @@ void GcodeSuite::M204() {
}
}
void GcodeSuite::M204_report(const bool forReplay/*=true*/) {
report_heading_etc(forReplay, PSTR(STR_ACCELERATION_P_R_T));
SERIAL_ECHOLNPGM_P(
PSTR(" M204 P"), LINEAR_UNIT(planner.settings.acceleration)
, PSTR(" R"), LINEAR_UNIT(planner.settings.retract_acceleration)
, SP_T_STR, LINEAR_UNIT(planner.settings.travel_acceleration)
);
}
/**
* M205: Set Advanced Settings
*
@@ -121,47 +245,72 @@ void GcodeSuite::M204() {
* J = Junction Deviation (mm) (If not using CLASSIC_JERK)
*/
void GcodeSuite::M205() {
#if DISABLED(CLASSIC_JERK)
#define J_PARAM "J"
#else
#define J_PARAM
#endif
#if HAS_CLASSIC_JERK
#define XYZE_PARAM "XYZE"
#else
#define XYZE_PARAM
#endif
if (!parser.seen("BST" J_PARAM XYZE_PARAM)) return;
if (!parser.seen("BST" TERN_(HAS_JUNCTION_DEVIATION, "J") TERN_(HAS_CLASSIC_JERK, "XYZE")))
return M205_report();
//planner.synchronize();
if (parser.seen('B')) planner.settings.min_segment_time_us = parser.value_ulong();
if (parser.seen('S')) planner.settings.min_feedrate_mm_s = parser.value_linear_units();
if (parser.seen('T')) planner.settings.min_travel_feedrate_mm_s = parser.value_linear_units();
#if DISABLED(CLASSIC_JERK)
if (parser.seen('J')) {
if (parser.seenval('B')) planner.settings.min_segment_time_us = parser.value_ulong();
if (parser.seenval('S')) planner.settings.min_feedrate_mm_s = parser.value_linear_units();
if (parser.seenval('T')) planner.settings.min_travel_feedrate_mm_s = parser.value_linear_units();
#if HAS_JUNCTION_DEVIATION
#if HAS_CLASSIC_JERK && (AXIS4_NAME == 'J' || AXIS5_NAME == 'J' || AXIS6_NAME == 'J')
#error "Can't set_max_jerk for 'J' axis because 'J' is used for Junction Deviation."
#endif
if (parser.seenval('J')) {
const float junc_dev = parser.value_linear_units();
if (WITHIN(junc_dev, 0.01f, 0.3f)) {
planner.junction_deviation_mm = junc_dev;
#if ENABLED(LIN_ADVANCE)
planner.recalculate_max_e_jerk();
#endif
TERN_(LIN_ADVANCE, planner.recalculate_max_e_jerk());
}
else
SERIAL_ERROR_MSG("?J out of range (0.01 to 0.3)");
}
#endif
#if HAS_CLASSIC_JERK
if (parser.seen('X')) planner.set_max_jerk(X_AXIS, parser.value_linear_units());
if (parser.seen('Y')) planner.set_max_jerk(Y_AXIS, parser.value_linear_units());
if (parser.seen('Z')) {
planner.set_max_jerk(Z_AXIS, parser.value_linear_units());
#if HAS_MESH && DISABLED(LIMITED_JERK_EDITING)
if (planner.max_jerk.z <= 0.1f)
SERIAL_ECHOLNPGM("WARNING! Low Z Jerk may lead to unwanted pauses.");
#endif
}
#if HAS_CLASSIC_E_JERK
if (parser.seen('E')) planner.set_max_jerk(E_AXIS, parser.value_linear_units());
bool seenZ = false;
LOGICAL_AXIS_CODE(
if (parser.seenval('E')) planner.set_max_jerk(E_AXIS, parser.value_linear_units()),
if (parser.seenval('X')) planner.set_max_jerk(X_AXIS, parser.value_linear_units()),
if (parser.seenval('Y')) planner.set_max_jerk(Y_AXIS, parser.value_linear_units()),
if ((seenZ = parser.seenval('Z'))) planner.set_max_jerk(Z_AXIS, parser.value_linear_units()),
if (parser.seenval(AXIS4_NAME)) planner.set_max_jerk(I_AXIS, parser.value_linear_units()),
if (parser.seenval(AXIS5_NAME)) planner.set_max_jerk(J_AXIS, parser.value_linear_units()),
if (parser.seenval(AXIS6_NAME)) planner.set_max_jerk(K_AXIS, parser.value_linear_units())
);
#if HAS_MESH && DISABLED(LIMITED_JERK_EDITING)
if (seenZ && planner.max_jerk.z <= 0.1f)
SERIAL_ECHOLNPGM("WARNING! Low Z Jerk may lead to unwanted pauses.");
#endif
#endif
#endif // HAS_CLASSIC_JERK
}
void GcodeSuite::M205_report(const bool forReplay/*=true*/) {
report_heading_etc(forReplay, PSTR(
"Advanced (B<min_segment_time_us> S<min_feedrate> T<min_travel_feedrate>"
TERN_(HAS_JUNCTION_DEVIATION, " J<junc_dev>")
TERN_(HAS_CLASSIC_JERK, " X<max_x_jerk> Y<max_y_jerk> Z<max_z_jerk>")
TERN_(HAS_CLASSIC_E_JERK, " E<max_e_jerk>")
")"
));
SERIAL_ECHOLNPGM_P(
PSTR(" M205 B"), LINEAR_UNIT(planner.settings.min_segment_time_us)
, PSTR(" S"), LINEAR_UNIT(planner.settings.min_feedrate_mm_s)
, SP_T_STR, LINEAR_UNIT(planner.settings.min_travel_feedrate_mm_s)
#if HAS_JUNCTION_DEVIATION
, PSTR(" J"), LINEAR_UNIT(planner.junction_deviation_mm)
#endif
#if HAS_CLASSIC_JERK
, LIST_N(DOUBLE(LINEAR_AXES),
SP_X_STR, LINEAR_UNIT(planner.max_jerk.x),
SP_Y_STR, LINEAR_UNIT(planner.max_jerk.y),
SP_Z_STR, LINEAR_UNIT(planner.max_jerk.z),
SP_I_STR, LINEAR_UNIT(planner.max_jerk.i),
SP_J_STR, LINEAR_UNIT(planner.max_jerk.j),
SP_K_STR, LINEAR_UNIT(planner.max_jerk.k)
)
#if HAS_CLASSIC_E_JERK
, SP_E_STR, LINEAR_UNIT(planner.max_jerk.e)
#endif
#endif
);
}

157
Marlin/src/gcode/config/M217.cpp Executable file → Normal file
View File

@@ -16,90 +16,153 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfigPre.h"
#if EXTRUDERS > 1
#if HAS_MULTI_EXTRUDER
#include "../gcode.h"
#include "../../module/tool_change.h"
#if ENABLED(TOOLCHANGE_MIGRATION_FEATURE)
#include "../../module/motion.h"
#endif
#include "../../MarlinCore.h" // for SP_X_STR, etc.
extern const char SP_X_STR[], SP_Y_STR[], SP_Z_STR[];
void M217_report(const bool eeprom=false) {
#if ENABLED(TOOLCHANGE_FILAMENT_SWAP)
serialprintPGM(eeprom ? PSTR(" M217") : PSTR("Toolchange:"));
SERIAL_ECHOPAIR(" S", LINEAR_UNIT(toolchange_settings.swap_length));
SERIAL_ECHOPAIR_P(SP_E_STR, LINEAR_UNIT(toolchange_settings.extra_prime));
SERIAL_ECHOPAIR_P(SP_P_STR, LINEAR_UNIT(toolchange_settings.prime_speed));
SERIAL_ECHOPAIR(" R", LINEAR_UNIT(toolchange_settings.retract_speed));
#if ENABLED(TOOLCHANGE_PARK)
SERIAL_ECHOPAIR_P(SP_X_STR, LINEAR_UNIT(toolchange_settings.change_point.x));
SERIAL_ECHOPAIR_P(SP_Y_STR, LINEAR_UNIT(toolchange_settings.change_point.y));
#endif
#else
UNUSED(eeprom);
#endif
SERIAL_ECHOPAIR_P(SP_Z_STR, LINEAR_UNIT(toolchange_settings.z_raise));
SERIAL_EOL();
}
/**
* M217 - Set SINGLENOZZLE toolchange parameters
*
* // Tool change command
* Q Prime active tool and exit
*
* // Tool change settings
* S[linear] Swap length
* E[linear] Purge length
* B[linear] Extra Swap length
* E[linear] Prime length
* P[linear/m] Prime speed
* R[linear/m] Retract speed
* U[linear/m] UnRetract speed
* V[linear] 0/1 Enable auto prime first extruder used
* W[linear] 0/1 Enable park & Z Raise
* X[linear] Park X (Requires TOOLCHANGE_PARK)
* Y[linear] Park Y (Requires TOOLCHANGE_PARK)
* Z[linear] Z Raise
* F[linear] Fan Speed 0-255
* G[linear/s] Fan time
*
* Tool migration settings
* A[0|1] Enable auto-migration on runout
* L[index] Last extruder to use for auto-migration
*
* Tool migration command
* T[index] Migrate to next extruder or the given extruder
*/
void GcodeSuite::M217() {
#define SPR_PARAM
#define XY_PARAM
#if ENABLED(TOOLCHANGE_FILAMENT_SWAP)
#undef SPR_PARAM
#define SPR_PARAM "SPRE"
static constexpr float max_extrude = TERN(PREVENT_LENGTHY_EXTRUDE, EXTRUDE_MAXLENGTH, 500);
static constexpr float max_extrude =
#if ENABLED(PREVENT_LENGTHY_EXTRUDE)
EXTRUDE_MAXLENGTH
#else
500
#endif
;
if (parser.seen('Q')) { tool_change_prime(); return; }
if (parser.seenval('S')) { const float v = parser.value_linear_units(); toolchange_settings.swap_length = constrain(v, 0, max_extrude); }
if (parser.seenval('B')) { const float v = parser.value_linear_units(); toolchange_settings.extra_resume = constrain(v, -10, 10); }
if (parser.seenval('E')) { const float v = parser.value_linear_units(); toolchange_settings.extra_prime = constrain(v, 0, max_extrude); }
if (parser.seenval('P')) { const int16_t v = parser.value_linear_units(); toolchange_settings.prime_speed = constrain(v, 10, 5400); }
if (parser.seenval('R')) { const int16_t v = parser.value_linear_units(); toolchange_settings.retract_speed = constrain(v, 10, 5400); }
if (parser.seenval('U')) { const int16_t v = parser.value_linear_units(); toolchange_settings.unretract_speed = constrain(v, 10, 5400); }
#if TOOLCHANGE_FS_FAN >= 0 && HAS_FAN
if (parser.seenval('F')) { const int16_t v = parser.value_linear_units(); toolchange_settings.fan_speed = constrain(v, 0, 255); }
if (parser.seenval('G')) { const int16_t v = parser.value_linear_units(); toolchange_settings.fan_time = constrain(v, 1, 30); }
#endif
#endif
#if ENABLED(TOOLCHANGE_FS_PRIME_FIRST_USED)
if (parser.seenval('V')) { enable_first_prime = parser.value_linear_units(); }
#endif
#if ENABLED(TOOLCHANGE_PARK)
#undef XY_PARAM
#define XY_PARAM "XY"
if (parser.seenval('X')) { toolchange_settings.change_point.x = parser.value_linear_units(); }
if (parser.seenval('Y')) { toolchange_settings.change_point.y = parser.value_linear_units(); }
if (parser.seenval('W')) { toolchange_settings.enable_park = parser.value_linear_units(); }
if (parser.seenval('X')) { const int16_t v = parser.value_linear_units(); toolchange_settings.change_point.x = constrain(v, X_MIN_POS, X_MAX_POS); }
if (parser.seenval('Y')) { const int16_t v = parser.value_linear_units(); toolchange_settings.change_point.y = constrain(v, Y_MIN_POS, Y_MAX_POS); }
#endif
if (parser.seenval('Z')) { toolchange_settings.z_raise = parser.value_linear_units(); }
if (!parser.seen(SPR_PARAM XY_PARAM "Z")) M217_report();
#if ENABLED(TOOLCHANGE_MIGRATION_FEATURE)
migration.target = 0; // 0 = disabled
if (parser.seenval('L')) { // Last
const int16_t lval = parser.value_int();
if (WITHIN(lval, 0, EXTRUDERS - 1)) {
migration.last = lval;
migration.automode = (active_extruder < migration.last);
}
}
if (parser.seen('A')) // Auto on/off
migration.automode = parser.value_bool();
if (parser.seen('T')) { // Migrate now
if (parser.has_value()) {
const int16_t tval = parser.value_int();
if (WITHIN(tval, 0, EXTRUDERS - 1) && tval != active_extruder) {
migration.target = tval + 1;
extruder_migration();
migration.target = 0; // disable
return;
}
else
migration.target = 0; // disable
}
else {
extruder_migration();
return;
}
}
#endif
M217_report();
}
#endif // EXTRUDERS > 1
void GcodeSuite::M217_report(const bool forReplay/*=true*/) {
report_heading_etc(forReplay, PSTR(STR_TOOL_CHANGING));
SERIAL_ECHOPGM(" M217");
#if ENABLED(TOOLCHANGE_FILAMENT_SWAP)
SERIAL_ECHOPGM(" S", LINEAR_UNIT(toolchange_settings.swap_length));
SERIAL_ECHOPGM_P(SP_B_STR, LINEAR_UNIT(toolchange_settings.extra_resume),
SP_E_STR, LINEAR_UNIT(toolchange_settings.extra_prime),
SP_P_STR, LINEAR_UNIT(toolchange_settings.prime_speed));
SERIAL_ECHOPGM(" R", LINEAR_UNIT(toolchange_settings.retract_speed),
" U", LINEAR_UNIT(toolchange_settings.unretract_speed),
" F", toolchange_settings.fan_speed,
" G", toolchange_settings.fan_time);
#if ENABLED(TOOLCHANGE_MIGRATION_FEATURE)
SERIAL_ECHOPGM(" A", migration.automode);
SERIAL_ECHOPGM(" L", LINEAR_UNIT(migration.last));
#endif
#if ENABLED(TOOLCHANGE_PARK)
SERIAL_ECHOPGM(" W", LINEAR_UNIT(toolchange_settings.enable_park));
SERIAL_ECHOPGM_P(SP_X_STR, LINEAR_UNIT(toolchange_settings.change_point.x));
SERIAL_ECHOPGM_P(SP_Y_STR, LINEAR_UNIT(toolchange_settings.change_point.y));
#endif
#if ENABLED(TOOLCHANGE_FS_PRIME_FIRST_USED)
SERIAL_ECHOPGM(" V", LINEAR_UNIT(enable_first_prime));
#endif
#endif
SERIAL_ECHOLNPGM_P(SP_Z_STR, LINEAR_UNIT(toolchange_settings.z_raise));
}
#endif // HAS_MULTI_EXTRUDER

31
Marlin/src/gcode/config/M218.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -41,6 +41,8 @@
*/
void GcodeSuite::M218() {
if (!parser.seen_any()) return M218_report();
const int8_t target_extruder = get_target_extruder_from_command();
if (target_extruder < 0) return;
@@ -48,24 +50,23 @@ void GcodeSuite::M218() {
if (parser.seenval('Y')) hotend_offset[target_extruder].y = parser.value_linear_units();
if (parser.seenval('Z')) hotend_offset[target_extruder].z = parser.value_linear_units();
if (!parser.seen("XYZ")) {
SERIAL_ECHO_START();
SERIAL_ECHOPGM(STR_HOTEND_OFFSET);
HOTEND_LOOP() {
SERIAL_CHAR(' ');
SERIAL_ECHO(hotend_offset[e].x);
SERIAL_CHAR(',');
SERIAL_ECHO(hotend_offset[e].y);
SERIAL_CHAR(',');
SERIAL_ECHO_F(hotend_offset[e].z, 3);
}
SERIAL_EOL();
}
#if ENABLED(DELTA)
if (target_extruder == active_extruder)
do_blocking_move_to_xy(current_position, planner.settings.max_feedrate_mm_s[X_AXIS]);
#endif
}
void GcodeSuite::M218_report(const bool forReplay/*=true*/) {
report_heading_etc(forReplay, PSTR(STR_HOTEND_OFFSETS));
LOOP_S_L_N(e, 1, HOTENDS) {
report_echo_start(forReplay);
SERIAL_ECHOPGM_P(
PSTR(" M218 T"), e,
SP_X_STR, LINEAR_UNIT(hotend_offset[e].x),
SP_Y_STR, LINEAR_UNIT(hotend_offset[e].y)
);
SERIAL_ECHOLNPAIR_F_P(SP_Z_STR, LINEAR_UNIT(hotend_offset[e].z), 3);
}
}
#endif // HAS_HOTEND_OFFSET

14
Marlin/src/gcode/config/M220.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -31,22 +31,20 @@
*
* Report the current speed percentage factor if no parameter is specified
*
* With PRUSA_MMU2...
* For MMU2 and MMU2S devices...
* B : Flag to back up the current factor
* R : Flag to restore the last-saved factor
*/
void GcodeSuite::M220() {
#if ENABLED(PRUSA_MMU2)
static int16_t backup_feedrate_percentage = 100;
if (parser.seen('B')) backup_feedrate_percentage = feedrate_percentage;
if (parser.seen('R')) feedrate_percentage = backup_feedrate_percentage;
#endif
static int16_t backup_feedrate_percentage = 100;
if (parser.seen('B')) backup_feedrate_percentage = feedrate_percentage;
if (parser.seen('R')) feedrate_percentage = backup_feedrate_percentage;
if (parser.seenval('S')) feedrate_percentage = parser.value_int();
if (!parser.seen_any()) {
SERIAL_ECHOPAIR("FR:", feedrate_percentage);
SERIAL_ECHOPGM("FR:", feedrate_percentage);
SERIAL_CHAR('%');
SERIAL_EOL();
}

12
Marlin/src/gcode/config/M221.cpp Executable file → Normal file
View File

@@ -16,14 +16,14 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "../gcode.h"
#include "../../module/planner.h"
#if EXTRUDERS
#if HAS_EXTRUDERS
/**
* M221: Set extrusion percentage (M221 T0 S95)
@@ -33,14 +33,12 @@ void GcodeSuite::M221() {
const int8_t target_extruder = get_target_extruder_from_command();
if (target_extruder < 0) return;
if (parser.seenval('S')) {
planner.flow_percentage[target_extruder] = parser.value_int();
planner.refresh_e_factor(target_extruder);
}
if (parser.seenval('S'))
planner.set_flow(target_extruder, parser.value_int());
else {
SERIAL_ECHO_START();
SERIAL_CHAR('E', '0' + target_extruder);
SERIAL_ECHOPAIR(" Flow: ", planner.flow_percentage[target_extruder]);
SERIAL_ECHOPGM(" Flow: ", planner.flow_percentage[target_extruder]);
SERIAL_CHAR('%');
SERIAL_EOL();
}

55
Marlin/src/gcode/config/M281.cpp Executable file → Normal file
View File

@@ -16,9 +16,10 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if ENABLED(EDITABLE_SERVO_ANGLES)
@@ -26,8 +27,18 @@
#include "../gcode.h"
#include "../../module/servo.h"
/**
* M281 - Edit / Report Servo Angles
*
* P<index> - Servo to update
* L<angle> - Deploy Angle
* U<angle> - Stowed Angle
*/
void GcodeSuite::M281() {
if (!parser.seen_any()) return M281_report();
if (!parser.seenval('P')) return;
const int servo_index = parser.value_int();
if (WITHIN(servo_index, 0, NUM_SERVOS - 1)) {
#if ENABLED(BLTOUCH)
@@ -36,25 +47,31 @@ void GcodeSuite::M281() {
return;
}
#endif
bool angle_change = false;
if (parser.seen('L')) {
servo_angles[servo_index][0] = parser.value_int();
angle_change = true;
}
if (parser.seen('U')) {
servo_angles[servo_index][1] = parser.value_int();
angle_change = true;
}
if (!angle_change) {
SERIAL_ECHO_START();
SERIAL_ECHOLNPAIR(" Servo ", servo_index,
" L", servo_angles[servo_index][0],
" U", servo_angles[servo_index][1]);
}
if (parser.seen('L')) servo_angles[servo_index][0] = parser.value_int();
if (parser.seen('U')) servo_angles[servo_index][1] = parser.value_int();
}
else {
SERIAL_ERROR_START();
SERIAL_ECHOLNPAIR("Servo ", servo_index, " out of range");
else
SERIAL_ERROR_MSG("Servo ", servo_index, " out of range");
}
void GcodeSuite::M281_report(const bool forReplay/*=true*/) {
report_heading_etc(forReplay, PSTR(STR_SERVO_ANGLES));
LOOP_L_N(i, NUM_SERVOS) {
switch (i) {
default: break;
#if ENABLED(SWITCHING_EXTRUDER)
case SWITCHING_EXTRUDER_SERVO_NR:
#if EXTRUDERS > 3
case SWITCHING_EXTRUDER_E23_SERVO_NR:
#endif
#elif ENABLED(SWITCHING_NOZZLE)
case SWITCHING_NOZZLE_SERVO_NR:
#elif ENABLED(BLTOUCH) || (HAS_Z_SERVO_PROBE && defined(Z_SERVO_ANGLES))
case Z_PROBE_SERVO_NR:
#endif
report_echo_start(forReplay);
SERIAL_ECHOLNPGM(" M281 P", i, " L", servo_angles[i][0], " U", servo_angles[i][1]);
}
}
}

67
Marlin/src/gcode/config/M301.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -30,6 +30,8 @@
/**
* M301: Set PID parameters P I D (and optionally C, L)
*
* E[extruder] Default: 0
*
* P[float] Kp term
* I[float] Ki term (unscaled)
* D[float] Kd term (unscaled)
@@ -44,45 +46,64 @@
* F[float] Kf term
*/
void GcodeSuite::M301() {
// multi-extruder PID patch: M301 updates or prints a single extruder's PID values
// default behavior (omitting E parameter) is to update for extruder 0 only
const uint8_t e = parser.byteval('E'); // extruder being updated
int8_t e = E_TERN0(parser.byteval('E', -1)); // extruder being updated
if (!parser.seen("PID" TERN_(PID_EXTRUSION_SCALING, "CL") TERN_(PID_FAN_SCALING, "F")))
return M301_report(true E_OPTARG(e));
if (e == -1) e = 0;
if (e < HOTENDS) { // catch bad input value
if (parser.seen('P')) PID_PARAM(Kp, e) = parser.value_float();
if (parser.seen('I')) PID_PARAM(Ki, e) = scalePID_i(parser.value_float());
if (parser.seen('D')) PID_PARAM(Kd, e) = scalePID_d(parser.value_float());
if (parser.seenval('P')) PID_PARAM(Kp, e) = parser.value_float();
if (parser.seenval('I')) PID_PARAM(Ki, e) = scalePID_i(parser.value_float());
if (parser.seenval('D')) PID_PARAM(Kd, e) = scalePID_d(parser.value_float());
#if ENABLED(PID_EXTRUSION_SCALING)
if (parser.seen('C')) PID_PARAM(Kc, e) = parser.value_float();
if (parser.seenval('C')) PID_PARAM(Kc, e) = parser.value_float();
if (parser.seenval('L')) thermalManager.lpq_len = parser.value_int();
NOMORE(thermalManager.lpq_len, LPQ_MAX_LEN);
NOLESS(thermalManager.lpq_len, 0);
#endif
#if ENABLED(PID_FAN_SCALING)
if (parser.seen('F')) PID_PARAM(Kf, e) = parser.value_float();
if (parser.seenval('F')) PID_PARAM(Kf, e) = parser.value_float();
#endif
thermalManager.updatePID();
SERIAL_ECHO_START();
#if ENABLED(PID_PARAMS_PER_HOTEND)
SERIAL_ECHOPAIR(" e:", e); // specify extruder in serial output
#endif
SERIAL_ECHOPAIR(" p:", PID_PARAM(Kp, e),
" i:", unscalePID_i(PID_PARAM(Ki, e)),
" d:", unscalePID_d(PID_PARAM(Kd, e)));
#if ENABLED(PID_EXTRUSION_SCALING)
SERIAL_ECHOPAIR(" c:", PID_PARAM(Kc, e));
#endif
#if ENABLED(PID_FAN_SCALING)
SERIAL_ECHOPAIR(" f:", PID_PARAM(Kf, e));
#endif
SERIAL_EOL();
}
else
SERIAL_ERROR_MSG(STR_INVALID_EXTRUDER);
}
void GcodeSuite::M301_report(const bool forReplay/*=true*/ E_OPTARG(const int8_t eindex/*=-1*/)) {
report_heading(forReplay, PSTR(STR_HOTEND_PID));
IF_DISABLED(HAS_MULTI_EXTRUDER, constexpr int8_t eindex = -1);
HOTEND_LOOP() {
if (e == eindex || eindex == -1) {
report_echo_start(forReplay);
SERIAL_ECHOPGM_P(
#if ENABLED(PID_PARAMS_PER_HOTEND)
PSTR(" M301 E"), e, SP_P_STR
#else
PSTR(" M301 P")
#endif
, PID_PARAM(Kp, e)
, PSTR(" I"), unscalePID_i(PID_PARAM(Ki, e))
, PSTR(" D"), unscalePID_d(PID_PARAM(Kd, e))
);
#if ENABLED(PID_EXTRUSION_SCALING)
SERIAL_ECHOPGM_P(SP_C_STR, PID_PARAM(Kc, e));
if (e == 0) SERIAL_ECHOPGM(" L", thermalManager.lpq_len);
#endif
#if ENABLED(PID_FAN_SCALING)
SERIAL_ECHOPGM(" F", PID_PARAM(Kf, e));
#endif
SERIAL_EOL();
}
}
}
#endif // PIDTEMP

8
Marlin/src/gcode/config/M302.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -37,7 +37,7 @@
*
* M302 ; report current cold extrusion state
* M302 P0 ; enable cold extrusion checking
* M302 P1 ; disables cold extrusion checking
* M302 P1 ; disable cold extrusion checking
* M302 S0 ; always allow extrusion (disables checking)
* M302 S170 ; only allow extrusion above 170
* M302 S170 P1 ; set min extrude temp to 170 but leave disabled
@@ -55,8 +55,8 @@ void GcodeSuite::M302() {
// Report current state
SERIAL_ECHO_START();
SERIAL_ECHOPGM("Cold extrudes are ");
serialprintPGM(thermalManager.allow_cold_extrude ? PSTR("en") : PSTR("dis"));
SERIAL_ECHOLNPAIR("abled (min temp ", thermalManager.extrude_min_temp, "C)");
SERIAL_ECHOPGM_P(thermalManager.allow_cold_extrude ? PSTR("en") : PSTR("dis"));
SERIAL_ECHOLNPGM("abled (min temp ", thermalManager.extrude_min_temp, "C)");
}
}

15
Marlin/src/gcode/config/M304.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -35,14 +35,19 @@
* D<dval> - Set the D value
*/
void GcodeSuite::M304() {
if (!parser.seen("PID")) return M304_report();
if (parser.seen('P')) thermalManager.temp_bed.pid.Kp = parser.value_float();
if (parser.seen('I')) thermalManager.temp_bed.pid.Ki = scalePID_i(parser.value_float());
if (parser.seen('D')) thermalManager.temp_bed.pid.Kd = scalePID_d(parser.value_float());
}
SERIAL_ECHO_START();
SERIAL_ECHOLNPAIR(" p:", thermalManager.temp_bed.pid.Kp,
" i:", unscalePID_i(thermalManager.temp_bed.pid.Ki),
" d:", unscalePID_d(thermalManager.temp_bed.pid.Kd));
void GcodeSuite::M304_report(const bool forReplay/*=true*/) {
report_heading_etc(forReplay, PSTR(STR_BED_PID));
SERIAL_ECHO_MSG(
" M304 P", thermalManager.temp_bed.pid.Kp
, " I", unscalePID_i(thermalManager.temp_bed.pid.Ki)
, " D", unscalePID_d(thermalManager.temp_bed.pid.Kd)
);
}
#endif // PIDTEMPBED

12
Marlin/src/gcode/config/M305.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -49,10 +49,8 @@ void GcodeSuite::M305() {
const bool do_set = parser.seen("BCRT");
// A valid P index is required
if (t_index >= (USER_THERMISTORS) || (do_set && t_index < 0)) {
SERIAL_ECHO_START();
SERIAL_ECHOLNPAIR("!Invalid index. (0 <= P <= ", int(USER_THERMISTORS - 1), ")");
}
if (t_index >= (USER_THERMISTORS) || (do_set && t_index < 0))
SERIAL_ECHO_MSG("!Invalid index. (0 <= P <= ", USER_THERMISTORS - 1, ")");
else if (do_set) {
if (parser.seen('R')) // Pullup resistor value
if (!thermalManager.set_pull_up_res(t_index, parser.value_float()))
@@ -72,10 +70,10 @@ void GcodeSuite::M305() {
} // If not setting then report parameters
else if (t_index < 0) { // ...all user thermistors
LOOP_L_N(i, USER_THERMISTORS)
thermalManager.log_user_thermistor(i);
thermalManager.M305_report(i);
}
else // ...one user thermistor
thermalManager.log_user_thermistor(t_index);
thermalManager.M305_report(t_index);
}
#endif // HAS_USER_THERMISTORS

View File

@@ -0,0 +1,53 @@
/**
* 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 ENABLED(PIDTEMPCHAMBER)
#include "../gcode.h"
#include "../../module/temperature.h"
/**
* M309 - Set and/or Report the current Chamber PID values
*
* P<pval> - Set the P value
* I<ival> - Set the I value
* D<dval> - Set the D value
*/
void GcodeSuite::M309() {
if (!parser.seen("PID")) return M309_report();
if (parser.seen('P')) thermalManager.temp_chamber.pid.Kp = parser.value_float();
if (parser.seen('I')) thermalManager.temp_chamber.pid.Ki = scalePID_i(parser.value_float());
if (parser.seen('D')) thermalManager.temp_chamber.pid.Kd = scalePID_d(parser.value_float());
}
void GcodeSuite::M309_report(const bool forReplay/*=true*/) {
report_heading_etc(forReplay, PSTR(STR_CHAMBER_PID));
SERIAL_ECHOLNPGM(
" M309 P", thermalManager.temp_chamber.pid.Kp
, " I", unscalePID_i(thermalManager.temp_chamber.pid.Ki)
, " D", unscalePID_d(thermalManager.temp_chamber.pid.Kd)
);
}
#endif // PIDTEMPCHAMBER

37
Marlin/src/gcode/config/M43.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -46,6 +46,10 @@
#include "../../lcd/extui/ui_api.h"
#endif
#if HAS_RESUME_CONTINUE
#include "../../lcd/marlinui.h"
#endif
#ifndef GET_PIN_MAP_PIN_M43
#define GET_PIN_MAP_PIN_M43(Q) GET_PIN_MAP_PIN(Q)
#endif
@@ -108,7 +112,7 @@ inline void toggle_pins() {
}
SERIAL_EOL();
}
SERIAL_ECHOLNPGM("Done.");
SERIAL_ECHOLNPGM(STR_DONE);
} // toggle_pins
@@ -126,8 +130,8 @@ inline void servo_probe_test() {
const uint8_t probe_index = parser.byteval('P', Z_PROBE_SERVO_NR);
SERIAL_ECHOLNPAIR("Servo probe test\n"
". using index: ", int(probe_index),
SERIAL_ECHOLNPGM("Servo probe test\n"
". using index: ", probe_index,
", deploy angle: ", servo_angles[probe_index][0],
", stow angle: ", servo_angles[probe_index][1]
);
@@ -139,7 +143,7 @@ inline void servo_probe_test() {
#define PROBE_TEST_PIN Z_MIN_PIN
constexpr bool probe_inverting = Z_MIN_ENDSTOP_INVERTING;
SERIAL_ECHOLNPAIR(". Probe Z_MIN_PIN: ", int(PROBE_TEST_PIN));
SERIAL_ECHOLNPGM(". Probe Z_MIN_PIN: ", PROBE_TEST_PIN);
SERIAL_ECHOPGM(". Z_MIN_ENDSTOP_INVERTING: ");
#else
@@ -147,7 +151,7 @@ inline void servo_probe_test() {
#define PROBE_TEST_PIN Z_MIN_PROBE_PIN
constexpr bool probe_inverting = Z_MIN_PROBE_ENDSTOP_INVERTING;
SERIAL_ECHOLNPAIR(". Probe Z_MIN_PROBE_PIN: ", int(PROBE_TEST_PIN));
SERIAL_ECHOLNPGM(". Probe Z_MIN_PROBE_PIN: ", PROBE_TEST_PIN);
SERIAL_ECHOPGM( ". Z_MIN_PROBE_ENDSTOP_INVERTING: ");
#endif
@@ -207,11 +211,11 @@ inline void servo_probe_test() {
if (deploy_state != stow_state) {
SERIAL_ECHOLNPGM("= Mechanical Switch detected");
if (deploy_state) {
SERIAL_ECHOLNPAIR(" DEPLOYED state: HIGH (logic 1)",
SERIAL_ECHOLNPGM(" DEPLOYED state: HIGH (logic 1)",
" STOWED (triggered) state: LOW (logic 0)");
}
else {
SERIAL_ECHOLNPAIR(" DEPLOYED state: LOW (logic 0)",
SERIAL_ECHOLNPGM(" DEPLOYED state: LOW (logic 0)",
" STOWED (triggered) state: HIGH (logic 1)");
}
#if ENABLED(BLTOUCH)
@@ -240,7 +244,7 @@ inline void servo_probe_test() {
if (probe_counter == 15)
SERIAL_ECHOLNPGM(": 30ms or more");
else
SERIAL_ECHOLNPAIR(" (+/- 4ms): ", probe_counter * 2);
SERIAL_ECHOLNPGM(" (+/- 4ms): ", probe_counter * 2);
if (probe_counter >= 4) {
if (probe_counter == 15) {
@@ -284,8 +288,8 @@ inline void servo_probe_test() {
* S<pin> - Start Pin number. If not given, will default to 0
* L<pin> - End Pin number. If not given, will default to last pin defined for this board
* I<bool> - Flag to ignore Marlin's pin protection. Use with caution!!!!
* R - Repeat pulses on each pin this number of times before continueing to next pin
* W - Wait time (in miliseconds) between pulses. If not given will default to 500
* R - Repeat pulses on each pin this number of times before continuing to next pin
* W - Wait time (in milliseconds) between pulses. If not given will default to 500
*
* M43 S - Servo probe test
* P<index> - Probe index (optional - defaults to 0
@@ -299,7 +303,7 @@ void GcodeSuite::M43() {
if (parser.seen('E')) {
endstops.monitor_flag = parser.value_bool();
SERIAL_ECHOPGM("endstop monitor ");
serialprintPGM(endstops.monitor_flag ? PSTR("en") : PSTR("dis"));
SERIAL_ECHOPGM_P(endstops.monitor_flag ? PSTR("en") : PSTR("dis"));
SERIAL_ECHOLNPGM("abled");
return;
}
@@ -340,12 +344,8 @@ void GcodeSuite::M43() {
#if HAS_RESUME_CONTINUE
KEEPALIVE_STATE(PAUSED_FOR_USER);
wait_for_user = true;
#if ENABLED(HOST_PROMPT_SUPPORT)
host_prompt_do(PROMPT_USER_CONTINUE, PSTR("M43 Wait Called"), CONTINUE_STR);
#endif
#if ENABLED(EXTENSIBLE_UI)
ExtUI::onUserConfirmRequired_P(PSTR("M43 Wait Called"));
#endif
TERN_(HOST_PROMPT_SUPPORT, host_prompt_do(PROMPT_USER_CONTINUE, PSTR("M43 Wait Called"), CONTINUE_STR));
TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired_P(PSTR("M43 Wait Called")));
#endif
for (;;) {
@@ -367,6 +367,7 @@ void GcodeSuite::M43() {
}
#if HAS_RESUME_CONTINUE
ui.update();
if (!wait_for_user) break;
#endif

2
Marlin/src/gcode/config/M540.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/

57
Marlin/src/gcode/config/M575.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -33,37 +33,44 @@
* B<baudrate> - Baud rate (bits per second)
*/
void GcodeSuite::M575() {
const int32_t baud = parser.ulongval('B');
int32_t baud = parser.ulongval('B');
switch (baud) {
case 24:
case 96:
case 192:
case 384:
case 576:
case 1152: baud *= 100; break;
case 250:
case 500: baud *= 1000; break;
case 19: baud = 19200; break;
case 38: baud = 38400; break;
case 57: baud = 57600; break;
case 115: baud = 115200; break;
}
switch (baud) {
case 2400: case 9600: case 19200: case 38400: case 57600:
case 115200: case 250000: case 500000: case 1000000: {
const int8_t port = parser.intval('P', -99);
const bool set0 = (port == -99 || port == 0);
if (set0) {
SERIAL_ECHO_START();
SERIAL_ECHOLNPAIR(" Serial "
#if NUM_SERIAL > 1
, '0',
#else
"0"
#endif
" baud rate set to ", baud
);
}
#if NUM_SERIAL > 1
const bool set1 = (port == -99 || port == 1);
if (set1) {
SERIAL_ECHO_START();
SERIAL_ECHOLNPAIR(" Serial ", '1', " baud rate set to ", baud);
}
const bool set1 = (port == -99 || port == 0);
if (set1) SERIAL_ECHO_MSG(" Serial ", AS_DIGIT(0), " baud rate set to ", baud);
#if HAS_MULTI_SERIAL
const bool set2 = (port == -99 || port == 1);
if (set2) SERIAL_ECHO_MSG(" Serial ", AS_DIGIT(1), " baud rate set to ", baud);
#ifdef SERIAL_PORT_3
const bool set3 = (port == -99 || port == 2);
if (set3) SERIAL_ECHO_MSG(" Serial ", AS_DIGIT(2), " baud rate set to ", baud);
#endif
#endif
SERIAL_FLUSH();
if (set0) { MYSERIAL0.end(); MYSERIAL0.begin(baud); }
#if NUM_SERIAL > 1
if (set1) { MYSERIAL1.end(); MYSERIAL1.begin(baud); }
if (set1) { MYSERIAL1.end(); MYSERIAL1.begin(baud); }
#if HAS_MULTI_SERIAL
if (set2) { MYSERIAL2.end(); MYSERIAL2.begin(baud); }
#ifdef SERIAL_PORT_3
if (set3) { MYSERIAL3.end(); MYSERIAL3.begin(baud); }
#endif
#endif
} break;
@@ -71,4 +78,4 @@ void GcodeSuite::M575() {
}
}
#endif // NUM_SERIAL > 0 && BAUD_RATE_GCODE
#endif // BAUD_RATE_GCODE

25
Marlin/src/gcode/config/M672.cpp Executable file → Normal file
View File

@@ -16,26 +16,18 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if ENABLED(SMART_EFFECTOR) && PIN_EXISTS(SMART_EFFECTOR_MOD)
#if ENABLED(DUET_SMART_EFFECTOR) && PIN_EXISTS(SMART_EFFECTOR_MOD)
#include "../gcode.h"
#include "../../HAL/shared/Delay.h"
#include "../parser.h"
/**
* M672 - Set/reset Duet Smart Effector sensitivity
*
* One of these is required:
* S<sensitivity> - 0-255
* R - Flag to reset sensitivity to default
*/
/**
* The Marlin format for the M672 command is different than shown in the Duet Smart Effector
* documentation https://duet3d.dozuki.com/Wiki/Smart_effector_and_carriage_adapters_for_delta_printer
@@ -51,8 +43,8 @@
* Marlin: M672 R
*/
#define M672_PROGBYTE 105 // magic byte to start programming custom sensitivity
#define M672_ERASEBYTE 131 // magic byte to clear custom sensitivity
#define M672_PROGBYTE 105 // magic byte to start programming custom sensitivity
#define M672_ERASEBYTE 131 // magic byte to clear custom sensitivity
//
// Smart Effector byte send protocol:
@@ -77,6 +69,13 @@ void M672_send(uint8_t b) { // bit rate requirement: 1KHz +/- 30%
}
}
/**
* M672 - Set/reset Duet Smart Effector sensitivity
*
* One of these is required:
* S<sensitivity> - 0-255
* R - Flag to reset sensitivity to default
*/
void GcodeSuite::M672() {
if (parser.seen('R')) {
M672_send(M672_ERASEBYTE);
@@ -96,4 +95,4 @@ void GcodeSuite::M672() {
OUT_WRITE(SMART_EFFECTOR_MOD_PIN, LOW); // Keep Smart Effector in NORMAL mode
}
#endif // SMART_EFFECTOR && SMART_EFFECTOR_MOD_PIN
#endif // DUET_SMART_EFFECTOR && SMART_EFFECTOR_MOD_PIN

108
Marlin/src/gcode/config/M92.cpp Executable file → Normal file
View File

@@ -16,35 +16,13 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "../gcode.h"
#include "../../module/planner.h"
void report_M92(const bool echo=true, const int8_t e=-1) {
if (echo) SERIAL_ECHO_START(); else SERIAL_CHAR(' ');
SERIAL_ECHOPAIR_P(PSTR(" M92 X"), LINEAR_UNIT(planner.settings.axis_steps_per_mm[X_AXIS]),
SP_Y_STR, LINEAR_UNIT(planner.settings.axis_steps_per_mm[Y_AXIS]),
SP_Z_STR, LINEAR_UNIT(planner.settings.axis_steps_per_mm[Z_AXIS]));
#if DISABLED(DISTINCT_E_FACTORS)
SERIAL_ECHOPAIR_P(SP_E_STR, VOLUMETRIC_UNIT(planner.settings.axis_steps_per_mm[E_AXIS]));
#endif
SERIAL_EOL();
#if ENABLED(DISTINCT_E_FACTORS)
LOOP_L_N(i, E_STEPPERS) {
if (e >= 0 && i != e) continue;
if (echo) SERIAL_ECHO_START(); else SERIAL_CHAR(' ');
SERIAL_ECHOLNPAIR_P(PSTR(" M92 T"), (int)i,
SP_E_STR, VOLUMETRIC_UNIT(planner.settings.axis_steps_per_mm[E_AXIS_N(i)]));
}
#endif
UNUSED_E(e);
}
/**
* M92: Set axis steps-per-unit for one or more axes, X, Y, Z, and E.
* (Follows the same syntax as G92)
@@ -53,10 +31,11 @@ void report_M92(const bool echo=true, const int8_t e=-1) {
*
* If no argument is given print the current values.
*
* With MAGIC_NUMBERS_GCODE:
* Use 'H' and/or 'L' to get ideal layer-height information.
* 'H' specifies micro-steps to use. We guess if it's not supplied.
* 'L' specifies a desired layer height. Nearest good heights are shown.
* With MAGIC_NUMBERS_GCODE:
*
* Use 'H' and/or 'L' to get ideal layer-height information.
* H<microsteps> - Specify micro-steps to use. Best guess if not supplied.
* L<linear> - Desired layer height in current units. Nearest good heights are shown.
*/
void GcodeSuite::M92() {
@@ -64,28 +43,26 @@ void GcodeSuite::M92() {
if (target_extruder < 0) return;
// No arguments? Show M92 report.
if (!parser.seen("XYZE"
#if ENABLED(MAGIC_NUMBERS_GCODE)
"HL"
#endif
)) return report_M92(true, target_extruder);
if (!parser.seen(LOGICAL_AXES_STRING TERN_(MAGIC_NUMBERS_GCODE, "HL")))
return M92_report(true, target_extruder);
LOOP_XYZE(i) {
LOOP_LOGICAL_AXES(i) {
if (parser.seenval(axis_codes[i])) {
if (i == E_AXIS) {
const float value = parser.value_per_axis_units((AxisEnum)(E_AXIS_N(target_extruder)));
if (value < 20) {
float factor = planner.settings.axis_steps_per_mm[E_AXIS_N(target_extruder)] / value; // increase e constants if M92 E14 is given for netfab.
#if HAS_CLASSIC_JERK && HAS_CLASSIC_E_JERK
planner.max_jerk.e *= factor;
#endif
planner.settings.max_feedrate_mm_s[E_AXIS_N(target_extruder)] *= factor;
planner.max_acceleration_steps_per_s2[E_AXIS_N(target_extruder)] *= factor;
}
planner.settings.axis_steps_per_mm[E_AXIS_N(target_extruder)] = value;
}
else {
if (TERN1(HAS_EXTRUDERS, i != E_AXIS))
planner.settings.axis_steps_per_mm[i] = parser.value_per_axis_units((AxisEnum)i);
else {
#if HAS_EXTRUDERS
const float value = parser.value_per_axis_units((AxisEnum)(E_AXIS_N(target_extruder)));
if (value < 20) {
float factor = planner.settings.axis_steps_per_mm[E_AXIS_N(target_extruder)] / value; // increase e constants if M92 E14 is given for netfab.
#if HAS_CLASSIC_JERK && HAS_CLASSIC_E_JERK
planner.max_jerk.e *= factor;
#endif
planner.settings.max_feedrate_mm_s[E_AXIS_N(target_extruder)] *= factor;
planner.max_acceleration_steps_per_s2[E_AXIS_N(target_extruder)] *= factor;
}
planner.settings.axis_steps_per_mm[E_AXIS_N(target_extruder)] = value;
#endif
}
}
}
@@ -95,20 +72,49 @@ void GcodeSuite::M92() {
#ifndef Z_MICROSTEPS
#define Z_MICROSTEPS 16
#endif
const float wanted = parser.floatval('L');
const float wanted = parser.linearval('L');
if (parser.seen('H') || wanted) {
const uint16_t argH = parser.ushortval('H'),
micro_steps = argH ?: Z_MICROSTEPS;
const float z_full_step_mm = micro_steps * planner.steps_to_mm[Z_AXIS];
const float z_full_step_mm = micro_steps * planner.mm_per_step[Z_AXIS];
SERIAL_ECHO_START();
SERIAL_ECHOPAIR("{ micro_steps:", micro_steps, ", z_full_step_mm:", z_full_step_mm);
SERIAL_ECHOPGM("{ micro_steps:", micro_steps, ", z_full_step_mm:", z_full_step_mm);
if (wanted) {
const float best = uint16_t(wanted / z_full_step_mm) * z_full_step_mm;
SERIAL_ECHOPAIR(", best:[", best);
if (best != wanted) { SERIAL_CHAR(','); SERIAL_ECHO(best + z_full_step_mm); }
SERIAL_ECHOPGM(", best:[", best);
if (best != wanted) { SERIAL_CHAR(','); SERIAL_DECIMAL(best + z_full_step_mm); }
SERIAL_CHAR(']');
}
SERIAL_ECHOLNPGM(" }");
}
#endif
}
void GcodeSuite::M92_report(const bool forReplay/*=true*/, const int8_t e/*=-1*/) {
report_heading_etc(forReplay, PSTR(STR_STEPS_PER_UNIT));
SERIAL_ECHOPGM_P(LIST_N(DOUBLE(LINEAR_AXES),
PSTR(" M92 X"), LINEAR_UNIT(planner.settings.axis_steps_per_mm[X_AXIS]),
SP_Y_STR, LINEAR_UNIT(planner.settings.axis_steps_per_mm[Y_AXIS]),
SP_Z_STR, LINEAR_UNIT(planner.settings.axis_steps_per_mm[Z_AXIS]),
SP_I_STR, LINEAR_UNIT(planner.settings.axis_steps_per_mm[I_AXIS]),
SP_J_STR, LINEAR_UNIT(planner.settings.axis_steps_per_mm[J_AXIS]),
SP_K_STR, LINEAR_UNIT(planner.settings.axis_steps_per_mm[K_AXIS]))
);
#if HAS_EXTRUDERS && DISABLED(DISTINCT_E_FACTORS)
SERIAL_ECHOPGM_P(SP_E_STR, VOLUMETRIC_UNIT(planner.settings.axis_steps_per_mm[E_AXIS]));
#endif
SERIAL_EOL();
#if ENABLED(DISTINCT_E_FACTORS)
LOOP_L_N(i, E_STEPPERS) {
if (e >= 0 && i != e) continue;
report_echo_start(forReplay);
SERIAL_ECHOLNPGM_P(
PSTR(" M92 T"), i,
SP_E_STR, VOLUMETRIC_UNIT(planner.settings.axis_steps_per_mm[E_AXIS_N(i)])
);
}
#else
UNUSED(e);
#endif
}

View File

@@ -0,0 +1,44 @@
/**
* 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/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if ENABLED(AIR_EVACUATION)
#include "../gcode.h"
#include "../../feature/spindle_laser.h"
/**
* M10: Vacuum or Blower On
*/
void GcodeSuite::M10() {
cutter.air_evac_enable(); // Turn on Vacuum or Blower motor
}
/**
* M11: Vacuum or Blower OFF
*/
void GcodeSuite::M11() {
cutter.air_evac_disable(); // Turn off Vacuum or Blower motor
}
#endif // AIR_EVACUATION

9
Marlin/src/gcode/control/M108_M112_M410.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -25,15 +25,14 @@
#if DISABLED(EMERGENCY_PARSER)
#include "../gcode.h"
#include "../../MarlinCore.h" // for wait_for_heatup, kill, quickstop_stepper
#include "../../MarlinCore.h" // for wait_for_heatup, kill, M112_KILL_STR
#include "../../module/motion.h" // for quickstop_stepper
/**
* M108: Stop the waiting for heaters in M109, M190, M303. Does not affect the target temperature.
*/
void GcodeSuite::M108() {
#if HAS_RESUME_CONTINUE
wait_for_user = false;
#endif
TERN_(HAS_RESUME_CONTINUE, wait_for_user = false);
wait_for_heatup = false;
}

37
Marlin/src/gcode/control/M111.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -28,21 +28,18 @@
void GcodeSuite::M111() {
if (parser.seen('S')) marlin_debug_flags = parser.byteval('S');
static const char str_debug_1[] PROGMEM = STR_DEBUG_ECHO,
str_debug_2[] PROGMEM = STR_DEBUG_INFO,
str_debug_4[] PROGMEM = STR_DEBUG_ERRORS,
str_debug_8[] PROGMEM = STR_DEBUG_DRYRUN,
str_debug_16[] PROGMEM = STR_DEBUG_COMMUNICATION
#if ENABLED(DEBUG_LEVELING_FEATURE)
, str_debug_lvl[] PROGMEM = STR_DEBUG_LEVELING
#endif
;
static PGMSTR(str_debug_1, STR_DEBUG_ECHO);
static PGMSTR(str_debug_2, STR_DEBUG_INFO);
static PGMSTR(str_debug_4, STR_DEBUG_ERRORS);
static PGMSTR(str_debug_8, STR_DEBUG_DRYRUN);
static PGMSTR(str_debug_16, STR_DEBUG_COMMUNICATION);
#if ENABLED(DEBUG_LEVELING_FEATURE)
static PGMSTR(str_debug_lvl, STR_DEBUG_LEVELING);
#endif
static PGM_P const debug_strings[] PROGMEM = {
str_debug_1, str_debug_2, str_debug_4, str_debug_8, str_debug_16
#if ENABLED(DEBUG_LEVELING_FEATURE)
, str_debug_lvl
#endif
str_debug_1, str_debug_2, str_debug_4, str_debug_8, str_debug_16,
TERN_(DEBUG_LEVELING_FEATURE, str_debug_lvl)
};
SERIAL_ECHO_START();
@@ -52,7 +49,7 @@ void GcodeSuite::M111() {
LOOP_L_N(i, COUNT(debug_strings)) {
if (TEST(marlin_debug_flags, i)) {
if (comma++) SERIAL_CHAR(',');
serialprintPGM((char*)pgm_read_ptr(&debug_strings[i]));
SERIAL_ECHOPGM_P((char*)pgm_read_ptr(&debug_strings[i]));
}
}
}
@@ -60,21 +57,21 @@ void GcodeSuite::M111() {
SERIAL_ECHOPGM(STR_DEBUG_OFF);
#if !defined(__AVR__) || !defined(USBCON)
#if ENABLED(SERIAL_STATS_RX_BUFFER_OVERRUNS)
SERIAL_ECHOPAIR("\nBuffer Overruns: ", MYSERIAL0.buffer_overruns());
SERIAL_ECHOPGM("\nBuffer Overruns: ", MYSERIAL1.buffer_overruns());
#endif
#if ENABLED(SERIAL_STATS_RX_FRAMING_ERRORS)
SERIAL_ECHOPAIR("\nFraming Errors: ", MYSERIAL0.framing_errors());
SERIAL_ECHOPGM("\nFraming Errors: ", MYSERIAL1.framing_errors());
#endif
#if ENABLED(SERIAL_STATS_DROPPED_RX)
SERIAL_ECHOPAIR("\nDropped bytes: ", MYSERIAL0.dropped());
SERIAL_ECHOPGM("\nDropped bytes: ", MYSERIAL1.dropped());
#endif
#if ENABLED(SERIAL_STATS_MAX_RX_QUEUED)
SERIAL_ECHOPAIR("\nMax RX Queue Size: ", MYSERIAL0.rxMaxEnqueued());
SERIAL_ECHOPGM("\nMax RX Queue Size: ", MYSERIAL1.rxMaxEnqueued());
#endif
#endif // !defined(__AVR__) || !defined(USBCON)
#endif // !__AVR__ || !USBCON
}
SERIAL_EOL();
}

2
Marlin/src/gcode/control/M120_M121.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/

232
Marlin/src/gcode/control/M17_M18_M84.cpp Executable file → Normal file
View File

@@ -16,62 +16,232 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "../gcode.h"
#include "../../MarlinCore.h" // for stepper_inactive_time, disable_e_steppers
#include "../../lcd/ultralcd.h"
#include "../../lcd/marlinui.h"
#include "../../module/stepper.h"
#if BOTH(AUTO_BED_LEVELING_UBL, ULTRA_LCD)
#if ENABLED(AUTO_BED_LEVELING_UBL)
#include "../../feature/bedlevel/bedlevel.h"
#endif
/**
* M17: Enable stepper motors
*/
void GcodeSuite::M17() {
if (parser.seen("XYZE")) {
if (parser.seen('X')) ENABLE_AXIS_X();
if (parser.seen('Y')) ENABLE_AXIS_Y();
if (parser.seen('Z')) ENABLE_AXIS_Z();
#if HAS_E_STEPPER_ENABLE
if (parser.seen('E')) enable_e_steppers();
#define DEBUG_OUT ENABLED(MARLIN_DEV_MODE)
#include "../../core/debug_out.h"
#include "../../libs/hex_print.h"
inline axis_flags_t selected_axis_bits() {
axis_flags_t selected{0};
#if HAS_EXTRUDERS
if (parser.seen('E')) {
if (E_TERN0(parser.has_value())) {
const uint8_t e = parser.value_int();
if (e < EXTRUDERS)
selected.bits = _BV(INDEX_OF_AXIS(E_AXIS, e));
}
else
selected.bits = selected.e_bits();
}
#endif
selected.bits |= LINEAR_AXIS_GANG(
(parser.seen_test('X') << X_AXIS),
| (parser.seen_test('Y') << Y_AXIS),
| (parser.seen_test('Z') << Z_AXIS),
| (parser.seen_test(AXIS4_NAME) << I_AXIS),
| (parser.seen_test(AXIS5_NAME) << J_AXIS),
| (parser.seen_test(AXIS6_NAME) << K_AXIS)
);
return selected;
}
// Enable specified axes and warn about other affected axes
void do_enable(const axis_flags_t to_enable) {
const ena_mask_t was_enabled = stepper.axis_enabled.bits,
shall_enable = to_enable.bits & ~was_enabled;
DEBUG_ECHOLNPGM("Now Enabled: ", hex_word(stepper.axis_enabled.bits), " Enabling: ", hex_word(to_enable.bits), " | ", shall_enable);
if (!shall_enable) return; // All specified axes already enabled?
ena_mask_t also_enabled = 0; // Track steppers enabled due to overlap
// Enable all flagged axes
LOOP_LINEAR_AXES(a) {
if (TEST(shall_enable, a)) {
stepper.enable_axis(AxisEnum(a)); // Mark and enable the requested axis
DEBUG_ECHOLNPGM("Enabled ", axis_codes[a], " (", a, ") with overlap ", hex_word(enable_overlap[a]), " ... Enabled: ", hex_word(stepper.axis_enabled.bits));
also_enabled |= enable_overlap[a];
}
}
#if HAS_EXTRUDERS
LOOP_L_N(e, EXTRUDERS) {
const uint8_t a = INDEX_OF_AXIS(E_AXIS, e);
if (TEST(shall_enable, a)) {
stepper.ENABLE_EXTRUDER(e);
DEBUG_ECHOLNPGM("Enabled E", AS_DIGIT(e), " (", a, ") with overlap ", hex_word(enable_overlap[a]), " ... ", hex_word(stepper.axis_enabled.bits));
also_enabled |= enable_overlap[a];
}
}
#endif
if ((also_enabled &= ~(shall_enable | was_enabled))) {
SERIAL_CHAR('(');
LOOP_LINEAR_AXES(a) if (TEST(also_enabled, a)) SERIAL_CHAR(axis_codes[a], ' ');
#if HAS_EXTRUDERS
#define _EN_ALSO(N) if (TEST(also_enabled, INDEX_OF_AXIS(E_AXIS, N))) SERIAL_CHAR('E', '0' + N, ' ');
REPEAT(EXTRUDERS, _EN_ALSO)
#endif
SERIAL_ECHOLNPGM("also enabled)");
}
else {
LCD_MESSAGEPGM(MSG_NO_MOVE);
enable_all_steppers();
}
DEBUG_ECHOLNPGM("Enabled Now: ", hex_word(stepper.axis_enabled.bits));
}
/**
* M18, M84: Disable stepper motors
* M17: Enable stepper motor power for one or more axes.
* Print warnings for axes that share an ENABLE_PIN.
*
* Examples:
*
* M17 XZ ; Enable X and Z axes
* M17 E ; Enable all E steppers
* M17 E1 ; Enable just the E1 stepper
*/
void GcodeSuite::M17() {
if (parser.seen_axis()) {
if (any_enable_overlap())
do_enable(selected_axis_bits());
else {
#if HAS_EXTRUDERS
if (parser.seen('E')) {
if (parser.has_value()) {
const uint8_t e = parser.value_int();
if (e < EXTRUDERS) stepper.ENABLE_EXTRUDER(e);
}
else
stepper.enable_e_steppers();
}
#endif
LINEAR_AXIS_CODE(
if (parser.seen_test('X')) stepper.enable_axis(X_AXIS),
if (parser.seen_test('Y')) stepper.enable_axis(Y_AXIS),
if (parser.seen_test('Z')) stepper.enable_axis(Z_AXIS),
if (parser.seen_test(AXIS4_NAME)) stepper.enable_axis(I_AXIS),
if (parser.seen_test(AXIS5_NAME)) stepper.enable_axis(J_AXIS),
if (parser.seen_test(AXIS6_NAME)) stepper.enable_axis(K_AXIS)
);
}
}
else {
LCD_MESSAGEPGM(MSG_NO_MOVE);
stepper.enable_all_steppers();
}
}
void try_to_disable(const axis_flags_t to_disable) {
ena_mask_t still_enabled = to_disable.bits & stepper.axis_enabled.bits;
DEBUG_ECHOLNPGM("Enabled: ", hex_word(stepper.axis_enabled.bits), " To Disable: ", hex_word(to_disable.bits), " | ", hex_word(still_enabled));
if (!still_enabled) return;
// Attempt to disable all flagged axes
LOOP_LINEAR_AXES(a)
if (TEST(to_disable.bits, a)) {
DEBUG_ECHOPGM("Try to disable ", axis_codes[a], " (", a, ") with overlap ", hex_word(enable_overlap[a]), " ... ");
if (stepper.disable_axis(AxisEnum(a))) { // Mark the requested axis and request to disable
DEBUG_ECHOPGM("OK");
still_enabled &= ~(_BV(a) | enable_overlap[a]); // If actually disabled, clear one or more tracked bits
}
else
DEBUG_ECHOPGM("OVERLAP");
DEBUG_ECHOLNPGM(" ... still_enabled=", hex_word(still_enabled));
}
#if HAS_EXTRUDERS
LOOP_L_N(e, EXTRUDERS) {
const uint8_t a = INDEX_OF_AXIS(E_AXIS, e);
if (TEST(to_disable.bits, a)) {
DEBUG_ECHOPGM("Try to disable E", AS_DIGIT(e), " (", a, ") with overlap ", hex_word(enable_overlap[a]), " ... ");
if (stepper.DISABLE_EXTRUDER(e)) {
DEBUG_ECHOPGM("OK");
still_enabled &= ~(_BV(a) | enable_overlap[a]);
}
else
DEBUG_ECHOPGM("OVERLAP");
DEBUG_ECHOLNPGM(" ... still_enabled=", hex_word(still_enabled));
}
}
#endif
auto overlap_warning = [](const ena_mask_t axis_bits) {
SERIAL_ECHOPGM(" not disabled. Shared with");
LOOP_LINEAR_AXES(a) if (TEST(axis_bits, a)) SERIAL_CHAR(' ', axis_codes[a]);
#if HAS_EXTRUDERS
#define _EN_STILLON(N) if (TEST(axis_bits, INDEX_OF_AXIS(E_AXIS, N))) SERIAL_CHAR(' ', 'E', '0' + N);
REPEAT(EXTRUDERS, _EN_STILLON)
#endif
SERIAL_ECHOLNPGM(".");
};
// If any of the requested axes are still enabled, give a warning
LOOP_LINEAR_AXES(a) {
if (TEST(still_enabled, a)) {
SERIAL_CHAR(axis_codes[a]);
overlap_warning(stepper.axis_enabled.bits & enable_overlap[a]);
}
}
#if HAS_EXTRUDERS
LOOP_L_N(e, EXTRUDERS) {
const uint8_t a = INDEX_OF_AXIS(E_AXIS, e);
if (TEST(still_enabled, a)) {
SERIAL_CHAR('E', '0' + e);
overlap_warning(stepper.axis_enabled.bits & enable_overlap[a]);
}
}
#endif
DEBUG_ECHOLNPGM("Enabled Now: ", hex_word(stepper.axis_enabled.bits));
}
/**
* M18, M84: Disable stepper motor power for one or more axes.
* Print warnings for axes that share an ENABLE_PIN.
*/
void GcodeSuite::M18_M84() {
if (parser.seenval('S')) {
reset_stepper_timeout();
stepper_inactive_time = parser.value_millis_from_seconds();
}
else {
if (parser.seen("XYZE")) {
if (parser.seen_axis()) {
planner.synchronize();
if (parser.seen('X')) DISABLE_AXIS_X();
if (parser.seen('Y')) DISABLE_AXIS_Y();
if (parser.seen('Z')) DISABLE_AXIS_Z();
#if HAS_E_STEPPER_ENABLE
if (parser.seen('E')) disable_e_steppers();
#endif
if (any_enable_overlap())
try_to_disable(selected_axis_bits());
else {
#if HAS_EXTRUDERS
if (parser.seen('E')) {
if (E_TERN0(parser.has_value()))
stepper.DISABLE_EXTRUDER(parser.value_int());
else
stepper.disable_e_steppers();
}
#endif
LINEAR_AXIS_CODE(
if (parser.seen_test('X')) stepper.disable_axis(X_AXIS),
if (parser.seen_test('Y')) stepper.disable_axis(Y_AXIS),
if (parser.seen_test('Z')) stepper.disable_axis(Z_AXIS),
if (parser.seen_test(AXIS4_NAME)) stepper.disable_axis(I_AXIS),
if (parser.seen_test(AXIS5_NAME)) stepper.disable_axis(J_AXIS),
if (parser.seen_test(AXIS6_NAME)) stepper.disable_axis(K_AXIS)
);
}
}
else
planner.finish_and_disable();
#if HAS_LCD_MENU && ENABLED(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());
}
}

28
Marlin/src/gcode/control/M211.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -33,14 +33,22 @@
* Usage: M211 S1 to enable, M211 S0 to disable, M211 alone for report
*/
void GcodeSuite::M211() {
const xyz_pos_t l_soft_min = soft_endstop.min.asLogical(),
l_soft_max = soft_endstop.max.asLogical();
SERIAL_ECHO_START();
SERIAL_ECHOPGM(STR_SOFT_ENDSTOPS);
if (parser.seen('S')) soft_endstops_enabled = parser.value_bool();
serialprint_onoff(soft_endstops_enabled);
print_xyz(l_soft_min, PSTR(STR_SOFT_MIN), PSTR(" "));
print_xyz(l_soft_max, PSTR(STR_SOFT_MAX));
if (parser.seen('S'))
soft_endstop._enabled = parser.value_bool();
else
M211_report();
}
#endif
void GcodeSuite::M211_report(const bool forReplay/*=true*/) {
report_heading_etc(forReplay, PSTR(STR_SOFT_ENDSTOPS));
SERIAL_ECHOPGM(" M211 S", AS_DIGIT(soft_endstop._enabled), " ; ");
serialprintln_onoff(soft_endstop._enabled);
report_echo_start(forReplay);
const xyz_pos_t l_soft_min = soft_endstop.min.asLogical(),
l_soft_max = soft_endstop.max.asLogical();
print_pos(l_soft_min, PSTR(STR_SOFT_MIN), PSTR(" "));
print_pos(l_soft_max, PSTR(STR_SOFT_MAX));
}
#endif // HAS_SOFTWARE_ENDSTOPS

10
Marlin/src/gcode/control/M226.cpp Executable file → Normal file
View File

@@ -16,14 +16,20 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if ENABLED(DIRECT_PIN_CONTROL)
#include "../gcode.h"
#include "../../MarlinCore.h" // for pin_is_protected and idle()
#include "../../module/stepper.h"
void protected_pin_err();
/**
* M226: Wait until the specified pin reaches the state required (M226 P<pin> S<state>)
*/
@@ -50,3 +56,5 @@ void GcodeSuite::M226() {
} // pin_state -1 0 1 && pin > -1
} // parser.seen('P')
}
#endif // DIRECT_PIN_CONTROL

53
Marlin/src/gcode/control/M280.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -26,30 +26,51 @@
#include "../gcode.h"
#include "../../module/servo.h"
#include "../../module/planner.h"
/**
* M280: Get or set servo position. P<index> [S<angle>]
* M280: Get or set servo position.
* P<index> - Servo index
* S<angle> - Angle to set, omit to read current angle, or use -1 to detach
*
* With POLARGRAPH:
* T<ms> - Duration of servo move
*/
void GcodeSuite::M280() {
if (!parser.seen('P')) return;
if (!parser.seenval('P')) return;
TERN_(POLARGRAPH, planner.synchronize());
const int servo_index = parser.value_int();
if (WITHIN(servo_index, 0, NUM_SERVOS - 1)) {
if (parser.seen('S')) {
const int a = parser.value_int();
if (a == -1)
servo[servo_index].detach();
if (parser.seenval('S')) {
const int anew = parser.value_int();
if (anew >= 0) {
#if ENABLED(POLARGRAPH)
if (parser.seen('T')) { // (ms) Total duration of servo move
const int16_t t = constrain(parser.value_int(), 0, 10000);
const int aold = servo[servo_index].read();
millis_t now = millis();
const millis_t start = now, end = start + t;
while (PENDING(now, end)) {
safe_delay(50);
now = _MIN(millis(), end);
MOVE_SERVO(servo_index, LROUND(aold + (anew - aold) * (float(now - start) / t)));
}
}
#endif // POLARGRAPH
MOVE_SERVO(servo_index, anew);
}
else
MOVE_SERVO(servo_index, a);
}
else {
SERIAL_ECHO_START();
SERIAL_ECHOLNPAIR(" Servo ", servo_index, ": ", servo[servo_index].read());
DETACH_SERVO(servo_index);
}
else
SERIAL_ECHO_MSG(" Servo ", servo_index, ": ", servo[servo_index].read());
}
else {
SERIAL_ERROR_START();
SERIAL_ECHOLNPAIR("Servo ", servo_index, " out of range");
}
else
SERIAL_ERROR_MSG("Servo ", servo_index, " out of range");
}
#endif // HAS_SERVOS

View File

@@ -0,0 +1,45 @@
/**
* 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/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if ENABLED(SERVO_DETACH_GCODE)
#include "../gcode.h"
#include "../../module/servo.h"
/**
* M282: Detach Servo. P<index>
*/
void GcodeSuite::M282() {
if (!parser.seenval('P')) return;
const int servo_index = parser.value_int();
if (WITHIN(servo_index, 0, NUM_SERVOS - 1))
DETACH_SERVO(servo_index);
else
SERIAL_ECHO_MSG("Servo ", servo_index, " out of range");
}
#endif // SERVO_DETACH_GCODE

76
Marlin/src/gcode/control/M3-M5.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -30,20 +30,16 @@
/**
* Laser:
*
* M3 - Laser ON/Power (Ramped power)
* M4 - Laser ON/Power (Continuous power)
*
* S<power> - Set power. S0 will turn the laser off.
* O<ocr> - Set power and OCR
*
* Spindle:
*
* M3 - Spindle ON (Clockwise)
* M4 - Spindle ON (Counter-clockwise)
*
* S<power> - Set power. S0 will turn the spindle off.
* O<ocr> - Set power and OCR
* 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)
*
* If no PWM pin is defined then M3/M4 just turns it on.
*
@@ -70,31 +66,75 @@
* PWM duty cycle goes from 0 (off) to 255 (always on).
*/
void GcodeSuite::M3_M4(const bool is_M4) {
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;
};
#if ENABLED(SPINDLE_FEATURE)
planner.synchronize(); // Wait for movement 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.seen('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;
}
// Non-inline, standard case
cutter.inline_disable(); // Prevent future blocks re-setting the power
#endif
cutter.set_direction(is_M4);
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_PWM)
if (parser.seenval('O'))
cutter.set_ocr_power(parser.value_byte()); // The OCR is a value from 0 to 255 (uint8_t)
#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)
}
else
cutter.set_power(parser.intval('S', 255));
cutter.set_power(cutter.upower_to_ocr(get_s_power()));
#elif ENABLED(SPINDLE_SERVO)
cutter.set_power(get_s_power());
#else
cutter.set_enabled(true);
#endif
cutter.menuPower = cutter.unitPower;
}
/**
* M5 - Cutter OFF
* M5 - Cutter OFF (when moves are complete)
*/
void GcodeSuite::M5() {
#if ENABLED(SPINDLE_FEATURE)
planner.synchronize();
#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;
}
#endif // HAS_CUTTER

10
Marlin/src/gcode/control/M350_M351.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -34,7 +34,7 @@
*/
void GcodeSuite::M350() {
if (parser.seen('S')) LOOP_LE_N(i, 4) stepper.microstep_mode(i, parser.value_byte());
LOOP_XYZE(i) if (parser.seen(axis_codes[i])) stepper.microstep_mode(i, parser.value_byte());
LOOP_LOGICAL_AXES(i) if (parser.seen(axis_codes[i])) stepper.microstep_mode(i, parser.value_byte());
if (parser.seen('B')) stepper.microstep_mode(4, parser.value_byte());
stepper.microstep_readings();
}
@@ -46,15 +46,15 @@ void GcodeSuite::M350() {
void GcodeSuite::M351() {
if (parser.seenval('S')) switch (parser.value_byte()) {
case 1:
LOOP_XYZE(i) if (parser.seenval(axis_codes[i])) stepper.microstep_ms(i, parser.value_byte(), -1, -1);
LOOP_LOGICAL_AXES(i) if (parser.seenval(axis_codes[i])) stepper.microstep_ms(i, parser.value_byte(), -1, -1);
if (parser.seenval('B')) stepper.microstep_ms(4, parser.value_byte(), -1, -1);
break;
case 2:
LOOP_XYZE(i) if (parser.seenval(axis_codes[i])) stepper.microstep_ms(i, -1, parser.value_byte(), -1);
LOOP_LOGICAL_AXES(i) if (parser.seenval(axis_codes[i])) stepper.microstep_ms(i, -1, parser.value_byte(), -1);
if (parser.seenval('B')) stepper.microstep_ms(4, -1, parser.value_byte(), -1);
break;
case 3:
LOOP_XYZE(i) if (parser.seenval(axis_codes[i])) stepper.microstep_ms(i, -1, -1, parser.value_byte());
LOOP_LOGICAL_AXES(i) if (parser.seenval(axis_codes[i])) stepper.microstep_ms(i, -1, -1, parser.value_byte());
if (parser.seenval('B')) stepper.microstep_ms(4, -1, -1, parser.value_byte());
break;
}

2
Marlin/src/gcode/control/M380_M381.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/

2
Marlin/src/gcode/control/M400.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/

58
Marlin/src/gcode/control/M42.cpp Executable file → Normal file
View File

@@ -16,18 +16,32 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "../gcode.h"
#include "../../MarlinCore.h" // for pin_is_protected
#include "../../inc/MarlinConfig.h"
#if FAN_COUNT > 0
#if ENABLED(DIRECT_PIN_CONTROL)
#include "../gcode.h"
#include "../../MarlinCore.h" // for pin_is_protected
#if HAS_FAN
#include "../../module/temperature.h"
#endif
#ifdef MAPLE_STM32F1
// these are enums on the F1...
#define INPUT_PULLDOWN INPUT_PULLDOWN
#define INPUT_ANALOG INPUT_ANALOG
#define OUTPUT_OPEN_DRAIN OUTPUT_OPEN_DRAIN
#endif
void protected_pin_err() {
SERIAL_ERROR_MSG(STR_ERR_PROTECTED_PIN);
}
/**
* M42: Change pin status via GCode
*
@@ -48,23 +62,29 @@ void GcodeSuite::M42() {
if (!parser.boolval('I') && pin_is_protected(pin)) return protected_pin_err();
bool avoidWrite = false;
if (parser.seenval('M')) {
switch (parser.value_byte()) {
case 0: pinMode(pin, INPUT); break;
case 0: pinMode(pin, INPUT); avoidWrite = true; break;
case 1: pinMode(pin, OUTPUT); break;
case 2: pinMode(pin, INPUT_PULLUP); break;
case 2: pinMode(pin, INPUT_PULLUP); avoidWrite = true; break;
#ifdef INPUT_PULLDOWN
case 3: pinMode(pin, INPUT_PULLDOWN); break;
case 3: pinMode(pin, INPUT_PULLDOWN); avoidWrite = true; break;
#endif
default: SERIAL_ECHOLNPGM("Invalid Pin Mode");
#ifdef INPUT_ANALOG
case 4: pinMode(pin, INPUT_ANALOG); avoidWrite = true; break;
#endif
#ifdef OUTPUT_OPEN_DRAIN
case 5: pinMode(pin, OUTPUT_OPEN_DRAIN); break;
#endif
default: SERIAL_ECHOLNPGM("Invalid Pin Mode"); return;
}
return;
}
if (!parser.seenval('S')) return;
const byte pin_status = parser.value_byte();
#if FAN_COUNT > 0
#if HAS_FAN
switch (pin) {
#if HAS_FAN0
case FAN0_PIN: thermalManager.fan_speed[0] = pin_status; return;
@@ -93,7 +113,23 @@ void GcodeSuite::M42() {
}
#endif
pinMode(pin, OUTPUT);
if (avoidWrite) {
SERIAL_ECHOLNPGM("?Cannot write to INPUT");
return;
}
// An OUTPUT_OPEN_DRAIN should not be changed to normal OUTPUT (STM32)
// Use M42 Px M1/5 S0/1 to set the output type and then set value
#ifndef OUTPUT_OPEN_DRAIN
pinMode(pin, OUTPUT);
#endif
extDigitalWrite(pin, pin_status);
#ifdef ARDUINO_ARCH_STM32
// A simple I/O will be set to 0 by analogWrite()
if (pin_status <= 1 && !PWM_PIN(pin)) return;
#endif
analogWrite(pin, pin_status);
}
#endif // DIRECT_PIN_CONTROL

88
Marlin/src/gcode/control/M605.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -68,42 +68,50 @@
const DualXMode previous_mode = dual_x_carriage_mode;
dual_x_carriage_mode = (DualXMode)parser.value_byte();
mirrored_duplication_mode = false;
if (dual_x_carriage_mode == DXC_MIRRORED_MODE) {
if (previous_mode != DXC_DUPLICATION_MODE) {
SERIAL_ECHOLNPGM("Printer must be in DXC_DUPLICATION_MODE prior to ");
SERIAL_ECHOLNPGM("specifying DXC_MIRRORED_MODE.");
dual_x_carriage_mode = DEFAULT_DUAL_X_CARRIAGE_MODE;
return;
}
mirrored_duplication_mode = true;
stepper.set_directions();
float x_jog = current_position.x - .1;
for (uint8_t i = 2; --i;) {
planner.buffer_line(x_jog, current_position.y, current_position.z, current_position.e, feedrate_mm_s, 0);
x_jog += .1;
}
return;
}
idex_set_mirrored_mode(false);
switch (dual_x_carriage_mode) {
case DXC_FULL_CONTROL_MODE:
case DXC_AUTO_PARK_MODE:
break;
case DXC_DUPLICATION_MODE:
if (parser.seen('X')) duplicate_extruder_x_offset = _MAX(parser.value_linear_units(), X2_MIN_POS - x_home_pos(0));
// Set the X offset, but no less than the safety gap
if (parser.seen('X')) duplicate_extruder_x_offset = _MAX(parser.value_linear_units(), (X2_MIN_POS) - (X1_MIN_POS));
if (parser.seen('R')) duplicate_extruder_temp_offset = parser.value_celsius_diff();
// Always switch back to tool 0
if (active_extruder != 0) tool_change(0);
break;
case DXC_MIRRORED_MODE: {
if (previous_mode != DXC_DUPLICATION_MODE) {
SERIAL_ECHOLNPGM("Printer must be in DXC_DUPLICATION_MODE prior to ");
SERIAL_ECHOLNPGM("specifying DXC_MIRRORED_MODE.");
dual_x_carriage_mode = DEFAULT_DUAL_X_CARRIAGE_MODE;
return;
}
idex_set_mirrored_mode(true);
// Do a small 'jog' motion in the X axis
xyze_pos_t dest = current_position; dest.x -= 0.1f;
for (uint8_t i = 2; --i;) {
planner.buffer_line(dest, feedrate_mm_s, 0);
dest.x += 0.1f;
}
} return;
default:
dual_x_carriage_mode = DEFAULT_DUAL_X_CARRIAGE_MODE;
break;
}
active_extruder_parked = false;
extruder_duplication_enabled = false;
stepper.set_directions();
delayed_move_time = 0;
idex_set_parked(false);
set_duplication_enabled(false);
#ifdef EVENT_GCODE_IDEX_AFTER_MODECHANGE
gcode.process_subcommands_now_P(PSTR(EVENT_GCODE_IDEX_AFTER_MODECHANGE));
#endif
}
else if (!parser.seen('W')) // if no S or W parameter, the DXC mode gets reset to the user's default
dual_x_carriage_mode = DEFAULT_DUAL_X_CARRIAGE_MODE;
@@ -119,26 +127,26 @@
case DXC_DUPLICATION_MODE: DEBUG_ECHOPGM("DUPLICATION"); break;
case DXC_MIRRORED_MODE: DEBUG_ECHOPGM("MIRRORED"); break;
}
DEBUG_ECHOPAIR("\nActive Ext: ", int(active_extruder));
DEBUG_ECHOPGM("\nActive Ext: ", active_extruder);
if (!active_extruder_parked) DEBUG_ECHOPGM(" NOT ");
DEBUG_ECHOPGM(" parked.");
DEBUG_ECHOPAIR("\nactive_extruder_x_pos: ", current_position.x);
DEBUG_ECHOPAIR("\ninactive_extruder_x_pos: ", inactive_extruder_x_pos);
DEBUG_ECHOPAIR("\nextruder_duplication_enabled: ", int(extruder_duplication_enabled));
DEBUG_ECHOPAIR("\nduplicate_extruder_x_offset: ", duplicate_extruder_x_offset);
DEBUG_ECHOPAIR("\nduplicate_extruder_temp_offset: ", duplicate_extruder_temp_offset);
DEBUG_ECHOPAIR("\ndelayed_move_time: ", delayed_move_time);
DEBUG_ECHOPAIR("\nX1 Home X: ", x_home_pos(0), "\nX1_MIN_POS=", int(X1_MIN_POS), "\nX1_MAX_POS=", int(X1_MAX_POS));
DEBUG_ECHOPAIR("\nX2 Home X: ", x_home_pos(1), "\nX2_MIN_POS=", int(X2_MIN_POS), "\nX2_MAX_POS=", int(X2_MAX_POS));
DEBUG_ECHOPAIR("\nX2_HOME_DIR=", int(X2_HOME_DIR), "\nX2_HOME_POS=", int(X2_HOME_POS));
DEBUG_ECHOPAIR("\nDEFAULT_DUAL_X_CARRIAGE_MODE=", STRINGIFY(DEFAULT_DUAL_X_CARRIAGE_MODE));
DEBUG_ECHOPAIR("\toolchange_settings.z_raise=", toolchange_settings.z_raise);
DEBUG_ECHOPAIR("\nDEFAULT_DUPLICATION_X_OFFSET=", int(DEFAULT_DUPLICATION_X_OFFSET));
DEBUG_ECHOPGM("\nactive_extruder_x_pos: ", current_position.x);
DEBUG_ECHOPGM("\ninactive_extruder_x: ", inactive_extruder_x);
DEBUG_ECHOPGM("\nextruder_duplication_enabled: ", extruder_duplication_enabled);
DEBUG_ECHOPGM("\nduplicate_extruder_x_offset: ", duplicate_extruder_x_offset);
DEBUG_ECHOPGM("\nduplicate_extruder_temp_offset: ", duplicate_extruder_temp_offset);
DEBUG_ECHOPGM("\ndelayed_move_time: ", delayed_move_time);
DEBUG_ECHOPGM("\nX1 Home X: ", x_home_pos(0), "\nX1_MIN_POS=", X1_MIN_POS, "\nX1_MAX_POS=", X1_MAX_POS);
DEBUG_ECHOPGM("\nX2 Home X: ", x_home_pos(1), "\nX2_MIN_POS=", X2_MIN_POS, "\nX2_MAX_POS=", X2_MAX_POS);
DEBUG_ECHOPGM("\nX2_HOME_DIR=", X2_HOME_DIR, "\nX2_HOME_POS=", X2_HOME_POS);
DEBUG_ECHOPGM("\nDEFAULT_DUAL_X_CARRIAGE_MODE=", STRINGIFY(DEFAULT_DUAL_X_CARRIAGE_MODE));
DEBUG_ECHOPGM("\toolchange_settings.z_raise=", toolchange_settings.z_raise);
DEBUG_ECHOPGM("\nDEFAULT_DUPLICATION_X_OFFSET=", DEFAULT_DUPLICATION_X_OFFSET);
DEBUG_EOL();
HOTEND_LOOP() {
DEBUG_ECHOPAIR_P(SP_T_STR, int(e));
LOOP_XYZ(a) DEBUG_ECHOPAIR(" hotend_offset[", int(e), "].", XYZ_CHAR(a) | 0x20, "=", hotend_offset[e][a]);
DEBUG_ECHOPGM_P(SP_T_STR, e);
LOOP_LINEAR_AXES(a) DEBUG_ECHOPGM(" hotend_offset[", e, "].", AS_CHAR(AXIS_CHAR(a) | 0x20), "=", hotend_offset[e][a]);
DEBUG_EOL();
}
DEBUG_EOL();
@@ -164,7 +172,7 @@
if (parser.seenval('P')) duplication_e_mask = parser.value_int(); // Set the mask directly
else if (parser.seenval('E')) duplication_e_mask = pow(2, parser.value_int() + 1) - 1; // Set the mask by E index
ena = (2 == parser.intval('S', extruder_duplication_enabled ? 2 : 0));
extruder_duplication_enabled = ena && (duplication_e_mask >= 3);
set_duplication_enabled(ena && (duplication_e_mask >= 3));
}
SERIAL_ECHO_START();
SERIAL_ECHOPGM(STR_DUPLICATION_MODE);

38
Marlin/src/gcode/control/M7-M9.cpp Executable file → Normal file
View File

@@ -16,13 +16,13 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#include "../../inc/MarlinConfigPre.h"
#if ENABLED(COOLANT_CONTROL)
#if ANY(COOLANT_MIST, COOLANT_FLOOD, AIR_ASSIST)
#include "../gcode.h"
#include "../../module/planner.h"
@@ -37,27 +37,41 @@
}
#endif
#if ENABLED(COOLANT_FLOOD)
#if EITHER(COOLANT_FLOOD, AIR_ASSIST)
#if ENABLED(AIR_ASSIST)
#include "../../feature/spindle_laser.h"
#endif
/**
* M8: Flood Coolant On
* M8: Flood Coolant / Air Assist ON
*/
void GcodeSuite::M8() {
planner.synchronize(); // Wait for move to arrive
WRITE(COOLANT_FLOOD_PIN, !(COOLANT_FLOOD_INVERT)); // Turn on Flood coolant
planner.synchronize(); // Wait for move to arrive
#if ENABLED(COOLANT_FLOOD)
WRITE(COOLANT_FLOOD_PIN, !(COOLANT_FLOOD_INVERT)); // Turn on Flood coolant
#endif
#if ENABLED(AIR_ASSIST)
cutter.air_assist_enable(); // Turn on Air Assist
#endif
}
#endif
/**
* M9: Coolant OFF
* M9: Coolant / Air Assist OFF
*/
void GcodeSuite::M9() {
planner.synchronize(); // Wait for move to arrive
planner.synchronize(); // Wait for move to arrive
#if ENABLED(COOLANT_MIST)
WRITE(COOLANT_MIST_PIN, COOLANT_MIST_INVERT); // Turn off Mist coolant
WRITE(COOLANT_MIST_PIN, COOLANT_MIST_INVERT); // Turn off Mist coolant
#endif
#if ENABLED(COOLANT_FLOOD)
WRITE(COOLANT_FLOOD_PIN, COOLANT_FLOOD_INVERT); // Turn off Flood coolant
WRITE(COOLANT_FLOOD_PIN, COOLANT_FLOOD_INVERT); // Turn off Flood coolant
#endif
#if ENABLED(AIR_ASSIST)
cutter.air_assist_disable(); // Turn off Air Assist
#endif
}
#endif // COOLANT_CONTROL
#endif // COOLANT_MIST | COOLANT_FLOOD | AIR_ASSIST

View File

@@ -16,41 +16,36 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "../gcode.h"
#include "../../module/temperature.h"
#include "../../module/stepper.h"
#include "../../module/printcounter.h" // for print_job_timer
#include "../../module/planner.h" // for planner.finish_and_disable
#include "../../module/printcounter.h" // for print_job_timer.stop
#include "../../lcd/marlinui.h" // for LCD_MESSAGEPGM_P
#include "../../inc/MarlinConfig.h"
#if HAS_LCD_MENU
#include "../../lcd/ultralcd.h"
#endif
// PATCH START: Knutwurst
#if ENABLED(ANYCUBIC_TOUCHSCREEN)
#include "../../lcd/anycubic_touchscreen.h"
#endif
// PATCH END: Knutwurst
#if ENABLED(PSU_CONTROL)
#include "../queue.h"
#include "../../feature/power.h"
#endif
#if HAS_SUICIDE
#include "../../MarlinCore.h"
#endif
#if ENABLED(PSU_CONTROL)
#if ENABLED(AUTO_POWER_CONTROL)
#include "../../feature/power.h"
#endif
// Could be moved to a feature, but this is all the data
bool powersupply_on;
#if HAS_TRINAMIC_CONFIG
#include "../../feature/tmc_util.h"
#endif
/**
* M80 : Turn on the Power Supply
* M80 S : Report the current state and exit
@@ -59,11 +54,11 @@
// S: Report the current power supply state and exit
if (parser.seen('S')) {
serialprintPGM(powersupply_on ? PSTR("PS:1\n") : PSTR("PS:0\n"));
SERIAL_ECHOPGM_P(powerManager.psu_on ? PSTR("PS:1\n") : PSTR("PS:0\n"));
return;
}
PSU_ON();
powerManager.power_on();
/**
* If you have a switch on suicide pin, this is useful
@@ -71,25 +66,19 @@
* a print without suicide...
*/
#if HAS_SUICIDE
OUT_WRITE(SUICIDE_PIN, !SUICIDE_PIN_INVERTING);
#endif
#if DISABLED(AUTO_POWER_CONTROL)
delay(PSU_POWERUP_DELAY); // Wait for power to settle
restore_stepper_drivers();
#endif
#if HAS_LCD_MENU
ui.reset_status();
OUT_WRITE(SUICIDE_PIN, !SUICIDE_PIN_STATE);
#endif
// PATCH START: Knutwurst
#ifdef ANYCUBIC_TOUCHSCREEN
AnycubicTouchscreen.CommandScan();
#endif
// PATCH END: Knutwurst
TERN_(HAS_LCD_MENU, ui.reset_status());
}
#endif // ENABLED(PSU_CONTROL)
#endif // PSU_CONTROL
/**
* M81: Turn off Power, including Power Supply, if there is one.
@@ -98,10 +87,11 @@
*/
void GcodeSuite::M81() {
thermalManager.disable_all_heaters();
print_job_timer.stop();
planner.finish_and_disable();
#if FAN_COUNT > 0
print_job_timer.stop();
#if HAS_FAN
thermalManager.zero_fan_speeds();
#if ENABLED(PROBING_FANS_OFF)
thermalManager.fans_paused = false;
@@ -114,10 +104,8 @@ void GcodeSuite::M81() {
#if HAS_SUICIDE
suicide();
#elif ENABLED(PSU_CONTROL)
PSU_OFF();
powerManager.power_off_soon();
#endif
#if HAS_LCD_MENU
LCD_MESSAGEPGM_P(PSTR(MACHINE_NAME " " STR_OFF "."));
#endif
LCD_MESSAGEPGM_P(PSTR(MACHINE_NAME " " STR_OFF "."));
}

3
Marlin/src/gcode/control/M85.cpp Executable file → Normal file
View File

@@ -16,12 +16,11 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "../gcode.h"
#include "../../MarlinCore.h" // for max_inactive_time
/**
* M85: Set inactivity shutdown timer with parameter S<seconds>. To disable set zero (default)

View File

@@ -0,0 +1,88 @@
/**
* 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 ALL(HAS_SPI_FLASH, SDSUPPORT, MARLIN_DEV_MODE)
#include "../gcode.h"
#include "../../sd/cardreader.h"
#include "../../libs/W25Qxx.h"
/**
* M993: Backup SPI Flash to SD
*/
void GcodeSuite::M993() {
if (!card.isMounted()) card.mount();
char fname[] = "spiflash.bin";
card.openFileWrite(fname);
if (!card.isFileOpen()) {
SERIAL_ECHOLNPGM("Failed to open ", fname, " to write.");
return;
}
uint8_t buf[1024];
uint32_t addr = 0;
W25QXX.init(SPI_QUARTER_SPEED);
SERIAL_ECHOPGM("Save SPI Flash");
while (addr < SPI_FLASH_SIZE) {
W25QXX.SPI_FLASH_BufferRead(buf, addr, COUNT(buf));
addr += COUNT(buf);
card.write(buf, COUNT(buf));
if (addr % (COUNT(buf) * 10) == 0) SERIAL_CHAR('.');
}
SERIAL_ECHOLNPGM(" done");
card.closefile();
}
/**
* M994: Load a backup from SD to SPI Flash
*/
void GcodeSuite::M994() {
if (!card.isMounted()) card.mount();
char fname[] = "spiflash.bin";
card.openFileRead(fname);
if (!card.isFileOpen()) {
SERIAL_ECHOLNPGM("Failed to open ", fname, " to read.");
return;
}
uint8_t buf[1024];
uint32_t addr = 0;
W25QXX.init(SPI_QUARTER_SPEED);
W25QXX.SPI_FLASH_BulkErase();
SERIAL_ECHOPGM("Load SPI Flash");
while (addr < SPI_FLASH_SIZE) {
card.read(buf, COUNT(buf));
W25QXX.SPI_FLASH_BufferWrite(buf, addr, COUNT(buf));
addr += COUNT(buf);
if (addr % (COUNT(buf) * 10) == 0) SERIAL_CHAR('.');
}
SERIAL_ECHOLNPGM(" done");
card.closefile();
}
#endif // HAS_SPI_FLASH && SDSUPPORT && MARLIN_DEV_MODE

10
Marlin/src/gcode/control/M997.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -24,11 +24,19 @@
#if ENABLED(PLATFORM_M997_SUPPORT)
#if ENABLED(DWIN_CREALITY_LCD_ENHANCED)
#include "../../lcd/e3v2/enhanced/dwin.h"
#endif
/**
* M997: Perform in-application firmware update
*/
void GcodeSuite::M997() {
TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_RebootScreen());
flashFirmware(parser.intval('S'));
}
#endif

7
Marlin/src/gcode/control/M999.cpp Executable file → Normal file
View File

@@ -16,13 +16,13 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "../gcode.h"
#include "../../lcd/ultralcd.h" // for lcd_reset_alert_level
#include "../../lcd/marlinui.h" // for lcd_reset_alert_level
#include "../../MarlinCore.h" // for marlin_state
#include "../queue.h" // for flush_and_request_resend
@@ -34,7 +34,6 @@
*
* Sending "M999 S1" will resume printing without flushing the
* existing command buffer.
*
*/
void GcodeSuite::M999() {
marlin_state = MF_RUNNING;
@@ -42,5 +41,5 @@ void GcodeSuite::M999() {
if (parser.boolval('S')) return;
queue.flush_and_request_resend();
queue.flush_and_request_resend(queue.ring_buffer.command_port());
}

46
Marlin/src/gcode/control/T.cpp Executable file → Normal file
View File

@@ -16,19 +16,19 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "../gcode.h"
#include "../../module/tool_change.h"
#if ENABLED(DEBUG_LEVELING_FEATURE) || EXTRUDERS > 1
#if EITHER(HAS_MULTI_EXTRUDER, DEBUG_LEVELING_FEATURE)
#include "../../module/motion.h"
#endif
#if ENABLED(PRUSA_MMU2)
#include "../../feature/mmu2/mmu2.h"
#if HAS_PRUSA_MMU2
#include "../../feature/mmu/mmu2.h"
#endif
#define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
@@ -40,41 +40,31 @@
* F[units/min] Set the movement feedrate
* S1 Don't move the tool in XY after change
*
* For PRUSA_MMU2:
* For PRUSA_MMU2(S) and EXTENDABLE_EMU_MMU2(S)
* T[n] Gcode to extrude at least 38.10 mm at feedrate 19.02 mm/s must follow immediately to load to extruder wheels.
* T? Gcode to extrude shouldn't have to follow. Load to extruder wheels is done automatically.
* Tx Same as T?, but nozzle doesn't have to be preheated. Tc requires a preheated nozzle to finish filament load.
* Tc Load to nozzle after filament was prepared by Tc and nozzle is already heated.
*/
void GcodeSuite::T(const uint8_t tool_index) {
void GcodeSuite::T(const int8_t tool_index) {
if (DEBUGGING(LEVELING)) {
DEBUG_ECHOLNPAIR(">>> T(", tool_index, ")");
DEBUG_POS("BEFORE", current_position);
}
DEBUG_SECTION(log_T, "T", DEBUGGING(LEVELING));
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("...(", tool_index, ")");
#if ENABLED(PRUSA_MMU2)
// Count this command as movement / activity
reset_stepper_timeout();
#if HAS_PRUSA_MMU2
if (parser.string_arg) {
mmu2.tool_change(parser.string_arg); // Special commands T?/Tx/Tc
return;
}
#endif
#if EXTRUDERS < 2
tool_change(tool_index);
#else
tool_change(
tool_index,
(tool_index == active_extruder) || parser.boolval('S')
);
#endif
if (DEBUGGING(LEVELING)) {
DEBUG_POS("AFTER", current_position);
DEBUG_ECHOLNPGM("<<< T()");
}
tool_change(tool_index
#if HAS_MULTI_EXTRUDER
, TERN(PARKING_EXTRUDER, false, tool_index == active_extruder) // For PARKING_EXTRUDER motion is decided in tool_change()
|| parser.boolval('S')
#endif
);
}

40
Marlin/src/gcode/eeprom/M500-M504.cpp Executable file → Normal file
View File

@@ -16,12 +16,12 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "../gcode.h"
#include "../../module/configuration_store.h"
#include "../../module/settings.h"
#include "../../core/serial.h"
#include "../../inc/MarlinConfig.h"
@@ -58,11 +58,47 @@ void GcodeSuite::M502() {
#endif // !DISABLE_M503
#if ENABLED(EEPROM_SETTINGS)
#if ENABLED(MARLIN_DEV_MODE)
#include "../../libs/hex_print.h"
#endif
/**
* M504: Validate EEPROM Contents
*/
void GcodeSuite::M504() {
#if ENABLED(MARLIN_DEV_MODE)
const bool dowrite = parser.seenval('W');
if (dowrite || parser.seenval('R')) {
uint8_t val = 0;
int addr = parser.value_ushort();
if (dowrite) {
val = parser.byteval('V');
persistentStore.write_data(addr, &val);
SERIAL_ECHOLNPGM("Wrote address ", addr, " with ", val);
}
else {
if (parser.seenval('T')) {
const int endaddr = parser.value_ushort();
while (addr <= endaddr) {
persistentStore.read_data(addr, &val);
SERIAL_ECHOLNPGM("0x", hex_word(addr), ":", hex_byte(val));
addr++;
safe_delay(10);
}
SERIAL_EOL();
}
else {
persistentStore.read_data(addr, &val);
SERIAL_ECHOLNPGM("Read address ", addr, " and got ", val);
}
}
return;
}
#endif
if (settings.validate())
SERIAL_ECHO_MSG("EEPROM OK");
}
#endif

16
Marlin/src/gcode/feature/L6470/M122.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -41,16 +41,16 @@ inline void L6470_say_status(const L64XX_axis_t axis) {
SERIAL_ECHO(temp_buf);
print_bin(sh.STATUS_AXIS_RAW);
switch (sh.STATUS_AXIS_LAYOUT) {
case L6470_STATUS_LAYOUT: serialprintPGM(PSTR(" L6470")); break;
case L6474_STATUS_LAYOUT: serialprintPGM(PSTR(" L6474")); break;
case L6480_STATUS_LAYOUT: serialprintPGM(PSTR(" L6480/powerSTEP01")); break;
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: ");
serialprintPGM(sh.STATUS_AXIS & STATUS_HIZ ? PSTR("OFF") : PSTR("ON "));
SERIAL_ECHOPGM_P(sh.STATUS_AXIS & STATUS_HIZ ? PSTR("OFF") : PSTR("ON "));
SERIAL_ECHOPGM(" BUSY: "); echo_yes_no((sh.STATUS_AXIS & STATUS_BUSY) == 0);
SERIAL_ECHOPGM(" DIR: ");
serialprintPGM((((sh.STATUS_AXIS & STATUS_DIR) >> 4) ^ L64xxManager.index_to_dir[axis]) ? PSTR("FORWARD") : PSTR("REVERSE"));
SERIAL_ECHOPGM_P((((sh.STATUS_AXIS & STATUS_DIR) >> 4) ^ L64xxManager.index_to_dir[axis]) ? PSTR("FORWARD") : PSTR("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");
@@ -67,8 +67,8 @@ inline void L6470_say_status(const L64XX_axis_t axis) {
SERIAL_ECHOPGM(" Last Command: ");
if (!(sh.STATUS_AXIS & sh.STATUS_AXIS_WRONG_CMD)) SERIAL_ECHOPGM("IN");
SERIAL_ECHOPGM("VALID ");
serialprintPGM(sh.STATUS_AXIS & sh.STATUS_AXIS_NOTPERF_CMD ? PSTR("COMPLETED ") : PSTR("Not PERFORMED"));
SERIAL_ECHOPAIR("\n...THERMAL: ", !(sh.STATUS_AXIS & sh.STATUS_AXIS_TH_SD) ? "SHUTDOWN " : !(sh.STATUS_AXIS & sh.STATUS_AXIS_TH_WRN) ? "WARNING " : "OK ");
SERIAL_ECHOPGM_P(sh.STATUS_AXIS & sh.STATUS_AXIS_NOTPERF_CMD ? PSTR("COMPLETED ") : PSTR("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) {

168
Marlin/src/gcode/feature/L6470/M906.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -32,33 +32,6 @@
#define DEBUG_OUT ENABLED(L6470_CHITCHAT)
#include "../../../core/debug_out.h"
/**
*
* 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
* 0 - (default) all drivers on the axis or E0
* 1 - monitor only X, Y, Z or E1
* 2 - monitor only X2, Y2, Z2 or E2
* 3 - monitor only Z3 or E3
* 4 - monitor only Z4 or E4
* 5 - monitor only E5
* Xxxx, Yxxx, Zxxx, Exxx - axis to change (optional)
* L6474 - current in mA (4A max)
* All others - 0-255
*/
/**
* Sets KVAL_HOLD wich 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.
*
*/
/**
* MACRO to fetch information on the items associated with current limiting
* and maximum voltage output.
@@ -90,7 +63,7 @@ void L64XX_report_current(L64XX &motor, const L64XX_axis_t axis) {
#if ENABLED(L6470_CHITCHAT)
char tmp[10];
sprintf_P(tmp, PSTR("%4x "), status);
DEBUG_ECHOPAIR(" status: ", tmp);
DEBUG_ECHOPGM(" status: ", tmp);
print_bin(status);
#else
UNUSED(status);
@@ -131,13 +104,13 @@ void L64XX_report_current(L64XX &motor, const L64XX_axis_t axis) {
}
SERIAL_EOL();
SERIAL_ECHOPAIR("...MicroSteps: ", MicroSteps,
SERIAL_ECHOPGM("...MicroSteps: ", MicroSteps,
" ADC_OUT: ", L6470_ADC_out);
SERIAL_ECHOPGM(" Vs_compensation: ");
serialprintPGM((motor.GetParam(sh.L6470_AXIS_CONFIG) & CONFIG_EN_VSCOMP) ? PSTR("ENABLED ") : PSTR("DISABLED"));
SERIAL_ECHOLNPAIR(" Compensation coefficient: ~", comp_coef * 0.01f);
SERIAL_ECHOPGM_P((motor.GetParam(sh.L6470_AXIS_CONFIG) & CONFIG_EN_VSCOMP) ? PSTR("ENABLED ") : PSTR("DISABLED"));
SERIAL_ECHOLNPGM(" Compensation coefficient: ~", comp_coef * 0.01f);
SERIAL_ECHOPAIR("...KVAL_HOLD: ", motor.GetParam(L6470_KVAL_HOLD),
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),
@@ -195,7 +168,7 @@ void L64XX_report_current(L64XX &motor, const L64XX_axis_t axis) {
SERIAL_ECHOLNPGM(" mA) Motor Status: NA");
const uint16_t MicroSteps = _BV(motor.GetParam(L6470_STEP_MODE) & 0x07); //NOMORE(MicroSteps, 16);
SERIAL_ECHOPAIR("...MicroSteps: ", MicroSteps,
SERIAL_ECHOPGM("...MicroSteps: ", MicroSteps,
" ADC_OUT: ", L6470_ADC_out);
SERIAL_ECHOLNPGM(" Vs_compensation: NA\n");
@@ -212,7 +185,7 @@ void L64XX_report_current(L64XX &motor, const L64XX_axis_t axis) {
case 1: DEBUG_ECHOLNPGM("75V/uS") ; break;
case 2: DEBUG_ECHOLNPGM("110V/uS") ; break;
case 3: DEBUG_ECHOLNPGM("260V/uS") ; break;
default: DEBUG_ECHOLNPAIR("slew rate: ", (motor.GetParam(sh.L6470_AXIS_CONFIG) & CONFIG_POW_SR) >> CONFIG_POW_SR_BIT); break;
default: DEBUG_ECHOLNPGM("slew rate: ", (motor.GetParam(sh.L6470_AXIS_CONFIG) & CONFIG_POW_SR) >> CONFIG_POW_SR_BIT); break;
}
#endif
SERIAL_EOL();
@@ -222,6 +195,28 @@ void L64XX_report_current(L64XX &motor, const L64XX_axis_t axis) {
}
}
/**
* 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
* 0 - (default) all drivers on the axis or E0
* 1 - monitor only X, Y, Z or E1
* 2 - monitor only X2, Y2, Z2 or E2
* 3 - monitor only Z3 or E3
* 4 - monitor only Z4 or E4
* 5 - monitor only E5
* Xxxx, Yxxx, Zxxx, 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
@@ -236,7 +231,7 @@ void GcodeSuite::M906() {
const uint8_t index = parser.byteval('I');
#endif
LOOP_XYZE(i) if (uint16_t value = parser.intval(axis_codes[i])) {
LOOP_LOGICAL_AXES(i) if (uint16_t value = parser.intval(axis_codes[i])) {
report_current = false;
@@ -254,58 +249,67 @@ void GcodeSuite::M906() {
if (index == 1) L6470_SET_KVAL_HOLD(X2);
#endif
break;
case Y_AXIS:
#if AXIS_IS_L64XX(Y)
if (index == 0) L6470_SET_KVAL_HOLD(Y);
#endif
#if AXIS_IS_L64XX(Y2)
if (index == 1) L6470_SET_KVAL_HOLD(Y2);
#endif
break;
case Z_AXIS:
#if AXIS_IS_L64XX(Z)
if (index == 0) L6470_SET_KVAL_HOLD(Z);
#endif
#if AXIS_IS_L64XX(Z2)
if (index == 1) L6470_SET_KVAL_HOLD(Z2);
#endif
#if AXIS_IS_L64XX(Z3)
if (index == 2) L6470_SET_KVAL_HOLD(Z3);
#endif
#if AXIS_DRIVER_TYPE_Z4(L6470)
if (index == 3) L6470_SET_KVAL_HOLD(Z4);
#endif
break;
case E_AXIS: {
const int8_t target_extruder = get_target_extruder_from_command();
if (target_extruder < 0) return;
switch (target_extruder) {
#if AXIS_IS_L64XX(E0)
case 0: L6470_SET_KVAL_HOLD(E0); break;
#if HAS_Y_AXIS
case Y_AXIS:
#if AXIS_IS_L64XX(Y)
if (index == 0) L6470_SET_KVAL_HOLD(Y);
#endif
#if AXIS_IS_L64XX(E1)
case 1: L6470_SET_KVAL_HOLD(E1); break;
#if AXIS_IS_L64XX(Y2)
if (index == 1) L6470_SET_KVAL_HOLD(Y2);
#endif
#if AXIS_IS_L64XX(E2)
case 2: L6470_SET_KVAL_HOLD(E2); break;
break;
#endif
#if HAS_Z_AXIS
case Z_AXIS:
#if AXIS_IS_L64XX(Z)
if (index == 0) L6470_SET_KVAL_HOLD(Z);
#endif
#if AXIS_IS_L64XX(E3)
case 3: L6470_SET_KVAL_HOLD(E3); break;
#if AXIS_IS_L64XX(Z2)
if (index == 1) L6470_SET_KVAL_HOLD(Z2);
#endif
#if AXIS_IS_L64XX(E4)
case 4: L6470_SET_KVAL_HOLD(E4); break;
#if AXIS_IS_L64XX(Z3)
if (index == 2) L6470_SET_KVAL_HOLD(Z3);
#endif
#if AXIS_IS_L64XX(E5)
case 5: L6470_SET_KVAL_HOLD(E5); break;
#if AXIS_DRIVER_TYPE_Z4(L6470)
if (index == 3) L6470_SET_KVAL_HOLD(Z4);
#endif
#if AXIS_IS_L64XX(E6)
case 6: L6470_SET_KVAL_HOLD(E6); break;
#endif
#if AXIS_IS_L64XX(E7)
case 7: L6470_SET_KVAL_HOLD(E7); break;
#endif
}
} break;
break;
#endif
#if E_STEPPERS
case E_AXIS: {
const int8_t target_e_stepper = get_target_e_stepper_from_command();
if (target_e_stepper < 0) return;
switch (target_e_stepper) {
#if AXIS_IS_L64XX(E0)
case 0: L6470_SET_KVAL_HOLD(E0); break;
#endif
#if AXIS_IS_L64XX(E1)
case 1: L6470_SET_KVAL_HOLD(E1); break;
#endif
#if AXIS_IS_L64XX(E2)
case 2: L6470_SET_KVAL_HOLD(E2); break;
#endif
#if AXIS_IS_L64XX(E3)
case 3: L6470_SET_KVAL_HOLD(E3); break;
#endif
#if AXIS_IS_L64XX(E4)
case 4: L6470_SET_KVAL_HOLD(E4); break;
#endif
#if AXIS_IS_L64XX(E5)
case 5: L6470_SET_KVAL_HOLD(E5); break;
#endif
#if AXIS_IS_L64XX(E6)
case 6: L6470_SET_KVAL_HOLD(E6); break;
#endif
#if AXIS_IS_L64XX(E7)
case 7: L6470_SET_KVAL_HOLD(E7); break;
#endif
}
} break;
#endif
}
}

44
Marlin/src/gcode/feature/L6470/M916-918.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -37,7 +37,6 @@
#include "../../../core/debug_out.h"
/**
*
* M916: increase KVAL_HOLD until get thermal warning
* NOTE - on L6474 it is TVAL that is used
*
@@ -62,7 +61,6 @@
*
* D - time (in seconds) to run each setting of KVAL_HOLD/TVAL
* optional - defaults to zero (runs each setting once)
*
*/
/**
@@ -98,7 +96,7 @@ void GcodeSuite::M916() {
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_ECHOLNPAIR("feedrate = ", final_feedrate);
DEBUG_ECHOLNPGM("feedrate = ", final_feedrate);
planner.synchronize(); // wait for all current movement commands to complete
@@ -121,7 +119,7 @@ void GcodeSuite::M916() {
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 = M91x_delay_s * 60 * 1000;
millis_t M91x_delay_ms = SEC_TO_MS(M91x_delay_s * 60);
millis_t M91x_delay_end;
DEBUG_ECHOLNPGM(".\n.");
@@ -129,9 +127,9 @@ void GcodeSuite::M916() {
do {
if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT)
DEBUG_ECHOLNPAIR("TVAL current (mA) = ", (M91x_counter + 1) * sh.AXIS_STALL_CURRENT_CONSTANT_INV); // report TVAL current for this run
DEBUG_ECHOLNPGM("TVAL current (mA) = ", (M91x_counter + 1) * sh.AXIS_STALL_CURRENT_CONSTANT_INV); // report TVAL current for this run
else
DEBUG_ECHOLNPAIR("kval_hold = ", M91x_counter); // report KVAL_HOLD for this run
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)
@@ -179,7 +177,7 @@ void GcodeSuite::M916() {
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 occured");
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");
@@ -187,7 +185,6 @@ void GcodeSuite::M916() {
}
/**
*
* M917: Find minimum current thresholds
*
* Decrease OCD current until overcurrent error
@@ -214,7 +211,6 @@ void GcodeSuite::M916() {
*
* K - value for KVAL_HOLD (0 - 255) (ignored for L6474)
* optional - will report current value from driver if not specified
*
*/
void GcodeSuite::M917() {
@@ -240,7 +236,7 @@ void GcodeSuite::M917() {
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_ECHOLNPAIR("feedrate = ", final_feedrate);
DEBUG_ECHOLNPGM("feedrate = ", final_feedrate);
planner.synchronize(); // wait for all current movement commands to complete
@@ -256,18 +252,18 @@ void GcodeSuite::M917() {
// 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_ECHOPAIR(".\n.\n.\nover_current threshold : ", (OCD_TH_val + 1) * 375); // first status display
DEBUG_ECHOPAIR(" (OCD_TH: : ", OCD_TH_val);
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_ECHOPAIR(") Stall threshold: ", (STALL_TH_val + 1) * 31.25);
DEBUG_ECHOPAIR(" (STALL_TH: ", STALL_TH_val);
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_ECHOPAIR("STALL threshold : ", (STALL_TH_val + 1) * 31.25);
DEBUG_ECHOLNPAIR(" OCD threshold : ", (OCD_TH_val + 1) * 375);
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));
gcode.process_subcommands_now_P(gcode_string);
@@ -307,13 +303,13 @@ void GcodeSuite::M917() {
if (!(k % 4)) {
kval_hold *= 0.95;
DEBUG_EOL();
DEBUG_ECHOLNPAIR("Lowering KVAL_HOLD by about 5% to ", kval_hold);
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(".");
gcode.reset_stepper_timeout(); // reset_stepper_timeout to keep steppers powered
watchdog_refresh();; // beat the dog
gcode.reset_stepper_timeout(); // keep steppers powered
watchdog_refresh();
safe_delay(5000);
status_composite_temp = 0;
for (j = 0; j < driver_count; j++) {
@@ -522,7 +518,6 @@ void GcodeSuite::M917() {
}
/**
*
* 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
@@ -543,7 +538,6 @@ void GcodeSuite::M917() {
*
* M - value for microsteps (1 - 128) (optional)
* optional - will report current value from driver if not specified
*
*/
void GcodeSuite::M918() {
@@ -596,8 +590,8 @@ void GcodeSuite::M918() {
}
m_steps = L64xxManager.get_param(axis_index[0], L6470_STEP_MODE) & 0x07; // get microsteps
DEBUG_ECHOLNPAIR("Microsteps = ", _BV(m_steps));
DEBUG_ECHOLNPAIR("target (maximum) feedrate = ", final_feedrate);
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
@@ -618,7 +612,7 @@ void GcodeSuite::M918() {
do {
current_feedrate += feedrate_inc;
DEBUG_ECHOLNPAIR("...feedrate = ", current_feedrate);
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));
gcode.process_subcommands_now_P(gcode_string);

27
Marlin/src/gcode/feature/advance/M900.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -43,7 +43,7 @@
void GcodeSuite::M900() {
auto echo_value_oor = [](const char ltr, const bool ten=true) {
SERIAL_CHAR('?'); SERIAL_CHAR(ltr);
SERIAL_CHAR('?', ltr);
SERIAL_ECHOPGM(" value out of range");
if (ten) SERIAL_ECHOPGM(" (0-10)");
SERIAL_ECHOLNPGM(".");
@@ -115,12 +115,12 @@ void GcodeSuite::M900() {
#if ENABLED(EXTRA_LIN_ADVANCE_K)
#if EXTRUDERS < 2
SERIAL_ECHOLNPAIR("Advance S", int(new_slot), " K", kref, "(S", int(!new_slot), " K", lref, ")");
SERIAL_ECHOLNPGM("Advance S", new_slot, " K", kref, "(S", !new_slot, " K", lref, ")");
#else
LOOP_L_N(i, EXTRUDERS) {
const bool slot = TEST(lin_adv_slot, i);
SERIAL_ECHOLNPAIR("Advance T", int(i), " S", int(slot), " K", planner.extruder_advance_K[i],
"(S", int(!slot), " K", other_extruder_advance_K[i], ")");
SERIAL_ECHOLNPGM("Advance T", i, " S", slot, " K", planner.extruder_advance_K[i],
"(S", !slot, " K", other_extruder_advance_K[i], ")");
SERIAL_EOL();
}
#endif
@@ -129,12 +129,12 @@ void GcodeSuite::M900() {
SERIAL_ECHO_START();
#if EXTRUDERS < 2
SERIAL_ECHOLNPAIR("Advance K=", planner.extruder_advance_K[0]);
SERIAL_ECHOLNPGM("Advance K=", planner.extruder_advance_K[0]);
#else
SERIAL_ECHOPGM("Advance K");
LOOP_L_N(i, EXTRUDERS) {
SERIAL_CHAR(' ', '0' + i, ':');
SERIAL_ECHO(planner.extruder_advance_K[i]);
SERIAL_DECIMAL(planner.extruder_advance_K[i]);
}
SERIAL_EOL();
#endif
@@ -144,4 +144,17 @@ void GcodeSuite::M900() {
}
void GcodeSuite::M900_report(const bool forReplay/*=true*/) {
report_heading(forReplay, PSTR(STR_LINEAR_ADVANCE));
#if EXTRUDERS < 2
report_echo_start(forReplay);
SERIAL_ECHOLNPGM(" M900 K", planner.extruder_advance_K[0]);
#else
LOOP_L_N(i, EXTRUDERS) {
report_echo_start(forReplay);
SERIAL_ECHOLNPGM(" M900 T", i, " K", planner.extruder_advance_K[i]);
}
#endif
}
#endif // LIN_ADVANCE

2
Marlin/src/gcode/feature/baricuda/M126-M129.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/

23
Marlin/src/gcode/feature/camera/M240.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -47,7 +47,7 @@
#endif
#ifdef PHOTO_RETRACT_MM
inline void e_move_m240(const float length, const feedRate_t &fr_mm_s) {
inline void e_move_m240(const float length, const_feedRate_t fr_mm_s) {
if (length && thermalManager.hotEnoughToExtrude(active_extruder))
unscaled_e_move(length, fr_mm_s);
}
@@ -100,9 +100,9 @@
* M240: Trigger a camera by...
*
* - CHDK : Emulate a Canon RC-1 with a configurable ON duration.
* http://captain-slow.dk/2014/03/09/3d-printing-timelapses/
* https://captain-slow.dk/2014/03/09/3d-printing-timelapses/
* - PHOTOGRAPH_PIN : Pulse a digital pin 16 times.
* See http://www.doc-diy.net/photo/rc-1_hacked/
* See https://www.doc-diy.net/photo/rc-1_hacked/
* - PHOTO_SWITCH_POSITION : Bump a physical switch with the X-carriage using a
* configured position, delay, and retract length.
*
@@ -126,7 +126,7 @@ void GcodeSuite::M240() {
#ifdef PHOTO_POSITION
if (axis_unhomed_error()) return;
if (homing_needed_error()) return;
const xyz_pos_t old_pos = {
current_position.x + parser.linearval('A'),
@@ -135,17 +135,8 @@ void GcodeSuite::M240() {
};
#ifdef PHOTO_RETRACT_MM
const float rval = parser.seenval('R') ? parser.value_linear_units() : _PHOTO_RETRACT_MM;
feedRate_t sval = (
#if ENABLED(ADVANCED_PAUSE_FEATURE)
PAUSE_PARK_RETRACT_FEEDRATE
#elif ENABLED(FWRETRACT)
RETRACT_FEEDRATE
#else
45
#endif
);
if (parser.seenval('S')) sval = parser.value_feedrate();
const float rval = parser.linearval('R', _PHOTO_RETRACT_MM);
const feedRate_t sval = parser.feedrateval('S', TERN(ADVANCED_PAUSE_FEATURE, PAUSE_PARK_RETRACT_FEEDRATE, TERN(FWRETRACT, RETRACT_FEEDRATE, 45)));
e_move_m240(-rval, sval);
#endif

2
Marlin/src/gcode/feature/cancel/M486.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/

83
Marlin/src/gcode/feature/caselight/M355.cpp Executable file → Normal file
View File

@@ -16,49 +16,58 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "../../gcode.h"
#include "../../../inc/MarlinConfig.h"
#if HAS_CASE_LIGHT
#include "../../../feature/caselight.h"
#if ENABLED(CASE_LIGHT_ENABLE)
/**
* M355: Turn case light on/off and set brightness
*
* P<byte> Set case light brightness (PWM pin required - ignored otherwise)
*
* S<bool> Set case light on/off
*
* When S turns on the light on a PWM pin then the current brightness level is used/restored
*
* M355 P200 S0 turns off the light & sets the brightness level
* M355 S1 turns on the light with a brightness of 200 (assuming a PWM pin)
*/
void GcodeSuite::M355() {
uint8_t args = 0;
#include "../../../feature/caselight.h"
#include "../../gcode.h"
/**
* M355: Turn case light on/off and set brightness
*
* P<byte> Set case light brightness (PWM pin required - ignored otherwise)
*
* S<bool> Set case light on/off
*
* When S turns on the light on a PWM pin then the current brightness level is used/restored
*
* M355 P200 S0 turns off the light & sets the brightness level
* M355 S1 turns on the light with a brightness of 200 (assuming a PWM pin)
*/
void GcodeSuite::M355() {
bool didset = false;
#if CASELIGHT_USES_BRIGHTNESS
if (parser.seenval('P')) {
++args, case_light_brightness = parser.value_byte();
case_light_arg_flag = false;
}
if (parser.seenval('S')) {
++args, case_light_on = parser.value_bool();
case_light_arg_flag = true;
}
if (args) update_case_light();
// always report case light status
SERIAL_ECHO_START();
if (!case_light_on) {
SERIAL_ECHOLNPGM("Case light: off");
}
else {
if (!PWM_PIN(CASE_LIGHT_PIN)) SERIAL_ECHOLNPGM("Case light: on");
else SERIAL_ECHOLNPAIR("Case light: ", case_light_brightness);
didset = true;
caselight.brightness = parser.value_byte();
}
#endif
const bool sflag = parser.seenval('S');
if (sflag) {
didset = true;
caselight.on = parser.value_bool();
}
#endif // HAS_CASE_LIGHT
if (didset) caselight.update(sflag);
// Always report case light status
SERIAL_ECHO_START();
SERIAL_ECHOPGM("Case light: ");
if (!caselight.on)
SERIAL_ECHOLNPGM(STR_OFF);
else {
#if CASELIGHT_USES_BRIGHTNESS
if (TERN(CASE_LIGHT_USE_NEOPIXEL, true, TERN0(NEED_CASE_LIGHT_PIN, PWM_PIN(CASE_LIGHT_PIN)))) {
SERIAL_ECHOLN(int(caselight.brightness));
return;
}
#endif
SERIAL_ECHOLNPGM(STR_ON);
}
}
#endif // CASE_LIGHT_ENABLE

27
Marlin/src/gcode/feature/clean/G12.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -37,22 +37,33 @@
/**
* G12: Clean the nozzle
*
* E<bool> : 0=Never or 1=Always apply the "software endstop" limits
* P0 S<strokes> : Stroke cleaning with S strokes
* P1 Sn T<objects> : Zigzag cleaning with S repeats and T zigzags
* P2 Sn R<radius> : Circle cleaning with S repeats and R radius
* X, Y, Z : Specify axes to move during cleaning. Default: ALL.
*/
void GcodeSuite::G12() {
// Don't allow nozzle cleaning without homing first
if (axis_unhomed_error()) return;
if (homing_needed_error()) return;
#ifdef WIPE_SEQUENCE_COMMANDS
if (!parser.seen_any()) {
gcode.process_subcommands_now_P(PSTR(WIPE_SEQUENCE_COMMANDS));
return;
}
#endif
const uint8_t pattern = parser.ushortval('P', 0),
strokes = parser.ushortval('S', NOZZLE_CLEAN_STROKES),
objects = parser.ushortval('T', NOZZLE_CLEAN_TRIANGLES);
const float radius = parser.floatval('R', NOZZLE_CLEAN_CIRCLE_RADIUS);
const float radius = parser.linearval('R', NOZZLE_CLEAN_CIRCLE_RADIUS);
const bool seenxyz = parser.seen("XYZ");
const uint8_t cleans = (!seenxyz || parser.boolval('X') ? _BV(X_AXIS) : 0)
| (!seenxyz || parser.boolval('Y') ? _BV(Y_AXIS) : 0)
#if DISABLED(NOZZLE_CLEAN_NO_Z)
| (!seenxyz || parser.boolval('Z') ? _BV(Z_AXIS) : 0)
#endif
| TERN(NOZZLE_CLEAN_NO_Z, 0, (!seenxyz || parser.boolval('Z') ? _BV(Z_AXIS) : 0))
;
#if HAS_LEVELING
@@ -60,7 +71,11 @@ void GcodeSuite::G12() {
TEMPORARY_BED_LEVELING_STATE(!TEST(cleans, Z_AXIS) && planner.leveling_active);
#endif
SET_SOFT_ENDSTOP_LOOSE(!parser.boolval('E'));
nozzle.clean(pattern, strokes, radius, objects, cleans);
SET_SOFT_ENDSTOP_LOOSE(false);
}
#endif // NOZZLE_CLEAN_FEATURE

32
Marlin/src/gcode/feature/controllerfan/M710.cpp Executable file → Normal file
View File

@@ -1,9 +1,9 @@
/**
* Marlin 3D Printer Firmware
* Copyright (C) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
* 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
@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -27,18 +27,6 @@
#include "../../gcode.h"
#include "../../../feature/controllerfan.h"
void M710_report(const bool forReplay) {
if (!forReplay) { SERIAL_ECHOLNPGM("; Controller Fan"); SERIAL_ECHO_START(); }
SERIAL_ECHOLNPAIR("M710 "
"S", int(controllerFan.settings.active_speed),
"I", int(controllerFan.settings.idle_speed),
"A", int(controllerFan.settings.auto_mode),
"D", controllerFan.settings.duration,
" ; (", (int(controllerFan.settings.active_speed) * 100) / 255, "%"
" ", (int(controllerFan.settings.idle_speed) * 100) / 255, "%)"
);
}
/**
* M710: Set controller fan settings
*
@@ -75,7 +63,19 @@ void GcodeSuite::M710() {
if (seenD) controllerFan.settings.duration = parser.value_ushort();
if (!(seenR || seenS || seenI || seenA || seenD))
M710_report(false);
M710_report();
}
void GcodeSuite::M710_report(const bool forReplay/*=true*/) {
report_heading_etc(forReplay, PSTR(STR_CONTROLLER_FAN));
SERIAL_ECHOLNPGM(" M710"
" S", int(controllerFan.settings.active_speed),
" I", int(controllerFan.settings.idle_speed),
" A", int(controllerFan.settings.auto_mode),
" D", controllerFan.settings.duration,
" ; (", (int(controllerFan.settings.active_speed) * 100) / 255, "%"
" ", (int(controllerFan.settings.idle_speed) * 100) / 255, "%)"
);
}
#endif // CONTROLLER_FAN_EDITABLE

100
Marlin/src/gcode/feature/digipot/M907-M910.cpp Executable file → Normal file
View File

@@ -16,25 +16,25 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if HAS_DIGIPOTSS || HAS_MOTOR_CURRENT_PWM || EITHER(DIGIPOT_I2C, DAC_STEPPER_CURRENT)
#if HAS_MOTOR_CURRENT_SPI || HAS_MOTOR_CURRENT_PWM || HAS_MOTOR_CURRENT_I2C || HAS_MOTOR_CURRENT_DAC
#include "../../gcode.h"
#if HAS_DIGIPOTSS || HAS_MOTOR_CURRENT_PWM
#if HAS_MOTOR_CURRENT_SPI || HAS_MOTOR_CURRENT_PWM
#include "../../../module/stepper.h"
#endif
#if ENABLED(DIGIPOT_I2C)
#if HAS_MOTOR_CURRENT_I2C
#include "../../../feature/digipot/digipot.h"
#endif
#if ENABLED(DAC_STEPPER_CURRENT)
#if HAS_MOTOR_CURRENT_DAC
#include "../../../feature/dac/stepper_dac.h"
#endif
@@ -42,65 +42,101 @@
* M907: Set digital trimpot motor current using axis codes X, Y, Z, E, B, S
*/
void GcodeSuite::M907() {
#if HAS_DIGIPOTSS
#if HAS_MOTOR_CURRENT_SPI
LOOP_XYZE(i) if (parser.seenval(axis_codes[i])) stepper.digipot_current(i, parser.value_int());
if (parser.seenval('B')) stepper.digipot_current(4, parser.value_int());
if (parser.seenval('S')) LOOP_LE_N(i, 4) stepper.digipot_current(i, parser.value_int());
if (!parser.seen("BS" LOGICAL_AXES_STRING))
return M907_report();
LOOP_LOGICAL_AXES(i) if (parser.seenval(axis_codes[i])) stepper.set_digipot_current(i, parser.value_int());
if (parser.seenval('B')) stepper.set_digipot_current(4, parser.value_int());
if (parser.seenval('S')) LOOP_LE_N(i, 4) stepper.set_digipot_current(i, parser.value_int());
#elif HAS_MOTOR_CURRENT_PWM
if (!parser.seen(
#if ANY_PIN(MOTOR_CURRENT_PWM_X, MOTOR_CURRENT_PWM_Y, MOTOR_CURRENT_PWM_XY)
"XY"
#endif
#if PIN_EXISTS(MOTOR_CURRENT_PWM_Z)
"Z"
#endif
#if PIN_EXISTS(MOTOR_CURRENT_PWM_E)
"E"
#endif
)) return M907_report();
#if ANY_PIN(MOTOR_CURRENT_PWM_X, MOTOR_CURRENT_PWM_Y, MOTOR_CURRENT_PWM_XY)
if (parser.seenval('X') || parser.seenval('Y')) stepper.digipot_current(0, parser.value_int());
if (parser.seenval('X') || parser.seenval('Y')) stepper.set_digipot_current(0, parser.value_int());
#endif
#if PIN_EXISTS(MOTOR_CURRENT_PWM_Z)
if (parser.seenval('Z')) stepper.digipot_current(1, parser.value_int());
if (parser.seenval('Z')) stepper.set_digipot_current(1, parser.value_int());
#endif
#if PIN_EXISTS(MOTOR_CURRENT_PWM_E)
if (parser.seenval('E')) stepper.digipot_current(2, parser.value_int());
if (parser.seenval('E')) stepper.set_digipot_current(2, parser.value_int());
#endif
#endif
#if ENABLED(DIGIPOT_I2C)
#if HAS_MOTOR_CURRENT_I2C
// this one uses actual amps in floating point
LOOP_XYZE(i) if (parser.seenval(axis_codes[i])) digipot_i2c_set_current(i, parser.value_float());
LOOP_LOGICAL_AXES(i) if (parser.seenval(axis_codes[i])) digipot_i2c.set_current(i, parser.value_float());
// Additional extruders use B,C,D for channels 4,5,6.
// TODO: Change these parameters because 'E' is used. B<index>?
for (uint8_t i = E_AXIS + 1; i < DIGIPOT_I2C_NUM_CHANNELS; i++)
if (parser.seenval('B' + i - (E_AXIS + 1))) digipot_i2c_set_current(i, parser.value_float());
#if HAS_EXTRUDERS
for (uint8_t i = E_AXIS + 1; i < DIGIPOT_I2C_NUM_CHANNELS; i++)
if (parser.seenval('B' + i - (E_AXIS + 1))) digipot_i2c.set_current(i, parser.value_float());
#endif
#endif
#if ENABLED(DAC_STEPPER_CURRENT)
#if HAS_MOTOR_CURRENT_DAC
if (parser.seenval('S')) {
const float dac_percent = parser.value_float();
LOOP_LE_N(i, 4) dac_current_percent(i, dac_percent);
LOOP_LE_N(i, 4) stepper_dac.set_current_percent(i, dac_percent);
}
LOOP_XYZE(i) if (parser.seenval(axis_codes[i])) dac_current_percent(i, parser.value_float());
LOOP_LOGICAL_AXES(i) if (parser.seenval(axis_codes[i])) stepper_dac.set_current_percent(i, parser.value_float());
#endif
}
#if HAS_DIGIPOTSS || ENABLED(DAC_STEPPER_CURRENT)
#if HAS_MOTOR_CURRENT_SPI || HAS_MOTOR_CURRENT_PWM
void GcodeSuite::M907_report(const bool forReplay/*=true*/) {
report_heading_etc(forReplay, PSTR(STR_STEPPER_MOTOR_CURRENTS));
#if HAS_MOTOR_CURRENT_PWM
SERIAL_ECHOLNPGM_P( // PWM-based has 3 values:
PSTR(" M907 X"), stepper.motor_current_setting[0] // X and Y
, SP_Z_STR, stepper.motor_current_setting[1] // Z
, SP_E_STR, stepper.motor_current_setting[2] // E
);
#elif HAS_MOTOR_CURRENT_SPI
SERIAL_ECHOPGM(" M907"); // SPI-based has 5 values:
LOOP_LOGICAL_AXES(q) { // X Y Z (I J K) E (map to X Y Z (I J K) E0 by default)
SERIAL_CHAR(' ', axis_codes[q]);
SERIAL_ECHO(stepper.motor_current_setting[q]);
}
SERIAL_CHAR(' ', 'B'); // B (maps to E1 by default)
SERIAL_ECHOLN(stepper.motor_current_setting[4]);
#endif
}
#endif // HAS_MOTOR_CURRENT_SPI || HAS_MOTOR_CURRENT_PWM
#if HAS_MOTOR_CURRENT_SPI || HAS_MOTOR_CURRENT_DAC
/**
* M908: Control digital trimpot directly (M908 P<pin> S<current>)
*/
void GcodeSuite::M908() {
#if HAS_DIGIPOTSS
stepper.digitalPotWrite(parser.intval('P'), parser.intval('S'));
#endif
#if ENABLED(DAC_STEPPER_CURRENT)
dac_current_raw(parser.byteval('P', -1), parser.ushortval('S', 0));
#endif
TERN_(HAS_MOTOR_CURRENT_SPI, stepper.set_digipot_value_spi(parser.intval('P'), parser.intval('S')));
TERN_(HAS_MOTOR_CURRENT_DAC, stepper_dac.set_current_value(parser.byteval('P', -1), parser.ushortval('S', 0)));
}
#endif // HAS_DIGIPOTSS || DAC_STEPPER_CURRENT
#if HAS_MOTOR_CURRENT_DAC
#if ENABLED(DAC_STEPPER_CURRENT)
void GcodeSuite::M909() { stepper_dac.print_values(); }
void GcodeSuite::M910() { stepper_dac.commit_eeprom(); }
void GcodeSuite::M909() { dac_print_values(); }
void GcodeSuite::M910() { dac_commit_eeprom(); }
#endif // HAS_MOTOR_CURRENT_DAC
#endif // DAC_STEPPER_CURRENT
#endif // HAS_MOTOR_CURRENT_SPI || HAS_MOTOR_CURRENT_DAC
#endif // HAS_DIGIPOTSS || DAC_STEPPER_CURRENT || HAS_MOTOR_CURRENT_PWM || DIGIPOT_I2C
#endif // HAS_MOTOR_CURRENT_SPI || HAS_MOTOR_CURRENT_PWM || HAS_MOTOR_CURRENT_I2C || HAS_MOTOR_CURRENT_DAC

7
Marlin/src/gcode/feature/filwidth/M404-M407.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -26,7 +26,6 @@
#include "../../../feature/filwidth.h"
#include "../../../module/planner.h"
#include "../../../module/temperature.h"
#include "../../../MarlinCore.h"
#include "../../gcode.h"
@@ -39,7 +38,7 @@ void GcodeSuite::M404() {
planner.volumetric_area_nominal = CIRCLE_AREA(filwidth.nominal_mm * 0.5);
}
else
SERIAL_ECHOLNPAIR("Filament dia (nominal mm):", filwidth.nominal_mm);
SERIAL_ECHOLNPGM("Filament dia (nominal mm):", filwidth.nominal_mm);
}
/**
@@ -66,7 +65,7 @@ void GcodeSuite::M406() {
* M407: Get measured filament diameter on serial output
*/
void GcodeSuite::M407() {
SERIAL_ECHOLNPAIR("Filament dia (measured mm):", filwidth.measured_mm);
SERIAL_ECHOLNPGM("Filament dia (measured mm):", filwidth.measured_mm);
}
#endif // FILAMENT_WIDTH_SENSOR

13
Marlin/src/gcode/feature/fwretract/G10_G11.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -32,16 +32,7 @@
* G10 - Retract filament according to settings of M207
* TODO: Handle 'G10 P' for tool settings and 'G10 L' for workspace settings
*/
void GcodeSuite::G10() {
#if EXTRUDERS > 1
const bool rs = parser.boolval('S');
#endif
fwretract.retract(true
#if EXTRUDERS > 1
, rs
#endif
);
}
void GcodeSuite::G10() { fwretract.retract(true E_OPTARG(parser.boolval('S'))); }
/**
* G11 - Recover filament according to settings of M208

37
Marlin/src/gcode/feature/fwretract/M207-M209.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -35,11 +35,11 @@
* F[units/min] retract_feedrate_mm_s
* Z[units] retract_zraise
*/
void GcodeSuite::M207() {
if (parser.seen('S')) fwretract.settings.retract_length = parser.value_axis_units(E_AXIS);
if (parser.seen('F')) fwretract.settings.retract_feedrate_mm_s = MMM_TO_MMS(parser.value_axis_units(E_AXIS));
if (parser.seen('Z')) fwretract.settings.retract_zraise = parser.value_linear_units();
if (parser.seen('W')) fwretract.settings.swap_retract_length = parser.value_axis_units(E_AXIS);
void GcodeSuite::M207() { fwretract.M207(); }
void GcodeSuite::M207_report(const bool forReplay/*=true*/) {
report_heading_etc(forReplay, PSTR(STR_RETRACT_S_F_Z));
fwretract.M207_report();
}
/**
@@ -50,25 +50,28 @@ void GcodeSuite::M207() {
* F[units/min] retract_recover_feedrate_mm_s
* R[units/min] swap_retract_recover_feedrate_mm_s
*/
void GcodeSuite::M208() {
if (parser.seen('S')) fwretract.settings.retract_recover_extra = parser.value_axis_units(E_AXIS);
if (parser.seen('F')) fwretract.settings.retract_recover_feedrate_mm_s = MMM_TO_MMS(parser.value_axis_units(E_AXIS));
if (parser.seen('R')) fwretract.settings.swap_retract_recover_feedrate_mm_s = MMM_TO_MMS(parser.value_axis_units(E_AXIS));
if (parser.seen('W')) fwretract.settings.swap_retract_recover_extra = parser.value_axis_units(E_AXIS);
void GcodeSuite::M208() { fwretract.M208(); }
void GcodeSuite::M208_report(const bool forReplay/*=true*/) {
report_heading_etc(forReplay, PSTR(STR_RECOVER_S_F));
fwretract.M208_report();
}
#if ENABLED(FWRETRACT_AUTORETRACT)
/**
* M209: Enable automatic retract (M209 S1)
* For slicers that don't support G10/11, reversed extrude-only
* moves will be classified as retraction.
*
* For slicers that don't support G10/11, reversed
* extruder-only moves can be classified as retraction.
*/
void GcodeSuite::M209() {
if (MIN_AUTORETRACT <= MAX_AUTORETRACT && parser.seen('S'))
fwretract.enable_autoretract(parser.value_bool());
void GcodeSuite::M209() { fwretract.M209(); }
void GcodeSuite::M209_report(const bool forReplay/*=true*/) {
report_heading_etc(forReplay, PSTR(STR_AUTO_RETRACT_S));
fwretract.M209_report();
}
#endif // FWRETRACT_AUTORETRACT
#endif
#endif // FWRETRACT

7
Marlin/src/gcode/feature/i2c/M260_M261.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -26,12 +26,12 @@
#include "../../gcode.h"
#include "../../../MarlinCore.h" // for i2c
#include "../../../feature/twibus.h"
/**
* M260: Send data to a I2C slave device
*
* This is a PoC, the formating and arguments for the GCODE will
* This is a PoC, the formatting and arguments for the GCODE will
* change to be more compatible, the current proposal is:
*
* M260 A<slave device address base 10> ; Sets the I2C slave address the data will be sent to
@@ -42,7 +42,6 @@
*
* M260 S1 ; Send the buffered data and reset the buffer
* M260 R1 ; Reset the buffer without sending data
*
*/
void GcodeSuite::M260() {
// Set the target address

46
Marlin/src/gcode/feature/leds/M150.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -34,6 +34,12 @@
* Always sets all 3 or 4 components. If a component is left out, set to 0.
* If brightness is left out, no value changed
*
* With NEOPIXEL_LED:
* I<index> Set the NeoPixel index to affect. Default: All
*
* With NEOPIXEL2_SEPARATE:
* S<index> The NeoPixel strip to set. Default is index 0.
*
* Examples:
*
* M150 R255 ; Turn LED red
@@ -43,15 +49,43 @@
* M150 W ; Turn LED white using a white LED
* M150 P127 ; Set LED 50% brightness
* M150 P ; Set LED full brightness
* M150 I1 R ; Set NEOPIXEL index 1 to red
* M150 S1 I1 R ; Set SEPARATE index 1 to red
*/
void GcodeSuite::M150() {
leds.set_color(MakeLEDColor(
#if ENABLED(NEOPIXEL_LED)
const int8_t index = parser.intval('I', -1);
#if ENABLED(NEOPIXEL2_SEPARATE)
int8_t brightness = neo.brightness(), unit = parser.intval('S', -1);
switch (unit) {
case -1: neo2.neoindex = index; // fall-thru
case 0: neo.neoindex = index; break;
case 1: neo2.neoindex = index; brightness = neo2.brightness(); break;
}
#else
const uint8_t brightness = neo.brightness();
neo.neoindex = index;
#endif
#endif
const LEDColor color = LEDColor(
parser.seen('R') ? (parser.has_value() ? parser.value_byte() : 255) : 0,
parser.seen('U') ? (parser.has_value() ? parser.value_byte() : 255) : 0,
parser.seen('B') ? (parser.has_value() ? parser.value_byte() : 255) : 0,
parser.seen('W') ? (parser.has_value() ? parser.value_byte() : 255) : 0,
parser.seen('P') ? (parser.has_value() ? parser.value_byte() : 255) : neo.brightness()
));
parser.seen('B') ? (parser.has_value() ? parser.value_byte() : 255) : 0
OPTARG(HAS_WHITE_LED, parser.seen('W') ? (parser.has_value() ? parser.value_byte() : 255) : 0)
OPTARG(NEOPIXEL_LED, parser.seen('P') ? (parser.has_value() ? parser.value_byte() : 255) : brightness)
);
#if ENABLED(NEOPIXEL2_SEPARATE)
switch (unit) {
case 0: leds.set_color(color); return;
case 1: leds2.set_color(color); return;
}
#endif
// If 'S' is not specified use both
leds.set_color(color);
TERN_(NEOPIXEL2_SEPARATE, leds2.set_color(color));
}
#endif // HAS_COLOR_LEDS

4
Marlin/src/gcode/feature/leds/M7219.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -82,7 +82,7 @@ void GcodeSuite::M7219() {
LOOP_L_N(r, MAX7219_LINES) {
SERIAL_ECHOPGM("led_line[");
if (r < 10) SERIAL_CHAR(' ');
SERIAL_ECHO(int(r));
SERIAL_ECHO(r);
SERIAL_ECHOPGM("]=");
for (uint8_t b = 8; b--;) SERIAL_CHAR('0' + TEST(max7219.led_line[r], b));
SERIAL_EOL();

2
Marlin/src/gcode/feature/macro/M810-M819.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/

2
Marlin/src/gcode/feature/mixing/M163-M165.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/

19
Marlin/src/gcode/feature/mixing/M166.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -30,12 +30,12 @@
#include "../../../feature/mixing.h"
inline void echo_mix() {
SERIAL_ECHOPAIR(" (", int(mixer.mix[0]), "%|", int(mixer.mix[1]), "%)");
SERIAL_ECHOPGM(" (", mixer.mix[0], "%|", mixer.mix[1], "%)");
}
inline void echo_zt(const int t, const float &z) {
inline void echo_zt(const int t, const_float_t z) {
mixer.update_mix_from_vtool(t);
SERIAL_ECHOPAIR_P(SP_Z_STR, z, SP_T_STR, t);
SERIAL_ECHOPGM_P(SP_Z_STR, z, SP_T_STR, t);
echo_mix();
}
@@ -74,7 +74,7 @@ void GcodeSuite::M166() {
#if ENABLED(GRADIENT_VTOOL)
if (mixer.gradient.vtool_index >= 0) {
SERIAL_ECHOPAIR(" (T", int(mixer.gradient.vtool_index));
SERIAL_ECHOPGM(" (T", mixer.gradient.vtool_index);
SERIAL_CHAR(')');
}
#endif
@@ -86,7 +86,14 @@ void GcodeSuite::M166() {
echo_zt(mixer.gradient.end_vtool, mixer.gradient.end_z);
mixer.update_mix_from_gradient();
SERIAL_ECHOPAIR(" ; Current Z", planner.get_axis_position_mm(Z_AXIS));
SERIAL_ECHOPGM(" ; Current Z");
#if ENABLED(DELTA)
get_cartesian_from_steppers();
SERIAL_ECHO(cartes.z);
#else
SERIAL_ECHO(planner.get_axis_position_mm(Z_AXIS));
#endif
echo_mix();
}

View File

@@ -0,0 +1,132 @@
/**
* 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/MarlinConfigPre.h"
#if HAS_ETHERNET
#include "../../../feature/ethernet.h"
#include "../../../core/serial.h"
#include "../../gcode.h"
void say_ethernet() { SERIAL_ECHOPGM(" Ethernet "); }
void ETH0_report() {
say_ethernet();
SERIAL_ECHO_TERNARY(ethernet.hardware_enabled, "port ", "en", "dis", "abled.\n");
if (ethernet.hardware_enabled) {
say_ethernet();
SERIAL_ECHO_TERNARY(ethernet.have_telnet_client, "client ", "en", "dis", "abled.\n");
}
else
SERIAL_ECHOLNPGM("Send 'M552 S1' to enable.");
}
void MAC_report() {
uint8_t mac[6];
if (ethernet.hardware_enabled) {
Ethernet.MACAddress(mac);
SERIAL_ECHOPGM(" MAC: ");
LOOP_L_N(i, 6) {
if (mac[i] < 16) SERIAL_CHAR('0');
SERIAL_PRINT(mac[i], PrintBase::Hex);
if (i < 5) SERIAL_CHAR(':');
}
}
SERIAL_EOL();
}
// Display current values when the link is active,
// otherwise show the stored values
void ip_report(const uint16_t cmd, PGM_P const post, const IPAddress &ipo) {
SERIAL_CHAR('M'); SERIAL_ECHO(cmd); SERIAL_CHAR(' ');
LOOP_L_N(i, 4) {
SERIAL_ECHO(ipo[i]);
if (i < 3) SERIAL_CHAR('.');
}
SERIAL_ECHOPGM(" ; ");
SERIAL_ECHOLNPGM_P(post);
}
/**
* M552: Set IP address, enable/disable network interface
*
* S0 : disable networking
* S1 : enable networking
* S-1 : reset network interface
*
* Pnnn : Set IP address, 0.0.0.0 means acquire an IP address using DHCP
*/
void GcodeSuite::M552() {
const bool seenP = parser.seenval('P');
if (seenP) ethernet.ip.fromString(parser.value_string());
const bool seenS = parser.seenval('S');
if (seenS) {
switch (parser.value_int()) {
case -1:
if (ethernet.telnetClient) ethernet.telnetClient.stop();
ethernet.init();
break;
case 0: ethernet.hardware_enabled = false; break;
case 1: ethernet.hardware_enabled = true; break;
default: break;
}
}
const bool nopar = !seenS && !seenP;
if (nopar || seenS) ETH0_report();
if (nopar || seenP) M552_report();
}
void GcodeSuite::M552_report() {
ip_report(552, PSTR("ip address"), Ethernet.linkStatus() == LinkON ? Ethernet.localIP() : ethernet.ip);
}
/**
* M553 Pnnn - Set netmask
*/
void GcodeSuite::M553() {
if (parser.seenval('P'))
ethernet.subnet.fromString(parser.value_string());
else
M553_report();
}
void GcodeSuite::M553_report() {
ip_report(553, PSTR("subnet mask"), Ethernet.linkStatus() == LinkON ? Ethernet.subnetMask() : ethernet.subnet);
}
/**
* M554 Pnnn - Set Gateway
*/
void GcodeSuite::M554() {
if (parser.seenval('P'))
ethernet.gateway.fromString(parser.value_string());
else
M554_report();
}
void GcodeSuite::M554_report() {
ip_report(554, PSTR("gateway"), Ethernet.linkStatus() == LinkON ? Ethernet.gatewayIP() : ethernet.gateway);
}
#endif // HAS_ETHERNET

View File

@@ -0,0 +1,83 @@
/**
* 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/MarlinConfigPre.h"
#if ENABLED(PASSWORD_FEATURE)
#include "../../../feature/password/password.h"
#include "../../../core/serial.h"
#include "../../gcode.h"
//
// M510: Lock Printer
//
void GcodeSuite::M510() {
password.lock_machine();
}
//
// M511: Unlock Printer
//
#if ENABLED(PASSWORD_UNLOCK_GCODE)
void GcodeSuite::M511() {
if (password.is_locked) {
password.value_entry = parser.ulongval('P');
password.authentication_check();
}
}
#endif // PASSWORD_UNLOCK_GCODE
//
// M512: Set/Change/Remove Password
//
#if ENABLED(PASSWORD_CHANGE_GCODE)
void GcodeSuite::M512() {
if (password.is_set && parser.ulongval('P') != password.value) {
SERIAL_ECHOLNPGM(STR_WRONG_PASSWORD);
return;
}
if (parser.seenval('S')) {
password.value_entry = parser.ulongval('S');
if (password.value_entry < CAT(1e, PASSWORD_LENGTH)) {
password.is_set = true;
password.value = password.value_entry;
SERIAL_ECHOLNPGM(STR_PASSWORD_SET, password.value); // TODO: Update password.string
}
else
SERIAL_ECHOLNPGM(STR_PASSWORD_TOO_LONG);
}
else {
password.is_set = false;
SERIAL_ECHOLNPGM(STR_PASSWORD_REMOVED);
}
SERIAL_ECHOLNPGM(STR_REMINDER_SAVE_SETTINGS);
}
#endif // PASSWORD_CHANGE_GCODE
#endif // PASSWORD_FEATURE

5
Marlin/src/gcode/feature/pause/G27.cpp Executable file → Normal file
View File

@@ -16,11 +16,10 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfig.h"
#if ENABLED(NOZZLE_PARK_FEATURE)
@@ -34,7 +33,7 @@
*/
void GcodeSuite::G27() {
// Don't allow nozzle parking without homing first
if (axis_unhomed_error()) return;
if (homing_needed_error()) return;
nozzle.park(parser.ushortval('P'));
}

13
Marlin/src/gcode/feature/pause/G60.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -24,7 +24,6 @@
#if SAVED_POSITIONS
#include "../../../core/language.h"
#include "../../gcode.h"
#include "../../../module/motion.h"
@@ -48,11 +47,13 @@ void GcodeSuite::G60() {
SBI(saved_slots[slot >> 3], slot & 0x07);
#if ENABLED(SAVED_POSITIONS_DEBUG)
DEBUG_ECHOPGM(STR_SAVED_POS " S", slot);
const xyze_pos_t &pos = stored_position[slot];
DEBUG_ECHOPAIR_F(STR_SAVED_POS " S", slot);
DEBUG_ECHOPAIR_F(" : X", pos.x);
DEBUG_ECHOPAIR_F_P(SP_Y_STR, pos.y);
DEBUG_ECHOLNPAIR_F_P(SP_Z_STR, pos.z);
DEBUG_ECHOLNPAIR_F_P(
LIST_N(DOUBLE(LOGICAL_AXES), SP_E_STR, pos.e,
PSTR(" : X"), pos.x, SP_Y_STR, pos.y, SP_Z_STR, pos.z,
SP_I_STR, pos.i, SP_J_STR, pos.j, SP_K_STR, pos.k)
);
#endif
}

52
Marlin/src/gcode/feature/pause/G61.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -24,22 +24,29 @@
#if SAVED_POSITIONS
#include "../../../core/language.h"
#include "../../../module/planner.h"
#include "../../gcode.h"
#include "../../../module/motion.h"
#include "../../../module/planner.h"
#define DEBUG_OUT ENABLED(SAVED_POSITIONS_DEBUG)
#include "../../../core/debug_out.h"
/**
* G61: Return to saved position
*
* F<rate> - Feedrate (optional) for the move back.
* S<slot> - Slot # (0-based) to restore from (default 0).
* X Y Z - Axes to restore. At least one is required.
* X Y Z E - Axes to restore. At least one is required.
*
* If XYZE are not given, default restore uses the smart blocking move.
*/
void GcodeSuite::G61(void) {
const uint8_t slot = parser.byteval('S');
#define SYNC_E(POINT) TERN_(HAS_EXTRUDERS, planner.set_e_position_mm((destination.e = current_position.e = (POINT))))
#if SAVED_POSITIONS < 256
if (slot >= SAVED_POSITIONS) {
SERIAL_ERROR_MSG(STR_INVALID_POS_SLOT STRINGIFY(SAVED_POSITIONS));
@@ -48,24 +55,41 @@ void GcodeSuite::G61(void) {
#endif
// No saved position? No axes being restored?
if (!TEST(saved_slots[slot >> 3], slot & 0x07) || !parser.seen("XYZ")) return;
if (!TEST(saved_slots[slot >> 3], slot & 0x07)) return;
// Apply any given feedrate over 0.0
feedRate_t saved_feedrate = feedrate_mm_s;
const float fr = parser.linearval('F');
if (fr > 0.0) feedrate_mm_s = MMM_TO_MMS(fr);
SERIAL_ECHOPAIR(STR_RESTORING_POS " S", int(slot));
LOOP_XYZ(i) {
destination[i] = parser.seen(XYZ_CHAR(i))
? stored_position[slot][i] + parser.value_axis_units((AxisEnum)i)
: current_position[i];
SERIAL_CHAR(' ', XYZ_CHAR(i));
SERIAL_ECHO_F(destination[i]);
if (!parser.seen_axis()) {
DEBUG_ECHOLNPGM("Default position restore");
do_blocking_move_to(stored_position[slot], feedrate_mm_s);
SYNC_E(stored_position[slot].e);
}
else {
if (parser.seen(LINEAR_AXIS_GANG("X", "Y", "Z", AXIS4_STR, AXIS5_STR, AXIS6_STR))) {
DEBUG_ECHOPGM(STR_RESTORING_POS " S", slot);
LOOP_LINEAR_AXES(i) {
destination[i] = parser.seen(AXIS_CHAR(i))
? stored_position[slot][i] + parser.value_axis_units((AxisEnum)i)
: current_position[i];
DEBUG_CHAR(' ', AXIS_CHAR(i));
DEBUG_ECHO_F(destination[i]);
}
DEBUG_EOL();
// Move to the saved position
prepare_line_to_destination();
}
#if HAS_EXTRUDERS
if (parser.seen_test('E')) {
DEBUG_ECHOLNPGM(STR_RESTORING_POS " S", slot, " E", current_position.e, "=>", stored_position[slot].e);
SYNC_E(stored_position[slot].e);
}
#endif
}
SERIAL_EOL();
// Move to the saved position
prepare_line_to_destination();
feedrate_mm_s = saved_feedrate;
}
#endif // SAVED_POSITIONS

51
Marlin/src/gcode/feature/pause/M125.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -27,13 +27,10 @@
#include "../../gcode.h"
#include "../../parser.h"
#include "../../../feature/pause.h"
#include "../../../lcd/marlinui.h"
#include "../../../module/motion.h"
#include "../../../sd/cardreader.h"
#include "../../../module/printcounter.h"
#if HAS_LCD_MENU
#include "../../../lcd/ultralcd.h"
#endif
#include "../../../sd/cardreader.h"
#if ENABLED(POWER_LOSS_RECOVERY)
#include "../../../feature/powerloss.h"
@@ -49,18 +46,17 @@
* position and waits, resuming with a button click or M108.
* Without PARK_HEAD_ON_PAUSE the M125 command does nothing.
*
* L = override retract length
* X = override X
* Y = override Y
* Z = override Z raise
* L<linear> = Override retract Length
* X<pos> = Override park position X
* Y<pos> = Override park position Y
* Z<linear> = Override Z raise
*
* With an LCD menu:
* P<bool> = Always show a prompt and await a response
*/
void GcodeSuite::M125() {
// Initial retract before move to filament change position
const float retract = -ABS(parser.seen('L') ? parser.value_axis_units(E_AXIS) : 0
#ifdef PAUSE_PARK_RETRACT_LENGTH
+ (PAUSE_PARK_RETRACT_LENGTH)
#endif
);
const float retract = TERN0(HAS_EXTRUDERS, -ABS(parser.axisunitsval('L', E_AXIS, PAUSE_PARK_RETRACT_LENGTH)));
xyz_pos_t park_point = NOZZLE_PARK_POINT;
@@ -75,26 +71,17 @@ void GcodeSuite::M125() {
park_point += hotend_offset[active_extruder];
#endif
#if ENABLED(SDSUPPORT)
const bool sd_printing = IS_SD_PRINTING();
#else
constexpr bool sd_printing = false;
#endif
const bool sd_printing = TERN0(SDSUPPORT, IS_SD_PRINTING());
#if HAS_LCD_MENU
lcd_pause_show_message(PAUSE_MESSAGE_PAUSING, PAUSE_MODE_PAUSE_PRINT);
const bool show_lcd = parser.seenval('P');
#else
constexpr bool show_lcd = false;
#endif
ui.pause_show_message(PAUSE_MESSAGE_PARKING, PAUSE_MODE_PAUSE_PRINT);
if (pause_print(retract, park_point, 0, show_lcd)) {
#if ENABLED(POWER_LOSS_RECOVERY)
if (recovery.enabled) recovery.save(true);
#endif
if (!sd_printing || show_lcd) {
// If possible, show an LCD prompt with the 'P' flag
const bool show_lcd = TERN0(HAS_LCD_MENU, parser.boolval('P'));
if (pause_print(retract, park_point, show_lcd, 0)) {
if (ENABLED(EXTENSIBLE_UI) || BOTH(EMERGENCY_PARSER, HOST_PROMPT_SUPPORT) || !sd_printing || show_lcd) {
wait_for_confirmation(false, 0);
resume_print(0, 0, PAUSE_PARK_RETRACT_LENGTH, 0);
resume_print(0, 0, -retract, 0);
}
}
}

View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -28,26 +28,29 @@
#include "../../../feature/pause.h"
#include "../../../module/motion.h"
#include "../../../module/printcounter.h"
#include "../../../lcd/marlinui.h"
// PATCH START: Knutwurst
#ifdef ANYCUBIC_TOUCHSCREEN
#include "../../../lcd/anycubic_touchscreen.h"
#include "../../../sd/cardreader.h"
#include "../../../lcd/anycubic_touchscreen.h"
#include "../../../sd/cardreader.h"
#endif
// PATCH END: Knutwurst
#if EXTRUDERS > 1
#include "../../../module/tool_change.h"
#endif
#if HAS_LCD_MENU
#include "../../../lcd/ultralcd.h"
#if HAS_MULTI_EXTRUDER
#include "../../../module/tool_change.h"
#endif
#if ENABLED(MMU2_MENUS)
#include "../../../lcd/menu/menu_mmu2.h"
#include "../../../lcd/menu/menu_mmu2.h"
#endif
#if ENABLED(MIXING_EXTRUDER)
#include "../../../feature/mixing.h"
#include "../../../feature/mixing.h"
#endif
#if HAS_FILAMENT_SENSOR
#include "../../../feature/runout.h"
#endif
/**
@@ -61,160 +64,134 @@
* L[distance] - Extrude distance for insertion (manual reload)
* B[count] - Number of times to beep, -1 for indefinite (if equipped with a buzzer)
* T[toolhead] - Select extruder for filament change
* R[temp] - Resume temperature (in current units)
*
* Default values are used for omitted arguments.
*/
void GcodeSuite::M600()
{
void GcodeSuite::M600() {
// PATCH START: Knutwurst
#ifdef ANYCUBIC_TOUCHSCREEN
#ifdef SDSUPPORT
if (card.isPrinting()) { // are we printing from sd?
if (AnycubicTouchscreen.ai3m_pause_state < 2) {
AnycubicTouchscreen.ai3m_pause_state = 2;
#ifdef ANYCUBIC_TFT_DEBUG
SERIAL_ECHOPAIR(" DEBUG: M600 - AI3M Pause State set to: ", AnycubicTouchscreen.ai3m_pause_state);
SERIAL_EOL();
#endif
}
#ifdef ANYCUBIC_TFT_DEBUG
SERIAL_ECHOLNPGM("DEBUG: Enter M600 TFTstate routine");
#endif
AnycubicTouchscreen.TFTstate = ANYCUBIC_TFT_STATE_SDPAUSE_REQ; // enter correct display state to show resume button
#ifdef ANYCUBIC_TFT_DEBUG
SERIAL_ECHOLNPGM("DEBUG: Set TFTstate to SDPAUSE_REQ");
#endif
}
#endif
#endif
// PATCH END: Knutwurst
#ifdef ANYCUBIC_TOUCHSCREEN
#ifdef SDSUPPORT
if (card.isPrinting())
{ // are we printing from sd?
if (AnycubicTouchscreen.ai3m_pause_state < 2)
{
AnycubicTouchscreen.ai3m_pause_state = 2;
#ifdef ANYCUBIC_TFT_DEBUG
SERIAL_ECHOPAIR(" DEBUG: M600 - AI3M Pause State set to: ", AnycubicTouchscreen.ai3m_pause_state);
SERIAL_EOL();
#endif
#if ENABLED(MIXING_EXTRUDER)
const int8_t target_e_stepper = get_target_e_stepper_from_command();
if (target_e_stepper < 0) return;
const uint8_t old_mixing_tool = mixer.get_current_vtool();
mixer.T(MIXER_DIRECT_SET_TOOL);
MIXER_STEPPER_LOOP(i) mixer.set_collector(i, i == uint8_t(target_e_stepper) ? 1.0 : 0.0);
mixer.normalize();
const int8_t target_extruder = active_extruder;
#else
const int8_t target_extruder = get_target_extruder_from_command();
if (target_extruder < 0) return;
#endif
#if ENABLED(DUAL_X_CARRIAGE)
int8_t DXC_ext = target_extruder;
if (!parser.seen_test('T')) { // If no tool index is specified, M600 was (probably) sent in response to filament runout.
// In this case, for duplicating modes set DXC_ext to the extruder that ran out.
#if MULTI_FILAMENT_SENSOR
if (idex_is_duplicating())
DXC_ext = (READ(FIL_RUNOUT2_PIN) == FIL_RUNOUT2_STATE) ? 1 : 0;
#else
DXC_ext = active_extruder;
#endif
}
#ifdef ANYCUBIC_TFT_DEBUG
SERIAL_ECHOLNPGM("DEBUG: Enter M600 TFTstate routine");
#endif
AnycubicTouchscreen.TFTstate = ANYCUBIC_TFT_STATE_SDPAUSE_REQ; // enter correct display state to show resume button
#ifdef ANYCUBIC_TFT_DEBUG
SERIAL_ECHOLNPGM("DEBUG: Set TFTstate to SDPAUSE_REQ");
#endif
}
#endif
#endif
#endif
xyz_pos_t park_point = NOZZLE_PARK_POINT;
// Show initial "wait for start" message
#if DISABLED(MMU2_MENUS)
ui.pause_show_message(PAUSE_MESSAGE_CHANGING, PAUSE_MODE_PAUSE_PRINT, target_extruder);
#endif
#if ENABLED(MIXING_EXTRUDER)
const int8_t target_e_stepper = get_target_e_stepper_from_command();
if (target_e_stepper < 0)
return;
#if ENABLED(HOME_BEFORE_FILAMENT_CHANGE)
// If needed, home before parking for filament change
home_if_needed(true);
#endif
const uint8_t old_mixing_tool = mixer.get_current_vtool();
mixer.T(MIXER_DIRECT_SET_TOOL);
MIXER_STEPPER_LOOP(i)
mixer.set_collector(i, i == uint8_t(target_e_stepper) ? 1.0 : 0.0);
mixer.normalize();
const int8_t target_extruder = active_extruder;
#else
const int8_t target_extruder = get_target_extruder_from_command();
if (target_extruder < 0)
return;
#endif
#if ENABLED(DUAL_X_CARRIAGE)
int8_t DXC_ext = target_extruder;
if (!parser.seen('T'))
{ // If no tool index is specified, M600 was (probably) sent in response to filament runout.
// In this case, for duplicating modes set DXC_ext to the extruder that ran out.
#if HAS_FILAMENT_SENSOR && NUM_RUNOUT_SENSORS > 1
if (dxc_is_duplicating())
DXC_ext = (READ(FIL_RUNOUT2_PIN) == FIL_RUNOUT_INVERTING) ? 1 : 0;
#else
DXC_ext = active_extruder;
#endif
}
#endif
// Show initial "wait for start" message
#if HAS_LCD_MENU && DISABLED(MMU2_MENUS)
lcd_pause_show_message(PAUSE_MESSAGE_CHANGING, PAUSE_MODE_PAUSE_PRINT, target_extruder);
#endif
#if ENABLED(HOME_BEFORE_FILAMENT_CHANGE)
// Don't allow filament change without homing first
if (axes_need_homing())
home_all_axes();
#endif
#if EXTRUDERS > 1
// Change toolhead if specified
const uint8_t active_extruder_before_filament_change = active_extruder;
if (
active_extruder != target_extruder
#if ENABLED(DUAL_X_CARRIAGE)
&& dual_x_carriage_mode != DXC_DUPLICATION_MODE && dual_x_carriage_mode != DXC_MIRRORED_MODE
#endif
)
tool_change(target_extruder, false);
#endif
#if HAS_MULTI_EXTRUDER
// Change toolhead if specified
const uint8_t active_extruder_before_filament_change = active_extruder;
if (active_extruder != target_extruder && TERN1(DUAL_X_CARRIAGE, !idex_is_duplicating()))
tool_change(target_extruder, false);
#endif
// Initial retract before move to filament change position
const float retract = -ABS(parser.seen('E') ? parser.value_axis_units(E_AXIS) : 0
#ifdef PAUSE_PARK_RETRACT_LENGTH
+ (PAUSE_PARK_RETRACT_LENGTH)
#endif
);
const float retract = -ABS(parser.axisunitsval('E', E_AXIS, PAUSE_PARK_RETRACT_LENGTH));
xyz_pos_t park_point NOZZLE_PARK_POINT;
// Lift Z axis
if (parser.seenval('Z'))
park_point.z = parser.linearval('Z');
if (parser.seenval('Z')) park_point.z = parser.linearval('Z');
// Move XY axes to filament change position or given position
if (parser.seenval('X'))
park_point.x = parser.linearval('X');
if (parser.seenval('Y'))
park_point.y = parser.linearval('Y');
if (parser.seenval('X')) park_point.x = parser.linearval('X');
if (parser.seenval('Y')) park_point.y = parser.linearval('Y');
#if HAS_HOTEND_OFFSET && NONE(DUAL_X_CARRIAGE, DELTA)
park_point += hotend_offset[active_extruder];
#endif
#if HAS_HOTEND_OFFSET && NONE(DUAL_X_CARRIAGE, DELTA)
park_point += hotend_offset[active_extruder];
#endif
#if ENABLED(MMU2_MENUS)
// For MMU2 reset retract and load/unload values so they don't mess with MMU filament handling
constexpr float unload_length = 0.5f,
slow_load_length = 0.0f,
fast_load_length = 0.0f;
#else
// Unload filament
const float unload_length = -ABS(parser.seen('U') ? parser.value_axis_units(E_AXIS)
: fc_settings[active_extruder].unload_length);
#if ENABLED(MMU2_MENUS)
// For MMU2 reset retract and load/unload values so they don't mess with MMU filament handling
constexpr float unload_length = 0.5f,
slow_load_length = 0.0f,
fast_load_length = 0.0f;
#else
// Unload filament
const float unload_length = -ABS(parser.axisunitsval('U', E_AXIS, fc_settings[active_extruder].unload_length));
// Slow load filament
constexpr float slow_load_length = FILAMENT_CHANGE_SLOW_LOAD_LENGTH;
// Fast load filament
const float fast_load_length = ABS(parser.axisunitsval('L', E_AXIS, fc_settings[active_extruder].load_length));
#endif
// Slow load filament
constexpr float slow_load_length = FILAMENT_CHANGE_SLOW_LOAD_LENGTH;
// Fast load filament
const float fast_load_length = ABS(parser.seen('L') ? parser.value_axis_units(E_AXIS)
: fc_settings[active_extruder].load_length);
#endif
const int beep_count = parser.intval('B',
#ifdef FILAMENT_CHANGE_ALERT_BEEPS
FILAMENT_CHANGE_ALERT_BEEPS
#else
-1
#endif
const int beep_count = parser.intval('B', -1
#ifdef FILAMENT_CHANGE_ALERT_BEEPS
+ 1 + FILAMENT_CHANGE_ALERT_BEEPS
#endif
);
if (pause_print(retract, park_point, unload_length, true DXC_PASS))
{
#if ENABLED(MMU2_MENUS)
mmu2_M600();
resume_print(slow_load_length, fast_load_length, 0, beep_count DXC_PASS);
#else
wait_for_confirmation(true, beep_count DXC_PASS);
resume_print(slow_load_length, fast_load_length, ADVANCED_PAUSE_PURGE_LENGTH, beep_count DXC_PASS);
#endif
if (pause_print(retract, park_point, true, unload_length DXC_PASS)) {
#if ENABLED(MMU2_MENUS)
mmu2_M600();
resume_print(slow_load_length, fast_load_length, 0, beep_count DXC_PASS);
#else
wait_for_confirmation(true, beep_count DXC_PASS);
resume_print(slow_load_length, fast_load_length, ADVANCED_PAUSE_PURGE_LENGTH,
beep_count, (parser.seenval('R') ? parser.value_celsius() : 0) DXC_PASS);
#endif
}
#if EXTRUDERS > 1
// Restore toolhead if it was changed
if (active_extruder_before_filament_change != active_extruder)
tool_change(active_extruder_before_filament_change, false);
#endif
#if HAS_MULTI_EXTRUDER
// Restore toolhead if it was changed
if (active_extruder_before_filament_change != active_extruder)
tool_change(active_extruder_before_filament_change, false);
#endif
#if ENABLED(MIXING_EXTRUDER)
mixer.T(old_mixing_tool); // Restore original mixing tool
#endif
TERN_(MIXING_EXTRUDER, mixer.T(old_mixing_tool)); // Restore original mixing tool
}
#endif // ADVANCED_PAUSE_FEATURE

23
Marlin/src/gcode/feature/pause/M603.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -29,7 +29,7 @@
#include "../../../module/motion.h"
#include "../../../module/printcounter.h"
#if EXTRUDERS > 1
#if HAS_MULTI_EXTRUDER
#include "../../../module/tool_change.h"
#endif
@@ -39,10 +39,11 @@
* T[toolhead] - Select extruder to configure, active extruder if not specified
* U[distance] - Retract distance for removal, for the specified extruder
* L[distance] - Extrude distance for insertion, for the specified extruder
*
*/
void GcodeSuite::M603() {
if (!parser.seen("TUL")) return M603_report();
const int8_t target_extruder = get_target_extruder_from_command();
if (target_extruder < 0) return;
@@ -63,4 +64,20 @@ void GcodeSuite::M603() {
}
}
void GcodeSuite::M603_report(const bool forReplay/*=true*/) {
report_heading(forReplay, PSTR(STR_FILAMENT_LOAD_UNLOAD));
#if EXTRUDERS == 1
report_echo_start(forReplay);
SERIAL_ECHOPGM(" M603 L", LINEAR_UNIT(fc_settings[0].load_length), " U", LINEAR_UNIT(fc_settings[0].unload_length), " ;");
say_units();
#else
LOOP_L_N(e, EXTRUDERS) {
report_echo_start(forReplay);
SERIAL_ECHOPGM(" M603 T", e, " L", LINEAR_UNIT(fc_settings[e].load_length), " U", LINEAR_UNIT(fc_settings[e].unload_length), " ;");
say_units();
}
#endif
}
#endif // ADVANCED_PAUSE_FEATURE

94
Marlin/src/gcode/feature/pause/M701_M702.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -29,17 +29,14 @@
#include "../../../module/motion.h"
#include "../../../module/temperature.h"
#include "../../../feature/pause.h"
#include "../../../lcd/marlinui.h"
#if EXTRUDERS > 1
#if HAS_MULTI_EXTRUDER
#include "../../../module/tool_change.h"
#endif
#if HAS_LCD_MENU
#include "../../../lcd/ultralcd.h"
#endif
#if ENABLED(PRUSA_MMU2)
#include "../../../feature/mmu2/mmu2.h"
#if HAS_PRUSA_MMU2
#include "../../../feature/mmu/mmu2.h"
#endif
#if ENABLED(MIXING_EXTRUDER)
@@ -59,10 +56,8 @@
void GcodeSuite::M701() {
xyz_pos_t park_point = NOZZLE_PARK_POINT;
#if ENABLED(NO_MOTION_BEFORE_HOMING)
// Don't raise Z if the machine isn't homed
if (axes_need_homing()) park_point.z = 0;
#endif
// Don't raise Z if the machine isn't homed
if (TERN0(NO_MOTION_BEFORE_HOMING, axes_should_home())) park_point.z = 0;
#if ENABLED(MIXING_EXTRUDER)
const int8_t target_e_stepper = get_target_e_stepper_from_command();
@@ -84,23 +79,29 @@ void GcodeSuite::M701() {
if (parser.seenval('Z')) park_point.z = parser.linearval('Z');
// Show initial "wait for load" message
#if HAS_LCD_MENU
lcd_pause_show_message(PAUSE_MESSAGE_LOAD, PAUSE_MODE_LOAD_FILAMENT, target_extruder);
#endif
ui.pause_show_message(PAUSE_MESSAGE_LOAD, PAUSE_MODE_LOAD_FILAMENT, target_extruder);
#if EXTRUDERS > 1 && DISABLED(PRUSA_MMU2)
#if HAS_MULTI_EXTRUDER && (HAS_PRUSA_MMU1 || !HAS_MMU)
// Change toolhead if specified
uint8_t active_extruder_before_filament_change = active_extruder;
if (active_extruder != target_extruder)
tool_change(target_extruder, false);
#endif
// Lift Z axis
if (park_point.z > 0)
do_blocking_move_to_z(_MIN(current_position.z + park_point.z, Z_MAX_POS), feedRate_t(NOZZLE_PARK_Z_FEEDRATE));
auto move_z_by = [](const_float_t zdist) {
if (zdist) {
destination = current_position;
destination.z += zdist;
prepare_internal_move_to_destination(NOZZLE_PARK_Z_FEEDRATE);
}
};
// Raise the Z axis (with max limit)
const float park_raise = _MIN(park_point.z, (Z_MAX_POS) - current_position.z);
move_z_by(park_raise);
// Load filament
#if ENABLED(PRUSA_MMU2)
#if HAS_PRUSA_MMU2
mmu2.load_filament_to_nozzle(target_extruder);
#else
constexpr float purge_length = ADVANCED_PAUSE_PURGE_LENGTH,
@@ -113,30 +114,23 @@ void GcodeSuite::M701() {
true, // show_lcd
thermalManager.still_heating(target_extruder), // pause_for_user
PAUSE_MODE_LOAD_FILAMENT // pause_mode
#if ENABLED(DUAL_X_CARRIAGE)
, target_extruder // Dual X target
#endif
OPTARG(DUAL_X_CARRIAGE, target_extruder) // Dual X target
);
#endif
// Restore Z axis
if (park_point.z > 0)
do_blocking_move_to_z(_MAX(current_position.z - park_point.z, 0), feedRate_t(NOZZLE_PARK_Z_FEEDRATE));
move_z_by(-park_raise);
#if EXTRUDERS > 1 && DISABLED(PRUSA_MMU2)
#if HAS_MULTI_EXTRUDER && (HAS_PRUSA_MMU1 || !HAS_MMU)
// Restore toolhead if it was changed
if (active_extruder_before_filament_change != active_extruder)
tool_change(active_extruder_before_filament_change, false);
#endif
#if ENABLED(MIXING_EXTRUDER)
mixer.T(old_mixing_tool); // Restore original mixing tool
#endif
TERN_(MIXING_EXTRUDER, mixer.T(old_mixing_tool)); // Restore original mixing tool
// Show status screen
#if HAS_LCD_MENU
lcd_pause_show_message(PAUSE_MESSAGE_STATUS);
#endif
ui.pause_show_message(PAUSE_MESSAGE_STATUS);
}
/**
@@ -153,26 +147,26 @@ void GcodeSuite::M701() {
void GcodeSuite::M702() {
xyz_pos_t park_point = NOZZLE_PARK_POINT;
#if ENABLED(NO_MOTION_BEFORE_HOMING)
// Don't raise Z if the machine isn't homed
if (axes_need_homing()) park_point.z = 0;
#endif
// Don't raise Z if the machine isn't homed
if (TERN0(NO_MOTION_BEFORE_HOMING, axes_should_home())) park_point.z = 0;
#if ENABLED(MIXING_EXTRUDER)
const uint8_t old_mixing_tool = mixer.get_current_vtool();
#if ENABLED(FILAMENT_UNLOAD_ALL_EXTRUDERS)
float mix_multiplier = 1.0;
if (!parser.seenval('T')) {
const bool seenT = parser.seenval('T');
if (!seenT) {
mixer.T(MIXER_AUTORETRACT_TOOL);
mix_multiplier = MIXING_STEPPERS;
}
else
#else
constexpr bool seenT = true;
#endif
{
if (seenT) {
const int8_t target_e_stepper = get_target_e_stepper_from_command();
if (target_e_stepper < 0) return;
mixer.T(MIXER_DIRECT_SET_TOOL);
MIXER_STEPPER_LOOP(i) mixer.set_collector(i, (i == (uint8_t)target_e_stepper) ? 1.0 : 0.0);
mixer.normalize();
@@ -188,11 +182,9 @@ void GcodeSuite::M702() {
if (parser.seenval('Z')) park_point.z = parser.linearval('Z');
// Show initial "wait for unload" message
#if HAS_LCD_MENU
lcd_pause_show_message(PAUSE_MESSAGE_UNLOAD, PAUSE_MODE_UNLOAD_FILAMENT, target_extruder);
#endif
ui.pause_show_message(PAUSE_MESSAGE_UNLOAD, PAUSE_MODE_UNLOAD_FILAMENT, target_extruder);
#if EXTRUDERS > 1 && DISABLED(PRUSA_MMU2)
#if HAS_MULTI_EXTRUDER && (HAS_PRUSA_MMU1 || !HAS_MMU)
// Change toolhead if specified
uint8_t active_extruder_before_filament_change = active_extruder;
if (active_extruder != target_extruder)
@@ -204,10 +196,10 @@ void GcodeSuite::M702() {
do_blocking_move_to_z(_MIN(current_position.z + park_point.z, Z_MAX_POS), feedRate_t(NOZZLE_PARK_Z_FEEDRATE));
// Unload filament
#if ENABLED(PRUSA_MMU2)
#if HAS_PRUSA_MMU2
mmu2.unload();
#else
#if EXTRUDERS > 1 && ENABLED(FILAMENT_UNLOAD_ALL_EXTRUDERS)
#if BOTH(HAS_MULTI_EXTRUDER, FILAMENT_UNLOAD_ALL_EXTRUDERS)
if (!parser.seenval('T')) {
HOTEND_LOOP() {
if (e != active_extruder) tool_change(e, false);
@@ -233,20 +225,16 @@ void GcodeSuite::M702() {
if (park_point.z > 0)
do_blocking_move_to_z(_MAX(current_position.z - park_point.z, 0), feedRate_t(NOZZLE_PARK_Z_FEEDRATE));
#if EXTRUDERS > 1 && DISABLED(PRUSA_MMU2)
#if HAS_MULTI_EXTRUDER && (HAS_PRUSA_MMU1 || !HAS_MMU)
// Restore toolhead if it was changed
if (active_extruder_before_filament_change != active_extruder)
tool_change(active_extruder_before_filament_change, false);
#endif
#if ENABLED(MIXING_EXTRUDER)
mixer.T(old_mixing_tool); // Restore original mixing tool
#endif
TERN_(MIXING_EXTRUDER, mixer.T(old_mixing_tool)); // Restore original mixing tool
// Show status screen
#if HAS_LCD_MENU
lcd_pause_show_message(PAUSE_MESSAGE_STATUS);
#endif
ui.pause_show_message(PAUSE_MESSAGE_STATUS);
}
#endif // ADVANCED_PAUSE_FEATURE

View File

@@ -0,0 +1,70 @@
/**
* 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_POWER_MONITOR
#include "../../../feature/power_monitor.h"
#include "../../../MarlinCore.h"
#include "../../gcode.h"
/**
* M430: Enable/disable current LCD display
* With no parameters report the system current draw (in Amps)
*
* I[bool] - Set Display of current on the LCD
* V[bool] - Set Display of voltage on the LCD
* W[bool] - Set Display of power on the LCD
*/
void GcodeSuite::M430() {
bool do_report = true;
#if HAS_WIRED_LCD
#if ENABLED(POWER_MONITOR_CURRENT)
if (parser.seen('I')) { power_monitor.set_current_display(parser.value_bool()); do_report = false; }
#endif
#if ENABLED(POWER_MONITOR_VOLTAGE)
if (parser.seen('V')) { power_monitor.set_voltage_display(parser.value_bool()); do_report = false; }
#endif
#if HAS_POWER_MONITOR_WATTS
if (parser.seen('W')) { power_monitor.set_power_display(parser.value_bool()); do_report = false; }
#endif
#endif
if (do_report) {
SERIAL_ECHOLNPGM(
#if ENABLED(POWER_MONITOR_CURRENT)
"Current: ", power_monitor.getAmps(), "A"
#if ENABLED(POWER_MONITOR_VOLTAGE)
" "
#endif
#endif
#if ENABLED(POWER_MONITOR_VOLTAGE)
"Voltage: ", power_monitor.getVolts(), "V"
#endif
#if HAS_POWER_MONITOR_WATTS
" Power: ", power_monitor.getPower(), "W"
#endif
);
}
}
#endif // HAS_POWER_MONITOR

27
Marlin/src/gcode/feature/powerloss/M1000.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -27,9 +27,16 @@
#include "../../gcode.h"
#include "../../../feature/powerloss.h"
#include "../../../module/motion.h"
#include "../../../lcd/ultralcd.h"
#include "../../../lcd/marlinui.h"
#if ENABLED(EXTENSIBLE_UI)
#include "../../../lcd/extui/ui_api.h"
#elif ENABLED(DWIN_CREALITY_LCD)
#include "../../../lcd/e3v2/creality/dwin.h"
#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED)
#include "../../../lcd/e3v2/enhanced/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)
@@ -40,8 +47,8 @@ void menu_job_recovery();
inline void plr_error(PGM_P const prefix) {
#if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
DEBUG_ECHO_START();
serialprintPGM(prefix);
DEBUG_ECHOLNPGM(" Power-Loss Recovery Data");
DEBUG_ECHOPGM_P(prefix);
DEBUG_ECHOLNPGM(" Job Recovery Data");
#else
UNUSED(prefix);
#endif
@@ -59,24 +66,26 @@ inline void plr_error(PGM_P const prefix) {
void GcodeSuite::M1000() {
if (recovery.valid()) {
if (parser.seen('S')) {
if (parser.seen_test('S')) {
#if HAS_LCD_MENU
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
SERIAL_ECHO_MSG("Resume requires LCD.");
#endif
}
else if (parser.seen('C')) {
else if (parser.seen_test('C')) {
#if HAS_LCD_MENU
lcd_power_loss_recovery_cancel();
#else
recovery.cancel();
#endif
#if ENABLED(EXTENSIBLE_UI)
ExtUI::onPrintTimerStopped();
#endif
TERN_(EXTENSIBLE_UI, ExtUI::onPrintTimerStopped());
}
else
recovery.resume();

29
Marlin/src/gcode/feature/powerloss/M413.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -27,7 +27,7 @@
#include "../../gcode.h"
#include "../../../feature/powerloss.h"
#include "../../../module/motion.h"
#include "../../../lcd/ultralcd.h"
#include "../../../lcd/marlinui.h"
/**
* M413: Enable / Disable power-loss recovery
@@ -40,19 +40,26 @@ void GcodeSuite::M413() {
if (parser.seen('S'))
recovery.enable(parser.value_bool());
else {
SERIAL_ECHO_START();
SERIAL_ECHOPGM("Power-loss recovery ");
serialprintln_onoff(recovery.enabled);
}
else
M413_report();
#if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
if (parser.seen("RL")) recovery.load();
if (parser.seen('W')) recovery.save(true);
if (parser.seen('P')) recovery.purge();
if (parser.seen('E')) serialprintPGM(recovery.exists() ? PSTR("PLR Exists\n") : PSTR("No PLR\n"));
if (parser.seen('V')) serialprintPGM(recovery.valid() ? PSTR("Valid\n") : PSTR("Invalid\n"));
if (parser.seen_test('W')) recovery.save(true);
if (parser.seen_test('P')) recovery.purge();
if (parser.seen_test('D')) recovery.debug(PSTR("M413"));
#if PIN_EXISTS(POWER_LOSS)
if (parser.seen_test('O')) recovery._outage();
#endif
if (parser.seen_test('E')) SERIAL_ECHOPGM_P(recovery.exists() ? PSTR("PLR Exists\n") : PSTR("No PLR\n"));
if (parser.seen_test('V')) SERIAL_ECHOPGM_P(recovery.valid() ? PSTR("Valid\n") : PSTR("Invalid\n"));
#endif
}
void GcodeSuite::M413_report(const bool forReplay/*=true*/) {
report_heading_etc(forReplay, PSTR(STR_POWER_LOSS_RECOVERY));
SERIAL_ECHOPGM(" M413 S", AS_DIGIT(recovery.enabled), " ; ");
serialprintln_onoff(recovery.enabled);
}
#endif // POWER_LOSS_RECOVERY

10
Marlin/src/gcode/feature/prusa_MMU2/M403.cpp Executable file → Normal file
View File

@@ -16,16 +16,16 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "../../../inc/MarlinConfigPre.h"
#if ENABLED(PRUSA_MMU2)
#if HAS_PRUSA_MMU2
#include "../../gcode.h"
#include "../../../feature/mmu2/mmu2.h"
#include "../../../feature/mmu/mmu2.h"
/**
* M403: Set filament type for MMU2
@@ -40,10 +40,10 @@ void GcodeSuite::M403() {
int8_t index = parser.intval('E', -1),
type = parser.intval('F', -1);
if (WITHIN(index, 0, 4) && WITHIN(type, 0, 2))
if (WITHIN(index, 0, EXTRUDERS - 1) && WITHIN(type, 0, 2))
mmu2.set_filament_type(index, type);
else
SERIAL_ECHO_MSG("M403 - bad arguments.");
}
#endif // PRUSA_MMU2
#endif // HAS_PRUSA_MMU2

43
Marlin/src/gcode/feature/runout/M412.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -29,34 +29,53 @@
/**
* M412: Enable / Disable filament runout detection
*
* Parameters
* R : Reset the runout sensor
* S<bool> : Reset and enable/disable the runout sensor
* H<bool> : Enable/disable host handling of filament runout
* D<linear> : Extra distance to continue after runout is triggered
*/
void GcodeSuite::M412() {
if (parser.seen("RS"
#ifdef FILAMENT_RUNOUT_DISTANCE_MM
"D"
#endif
#if ENABLED(HOST_ACTION_COMMANDS)
"H"
#endif
TERN_(HAS_FILAMENT_RUNOUT_DISTANCE, "D")
TERN_(HOST_ACTION_COMMANDS, "H")
)) {
#if ENABLED(HOST_ACTION_COMMANDS)
if (parser.seen('H')) runout.host_handling = parser.value_bool();
#endif
const bool seenR = parser.seen('R'), seenS = parser.seen('S');
const bool seenR = parser.seen_test('R'), seenS = parser.seen('S');
if (seenR || seenS) runout.reset();
if (seenS) runout.enabled = parser.value_bool();
#ifdef FILAMENT_RUNOUT_DISTANCE_MM
#if HAS_FILAMENT_RUNOUT_DISTANCE
if (parser.seen('D')) runout.set_runout_distance(parser.value_linear_units());
#endif
}
else {
SERIAL_ECHO_START();
SERIAL_ECHOPGM("Filament runout ");
serialprintln_onoff(runout.enabled);
#ifdef FILAMENT_RUNOUT_DISTANCE_MM
SERIAL_ECHOLNPAIR("Filament runout distance (mm): ", runout.runout_distance());
serialprint_onoff(runout.enabled);
#if HAS_FILAMENT_RUNOUT_DISTANCE
SERIAL_ECHOPGM(" ; Distance ", runout.runout_distance(), "mm");
#endif
#if ENABLED(HOST_ACTION_COMMANDS)
SERIAL_ECHOPGM(" ; Host handling ");
serialprint_onoff(runout.host_handling);
#endif
SERIAL_EOL();
}
}
void GcodeSuite::M412_report(const bool forReplay/*=true*/) {
report_heading_etc(forReplay, PSTR(STR_FILAMENT_RUNOUT_SENSOR));
SERIAL_ECHOPGM(
" M412 S", runout.enabled
#if HAS_FILAMENT_RUNOUT_DISTANCE
, " D", LINEAR_UNIT(runout.runout_distance())
#endif
, " ; Sensor "
);
serialprintln_onoff(runout.enabled);
}
#endif // HAS_FILAMENT_SENSOR

32
Marlin/src/gcode/feature/trinamic/M122.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -26,31 +26,39 @@
#include "../../gcode.h"
#include "../../../feature/tmc_util.h"
#include "../../../module/stepper/indirection.h"
/**
* M122: Debug TMC drivers
*/
void GcodeSuite::M122() {
xyze_bool_t print_axis = { false, false, false, false };
bool print_all = true;
LOOP_XYZE(i) if (parser.seen(axis_codes[i])) { print_axis[i] = true; print_all = false; }
xyze_bool_t print_axis = ARRAY_N_1(LOGICAL_AXES, false);
if (print_all) LOOP_XYZE(i) print_axis[i] = true;
bool print_all = true;
LOOP_LOGICAL_AXES(i) if (parser.seen_test(axis_codes[i])) { print_axis[i] = true; print_all = false; }
if (print_all) LOOP_LOGICAL_AXES(i) print_axis[i] = true;
if (parser.boolval('I')) restore_stepper_drivers();
#if ENABLED(TMC_DEBUG)
#if ENABLED(MONITOR_DRIVER_STATUS)
const bool sflag = parser.seen('S'), s0 = sflag && !parser.value_bool();
if (sflag) tmc_set_report_interval(s0 ? 0 : MONITOR_DRIVER_STATUS_INTERVAL_MS);
if (!s0 && parser.seenval('P')) tmc_set_report_interval(_MIN(parser.value_ushort(), MONITOR_DRIVER_STATUS_INTERVAL_MS));
const bool sflag = parser.seen_test('S'), sval = sflag && parser.value_bool();
if (sflag && !sval)
tmc_set_report_interval(0);
else if (parser.seenval('P'))
tmc_set_report_interval(_MAX(250, parser.value_ushort()));
else if (sval)
tmc_set_report_interval(MONITOR_DRIVER_STATUS_INTERVAL_MS);
#endif
if (parser.seen('V'))
tmc_get_registers(print_axis.x, print_axis.y, print_axis.z, print_axis.e);
if (parser.seen_test('V'))
tmc_get_registers(LOGICAL_AXIS_ELEM(print_axis));
else
tmc_report_all(print_axis.x, print_axis.y, print_axis.z, print_axis.e);
tmc_report_all(LOGICAL_AXIS_ELEM(print_axis));
#endif
test_tmc_connection(print_axis.x, print_axis.y, print_axis.z, print_axis.e);
test_tmc_connection(LOGICAL_AXIS_ELEM(print_axis));
}
#endif // HAS_TRINAMIC_CONFIG

249
Marlin/src/gcode/feature/trinamic/M569.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -32,8 +32,7 @@ template<typename TMC>
void tmc_say_stealth_status(TMC &st) {
st.printLabel();
SERIAL_ECHOPGM(" driver mode:\t");
serialprintPGM(st.get_stealthChop_status() ? PSTR("stealthChop") : PSTR("spreadCycle"));
SERIAL_EOL();
SERIAL_ECHOLNPGM_P(st.get_stealthChop() ? PSTR("stealthChop") : PSTR("spreadCycle"));
}
template<typename TMC>
void tmc_set_stealthChop(TMC &st, const bool enable) {
@@ -41,132 +40,88 @@ void tmc_set_stealthChop(TMC &st, const bool enable) {
st.refresh_stepping_mode();
}
static void set_stealth_status(const bool enable, const int8_t target_extruder) {
static void set_stealth_status(const bool enable, const int8_t target_e_stepper) {
#define TMC_SET_STEALTH(Q) tmc_set_stealthChop(stepper##Q, enable)
#if AXIS_HAS_STEALTHCHOP(X) || AXIS_HAS_STEALTHCHOP(X2) \
|| AXIS_HAS_STEALTHCHOP(Y) || AXIS_HAS_STEALTHCHOP(Y2) \
|| AXIS_HAS_STEALTHCHOP(Z) || AXIS_HAS_STEALTHCHOP(Z2) \
|| AXIS_HAS_STEALTHCHOP(Z3) || AXIS_HAS_STEALTHCHOP(Z4)
#if X_HAS_STEALTHCHOP || Y_HAS_STEALTHCHOP || Z_HAS_STEALTHCHOP \
|| I_HAS_STEALTHCHOP || J_HAS_STEALTHCHOP || K_HAS_STEALTHCHOP \
|| X2_HAS_STEALTHCHOP || Y2_HAS_STEALTHCHOP || Z2_HAS_STEALTHCHOP || Z3_HAS_STEALTHCHOP || Z4_HAS_STEALTHCHOP
const uint8_t index = parser.byteval('I');
#endif
LOOP_XYZE(i) if (parser.seen(axis_codes[i])) {
LOOP_LOGICAL_AXES(i) if (parser.seen(axis_codes[i])) {
switch (i) {
case X_AXIS:
#if AXIS_HAS_STEALTHCHOP(X)
if (index == 0) TMC_SET_STEALTH(X);
#endif
#if AXIS_HAS_STEALTHCHOP(X2)
if (index == 1) TMC_SET_STEALTH(X2);
#endif
TERN_(X_HAS_STEALTHCHOP, if (index == 0) TMC_SET_STEALTH(X));
TERN_(X2_HAS_STEALTHCHOP, if (index == 1) TMC_SET_STEALTH(X2));
break;
case Y_AXIS:
#if AXIS_HAS_STEALTHCHOP(Y)
if (index == 0) TMC_SET_STEALTH(Y);
#endif
#if AXIS_HAS_STEALTHCHOP(Y2)
if (index == 1) TMC_SET_STEALTH(Y2);
#endif
break;
case Z_AXIS:
#if AXIS_HAS_STEALTHCHOP(Z)
if (index == 0) TMC_SET_STEALTH(Z);
#endif
#if AXIS_HAS_STEALTHCHOP(Z2)
if (index == 1) TMC_SET_STEALTH(Z2);
#endif
#if AXIS_HAS_STEALTHCHOP(Z3)
if (index == 2) TMC_SET_STEALTH(Z3);
#endif
#if AXIS_HAS_STEALTHCHOP(Z4)
if (index == 3) TMC_SET_STEALTH(Z4);
#endif
break;
case E_AXIS: {
if (target_extruder < 0) return;
switch (target_extruder) {
#if AXIS_HAS_STEALTHCHOP(E0)
case 0: TMC_SET_STEALTH(E0); break;
#endif
#if AXIS_HAS_STEALTHCHOP(E1)
case 1: TMC_SET_STEALTH(E1); break;
#endif
#if AXIS_HAS_STEALTHCHOP(E2)
case 2: TMC_SET_STEALTH(E2); break;
#endif
#if AXIS_HAS_STEALTHCHOP(E3)
case 3: TMC_SET_STEALTH(E3); break;
#endif
#if AXIS_HAS_STEALTHCHOP(E4)
case 4: TMC_SET_STEALTH(E4); break;
#endif
#if AXIS_HAS_STEALTHCHOP(E5)
case 5: TMC_SET_STEALTH(E5); break;
#endif
#if AXIS_HAS_STEALTHCHOP(E6)
case 6: TMC_SET_STEALTH(E6); break;
#endif
#if AXIS_HAS_STEALTHCHOP(E7)
case 7: TMC_SET_STEALTH(E7); break;
#endif
}
} break;
#if HAS_Y_AXIS
case Y_AXIS:
TERN_(Y_HAS_STEALTHCHOP, if (index == 0) TMC_SET_STEALTH(Y));
TERN_(Y2_HAS_STEALTHCHOP, if (index == 1) TMC_SET_STEALTH(Y2));
break;
#endif
#if HAS_Z_AXIS
case Z_AXIS:
TERN_(Z_HAS_STEALTHCHOP, if (index == 0) TMC_SET_STEALTH(Z));
TERN_(Z2_HAS_STEALTHCHOP, if (index == 1) TMC_SET_STEALTH(Z2));
TERN_(Z3_HAS_STEALTHCHOP, if (index == 2) TMC_SET_STEALTH(Z3));
TERN_(Z4_HAS_STEALTHCHOP, if (index == 3) TMC_SET_STEALTH(Z4));
break;
#endif
#if I_HAS_STEALTHCHOP
case I_AXIS: TMC_SET_STEALTH(I); break;
#endif
#if J_HAS_STEALTHCHOP
case J_AXIS: TMC_SET_STEALTH(J); break;
#endif
#if K_HAS_STEALTHCHOP
case K_AXIS: TMC_SET_STEALTH(K); break;
#endif
#if E_STEPPERS
case E_AXIS: {
if (target_e_stepper < 0) return;
switch (target_e_stepper) {
TERN_(E0_HAS_STEALTHCHOP, case 0: TMC_SET_STEALTH(E0); break;)
TERN_(E1_HAS_STEALTHCHOP, case 1: TMC_SET_STEALTH(E1); break;)
TERN_(E2_HAS_STEALTHCHOP, case 2: TMC_SET_STEALTH(E2); break;)
TERN_(E3_HAS_STEALTHCHOP, case 3: TMC_SET_STEALTH(E3); break;)
TERN_(E4_HAS_STEALTHCHOP, case 4: TMC_SET_STEALTH(E4); break;)
TERN_(E5_HAS_STEALTHCHOP, case 5: TMC_SET_STEALTH(E5); break;)
TERN_(E6_HAS_STEALTHCHOP, case 6: TMC_SET_STEALTH(E6); break;)
TERN_(E7_HAS_STEALTHCHOP, case 7: TMC_SET_STEALTH(E7); break;)
}
} break;
#endif
}
}
}
static void say_stealth_status() {
#define TMC_SAY_STEALTH_STATUS(Q) tmc_say_stealth_status(stepper##Q)
#if AXIS_HAS_STEALTHCHOP(X)
TMC_SAY_STEALTH_STATUS(X);
#endif
#if AXIS_HAS_STEALTHCHOP(X2)
TMC_SAY_STEALTH_STATUS(X2);
#endif
#if AXIS_HAS_STEALTHCHOP(Y)
TMC_SAY_STEALTH_STATUS(Y);
#endif
#if AXIS_HAS_STEALTHCHOP(Y2)
TMC_SAY_STEALTH_STATUS(Y2);
#endif
#if AXIS_HAS_STEALTHCHOP(Z)
TMC_SAY_STEALTH_STATUS(Z);
#endif
#if AXIS_HAS_STEALTHCHOP(Z2)
TMC_SAY_STEALTH_STATUS(Z2);
#endif
#if AXIS_HAS_STEALTHCHOP(Z3)
TMC_SAY_STEALTH_STATUS(Z3);
#endif
#if AXIS_HAS_STEALTHCHOP(Z4)
TMC_SAY_STEALTH_STATUS(Z4);
#endif
#if AXIS_HAS_STEALTHCHOP(E0)
TMC_SAY_STEALTH_STATUS(E0);
#endif
#if AXIS_HAS_STEALTHCHOP(E1)
TMC_SAY_STEALTH_STATUS(E1);
#endif
#if AXIS_HAS_STEALTHCHOP(E2)
TMC_SAY_STEALTH_STATUS(E2);
#endif
#if AXIS_HAS_STEALTHCHOP(E3)
TMC_SAY_STEALTH_STATUS(E3);
#endif
#if AXIS_HAS_STEALTHCHOP(E4)
TMC_SAY_STEALTH_STATUS(E4);
#endif
#if AXIS_HAS_STEALTHCHOP(E5)
TMC_SAY_STEALTH_STATUS(E5);
#endif
#if AXIS_HAS_STEALTHCHOP(E6)
TMC_SAY_STEALTH_STATUS(E6);
#endif
#if AXIS_HAS_STEALTHCHOP(E7)
TMC_SAY_STEALTH_STATUS(E7);
#endif
OPTCODE( X_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(X))
OPTCODE(X2_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(X2))
OPTCODE( Y_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(Y))
OPTCODE(Y2_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(Y2))
OPTCODE( Z_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(Z))
OPTCODE(Z2_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(Z2))
OPTCODE(Z3_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(Z3))
OPTCODE(Z4_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(Z4))
OPTCODE( I_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(I))
OPTCODE( J_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(J))
OPTCODE( K_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(K))
OPTCODE(E0_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(E0))
OPTCODE(E1_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(E1))
OPTCODE(E2_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(E2))
OPTCODE(E3_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(E3))
OPTCODE(E4_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(E4))
OPTCODE(E5_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(E5))
OPTCODE(E6_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(E6))
OPTCODE(E7_HAS_STEALTHCHOP, TMC_SAY_STEALTH_STATUS(E7))
}
/**
@@ -178,9 +133,71 @@ static void say_stealth_status() {
*/
void GcodeSuite::M569() {
if (parser.seen('S'))
set_stealth_status(parser.value_bool(), get_target_extruder_from_command());
set_stealth_status(parser.value_bool(), get_target_e_stepper_from_command());
else
say_stealth_status();
}
void GcodeSuite::M569_report(const bool forReplay/*=true*/) {
report_heading(forReplay, PSTR(STR_DRIVER_STEPPING_MODE));
auto say_M569 = [](const bool forReplay, const char * const etc=nullptr, const bool eol=false) {
if (!forReplay) SERIAL_ECHO_START();
SERIAL_ECHOPGM(" M569 S1");
if (etc) {
SERIAL_CHAR(' ');
SERIAL_ECHOPGM_P(etc);
}
if (eol) SERIAL_EOL();
};
const bool chop_x = TERN0(X_HAS_STEALTHCHOP, stepperX.get_stored_stealthChop()),
chop_y = TERN0(Y_HAS_STEALTHCHOP, stepperY.get_stored_stealthChop()),
chop_z = TERN0(Z_HAS_STEALTHCHOP, stepperZ.get_stored_stealthChop()),
chop_i = TERN0(I_HAS_STEALTHCHOP, stepperI.get_stored_stealthChop()),
chop_j = TERN0(J_HAS_STEALTHCHOP, stepperJ.get_stored_stealthChop()),
chop_k = TERN0(K_HAS_STEALTHCHOP, stepperK.get_stored_stealthChop());
if (chop_x || chop_y || chop_z || chop_i || chop_j || chop_k) {
say_M569(forReplay);
LINEAR_AXIS_CODE(
if (chop_x) SERIAL_ECHOPGM_P(SP_X_STR),
if (chop_y) SERIAL_ECHOPGM_P(SP_Y_STR),
if (chop_z) SERIAL_ECHOPGM_P(SP_Z_STR),
if (chop_i) SERIAL_ECHOPGM_P(SP_I_STR),
if (chop_j) SERIAL_ECHOPGM_P(SP_J_STR),
if (chop_k) SERIAL_ECHOPGM_P(SP_K_STR)
);
SERIAL_EOL();
}
const bool chop_x2 = TERN0(X2_HAS_STEALTHCHOP, stepperX2.get_stored_stealthChop()),
chop_y2 = TERN0(Y2_HAS_STEALTHCHOP, stepperY2.get_stored_stealthChop()),
chop_z2 = TERN0(Z2_HAS_STEALTHCHOP, stepperZ2.get_stored_stealthChop());
if (chop_x2 || chop_y2 || chop_z2) {
say_M569(forReplay, PSTR("I1"));
if (chop_x2) SERIAL_ECHOPGM_P(SP_X_STR);
if (chop_y2) SERIAL_ECHOPGM_P(SP_Y_STR);
if (chop_z2) SERIAL_ECHOPGM_P(SP_Z_STR);
SERIAL_EOL();
}
if (TERN0(Z3_HAS_STEALTHCHOP, stepperZ3.get_stored_stealthChop())) { say_M569(forReplay, PSTR("I2 Z"), true); }
if (TERN0(Z4_HAS_STEALTHCHOP, stepperZ4.get_stored_stealthChop())) { say_M569(forReplay, PSTR("I3 Z"), true); }
if (TERN0( I_HAS_STEALTHCHOP, stepperI.get_stored_stealthChop())) { say_M569(forReplay, SP_I_STR, true); }
if (TERN0( J_HAS_STEALTHCHOP, stepperJ.get_stored_stealthChop())) { say_M569(forReplay, SP_J_STR, true); }
if (TERN0( K_HAS_STEALTHCHOP, stepperK.get_stored_stealthChop())) { say_M569(forReplay, SP_K_STR, true); }
if (TERN0(E0_HAS_STEALTHCHOP, stepperE0.get_stored_stealthChop())) { say_M569(forReplay, PSTR("T0 E"), true); }
if (TERN0(E1_HAS_STEALTHCHOP, stepperE1.get_stored_stealthChop())) { say_M569(forReplay, PSTR("T1 E"), true); }
if (TERN0(E2_HAS_STEALTHCHOP, stepperE2.get_stored_stealthChop())) { say_M569(forReplay, PSTR("T2 E"), true); }
if (TERN0(E3_HAS_STEALTHCHOP, stepperE3.get_stored_stealthChop())) { say_M569(forReplay, PSTR("T3 E"), true); }
if (TERN0(E4_HAS_STEALTHCHOP, stepperE4.get_stored_stealthChop())) { say_M569(forReplay, PSTR("T4 E"), true); }
if (TERN0(E5_HAS_STEALTHCHOP, stepperE5.get_stored_stealthChop())) { say_M569(forReplay, PSTR("T5 E"), true); }
if (TERN0(E6_HAS_STEALTHCHOP, stepperE6.get_stored_stealthChop())) { say_M569(forReplay, PSTR("T6 E"), true); }
if (TERN0(E7_HAS_STEALTHCHOP, stepperE7.get_stored_stealthChop())) { say_M569(forReplay, PSTR("T7 E"), true); }
}
#endif // HAS_STEALTHCHOP

217
Marlin/src/gcode/feature/trinamic/M906.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -48,11 +48,11 @@ void GcodeSuite::M906() {
bool report = true;
#if AXIS_IS_TMC(X) || AXIS_IS_TMC(X2) || AXIS_IS_TMC(Y) || AXIS_IS_TMC(Y2) || AXIS_IS_TMC(Z) || AXIS_IS_TMC(Z2) || AXIS_IS_TMC(Z3) || AXIS_IS_TMC(Z4)
#if AXIS_IS_TMC(X) || AXIS_IS_TMC(X2) || AXIS_IS_TMC(Y) || AXIS_IS_TMC(Y2) || AXIS_IS_TMC(Z) || AXIS_IS_TMC(Z2) || AXIS_IS_TMC(Z3) || AXIS_IS_TMC(Z4) || AXIS_IS_TMC(I) || AXIS_IS_TMC(J) || AXIS_IS_TMC(K)
const uint8_t index = parser.byteval('I');
#endif
LOOP_XYZE(i) if (uint16_t value = parser.intval(axis_codes[i])) {
LOOP_LOGICAL_AXES(i) if (uint16_t value = parser.intval(axis_codes[i])) {
report = false;
switch (i) {
case X_AXIS:
@@ -63,58 +63,77 @@ void GcodeSuite::M906() {
if (index == 1) TMC_SET_CURRENT(X2);
#endif
break;
case Y_AXIS:
#if AXIS_IS_TMC(Y)
if (index == 0) TMC_SET_CURRENT(Y);
#endif
#if AXIS_IS_TMC(Y2)
if (index == 1) TMC_SET_CURRENT(Y2);
#endif
break;
case Z_AXIS:
#if AXIS_IS_TMC(Z)
if (index == 0) TMC_SET_CURRENT(Z);
#endif
#if AXIS_IS_TMC(Z2)
if (index == 1) TMC_SET_CURRENT(Z2);
#endif
#if AXIS_IS_TMC(Z3)
if (index == 2) TMC_SET_CURRENT(Z3);
#endif
#if AXIS_IS_TMC(Z4)
if (index == 3) TMC_SET_CURRENT(Z4);
#endif
break;
case E_AXIS: {
const int8_t target_extruder = get_target_extruder_from_command();
if (target_extruder < 0) return;
switch (target_extruder) {
#if AXIS_IS_TMC(E0)
case 0: TMC_SET_CURRENT(E0); break;
#if HAS_Y_AXIS
case Y_AXIS:
#if AXIS_IS_TMC(Y)
if (index == 0) TMC_SET_CURRENT(Y);
#endif
#if AXIS_IS_TMC(E1)
case 1: TMC_SET_CURRENT(E1); break;
#if AXIS_IS_TMC(Y2)
if (index == 1) TMC_SET_CURRENT(Y2);
#endif
#if AXIS_IS_TMC(E2)
case 2: TMC_SET_CURRENT(E2); break;
break;
#endif
#if HAS_Z_AXIS
case Z_AXIS:
#if AXIS_IS_TMC(Z)
if (index == 0) TMC_SET_CURRENT(Z);
#endif
#if AXIS_IS_TMC(E3)
case 3: TMC_SET_CURRENT(E3); break;
#if AXIS_IS_TMC(Z2)
if (index == 1) TMC_SET_CURRENT(Z2);
#endif
#if AXIS_IS_TMC(E4)
case 4: TMC_SET_CURRENT(E4); break;
#if AXIS_IS_TMC(Z3)
if (index == 2) TMC_SET_CURRENT(Z3);
#endif
#if AXIS_IS_TMC(E5)
case 5: TMC_SET_CURRENT(E5); break;
#if AXIS_IS_TMC(Z4)
if (index == 3) TMC_SET_CURRENT(Z4);
#endif
#if AXIS_IS_TMC(E6)
case 6: TMC_SET_CURRENT(E6); break;
#endif
#if AXIS_IS_TMC(E7)
case 7: TMC_SET_CURRENT(E7); break;
#endif
}
} break;
break;
#endif
#if AXIS_IS_TMC(I)
case I_AXIS: TMC_SET_CURRENT(I); break;
#endif
#if AXIS_IS_TMC(J)
case J_AXIS: TMC_SET_CURRENT(J); break;
#endif
#if AXIS_IS_TMC(K)
case K_AXIS: TMC_SET_CURRENT(K); break;
#endif
#if E_STEPPERS
case E_AXIS: {
const int8_t target_e_stepper = get_target_e_stepper_from_command();
if (target_e_stepper < 0) return;
switch (target_e_stepper) {
#if AXIS_IS_TMC(E0)
case 0: TMC_SET_CURRENT(E0); break;
#endif
#if AXIS_IS_TMC(E1)
case 1: TMC_SET_CURRENT(E1); break;
#endif
#if AXIS_IS_TMC(E2)
case 2: TMC_SET_CURRENT(E2); break;
#endif
#if AXIS_IS_TMC(E3)
case 3: TMC_SET_CURRENT(E3); break;
#endif
#if AXIS_IS_TMC(E4)
case 4: TMC_SET_CURRENT(E4); break;
#endif
#if AXIS_IS_TMC(E5)
case 5: TMC_SET_CURRENT(E5); break;
#endif
#if AXIS_IS_TMC(E6)
case 6: TMC_SET_CURRENT(E6); break;
#endif
#if AXIS_IS_TMC(E7)
case 7: TMC_SET_CURRENT(E7); break;
#endif
}
} break;
#endif
}
}
@@ -143,6 +162,15 @@ void GcodeSuite::M906() {
#if AXIS_IS_TMC(Z4)
TMC_SAY_CURRENT(Z4);
#endif
#if AXIS_IS_TMC(I)
TMC_SAY_CURRENT(I);
#endif
#if AXIS_IS_TMC(J)
TMC_SAY_CURRENT(J);
#endif
#if AXIS_IS_TMC(K)
TMC_SAY_CURRENT(K);
#endif
#if AXIS_IS_TMC(E0)
TMC_SAY_CURRENT(E0);
#endif
@@ -170,4 +198,95 @@ void GcodeSuite::M906() {
}
}
void GcodeSuite::M906_report(const bool forReplay/*=true*/) {
report_heading(forReplay, PSTR(STR_STEPPER_DRIVER_CURRENT));
auto say_M906 = [](const bool forReplay) {
report_echo_start(forReplay);
SERIAL_ECHOPGM(" M906");
};
#if AXIS_IS_TMC(X) || AXIS_IS_TMC(Y) || AXIS_IS_TMC(Z) \
|| AXIS_IS_TMC(I) || AXIS_IS_TMC(J) || AXIS_IS_TMC(K)
say_M906(forReplay);
#if AXIS_IS_TMC(X)
SERIAL_ECHOPGM_P(SP_X_STR, stepperX.getMilliamps());
#endif
#if AXIS_IS_TMC(Y)
SERIAL_ECHOPGM_P(SP_Y_STR, stepperY.getMilliamps());
#endif
#if AXIS_IS_TMC(Z)
SERIAL_ECHOPGM_P(SP_Z_STR, stepperZ.getMilliamps());
#endif
#if AXIS_IS_TMC(I)
SERIAL_ECHOPGM_P(SP_I_STR, stepperI.getMilliamps());
#endif
#if AXIS_IS_TMC(J)
SERIAL_ECHOPGM_P(SP_J_STR, stepperJ.getMilliamps());
#endif
#if AXIS_IS_TMC(K)
SERIAL_ECHOPGM_P(SP_K_STR, stepperK.getMilliamps());
#endif
SERIAL_EOL();
#endif
#if AXIS_IS_TMC(X2) || AXIS_IS_TMC(Y2) || AXIS_IS_TMC(Z2)
say_M906(forReplay);
SERIAL_ECHOPGM(" I1");
#if AXIS_IS_TMC(X2)
SERIAL_ECHOPGM_P(SP_X_STR, stepperX2.getMilliamps());
#endif
#if AXIS_IS_TMC(Y2)
SERIAL_ECHOPGM_P(SP_Y_STR, stepperY2.getMilliamps());
#endif
#if AXIS_IS_TMC(Z2)
SERIAL_ECHOPGM_P(SP_Z_STR, stepperZ2.getMilliamps());
#endif
SERIAL_EOL();
#endif
#if AXIS_IS_TMC(Z3)
say_M906(forReplay);
SERIAL_ECHOLNPGM(" I2 Z", stepperZ3.getMilliamps());
#endif
#if AXIS_IS_TMC(Z4)
say_M906(forReplay);
SERIAL_ECHOLNPGM(" I3 Z", stepperZ4.getMilliamps());
#endif
#if AXIS_IS_TMC(E0)
say_M906(forReplay);
SERIAL_ECHOLNPGM(" T0 E", stepperE0.getMilliamps());
#endif
#if AXIS_IS_TMC(E1)
say_M906(forReplay);
SERIAL_ECHOLNPGM(" T1 E", stepperE1.getMilliamps());
#endif
#if AXIS_IS_TMC(E2)
say_M906(forReplay);
SERIAL_ECHOLNPGM(" T2 E", stepperE2.getMilliamps());
#endif
#if AXIS_IS_TMC(E3)
say_M906(forReplay);
SERIAL_ECHOLNPGM(" T3 E", stepperE3.getMilliamps());
#endif
#if AXIS_IS_TMC(E4)
say_M906(forReplay);
SERIAL_ECHOLNPGM(" T4 E", stepperE4.getMilliamps());
#endif
#if AXIS_IS_TMC(E5)
say_M906(forReplay);
SERIAL_ECHOLNPGM(" T5 E", stepperE5.getMilliamps());
#endif
#if AXIS_IS_TMC(E6)
say_M906(forReplay);
SERIAL_ECHOLNPGM(" T6 E", stepperE6.getMilliamps());
#endif
#if AXIS_IS_TMC(E7)
say_M906(forReplay);
SERIAL_ECHOLNPGM(" T7 E", stepperE7.getMilliamps());
#endif
SERIAL_EOL();
}
#endif // HAS_TRINAMIC_CONFIG

420
Marlin/src/gcode/feature/trinamic/M911-M914.cpp Executable file → Normal file
View File

@@ -16,7 +16,7 @@
* 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 <http://www.gnu.org/licenses/>.
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
@@ -35,12 +35,30 @@
#define M91x_USE(ST) (AXIS_DRIVER_TYPE(ST, TMC2130) || AXIS_DRIVER_TYPE(ST, TMC2160) || AXIS_DRIVER_TYPE(ST, TMC2208) || AXIS_DRIVER_TYPE(ST, TMC2209) || AXIS_DRIVER_TYPE(ST, TMC2660) || AXIS_DRIVER_TYPE(ST, TMC5130) || AXIS_DRIVER_TYPE(ST, TMC5160))
#define M91x_USE_E(N) (E_STEPPERS > N && M91x_USE(E##N))
#define M91x_SOME_X (M91x_USE(X) || M91x_USE(X2))
#define M91x_SOME_Y (M91x_USE(Y) || M91x_USE(Y2))
#define M91x_SOME_Z (M91x_USE(Z) || M91x_USE(Z2) || M91x_USE(Z3) || M91x_USE(Z4))
#define M91x_SOME_E (M91x_USE_E(0) || M91x_USE_E(1) || M91x_USE_E(2) || M91x_USE_E(3) || M91x_USE_E(4) || M91x_USE_E(5) || M91x_USE_E(6) || M91x_USE_E(7))
#if M91x_USE(X) || M91x_USE(X2)
#define M91x_SOME_X 1
#endif
#if LINEAR_AXES >= 2 && (M91x_USE(Y) || M91x_USE(Y2))
#define M91x_SOME_Y 1
#endif
#if HAS_Z_AXIS && (M91x_USE(Z) || M91x_USE(Z2) || M91x_USE(Z3) || M91x_USE(Z4))
#define M91x_SOME_Z 1
#endif
#if LINEAR_AXES >= 4 && M91x_USE(I)
#define M91x_USE_I 1
#endif
#if LINEAR_AXES >= 5 && M91x_USE(J)
#define M91x_USE_J 1
#endif
#if LINEAR_AXES >= 6 && M91x_USE(K)
#define M91x_USE_K 1
#endif
#if !M91x_SOME_X && !M91x_SOME_Y && !M91x_SOME_Z && !M91x_SOME_E
#if M91x_USE_E(0) || M91x_USE_E(1) || M91x_USE_E(2) || M91x_USE_E(3) || M91x_USE_E(4) || M91x_USE_E(5) || M91x_USE_E(6) || M91x_USE_E(7)
#define M91x_SOME_E 1
#endif
#if !M91x_SOME_X && !M91x_SOME_Y && !M91x_SOME_Z && !M91x_USE_I && !M91x_USE_J && !M91x_USE_K && !M91x_SOME_E
#error "MONITOR_DRIVER_STATUS requires at least one TMC2130, 2160, 2208, 2209, 2660, 5130, or 5160."
#endif
@@ -73,6 +91,9 @@
#if M91x_USE(Z4)
tmc_report_otpw(stepperZ4);
#endif
TERN_(M91x_USE_I, tmc_report_otpw(stepperI));
TERN_(M91x_USE_J, tmc_report_otpw(stepperJ));
TERN_(M91x_USE_K, tmc_report_otpw(stepperK));
#if M91x_USE_E(0)
tmc_report_otpw(stepperE0);
#endif
@@ -112,31 +133,15 @@
* M912 E1 ; clear E1 only
*/
void GcodeSuite::M912() {
#if M91x_SOME_X
const bool hasX = parser.seen(axis_codes.x);
#else
constexpr bool hasX = false;
#endif
const bool hasX = TERN0(M91x_SOME_X, parser.seen(axis_codes.x)),
hasY = TERN0(M91x_SOME_Y, parser.seen(axis_codes.y)),
hasZ = TERN0(M91x_SOME_Z, parser.seen(axis_codes.z)),
hasI = TERN0(M91x_USE_I, parser.seen(axis_codes.i)),
hasJ = TERN0(M91x_USE_J, parser.seen(axis_codes.j)),
hasK = TERN0(M91x_USE_K, parser.seen(axis_codes.k)),
hasE = TERN0(M91x_SOME_E, parser.seen(axis_codes.e));
#if M91x_SOME_Y
const bool hasY = parser.seen(axis_codes.y);
#else
constexpr bool hasY = false;
#endif
#if M91x_SOME_Z
const bool hasZ = parser.seen(axis_codes.z);
#else
constexpr bool hasZ = false;
#endif
#if M91x_SOME_E
const bool hasE = parser.seen(axis_codes.e);
#else
constexpr bool hasE = false;
#endif
const bool hasNone = !hasX && !hasY && !hasZ && !hasE;
const bool hasNone = !hasE && !hasX && !hasY && !hasZ && !hasI && !hasJ && !hasK;
#if M91x_SOME_X
const int8_t xval = int8_t(parser.byteval(axis_codes.x, 0xFF));
@@ -174,6 +179,19 @@
#endif
#endif
#if M91x_USE_I
const int8_t ival = int8_t(parser.byteval(axis_codes.i, 0xFF));
if (hasNone || ival == 1 || (hasI && ival < 0)) tmc_clear_otpw(stepperI);
#endif
#if M91x_USE_J
const int8_t jval = int8_t(parser.byteval(axis_codes.j, 0xFF));
if (hasNone || jval == 1 || (hasJ && jval < 0)) tmc_clear_otpw(stepperJ);
#endif
#if M91x_USE_K
const int8_t kval = int8_t(parser.byteval(axis_codes.k, 0xFF));
if (hasNone || kval == 1 || (hasK && kval < 0)) tmc_clear_otpw(stepperK);
#endif
#if M91x_SOME_E
const int8_t eval = int8_t(parser.byteval(axis_codes.e, 0xFF));
#if M91x_USE_E(0)
@@ -209,6 +227,7 @@
* M913: Set HYBRID_THRESHOLD speed.
*/
#if ENABLED(HYBRID_THRESHOLD)
void GcodeSuite::M913() {
#define TMC_SAY_PWMTHRS(A,Q) tmc_print_pwmthrs(stepper##Q)
#define TMC_SET_PWMTHRS(A,Q) stepper##Q.set_pwm_thrs(value)
@@ -216,139 +235,188 @@
#define TMC_SET_PWMTHRS_E(E) stepperE##E.set_pwm_thrs(value)
bool report = true;
#if AXIS_IS_TMC(X) || AXIS_IS_TMC(X2) || AXIS_IS_TMC(Y) || AXIS_IS_TMC(Y2) || AXIS_IS_TMC(Z) || AXIS_IS_TMC(Z2) || AXIS_IS_TMC(Z3) || AXIS_IS_TMC(Z4)
#if AXIS_IS_TMC(X) || AXIS_IS_TMC(X2) || AXIS_IS_TMC(Y) || AXIS_IS_TMC(Y2) || AXIS_IS_TMC(Z) || AXIS_IS_TMC(Z2) || AXIS_IS_TMC(Z3) || AXIS_IS_TMC(Z4) || AXIS_IS_TMC(I) || AXIS_IS_TMC(J) || AXIS_IS_TMC(K)
const uint8_t index = parser.byteval('I');
#endif
LOOP_XYZE(i) if (int32_t value = parser.longval(axis_codes[i])) {
LOOP_LOGICAL_AXES(i) if (int32_t value = parser.longval(axis_codes[i])) {
report = false;
switch (i) {
case X_AXIS:
#if AXIS_HAS_STEALTHCHOP(X)
if (index < 2) TMC_SET_PWMTHRS(X,X);
#endif
#if AXIS_HAS_STEALTHCHOP(X2)
if (!(index & 1)) TMC_SET_PWMTHRS(X,X2);
#endif
TERN_(X_HAS_STEALTHCHOP, if (index < 2) TMC_SET_PWMTHRS(X,X));
TERN_(X2_HAS_STEALTHCHOP, if (!(index & 1)) TMC_SET_PWMTHRS(X,X2));
break;
case Y_AXIS:
#if AXIS_HAS_STEALTHCHOP(Y)
if (index < 2) TMC_SET_PWMTHRS(Y,Y);
#endif
#if AXIS_HAS_STEALTHCHOP(Y2)
if (!(index & 1)) TMC_SET_PWMTHRS(Y,Y2);
#endif
TERN_(Y_HAS_STEALTHCHOP, if (index < 2) TMC_SET_PWMTHRS(Y,Y));
TERN_(Y2_HAS_STEALTHCHOP, if (!(index & 1)) TMC_SET_PWMTHRS(Y,Y2));
break;
#if I_HAS_STEALTHCHOP
case I_AXIS: TMC_SET_PWMTHRS(I,I); break;
#endif
#if J_HAS_STEALTHCHOP
case J_AXIS: TMC_SET_PWMTHRS(J,J); break;
#endif
#if K_HAS_STEALTHCHOP
case K_AXIS: TMC_SET_PWMTHRS(K,K); break;
#endif
case Z_AXIS:
#if AXIS_HAS_STEALTHCHOP(Z)
if (index < 2) TMC_SET_PWMTHRS(Z,Z);
#endif
#if AXIS_HAS_STEALTHCHOP(Z2)
if (index == 0 || index == 2) TMC_SET_PWMTHRS(Z,Z2);
#endif
#if AXIS_HAS_STEALTHCHOP(Z3)
if (index == 0 || index == 3) TMC_SET_PWMTHRS(Z,Z3);
#endif
#if AXIS_HAS_STEALTHCHOP(Z4)
if (index == 0 || index == 4) TMC_SET_PWMTHRS(Z,Z4);
#endif
TERN_(Z_HAS_STEALTHCHOP, if (index < 2) TMC_SET_PWMTHRS(Z,Z));
TERN_(Z2_HAS_STEALTHCHOP, if (index == 0 || index == 2) TMC_SET_PWMTHRS(Z,Z2));
TERN_(Z3_HAS_STEALTHCHOP, if (index == 0 || index == 3) TMC_SET_PWMTHRS(Z,Z3));
TERN_(Z4_HAS_STEALTHCHOP, if (index == 0 || index == 4) TMC_SET_PWMTHRS(Z,Z4));
break;
case E_AXIS: {
#if E_STEPPERS
const int8_t target_extruder = get_target_extruder_from_command();
if (target_extruder < 0) return;
switch (target_extruder) {
#if AXIS_HAS_STEALTHCHOP(E0)
case 0: TMC_SET_PWMTHRS_E(0); break;
#endif
#if E_STEPPERS > 1 && AXIS_HAS_STEALTHCHOP(E1)
case 1: TMC_SET_PWMTHRS_E(1); break;
#endif
#if E_STEPPERS > 2 && AXIS_HAS_STEALTHCHOP(E2)
case 2: TMC_SET_PWMTHRS_E(2); break;
#endif
#if E_STEPPERS > 3 && AXIS_HAS_STEALTHCHOP(E3)
case 3: TMC_SET_PWMTHRS_E(3); break;
#endif
#if E_STEPPERS > 4 && AXIS_HAS_STEALTHCHOP(E4)
case 4: TMC_SET_PWMTHRS_E(4); break;
#endif
#if E_STEPPERS > 5 && AXIS_HAS_STEALTHCHOP(E5)
case 5: TMC_SET_PWMTHRS_E(5); break;
#endif
#if E_STEPPERS > 6 && AXIS_HAS_STEALTHCHOP(E6)
case 6: TMC_SET_PWMTHRS_E(6); break;
#endif
#if E_STEPPERS > 7 && AXIS_HAS_STEALTHCHOP(E7)
case 7: TMC_SET_PWMTHRS_E(7); break;
#endif
#if E_STEPPERS
case E_AXIS: {
const int8_t target_e_stepper = get_target_e_stepper_from_command();
if (target_e_stepper < 0) return;
switch (target_e_stepper) {
TERN_(E0_HAS_STEALTHCHOP, case 0: TMC_SET_PWMTHRS_E(0); break;)
TERN_(E1_HAS_STEALTHCHOP, case 1: TMC_SET_PWMTHRS_E(1); break;)
TERN_(E2_HAS_STEALTHCHOP, case 2: TMC_SET_PWMTHRS_E(2); break;)
TERN_(E3_HAS_STEALTHCHOP, case 3: TMC_SET_PWMTHRS_E(3); break;)
TERN_(E4_HAS_STEALTHCHOP, case 4: TMC_SET_PWMTHRS_E(4); break;)
TERN_(E5_HAS_STEALTHCHOP, case 5: TMC_SET_PWMTHRS_E(5); break;)
TERN_(E6_HAS_STEALTHCHOP, case 6: TMC_SET_PWMTHRS_E(6); break;)
TERN_(E7_HAS_STEALTHCHOP, case 7: TMC_SET_PWMTHRS_E(7); break;)
}
#endif // E_STEPPERS
} break;
} break;
#endif // E_STEPPERS
}
}
if (report) {
#if AXIS_HAS_STEALTHCHOP(X)
TMC_SAY_PWMTHRS(X,X);
#endif
#if AXIS_HAS_STEALTHCHOP(X2)
TMC_SAY_PWMTHRS(X,X2);
#endif
#if AXIS_HAS_STEALTHCHOP(Y)
TMC_SAY_PWMTHRS(Y,Y);
#endif
#if AXIS_HAS_STEALTHCHOP(Y2)
TMC_SAY_PWMTHRS(Y,Y2);
#endif
#if AXIS_HAS_STEALTHCHOP(Z)
TMC_SAY_PWMTHRS(Z,Z);
#endif
#if AXIS_HAS_STEALTHCHOP(Z2)
TMC_SAY_PWMTHRS(Z,Z2);
#endif
#if AXIS_HAS_STEALTHCHOP(Z3)
TMC_SAY_PWMTHRS(Z,Z3);
#endif
#if AXIS_HAS_STEALTHCHOP(Z4)
TMC_SAY_PWMTHRS(Z,Z4);
#endif
#if E_STEPPERS && AXIS_HAS_STEALTHCHOP(E0)
TMC_SAY_PWMTHRS_E(0);
#endif
#if E_STEPPERS > 1 && AXIS_HAS_STEALTHCHOP(E1)
TMC_SAY_PWMTHRS_E(1);
#endif
#if E_STEPPERS > 2 && AXIS_HAS_STEALTHCHOP(E2)
TMC_SAY_PWMTHRS_E(2);
#endif
#if E_STEPPERS > 3 && AXIS_HAS_STEALTHCHOP(E3)
TMC_SAY_PWMTHRS_E(3);
#endif
#if E_STEPPERS > 4 && AXIS_HAS_STEALTHCHOP(E4)
TMC_SAY_PWMTHRS_E(4);
#endif
#if E_STEPPERS > 5 && AXIS_HAS_STEALTHCHOP(E5)
TMC_SAY_PWMTHRS_E(5);
#endif
#if E_STEPPERS > 6 && AXIS_HAS_STEALTHCHOP(E6)
TMC_SAY_PWMTHRS_E(6);
#endif
#if E_STEPPERS > 7 && AXIS_HAS_STEALTHCHOP(E7)
TMC_SAY_PWMTHRS_E(7);
#endif
TERN_( X_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS(X,X));
TERN_(X2_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS(X,X2));
TERN_( Y_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS(Y,Y));
TERN_(Y2_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS(Y,Y2));
TERN_( Z_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS(Z,Z));
TERN_(Z2_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS(Z,Z2));
TERN_(Z3_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS(Z,Z3));
TERN_(Z4_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS(Z,Z4));
TERN_( I_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS(I,I));
TERN_( J_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS(J,J));
TERN_( K_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS(K,K));
TERN_(E0_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS_E(0));
TERN_(E1_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS_E(1));
TERN_(E2_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS_E(2));
TERN_(E3_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS_E(3));
TERN_(E4_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS_E(4));
TERN_(E5_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS_E(5));
TERN_(E6_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS_E(6));
TERN_(E7_HAS_STEALTHCHOP, TMC_SAY_PWMTHRS_E(7));
}
}
void GcodeSuite::M913_report(const bool forReplay/*=true*/) {
report_heading(forReplay, PSTR(STR_HYBRID_THRESHOLD));
auto say_M913 = [](const bool forReplay) {
report_echo_start(forReplay);
SERIAL_ECHOPGM(" M913");
};
#if X_HAS_STEALTHCHOP || Y_HAS_STEALTHCHOP || Z_HAS_STEALTHCHOP
say_M913(forReplay);
#if X_HAS_STEALTHCHOP
SERIAL_ECHOPGM_P(SP_X_STR, stepperX.get_pwm_thrs());
#endif
#if Y_HAS_STEALTHCHOP
SERIAL_ECHOPGM_P(SP_Y_STR, stepperY.get_pwm_thrs());
#endif
#if Z_HAS_STEALTHCHOP
SERIAL_ECHOPGM_P(SP_Z_STR, stepperZ.get_pwm_thrs());
#endif
SERIAL_EOL();
#endif
#if X2_HAS_STEALTHCHOP || Y2_HAS_STEALTHCHOP || Z2_HAS_STEALTHCHOP
say_M913(forReplay);
SERIAL_ECHOPGM(" I1");
#if X2_HAS_STEALTHCHOP
SERIAL_ECHOPGM_P(SP_X_STR, stepperX2.get_pwm_thrs());
#endif
#if Y2_HAS_STEALTHCHOP
SERIAL_ECHOPGM_P(SP_Y_STR, stepperY2.get_pwm_thrs());
#endif
#if Z2_HAS_STEALTHCHOP
SERIAL_ECHOPGM_P(SP_Z_STR, stepperZ2.get_pwm_thrs());
#endif
SERIAL_EOL();
#endif
#if Z3_HAS_STEALTHCHOP
say_M913(forReplay);
SERIAL_ECHOLNPGM(" I2 Z", stepperZ3.get_pwm_thrs());
#endif
#if Z4_HAS_STEALTHCHOP
say_M913(forReplay);
SERIAL_ECHOLNPGM(" I3 Z", stepperZ4.get_pwm_thrs());
#endif
#if I_HAS_STEALTHCHOP
say_M913(forReplay);
SERIAL_ECHOLNPGM_P(SP_I_STR, stepperI.get_pwm_thrs());
#endif
#if J_HAS_STEALTHCHOP
say_M913(forReplay);
SERIAL_ECHOLNPGM_P(SP_J_STR, stepperJ.get_pwm_thrs());
#endif
#if K_HAS_STEALTHCHOP
say_M913(forReplay);
SERIAL_ECHOLNPGM_P(SP_K_STR, stepperK.get_pwm_thrs());
#endif
#if E0_HAS_STEALTHCHOP
say_M913(forReplay);
SERIAL_ECHOLNPGM(" T0 E", stepperE0.get_pwm_thrs());
#endif
#if E1_HAS_STEALTHCHOP
say_M913(forReplay);
SERIAL_ECHOLNPGM(" T1 E", stepperE1.get_pwm_thrs());
#endif
#if E2_HAS_STEALTHCHOP
say_M913(forReplay);
SERIAL_ECHOLNPGM(" T2 E", stepperE2.get_pwm_thrs());
#endif
#if E3_HAS_STEALTHCHOP
say_M913(forReplay);
SERIAL_ECHOLNPGM(" T3 E", stepperE3.get_pwm_thrs());
#endif
#if E4_HAS_STEALTHCHOP
say_M913(forReplay);
SERIAL_ECHOLNPGM(" T4 E", stepperE4.get_pwm_thrs());
#endif
#if E5_HAS_STEALTHCHOP
say_M913(forReplay);
SERIAL_ECHOLNPGM(" T5 E", stepperE5.get_pwm_thrs());
#endif
#if E6_HAS_STEALTHCHOP
say_M913(forReplay);
SERIAL_ECHOLNPGM(" T6 E", stepperE6.get_pwm_thrs());
#endif
#if E7_HAS_STEALTHCHOP
say_M913(forReplay);
SERIAL_ECHOLNPGM(" T7 E", stepperE7.get_pwm_thrs());
#endif
SERIAL_EOL();
}
#endif // HYBRID_THRESHOLD
/**
* M914: Set StallGuard sensitivity.
*/
#if USE_SENSORLESS
void GcodeSuite::M914() {
bool report = true;
const uint8_t index = parser.byteval('I');
LOOP_XYZ(i) if (parser.seen(XYZ_CHAR(i))) {
LOOP_LINEAR_AXES(i) if (parser.seen(AXIS_CHAR(i))) {
const int16_t value = parser.value_int();
report = false;
switch (i) {
@@ -388,6 +456,15 @@
#endif
break;
#endif
#if I_SENSORLESS && AXIS_HAS_STALLGUARD(I)
case I_AXIS: stepperI.homing_threshold(value); break;
#endif
#if J_SENSORLESS && AXIS_HAS_STALLGUARD(J)
case J_AXIS: stepperJ.homing_threshold(value); break;
#endif
#if K_SENSORLESS && AXIS_HAS_STALLGUARD(K)
case K_AXIS: stepperK.homing_threshold(value); break;
#endif
}
}
@@ -422,8 +499,79 @@
tmc_print_sgt(stepperZ4);
#endif
#endif
#if I_SENSORLESS && AXIS_HAS_STALLGUARD(I)
tmc_print_sgt(stepperI);
#endif
#if J_SENSORLESS && AXIS_HAS_STALLGUARD(J)
tmc_print_sgt(stepperJ);
#endif
#if K_SENSORLESS && AXIS_HAS_STALLGUARD(K)
tmc_print_sgt(stepperK);
#endif
}
}
void GcodeSuite::M914_report(const bool forReplay/*=true*/) {
report_heading(forReplay, PSTR(STR_STALLGUARD_THRESHOLD));
auto say_M914 = [](const bool forReplay) {
report_echo_start(forReplay);
SERIAL_ECHOPGM(" M914");
};
#if X_SENSORLESS || Y_SENSORLESS || Z_SENSORLESS
say_M914(forReplay);
#if X_SENSORLESS
SERIAL_ECHOPGM_P(SP_X_STR, stepperX.homing_threshold());
#endif
#if Y_SENSORLESS
SERIAL_ECHOPGM_P(SP_Y_STR, stepperY.homing_threshold());
#endif
#if Z_SENSORLESS
SERIAL_ECHOPGM_P(SP_Z_STR, stepperZ.homing_threshold());
#endif
SERIAL_EOL();
#endif
#if X2_SENSORLESS || Y2_SENSORLESS || Z2_SENSORLESS
say_M914(forReplay);
SERIAL_ECHOPGM(" I1");
#if X2_SENSORLESS
SERIAL_ECHOPGM_P(SP_X_STR, stepperX2.homing_threshold());
#endif
#if Y2_SENSORLESS
SERIAL_ECHOPGM_P(SP_Y_STR, stepperY2.homing_threshold());
#endif
#if Z2_SENSORLESS
SERIAL_ECHOPGM_P(SP_Z_STR, stepperZ2.homing_threshold());
#endif
SERIAL_EOL();
#endif
#if Z3_SENSORLESS
say_M914(forReplay);
SERIAL_ECHOLNPGM(" I2 Z", stepperZ3.homing_threshold());
#endif
#if Z4_SENSORLESS
say_M914(forReplay);
SERIAL_ECHOLNPGM(" I3 Z", stepperZ4.homing_threshold());
#endif
#if I_SENSORLESS
say_M914(forReplay);
SERIAL_ECHOLNPGM_P(SP_I_STR, stepperI.homing_threshold());
#endif
#if J_SENSORLESS
say_M914(forReplay);
SERIAL_ECHOLNPGM_P(SP_J_STR, stepperJ.homing_threshold());
#endif
#if K_SENSORLESS
say_M914(forReplay);
SERIAL_ECHOLNPGM_P(SP_K_STR, stepperK.homing_threshold());
#endif
}
#endif // USE_SENSORLESS
#endif // HAS_TRINAMIC_CONFIG

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