Prusa MINI Firmware overview
tmc_util.h
Go to the documentation of this file.
1 /**
2  * Marlin 3D Printer Firmware
3  * Copyright (c) 2019 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 <http://www.gnu.org/licenses/>.
20  *
21  */
22 #pragma once
23 
24 #include "../inc/MarlinConfig.h"
25 #include "../lcd/ultralcd.h"
26 
27 #if HAS_TRINAMIC
28 
29 #include <TMCStepper.h>
30 #include "../module/planner.h"
31 
32 #define TMC_X_LABEL 'X', '0'
33 #define TMC_Y_LABEL 'Y', '0'
34 #define TMC_Z_LABEL 'Z', '0'
35 
36 #define TMC_X2_LABEL 'X', '2'
37 #define TMC_Y2_LABEL 'Y', '2'
38 #define TMC_Z2_LABEL 'Z', '2'
39 #define TMC_Z3_LABEL 'Z', '3'
40 
41 #define TMC_E0_LABEL 'E', '0'
42 #define TMC_E1_LABEL 'E', '1'
43 #define TMC_E2_LABEL 'E', '2'
44 #define TMC_E3_LABEL 'E', '3'
45 #define TMC_E4_LABEL 'E', '4'
46 #define TMC_E5_LABEL 'E', '5'
47 
48 #define CHOPPER_DEFAULT_12V { 3, -1, 1 }
49 #define CHOPPER_DEFAULT_19V { 4, 1, 1 }
50 #define CHOPPER_DEFAULT_24V { 4, 2, 1 }
51 #define CHOPPER_DEFAULT_36V { 5, 2, 4 }
52 #define CHOPPER_PRUSAMK3_24V { 3, -2, 6 }
53 #define CHOPPER_MARLIN_119 { 5, 2, 3 }
54 
55 #if ENABLED(MONITOR_DRIVER_STATUS) && !defined(MONITOR_DRIVER_STATUS_INTERVAL_MS)
56  #define MONITOR_DRIVER_STATUS_INTERVAL_MS 500u
57 #endif
58 
59 constexpr uint16_t _tmc_thrs(const uint16_t msteps, const uint32_t thrs, const uint32_t spmm) {
60  return 12650000UL * msteps / (256 * thrs * spmm);
61 }
62 
63 template<char AXIS_LETTER, char DRIVER_ID>
64 class TMCStorage {
65  protected:
66  // Only a child class has access to constructor => Don't create on its own! "Poor man's abstract class"
67  TMCStorage() {}
68 
69  public:
70  uint16_t val_mA = 0;
71 
72  #if ENABLED(MONITOR_DRIVER_STATUS)
73  uint8_t otpw_count = 0,
74  error_count = 0;
75  bool flag_otpw = false;
76  inline bool getOTPW() { return flag_otpw; }
77  inline void clear_otpw() { flag_otpw = 0; }
78  #endif
79 
80  inline uint16_t getMilliamps() { return val_mA; }
81 
82  inline void printLabel() {
83  SERIAL_CHAR(AXIS_LETTER);
84  if (DRIVER_ID > '0') SERIAL_CHAR(DRIVER_ID);
85  }
86 
87  struct {
88  #if HAS_STEALTHCHOP
89  bool stealthChop_enabled = false;
90  #endif
91  #if ENABLED(HYBRID_THRESHOLD)
92  uint8_t hybrid_thrs = 0;
93  #endif
94  #if USE_SENSORLESS
95  int16_t homing_thrs = 0;
96  #endif
97  } stored;
98 };
99 
100 template<class TMC, char AXIS_LETTER, char DRIVER_ID, AxisEnum AXIS_ID>
101 class TMCMarlin : public TMC, public TMCStorage<AXIS_LETTER, DRIVER_ID> {
102  public:
103  TMCMarlin(const uint16_t cs_pin, const float RS) :
104  TMC(cs_pin, RS)
105  {}
106  TMCMarlin(const uint16_t cs_pin, const float RS, const uint8_t axis_chain_index) :
107  TMC(cs_pin, RS, axis_chain_index)
108  {}
109  TMCMarlin(const uint16_t CS, const float RS, const uint16_t pinMOSI, const uint16_t pinMISO, const uint16_t pinSCK) :
110  TMC(CS, RS, pinMOSI, pinMISO, pinSCK)
111  {}
112  TMCMarlin(const uint16_t CS, const float RS, const uint16_t pinMOSI, const uint16_t pinMISO, const uint16_t pinSCK, const uint8_t axis_chain_index) :
113  TMC(CS, RS, pinMOSI, pinMISO, pinSCK, axis_chain_index)
114  {}
115  inline uint16_t rms_current() { return TMC::rms_current(); }
116  inline void rms_current(uint16_t mA) {
117  this->val_mA = mA;
118  TMC::rms_current(mA);
119  }
120  inline void rms_current(const uint16_t mA, const float mult) {
121  this->val_mA = mA;
122  TMC::rms_current(mA, mult);
123  }
124 
125  #if HAS_STEALTHCHOP
126  inline void refresh_stepping_mode() { this->en_pwm_mode(this->stored.stealthChop_enabled); }
127  inline bool get_stealthChop_status() { return this->en_pwm_mode(); }
128  #endif
129  #if ENABLED(HYBRID_THRESHOLD)
130  uint32_t get_pwm_thrs() {
131  return _tmc_thrs(this->microsteps(), this->TPWMTHRS(), planner.settings.axis_steps_per_mm[AXIS_ID]);
132  }
133  void set_pwm_thrs(const uint32_t thrs) {
134  TMC::TPWMTHRS(_tmc_thrs(this->microsteps(), thrs, planner.settings.axis_steps_per_mm[AXIS_ID]));
135  #if HAS_LCD_MENU
136  this->stored.hybrid_thrs = thrs;
137  #endif
138  }
139  #endif
140  #if USE_SENSORLESS
141  inline int16_t homing_threshold() { return TMC::sgt(); }
142  void homing_threshold(int16_t sgt_val) {
143  sgt_val = (int16_t)constrain(sgt_val, sgt_min, sgt_max);
144  TMC::sgt(sgt_val);
145  #if HAS_LCD_MENU
146  this->stored.homing_thrs = sgt_val;
147  #endif
148  }
149  #if ENABLED(SPI_ENDSTOPS)
150  bool test_stall_status();
151  #endif
152  #endif
153 
154  #if HAS_LCD_MENU
155  inline void refresh_stepper_current() { rms_current(this->val_mA); }
156 
157  #if ENABLED(HYBRID_THRESHOLD)
158  inline void refresh_hybrid_thrs() { set_pwm_thrs(this->stored.hybrid_thrs); }
159  #endif
160  #if USE_SENSORLESS
161  inline void refresh_homing_thrs() { homing_threshold(this->stored.homing_thrs); }
162  #endif
163  #endif
164 
165  static constexpr int8_t sgt_min = -64,
166  sgt_max = 63;
167 };
168 template<char AXIS_LETTER, char DRIVER_ID, AxisEnum AXIS_ID>
169 class TMCMarlin<TMC2208Stepper, AXIS_LETTER, DRIVER_ID, AXIS_ID> : public TMC2208Stepper, public TMCStorage<AXIS_LETTER, DRIVER_ID> {
170  public:
171  TMCMarlin(Stream * SerialPort, const float RS, const uint8_t) :
172  TMC2208Stepper(SerialPort, RS, /*has_rx=*/true)
173  {}
174  TMCMarlin(const uint16_t RX, const uint16_t TX, const float RS, const uint8_t, const bool has_rx=true) :
175  TMC2208Stepper(RX, TX, RS, has_rx)
176  {}
177  uint16_t rms_current() { return TMC2208Stepper::rms_current(); }
178  inline void rms_current(const uint16_t mA) {
179  this->val_mA = mA;
180  TMC2208Stepper::rms_current(mA);
181  }
182  inline void rms_current(const uint16_t mA, const float mult) {
183  this->val_mA = mA;
184  TMC2208Stepper::rms_current(mA, mult);
185  }
186 
187  #if HAS_STEALTHCHOP
188  inline void refresh_stepping_mode() { en_spreadCycle(!this->stored.stealthChop_enabled); }
189  inline bool get_stealthChop_status() { return !this->en_spreadCycle(); }
190  #endif
191  #if ENABLED(HYBRID_THRESHOLD)
192  uint32_t get_pwm_thrs() {
193  return _tmc_thrs(this->microsteps(), this->TPWMTHRS(), planner.settings.axis_steps_per_mm[AXIS_ID]);
194  }
195  void set_pwm_thrs(const uint32_t thrs) {
196  TMC2208Stepper::TPWMTHRS(_tmc_thrs(this->microsteps(), thrs, planner.settings.axis_steps_per_mm[AXIS_ID]));
197  #if HAS_LCD_MENU
198  this->stored.hybrid_thrs = thrs;
199  #endif
200  }
201  #endif
202 
203  #if HAS_LCD_MENU
204  inline void refresh_stepper_current() { rms_current(this->val_mA); }
205 
206  #if ENABLED(HYBRID_THRESHOLD)
207  inline void refresh_hybrid_thrs() { set_pwm_thrs(this->stored.hybrid_thrs); }
208  #endif
209  #endif
210 };
211 
212 template<char AXIS_LETTER, char DRIVER_ID, AxisEnum AXIS_ID>
213 class TMCMarlin<TMC2209Stepper, AXIS_LETTER, DRIVER_ID, AXIS_ID> : public TMC2209Stepper, public TMCStorage<AXIS_LETTER, DRIVER_ID> {
214  public:
215  TMCMarlin(Stream * SerialPort, const float RS, const uint8_t addr) :
216  TMC2209Stepper(SerialPort, RS, addr)
217  {}
218  TMCMarlin(const uint16_t RX, const uint16_t TX, const float RS, const uint8_t addr, const bool) :
219  TMC2209Stepper(RX, TX, RS, addr)
220  {}
221  uint8_t get_address() { return slave_address; }
222  uint16_t rms_current() { return TMC2209Stepper::rms_current(); }
223  inline void rms_current(const uint16_t mA) {
224  this->val_mA = mA;
225  TMC2209Stepper::rms_current(mA);
226  }
227  inline void rms_current(const uint16_t mA, const float mult) {
228  this->val_mA = mA;
229  TMC2209Stepper::rms_current(mA, mult);
230  }
231 
232  #if HAS_STEALTHCHOP
233  inline void refresh_stepping_mode() { en_spreadCycle(!this->stored.stealthChop_enabled); }
234  inline bool get_stealthChop_status() { return !this->en_spreadCycle(); }
235  #endif
236  #if ENABLED(HYBRID_THRESHOLD)
237  uint32_t get_pwm_thrs() {
238  return _tmc_thrs(this->microsteps(), this->TPWMTHRS(), planner.settings.axis_steps_per_mm[AXIS_ID]);
239  }
240  void set_pwm_thrs(const uint32_t thrs) {
241  TMC2209Stepper::TPWMTHRS(_tmc_thrs(this->microsteps(), thrs, planner.settings.axis_steps_per_mm[AXIS_ID]));
242  #if HAS_LCD_MENU
243  this->stored.hybrid_thrs = thrs;
244  #endif
245  }
246  #endif
247  #if USE_SENSORLESS
248  inline int16_t homing_threshold() { return TMC2209Stepper::SGTHRS(); }
249  void homing_threshold(int16_t sgt_val) {
250  sgt_val = (int16_t)constrain(sgt_val, sgt_min, sgt_max);
251  TMC2209Stepper::SGTHRS(sgt_val);
252  #if HAS_LCD_MENU
253  this->stored.homing_thrs = sgt_val;
254  #endif
255  }
256  #endif
257 
258  #if HAS_LCD_MENU
259  inline void refresh_stepper_current() { rms_current(this->val_mA); }
260 
261  #if ENABLED(HYBRID_THRESHOLD)
262  inline void refresh_hybrid_thrs() { set_pwm_thrs(this->stored.hybrid_thrs); }
263  #endif
264  #if USE_SENSORLESS
265  inline void refresh_homing_thrs() { homing_threshold(this->stored.homing_thrs); }
266  #endif
267  #endif
268 
269  static constexpr uint8_t sgt_min = 0,
270  sgt_max = 255;
271 };
272 
273 template<char AXIS_LETTER, char DRIVER_ID, AxisEnum AXIS_ID>
274 class TMCMarlin<TMC2660Stepper, AXIS_LETTER, DRIVER_ID, AXIS_ID> : public TMC2660Stepper, public TMCStorage<AXIS_LETTER, DRIVER_ID> {
275  public:
276  TMCMarlin(const uint16_t cs_pin, const float RS, const uint8_t) :
277  TMC2660Stepper(cs_pin, RS)
278  {}
279  TMCMarlin(const uint16_t CS, const float RS, const uint16_t pinMOSI, const uint16_t pinMISO, const uint16_t pinSCK, const uint8_t) :
280  TMC2660Stepper(CS, RS, pinMOSI, pinMISO, pinSCK)
281  {}
282  inline uint16_t rms_current() { return TMC2660Stepper::rms_current(); }
283  inline void rms_current(const uint16_t mA) {
284  this->val_mA = mA;
285  TMC2660Stepper::rms_current(mA);
286  }
287 
288  #if USE_SENSORLESS
289  inline int16_t homing_threshold() { return TMC2660Stepper::sgt(); }
290  void homing_threshold(int16_t sgt_val) {
291  sgt_val = (int16_t)constrain(sgt_val, sgt_min, sgt_max);
292  TMC2660Stepper::sgt(sgt_val);
293  #if HAS_LCD_MENU
294  this->stored.homing_thrs = sgt_val;
295  #endif
296  }
297  #endif
298 
299  #if HAS_LCD_MENU
300  inline void refresh_stepper_current() { rms_current(this->val_mA); }
301 
302  #if USE_SENSORLESS
303  inline void refresh_homing_thrs() { homing_threshold(this->stored.homing_thrs); }
304  #endif
305  #endif
306 
307  static constexpr int8_t sgt_min = -64,
308  sgt_max = 63;
309 };
310 
311 template<typename TMC>
312 void tmc_print_current(TMC &st) {
313  st.printLabel();
314  SERIAL_ECHOLNPAIR(" driver current: ", st.getMilliamps());
315 }
316 
317 #if ENABLED(MONITOR_DRIVER_STATUS)
318  template<typename TMC>
319  void tmc_report_otpw(TMC &st) {
320  st.printLabel();
321  SERIAL_ECHOPGM(" temperature prewarn triggered: ");
322  serialprint_truefalse(st.getOTPW());
323  SERIAL_EOL();
324  }
325  template<typename TMC>
326  void tmc_clear_otpw(TMC &st) {
327  st.clear_otpw();
328  st.printLabel();
329  SERIAL_ECHOLNPGM(" prewarn flag cleared");
330  }
331 #endif
332 #if ENABLED(HYBRID_THRESHOLD)
333  template<typename TMC>
334  void tmc_print_pwmthrs(TMC &st) {
335  st.printLabel();
336  SERIAL_ECHOLNPAIR(" stealthChop max speed: ", st.get_pwm_thrs());
337  }
338 #endif
339 #if USE_SENSORLESS
340  template<typename TMC>
341  void tmc_print_sgt(TMC &st) {
342  st.printLabel();
343  SERIAL_ECHOPGM(" homing sensitivity: ");
344  SERIAL_PRINTLN(st.homing_threshold(), DEC);
345  }
346 #endif
347 
348 void monitor_tmc_driver();
349 void test_tmc_connection(const bool test_x, const bool test_y, const bool test_z, const bool test_e);
350 
351 #if ENABLED(TMC_DEBUG)
352  #if ENABLED(MONITOR_DRIVER_STATUS)
353  void tmc_set_report_interval(const uint16_t update_interval);
354  #endif
355  void tmc_report_all(const bool print_x, const bool print_y, const bool print_z, const bool print_e);
356  void tmc_get_registers(const bool print_x, const bool print_y, const bool print_z, const bool print_e);
357 #endif
358 
359 /**
360  * TMC2130-specific sensorless homing using stallGuard2.
361  * stallGuard2 only works when in spreadCycle mode.
362  * spreadCycle and stealthChop are mutually-exclusive.
363  *
364  * Defined here because of limitations with templates and headers.
365  */
366 #if USE_SENSORLESS
367 
368  // Track enabled status of stealthChop and only re-enable where applicable
369  struct sensorless_t { bool x, y, z, x2, y2, z2, z3; };
370 
371  #if ENABLED(IMPROVE_HOMING_RELIABILITY)
372  extern millis_t sg_guard_period;
373  constexpr uint16_t default_sg_guard_duration = 400;
374 
375  struct slow_homing_t {
376  xy_ulong_t acceleration;
377  #if HAS_CLASSIC_JERK
378  xy_float_t jerk_xy;
379  #endif
380  };
381  #endif
382 
383  bool tmc_enable_stallguard(TMC2130Stepper &st);
384  void tmc_disable_stallguard(TMC2130Stepper &st, const bool restore_stealth);
385 
386  bool tmc_enable_stallguard(TMC2209Stepper &st);
387  void tmc_disable_stallguard(TMC2209Stepper &st, const bool restore_stealth);
388 
389  bool tmc_enable_stallguard(TMC2660Stepper);
390  void tmc_disable_stallguard(TMC2660Stepper, const bool);
391 
392  #if ENABLED(SPI_ENDSTOPS)
393 
394  template<class TMC, char AXIS_LETTER, char DRIVER_ID, AxisEnum AXIS_ID>
395  bool TMCMarlin<TMC, AXIS_LETTER, DRIVER_ID, AXIS_ID>::test_stall_status() {
396  this->switchCSpin(LOW);
397 
398  // read stallGuard flag from TMC library, will handle HW and SW SPI
399  TMC2130_n::DRV_STATUS_t drv_status{0};
400  drv_status.sr = this->DRV_STATUS();
401 
402  this->switchCSpin(HIGH);
403 
404  return drv_status.stallGuard;
405  }
406  #endif // SPI_ENDSTOPS
407 
408 #endif // USE_SENSORLESS
409 
410 #if TMC_HAS_SPI
411  void tmc_init_cs_pins();
412 #endif
413 
414 #endif // HAS_TRINAMIC
HEX
#define HEX
Definition: Print.h:30
GET_TEXT
#define GET_TEXT(MSG)
Definition: multi_language.h:72
SERIAL_CHAR
#define SERIAL_CHAR(x)
Definition: serial.h:69
SERIAL_ECHO
#define SERIAL_ECHO(x)
Definition: serial.h:70
E4
Definition: L6470_Marlin.h:30
E1
Definition: L6470_Marlin.h:30
LOW
#define LOW
Definition: wiring_constants.h:70
print_job_timer
Stopwatch print_job_timer
Definition: printcounter.cpp:63
E0
Definition: L6470_Marlin.h:30
data
uint8_t data[8]
Definition: masstorage.h:49
i
uint8_t i
Definition: screen_test_graph.c:72
Y
Definition: L6470_Marlin.h:30
kill
void kill(PGM_P const lcd_error, PGM_P const lcd_component, const bool steppers_off)
Definition: Marlin.cpp:718
millis
uint32_t millis(void)
Definition: wiring_time.c:29
Z3
Definition: L6470_Marlin.h:30
Z
Definition: L6470_Marlin.h:30
print_hex_long
void print_hex_long(const uint32_t w, const char delimiter)
Definition: hex_print_routines.cpp:81
PSTR
#define PSTR(str)
Definition: pgmspace.h:31
E3
Definition: L6470_Marlin.h:30
E2
Definition: L6470_Marlin.h:30
X2
Definition: L6470_Marlin.h:30
SERIAL_PRINT
#define SERIAL_PRINT(x, b)
Definition: serial.h:73
duration_t::value
uint32_t value
Duration is stored in seconds.
Definition: duration_t.h:30
planner_settings_t::axis_steps_per_mm
float axis_steps_per_mm[XYZE_N]
Definition: planner.h:181
Stopwatch::duration
static millis_t duration()
Get the running time.
Definition: stopwatch.cpp:108
void
void
Definition: png.h:1083
Stream
Definition: Stream.h:49
constrain
#define constrain(amt, low, high)
Definition: wiring_constants.h:79
SERIAL_ECHOPGM
#define SERIAL_ECHOPGM(S)
Definition: serial.h:173
SERIAL_ECHOLNPAIR
#define SERIAL_ECHOLNPAIR(V...)
Definition: serial.h:144
XYval
Definition: types.h:99
L
#define L(CODE)
Definition: macros.h:76
_UNUSED
#define _UNUSED
Definition: macros.h:41
ELAPSED
#define ELAPSED(NOW, SOON)
Definition: millis_t.h:29
tmc_util.h
uint8_t
const uint8_t[]
Definition: 404_html.c:3
ui
MarlinUI ui
Z2
Definition: L6470_Marlin.h:30
duration_t::toDigital
uint8_t toDigital(char *buffer, bool with_days=false) const
Formats the duration as a string.
Definition: duration_t.h:149
HIGH
#define HIGH
Definition: wiring_constants.h:71
E5
Definition: L6470_Marlin.h:30
DEC
#define DEC
Definition: Print.h:29
SERIAL_EOL
#define SERIAL_EOL()
Definition: serial.h:181
TEST
#define TEST(n, b)
Definition: macros.h:81
SERIAL_PRINTLN
#define SERIAL_PRINTLN(x, b)
Definition: serial.h:74
serialprint_truefalse
void serialprint_truefalse(const bool tf)
Definition: serial.cpp:59
duration_t
Definition: duration_t.h:26
serialprintPGM
void serialprintPGM(PGM_P str)
Definition: serial.cpp:35
Planner::settings
static planner_settings_t settings
Definition: planner.h:251
SERIAL_ECHOLNPGM
#define SERIAL_ECHOLNPGM(S)
Definition: serial.h:174
X
Definition: L6470_Marlin.h:30
Y2
Definition: L6470_Marlin.h:30
millis_t
uint32_t millis_t
Definition: millis_t.h:26
planner
Planner planner
Definition: planner.cpp:111