aboutsummaryrefslogtreecommitdiff
path: root/Marlin/src/gcode/feature/L6470/M906.cpp
blob: 7bd446a1ab7719b722e6e8b090ca3b15b57d3d5f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
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