Skip to content

Commit 8b060a3

Browse files
G34 Mechanical Gantry Calibration (like Prusa M915) (MarlinFirmware#18972)
Co-authored-by: Scott Lahteine <thinkyhead@users.noreply.github.com>
1 parent faae900 commit 8b060a3

File tree

10 files changed

+269
-34
lines changed

10 files changed

+269
-34
lines changed

Marlin/Configuration_adv.h

+19
Original file line numberDiff line numberDiff line change
@@ -3377,6 +3377,25 @@
33773377
//#define JOYSTICK_DEBUG
33783378
#endif
33793379

3380+
/**
3381+
* Mechanical Gantry Calibration
3382+
* Modern replacement for the Prusa TMC_Z_CALIBRATION.
3383+
* Adds capability to work with any adjustable current drivers.
3384+
* Implemented as G34 because M915 is deprecated.
3385+
*/
3386+
//#define MECHANICAL_GANTRY_CALIBRATION
3387+
#if ENABLED(MECHANICAL_GANTRY_CALIBRATION)
3388+
#define GANTRY_CALIBRATION_CURRENT 600 // Default calibration current in ma
3389+
#define GANTRY_CALIBRATION_EXTRA_HEIGHT 15 // Extra distance in mm past Z_###_POS to move
3390+
#define GANTRY_CALIBRATION_FEEDRATE 500 // Feedrate for correction move
3391+
//#define GANTRY_CALIBRATION_TO_MIN // Enable to calibrate Z in the MIN direction
3392+
3393+
//#define GANTRY_CALIBRATION_SAFE_POSITION { X_CENTER, Y_CENTER } // Safe position for nozzle
3394+
//#define GANTRY_CALIBRATION_XY_PARK_FEEDRATE 3000 // XY Park Feedrate - MMM
3395+
//#define GANTRY_CALIBRATION_COMMANDS_PRE ""
3396+
#define GANTRY_CALIBRATION_COMMANDS_POST "G28" // G28 highly recommended to ensure an accurate position
3397+
#endif
3398+
33803399
/**
33813400
* MAX7219 Debug Matrix
33823401
*

Marlin/src/gcode/calibrate/G34.cpp

+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/**
2+
* Marlin 3D Printer Firmware
3+
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4+
*
5+
* Based on Sprinter and grbl.
6+
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU General Public License as published by
10+
* the Free Software Foundation, either version 3 of the License, or
11+
* (at your option) any later version.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU General Public License
19+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
20+
*
21+
*/
22+
23+
#include "../../inc/MarlinConfigPre.h"
24+
25+
#if ENABLED(MECHANICAL_GANTRY_CALIBRATION)
26+
27+
#include "../gcode.h"
28+
#include "../../module/motion.h"
29+
#include "../../module/stepper.h"
30+
#include "../../module/endstops.h"
31+
32+
#if HAS_LEVELING
33+
#include "../../feature/bedlevel/bedlevel.h"
34+
#endif
35+
36+
#define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
37+
#include "../../core/debug_out.h"
38+
39+
void GcodeSuite::G34() {
40+
41+
if (homing_needed()) return;
42+
43+
TEMPORARY_SOFT_ENDSTOP_STATE(false);
44+
TEMPORARY_BED_LEVELING_STATE(false);
45+
TemporaryGlobalEndstopsState unlock_z(false);
46+
47+
#ifdef GANTRY_CALIBRATION_COMMANDS_PRE
48+
gcode.process_subcommands_now_P(PSTR(GANTRY_CALIBRATION_COMMANDS_PRE));
49+
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Sub Commands Processed");
50+
#endif
51+
52+
#ifdef GANTRY_CALIBRATION_SAFE_POSITION
53+
// Move XY to safe position
54+
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Parking XY");
55+
const xy_pos_t safe_pos = GANTRY_CALIBRATION_SAFE_POSITION;
56+
do_blocking_move_to(safe_pos, MMM_TO_MMS(GANTRY_CALIBRATION_XY_PARK_FEEDRATE));
57+
#endif
58+
59+
const float move_distance = parser.intval('Z', GANTRY_CALIBRATION_EXTRA_HEIGHT),
60+
zbase = ENABLED(GANTRY_CALIBRATION_TO_MIN) ? Z_MIN_POS : Z_MAX_POS,
61+
zpounce = zbase - move_distance, zgrind = zbase + move_distance;
62+
63+
// Move Z to pounce position
64+
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Setting Z Pounce");
65+
do_blocking_move_to_z(zpounce, MMM_TO_MMS(HOMING_FEEDRATE_Z));
66+
67+
// Store current motor settings, then apply reduced value
68+
69+
#define _REDUCE_CURRENT ANY(HAS_MOTOR_CURRENT_SPI, HAS_MOTOR_CURRENT_PWM, HAS_MOTOR_CURRENT_DAC, HAS_MOTOR_CURRENT_I2C, HAS_TRINAMIC_CONFIG)
70+
#if _REDUCE_CURRENT
71+
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Reducing Current");
72+
#endif
73+
74+
#if HAS_MOTOR_CURRENT_SPI
75+
const uint16_t target_current = parser.intval('S', GANTRY_CALIBRATION_CURRENT);
76+
const uint32_t previous_current = stepper.motor_current_setting[Z_AXIS];
77+
stepper.set_digipot_current(Z_AXIS, target_current);
78+
#elif HAS_MOTOR_CURRENT_PWM
79+
const uint16_t target_current = parser.intval('S', GANTRY_CALIBRATION_CURRENT);
80+
const uint32_t previous_current = stepper.motor_current_setting[Z_AXIS];
81+
stepper.set_digipot_current(1, target_current);
82+
#elif HAS_MOTOR_CURRENT_DAC
83+
const float target_current = parser.floatval('S', GANTRY_CALIBRATION_CURRENT);
84+
const float previous_current = dac_amps(Z_AXIS, target_current);
85+
stepper_dac.set_current_value(Z_AXIS, target_current);
86+
#elif ENABLED(HAS_MOTOR_CURRENT_I2C)
87+
const uint16_t target_current = parser.intval('S', GANTRY_CALIBRATION_CURRENT);
88+
previous_current = dac_amps(Z_AXIS);
89+
digipot_i2c.set_current(Z_AXIS, target_current)
90+
#elif HAS_TRINAMIC_CONFIG
91+
const uint16_t target_current = parser.intval('S', GANTRY_CALIBRATION_CURRENT);
92+
static uint16_t previous_current_arr[NUM_Z_STEPPER_DRIVERS];
93+
#if AXIS_IS_TMC(Z)
94+
previous_current_arr[0] = stepperZ.getMilliamps();
95+
stepperZ.rms_current(target_current);
96+
#endif
97+
#if AXIS_IS_TMC(Z2)
98+
previous_current_arr[1] = stepperZ2.getMilliamps();
99+
stepperZ2.rms_current(target_current);
100+
#endif
101+
#if AXIS_IS_TMC(Z3)
102+
previous_current_arr[2] = stepperZ3.getMilliamps();
103+
stepperZ3.rms_current(target_current);
104+
#endif
105+
#if AXIS_IS_TMC(Z4)
106+
previous_current_arr[3] = stepperZ4.getMilliamps();
107+
stepperZ4.rms_current(target_current);
108+
#endif
109+
#endif
110+
111+
// Do Final Z move to adjust
112+
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Final Z Move");
113+
do_blocking_move_to_z(zgrind, MMM_TO_MMS(GANTRY_CALIBRATION_FEEDRATE));
114+
115+
// Back off end plate, back to normal motion range
116+
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Z Backoff");
117+
do_blocking_move_to_z(zpounce, MMM_TO_MMS(GANTRY_CALIBRATION_FEEDRATE));
118+
119+
#if _REDUCE_CURRENT
120+
// Reset current to original values
121+
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Restore Current");
122+
#endif
123+
124+
#if HAS_MOTOR_CURRENT_SPI
125+
stepper.set_digipot_current(Z_AXIS, previous_current);
126+
#elif HAS_MOTOR_CURRENT_PWM
127+
stepper.set_digipot_current(1, previous_current);
128+
#elif HAS_MOTOR_CURRENT_DAC
129+
stepper_dac.set_current_value(Z_AXIS, previous_current);
130+
#elif ENABLED(HAS_MOTOR_CURRENT_I2C)
131+
digipot_i2c.set_current(Z_AXIS, previous_current)
132+
#elif HAS_TRINAMIC_CONFIG
133+
#if AXIS_IS_TMC(Z)
134+
stepperZ.rms_current(previous_current_arr[0]);
135+
#endif
136+
#if AXIS_IS_TMC(Z2)
137+
stepperZ2.rms_current(previous_current_arr[1]);
138+
#endif
139+
#if AXIS_IS_TMC(Z3)
140+
stepperZ3.rms_current(previous_current_arr[2]);
141+
#endif
142+
#if AXIS_IS_TMC(Z4)
143+
stepperZ4.rms_current(previous_current_arr[3]);
144+
#endif
145+
#endif
146+
147+
#ifdef GANTRY_CALIBRATION_COMMANDS_POST
148+
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Running Post Commands");
149+
gcode.process_subcommands_now_P(PSTR(GANTRY_CALIBRATION_COMMANDS_POST));
150+
#endif
151+
}
152+
153+
#endif // MECHANICAL_GANTRY_CALIBRATION

Marlin/src/gcode/calibrate/G34_M422.cpp

+70-28
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,29 @@
2020
*
2121
*/
2222

23-
#include "../../inc/MarlinConfig.h"
23+
#include "../../inc/MarlinConfigPre.h"
2424

2525
#if ENABLED(Z_STEPPER_AUTO_ALIGN)
2626

2727
#include "../../feature/z_stepper_align.h"
2828

2929
#include "../gcode.h"
30-
#include "../../module/planner.h"
31-
#include "../../module/stepper.h"
3230
#include "../../module/motion.h"
31+
#include "../../module/stepper.h"
32+
#include "../../module/planner.h"
3333
#include "../../module/probe.h"
34-
35-
#if HAS_MULTI_HOTEND
36-
#include "../../module/tool_change.h"
37-
#endif
34+
#include "../../lcd/ultralcd.h" // for LCD_MESSAGEPGM
3835

3936
#if HAS_LEVELING
4037
#include "../../feature/bedlevel/bedlevel.h"
4138
#endif
4239

40+
#if HAS_MULTI_HOTEND
41+
#include "../../module/tool_change.h"
42+
#endif
43+
4344
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
44-
#include "../../libs/least_squares_fit.h"
45+
#include "../../libs/least_squares_fit.h"
4546
#endif
4647

4748
#define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
@@ -117,7 +118,7 @@ void GcodeSuite::G34() {
117118
// In BLTOUCH HS mode, the probe travels in a deployed state.
118119
// Users of G34 might have a badly misaligned bed, so raise Z by the
119120
// length of the deployed pin (BLTOUCH stroke < 7mm)
120-
#define Z_BASIC_CLEARANCE Z_CLEARANCE_BETWEEN_PROBES + 7.0f * BOTH(BLTOUCH, BLTOUCH_HS_MODE)
121+
#define Z_BASIC_CLEARANCE (Z_CLEARANCE_BETWEEN_PROBES + 7.0f * BOTH(BLTOUCH, BLTOUCH_HS_MODE))
121122

122123
// Compute a worst-case clearance height to probe from. After the first
123124
// iteration this will be re-calculated based on the actual bed position
@@ -154,21 +155,29 @@ void GcodeSuite::G34() {
154155
z_maxdiff = 0.0f,
155156
amplification = z_auto_align_amplification;
156157

157-
// These are needed after the for-loop
158-
uint8_t iteration;
159-
bool err_break = false;
160-
float z_measured_min;
161-
162158
#if DISABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
163159
bool adjustment_reverse = false;
164160
#endif
165161

166-
// 'iteration' is declared above and is also used after the for-loop.
167-
// *not* the same as LOOP_L_N(iteration, z_auto_align_iterations)
168-
for (iteration = 0; iteration < z_auto_align_iterations; ++iteration) {
162+
#if HAS_DISPLAY
163+
PGM_P const msg_iteration = GET_TEXT(MSG_ITERATION);
164+
const uint8_t iter_str_len = strlen_P(msg_iteration);
165+
#endif
166+
167+
// Final z and iteration values will be used after breaking the loop
168+
float z_measured_min;
169+
uint8_t iteration = 0;
170+
bool err_break = false; // To break out of nested loops
171+
while (iteration < z_auto_align_iterations) {
169172
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> probing all positions.");
170173

171-
SERIAL_ECHOLNPAIR("\nITERATION: ", int(iteration + 1));
174+
const int iter = iteration + 1;
175+
SERIAL_ECHOLNPAIR("\nG34 Iteration: ", iter);
176+
#if HAS_DISPLAY
177+
char str[iter_str_len + 2 + 1];
178+
sprintf_P(str, msg_iteration, iter);
179+
ui.set_status(str);
180+
#endif
172181

173182
// Initialize minimum value
174183
z_measured_min = 100000.0f;
@@ -190,7 +199,8 @@ void GcodeSuite::G34() {
190199
// current_position.z has been manually altered in the "dirty trick" above.
191200
const float z_probed_height = probe.probe_at_point(z_stepper_align.xy[iprobe], raise_after, 0, true, false);
192201
if (isnan(z_probed_height)) {
193-
SERIAL_ECHOLNPGM("Probing failed.");
202+
SERIAL_ECHOLNPGM("Probing failed");
203+
LCD_MESSAGEPGM(MSG_LCD_PROBING_FAILED);
194204
err_break = true;
195205
break;
196206
}
@@ -249,8 +259,39 @@ void GcodeSuite::G34() {
249259
, " Z3-Z1=", ABS(z_measured[2] - z_measured[0])
250260
#endif
251261
);
262+
#if HAS_DISPLAY
263+
char fstr1[10];
264+
#if NUM_Z_STEPPER_DRIVERS == 2
265+
char msg[6 + (6 + 5) * 1 + 1];
266+
#else
267+
char msg[6 + (6 + 5) * 3 + 1], fstr2[10], fstr3[10];
268+
#endif
269+
sprintf_P(msg,
270+
PSTR("Diffs Z1-Z2=%s"
271+
#if NUM_Z_STEPPER_DRIVERS == 3
272+
" Z2-Z3=%s"
273+
" Z3-Z1=%s"
274+
#endif
275+
), dtostrf(ABS(z_measured[0] - z_measured[1]), 1, 3, fstr1)
276+
#if NUM_Z_STEPPER_DRIVERS == 3
277+
, dtostrf(ABS(z_measured[1] - z_measured[2]), 1, 3, fstr2)
278+
, dtostrf(ABS(z_measured[2] - z_measured[0]), 1, 3, fstr3)
279+
#endif
280+
);
281+
ui.set_status(msg);
282+
#endif
283+
284+
auto decreasing_accuracy = [](const float &v1, const float &v2){
285+
if (v1 < v2 * 0.7f) {
286+
SERIAL_ECHOLNPGM("Decreasing Accuracy Detected.");
287+
LCD_MESSAGEPGM(MSG_DECREASING_ACCURACY);
288+
return true;
289+
}
290+
return false;
291+
};
252292

253293
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
294+
254295
// Check if the applied corrections go in the correct direction.
255296
// Calculate the sum of the absolute deviations from the mean of the probe measurements.
256297
// Compare to the last iteration to ensure it's getting better.
@@ -266,11 +307,8 @@ void GcodeSuite::G34() {
266307
z_align_level_indicator += ABS(z_measured[zstepper] - z_measured_mean);
267308

268309
// If it's getting worse, stop and throw an error
269-
if (last_z_align_level_indicator < z_align_level_indicator * 0.7f) {
270-
SERIAL_ECHOLNPGM("Decreasing accuracy detected.");
271-
err_break = true;
272-
break;
273-
}
310+
err_break = decreasing_accuracy(last_z_align_level_indicator, z_align_level_indicator);
311+
if (err_break) break;
274312

275313
last_z_align_level_indicator = z_align_level_indicator;
276314
#endif
@@ -290,8 +328,7 @@ void GcodeSuite::G34() {
290328
if (z_align_abs) amplification = (iteration == 1) ? _MIN(last_z_align_move[zstepper] / z_align_abs, 2.0f) : z_auto_align_amplification;
291329

292330
// Check for less accuracy compared to last move
293-
if (last_z_align_move[zstepper] < z_align_abs * 0.7f) {
294-
SERIAL_ECHOLNPGM("Decreasing accuracy detected.");
331+
if (decreasing_accuracy(last_z_align_move[zstepper], z_align_abs)) {
295332
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("> Z", int(zstepper + 1), " last_z_align_move = ", last_z_align_move[zstepper]);
296333
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("> Z", int(zstepper + 1), " z_align_abs = ", z_align_abs);
297334
adjustment_reverse = !adjustment_reverse;
@@ -329,9 +366,14 @@ void GcodeSuite::G34() {
329366

330367
if (err_break) break;
331368

332-
if (success_break) { SERIAL_ECHOLNPGM("Target accuracy achieved."); break; }
369+
if (success_break) {
370+
SERIAL_ECHOLNPGM("Target accuracy achieved.");
371+
LCD_MESSAGEPGM(MSG_ACCURACY_ACHIEVED);
372+
break;
373+
}
333374

334-
} // for (iteration)
375+
iteration++;
376+
} // while (iteration < z_auto_align_iterations)
335377

336378
if (err_break)
337379
SERIAL_ECHOLNPGM("G34 aborted.");

Marlin/src/gcode/gcode.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) {
327327
case 33: G33(); break; // G33: Delta Auto-Calibration
328328
#endif
329329

330-
#if ENABLED(Z_STEPPER_AUTO_ALIGN)
330+
#if EITHER(Z_STEPPER_AUTO_ALIGN, MECHANICAL_GANTRY_CALIBRATION)
331331
case 34: G34(); break; // G34: Z Stepper automatic alignment using probe
332332
#endif
333333

Marlin/src/gcode/gcode.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -465,11 +465,12 @@ class GcodeSuite {
465465

466466
TERN_(DELTA_AUTO_CALIBRATION, static void G33());
467467

468-
#if ENABLED(Z_STEPPER_AUTO_ALIGN)
468+
#if EITHER(Z_STEPPER_AUTO_ALIGN, MECHANICAL_GANTRY_CALIBRATION)
469469
static void G34();
470-
static void M422();
471470
#endif
472471

472+
TERN_(Z_STEPPER_AUTO_ALIGN, static void M422());
473+
473474
TERN_(ASSISTED_TRAMMING, static void G35());
474475

475476
TERN_(G38_PROBE_TARGET, static void G38(const int8_t subcode));

0 commit comments

Comments
 (0)