diff options
author | Georgiy Bondarenko <69736697+nehilo@users.noreply.github.com> | 2021-03-04 20:54:23 +0300 |
---|---|---|
committer | Georgiy Bondarenko <69736697+nehilo@users.noreply.github.com> | 2021-03-04 20:54:23 +0300 |
commit | e8701195e66f2d27ffe17fb514eae8173795aaf7 (patch) | |
tree | 9f519c4abf6556b9ae7190a6210d87ead1dfadde /Marlin/src/gcode/feature | |
download | kp3s-lgvl-e8701195e66f2d27ffe17fb514eae8173795aaf7.tar.xz kp3s-lgvl-e8701195e66f2d27ffe17fb514eae8173795aaf7.zip |
Initial commit
Diffstat (limited to 'Marlin/src/gcode/feature')
38 files changed, 4817 insertions, 0 deletions
diff --git a/Marlin/src/gcode/feature/L6470/M122.cpp b/Marlin/src/gcode/feature/L6470/M122.cpp new file mode 100644 index 0000000..d2b7f73 --- /dev/null +++ b/Marlin/src/gcode/feature/L6470/M122.cpp @@ -0,0 +1,151 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + */ + +#include "../../../inc/MarlinConfig.h" + +#if HAS_L64XX + +#include "../../gcode.h" +#include "../../../libs/L64XX/L64XX_Marlin.h" +#include "../../../module/stepper/indirection.h" + +void echo_yes_no(const bool yes); + +inline void L6470_say_status(const L64XX_axis_t axis) { + if (L64xxManager.spi_abort) return; + const L64XX_Marlin::L64XX_shadow_t &sh = L64xxManager.shadow; + L64xxManager.get_status(axis); + L64xxManager.say_axis(axis); + #if ENABLED(L6470_CHITCHAT) + char temp_buf[20]; + sprintf_P(temp_buf, PSTR(" status: %4x "), sh.STATUS_AXIS_RAW); + SERIAL_ECHO(temp_buf); + print_bin(sh.STATUS_AXIS_RAW); + switch (sh.STATUS_AXIS_LAYOUT) { + case L6470_STATUS_LAYOUT: serialprintPGM(PSTR(" L6470")); break; + case L6474_STATUS_LAYOUT: serialprintPGM(PSTR(" L6474")); break; + case L6480_STATUS_LAYOUT: serialprintPGM(PSTR(" L6480/powerSTEP01")); break; + } + #endif + SERIAL_ECHOPGM("\n...OUTPUT: "); + serialprintPGM(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")); + if (sh.STATUS_AXIS_LAYOUT == L6480_STATUS_LAYOUT) { + SERIAL_ECHOPGM(" Last Command: "); + if (sh.STATUS_AXIS & sh.STATUS_AXIS_WRONG_CMD) SERIAL_ECHOPGM("VALID"); + else SERIAL_ECHOPGM("ERROR"); + SERIAL_ECHOPGM("\n...THERMAL: "); + switch ((sh.STATUS_AXIS & (sh.STATUS_AXIS_TH_SD | sh.STATUS_AXIS_TH_WRN)) >> 11) { + case 0: SERIAL_ECHOPGM("DEVICE SHUTDOWN"); break; + case 1: SERIAL_ECHOPGM("BRIDGE SHUTDOWN"); break; + case 2: SERIAL_ECHOPGM("WARNING "); break; + case 3: SERIAL_ECHOPGM("OK "); break; + } + } + else { + SERIAL_ECHOPGM(" Last Command: "); + if (!(sh.STATUS_AXIS & sh.STATUS_AXIS_WRONG_CMD)) SERIAL_ECHOPGM("IN"); + SERIAL_ECHOPGM("VALID "); + 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(" OVERCURRENT:"); echo_yes_no((sh.STATUS_AXIS & sh.STATUS_AXIS_OCD) == 0); + if (sh.STATUS_AXIS_LAYOUT != L6474_STATUS_LAYOUT) { + SERIAL_ECHOPGM(" STALL:"); echo_yes_no((sh.STATUS_AXIS & sh.STATUS_AXIS_STEP_LOSS_A) == 0 || (sh.STATUS_AXIS & sh.STATUS_AXIS_STEP_LOSS_B) == 0); + SERIAL_ECHOPGM(" STEP-CLOCK MODE:"); echo_yes_no((sh.STATUS_AXIS & sh.STATUS_AXIS_SCK_MOD) != 0); + } + else { + SERIAL_ECHOPGM(" STALL: NA " + " STEP-CLOCK MODE: NA" + " UNDER VOLTAGE LOCKOUT: "); echo_yes_no((sh.STATUS_AXIS & sh.STATUS_AXIS_UVLO) == 0); + } + SERIAL_EOL(); +} + +/** + * M122: Debug L6470 drivers + */ +void GcodeSuite::M122() { + L64xxManager.pause_monitor(true); // Keep monitor_driver() from stealing status + L64xxManager.spi_active = true; // Tell set_directions() a series of SPI transfers is underway + + //if (parser.seen('S')) + // tmc_set_report_interval(parser.value_bool()); + //else + + #if AXIS_IS_L64XX(X) + L6470_say_status(X); + #endif + #if AXIS_IS_L64XX(X2) + L6470_say_status(X2); + #endif + #if AXIS_IS_L64XX(Y) + L6470_say_status(Y); + #endif + #if AXIS_IS_L64XX(Y2) + L6470_say_status(Y2); + #endif + #if AXIS_IS_L64XX(Z) + L6470_say_status(Z); + #endif + #if AXIS_IS_L64XX(Z2) + L6470_say_status(Z2); + #endif + #if AXIS_IS_L64XX(Z3) + L6470_say_status(Z3); + #endif + #if AXIS_IS_L64XX(Z4) + L6470_say_status(Z4); + #endif + #if AXIS_IS_L64XX(E0) + L6470_say_status(E0); + #endif + #if AXIS_IS_L64XX(E1) + L6470_say_status(E1); + #endif + #if AXIS_IS_L64XX(E2) + L6470_say_status(E2); + #endif + #if AXIS_IS_L64XX(E3) + L6470_say_status(E3); + #endif + #if AXIS_IS_L64XX(E4) + L6470_say_status(E4); + #endif + #if AXIS_IS_L64XX(E5) + L6470_say_status(E5); + #endif + #if AXIS_IS_L64XX(E6) + L6470_say_status(E6); + #endif + #if AXIS_IS_L64XX(E7) + L6470_say_status(E7); + #endif + + L64xxManager.spi_active = false; // done with all SPI transfers - clear handshake flags + L64xxManager.spi_abort = false; + L64xxManager.pause_monitor(false); +} + +#endif // HAS_L64XX diff --git a/Marlin/src/gcode/feature/L6470/M906.cpp b/Marlin/src/gcode/feature/L6470/M906.cpp new file mode 100644 index 0000000..7bd446a --- /dev/null +++ b/Marlin/src/gcode/feature/L6470/M906.cpp @@ -0,0 +1,370 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + */ + +#include "../../../inc/MarlinConfig.h" + +#if HAS_L64XX + +#include "../../gcode.h" +#include "../../../libs/L64XX/L64XX_Marlin.h" +#include "../../../module/stepper/indirection.h" +#include "../../../module/planner.h" + +#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. + * + * L6470 can be setup to shutdown if either current threshold is exceeded. + * + * L6470 output current can not be set directly. It is set indirectly by + * setting the maximum effective output voltage. + * + * Effective output voltage is set by PWM duty cycle. + * + * Maximum effective output voltage is affected by MANY variables. The main ones are: + * KVAL_HOLD + * KVAL_RUN + * KVAL_ACC + * KVAL_DEC + * Vs compensation (if enabled) + */ +void L64XX_report_current(L64XX &motor, const L64XX_axis_t axis) { + + if (L64xxManager.spi_abort) return; // don't do anything if set_directions() has occurred + + const L64XX_Marlin::L64XX_shadow_t &sh = L64xxManager.shadow; + const uint16_t status = L64xxManager.get_status(axis); //also populates shadow structure + const uint8_t OverCurrent_Threshold = uint8_t(motor.GetParam(L6470_OCD_TH)); + + auto say_axis_status = [](const L64XX_axis_t axis, const uint16_t status) { + L64xxManager.say_axis(axis); + #if ENABLED(L6470_CHITCHAT) + char tmp[10]; + sprintf_P(tmp, PSTR("%4x "), status); + DEBUG_ECHOPAIR(" status: ", tmp); + print_bin(status); + #else + UNUSED(status); + #endif + SERIAL_EOL(); + }; + + char temp_buf[10]; + + switch (sh.STATUS_AXIS_LAYOUT) { + case L6470_STATUS_LAYOUT: // L6470 + case L6480_STATUS_LAYOUT: { // L6480 & powerstep01 + const uint16_t Stall_Threshold = (uint8_t)motor.GetParam(L6470_STALL_TH), + motor_status = (status & (STATUS_MOT_STATUS)) >> 5, + L6470_ADC_out = motor.GetParam(L6470_ADC_OUT), + L6470_ADC_out_limited = constrain(L6470_ADC_out, 8, 24); + const float comp_coef = 1600.0f / L6470_ADC_out_limited; + const uint16_t MicroSteps = _BV(motor.GetParam(L6470_STEP_MODE) & 0x07); + + say_axis_status(axis, sh.STATUS_AXIS_RAW); + + SERIAL_ECHOPGM("...OverCurrent Threshold: "); + sprintf_P(temp_buf, PSTR("%2d ("), OverCurrent_Threshold); + SERIAL_ECHO(temp_buf); + SERIAL_ECHO((OverCurrent_Threshold + 1) * motor.OCD_CURRENT_CONSTANT_INV); + SERIAL_ECHOPGM(" mA)"); + SERIAL_ECHOPGM(" Stall Threshold: "); + sprintf_P(temp_buf, PSTR("%2d ("), Stall_Threshold); + SERIAL_ECHO(temp_buf); + SERIAL_ECHO((Stall_Threshold + 1) * motor.STALL_CURRENT_CONSTANT_INV); + SERIAL_ECHOPGM(" mA)"); + SERIAL_ECHOPGM(" Motor Status: "); + switch (motor_status) { + case 0: SERIAL_ECHOPGM("stopped"); break; + case 1: SERIAL_ECHOPGM("accelerating"); break; + case 2: SERIAL_ECHOPGM("decelerating"); break; + case 3: SERIAL_ECHOPGM("at constant speed"); break; + } + SERIAL_EOL(); + + SERIAL_ECHOPAIR("...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_ECHOPAIR("...KVAL_HOLD: ", motor.GetParam(L6470_KVAL_HOLD), + " KVAL_RUN : ", motor.GetParam(L6470_KVAL_RUN), + " KVAL_ACC: ", motor.GetParam(L6470_KVAL_ACC), + " KVAL_DEC: ", motor.GetParam(L6470_KVAL_DEC), + " V motor max = "); + switch (motor_status) { + case 0: SERIAL_ECHO(motor.GetParam(L6470_KVAL_HOLD) * 100 / 256); SERIAL_ECHOPGM("% (KVAL_HOLD)"); break; + case 1: SERIAL_ECHO(motor.GetParam(L6470_KVAL_RUN) * 100 / 256); SERIAL_ECHOPGM("% (KVAL_RUN)"); break; + case 2: SERIAL_ECHO(motor.GetParam(L6470_KVAL_ACC) * 100 / 256); SERIAL_ECHOPGM("% (KVAL_ACC)"); break; + case 3: SERIAL_ECHO(motor.GetParam(L6470_KVAL_DEC) * 100 / 256); SERIAL_ECHOPGM("% (KVAL_HOLD)"); break; + } + SERIAL_EOL(); + + #if ENABLED(L6470_CHITCHAT) + DEBUG_ECHOPGM("...SLEW RATE: "); + switch (sh.STATUS_AXIS_LAYOUT) { + case L6470_STATUS_LAYOUT: { + switch ((motor.GetParam(sh.L6470_AXIS_CONFIG) & CONFIG_POW_SR) >> CONFIG_POW_SR_BIT) { + case 0: { DEBUG_ECHOLNPGM("320V/uS") ; break; } + case 1: { DEBUG_ECHOLNPGM("75V/uS") ; break; } + case 2: { DEBUG_ECHOLNPGM("110V/uS") ; break; } + case 3: { DEBUG_ECHOLNPGM("260V/uS") ; break; } + } + break; + } + case L6480_STATUS_LAYOUT: { + switch (motor.GetParam(L6470_GATECFG1) & CONFIG1_SR ) { + case CONFIG1_SR_220V_us: { DEBUG_ECHOLNPGM("220V/uS") ; break; } + case CONFIG1_SR_400V_us: { DEBUG_ECHOLNPGM("400V/uS") ; break; } + case CONFIG1_SR_520V_us: { DEBUG_ECHOLNPGM("520V/uS") ; break; } + case CONFIG1_SR_980V_us: { DEBUG_ECHOLNPGM("980V/uS") ; break; } + default: { DEBUG_ECHOLNPGM("unknown") ; break; } + } + } + } + #endif + SERIAL_EOL(); + break; + } + + case L6474_STATUS_LAYOUT: { // L6474 + const uint16_t L6470_ADC_out = motor.GetParam(L6470_ADC_OUT) & 0x1F, + L6474_TVAL_val = motor.GetParam(L6474_TVAL) & 0x7F; + + say_axis_status(axis, sh.STATUS_AXIS_RAW); + + SERIAL_ECHOPGM("...OverCurrent Threshold: "); + sprintf_P(temp_buf, PSTR("%2d ("), OverCurrent_Threshold); + SERIAL_ECHO(temp_buf); + SERIAL_ECHO((OverCurrent_Threshold + 1) * motor.OCD_CURRENT_CONSTANT_INV); + SERIAL_ECHOPGM(" mA)"); + SERIAL_ECHOPGM(" TVAL: "); + sprintf_P(temp_buf, PSTR("%2d ("), L6474_TVAL_val); + SERIAL_ECHO(temp_buf); + SERIAL_ECHO((L6474_TVAL_val + 1) * motor.STALL_CURRENT_CONSTANT_INV); + SERIAL_ECHOLNPGM(" mA) Motor Status: NA"); + + const uint16_t MicroSteps = _BV(motor.GetParam(L6470_STEP_MODE) & 0x07); //NOMORE(MicroSteps, 16); + SERIAL_ECHOPAIR("...MicroSteps: ", MicroSteps, + " ADC_OUT: ", L6470_ADC_out); + + SERIAL_ECHOLNPGM(" Vs_compensation: NA\n"); + SERIAL_ECHOLNPGM("...KVAL_HOLD: NA" + " KVAL_RUN : NA" + " KVAL_ACC: NA" + " KVAL_DEC: NA" + " V motor max = NA"); + + #if ENABLED(L6470_CHITCHAT) + DEBUG_ECHOPGM("...SLEW RATE: "); + switch ((motor.GetParam(sh.L6470_AXIS_CONFIG) & CONFIG_POW_SR) >> CONFIG_POW_SR_BIT) { + case 0: DEBUG_ECHOLNPGM("320V/uS") ; break; + case 1: DEBUG_ECHOLNPGM("75V/uS") ; break; + case 2: DEBUG_ECHOLNPGM("110V/uS") ; break; + case 3: DEBUG_ECHOLNPGM("260V/uS") ; break; + default: DEBUG_ECHOLNPAIR("slew rate: ", (motor.GetParam(sh.L6470_AXIS_CONFIG) & CONFIG_POW_SR) >> CONFIG_POW_SR_BIT); break; + } + #endif + SERIAL_EOL(); + SERIAL_EOL(); + break; + } + } +} + +void GcodeSuite::M906() { + + L64xxManager.pause_monitor(true); // Keep monitor_driver() from stealing status + + #define L6470_SET_KVAL_HOLD(Q) (AXIS_IS_L64XX(Q) ? stepper##Q.setTVALCurrent(value) : stepper##Q.SetParam(L6470_KVAL_HOLD, uint8_t(value))) + + DEBUG_ECHOLNPGM("M906"); + + uint8_t report_current = true; + + #if HAS_L64XX + const uint8_t index = parser.byteval('I'); + #endif + + LOOP_XYZE(i) if (uint16_t value = parser.intval(axis_codes[i])) { + + report_current = false; + + if (planner.has_blocks_queued() || planner.cleaning_buffer_counter) { + SERIAL_ECHOLNPGM("Test aborted. Can't set KVAL_HOLD while steppers are moving."); + return; + } + + switch (i) { + case X_AXIS: + #if AXIS_IS_L64XX(X) + if (index == 0) L6470_SET_KVAL_HOLD(X); + #endif + #if AXIS_IS_L64XX(X2) + 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; + #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; + } + } + + if (report_current) { + #define L64XX_REPORT_CURRENT(Q) L64XX_report_current(stepper##Q, Q) + + L64xxManager.spi_active = true; // Tell set_directions() a series of SPI transfers is underway + + #if AXIS_IS_L64XX(X) + L64XX_REPORT_CURRENT(X); + #endif + #if AXIS_IS_L64XX(X2) + L64XX_REPORT_CURRENT(X2); + #endif + #if AXIS_IS_L64XX(Y) + L64XX_REPORT_CURRENT(Y); + #endif + #if AXIS_IS_L64XX(Y2) + L64XX_REPORT_CURRENT(Y2); + #endif + #if AXIS_IS_L64XX(Z) + L64XX_REPORT_CURRENT(Z); + #endif + #if AXIS_IS_L64XX(Z2) + L64XX_REPORT_CURRENT(Z2); + #endif + #if AXIS_IS_L64XX(Z3) + L64XX_REPORT_CURRENT(Z3); + #endif + #if AXIS_IS_L64XX(Z4) + L64XX_REPORT_CURRENT(Z4); + #endif + #if AXIS_IS_L64XX(E0) + L64XX_REPORT_CURRENT(E0); + #endif + #if AXIS_IS_L64XX(E1) + L64XX_REPORT_CURRENT(E1); + #endif + #if AXIS_IS_L64XX(E2) + L64XX_REPORT_CURRENT(E2); + #endif + #if AXIS_IS_L64XX(E3) + L64XX_REPORT_CURRENT(E3); + #endif + #if AXIS_IS_L64XX(E4) + L64XX_REPORT_CURRENT(E4); + #endif + #if AXIS_IS_L64XX(E5) + L64XX_REPORT_CURRENT(E5); + #endif + #if AXIS_IS_L64XX(E6) + L64XX_REPORT_CURRENT(E6); + #endif + #if AXIS_IS_L64XX(E7) + L64XX_REPORT_CURRENT(E7); + #endif + + L64xxManager.spi_active = false; // done with all SPI transfers - clear handshake flags + L64xxManager.spi_abort = false; + L64xxManager.pause_monitor(false); + } +} + +#endif // HAS_L64XX diff --git a/Marlin/src/gcode/feature/L6470/M916-918.cpp b/Marlin/src/gcode/feature/L6470/M916-918.cpp new file mode 100644 index 0000000..8a1ea48 --- /dev/null +++ b/Marlin/src/gcode/feature/L6470/M916-918.cpp @@ -0,0 +1,651 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + */ + +// +// NOTE: All tests assume each axis uses matching driver chips. +// + +#include "../../../inc/MarlinConfig.h" + +#if HAS_L64XX + +#include "../../gcode.h" +#include "../../../module/stepper/indirection.h" +#include "../../../module/planner.h" +#include "../../../libs/L64XX/L64XX_Marlin.h" + +#define DEBUG_OUT ENABLED(L6470_CHITCHAT) +#include "../../../core/debug_out.h" + +/** + * M916: increase KVAL_HOLD until get thermal warning + * NOTE - on L6474 it is TVAL that is used + * + * J - select which driver(s) to monitor on multi-driver axis + * 0 - (default) monitor all drivers on the axis or E0 + * 1 - monitor only X, Y, Z, E1 + * 2 - monitor only X2, Y2, Z2, E2 + * 3 - monitor only Z3, E3 + * 4 - monitor only Z4, E4 + * + * Xxxx, Yxxx, Zxxx, Exxx - axis to be monitored with displacement + * xxx (1-255) is distance moved on either side of current position + * + * F - feedrate + * optional - will use default max feedrate from configuration.h if not specified + * + * T - current (mA) setting for TVAL (0 - 4A in 31.25mA increments, rounds down) - L6474 only + * optional - will report current value from driver if not specified + * + * K - value for KVAL_HOLD (0 - 255) (ignored for L6474) + * optional - will report current value from driver if not specified + * + * D - time (in seconds) to run each setting of KVAL_HOLD/TVAL + * optional - defaults to zero (runs each setting once) + */ + +/** + * This routine is also useful for determining the approximate KVAL_HOLD + * where the stepper stops losing steps. The sound will get noticeably quieter + * as it stops losing steps. + */ + +void GcodeSuite::M916() { + + DEBUG_ECHOLNPGM("M916"); + + L64xxManager.pause_monitor(true); // Keep monitor_driver() from stealing status + + // Variables used by L64xxManager.get_user_input function - some may not be used + char axis_mon[3][3] = { {" "}, {" "}, {" "} }; // list of Axes to be monitored + L64XX_axis_t axis_index[3]; + uint16_t axis_status[3]; + uint8_t driver_count = 1; + float position_max; + float position_min; + float final_feedrate; + uint8_t kval_hold; + uint8_t OCD_TH_val = 0; + uint8_t STALL_TH_val = 0; + uint16_t over_current_threshold; + constexpr uint8_t over_current_flag = false; // M916 doesn't play with the overcurrent thresholds + + #define DRIVER_TYPE_L6474(Q) AXIS_DRIVER_TYPE_##Q(L6474) + + uint8_t j; // general purpose counter + + if (L64xxManager.get_user_input(driver_count, axis_index, axis_mon, position_max, position_min, final_feedrate, kval_hold, over_current_flag, OCD_TH_val, STALL_TH_val, over_current_threshold)) + return; // quit if invalid user input + + DEBUG_ECHOLNPAIR("feedrate = ", final_feedrate); + + planner.synchronize(); // wait for all current movement commands to complete + + const L64XX_Marlin::L64XX_shadow_t &sh = L64xxManager.shadow; + for (j = 0; j < driver_count; j++) + L64xxManager.get_status(axis_index[j]); // clear out any pre-existing error flags + + char temp_axis_string[] = " "; + temp_axis_string[0] = axis_mon[0][0]; // need to have a string for use within sprintf format section + char gcode_string[80]; + uint16_t status_composite = 0; + + uint16_t M91x_counter = kval_hold; + uint16_t M91x_counter_max; + if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT) { + M91x_counter_max = 128; // TVAL is 7 bits + LIMIT(M91x_counter, 0U, 127U); + } + else + M91x_counter_max = 256; // KVAL_HOLD is 8 bits + + uint8_t M91x_delay_s = parser.byteval('D'); // get delay in seconds + millis_t M91x_delay_ms = SEC_TO_MS(M91x_delay_s * 60); + millis_t M91x_delay_end; + + DEBUG_ECHOLNPGM(".\n."); + + do { + + if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT) + DEBUG_ECHOLNPAIR("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 + + for (j = 0; j < driver_count; j++) + L64xxManager.set_param(axis_index[j], L6470_KVAL_HOLD, M91x_counter); //set KVAL_HOLD or TVAL (same register address) + + M91x_delay_end = millis() + M91x_delay_ms; + do { + // turn the motor(s) both directions + sprintf_P(gcode_string, PSTR("G0 %s%03d F%03d"), temp_axis_string, uint16_t(position_min), uint16_t(final_feedrate)); + gcode.process_subcommands_now_P(gcode_string); + + sprintf_P(gcode_string, PSTR("G0 %s%03d F%03d"), temp_axis_string, uint16_t(position_max), uint16_t(final_feedrate)); + gcode.process_subcommands_now_P(gcode_string); + + // get the status after the motors have stopped + planner.synchronize(); + + status_composite = 0; // clear out the old bits + + for (j = 0; j < driver_count; j++) { + axis_status[j] = (~L64xxManager.get_status(axis_index[j])) & sh.L6470_ERROR_MASK; // bits of interest are all active low + status_composite |= axis_status[j] ; + } + + if (status_composite) break; + } while (millis() < M91x_delay_end); + + if (status_composite) break; + + M91x_counter++; + + } while (!(status_composite & (sh.STATUS_AXIS_TH_WRN | sh.STATUS_AXIS_TH_SD)) && (M91x_counter < M91x_counter_max)); + + DEBUG_ECHOLNPGM("."); + + #if ENABLED(L6470_CHITCHAT) + if (status_composite) { + L64xxManager.error_status_decode(status_composite, axis_index[0], + sh.STATUS_AXIS_TH_SD, sh.STATUS_AXIS_TH_WRN, + sh.STATUS_AXIS_STEP_LOSS_A, sh.STATUS_AXIS_STEP_LOSS_B, + sh.STATUS_AXIS_OCD, sh.STATUS_AXIS_LAYOUT); + DEBUG_ECHOLNPGM("."); + } + #endif + + if ((status_composite & (sh.STATUS_AXIS_TH_WRN | sh.STATUS_AXIS_TH_SD))) + DEBUG_ECHOLNPGM(".\n.\nTest completed normally - Thermal warning/shutdown has occurred"); + else if (status_composite) + DEBUG_ECHOLNPGM(".\n.\nTest completed abnormally - non-thermal error has occured"); + else + DEBUG_ECHOLNPGM(".\n.\nTest completed normally - Unable to get to thermal warning/shutdown"); + + L64xxManager.pause_monitor(false); +} + +/** + * M917: Find minimum current thresholds + * + * Decrease OCD current until overcurrent error + * Increase OCD until overcurrent error goes away + * Decrease stall threshold until stall (not done on L6474) + * Increase stall until stall error goes away (not done on L6474) + * + * J - select which driver(s) to monitor on multi-driver axis + * 0 - (default) monitor all drivers on the axis or E0 + * 1 - monitor only X, Y, Z, E1 + * 2 - monitor only X2, Y2, Z2, E2 + * Xxxx, Yxxx, Zxxx, Exxx - axis to be monitored with displacement + * xxx (1-255) is distance moved on either side of current position + * + * F - feedrate + * optional - will use default max feedrate from Configuration.h if not specified + * + * I - starting over-current threshold + * optional - will report current value from driver if not specified + * if there are multiple drivers on the axis then all will be set the same + * + * T - current (mA) setting for TVAL (0 - 4A in 31.25mA increments, rounds down) - L6474 only + * optional - will report current value from driver if not specified + * + * K - value for KVAL_HOLD (0 - 255) (ignored for L6474) + * optional - will report current value from driver if not specified + */ +void GcodeSuite::M917() { + + DEBUG_ECHOLNPGM("M917"); + + L64xxManager.pause_monitor(true); // Keep monitor_driver() from stealing status + + char axis_mon[3][3] = { {" "}, {" "}, {" "} }; // list of Axes to be monitored + L64XX_axis_t axis_index[3]; + uint16_t axis_status[3]; + uint8_t driver_count = 1; + float position_max; + float position_min; + float final_feedrate; + uint8_t kval_hold; + uint8_t OCD_TH_val = 0; + uint8_t STALL_TH_val = 0; + uint16_t over_current_threshold; + constexpr uint8_t over_current_flag = true; + + uint8_t j; // general purpose counter + + if (L64xxManager.get_user_input(driver_count, axis_index, axis_mon, position_max, position_min, final_feedrate, kval_hold, over_current_flag, OCD_TH_val, STALL_TH_val, over_current_threshold)) + return; // quit if invalid user input + + DEBUG_ECHOLNPAIR("feedrate = ", final_feedrate); + + planner.synchronize(); // wait for all current movement commands to complete + + const L64XX_Marlin::L64XX_shadow_t &sh = L64xxManager.shadow; + for (j = 0; j < driver_count; j++) + L64xxManager.get_status(axis_index[j]); // clear error flags + char temp_axis_string[] = " "; + temp_axis_string[0] = axis_mon[0][0]; // need a sprintf format string + char gcode_string[80]; + uint16_t status_composite = 0; + uint8_t test_phase = 0; // 0 - decreasing OCD - exit when OCD warning occurs (ignore STALL) + // 1 - increasing OCD - exit when OCD warning stops (ignore STALL) + // 2 - OCD finalized - decreasing STALL - exit when STALL warning happens + // 3 - OCD finalized - increasing STALL - exit when STALL warning stop + // 4 - all testing completed + DEBUG_ECHOPAIR(".\n.\n.\nover_current threshold : ", (OCD_TH_val + 1) * 375); // first status display + DEBUG_ECHOPAIR(" (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_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); + + 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); + + sprintf_P(gcode_string, PSTR("G0 %s%03d F%03d"), temp_axis_string, uint16_t(position_max), uint16_t(final_feedrate)); + gcode.process_subcommands_now_P(gcode_string); + + planner.synchronize(); + + status_composite = 0; // clear out the old bits + + for (j = 0; j < driver_count; j++) { + axis_status[j] = (~L64xxManager.get_status(axis_index[j])) & sh.L6470_ERROR_MASK; // bits of interest are all active low + status_composite |= axis_status[j]; + } + + if (status_composite && (status_composite & sh.STATUS_AXIS_UVLO)) { + DEBUG_ECHOLNPGM("Test aborted (Undervoltage lockout active)"); + #if ENABLED(L6470_CHITCHAT) + for (j = 0; j < driver_count; j++) { + if (j) DEBUG_ECHOPGM("..."); + L64xxManager.error_status_decode(axis_status[j], axis_index[j], + sh.STATUS_AXIS_TH_SD, sh.STATUS_AXIS_TH_WRN, + sh.STATUS_AXIS_STEP_LOSS_A, sh.STATUS_AXIS_STEP_LOSS_B, + sh.STATUS_AXIS_OCD, sh.STATUS_AXIS_LAYOUT); + } + #endif + return; + } + + if (status_composite & (sh.STATUS_AXIS_TH_WRN | sh.STATUS_AXIS_TH_SD)) { + DEBUG_ECHOLNPGM("thermal problem - waiting for chip(s) to cool down "); + uint16_t status_composite_temp = 0; + uint8_t k = 0; + do { + k++; + if (!(k % 4)) { + kval_hold *= 0.95; + DEBUG_EOL(); + DEBUG_ECHOLNPAIR("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(); // keep steppers powered + watchdog_refresh(); + safe_delay(5000); + status_composite_temp = 0; + for (j = 0; j < driver_count; j++) { + axis_status[j] = (~L64xxManager.get_status(axis_index[j])) & sh.L6470_ERROR_MASK; // bits of interest are all active low + status_composite_temp |= axis_status[j]; + } + } + while (status_composite_temp & (sh.STATUS_AXIS_TH_WRN | sh.STATUS_AXIS_TH_SD)); + DEBUG_EOL(); + } + if (status_composite & (sh.STATUS_AXIS_STEP_LOSS_A | sh.STATUS_AXIS_STEP_LOSS_B | sh.STATUS_AXIS_OCD)) { + switch (test_phase) { + + case 0: { + if (status_composite & sh.STATUS_AXIS_OCD) { + // phase 0 with OCD warning - time to go to next phase + if (OCD_TH_val >= sh.AXIS_OCD_TH_MAX) { + OCD_TH_val = sh.AXIS_OCD_TH_MAX; // limit to max + test_phase = 2; // at highest value so skip phase 1 + //DEBUG_ECHOLNPGM("LOGIC E0A OCD at highest - skip to 2"); + DEBUG_ECHOLNPGM("OCD at highest - OCD finalized"); + } + else { + OCD_TH_val++; // normal exit to next phase + test_phase = 1; // setup for first pass of phase 1 + //DEBUG_ECHOLNPGM("LOGIC E0B - inc OCD & go to 1"); + DEBUG_ECHOLNPGM("inc OCD"); + } + } + else { // phase 0 without OCD warning - keep on decrementing if can + if (OCD_TH_val) { + OCD_TH_val--; // try lower value + //DEBUG_ECHOLNPGM("LOGIC E0C - dec OCD"); + DEBUG_ECHOLNPGM("dec OCD"); + } + else { + test_phase = 2; // at lowest value without warning so skip phase 1 + //DEBUG_ECHOLNPGM("LOGIC E0D - OCD at latest - go to 2"); + DEBUG_ECHOLNPGM("OCD finalized"); + } + } + } break; + + case 1: { + if (status_composite & sh.STATUS_AXIS_OCD) { + // phase 1 with OCD warning - increment if can + if (OCD_TH_val >= sh.AXIS_OCD_TH_MAX) { + OCD_TH_val = sh.AXIS_OCD_TH_MAX; // limit to max + test_phase = 2; // at highest value so go to next phase + //DEBUG_ECHOLNPGM("LOGIC E1A - OCD at max - go to 2"); + DEBUG_ECHOLNPGM("OCD finalized"); + } + else { + OCD_TH_val++; // try a higher value + //DEBUG_ECHOLNPGM("LOGIC E1B - inc OCD"); + DEBUG_ECHOLNPGM("inc OCD"); + } + } + else { // phase 1 without OCD warning - normal exit to phase 2 + test_phase = 2; + //DEBUG_ECHOLNPGM("LOGIC E1C - no OCD warning - go to 1"); + DEBUG_ECHOLNPGM("OCD finalized"); + } + } break; + + case 2: { + if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT) { // skip all STALL_TH steps if L6474 + test_phase = 4; + break; + } + if (status_composite & (sh.STATUS_AXIS_STEP_LOSS_A | sh.STATUS_AXIS_STEP_LOSS_B)) { + // phase 2 with stall warning - time to go to next phase + if (STALL_TH_val >= 127) { + STALL_TH_val = 127; // limit to max + //DEBUG_ECHOLNPGM("LOGIC E2A - STALL warning, STALL at max, quit"); + DEBUG_ECHOLNPGM("finished - STALL at maximum value but still have stall warning"); + test_phase = 4; + } + else { + test_phase = 3; // normal exit to next phase (found failing value of STALL) + STALL_TH_val++; // setup for first pass of phase 3 + //DEBUG_ECHOLNPGM("LOGIC E2B - INC - STALL warning, inc Stall, go to 3"); + DEBUG_ECHOLNPGM("inc Stall"); + } + } + else { // phase 2 without stall warning - decrement if can + if (STALL_TH_val) { + STALL_TH_val--; // try a lower value + //DEBUG_ECHOLNPGM("LOGIC E2C - no STALL, dec STALL"); + DEBUG_ECHOLNPGM("dec STALL"); + } + else { + DEBUG_ECHOLNPGM("finished - STALL at lowest value but still do NOT have stall warning"); + test_phase = 4; + //DEBUG_ECHOLNPGM("LOGIC E2D - no STALL, at lowest so quit"); + } + } + } break; + + case 3: { + if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT) { // skip all STALL_TH steps if L6474 + test_phase = 4; + break; + } + if (status_composite & (sh.STATUS_AXIS_STEP_LOSS_A | sh.STATUS_AXIS_STEP_LOSS_B)) { + // phase 3 with stall warning - increment if can + if (STALL_TH_val >= 127) { + STALL_TH_val = 127; // limit to max + DEBUG_ECHOLNPGM("finished - STALL at maximum value but still have stall warning"); + test_phase = 4; + //DEBUG_ECHOLNPGM("LOGIC E3A - STALL, at max so quit"); + } + else { + STALL_TH_val++; // still looking for passing value + //DEBUG_ECHOLNPGM("LOGIC E3B - STALL, inc stall"); + DEBUG_ECHOLNPGM("inc stall"); + } + } + else { //phase 3 without stall warning but have OCD warning + DEBUG_ECHOLNPGM("Hardware problem - OCD warning without STALL warning"); + test_phase = 4; + //DEBUG_ECHOLNPGM("LOGIC E3C - not STALLED, hardware problem (quit)"); + } + } break; + + } + + } + else { + switch (test_phase) { + case 0: { // phase 0 without OCD warning - keep on decrementing if can + if (OCD_TH_val) { + OCD_TH_val--; // try lower value + //DEBUG_ECHOLNPGM("LOGIC N0A - DEC OCD"); + DEBUG_ECHOLNPGM("DEC OCD"); + } + else { + test_phase = 2; // at lowest value without warning so skip phase 1 + //DEBUG_ECHOLNPGM("LOGIC N0B - OCD at lowest (go to phase 2)"); + DEBUG_ECHOLNPGM("OCD finalized"); + } + } break; + + case 1: //DEBUG_ECHOLNPGM("LOGIC N1 (go directly to 2)"); // phase 1 without OCD warning - drop directly to phase 2 + DEBUG_ECHOLNPGM("OCD finalized"); + + case 2: { // phase 2 without stall warning - keep on decrementing if can + if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT) { // skip all STALL_TH steps if L6474 + test_phase = 4; + break; + } + if (STALL_TH_val) { + STALL_TH_val--; // try a lower value (stay in phase 2) + //DEBUG_ECHOLNPGM("LOGIC N2B - dec STALL"); + DEBUG_ECHOLNPGM("dec STALL"); + } + else { + DEBUG_ECHOLNPGM("finished - STALL at lowest value but still no stall warning"); + test_phase = 4; + //DEBUG_ECHOLNPGM("LOGIC N2C - STALL at lowest (quit)"); + } + } break; + + case 3: { + if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT) { // skip all STALL_TH steps if L6474 + test_phase = 4; + break; + } + test_phase = 4; + //DEBUG_ECHOLNPGM("LOGIC N3 - finished!"); + DEBUG_ECHOLNPGM("finished!"); + } break; // phase 3 without any warnings - desired exit + } // + } // end of status checks + + if (test_phase != 4) { + for (j = 0; j < driver_count; j++) { // update threshold(s) + L64xxManager.set_param(axis_index[j], L6470_OCD_TH, OCD_TH_val); + if (sh.STATUS_AXIS_LAYOUT != L6474_STATUS_LAYOUT) L64xxManager.set_param(axis_index[j], L6470_STALL_TH, STALL_TH_val); + if (L64xxManager.get_param(axis_index[j], L6470_OCD_TH) != OCD_TH_val) DEBUG_ECHOLNPGM("OCD mismatch"); + if ((L64xxManager.get_param(axis_index[j], L6470_STALL_TH) != STALL_TH_val) && (sh.STATUS_AXIS_LAYOUT != L6474_STATUS_LAYOUT)) DEBUG_ECHOLNPGM("STALL mismatch"); + } + } + + } while (test_phase != 4); + + DEBUG_ECHOLNPGM("."); + if (status_composite) { + #if ENABLED(L6470_CHITCHAT) + for (j = 0; j < driver_count; j++) { + if (j) DEBUG_ECHOPGM("..."); + L64xxManager.error_status_decode(axis_status[j], axis_index[j], + sh.STATUS_AXIS_TH_SD, sh.STATUS_AXIS_TH_WRN, + sh.STATUS_AXIS_STEP_LOSS_A, sh.STATUS_AXIS_STEP_LOSS_B, + sh.STATUS_AXIS_OCD, sh.STATUS_AXIS_LAYOUT); + } + DEBUG_ECHOLNPGM("."); + #endif + DEBUG_ECHOLNPGM("Completed with errors"); + } + else + DEBUG_ECHOLNPGM("Completed with no errors"); + DEBUG_ECHOLNPGM("."); + + L64xxManager.pause_monitor(false); +} + +/** + * M918: increase speed until error or max feedrate achieved (as shown in configuration.h)) + * + * J - select which driver(s) to monitor on multi-driver axis + * 0 - (default) monitor all drivers on the axis or E0 + * 1 - monitor only X, Y, Z, E1 + * 2 - monitor only X2, Y2, Z2, E2 + * Xxxx, Yxxx, Zxxx, Exxx - axis to be monitored with displacement + * xxx (1-255) is distance moved on either side of current position + * + * I - over current threshold + * optional - will report current value from driver if not specified + * + * T - current (mA) setting for TVAL (0 - 4A in 31.25mA increments, rounds down) - L6474 only + * optional - will report current value from driver if not specified + * + * K - value for KVAL_HOLD (0 - 255) (ignored for L6474) + * optional - will report current value from driver if not specified + * + * M - value for microsteps (1 - 128) (optional) + * optional - will report current value from driver if not specified + */ +void GcodeSuite::M918() { + + DEBUG_ECHOLNPGM("M918"); + + L64xxManager.pause_monitor(true); // Keep monitor_driver() from stealing status + + char axis_mon[3][3] = { {" "}, {" "}, {" "} }; // list of Axes to be monitored + L64XX_axis_t axis_index[3]; + uint16_t axis_status[3]; + uint8_t driver_count = 1; + float position_max, position_min; + float final_feedrate; + uint8_t kval_hold; + uint8_t OCD_TH_val = 0; + uint8_t STALL_TH_val = 0; + uint16_t over_current_threshold; + constexpr uint8_t over_current_flag = true; + + const L64XX_Marlin::L64XX_shadow_t &sh = L64xxManager.shadow; + + uint8_t j; // general purpose counter + + if (L64xxManager.get_user_input(driver_count, axis_index, axis_mon, position_max, position_min, final_feedrate, kval_hold, over_current_flag, OCD_TH_val, STALL_TH_val, over_current_threshold)) + return; // quit if invalid user input + + L64xxManager.get_status(axis_index[0]); // populate shadow array + + uint8_t m_steps = parser.byteval('M'); + + if (m_steps != 0) { + LIMIT(m_steps, 1, sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT ? 16 : 128); // L6474 + + uint8_t stepVal; + for (stepVal = 0; stepVal < 8; stepVal++) { // convert to L64xx register value + if (m_steps == 1) break; + m_steps >>= 1; + } + + if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT) + stepVal |= 0x98; // NO SYNC + else + stepVal |= (!SYNC_EN) | SYNC_SEL_1 | stepVal; + + for (j = 0; j < driver_count; j++) { + L64xxManager.set_param(axis_index[j], dSPIN_HARD_HIZ, 0); // can't write STEP register if stepper being powered + // results in an extra NOOP being sent (data 00) + L64xxManager.set_param(axis_index[j], L6470_STEP_MODE, stepVal); // set microsteps + } + } + m_steps = L64xxManager.get_param(axis_index[0], L6470_STEP_MODE) & 0x07; // get microsteps + + DEBUG_ECHOLNPAIR("Microsteps = ", _BV(m_steps)); + DEBUG_ECHOLNPAIR("target (maximum) feedrate = ", final_feedrate); + + const float feedrate_inc = final_feedrate / 10, // Start at 1/10 of max & go up by 1/10 per step + fr_limit = final_feedrate * 0.99f; // Rounding-safe comparison value + float current_feedrate = 0; + + planner.synchronize(); // Wait for moves to complete + + for (j = 0; j < driver_count; j++) + L64xxManager.get_status(axis_index[j]); // Clear error flags + + char temp_axis_string[2] = " "; + temp_axis_string[0] = axis_mon[0][0]; // Need a sprintf format string + //temp_axis_string[1] = '\n'; + + char gcode_string[80]; + uint16_t status_composite = 0; + DEBUG_ECHOLNPGM(".\n.\n."); // Make feedrate outputs easier to read + + do { + current_feedrate += feedrate_inc; + DEBUG_ECHOLNPAIR("...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); + + sprintf_P(gcode_string, PSTR("G0 %s%03d F%03d"), temp_axis_string, uint16_t(position_max), uint16_t(current_feedrate)); + gcode.process_subcommands_now_P(gcode_string); + + planner.synchronize(); + + for (j = 0; j < driver_count; j++) { + axis_status[j] = (~L64xxManager.get_status(axis_index[j])) & 0x0800; // Bits of interest are all active LOW + status_composite |= axis_status[j]; + } + if (status_composite) break; // Break on any error + } while (current_feedrate < fr_limit); + + DEBUG_ECHOPGM("Completed with "); + if (status_composite) { + DEBUG_ECHOLNPGM("errors"); + #if ENABLED(L6470_CHITCHAT) + for (j = 0; j < driver_count; j++) { + if (j) DEBUG_ECHOPGM("..."); + L64xxManager.error_status_decode(axis_status[j], axis_index[j], + sh.STATUS_AXIS_TH_SD, sh.STATUS_AXIS_TH_WRN, + sh.STATUS_AXIS_STEP_LOSS_A, sh.STATUS_AXIS_STEP_LOSS_B, + sh.STATUS_AXIS_OCD, sh.STATUS_AXIS_LAYOUT); + } + #endif + } + else + DEBUG_ECHOLNPGM("no errors"); + + L64xxManager.pause_monitor(false); +} + +#endif // HAS_L64XX diff --git a/Marlin/src/gcode/feature/advance/M900.cpp b/Marlin/src/gcode/feature/advance/M900.cpp new file mode 100644 index 0000000..5c7155d --- /dev/null +++ b/Marlin/src/gcode/feature/advance/M900.cpp @@ -0,0 +1,147 @@ +/** + * 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(LIN_ADVANCE) + +#include "../../gcode.h" +#include "../../../module/planner.h" +#include "../../../module/stepper.h" + +#if ENABLED(EXTRA_LIN_ADVANCE_K) + float other_extruder_advance_K[EXTRUDERS]; + uint8_t lin_adv_slot = 0; +#endif + +/** + * M900: Get or Set Linear Advance K-factor + * T<tool> Which tool to address + * K<factor> Set current advance K factor (Slot 0). + * L<factor> Set secondary advance K factor (Slot 1). Requires EXTRA_LIN_ADVANCE_K. + * S<0/1> Activate slot 0 or 1. Requires EXTRA_LIN_ADVANCE_K. + */ +void GcodeSuite::M900() { + + auto echo_value_oor = [](const char ltr, const bool ten=true) { + SERIAL_CHAR('?'); SERIAL_CHAR(ltr); + SERIAL_ECHOPGM(" value out of range"); + if (ten) SERIAL_ECHOPGM(" (0-10)"); + SERIAL_ECHOLNPGM("."); + }; + + #if EXTRUDERS < 2 + constexpr uint8_t tool_index = 0; + #else + const uint8_t tool_index = parser.intval('T', active_extruder); + if (tool_index >= EXTRUDERS) { + echo_value_oor('T', false); + return; + } + #endif + + float &kref = planner.extruder_advance_K[tool_index], newK = kref; + const float oldK = newK; + + #if ENABLED(EXTRA_LIN_ADVANCE_K) + + float &lref = other_extruder_advance_K[tool_index]; + + const bool old_slot = TEST(lin_adv_slot, tool_index), // The tool's current slot (0 or 1) + new_slot = parser.boolval('S', old_slot); // The passed slot (default = current) + + // If a new slot is being selected swap the current and + // saved K values. Do here so K/L will apply correctly. + if (new_slot != old_slot) { // Not the same slot? + SET_BIT_TO(lin_adv_slot, tool_index, new_slot); // Update the slot for the tool + newK = lref; // Get new K value from backup + lref = oldK; // Save K to backup + } + + // Set the main K value. Apply if the main slot is active. + if (parser.seenval('K')) { + const float K = parser.value_float(); + if (!WITHIN(K, 0, 10)) echo_value_oor('K'); + else if (new_slot) lref = K; // S1 Knn + else newK = K; // S0 Knn + } + + // Set the extra K value. Apply if the extra slot is active. + if (parser.seenval('L')) { + const float L = parser.value_float(); + if (!WITHIN(L, 0, 10)) echo_value_oor('L'); + else if (!new_slot) lref = L; // S0 Lnn + else newK = L; // S1 Lnn + } + + #else + + if (parser.seenval('K')) { + const float K = parser.value_float(); + if (WITHIN(K, 0, 10)) + newK = K; + else + echo_value_oor('K'); + } + + #endif + + if (newK != oldK) { + planner.synchronize(); + kref = newK; + } + + if (!parser.seen_any()) { + + #if ENABLED(EXTRA_LIN_ADVANCE_K) + + #if EXTRUDERS < 2 + SERIAL_ECHOLNPAIR("Advance S", int(new_slot), " K", kref, "(S", int(!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_EOL(); + } + #endif + + #else + + SERIAL_ECHO_START(); + #if EXTRUDERS < 2 + SERIAL_ECHOLNPAIR("Advance K=", planner.extruder_advance_K[0]); + #else + SERIAL_ECHOPGM("Advance K"); + LOOP_L_N(i, EXTRUDERS) { + SERIAL_CHAR(' ', '0' + i, ':'); + SERIAL_DECIMAL(planner.extruder_advance_K[i]); + } + SERIAL_EOL(); + #endif + + #endif + } + +} + +#endif // LIN_ADVANCE diff --git a/Marlin/src/gcode/feature/baricuda/M126-M129.cpp b/Marlin/src/gcode/feature/baricuda/M126-M129.cpp new file mode 100644 index 0000000..edeba0d --- /dev/null +++ b/Marlin/src/gcode/feature/baricuda/M126-M129.cpp @@ -0,0 +1,58 @@ +/** + * 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(BARICUDA) + +#include "../../gcode.h" +#include "../../../feature/baricuda.h" + +#if HAS_HEATER_1 + + /** + * M126: Heater 1 valve open + */ + void GcodeSuite::M126() { baricuda_valve_pressure = parser.byteval('S', 255); } + + /** + * M127: Heater 1 valve close + */ + void GcodeSuite::M127() { baricuda_valve_pressure = 0; } + +#endif // HAS_HEATER_1 + +#if HAS_HEATER_2 + + /** + * M128: Heater 2 valve open + */ + void GcodeSuite::M128() { baricuda_e_to_p_pressure = parser.byteval('S', 255); } + + /** + * M129: Heater 2 valve close + */ + void GcodeSuite::M129() { baricuda_e_to_p_pressure = 0; } + +#endif // HAS_HEATER_2 + +#endif // BARICUDA diff --git a/Marlin/src/gcode/feature/camera/M240.cpp b/Marlin/src/gcode/feature/camera/M240.cpp new file mode 100644 index 0000000..fc350d8 --- /dev/null +++ b/Marlin/src/gcode/feature/camera/M240.cpp @@ -0,0 +1,204 @@ +/** + * 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(PHOTO_GCODE) + +#include "../../gcode.h" +#include "../../../module/motion.h" // for active_extruder and current_position + +#if PIN_EXISTS(CHDK) + millis_t chdk_timeout; // = 0 +#endif + +#if defined(PHOTO_POSITION) && PHOTO_DELAY_MS > 0 + #include "../../../MarlinCore.h" // for idle() +#endif + +#ifdef PHOTO_RETRACT_MM + + #define _PHOTO_RETRACT_MM (PHOTO_RETRACT_MM + 0) + + #include "../../../module/planner.h" + #include "../../../module/temperature.h" + + #if ENABLED(ADVANCED_PAUSE_FEATURE) + #include "../../../feature/pause.h" + #endif + + #ifdef PHOTO_RETRACT_MM + 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); + } + #endif + +#endif + +#if PIN_EXISTS(PHOTOGRAPH) + + FORCE_INLINE void set_photo_pin(const uint8_t state) { + constexpr uint32_t pulse_length = ( + #ifdef PHOTO_PULSES_US + PHOTO_PULSE_DELAY_US + #else + 15 // 15.24 from _delay_ms(0.01524) + #endif + ); + WRITE(PHOTOGRAPH_PIN, state); + delayMicroseconds(pulse_length); + } + + FORCE_INLINE void tweak_photo_pin() { set_photo_pin(HIGH); set_photo_pin(LOW); } + + #ifdef PHOTO_PULSES_US + + inline void pulse_photo_pin(const uint32_t duration, const uint8_t state) { + if (state) { + for (const uint32_t stop = micros() + duration; micros() < stop;) + tweak_photo_pin(); + } + else + delayMicroseconds(duration); + } + + inline void spin_photo_pin() { + static constexpr uint32_t sequence[] = PHOTO_PULSES_US; + LOOP_L_N(i, COUNT(sequence)) + pulse_photo_pin(sequence[i], !(i & 1)); + } + + #else + + constexpr uint8_t NUM_PULSES = 16; + inline void spin_photo_pin() { for (uint8_t i = NUM_PULSES; i--;) tweak_photo_pin(); } + + #endif +#endif + +/** + * M240: Trigger a camera by... + * + * - CHDK : Emulate a Canon RC-1 with a configurable ON duration. + * https://captain-slow.dk/2014/03/09/3d-printing-timelapses/ + * - PHOTOGRAPH_PIN : Pulse a digital pin 16 times. + * 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. + * + * PHOTO_POSITION parameters: + * A - X offset to the return position + * B - Y offset to the return position + * F - Override the XY movement feedrate + * R - Retract/recover length (current units) + * S - Retract/recover feedrate (mm/m) + * X - Move to X before triggering the shutter + * Y - Move to Y before triggering the shutter + * Z - Raise Z by a distance before triggering the shutter + * + * PHOTO_SWITCH_POSITION parameters: + * D - Duration (ms) to hold down switch (Requires PHOTO_SWITCH_MS) + * P - Delay (ms) after triggering the shutter (Requires PHOTO_SWITCH_MS) + * I - Switch trigger position override X + * J - Switch trigger position override Y + */ +void GcodeSuite::M240() { + + #ifdef PHOTO_POSITION + + if (homing_needed_error()) return; + + const xyz_pos_t old_pos = { + current_position.x + parser.linearval('A'), + current_position.y + parser.linearval('B'), + current_position.z + }; + + #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(); + e_move_m240(-rval, sval); + #endif + + feedRate_t fr_mm_s = MMM_TO_MMS(parser.linearval('F')); + if (fr_mm_s) NOLESS(fr_mm_s, 10.0f); + + constexpr xyz_pos_t photo_position = PHOTO_POSITION; + xyz_pos_t raw = { + parser.seenval('X') ? RAW_X_POSITION(parser.value_linear_units()) : photo_position.x, + parser.seenval('Y') ? RAW_Y_POSITION(parser.value_linear_units()) : photo_position.y, + (parser.seenval('Z') ? parser.value_linear_units() : photo_position.z) + current_position.z + }; + apply_motion_limits(raw); + do_blocking_move_to(raw, fr_mm_s); + + #ifdef PHOTO_SWITCH_POSITION + constexpr xy_pos_t photo_switch_position = PHOTO_SWITCH_POSITION; + const xy_pos_t sraw = { + parser.seenval('I') ? RAW_X_POSITION(parser.value_linear_units()) : photo_switch_position.x, + parser.seenval('J') ? RAW_Y_POSITION(parser.value_linear_units()) : photo_switch_position.y + }; + do_blocking_move_to_xy(sraw, get_homing_bump_feedrate(X_AXIS)); + #if PHOTO_SWITCH_MS > 0 + safe_delay(parser.intval('D', PHOTO_SWITCH_MS)); + #endif + do_blocking_move_to(raw); + #endif + + #endif + + #if PIN_EXISTS(CHDK) + + OUT_WRITE(CHDK_PIN, HIGH); + chdk_timeout = millis() + parser.intval('D', PHOTO_SWITCH_MS); + + #elif HAS_PHOTOGRAPH + + spin_photo_pin(); + delay(7.33); + spin_photo_pin(); + + #endif + + #ifdef PHOTO_POSITION + #if PHOTO_DELAY_MS > 0 + const millis_t timeout = millis() + parser.intval('P', PHOTO_DELAY_MS); + while (PENDING(millis(), timeout)) idle(); + #endif + do_blocking_move_to(old_pos, fr_mm_s); + #ifdef PHOTO_RETRACT_MM + e_move_m240(rval, sval); + #endif + #endif +} + +#endif // PHOTO_GCODE diff --git a/Marlin/src/gcode/feature/cancel/M486.cpp b/Marlin/src/gcode/feature/cancel/M486.cpp new file mode 100644 index 0000000..1f14ae0 --- /dev/null +++ b/Marlin/src/gcode/feature/cancel/M486.cpp @@ -0,0 +1,57 @@ +/** + * 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(CANCEL_OBJECTS) + +#include "../../gcode.h" +#include "../../../feature/cancel_object.h" + +/** + * M486: A simple interface to cancel objects + * + * T[count] : Reset objects and/or set the count + * S<index> : Start an object with the given index + * P<index> : Cancel the object with the given index + * U<index> : Un-cancel object with the given index + * C : Cancel the current object (the last index given by S<index>) + * S-1 : Start a non-object like a brim or purge tower that should always print + */ +void GcodeSuite::M486() { + + if (parser.seen('T')) { + cancelable.reset(); + cancelable.object_count = parser.intval('T', 1); + } + + if (parser.seen('S')) + cancelable.set_active_object(parser.value_int()); + + if (parser.seen('C')) cancelable.cancel_active_object(); + + if (parser.seen('P')) cancelable.cancel_object(parser.value_int()); + + if (parser.seen('U')) cancelable.uncancel_object(parser.value_int()); +} + +#endif // CANCEL_OBJECTS diff --git a/Marlin/src/gcode/feature/caselight/M355.cpp b/Marlin/src/gcode/feature/caselight/M355.cpp new file mode 100644 index 0000000..12ae5cf --- /dev/null +++ b/Marlin/src/gcode/feature/caselight/M355.cpp @@ -0,0 +1,73 @@ +/** + * 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(CASE_LIGHT_ENABLE) + +#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')) { + didset = true; + caselight.brightness = parser.value_byte(); + } + #endif + const bool sflag = parser.seenval('S'); + if (sflag) { + didset = true; + caselight.on = parser.value_bool(); + } + 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 (PWM_PIN(CASE_LIGHT_PIN)) { + SERIAL_ECHOLN(int(caselight.brightness)); + return; + } + #endif + SERIAL_ECHOLNPGM(STR_ON); + } +} + +#endif // CASE_LIGHT_ENABLE diff --git a/Marlin/src/gcode/feature/clean/G12.cpp b/Marlin/src/gcode/feature/clean/G12.cpp new file mode 100644 index 0000000..216db5b --- /dev/null +++ b/Marlin/src/gcode/feature/clean/G12.cpp @@ -0,0 +1,80 @@ +/** + * 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(NOZZLE_CLEAN_FEATURE) + +#include "../../../libs/nozzle.h" + +#include "../../gcode.h" +#include "../../parser.h" +#include "../../../module/motion.h" + +#if HAS_LEVELING + #include "../../../module/planner.h" + #include "../../../feature/bedlevel/bedlevel.h" +#endif + +/** + * 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 + */ +void GcodeSuite::G12() { + // Don't allow nozzle cleaning without homing first + 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.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) + | TERN(NOZZLE_CLEAN_NO_Z, 0, (!seenxyz || parser.boolval('Z') ? _BV(Z_AXIS) : 0)) + ; + + #if HAS_LEVELING + // Disable bed leveling if cleaning Z + 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 diff --git a/Marlin/src/gcode/feature/controllerfan/M710.cpp b/Marlin/src/gcode/feature/controllerfan/M710.cpp new file mode 100644 index 0000000..cc45073 --- /dev/null +++ b/Marlin/src/gcode/feature/controllerfan/M710.cpp @@ -0,0 +1,81 @@ +/** + * 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(CONTROLLER_FAN_EDITABLE) + +#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 + * + * R : Reset to defaults + * S[0-255] : Fan speed when motors are active + * I[0-255] : Fan speed when motors are idle + * A[0|1] : Turn auto mode on or off + * D : Set auto mode idle duration + * + * Examples: + * M710 ; Report current Settings + * M710 R ; Reset SIAD to defaults + * M710 I64 ; Set controller fan Idle Speed to 25% + * M710 S255 ; Set controller fan Active Speed to 100% + * M710 S0 ; Set controller fan Active Speed to OFF + * M710 I255 A0 ; Set controller fan Idle Speed to 100% with Auto Mode OFF + * M710 I127 A1 S255 D160 ; Set controller fan idle speed 50%, AutoMode On, Fan speed 100%, duration to 160 Secs + */ +void GcodeSuite::M710() { + + const bool seenR = parser.seen('R'); + if (seenR) controllerFan.reset(); + + const bool seenS = parser.seenval('S'); + if (seenS) controllerFan.settings.active_speed = parser.value_byte(); + + const bool seenI = parser.seenval('I'); + if (seenI) controllerFan.settings.idle_speed = parser.value_byte(); + + const bool seenA = parser.seenval('A'); + if (seenA) controllerFan.settings.auto_mode = parser.value_bool(); + + const bool seenD = parser.seenval('D'); + if (seenD) controllerFan.settings.duration = parser.value_ushort(); + + if (!(seenR || seenS || seenI || seenA || seenD)) + M710_report(false); +} + +#endif // CONTROLLER_FAN_EDITABLE diff --git a/Marlin/src/gcode/feature/digipot/M907-M910.cpp b/Marlin/src/gcode/feature/digipot/M907-M910.cpp new file mode 100644 index 0000000..e463666 --- /dev/null +++ b/Marlin/src/gcode/feature/digipot/M907-M910.cpp @@ -0,0 +1,102 @@ +/** + * 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 ANY(HAS_MOTOR_CURRENT_SPI, HAS_MOTOR_CURRENT_PWM, HAS_MOTOR_CURRENT_I2C, HAS_MOTOR_CURRENT_DAC) + +#include "../../gcode.h" + +#if HAS_MOTOR_CURRENT_SPI || HAS_MOTOR_CURRENT_PWM + #include "../../../module/stepper.h" +#endif + +#if HAS_MOTOR_CURRENT_I2C + #include "../../../feature/digipot/digipot.h" +#endif + +#if ENABLED(HAS_MOTOR_CURRENT_DAC) + #include "../../../feature/dac/stepper_dac.h" +#endif + +/** + * M907: Set digital trimpot motor current using axis codes X, Y, Z, E, B, S + */ +void GcodeSuite::M907() { + #if HAS_MOTOR_CURRENT_SPI + + LOOP_XYZE(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 ANY_PIN(MOTOR_CURRENT_PWM_X, MOTOR_CURRENT_PWM_Y, MOTOR_CURRENT_PWM_XY) + 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.set_digipot_current(1, parser.value_int()); + #endif + #if PIN_EXISTS(MOTOR_CURRENT_PWM_E) + if (parser.seenval('E')) stepper.set_digipot_current(2, parser.value_int()); + #endif + + #endif + + #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()); + // 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()); + #endif + + #if ENABLED(HAS_MOTOR_CURRENT_DAC) + if (parser.seenval('S')) { + const float dac_percent = parser.value_float(); + LOOP_LE_N(i, 4) stepper_dac.set_current_percent(i, dac_percent); + } + LOOP_XYZE(i) if (parser.seenval(axis_codes[i])) stepper_dac.set_current_percent(i, parser.value_float()); + #endif +} + +#if EITHER(HAS_MOTOR_CURRENT_SPI, HAS_MOTOR_CURRENT_DAC) + + /** + * M908: Control digital trimpot directly (M908 P<pin> S<current>) + */ + void GcodeSuite::M908() { + 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))); + } + + #if ENABLED(HAS_MOTOR_CURRENT_DAC) + + void GcodeSuite::M909() { stepper_dac.print_values(); } + void GcodeSuite::M910() { stepper_dac.commit_eeprom(); } + + #endif // HAS_MOTOR_CURRENT_DAC + +#endif // HAS_MOTOR_CURRENT_SPI || HAS_MOTOR_CURRENT_DAC + +#endif // HAS_MOTOR_CURRENT_SPI || HAS_MOTOR_CURRENT_PWM || HAS_MOTOR_CURRENT_I2C || HAS_MOTOR_CURRENT_DAC diff --git a/Marlin/src/gcode/feature/filwidth/M404-M407.cpp b/Marlin/src/gcode/feature/filwidth/M404-M407.cpp new file mode 100644 index 0000000..a70f7a6 --- /dev/null +++ b/Marlin/src/gcode/feature/filwidth/M404-M407.cpp @@ -0,0 +1,71 @@ +/** + * 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(FILAMENT_WIDTH_SENSOR) + +#include "../../../feature/filwidth.h" +#include "../../../module/planner.h" +#include "../../../MarlinCore.h" +#include "../../gcode.h" + +/** + * M404: Display or set (in current units) the nominal filament width (3mm, 1.75mm ) W<3.0> + */ +void GcodeSuite::M404() { + if (parser.seenval('W')) { + filwidth.nominal_mm = parser.value_linear_units(); + planner.volumetric_area_nominal = CIRCLE_AREA(filwidth.nominal_mm * 0.5); + } + else + SERIAL_ECHOLNPAIR("Filament dia (nominal mm):", filwidth.nominal_mm); +} + +/** + * M405: Turn on filament sensor for control + */ +void GcodeSuite::M405() { + // This is technically a linear measurement, but since it's quantized to centimeters and is a different + // unit than everything else, it uses parser.value_byte() instead of parser.value_linear_units(). + if (parser.seenval('D')) + filwidth.set_delay_cm(parser.value_byte()); + + filwidth.enable(true); +} + +/** + * M406: Turn off filament sensor for control + */ +void GcodeSuite::M406() { + filwidth.enable(false); + planner.calculate_volumetric_multipliers(); // Restore correct 'volumetric_multiplier' value +} + +/** + * M407: Get measured filament diameter on serial output + */ +void GcodeSuite::M407() { + SERIAL_ECHOLNPAIR("Filament dia (measured mm):", filwidth.measured_mm); +} + +#endif // FILAMENT_WIDTH_SENSOR diff --git a/Marlin/src/gcode/feature/fwretract/G10_G11.cpp b/Marlin/src/gcode/feature/fwretract/G10_G11.cpp new file mode 100644 index 0000000..219502f --- /dev/null +++ b/Marlin/src/gcode/feature/fwretract/G10_G11.cpp @@ -0,0 +1,51 @@ +/** + * 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(FWRETRACT) + +#include "../../../feature/fwretract.h" +#include "../../gcode.h" +#include "../../../module/motion.h" + +/** + * 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 HAS_MULTI_EXTRUDER + const bool rs = parser.boolval('S'); + #endif + fwretract.retract(true + #if HAS_MULTI_EXTRUDER + , rs + #endif + ); +} + +/** + * G11 - Recover filament according to settings of M208 + */ +void GcodeSuite::G11() { fwretract.retract(false); } + +#endif // FWRETRACT diff --git a/Marlin/src/gcode/feature/fwretract/M207-M209.cpp b/Marlin/src/gcode/feature/fwretract/M207-M209.cpp new file mode 100644 index 0000000..538f16c --- /dev/null +++ b/Marlin/src/gcode/feature/fwretract/M207-M209.cpp @@ -0,0 +1,74 @@ +/** + * 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(FWRETRACT) + +#include "../../../feature/fwretract.h" +#include "../../gcode.h" + +/** + * M207: Set firmware retraction values + * + * S[+units] retract_length + * W[+units] swap_retract_length (multi-extruder) + * 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); +} + +/** + * M208: Set firmware un-retraction values + * + * S[+units] retract_recover_extra (in addition to M207 S*) + * W[+units] swap_retract_recover_extra (multi-extruder) + * 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); +} + +#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. + */ + void GcodeSuite::M209() { + if (MIN_AUTORETRACT <= MAX_AUTORETRACT && parser.seen('S')) + fwretract.enable_autoretract(parser.value_bool()); + } + +#endif // FWRETRACT_AUTORETRACT + +#endif // FWRETRACT diff --git a/Marlin/src/gcode/feature/i2c/M260_M261.cpp b/Marlin/src/gcode/feature/i2c/M260_M261.cpp new file mode 100644 index 0000000..438d152 --- /dev/null +++ b/Marlin/src/gcode/feature/i2c/M260_M261.cpp @@ -0,0 +1,76 @@ +/** + * 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(EXPERIMENTAL_I2CBUS) + +#include "../../gcode.h" + +#include "../../../feature/twibus.h" + +/** + * M260: Send data to a I2C slave device + * + * 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 + * + * M260 B<byte-1 value in base 10> + * M260 B<byte-2 value in base 10> + * M260 B<byte-3 value in base 10> + * + * 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 + if (parser.seen('A')) i2c.address(parser.value_byte()); + + // Add a new byte to the buffer + if (parser.seen('B')) i2c.addbyte(parser.value_byte()); + + // Flush the buffer to the bus + if (parser.seen('S')) i2c.send(); + + // Reset and rewind the buffer + else if (parser.seen('R')) i2c.reset(); +} + +/** + * M261: Request X bytes from I2C slave device + * + * Usage: M261 A<slave device address base 10> B<number of bytes> + */ +void GcodeSuite::M261() { + if (parser.seen('A')) i2c.address(parser.value_byte()); + + uint8_t bytes = parser.byteval('B', 1); + + if (i2c.addr && bytes && bytes <= TWIBUS_BUFFER_SIZE) + i2c.relay(bytes); + else + SERIAL_ERROR_MSG("Bad i2c request"); +} + +#endif diff --git a/Marlin/src/gcode/feature/leds/M150.cpp b/Marlin/src/gcode/feature/leds/M150.cpp new file mode 100644 index 0000000..cf09bf1 --- /dev/null +++ b/Marlin/src/gcode/feature/leds/M150.cpp @@ -0,0 +1,84 @@ +/** + * 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_COLOR_LEDS + +#include "../../gcode.h" +#include "../../../feature/leds/leds.h" + +/** + * M150: Set Status LED Color - Use R-U-B-W for R-G-B-W + * and Brightness - Use P (for NEOPIXEL only) + * + * 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 + * M150 R255 U127 ; Turn LED orange (PWM only) + * M150 ; Turn LED off + * M150 R U B ; Turn LED white + * 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() { + #if ENABLED(NEOPIXEL_LED) + const uint8_t index = parser.intval('I', -1); + #if ENABLED(NEOPIXEL2_SEPARATE) + const uint8_t unit = parser.intval('S'), + brightness = unit ? neo2.brightness() : neo.brightness(); + *(unit ? &neo2.neoindex : &neo.neoindex) = index; + #else + const uint8_t brightness = neo.brightness(); + neo.neoindex = index; + #endif + #endif + + const LEDColor color = MakeLEDColor( + 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) : brightness + ); + + #if ENABLED(NEOPIXEL2_SEPARATE) + if (unit == 1) { leds2.set_color(color); return; } + #endif + + leds.set_color(color); +} + +#endif // HAS_COLOR_LEDS diff --git a/Marlin/src/gcode/feature/leds/M7219.cpp b/Marlin/src/gcode/feature/leds/M7219.cpp new file mode 100644 index 0000000..a6ee711 --- /dev/null +++ b/Marlin/src/gcode/feature/leds/M7219.cpp @@ -0,0 +1,93 @@ +/** + * 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(MAX7219_GCODE) + +#include "../../gcode.h" +#include "../../../feature/max7219.h" + +/** + * M7219: Control the Max7219 LED matrix + * + * I - Initialize (clear) the matrix + * F - Fill the matrix (set all bits) + * P - Dump the led_line[] array values + * C<column> - Set a column to the bitmask given by 'V' (Units 0-3 in portrait layout) + * R<row> - Set a row to the bitmask given by 'V' (Units 0-3 in landscape layout) + * X<pos> - X index of an LED to set or toggle + * Y<pos> - Y index of an LED to set or toggle + * V<value> - LED on/off state or row/column bitmask (8, 16, 24, or 32-bits) + * ('C' / 'R' can be used to update up to 4 units at once) + * + * Directly set a native matrix row to the 8-bit value 'V': + * D<line> - Display line (0..7) + * U<unit> - Unit index (0..MAX7219_NUMBER_UNITS-1) + */ +void GcodeSuite::M7219() { + if (parser.seen('I')) { + max7219.register_setup(); + max7219.clear(); + } + + if (parser.seen('F')) max7219.fill(); + + const uint32_t v = parser.ulongval('V'); + + if (parser.seenval('R')) { + const uint8_t r = parser.value_byte(); + max7219.set_row(r, v); + } + else if (parser.seenval('C')) { + const uint8_t c = parser.value_byte(); + max7219.set_column(c, v); + } + else if (parser.seenval('X') || parser.seenval('Y')) { + const uint8_t x = parser.byteval('X'), y = parser.byteval('Y'); + if (parser.seenval('V')) + max7219.led_set(x, y, v > 0); + else + max7219.led_toggle(x, y); + } + else if (parser.seen('D')) { + const uint8_t uline = parser.value_byte() & 0x7, + line = uline + (parser.byteval('U') << 3); + if (line < MAX7219_LINES) { + max7219.led_line[line] = v; + return max7219.refresh_line(line); + } + } + + if (parser.seen('P')) { + LOOP_L_N(r, MAX7219_LINES) { + SERIAL_ECHOPGM("led_line["); + if (r < 10) SERIAL_CHAR(' '); + SERIAL_ECHO(int(r)); + SERIAL_ECHOPGM("]="); + for (uint8_t b = 8; b--;) SERIAL_CHAR('0' + TEST(max7219.led_line[r], b)); + SERIAL_EOL(); + } + } +} + +#endif // MAX7219_GCODE diff --git a/Marlin/src/gcode/feature/macro/M810-M819.cpp b/Marlin/src/gcode/feature/macro/M810-M819.cpp new file mode 100644 index 0000000..7b9e1a1 --- /dev/null +++ b/Marlin/src/gcode/feature/macro/M810-M819.cpp @@ -0,0 +1,65 @@ +/** + * 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(GCODE_MACROS) + +#include "../../gcode.h" +#include "../../queue.h" +#include "../../parser.h" + +char gcode_macros[GCODE_MACROS_SLOTS][GCODE_MACROS_SLOT_SIZE + 1] = {{ 0 }}; + +/** + * M810_819: Set/execute a G-code macro. + * + * Usage: + * M810 <command>|... Set Macro 0 to the given commands, separated by the pipe character + * M810 Execute Macro 0 + */ +void GcodeSuite::M810_819() { + const uint8_t index = parser.codenum - 810; + if (index >= GCODE_MACROS_SLOTS) return; + + const size_t len = strlen(parser.string_arg); + + if (len) { + // Set a macro + if (len > GCODE_MACROS_SLOT_SIZE) + SERIAL_ERROR_MSG("Macro too long."); + else { + char c, *s = parser.string_arg, *d = gcode_macros[index]; + do { + c = *s++; + *d++ = c == '|' ? '\n' : c; + } while (c); + } + } + else { + // Execute a macro + char * const cmd = gcode_macros[index]; + if (strlen(cmd)) process_subcommands_now(cmd); + } +} + +#endif // GCODE_MACROS diff --git a/Marlin/src/gcode/feature/mixing/M163-M165.cpp b/Marlin/src/gcode/feature/mixing/M163-M165.cpp new file mode 100644 index 0000000..a4cb64e --- /dev/null +++ b/Marlin/src/gcode/feature/mixing/M163-M165.cpp @@ -0,0 +1,101 @@ +/** + * 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(MIXING_EXTRUDER) + +#include "../../gcode.h" +#include "../../../feature/mixing.h" + +/** + * M163: Set a single mix factor for a mixing extruder + * This is called "weight" by some systems. + * Must be followed by M164 to normalize and commit them. + * + * S[index] The channel index to set + * P[float] The mix value + */ +void GcodeSuite::M163() { + const int mix_index = parser.intval('S'); + if (mix_index < MIXING_STEPPERS) + mixer.set_collector(mix_index, parser.floatval('P')); +} + +/** + * M164: Normalize and commit the mix. + * + * S[index] The virtual tool to store + * If 'S' is omitted update the active virtual tool. + */ +void GcodeSuite::M164() { + #if MIXING_VIRTUAL_TOOLS > 1 + const int tool_index = parser.intval('S', -1); + #else + constexpr int tool_index = 0; + #endif + if (tool_index >= 0) { + if (tool_index < MIXING_VIRTUAL_TOOLS) + mixer.normalize(tool_index); + } + else + mixer.normalize(); +} + +#if ENABLED(DIRECT_MIXING_IN_G1) + + /** + * M165: Set multiple mix factors for a mixing extruder. + * Omitted factors will be set to 0. + * The mix is normalized and stored in the current virtual tool. + * + * A[factor] Mix factor for extruder stepper 1 + * B[factor] Mix factor for extruder stepper 2 + * C[factor] Mix factor for extruder stepper 3 + * D[factor] Mix factor for extruder stepper 4 + * H[factor] Mix factor for extruder stepper 5 + * I[factor] Mix factor for extruder stepper 6 + */ + void GcodeSuite::M165() { + // Get mixing parameters from the GCode + // The total "must" be 1.0 (but it will be normalized) + // If no mix factors are given, the old mix is preserved + const char mixing_codes[] = { LIST_N(MIXING_STEPPERS, 'A', 'B', 'C', 'D', 'H', 'I') }; + uint8_t mix_bits = 0; + MIXER_STEPPER_LOOP(i) { + if (parser.seenval(mixing_codes[i])) { + SBI(mix_bits, i); + mixer.set_collector(i, parser.value_float()); + } + } + // If any mixing factors were included, clear the rest + // If none were included, preserve the last mix + if (mix_bits) { + MIXER_STEPPER_LOOP(i) + if (!TEST(mix_bits, i)) mixer.set_collector(i, 0.0f); + mixer.normalize(); + } + } + +#endif // DIRECT_MIXING_IN_G1 + +#endif // MIXING_EXTRUDER diff --git a/Marlin/src/gcode/feature/mixing/M166.cpp b/Marlin/src/gcode/feature/mixing/M166.cpp new file mode 100644 index 0000000..9e071a4 --- /dev/null +++ b/Marlin/src/gcode/feature/mixing/M166.cpp @@ -0,0 +1,103 @@ +/** + * 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(GRADIENT_MIX) + +#include "../../gcode.h" +#include "../../../module/motion.h" +#include "../../../module/planner.h" +#include "../../../feature/mixing.h" + +inline void echo_mix() { + SERIAL_ECHOPAIR(" (", int(mixer.mix[0]), "%|", int(mixer.mix[1]), "%)"); +} + +inline void echo_zt(const int t, const float &z) { + mixer.update_mix_from_vtool(t); + SERIAL_ECHOPAIR_P(SP_Z_STR, z, SP_T_STR, t); + echo_mix(); +} + +/** + * M166: Set a simple gradient mix for a two-component mixer + * based on the Geeetech A10M implementation by Jone Liu. + * + * S[bool] - Enable / disable gradients + * A[float] - Starting Z for the gradient + * Z[float] - Ending Z for the gradient. (Must be greater than the starting Z.) + * I[index] - V-Tool to use as the starting mix. + * J[index] - V-Tool to use as the ending mix. + * + * T[index] - A V-Tool index to use as an alias for the Gradient (Requires GRADIENT_VTOOL) + * T with no index clears the setting. Note: This can match the I or J value. + * + * Example: M166 S1 A0 Z20 I0 J1 + */ +void GcodeSuite::M166() { + if (parser.seenval('A')) mixer.gradient.start_z = parser.value_float(); + if (parser.seenval('Z')) mixer.gradient.end_z = parser.value_float(); + if (parser.seenval('I')) mixer.gradient.start_vtool = (uint8_t)constrain(parser.value_int(), 0, MIXING_VIRTUAL_TOOLS); + if (parser.seenval('J')) mixer.gradient.end_vtool = (uint8_t)constrain(parser.value_int(), 0, MIXING_VIRTUAL_TOOLS); + + #if ENABLED(GRADIENT_VTOOL) + if (parser.seen('T')) mixer.gradient.vtool_index = parser.byteval('T', -1); + #endif + + if (parser.seen('S')) mixer.gradient.enabled = parser.value_bool(); + + mixer.refresh_gradient(); + + SERIAL_ECHOPGM("Gradient Mix "); + serialprint_onoff(mixer.gradient.enabled); + if (mixer.gradient.enabled) { + + #if ENABLED(GRADIENT_VTOOL) + if (mixer.gradient.vtool_index >= 0) { + SERIAL_ECHOPAIR(" (T", int(mixer.gradient.vtool_index)); + SERIAL_CHAR(')'); + } + #endif + + SERIAL_ECHOPGM(" ; Start"); + echo_zt(mixer.gradient.start_vtool, mixer.gradient.start_z); + + SERIAL_ECHOPGM(" ; End"); + echo_zt(mixer.gradient.end_vtool, mixer.gradient.end_z); + + mixer.update_mix_from_gradient(); + + 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(); + } + + SERIAL_EOL(); +} + +#endif // GRADIENT_MIX diff --git a/Marlin/src/gcode/feature/network/M552-M554.cpp b/Marlin/src/gcode/feature/network/M552-M554.cpp new file mode 100644 index 0000000..6ea15fe --- /dev/null +++ b/Marlin/src/gcode/feature/network/M552-M554.cpp @@ -0,0 +1,126 @@ +/** + * 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], 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_ECHOPGM_P(post); + SERIAL_EOL(); +} +void M552_report() { + ip_report(552, PSTR("ip address"), Ethernet.linkStatus() == LinkON ? Ethernet.localIP() : ethernet.ip); +} +void M553_report() { + ip_report(553, PSTR("subnet mask"), Ethernet.linkStatus() == LinkON ? Ethernet.subnetMask() : ethernet.subnet); +} +void M554_report() { + ip_report(554, PSTR("gateway"), Ethernet.linkStatus() == LinkON ? Ethernet.gatewayIP() : ethernet.gateway); +} + +/** + * 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(); +} + +/** + * M553 Pnnn - Set netmask + */ +void GcodeSuite::M553() { + if (parser.seenval('P')) ethernet.subnet.fromString(parser.value_string()); + M553_report(); +} + +/** + * M554 Pnnn - Set Gateway + */ +void GcodeSuite::M554() { + if (parser.seenval('P')) ethernet.gateway.fromString(parser.value_string()); + M554_report(); +} + +#endif // HAS_ETHERNET diff --git a/Marlin/src/gcode/feature/password/M510-M512.cpp b/Marlin/src/gcode/feature/password/M510-M512.cpp new file mode 100644 index 0000000..eeb9b1d --- /dev/null +++ b/Marlin/src/gcode/feature/password/M510-M512.cpp @@ -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_ECHOLNPAIR(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 diff --git a/Marlin/src/gcode/feature/pause/G27.cpp b/Marlin/src/gcode/feature/pause/G27.cpp new file mode 100644 index 0000000..3ce618d --- /dev/null +++ b/Marlin/src/gcode/feature/pause/G27.cpp @@ -0,0 +1,41 @@ +/** + * 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(NOZZLE_PARK_FEATURE) + +#include "../../gcode.h" +#include "../../../libs/nozzle.h" +#include "../../../module/motion.h" + +/** + * G27: Park the nozzle + */ +void GcodeSuite::G27() { + // Don't allow nozzle parking without homing first + if (homing_needed_error()) return; + nozzle.park(parser.ushortval('P')); +} + +#endif // NOZZLE_PARK_FEATURE diff --git a/Marlin/src/gcode/feature/pause/G60.cpp b/Marlin/src/gcode/feature/pause/G60.cpp new file mode 100644 index 0000000..6f695b9 --- /dev/null +++ b/Marlin/src/gcode/feature/pause/G60.cpp @@ -0,0 +1,58 @@ +/** + * 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 SAVED_POSITIONS + +#include "../../gcode.h" +#include "../../../module/motion.h" + +#define DEBUG_OUT ENABLED(SAVED_POSITIONS_DEBUG) +#include "../../../core/debug_out.h" + +/** + * G60: Save current position + * + * S<slot> - Memory slot # (0-based) to save into (default 0) + */ +void GcodeSuite::G60() { + const uint8_t slot = parser.byteval('S'); + + if (slot >= SAVED_POSITIONS) { + SERIAL_ERROR_MSG(STR_INVALID_POS_SLOT STRINGIFY(SAVED_POSITIONS)); + return; + } + + stored_position[slot] = current_position; + SBI(saved_slots[slot >> 3], slot & 0x07); + + #if ENABLED(SAVED_POSITIONS_DEBUG) + 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); + #endif +} + +#endif // SAVED_POSITIONS diff --git a/Marlin/src/gcode/feature/pause/G61.cpp b/Marlin/src/gcode/feature/pause/G61.cpp new file mode 100644 index 0000000..5d89af0 --- /dev/null +++ b/Marlin/src/gcode/feature/pause/G61.cpp @@ -0,0 +1,73 @@ +/** + * 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 SAVED_POSITIONS + +#include "../../../module/planner.h" +#include "../../gcode.h" +#include "../../../module/motion.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. + */ +void GcodeSuite::G61(void) { + + const uint8_t slot = parser.byteval('S'); + + #if SAVED_POSITIONS < 256 + if (slot >= SAVED_POSITIONS) { + SERIAL_ERROR_MSG(STR_INVALID_POS_SLOT STRINGIFY(SAVED_POSITIONS)); + return; + } + #endif + + // No saved position? No axes being restored? + if (!TEST(saved_slots[slot >> 3], slot & 0x07) || !parser.seen("XYZ")) return; + + 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]); + } + SERIAL_EOL(); + + // 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); + + // Move to the saved position + prepare_line_to_destination(); + + feedrate_mm_s = saved_feedrate; +} + +#endif // SAVED_POSITIONS diff --git a/Marlin/src/gcode/feature/pause/M125.cpp b/Marlin/src/gcode/feature/pause/M125.cpp new file mode 100644 index 0000000..9391b86 --- /dev/null +++ b/Marlin/src/gcode/feature/pause/M125.cpp @@ -0,0 +1,90 @@ +/** + * 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(PARK_HEAD_ON_PAUSE) + +#include "../../gcode.h" +#include "../../parser.h" +#include "../../../feature/pause.h" +#include "../../../lcd/marlinui.h" +#include "../../../module/motion.h" +#include "../../../module/printcounter.h" +#include "../../../sd/cardreader.h" + +#if ENABLED(POWER_LOSS_RECOVERY) + #include "../../../feature/powerloss.h" +#endif + +/** + * M125: Store current position and move to parking position. + * Called on pause (by M25) to prevent material leaking onto the + * object. On resume (M24) the head will be moved back and the + * print will resume. + * + * When not actively SD printing, M125 simply moves to the park + * position and waits, resuming with a button click or M108. + * Without PARK_HEAD_ON_PAUSE the M125 command does nothing. + * + * 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) : (PAUSE_PARK_RETRACT_LENGTH)); + + xyz_pos_t park_point = NOZZLE_PARK_POINT; + + // Move XY axes to filament change position or given position + if (parser.seenval('X')) park_point.x = RAW_X_POSITION(parser.linearval('X')); + if (parser.seenval('Y')) park_point.y = RAW_X_POSITION(parser.linearval('Y')); + + // Lift Z axis + if (parser.seenval('Z')) park_point.z = parser.linearval('Z'); + + #if HAS_HOTEND_OFFSET && NONE(DUAL_X_CARRIAGE, DELTA) + park_point += hotend_offset[active_extruder]; + #endif + + const bool sd_printing = TERN0(SDSUPPORT, IS_SD_PRINTING()); + + ui.pause_show_message(PAUSE_MESSAGE_PARKING, PAUSE_MODE_PAUSE_PRINT); + + // 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, 0, show_lcd)) { + TERN_(POWER_LOSS_RECOVERY, if (recovery.enabled) recovery.save(true)); + if (ENABLED(EXTENSIBLE_UI) || !sd_printing || show_lcd) { + wait_for_confirmation(false, 0); + resume_print(0, 0, -retract, 0); + } + } +} + +#endif // PARK_HEAD_ON_PAUSE diff --git a/Marlin/src/gcode/feature/pause/M600.cpp b/Marlin/src/gcode/feature/pause/M600.cpp new file mode 100644 index 0000000..1c282f2 --- /dev/null +++ b/Marlin/src/gcode/feature/pause/M600.cpp @@ -0,0 +1,172 @@ +/** + * 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(ADVANCED_PAUSE_FEATURE) + +#include "../../gcode.h" +#include "../../../feature/pause.h" +#include "../../../module/motion.h" +#include "../../../module/printcounter.h" +#include "../../../lcd/marlinui.h" + +#if HAS_MULTI_EXTRUDER + #include "../../../module/tool_change.h" +#endif + +#if ENABLED(MMU2_MENUS) + #include "../../../lcd/menu/menu_mmu2.h" +#endif + +#if ENABLED(MIXING_EXTRUDER) + #include "../../../feature/mixing.h" +#endif + +#if HAS_FILAMENT_SENSOR + #include "../../../feature/runout.h" +#endif + +/** + * M600: Pause for filament change + * + * E[distance] - Retract the filament this far + * Z[distance] - Move the Z axis by this distance + * X[position] - Move to this X position, with Y + * Y[position] - Move to this Y position, with X + * U[distance] - Retract distance for removal (manual reload) + * 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() { + + #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('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 (idex_is_duplicating()) + DXC_ext = (READ(FIL_RUNOUT2_PIN) == FIL_RUNOUT2_STATE) ? 1 : 0; + #else + DXC_ext = active_extruder; + #endif + } + #endif + + // 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(HOME_BEFORE_FILAMENT_CHANGE) + // If needed, home before parking for filament change + if (!all_axes_trusted()) home_all_axes(true); + #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) : (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'); + + // 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 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); + + // 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', -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, (parser.seenval('R') ? parser.value_celsius() : 0) DXC_PASS); + #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 + + TERN_(MIXING_EXTRUDER, mixer.T(old_mixing_tool)); // Restore original mixing tool +} + +#endif // ADVANCED_PAUSE_FEATURE diff --git a/Marlin/src/gcode/feature/pause/M603.cpp b/Marlin/src/gcode/feature/pause/M603.cpp new file mode 100644 index 0000000..9c3b774 --- /dev/null +++ b/Marlin/src/gcode/feature/pause/M603.cpp @@ -0,0 +1,65 @@ +/** + * 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(ADVANCED_PAUSE_FEATURE) + +#include "../../gcode.h" +#include "../../../feature/pause.h" +#include "../../../module/motion.h" +#include "../../../module/printcounter.h" + +#if HAS_MULTI_EXTRUDER + #include "../../../module/tool_change.h" +#endif + +/** + * M603: Configure filament change + * + * 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() { + + const int8_t target_extruder = get_target_extruder_from_command(); + if (target_extruder < 0) return; + + // Unload length + if (parser.seen('U')) { + fc_settings[target_extruder].unload_length = ABS(parser.value_axis_units(E_AXIS)); + #if ENABLED(PREVENT_LENGTHY_EXTRUDE) + NOMORE(fc_settings[target_extruder].unload_length, EXTRUDE_MAXLENGTH); + #endif + } + + // Load length + if (parser.seen('L')) { + fc_settings[target_extruder].load_length = ABS(parser.value_axis_units(E_AXIS)); + #if ENABLED(PREVENT_LENGTHY_EXTRUDE) + NOMORE(fc_settings[target_extruder].load_length, EXTRUDE_MAXLENGTH); + #endif + } +} + +#endif // ADVANCED_PAUSE_FEATURE diff --git a/Marlin/src/gcode/feature/pause/M701_M702.cpp b/Marlin/src/gcode/feature/pause/M701_M702.cpp new file mode 100644 index 0000000..9a2b774 --- /dev/null +++ b/Marlin/src/gcode/feature/pause/M701_M702.cpp @@ -0,0 +1,235 @@ +/** + * 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(FILAMENT_LOAD_UNLOAD_GCODES) + +#include "../../gcode.h" +#include "../../../MarlinCore.h" +#include "../../../module/motion.h" +#include "../../../module/temperature.h" +#include "../../../feature/pause.h" +#include "../../../lcd/marlinui.h" + +#if HAS_MULTI_EXTRUDER + #include "../../../module/tool_change.h" +#endif + +#if HAS_PRUSA_MMU2 + #include "../../../feature/mmu/mmu2.h" +#endif + +#if ENABLED(MIXING_EXTRUDER) + #include "../../../feature/mixing.h" +#endif + +/** + * M701: Load filament + * + * T<extruder> - Extruder number. Required for mixing extruder. + * For non-mixing, current extruder if omitted. + * Z<distance> - Move the Z axis by this distance + * L<distance> - Extrude distance for insertion (positive value) (manual reload) + * + * Default values are used for omitted arguments. + */ +void GcodeSuite::M701() { + xyz_pos_t park_point = NOZZLE_PARK_POINT; + + // 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(); + 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 + + // Z axis lift + if (parser.seenval('Z')) park_point.z = parser.linearval('Z'); + + // Show initial "wait for load" message + ui.pause_show_message(PAUSE_MESSAGE_LOAD, PAUSE_MODE_LOAD_FILAMENT, target_extruder); + + #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)); + + // Load filament + #if HAS_PRUSA_MMU2 + mmu2.load_filament_to_nozzle(target_extruder); + #else + constexpr float purge_length = ADVANCED_PAUSE_PURGE_LENGTH, + slow_load_length = FILAMENT_CHANGE_SLOW_LOAD_LENGTH; + const float fast_load_length = ABS(parser.seen('L') ? parser.value_axis_units(E_AXIS) + : fc_settings[active_extruder].load_length); + load_filament( + slow_load_length, fast_load_length, purge_length, + FILAMENT_CHANGE_ALERT_BEEPS, + 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 + ); + #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)); + + #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 + + TERN_(MIXING_EXTRUDER, mixer.T(old_mixing_tool)); // Restore original mixing tool + + // Show status screen + ui.pause_show_message(PAUSE_MESSAGE_STATUS); +} + +/** + * M702: Unload filament + * + * T<extruder> - Extruder number. Required for mixing extruder. + * For non-mixing, if omitted, current extruder + * (or ALL extruders with FILAMENT_UNLOAD_ALL_EXTRUDERS). + * Z<distance> - Move the Z axis by this distance + * U<distance> - Retract distance for removal (manual reload) + * + * Default values are used for omitted arguments. + */ +void GcodeSuite::M702() { + xyz_pos_t park_point = NOZZLE_PARK_POINT; + + // 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; + const bool seenT = parser.seenval('T'); + if (!seenT) { + mixer.T(MIXER_AUTORETRACT_TOOL); + mix_multiplier = MIXING_STEPPERS; + } + #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(); + } + + const int8_t target_extruder = active_extruder; + #else + const int8_t target_extruder = get_target_extruder_from_command(); + if (target_extruder < 0) return; + #endif + + // Z axis lift + if (parser.seenval('Z')) park_point.z = parser.linearval('Z'); + + // Show initial "wait for unload" message + ui.pause_show_message(PAUSE_MESSAGE_UNLOAD, PAUSE_MODE_UNLOAD_FILAMENT, target_extruder); + + #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)); + + // Unload filament + #if HAS_PRUSA_MMU2 + mmu2.unload(); + #else + #if BOTH(HAS_MULTI_EXTRUDER, FILAMENT_UNLOAD_ALL_EXTRUDERS) + if (!parser.seenval('T')) { + HOTEND_LOOP() { + if (e != active_extruder) tool_change(e, false); + unload_filament(-fc_settings[e].unload_length, true, PAUSE_MODE_UNLOAD_FILAMENT); + } + } + else + #endif + { + // Unload length + const float unload_length = -ABS(parser.seen('U') ? parser.value_axis_units(E_AXIS) + : fc_settings[target_extruder].unload_length); + + unload_filament(unload_length, true, PAUSE_MODE_UNLOAD_FILAMENT + #if ALL(FILAMENT_UNLOAD_ALL_EXTRUDERS, MIXING_EXTRUDER) + , mix_multiplier + #endif + ); + } + #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)); + + #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 + + TERN_(MIXING_EXTRUDER, mixer.T(old_mixing_tool)); // Restore original mixing tool + + // Show status screen + ui.pause_show_message(PAUSE_MESSAGE_STATUS); +} + +#endif // ADVANCED_PAUSE_FEATURE diff --git a/Marlin/src/gcode/feature/power_monitor/M430.cpp b/Marlin/src/gcode/feature/power_monitor/M430.cpp new file mode 100644 index 0000000..9559404 --- /dev/null +++ b/Marlin/src/gcode/feature/power_monitor/M430.cpp @@ -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 HAS_POWER_MONITOR_VREF + 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_ECHOLNPAIR( + #if ENABLED(POWER_MONITOR_CURRENT) + "Current: ", power_monitor.getAmps(), "A" + #if HAS_POWER_MONITOR_VREF + " " + #endif + #endif + #if HAS_POWER_MONITOR_VREF + "Voltage: ", power_monitor.getVolts(), "V" + #endif + #if HAS_POWER_MONITOR_WATTS + " Power: ", power_monitor.getPower(), "W" + #endif + ); + } +} + +#endif // HAS_POWER_MONITOR diff --git a/Marlin/src/gcode/feature/powerloss/M1000.cpp b/Marlin/src/gcode/feature/powerloss/M1000.cpp new file mode 100644 index 0000000..14c9253 --- /dev/null +++ b/Marlin/src/gcode/feature/powerloss/M1000.cpp @@ -0,0 +1,89 @@ +/** + * 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(POWER_LOSS_RECOVERY) + +#include "../../gcode.h" +#include "../../../feature/powerloss.h" +#include "../../../module/motion.h" +#include "../../../lcd/marlinui.h" +#if ENABLED(EXTENSIBLE_UI) + #include "../../../lcd/extui/ui_api.h" +#endif + +#define DEBUG_OUT ENABLED(DEBUG_POWER_LOSS_RECOVERY) +#include "../../../core/debug_out.h" + +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(" Job Recovery Data"); + #else + UNUSED(prefix); + #endif +} + +#if HAS_LCD_MENU + void lcd_power_loss_recovery_cancel(); +#endif + +/** + * M1000: Resume from power-loss (undocumented) + * - With 'S' go to the Resume/Cancel menu + * - With no parameters, run recovery commands + */ +void GcodeSuite::M1000() { + + if (recovery.valid()) { + if (parser.seen('S')) { + #if HAS_LCD_MENU + ui.goto_screen(menu_job_recovery); + #elif ENABLED(DWIN_CREALITY_LCD) + recovery.dwin_flag = true; + #elif ENABLED(EXTENSIBLE_UI) + ExtUI::onPowerLossResume(); + #else + SERIAL_ECHO_MSG("Resume requires LCD."); + #endif + } + else if (parser.seen('C')) { + #if HAS_LCD_MENU + lcd_power_loss_recovery_cancel(); + #else + recovery.cancel(); + #endif + TERN_(EXTENSIBLE_UI, ExtUI::onPrintTimerStopped()); + } + else + recovery.resume(); + } + else + plr_error(recovery.info.valid_head ? PSTR("No") : PSTR("Invalid")); + +} + +#endif // POWER_LOSS_RECOVERY diff --git a/Marlin/src/gcode/feature/powerloss/M413.cpp b/Marlin/src/gcode/feature/powerloss/M413.cpp new file mode 100644 index 0000000..64573e5 --- /dev/null +++ b/Marlin/src/gcode/feature/powerloss/M413.cpp @@ -0,0 +1,62 @@ +/** + * 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(POWER_LOSS_RECOVERY) + +#include "../../gcode.h" +#include "../../../feature/powerloss.h" +#include "../../../module/motion.h" +#include "../../../lcd/marlinui.h" + +/** + * M413: Enable / Disable power-loss recovery + * + * Parameters + * S[bool] - Flag to enable / disable. + * If omitted, report current state. + */ +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); + } + + #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('D')) recovery.debug(PSTR("M413")); + #if PIN_EXISTS(POWER_LOSS) + if (parser.seen('O')) recovery._outage(); + #endif + 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")); + #endif +} + +#endif // POWER_LOSS_RECOVERY diff --git a/Marlin/src/gcode/feature/prusa_MMU2/M403.cpp b/Marlin/src/gcode/feature/prusa_MMU2/M403.cpp new file mode 100644 index 0000000..31d0763 --- /dev/null +++ b/Marlin/src/gcode/feature/prusa_MMU2/M403.cpp @@ -0,0 +1,49 @@ +/** + * 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_PRUSA_MMU2 + +#include "../../gcode.h" +#include "../../../feature/mmu/mmu2.h" + +/** + * M403: Set filament type for MMU2 + * + * Valid filament type values: + * + * 0 Default + * 1 Flexible + * 2 PVA + */ +void GcodeSuite::M403() { + int8_t index = parser.intval('E', -1), + type = parser.intval('F', -1); + + if (WITHIN(index, 0, 4) && WITHIN(type, 0, 2)) + mmu2.set_filament_type(index, type); + else + SERIAL_ECHO_MSG("M403 - bad arguments."); +} + +#endif // HAS_PRUSA_MMU2 diff --git a/Marlin/src/gcode/feature/runout/M412.cpp b/Marlin/src/gcode/feature/runout/M412.cpp new file mode 100644 index 0000000..130f9c8 --- /dev/null +++ b/Marlin/src/gcode/feature/runout/M412.cpp @@ -0,0 +1,64 @@ +/** + * 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_FILAMENT_SENSOR + +#include "../../gcode.h" +#include "../../../feature/runout.h" + +/** + * 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" + 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'); + if (seenR || seenS) runout.reset(); + if (seenS) runout.enabled = parser.value_bool(); + #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); + #if HAS_FILAMENT_RUNOUT_DISTANCE + SERIAL_ECHOLNPAIR("Filament runout distance (mm): ", runout.runout_distance()); + #endif + } +} + +#endif // HAS_FILAMENT_SENSOR diff --git a/Marlin/src/gcode/feature/trinamic/M122.cpp b/Marlin/src/gcode/feature/trinamic/M122.cpp new file mode 100644 index 0000000..46e4365 --- /dev/null +++ b/Marlin/src/gcode/feature/trinamic/M122.cpp @@ -0,0 +1,60 @@ +/** + * 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_TRINAMIC_CONFIG + +#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; } + + if (print_all) LOOP_XYZE(i) print_axis[i] = true; + + if (parser.boolval('I')) restore_stepper_drivers(); + + #if ENABLED(TMC_DEBUG) + #if ENABLED(MONITOR_DRIVER_STATUS) + uint16_t interval = MONITOR_DRIVER_STATUS_INTERVAL_MS; + if (parser.seen('S') && !parser.value_bool()) interval = 0; + if (parser.seenval('P')) NOMORE(interval, parser.value_ushort()); + tmc_set_report_interval(interval); + #endif + + if (parser.seen('V')) + tmc_get_registers(print_axis.x, print_axis.y, print_axis.z, print_axis.e); + else + tmc_report_all(print_axis.x, print_axis.y, print_axis.z, print_axis.e); + #endif + + test_tmc_connection(print_axis.x, print_axis.y, print_axis.z, print_axis.e); +} + +#endif // HAS_TRINAMIC_CONFIG diff --git a/Marlin/src/gcode/feature/trinamic/M569.cpp b/Marlin/src/gcode/feature/trinamic/M569.cpp new file mode 100644 index 0000000..6b379f1 --- /dev/null +++ b/Marlin/src/gcode/feature/trinamic/M569.cpp @@ -0,0 +1,186 @@ +/** + * 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_STEALTHCHOP + +#include "../../gcode.h" +#include "../../../feature/tmc_util.h" +#include "../../../module/stepper/indirection.h" + +template<typename TMC> +void tmc_say_stealth_status(TMC &st) { + st.printLabel(); + SERIAL_ECHOPGM(" driver mode:\t"); + serialprintPGM(st.get_stealthChop() ? PSTR("stealthChop") : PSTR("spreadCycle")); + SERIAL_EOL(); +} +template<typename TMC> +void tmc_set_stealthChop(TMC &st, const bool enable) { + st.stored.stealthChop_enabled = enable; + st.refresh_stepping_mode(); +} + +static void set_stealth_status(const bool enable, const int8_t target_extruder) { + #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) + const uint8_t index = parser.byteval('I'); + #endif + + LOOP_XYZE(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 + 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; + } + } +} + +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 +} + +/** + * M569: Enable stealthChop on an axis + * + * S[1|0] to enable or disable + * XYZE to target an axis + * No arguments reports the stealthChop status of all capable drivers. + */ +void GcodeSuite::M569() { + if (parser.seen('S')) + set_stealth_status(parser.value_bool(), get_target_extruder_from_command()); + else + say_stealth_status(); +} + +#endif // HAS_STEALTHCHOP diff --git a/Marlin/src/gcode/feature/trinamic/M906.cpp b/Marlin/src/gcode/feature/trinamic/M906.cpp new file mode 100644 index 0000000..e834ebd --- /dev/null +++ b/Marlin/src/gcode/feature/trinamic/M906.cpp @@ -0,0 +1,173 @@ +/** + * 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_TRINAMIC_CONFIG + +#include "../../gcode.h" +#include "../../../feature/tmc_util.h" +#include "../../../module/stepper/indirection.h" + +/** + * M906: Set motor current in milliamps. + * + * Parameters: + * X[current] - Set mA current for X driver(s) + * Y[current] - Set mA current for Y driver(s) + * Z[current] - Set mA current for Z driver(s) + * E[current] - Set mA current for E driver(s) + * + * I[index] - Axis sub-index (Omit or 0 for X, Y, Z; 1 for X2, Y2, Z2; 2 for Z3; 3 for Z4.) + * T[index] - Extruder index (Zero-based. Omit for E0 only.) + * + * With no parameters report driver currents. + */ +void GcodeSuite::M906() { + #define TMC_SAY_CURRENT(Q) tmc_print_current(stepper##Q) + #define TMC_SET_CURRENT(Q) stepper##Q.rms_current(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) + const uint8_t index = parser.byteval('I'); + #endif + + LOOP_XYZE(i) if (uint16_t value = parser.intval(axis_codes[i])) { + report = false; + switch (i) { + case X_AXIS: + #if AXIS_IS_TMC(X) + if (index == 0) TMC_SET_CURRENT(X); + #endif + #if AXIS_IS_TMC(X2) + 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; + #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; + } + } + + if (report) { + #if AXIS_IS_TMC(X) + TMC_SAY_CURRENT(X); + #endif + #if AXIS_IS_TMC(X2) + TMC_SAY_CURRENT(X2); + #endif + #if AXIS_IS_TMC(Y) + TMC_SAY_CURRENT(Y); + #endif + #if AXIS_IS_TMC(Y2) + TMC_SAY_CURRENT(Y2); + #endif + #if AXIS_IS_TMC(Z) + TMC_SAY_CURRENT(Z); + #endif + #if AXIS_IS_TMC(Z2) + TMC_SAY_CURRENT(Z2); + #endif + #if AXIS_IS_TMC(Z3) + TMC_SAY_CURRENT(Z3); + #endif + #if AXIS_IS_TMC(Z4) + TMC_SAY_CURRENT(Z4); + #endif + #if AXIS_IS_TMC(E0) + TMC_SAY_CURRENT(E0); + #endif + #if AXIS_IS_TMC(E1) + TMC_SAY_CURRENT(E1); + #endif + #if AXIS_IS_TMC(E2) + TMC_SAY_CURRENT(E2); + #endif + #if AXIS_IS_TMC(E3) + TMC_SAY_CURRENT(E3); + #endif + #if AXIS_IS_TMC(E4) + TMC_SAY_CURRENT(E4); + #endif + #if AXIS_IS_TMC(E5) + TMC_SAY_CURRENT(E5); + #endif + #if AXIS_IS_TMC(E6) + TMC_SAY_CURRENT(E6); + #endif + #if AXIS_IS_TMC(E7) + TMC_SAY_CURRENT(E7); + #endif + } +} + +#endif // HAS_TRINAMIC_CONFIG diff --git a/Marlin/src/gcode/feature/trinamic/M911-M914.cpp b/Marlin/src/gcode/feature/trinamic/M911-M914.cpp new file mode 100644 index 0000000..8c840db --- /dev/null +++ b/Marlin/src/gcode/feature/trinamic/M911-M914.cpp @@ -0,0 +1,429 @@ +/** + * 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_TRINAMIC_CONFIG + +#include "../../gcode.h" +#include "../../../feature/tmc_util.h" +#include "../../../module/stepper/indirection.h" +#include "../../../module/planner.h" +#include "../../queue.h" + +#if ENABLED(MONITOR_DRIVER_STATUS) + + #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_SOME_X && !M91x_SOME_Y && !M91x_SOME_Z && !M91x_SOME_E + #error "MONITOR_DRIVER_STATUS requires at least one TMC2130, 2160, 2208, 2209, 2660, 5130, or 5160." + #endif + + /** + * M911: Report TMC stepper driver overtemperature pre-warn flag + * This flag is held by the library, persisting until cleared by M912 + */ + void GcodeSuite::M911() { + #if M91x_USE(X) + tmc_report_otpw(stepperX); + #endif + #if M91x_USE(X2) + tmc_report_otpw(stepperX2); + #endif + #if M91x_USE(Y) + tmc_report_otpw(stepperY); + #endif + #if M91x_USE(Y2) + tmc_report_otpw(stepperY2); + #endif + #if M91x_USE(Z) + tmc_report_otpw(stepperZ); + #endif + #if M91x_USE(Z2) + tmc_report_otpw(stepperZ2); + #endif + #if M91x_USE(Z3) + tmc_report_otpw(stepperZ3); + #endif + #if M91x_USE(Z4) + tmc_report_otpw(stepperZ4); + #endif + #if M91x_USE_E(0) + tmc_report_otpw(stepperE0); + #endif + #if M91x_USE_E(1) + tmc_report_otpw(stepperE1); + #endif + #if M91x_USE_E(2) + tmc_report_otpw(stepperE2); + #endif + #if M91x_USE_E(3) + tmc_report_otpw(stepperE3); + #endif + #if M91x_USE_E(4) + tmc_report_otpw(stepperE4); + #endif + #if M91x_USE_E(5) + tmc_report_otpw(stepperE5); + #endif + #if M91x_USE_E(6) + tmc_report_otpw(stepperE6); + #endif + #if M91x_USE_E(7) + tmc_report_otpw(stepperE7); + #endif + } + + /** + * M912: Clear TMC stepper driver overtemperature pre-warn flag held by the library + * Specify one or more axes with X, Y, Z, X1, Y1, Z1, X2, Y2, Z2, Z3, Z4 and E[index]. + * If no axes are given, clear all. + * + * Examples: + * M912 X ; clear X and X2 + * M912 X1 ; clear X1 only + * M912 X2 ; clear X2 only + * M912 X E ; clear X, X2, and all E + * M912 E1 ; clear E1 only + */ + void GcodeSuite::M912() { + #if M91x_SOME_X + const bool hasX = parser.seen(axis_codes.x); + #else + constexpr bool hasX = false; + #endif + + #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; + + #if M91x_SOME_X + const int8_t xval = int8_t(parser.byteval(axis_codes.x, 0xFF)); + #if M91x_USE(X) + if (hasNone || xval == 1 || (hasX && xval < 0)) tmc_clear_otpw(stepperX); + #endif + #if M91x_USE(X2) + if (hasNone || xval == 2 || (hasX && xval < 0)) tmc_clear_otpw(stepperX2); + #endif + #endif + + #if M91x_SOME_Y + const int8_t yval = int8_t(parser.byteval(axis_codes.y, 0xFF)); + #if M91x_USE(Y) + if (hasNone || yval == 1 || (hasY && yval < 0)) tmc_clear_otpw(stepperY); + #endif + #if M91x_USE(Y2) + if (hasNone || yval == 2 || (hasY && yval < 0)) tmc_clear_otpw(stepperY2); + #endif + #endif + + #if M91x_SOME_Z + const int8_t zval = int8_t(parser.byteval(axis_codes.z, 0xFF)); + #if M91x_USE(Z) + if (hasNone || zval == 1 || (hasZ && zval < 0)) tmc_clear_otpw(stepperZ); + #endif + #if M91x_USE(Z2) + if (hasNone || zval == 2 || (hasZ && zval < 0)) tmc_clear_otpw(stepperZ2); + #endif + #if M91x_USE(Z3) + if (hasNone || zval == 3 || (hasZ && zval < 0)) tmc_clear_otpw(stepperZ3); + #endif + #if M91x_USE(Z4) + if (hasNone || zval == 4 || (hasZ && zval < 0)) tmc_clear_otpw(stepperZ4); + #endif + #endif + + #if M91x_SOME_E + const int8_t eval = int8_t(parser.byteval(axis_codes.e, 0xFF)); + #if M91x_USE_E(0) + if (hasNone || eval == 0 || (hasE && eval < 0)) tmc_clear_otpw(stepperE0); + #endif + #if M91x_USE_E(1) + if (hasNone || eval == 1 || (hasE && eval < 0)) tmc_clear_otpw(stepperE1); + #endif + #if M91x_USE_E(2) + if (hasNone || eval == 2 || (hasE && eval < 0)) tmc_clear_otpw(stepperE2); + #endif + #if M91x_USE_E(3) + if (hasNone || eval == 3 || (hasE && eval < 0)) tmc_clear_otpw(stepperE3); + #endif + #if M91x_USE_E(4) + if (hasNone || eval == 4 || (hasE && eval < 0)) tmc_clear_otpw(stepperE4); + #endif + #if M91x_USE_E(5) + if (hasNone || eval == 5 || (hasE && eval < 0)) tmc_clear_otpw(stepperE5); + #endif + #if M91x_USE_E(6) + if (hasNone || eval == 6 || (hasE && eval < 0)) tmc_clear_otpw(stepperE6); + #endif + #if M91x_USE_E(7) + if (hasNone || eval == 7 || (hasE && eval < 0)) tmc_clear_otpw(stepperE7); + #endif + #endif + } + +#endif // MONITOR_DRIVER_STATUS + +/** + * 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) + #define TMC_SAY_PWMTHRS_E(E) tmc_print_pwmthrs(stepperE##E) + #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) + const uint8_t index = parser.byteval('I'); + #endif + LOOP_XYZE(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 + 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 + break; + 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 + 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 + } + #endif // E_STEPPERS + } break; + } + } + + 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 + } + } +#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))) { + const int16_t value = parser.value_int(); + report = false; + switch (i) { + #if X_SENSORLESS + case X_AXIS: + #if AXIS_HAS_STALLGUARD(X) + if (index < 2) stepperX.homing_threshold(value); + #endif + #if AXIS_HAS_STALLGUARD(X2) + if (!(index & 1)) stepperX2.homing_threshold(value); + #endif + break; + #endif + #if Y_SENSORLESS + case Y_AXIS: + #if AXIS_HAS_STALLGUARD(Y) + if (index < 2) stepperY.homing_threshold(value); + #endif + #if AXIS_HAS_STALLGUARD(Y2) + if (!(index & 1)) stepperY2.homing_threshold(value); + #endif + break; + #endif + #if Z_SENSORLESS + case Z_AXIS: + #if AXIS_HAS_STALLGUARD(Z) + if (index < 2) stepperZ.homing_threshold(value); + #endif + #if AXIS_HAS_STALLGUARD(Z2) + if (index == 0 || index == 2) stepperZ2.homing_threshold(value); + #endif + #if AXIS_HAS_STALLGUARD(Z3) + if (index == 0 || index == 3) stepperZ3.homing_threshold(value); + #endif + #if AXIS_HAS_STALLGUARD(Z4) + if (index == 0 || index == 4) stepperZ4.homing_threshold(value); + #endif + break; + #endif + } + } + + if (report) { + #if X_SENSORLESS + #if AXIS_HAS_STALLGUARD(X) + tmc_print_sgt(stepperX); + #endif + #if AXIS_HAS_STALLGUARD(X2) + tmc_print_sgt(stepperX2); + #endif + #endif + #if Y_SENSORLESS + #if AXIS_HAS_STALLGUARD(Y) + tmc_print_sgt(stepperY); + #endif + #if AXIS_HAS_STALLGUARD(Y2) + tmc_print_sgt(stepperY2); + #endif + #endif + #if Z_SENSORLESS + #if AXIS_HAS_STALLGUARD(Z) + tmc_print_sgt(stepperZ); + #endif + #if AXIS_HAS_STALLGUARD(Z2) + tmc_print_sgt(stepperZ2); + #endif + #if AXIS_HAS_STALLGUARD(Z3) + tmc_print_sgt(stepperZ3); + #endif + #if AXIS_HAS_STALLGUARD(Z4) + tmc_print_sgt(stepperZ4); + #endif + #endif + } + } +#endif // USE_SENSORLESS + +#endif // HAS_TRINAMIC_CONFIG |