L6470 SPI daisy chain support (#12895)
parent
6453b82a5e
commit
2f35747f29
@ -0,0 +1,144 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2016 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Software L6470 SPI functions originally from Arduino Sd2Card Library
|
||||
* Copyright (C) 2009 by William Greiman
|
||||
*/
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Includes
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if HAS_DRIVER(L6470)
|
||||
|
||||
#include "Delay.h"
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Public Variables
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Public functions
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
#include "../../core/serial.h"
|
||||
#include "../../libs/L6470/L6470_Marlin.h"
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Software L6470 SPI
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
// Make sure GCC optimizes this file.
|
||||
// Note that this line triggers a bug in GCC which is fixed by casting.
|
||||
// See the note below.
|
||||
#pragma GCC optimize (3)
|
||||
|
||||
// run at ~4Mhz
|
||||
uint8_t L6470_SpiTransfer_Mode_0(uint8_t b) { // using Mode 0
|
||||
for (uint8_t bits = 8; bits--;) {
|
||||
WRITE(L6470_CHAIN_MOSI_PIN, b & 0x80);
|
||||
b <<= 1; // little setup time
|
||||
|
||||
WRITE(L6470_CHAIN_SCK_PIN, HIGH);
|
||||
DELAY_NS(125); // 10 cycles @ 84mhz
|
||||
|
||||
b |= (READ(L6470_CHAIN_MISO_PIN) != 0);
|
||||
|
||||
WRITE(L6470_CHAIN_SCK_PIN, LOW);
|
||||
DELAY_NS(125); // 10 cycles @ 84mhz
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
uint8_t L6470_SpiTransfer_Mode_3(uint8_t b) { // using Mode 3
|
||||
for (uint8_t bits = 8; bits--;) {
|
||||
WRITE(L6470_CHAIN_SCK_PIN, LOW);
|
||||
WRITE(L6470_CHAIN_MOSI_PIN, b & 0x80);
|
||||
|
||||
DELAY_NS(125); // 10 cycles @ 84mhz
|
||||
|
||||
WRITE(L6470_CHAIN_SCK_PIN, HIGH);
|
||||
|
||||
b <<= 1; // little setup time
|
||||
b |= (READ(L6470_CHAIN_MISO_PIN) != 0);
|
||||
}
|
||||
|
||||
DELAY_NS(125); // 10 cycles @ 84mhz
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* The following are weak-linked and defined as do-nothing
|
||||
* functions by the L6470-Arduino library. They must be
|
||||
* defined by the client (Marlin) to provide an SPI interface.
|
||||
*/
|
||||
|
||||
uint8_t L6470_transfer(uint8_t data, int _SSPin, const uint8_t chain_position) {
|
||||
uint8_t data_out = 0;
|
||||
|
||||
// first device in chain has data sent last
|
||||
digitalWrite(_SSPin, LOW);
|
||||
|
||||
for (uint8_t i = L6470::chain[0]; (i >= 1) && !spi_abort; i--) { // stop sending data if spi_abort is active
|
||||
DISABLE_ISRS(); // disable interrupts during SPI transfer (can't allow partial command to chips)
|
||||
uint8_t temp = L6470_SpiTransfer_Mode_3(uint8_t(i == chain_position ? data : dSPIN_NOP));
|
||||
ENABLE_ISRS(); // enable interrupts
|
||||
if (i == chain_position) data_out = temp;
|
||||
}
|
||||
|
||||
digitalWrite(_SSPin, HIGH);
|
||||
return data_out;
|
||||
}
|
||||
|
||||
void L6470_transfer(uint8_t L6470_buf[], const uint8_t length) {
|
||||
// first device in chain has data sent last
|
||||
|
||||
if (spi_active) { // interrupted SPI transfer so need to
|
||||
WRITE(L6470_CHAIN_SS_PIN, HIGH); // guarantee min high of 650nS
|
||||
DELAY_US(1);
|
||||
}
|
||||
|
||||
WRITE(L6470_CHAIN_SS_PIN, LOW);
|
||||
for (uint8_t i = length; i >= 1; i--)
|
||||
L6470_SpiTransfer_Mode_3(uint8_t(L6470_buf[i]));
|
||||
WRITE(L6470_CHAIN_SS_PIN, HIGH);
|
||||
}
|
||||
|
||||
void L6470_spi_init() {
|
||||
OUT_WRITE(L6470_CHAIN_SS_PIN, HIGH);
|
||||
OUT_WRITE(L6470_CHAIN_SCK_PIN, HIGH);
|
||||
OUT_WRITE(L6470_CHAIN_MOSI_PIN, HIGH);
|
||||
SET_INPUT(L6470_CHAIN_MISO_PIN);
|
||||
|
||||
#if PIN_EXISTS(L6470_BUSY)
|
||||
SET_INPUT(L6470_BUSY_PIN);
|
||||
#endif
|
||||
|
||||
OUT_WRITE(L6470_CHAIN_MOSI_PIN, HIGH);
|
||||
}
|
||||
|
||||
#pragma GCC reset_options
|
||||
|
||||
#endif // HAS_DRIVER(L6470)
|
@ -0,0 +1,115 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2016 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../../inc/MarlinConfig.h"
|
||||
|
||||
#if HAS_DRIVER(L6470)
|
||||
|
||||
#include "../../gcode.h"
|
||||
#include "../../../libs/L6470/L6470_Marlin.h"
|
||||
#include "../../../module/stepper_indirection.h"
|
||||
|
||||
inline void echo_yes_no(const bool yes) { serialprintPGM(yes ? PSTR(" YES") : PSTR(" NO ")); }
|
||||
|
||||
void L6470_status_decode(const uint16_t status, const uint8_t axis) {
|
||||
if (L6470.spi_abort) return; // don't do anything if set_directions() has occurred
|
||||
L6470.say_axis(axis);
|
||||
#if ENABLED(L6470_CHITCHAT)
|
||||
char temp_buf[20];
|
||||
sprintf_P(temp_buf, PSTR(" status: %4x "), status);
|
||||
SERIAL_ECHO(temp_buf);
|
||||
print_bin(status);
|
||||
#endif
|
||||
SERIAL_ECHOPGM("\n...OUTPUT: ");
|
||||
serialprintPGM(status & STATUS_HIZ ? PSTR("OFF") : PSTR("ON "));
|
||||
SERIAL_ECHOPGM(" BUSY: "); echo_yes_no(!(status & STATUS_BUSY));
|
||||
SERIAL_ECHOPGM(" DIR: ");
|
||||
serialprintPGM((((status & STATUS_DIR) >> 4) ^ L6470.index_to_dir[axis]) ? PSTR("FORWARD") : PSTR("REVERSE"));
|
||||
SERIAL_ECHOPGM(" Last Command: ");
|
||||
if (status & STATUS_WRONG_CMD) SERIAL_ECHOPGM("IN");
|
||||
SERIAL_ECHOPGM("VALID ");
|
||||
serialprintPGM(status & STATUS_NOTPERF_CMD ? PSTR("Not PERFORMED") : PSTR("COMPLETED "));
|
||||
SERIAL_ECHOPAIR("\n...THERMAL: ", !(status & STATUS_TH_SD) ? "SHUTDOWN" : !(status & STATUS_TH_WRN) ? "WARNING " : "OK ");
|
||||
SERIAL_ECHOPGM(" OVERCURRENT:"); echo_yes_no(!(status & STATUS_OCD));
|
||||
SERIAL_ECHOPGM(" STALL:"); echo_yes_no(!(status & STATUS_STEP_LOSS_A) || !(status & STATUS_STEP_LOSS_B));
|
||||
SERIAL_ECHOPGM(" STEP-CLOCK MODE:"); echo_yes_no(status & STATUS_SCK_MOD);
|
||||
SERIAL_EOL();
|
||||
}
|
||||
|
||||
/**
|
||||
* M122: Debug L6470 drivers
|
||||
*/
|
||||
void GcodeSuite::M122() {
|
||||
|
||||
L6470.spi_active = true; // let set_directions() know we're in the middle of a series of SPI transfers
|
||||
|
||||
#define L6470_SAY_STATUS(Q) L6470_status_decode(stepper##Q.getStatus(), Q)
|
||||
|
||||
//if (parser.seen('S'))
|
||||
// tmc_set_report_status(parser.value_bool());
|
||||
//else
|
||||
|
||||
#if AXIS_DRIVER_TYPE_X(L6470)
|
||||
L6470_SAY_STATUS(X);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_X2(L6470)
|
||||
L6470_SAY_STATUS(X2);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Y(L6470)
|
||||
L6470_SAY_STATUS(Y);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Y2(L6470)
|
||||
L6470_SAY_STATUS(Y2);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Z(L6470)
|
||||
L6470_SAY_STATUS(Z);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Z2(L6470)
|
||||
L6470_SAY_STATUS(Z2);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Z3(L6470)
|
||||
L6470_SAY_STATUS(Z3);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E0(L6470)
|
||||
L6470_SAY_STATUS(E0);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E1(L6470)
|
||||
L6470_SAY_STATUS(E1);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E2(L6470)
|
||||
L6470_SAY_STATUS(E2);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E3(L6470)
|
||||
L6470_SAY_STATUS(E3);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E4(L6470)
|
||||
L6470_SAY_STATUS(E4);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E5(L6470)
|
||||
L6470_SAY_STATUS(E5);
|
||||
#endif
|
||||
|
||||
L6470.spi_active = false; // done with all SPI transfers - clear handshake flags
|
||||
L6470.spi_abort = false;
|
||||
}
|
||||
|
||||
#endif // HAS_DRIVER(L6470)
|
@ -0,0 +1,259 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2018 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../../inc/MarlinConfig.h"
|
||||
|
||||
#if HAS_DRIVER(L6470)
|
||||
|
||||
#include "../../gcode.h"
|
||||
#include "../../../libs/L6470/L6470_Marlin.h"
|
||||
#include "../../../module/stepper_indirection.h"
|
||||
#include "../../../module/planner.h"
|
||||
|
||||
/**
|
||||
*
|
||||
* M906: report or set KVAL_HOLD which sets the maximum effective voltage provided by the
|
||||
* PWMs to the steppers
|
||||
*
|
||||
* 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 or E1
|
||||
* 2 - monitor only X2, Y2, Z2 or E2
|
||||
* 3 - monitor only Z3 or E3
|
||||
* 4 - monitor only E4
|
||||
* 5 - monitor only E5
|
||||
* 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
|
||||
*
|
||||
* K - value for KVAL_HOLD (0 - 255) (optional)
|
||||
* optional - will report current value from driver if not specified
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 L6470_report_current(L6470 &motor, const uint8_t axis) {
|
||||
if (L6470.spi_abort) return; // don't do anything if set_directions() has occurred
|
||||
const uint16_t status = motor.getStatus() ;
|
||||
const uint8_t overcurrent_threshold = (uint8_t)motor.GetParam(L6470_OCD_TH),
|
||||
stall_threshold = (uint8_t)motor.GetParam(L6470_STALL_TH),
|
||||
motor_status = (status & (STATUS_MOT_STATUS)) >> 13,
|
||||
adc_out = motor.GetParam(L6470_ADC_OUT),
|
||||
adc_out_limited = constrain(adc_out, 8, 24);
|
||||
const float comp_coef = 1600.0f / adc_out_limited;
|
||||
const int microsteps = _BV(motor.GetParam(L6470_STEP_MODE) & 0x07);
|
||||
char temp_buf[80];
|
||||
L6470.say_axis(axis);
|
||||
#if ENABLED(L6470_CHITCHAT)
|
||||
sprintf_P(temp_buf, PSTR(" status: %4x "), status);
|
||||
SERIAL_ECHO(temp_buf);
|
||||
print_bin(status);
|
||||
#endif
|
||||
sprintf_P(temp_buf, PSTR("\n...OverCurrent Threshold: %2d (%4d mA)"), overcurrent_threshold, (overcurrent_threshold + 1) * 375);
|
||||
SERIAL_ECHO(temp_buf);
|
||||
sprintf_P(temp_buf, PSTR(" Stall Threshold: %2d (%7.2f mA)"), stall_threshold, (stall_threshold + 1) * 31.25);
|
||||
SERIAL_ECHO(temp_buf);
|
||||
SERIAL_ECHOPGM(" Motor Status: ");
|
||||
const char * const stat_str;
|
||||
switch (motor_status) {
|
||||
default:
|
||||
case 0: stat_str = PSTR("stopped"); break;
|
||||
case 1: stat_str = PSTR("accelerating"); break;
|
||||
case 2: stat_str = PSTR("decelerating"); break;
|
||||
case 3: stat_str = PSTR("at constant speed"); break;
|
||||
}
|
||||
serialprintPGM(stat_str);
|
||||
SERIAL_EOL();
|
||||
SERIAL_ECHOPAIR("...microsteps: ", microsteps);
|
||||
SERIAL_ECHOPAIR(" ADC_OUT: ", adc_out);
|
||||
SERIAL_ECHOPGM(" Vs_compensation: ");
|
||||
serialprintPGM((motor.GetParam(L6470_CONFIG) & CONFIG_EN_VSCOMP) ? PSTR("ENABLED ") : PSTR("DISABLED"));
|
||||
sprintf_P(temp_buf, PSTR(" Compensation coefficient: ~%4.2f\n"), comp_coef * 0.01f);
|
||||
SERIAL_ECHO(temp_buf);
|
||||
SERIAL_ECHOPAIR("...KVAL_HOLD: ", motor.GetParam(L6470_KVAL_HOLD));
|
||||
SERIAL_ECHOPAIR(" KVAL_RUN : ", motor.GetParam(L6470_KVAL_RUN));
|
||||
SERIAL_ECHOPAIR(" KVAL_ACC: ", motor.GetParam(L6470_KVAL_ACC));
|
||||
SERIAL_ECHOPAIR(" KVAL_DEC: ", motor.GetParam(L6470_KVAL_DEC));
|
||||
SERIAL_ECHOPGM(" V motor max = ");
|
||||
switch (motor_status) {
|
||||
case 0: sprintf_P(temp_buf, PSTR(" %4.1f%% (KVAL_HOLD)\n"), float(motor.GetParam(L6470_KVAL_HOLD)) * 100 / 256); break;
|
||||
case 1: sprintf_P(temp_buf, PSTR(" %4.1f%% (KVAL_RUN) \n"), float(motor.GetParam(L6470_KVAL_RUN)) * 100 / 256); break;
|
||||
case 2: sprintf_P(temp_buf, PSTR(" %4.1f%% (KVAL_ACC) \n"), float(motor.GetParam(L6470_KVAL_ACC)) * 100 / 256); break;
|
||||
case 3: sprintf_P(temp_buf, PSTR(" %4.1f%% (KVAL_DEC) \n"), float(motor.GetParam(L6470_KVAL_DEC)) * 100 / 256); break;
|
||||
}
|
||||
SERIAL_ECHO(temp_buf);
|
||||
}
|
||||
|
||||
void GcodeSuite::M906() {
|
||||
#define L6470_SET_KVAL_HOLD(Q) stepper##Q.SetParam(L6470_KVAL_HOLD, value)
|
||||
|
||||
L6470_ECHOLNPGM("M906");
|
||||
|
||||
bool report_current = true;
|
||||
|
||||
#if HAS_DRIVER(L6470)
|
||||
const uint8_t index = parser.byteval('I');
|
||||
#endif
|
||||
|
||||
LOOP_XYZE(i) if (uint8_t value = parser.byteval(axis_codes[i])) {
|
||||
|
||||
report_current = false;
|
||||
|
||||
if (planner.has_blocks_queued() || planner.cleaning_buffer_counter) {
|
||||
SERIAL_ECHOLNPGM("ERROR - can't set KVAL_HOLD while steppers are moving");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (i) {
|
||||
case X_AXIS:
|
||||
#if AXIS_DRIVER_TYPE_X(L6470)
|
||||
if (index == 0) L6470_SET_KVAL_HOLD(X);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_X2(L6470)
|
||||
if (index == 1) L6470_SET_KVAL_HOLD(X2);
|
||||
#endif
|
||||
break;
|
||||
case Y_AXIS:
|
||||
#if AXIS_DRIVER_TYPE_Y(L6470)
|
||||
if (index == 0) L6470_SET_KVAL_HOLD(Y);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Y2(L6470)
|
||||
if (index == 1) L6470_SET_KVAL_HOLD(Y2);
|
||||
#endif
|
||||
break;
|
||||
case Z_AXIS:
|
||||
#if AXIS_DRIVER_TYPE_Z(L6470)
|
||||
if (index == 0) L6470_SET_KVAL_HOLD(Z);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Z2(L6470)
|
||||
if (index == 1) L6470_SET_KVAL_HOLD(Z2);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Z3(L6470)
|
||||
if (index == 2) L6470_SET_KVAL_HOLD(Z3);
|
||||
#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_DRIVER_TYPE_E0(L6470)
|
||||
case 0: L6470_SET_KVAL_HOLD(E0); break;
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E1(L6470)
|
||||
case 1: L6470_SET_KVAL_HOLD(E1); break;
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E2(L6470)
|
||||
case 2: L6470_SET_KVAL_HOLD(E2); break;
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E3(L6470)
|
||||
case 3: L6470_SET_KVAL_HOLD(E3); break;
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E4(L6470)
|
||||
case 4: L6470_SET_KVAL_HOLD(E4); break;
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E5(L6470)
|
||||
case 5: L6470_SET_KVAL_HOLD(E5); break;
|
||||
#endif
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
if (report_current) {
|
||||
#define L6470_REPORT_CURRENT(Q) L6470_report_current(stepper##Q, Q)
|
||||
|
||||
L6470.spi_active = true; // let set_directions() know we're in the middle of a series of SPI transfers
|
||||
|
||||
#if AXIS_DRIVER_TYPE_X(L6470)
|
||||
L6470_REPORT_CURRENT(X);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_X2(L6470)
|
||||
L6470_REPORT_CURRENT(X2);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Y(L6470)
|
||||
L6470_REPORT_CURRENT(Y);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Y2(L6470)
|
||||
L6470_REPORT_CURRENT(Y2);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Z(L6470)
|
||||
L6470_REPORT_CURRENT(Z);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Z2(L6470)
|
||||
L6470_REPORT_CURRENT(Z2);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Z3(L6470)
|
||||
L6470_REPORT_CURRENT(Z3);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E0(L6470)
|
||||
L6470_REPORT_CURRENT(E0);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E1(L6470)
|
||||
L6470_REPORT_CURRENT(E1);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E2(L6470)
|
||||
L6470_REPORT_CURRENT(E2);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E3(L6470)
|
||||
L6470_REPORT_CURRENT(E3);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E4(L6470)
|
||||
L6470_REPORT_CURRENT(E4);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E5(L6470)
|
||||
L6470_REPORT_CURRENT(E5);
|
||||
#endif
|
||||
|
||||
L6470.spi_active = false; // done with all SPI transfers - clear handshake flags
|
||||
L6470.spi_abort = false;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HAS_DRIVER(L6470)
|
@ -0,0 +1,544 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2018 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../../inc/MarlinConfig.h"
|
||||
|
||||
#if HAS_DRIVER(L6470)
|
||||
|
||||
#include "../../gcode.h"
|
||||
#include "../../../module/stepper_indirection.h"
|
||||
#include "../../../module/planner.h"
|
||||
#include "../../../libs/L6470/L6470_Marlin.h"
|
||||
|
||||
/**
|
||||
*
|
||||
* M916: increase KVAL_HOLD until get thermal warning
|
||||
*
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* K - starting value for KVAL_HOLD (0 - 255)
|
||||
* optional - will use & report current value from driver if not specified
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
|
||||
L6470_ECHOLNPGM("M916");
|
||||
|
||||
// Variables used by L6470_get_user_input function - some may not be used
|
||||
char axis_mon[3][3] = { " ", " ", " " }; // list of Axes to be monitored
|
||||
uint8_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 bool over_current_flag = false; // M916 doesn't play with the overcurrent thresholds
|
||||
|
||||
uint8_t j; // general purpose counter
|
||||
|
||||
if (L6470.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
|
||||
|
||||
L6470_ECHOLNPAIR("feedrate = ", final_feedrate);
|
||||
|
||||
planner.synchronize(); // wait for all current movement commands to complete
|
||||
|
||||
for (j = 0; j < driver_count; j++)
|
||||
L6470.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;
|
||||
|
||||
L6470_ECHOLNPGM(".\n.");
|
||||
|
||||
do {
|
||||
|
||||
L6470_ECHOLNPAIR("kval_hold = ", kval_hold); // set & report KVAL_HOLD for this run
|
||||
|
||||
for (j = 0; j < driver_count; j++)
|
||||
L6470.set_param(axis_index[j], L6470_KVAL_HOLD, kval_hold);
|
||||
|
||||
// turn the motor(s) both directions
|
||||
sprintf_P(gcode_string, PSTR("G0 %s%4.3f F%4.3f"), temp_axis_string, position_min, final_feedrate);
|
||||
gcode.process_subcommands_now_P(gcode_string);
|
||||
|
||||
sprintf_P(gcode_string, PSTR("G0 %s%4.3f F%4.3f"), temp_axis_string, position_max, 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] = (~L6470.get_status(axis_index[j])) & L6470_ERROR_MASK; // bits of interest are all active low
|
||||
status_composite |= axis_status[j] ;
|
||||
}
|
||||
|
||||
if (status_composite && (status_composite & STATUS_UVLO)) {
|
||||
L6470_ECHOLNPGM("Test aborted (Undervoltage lockout active)");
|
||||
for (j = 0; j < driver_count; j++) {
|
||||
L6470_ECHOPGM("...");
|
||||
L6470.error_status_decode(axis_status[j], axis_index[j]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// increment KVAL_HOLD if not yet at thermal warning/shutdown
|
||||
if (!(status_composite & (STATUS_TH_WRN | STATUS_TH_SD)))
|
||||
kval_hold++;
|
||||
|
||||
} while (!(status_composite & (STATUS_TH_WRN | STATUS_TH_SD)) && kval_hold); // exit when kval_hold == 0 (rolls over)
|
||||
|
||||
L6470_ECHOPGM(".\n.\nThermal warning/shutdown ");
|
||||
if ((status_composite & (STATUS_TH_WRN | STATUS_TH_SD))) {
|
||||
L6470_ECHOLNPGM("has occurred");
|
||||
for (j = 0; j < driver_count; j++) {
|
||||
L6470_ECHOPGM("...");
|
||||
L6470.error_status_decode(axis_status[j], axis_index[j]);
|
||||
}
|
||||
}
|
||||
else
|
||||
L6470_ECHOLNPGM("(Unable to get)");
|
||||
|
||||
L6470_ECHOLNPGM(".");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* M917: Find minimum current thresholds
|
||||
*
|
||||
* Decrease OCD current until overcurrent error
|
||||
* Increase OCD until overcurrent error goes away
|
||||
* Decrease stall threshold until stall
|
||||
* Increase stall until stall error goes away
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* K - value for KVAL_HOLD (0 - 255)
|
||||
* optional - will report current value from driver if not specified
|
||||
*
|
||||
*/
|
||||
void GcodeSuite::M917() {
|
||||
|
||||
L6470_ECHOLNPGM("M917");
|
||||
|
||||
char axis_mon[3][3] = { " ", " ", " " }; // list of axes to be monitored
|
||||
uint8_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 bool over_current_flag = true;
|
||||
|
||||
uint8_t j; // general purpose counter
|
||||
|
||||
if (L6470.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
|
||||
|
||||
L6470_ECHOLNPAIR("feedrate = ", final_feedrate);
|
||||
|
||||
planner.synchronize(); // wait for all current movement commands to complete
|
||||
for (j = 0; j < driver_count; j++)
|
||||
L6470.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;
|
||||
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
|
||||
L6470_ECHOPAIR(".\n.\n.\nover_current threshold : ", (ocd_th_val + 1) * 375); // first status display
|
||||
L6470_ECHOPAIR(" (OCD_TH: : ", ocd_th_val);
|
||||
L6470_ECHOPAIR(") Stall threshold: ", (stall_th_val + 1) * 31.25);
|
||||
L6470_ECHOPAIR(" (STALL_TH: ", stall_th_val);
|
||||
L6470_ECHOLNPGM(")");
|
||||
|
||||
do {
|
||||
|
||||
L6470_ECHOPAIR("STALL threshold : ", (stall_th_val + 1) * 31.25);
|
||||
L6470_ECHOLNPAIR(" OCD threshold : ", (ocd_th_val + 1) * 375);
|
||||
|
||||
sprintf_P(gcode_string, PSTR("G0 %s%4.3f F%4.3f"), temp_axis_string, position_min, final_feedrate);
|
||||
gcode.process_subcommands_now_P(gcode_string);
|
||||
|
||||
sprintf_P(gcode_string, PSTR("G0 %s%4.3f F%4.3f"), temp_axis_string, position_max, 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] = (~L6470.get_status(axis_index[j])) & L6470_ERROR_MASK; // bits of interest are all active low
|
||||
status_composite |= axis_status[j];
|
||||
}
|
||||
|
||||
if (status_composite && (status_composite & STATUS_UVLO)) {
|
||||
L6470_ECHOLNPGM("Test aborted (Undervoltage lockout active)");
|
||||
for (j = 0; j < driver_count; j++) {
|
||||
L6470_ECHOPGM("...");
|
||||
L6470.error_status_decode(axis_status[j], axis_index[j]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (status_composite & (STATUS_TH_WRN | STATUS_TH_SD)) {
|
||||
L6470_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;
|
||||
L6470_EOL();
|
||||
L6470_ECHOLNPAIR("Lowering KVAL_HOLD by about 5% to ", kval_hold);
|
||||
for (j = 0; j < driver_count; j++)
|
||||
L6470.set_param(axis_index[j], L6470_KVAL_HOLD, kval_hold);
|
||||
}
|
||||
L6470_ECHOLNPGM(".");
|
||||
gcode.reset_stepper_timeout(); // reset_stepper_timeout to keep steppers powered
|
||||
watchdog_reset(); // beat the dog
|
||||
safe_delay(5000);
|
||||
status_composite_temp = 0;
|
||||
for (j = 0; j < driver_count; j++) {
|
||||
axis_status[j] = (~L6470.get_status(axis_index[j])) & L6470_ERROR_MASK; // bits of interest are all active low
|
||||
status_composite_temp |= axis_status[j];
|
||||
}
|
||||
}
|
||||
while (status_composite_temp & (STATUS_TH_WRN | STATUS_TH_SD));
|
||||
L6470_EOL();
|
||||
}
|
||||
if (status_composite & (STATUS_STEP_LOSS_A | STATUS_STEP_LOSS_B | STATUS_OCD)) {
|
||||
switch (test_phase) {
|
||||
|
||||
case 0: {
|
||||
if (status_composite & STATUS_OCD) {
|
||||
// phase 0 with OCD warning - time to go to next phase
|
||||
if (ocd_th_val >=15) {
|
||||
ocd_th_val = 15; // limit to max
|
||||
test_phase = 2; // at highest value so skip phase 1
|
||||
L6470_ECHOLNPGM("LOGIC E0A OCD at highest - skip to 2");
|
||||
}
|
||||
else {
|
||||
ocd_th_val++; // normal exit to next phase
|
||||
test_phase = 1; // setup for first pass of phase 1
|
||||
L6470_ECHOLNPGM("LOGIC E0B - inc OCD & go to 1");
|
||||
}
|
||||
}
|
||||
else { // phase 0 without OCD warning - keep on decrementing if can
|
||||
if (ocd_th_val) {
|
||||
ocd_th_val--; // try lower value
|
||||
L6470_ECHOLNPGM("LOGIC E0C - dec OCD");
|
||||
}
|
||||
else {
|
||||
test_phase = 2; // at lowest value without warning so skip phase 1
|
||||
L6470_ECHOLNPGM("LOGIC E0D - OCD at latest - go to 2");
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case 1: {
|
||||
if (status_composite & STATUS_OCD) {
|
||||
// phase 1 with OCD warning - increment if can
|
||||
if (ocd_th_val >= 15) {
|
||||
ocd_th_val = 15; // limit to max
|
||||
test_phase = 2; // at highest value so go to next phase
|
||||
L6470_ECHOLNPGM("LOGIC E1A - OCD at max - go to 2");
|
||||
}
|
||||
else {
|
||||
ocd_th_val++; // try a higher value
|
||||
L6470_ECHOLNPGM("LOGIC E1B - inc OCD");
|
||||
}
|
||||
}
|
||||
else { // phase 1 without OCD warning - normal exit to phase 2
|
||||
test_phase = 2;
|
||||
L6470_ECHOLNPGM("LOGIC E1C - no OCD warning - go to 1");
|
||||
}
|
||||
} break;
|
||||
|
||||
case 2: {
|
||||
if (status_composite & (STATUS_STEP_LOSS_A | STATUS_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
|
||||
L6470_ECHOLNPGM("LOGIC E2A - STALL warning, STALL at max, quit");
|
||||
L6470_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
|
||||
L6470_ECHOLNPGM("LOGIC E2B - INC - STALL warning, inc Stall, go to 3");
|
||||
}
|
||||
}
|
||||
else { // phase 2 without stall warning - decrement if can
|
||||
if (stall_th_val) {
|
||||
stall_th_val--; // try a lower value
|
||||
L6470_ECHOLNPGM("LOGIC E2C - no STALL, dec STALL");
|
||||
}
|
||||
else {
|
||||
L6470_ECHOLNPGM("finished - STALL at lowest value but still do NOT have stall warning");
|
||||
test_phase = 4;
|
||||
L6470_ECHOLNPGM("LOGIC E2D - no STALL, at lowest so quit");
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case 3: {
|
||||
if (status_composite & (STATUS_STEP_LOSS_A | STATUS_STEP_LOSS_B)) {
|
||||
// phase 3 with stall warning - increment if can
|
||||
if (stall_th_val >= 127) {
|
||||
stall_th_val = 127; // limit to max
|
||||
L6470_ECHOLNPGM("finished - STALL at maximum value but still have stall warning");
|
||||
test_phase = 4;
|
||||
L6470_ECHOLNPGM("LOGIC E3A - STALL, at max so quit");
|
||||
}
|
||||
else {
|
||||
stall_th_val++; // still looking for passing value
|
||||
L6470_ECHOLNPGM("LOGIC E3B - STALL, inc stall");
|
||||
}
|
||||
}
|
||||
else { //phase 3 without stall warning but have OCD warning
|
||||
L6470_ECHOLNPGM("Hardware problem - OCD warning without STALL warning");
|
||||
test_phase = 4;
|
||||
L6470_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
|
||||
L6470_ECHOLNPGM("LOGIC N0A - DEC OCD");
|
||||
}
|
||||
else {
|
||||
test_phase = 2; // at lowest value without warning so skip phase 1
|
||||
L6470_ECHOLNPGM("LOGIC N0B - OCD at lowest (go to phase 2)");
|
||||
}
|
||||
} break;
|
||||
|
||||
case 1: L6470_ECHOLNPGM("LOGIC N1 (go directly to 2)"); // phase 1 without OCD warning - drop directly to phase 2
|
||||
|
||||
case 2: { // phase 2 without stall warning - keep on decrementing if can
|
||||
if (stall_th_val) {
|
||||
stall_th_val--; // try a lower value (stay in phase 2)
|
||||
L6470_ECHOLNPGM("LOGIC N2B - dec STALL");
|
||||
}
|
||||
else {
|
||||
L6470_ECHOLNPGM("finished - STALL at lowest value but still no stall warning");
|
||||
test_phase = 4;
|
||||
L6470_ECHOLNPGM("LOGIC N2C - STALL at lowest (quit)");
|
||||
}
|
||||
} break;
|
||||
|
||||
case 3: { test_phase = 4;
|
||||
L6470_ECHOLNPGM("LOGIC N3 - 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)
|
||||
L6470.set_param(axis_index[j], L6470_OCD_TH, ocd_th_val);
|
||||
L6470.set_param(axis_index[j], L6470_STALL_TH, stall_th_val);
|
||||
if (L6470.get_param(axis_index[j], L6470_OCD_TH) != ocd_th_val) L6470_ECHOLNPGM("OCD mismatch");
|
||||
if (L6470.get_param(axis_index[j], L6470_STALL_TH) != stall_th_val) L6470_ECHOLNPGM("STALL mismatch");
|
||||
}
|
||||
}
|
||||
|
||||
} while (test_phase != 4);
|
||||
|
||||
if (status_composite) {
|
||||
L6470_ECHOLNPGM("Completed with errors");
|
||||
for (j = 0; j < driver_count; j++) {
|
||||
L6470_ECHOPGM("...");
|
||||
L6470.error_status_decode(axis_status[j], axis_index[j]);
|
||||
}
|
||||
}
|
||||
else
|
||||
L6470_ECHOLNPGM("Completed with no errors");
|
||||
|
||||
} // M917
|
||||
|
||||
/**
|
||||
*
|
||||
* M918: increase speed until error or max feedrate achieved (as shown in configuration.h))
|
||||
*
|
||||
* J - select which driver(s) to monitor on multi-driver axis
|
||||
* 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
|
||||
*
|
||||
* K - value for KVAL_HOLD (0 - 255) (optional)
|
||||
* optional - will report current value from driver if not specified
|
||||
*
|
||||
*/
|
||||
void GcodeSuite::M918() {
|
||||
|
||||
L6470_ECHOLNPGM("M918");
|
||||
|
||||
char axis_mon[3][3] = { " ", " ", " " }; // List of axes to monitor
|
||||
uint8_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 bool over_current_flag = true;
|
||||
|
||||
uint8_t j; // general purpose counter
|
||||
|
||||
if (L6470.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
|
||||
|
||||
uint8_t m_steps = parser.byteval('M');
|
||||
LIMIT(m_steps, 0, 128);
|
||||
L6470_ECHOLNPAIR("M = ", m_steps);
|
||||
|
||||
int8_t m_bits = -1;
|
||||
if (m_steps > 85) m_bits = 7; // 128 (no synch output)
|
||||
else if (m_steps > 42) m_bits = 6; // 64 (no synch output)
|
||||
else if (m_steps > 22) m_bits = 5; // 32 (no synch output)
|
||||
else if (m_steps > 12) m_bits = 4; // 16 (no synch output)
|
||||
else if (m_steps > 5) m_bits = 3; // 8 (no synch output)
|
||||
else if (m_steps > 2) m_bits = 2; // 4 (no synch output)
|
||||
else if (m_steps == 2) m_bits = 1; // 2 (no synch output)
|
||||
else if (m_steps == 1) m_bits = 0; // 1 (no synch output)
|
||||
else if (m_steps == 0) m_bits = 7; // 128 (no synch output)
|
||||
|
||||
if (m_bits >= 0) {
|
||||
const int micros = _BV(m_bits);
|
||||
if (micros < 100) { L6470_CHAR(' '); if (micros < 10) L6470_CHAR(' '); }
|
||||
L6470_ECHO(micros);
|
||||
L6470_ECHOPGM(" uSTEPS");
|
||||
}
|
||||
|
||||
for (j = 0; j < driver_count; j++)
|
||||
L6470.set_param(axis_index[j], L6470_STEP_MODE, m_bits); // set microsteps
|
||||
|
||||
L6470_ECHOLNPAIR("target (maximum) feedrate = ",final_feedrate);
|
||||
|
||||
float feedrate_inc = final_feedrate / 10, // start at 1/10 of max & go up by 1/10 per step)
|
||||
current_feedrate = 0;
|
||||
|
||||
planner.synchronize(); // wait for all current movement commands to complete
|
||||
|
||||
for (j = 0; j < driver_count; j++)
|
||||
L6470.get_status(axis_index[j]); // clear all error flags
|
||||
|
||||
char temp_axis_string[2];
|
||||
temp_axis_string[0] = axis_mon[0][0]; // need to have a string for use within sprintf format section
|
||||
temp_axis_string[1] = '\n';
|
||||
|
||||
char gcode_string[80];
|
||||
uint16_t status_composite = 0;
|
||||
L6470_ECHOLNPGM(".\n.\n."); // make the feedrate prints easier to see
|
||||
|
||||
do {
|
||||
current_feedrate += feedrate_inc;
|
||||
L6470_ECHOLNPAIR("...feedrate = ", current_feedrate);
|
||||
|
||||
sprintf_P(gcode_string, PSTR("G0 %s%4.3f F%4.3f"), temp_axis_string, position_min, current_feedrate);
|
||||
gcode.process_subcommands_now_P(gcode_string);
|
||||
|
||||
sprintf_P(gcode_string, PSTR("G0 %s%4.3f F%4.3f"), temp_axis_string, position_max, current_feedrate);
|
||||
gcode.process_subcommands_now_P(gcode_string);
|
||||
|
||||
planner.synchronize();
|
||||
|
||||
for (j = 0; j < driver_count; j++) {
|
||||
axis_status[j] = (~L6470.get_status(axis_index[j])) & 0x0800; // bits of interest are all active low
|
||||
status_composite |= axis_status[j];
|
||||
}
|
||||
if (status_composite) break; // quit if any errors flags are raised
|
||||
} while (current_feedrate < final_feedrate * 0.99);
|
||||
|
||||
if (status_composite) {
|
||||
L6470_ECHOLNPGM("Completed with errors");
|
||||
for (j = 0; j < driver_count; j++) {
|
||||
L6470_ECHOPGM("...");
|
||||
L6470.error_status_decode(axis_status[j], axis_index[j]);
|
||||
}
|
||||
}
|
||||
else
|
||||
L6470_ECHOLNPGM("Completed with no errors");
|
||||
|
||||
} // M918
|
||||
|
||||
#endif // HAS_DRIVER(L6470)
|
@ -0,0 +1,793 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2019 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* The monitor_driver routines are a close copy of the TMC code
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if HAS_DRIVER(L6470)
|
||||
|
||||
#include "L6470_Marlin.h"
|
||||
|
||||
L6470_Marlin L6470;
|
||||
|
||||
#include "../stepper_indirection.h"
|
||||
#include "../../gcode/gcode.h"
|
||||
#include "../planner.h"
|
||||
|
||||
uint8_t L6470_Marlin::dir_commands[MAX_L6470]; // array to hold direction command for each driver
|
||||
|
||||
char L6470_Marlin::index_to_axis[MAX_L6470][3] = { "X ", "Y ", "Z ", "X2", "Y2", "Z2", "Z3", "E0", "E1", "E2", "E3", "E4", "E5" };
|
||||
|
||||
bool L6470_Marlin::index_to_dir[MAX_L6470] = {
|
||||
INVERT_X_DIR , // 0 X
|
||||
INVERT_Y_DIR , // 1 Y
|
||||
INVERT_Z_DIR , // 2 Z
|
||||
#if ENABLED(X_DUAL_STEPPER_DRIVERS)
|
||||
INVERT_X_DIR ^ INVERT_X2_VS_X_DIR , // 3 X2
|
||||
#else
|
||||
INVERT_X_DIR , // 3 X2
|
||||
#endif
|
||||
#if ENABLED(Y_DUAL_STEPPER_DRIVERS)
|
||||
INVERT_Y_DIR ^ INVERT_Y2_VS_Y_DIR , // 4 Y2
|
||||
#else
|
||||
INVERT_Y_DIR , // 4 Y2
|
||||
#endif
|
||||
INVERT_Z_DIR , // 5 Z2
|
||||
INVERT_Z_DIR , // 6 Z3
|
||||
INVERT_E0_DIR , // 7 E0
|
||||
INVERT_E1_DIR , // 8 E1
|
||||
INVERT_E2_DIR , // 9 E2
|
||||
INVERT_E3_DIR , //10 E3
|
||||
INVERT_E4_DIR , //11 E4
|
||||
INVERT_E5_DIR , //12 E5
|
||||
};
|
||||
|
||||
uint8_t L6470_Marlin::axis_xref[MAX_L6470] = {
|
||||
AxisEnum(X_AXIS), // X
|
||||
AxisEnum(Y_AXIS), // Y
|
||||
AxisEnum(Z_AXIS), // Z
|
||||
AxisEnum(X_AXIS), // X2
|
||||
AxisEnum(Y_AXIS), // Y2
|
||||
AxisEnum(Z_AXIS), // Z2
|
||||
AxisEnum(Z_AXIS), // Z3
|
||||
AxisEnum(E_AXIS), // E0
|
||||
AxisEnum(E_AXIS), // E1
|
||||
AxisEnum(E_AXIS), // E2
|
||||
AxisEnum(E_AXIS), // E3
|
||||
AxisEnum(E_AXIS), // E4
|
||||
AxisEnum(E_AXIS) // E5
|
||||
};
|
||||
|
||||
volatile bool L6470_Marlin::spi_abort = false;
|
||||
bool L6470_Marlin::spi_active = false;
|
||||
|
||||
void L6470_Marlin::populate_chain_array() {
|
||||
|
||||
#define _L6470_INIT_SPI(Q) do{ stepper##Q.set_chain_info(Q, Q##_CHAIN_POS); }while(0)
|
||||
|
||||
#if AXIS_DRIVER_TYPE_X(L6470)
|
||||
_L6470_INIT_SPI(X);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_X2(L6470)
|
||||
_L6470_INIT_SPI(X2);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Y(L6470)
|
||||
_L6470_INIT_SPI(Y);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Y2(L6470)
|
||||
_L6470_INIT_SPI(Y2);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Z(L6470)
|
||||
_L6470_INIT_SPI(Z);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Z2(L6470)
|
||||
_L6470_INIT_SPI(Z2);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Z3(L6470)
|
||||
_L6470_INIT_SPI(Z3);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E0(L6470)
|
||||
_L6470_INIT_SPI(E0);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E1(L6470)
|
||||
_L6470_INIT_SPI(E1);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E2(L6470)
|
||||
_L6470_INIT_SPI(E2);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E3(L6470)
|
||||
_L6470_INIT_SPI(E3);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E4(L6470)
|
||||
_L6470_INIT_SPI(E4);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E5(L6470)
|
||||
_L6470_INIT_SPI(E5);
|
||||
#endif
|
||||
}
|
||||
|
||||
void L6470_Marlin::init() { // Set up SPI and then init chips
|
||||
#if PIN_EXISTS(L6470_RESET_CHAIN)
|
||||
OUT_WRITE(L6470_RESET_CHAIN_PIN, LOW); // hardware reset of drivers
|
||||
delay(1);
|
||||
OUT_WRITE(L6470_RESET_CHAIN_PIN, HIGH);
|
||||
delay(1); // need about 650uS for the chip to fully start up
|
||||
#endif
|
||||
populate_chain_array(); // Set up array to control where in the SPI transfer sequence a particular stepper's data goes
|
||||
L6470_spi_init(); // Set up L6470 soft SPI pins
|
||||
init_to_defaults(); // init the chips
|
||||
}
|
||||
|
||||
uint16_t L6470_Marlin::get_status(const uint8_t axis) {
|
||||
|
||||
#define GET_L6470_STATUS(Q) stepper##Q.getStatus()
|
||||
|
||||
switch (axis) {
|
||||
#if AXIS_DRIVER_TYPE_X(L6470)
|
||||
case 0: return GET_L6470_STATUS(X);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Y(L6470)
|
||||
case 1: return GET_L6470_STATUS(Y);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Z(L6470)
|
||||
case 2: return GET_L6470_STATUS(Z);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_X2(L6470)
|
||||
case 3: return GET_L6470_STATUS(X2);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Y2(L6470)
|
||||
case 4: return GET_L6470_STATUS(Y2);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Z2(L6470)
|
||||
case 5: return GET_L6470_STATUS(Z2);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Z3(L6470)
|
||||
case 6: return GET_L6470_STATUS(Z3);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E0(L6470)
|
||||
case 7: return GET_L6470_STATUS(E0);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E1(L6470)
|
||||
case 8: return GET_L6470_STATUS(E1);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E2(L6470)
|
||||
case 9: return GET_L6470_STATUS(E2);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E3(L6470)
|
||||
case 10: return GET_L6470_STATUS(E3);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E4(L6470)
|
||||
case 11: return GET_L6470_STATUS(E4);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E5(L6470)
|
||||
case 12: return GET_L6470_STATUS(E5);
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0; // Not needed but kills a compiler warning
|
||||
}
|
||||
|
||||
uint32_t L6470_Marlin::get_param(uint8_t axis, uint8_t param) {
|
||||
|
||||
#define GET_L6470_PARAM(Q) L6470_GETPARAM(param,Q)
|
||||
|
||||
switch (axis) {
|
||||
#if AXIS_DRIVER_TYPE_X(L6470)
|
||||
case 0: return GET_L6470_PARAM(X);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Y(L6470)
|
||||
case 1: return GET_L6470_PARAM(Y);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Z(L6470)
|
||||
case 2: return GET_L6470_PARAM(Z);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_X2(L6470)
|
||||
case 3: return GET_L6470_PARAM(X2);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Y2(L6470)
|
||||
case 4: return GET_L6470_PARAM(Y2);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Z2(L6470)
|
||||
case 5: return GET_L6470_PARAM(Z2);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Z3(L6470)
|
||||
case 6: return GET_L6470_PARAM(Z3);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E0(L6470)
|
||||
case 7: return GET_L6470_PARAM(E0);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E1(L6470)
|
||||
case 8: return GET_L6470_PARAM(E1);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E2(L6470)
|
||||
case 9: return GET_L6470_PARAM(E2);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E3(L6470)
|
||||
case 10: return GET_L6470_PARAM(E3);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E4(L6470)
|
||||
case 11: return GET_L6470_PARAM(E4);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E5(L6470)
|
||||
case 12: return GET_L6470_PARAM(E5);
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0 ; // not needed but kills a compiler warning
|
||||
}
|
||||
|
||||
void L6470_Marlin::set_param(uint8_t axis, uint8_t param, uint32_t value) {
|
||||
|
||||
#define SET_L6470_PARAM(Q) stepper##Q.SetParam(param, value)
|
||||
|
||||
switch (axis) {
|
||||
#if AXIS_DRIVER_TYPE_X(L6470)
|
||||
case 0: SET_L6470_PARAM(X);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Y(L6470)
|
||||
case 1: SET_L6470_PARAM(Y);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Z(L6470)
|
||||
case 2: SET_L6470_PARAM(Z);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_X2(L6470)
|
||||
case 3: SET_L6470_PARAM(X2);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Y2(L6470)
|
||||
case 4: SET_L6470_PARAM(Y2);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Z2(L6470)
|
||||
case 5: SET_L6470_PARAM(Z2);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Z3(L6470)
|
||||
case 6: SET_L6470_PARAM(Z3);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E0(L6470)
|
||||
case 7: SET_L6470_PARAM(E0);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E1(L6470)
|
||||
case 8: SET_L6470_PARAM(E1);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E2(L6470)
|
||||
case 9: SET_L6470_PARAM(E2);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E3(L6470)
|
||||
case 10: SET_L6470_PARAM(E3);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E4(L6470)
|
||||
case 11: SET_L6470_PARAM(E4);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E5(L6470)
|
||||
case 12: SET_L6470_PARAM(E5);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
inline void echo_min_max(const char a, const float &min, const float &max) {
|
||||
L6470_CHAR(' '); L6470_CHAR(a);
|
||||
L6470_ECHOPAIR(" min = ", min);
|
||||
L6470_ECHOLNPAIR(" max = ", max);
|
||||
}
|
||||
inline void echo_oct_used(const float &oct, const bool stall) {
|
||||
L6470_ECHOPAIR("over_current_threshold used : ", oct);
|
||||
serialprintPGM(stall ? PSTR(" (Stall") : PSTR(" (OCD"));
|
||||
L6470_ECHOLNPGM(" threshold)");
|
||||
}
|
||||
inline void err_out_of_bounds() { L6470_ECHOLNPGM("ERROR - motion out of bounds"); }
|
||||
|
||||
bool L6470_Marlin::get_user_input(uint8_t &driver_count, uint8_t axis_index[3], char axis_mon[3][3],
|
||||
float &position_max, float &position_min, float &final_feedrate, uint8_t &kval_hold,
|
||||
bool over_current_flag, uint8_t &OCD_TH_val, uint8_t &STALL_TH_val, uint16_t &over_current_threshold
|
||||
) {
|
||||
// Return TRUE if the calling routine needs to abort/kill
|
||||
|
||||
uint16_t displacement = 0; // " = 0" to eliminate compiler warning
|
||||
uint8_t j; // general purpose counter
|
||||
|
||||
if (!all_axes_homed()) {
|
||||
L6470_ECHOLNPGM("ERROR - home all before running this command");
|
||||
//return true;
|
||||
}
|
||||
|
||||
LOOP_XYZE(i) if (uint16_t _displacement = parser.intval(axis_codes[i])) {
|
||||
displacement = _displacement;
|
||||
uint8_t axis_offset = parser.byteval('J');
|
||||
axis_mon[0][0] = axis_codes[i]; // axis ASCII value (target character)
|
||||
if (axis_offset >= 2 || axis_mon[0][0] == 'E') // Single axis, E0, or E1
|
||||
axis_mon[0][1] = axis_offset + '0';
|
||||
else if (axis_offset == 0) { // one or more axes
|
||||
uint8_t driver_count_local = 0; // can't use "driver_count" directly as a subscript because it's passed by reference
|
||||
for (j = 0; j < MAX_L6470; j++) // see how many drivers on this axis
|
||||
if (axis_mon[0][0] == index_to_axis[j][0]) {
|
||||
axis_mon[driver_count_local][0] = axis_mon[0][0];
|
||||
axis_mon[driver_count_local][1] = index_to_axis[j][1];
|
||||
axis_mon[driver_count_local][2] = index_to_axis[j][2]; // append end of string
|
||||
axis_index[driver_count_local] = j; // set axis index
|
||||
driver_count_local++;
|
||||
}
|
||||
driver_count = driver_count_local;
|
||||
}
|
||||
break; // only take first axis found
|
||||
}
|
||||
|
||||
//
|
||||
// Position calcs & checks
|
||||
//
|
||||
|
||||
const float center[] = {
|
||||
LOGICAL_X_POSITION(current_position[X_AXIS]),
|
||||
LOGICAL_Y_POSITION(current_position[Y_AXIS]),
|
||||
LOGICAL_Z_POSITION(current_position[Z_AXIS]),
|
||||
current_position[E_AXIS]
|
||||
};
|
||||
|
||||
switch (axis_mon[0][0]) {
|
||||
default: position_max = position_min = 0; break;
|
||||
|
||||
case 'X': {
|
||||
position_min = center[X_AXIS] - displacement;
|
||||
position_max = center[X_AXIS] + displacement;
|
||||
echo_min_max('X', position_min, position_max);
|
||||
if (false
|
||||
#ifdef X_MIN_POS
|
||||
|| position_min < (X_MIN_POS)
|
||||
#endif
|
||||
#ifdef X_MAX_POS
|
||||
|| position_max > (X_MAX_POS)
|
||||
#endif
|
||||
) {
|
||||
err_out_of_bounds();
|
||||
return true;
|
||||
}
|
||||
} break;
|
||||
|
||||
case 'Y': {
|
||||
position_min = center[Y_AXIS] - displacement;
|
||||
position_max = center[Y_AXIS] + displacement;
|
||||
echo_min_max('Y', position_min, position_max);
|
||||
if (false
|
||||
#ifdef Y_MIN_POS
|
||||
|| position_min < (Y_MIN_POS)
|
||||
#endif
|
||||
#ifdef Y_MAX_POS
|
||||
|| position_max > (Y_MAX_POS)
|
||||
#endif
|
||||
) {
|
||||
err_out_of_bounds();
|
||||
return true;
|
||||
}
|
||||
} break;
|
||||
|
||||
case 'Z': {
|
||||
position_min = center[E_AXIS] - displacement;
|
||||
position_max = center[E_AXIS] + displacement;
|
||||
echo_min_max('Z', position_min, position_max);
|
||||
if (false
|
||||
#ifdef Z_MIN_POS
|
||||
|| position_min < (Z_MIN_POS)
|
||||
#endif
|
||||
#ifdef Z_MAX_POS
|
||||
|| position_max > (Z_MAX_POS)
|
||||
#endif
|
||||
) {
|
||||
err_out_of_bounds();
|
||||
return true;
|
||||
}
|
||||
} break;
|
||||
|
||||
case 'E': {
|
||||
position_min = center[E_AXIS] - displacement;
|
||||
position_max = center[E_AXIS] + displacement;
|
||||
echo_min_max('E', position_min, position_max);
|
||||
} break;
|
||||
}
|
||||
|
||||
//
|
||||
// Work on the drivers
|
||||
//
|
||||
for (uint8_t k = 0; k < driver_count; k++) {
|
||||
bool not_found = true;
|
||||
for (j = 1; j <= L6470::chain[0]; j++) {
|
||||
const char * const ind_axis = index_to_axis[L6470::chain[j]];
|
||||
if (ind_axis[0] == axis_mon[k][0] && ind_axis[1] == axis_mon[k][1]) { // See if a L6470 driver
|
||||
not_found = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (not_found) {
|
||||
driver_count = k;
|
||||
axis_mon[k][0] = ' '; // mark this entry invalid
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (driver_count == 0) {
|
||||
L6470_ECHOLNPGM("ERROR - not a L6470 axis");
|
||||
return true;
|
||||
}
|
||||
|
||||
L6470_ECHOPGM("Monitoring:");
|
||||
for (j = 0; j < driver_count; j++) L6470_ECHOPAIR(" ", axis_mon[j]);
|
||||
L6470_EOL();
|
||||
|
||||
// now have a list of driver(s) to monitor
|
||||
|
||||
//
|
||||
// kVAL_HOLD checks & settings
|
||||
//
|
||||
|
||||
kval_hold = parser.byteval('K');
|
||||
if (kval_hold) {
|
||||
L6470_ECHOLNPAIR("kval_hold = ", kval_hold);
|
||||
for (j = 0; j < driver_count; j++)
|
||||
set_param(axis_index[j], L6470_KVAL_HOLD, kval_hold);
|
||||
}
|
||||
else {
|
||||
// only print the KVAL_HOLD from one of the drivers
|
||||
kval_hold = get_param(axis_index[0], L6470_KVAL_HOLD);
|
||||
L6470_ECHOLNPAIR("KVAL_HOLD = ", kval_hold);
|
||||
}
|
||||
|
||||
//
|
||||
// Overcurrent checks & settings
|
||||
//
|
||||
|
||||
if (over_current_flag) {
|
||||
|
||||
uint8_t OCD_TH_val_local = 0, // compiler thinks OCD_TH_val is unused if use it directly
|
||||
STALL_TH_val_local = 0; // just in case ...
|
||||
|
||||
over_current_threshold = parser.intval('I');
|
||||
|
||||
if (over_current_threshold) {
|
||||
|
||||
OCD_TH_val_local = over_current_threshold/375;
|
||||
LIMIT(OCD_TH_val_local, 0, 15);
|
||||
STALL_TH_val_local = over_current_threshold/31.25;
|
||||
LIMIT(STALL_TH_val_local, 0, 127);
|
||||
uint16_t OCD_TH_actual = (OCD_TH_val_local + 1) * 375,
|
||||
STALL_TH_actual = (STALL_TH_val_local + 1) * 31.25;
|
||||
if (OCD_TH_actual < STALL_TH_actual) {
|
||||
OCD_TH_val_local++;
|
||||
OCD_TH_actual = (OCD_TH_val_local + 1) * 375;
|
||||
}
|
||||
|
||||
L6470_ECHOLNPAIR("over_current_threshold specified: ", over_current_threshold);
|
||||
echo_oct_used(STALL_TH_actual, true);
|
||||
echo_oct_used(OCD_TH_actual, false);
|
||||
|
||||
#define SET_OVER_CURRENT(Q) do { stepper##Q.SetParam(L6470_STALL_TH, STALL_TH_val_local); stepper##Q.SetParam(L6470_OCD_TH, OCD_TH_val_local);} while (0)
|
||||
|
||||
for (j = 0; j < driver_count; j++) {
|
||||
set_param(axis_index[j], L6470_STALL_TH, STALL_TH_val_local);
|
||||
set_param(axis_index[j], L6470_OCD_TH, OCD_TH_val_local);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// only get & print the OVER_CURRENT values from one of the drivers
|
||||
STALL_TH_val_local = get_param(axis_index[0], L6470_STALL_TH);
|
||||
OCD_TH_val_local = get_param(axis_index[0], L6470_OCD_TH);
|
||||
|
||||
echo_oct_used((STALL_TH_val_local + 1) * 31.25, true);
|
||||
echo_oct_used((OCD_TH_val_local + 1) * 375, false);
|
||||
} // over_current_threshold
|
||||
|
||||
for (j = 0; j < driver_count; j++) { // set all drivers on axis the same
|
||||
set_param(axis_index[j], L6470_STALL_TH, STALL_TH_val_local);
|
||||
set_param(axis_index[j], L6470_OCD_TH, OCD_TH_val_local);
|
||||
}
|
||||
|
||||
OCD_TH_val = OCD_TH_val_local; // force compiler to update the main routine's copy
|
||||
STALL_TH_val = STALL_TH_val_local; // force compiler to update the main routine's copy
|
||||
} // end of overcurrent
|
||||
|
||||
//
|
||||
// Feedrate
|
||||
//
|
||||
|
||||
final_feedrate = parser.floatval('F');
|
||||
if (final_feedrate == 0) {
|
||||
static constexpr float default_max_feedrate[] = DEFAULT_MAX_FEEDRATE;
|
||||
const uint8_t num_feedrates = COUNT(default_max_feedrate);
|
||||
for (j = 0; j < num_feedrates; j++) {
|
||||
if (axis_codes[j] == axis_mon[0][0]) {
|
||||
final_feedrate = default_max_feedrate[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == 3 && num_feedrates > 4) { // have more than one extruder feedrate
|
||||
uint8_t extruder_num = axis_mon[0][1] - '0';
|
||||
if (j <= num_feedrates - extruder_num) // have a feedrate specifically for this extruder
|
||||
final_feedrate = default_max_feedrate[j + extruder_num];
|
||||
else
|
||||
final_feedrate = default_max_feedrate[3]; // use E0 feedrate for this extruder
|
||||
}
|
||||
final_feedrate *= 60; // convert to mm/minute
|
||||
} // end of feedrate
|
||||
|
||||
return false; // FALSE indicates no user input problems
|
||||
}
|
||||
|
||||
#if ENABLED(L6470_CHITCHAT)
|
||||
inline void echo_yes_no(const bool yes) { serialprintPGM(yes ? PSTR("YES") : PSTR("NO ")); }
|
||||
#endif
|
||||
|
||||
void L6470_Marlin::say_axis(const uint8_t axis, const bool label/*=true*/) {
|
||||
if (label) SERIAL_ECHOPGM("AXIS:");
|
||||
SERIAL_CHAR(' ');
|
||||
SERIAL_CHAR(index_to_axis[axis][0]);
|
||||
SERIAL_CHAR(index_to_axis[axis][1]);
|
||||
SERIAL_CHAR(' ');
|
||||
}
|
||||
|
||||
void L6470_Marlin::error_status_decode(const uint16_t status, const uint8_t axis) { // assumes status bits have been inverted
|
||||
#if ENABLED(L6470_CHITCHAT)
|
||||
char temp_buf[10];
|
||||
say_axis(axis);
|
||||
sprintf_P(temp_buf, PSTR(" %4x "), status);
|
||||
L6470_ECHO(temp_buf);
|
||||
print_bin(status);
|
||||
L6470_ECHOPGM(" THERMAL: ");
|
||||
serialprintPGM((status & STATUS_TH_SD) ? PSTR("SHUTDOWN") : (status & STATUS_TH_WRN) ? PSTR("WARNING ") : PSTR("OK "));
|
||||
L6470_ECHOPGM(" OVERCURRENT: ");
|
||||
echo_yes_no(status & STATUS_OCD);
|
||||
L6470_ECHOPGM(" STALL: ");
|
||||
echo_yes_no(status & (STATUS_STEP_LOSS_A | STATUS_STEP_LOSS_B));
|
||||
L6470_EOL();
|
||||
#else
|
||||
UNUSED(status); UNUSED(axis);
|
||||
#endif
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////
|
||||
//// MONITOR_L6470_DRIVER_STATUS routines
|
||||
////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if ENABLED(MONITOR_L6470_DRIVER_STATUS)
|
||||
|
||||
struct L6470_driver_data {
|
||||
uint8_t driver_index;
|
||||
uint32_t driver_status;
|
||||
bool is_otw;
|
||||
uint8_t otw_counter;
|
||||
bool is_ot;
|
||||
bool is_hi_Z;
|
||||
uint8_t com_counter;
|
||||
};
|
||||
|
||||
L6470_driver_data driver_L6470_data[] = {
|
||||
#if AXIS_DRIVER_TYPE_X(L6470)
|
||||
{ 0, 0, 0, 0, 0, 0, 0 },
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Y(L6470)
|
||||
{ 1, 0, 0, 0, 0, 0, 0 },
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Z(L6470)
|
||||
{ 2, 0, 0, 0, 0, 0, 0 },
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_X2(L6470)
|
||||
{ 3, 0, 0, 0, 0, 0, 0 },
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Y2(L6470)
|
||||
{ 4, 0, 0, 0, 0, 0, 0 },
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Z2(L6470)
|
||||
{ 5, 0, 0, 0, 0, 0, 0 },
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Z3(L6470)
|
||||
{ 6, 0, 0, 0, 0, 0, 0 },
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E0(L6470)
|
||||
{ 7, 0, 0, 0, 0, 0, 0 },
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E1(L6470)
|
||||
{ 8, 0, 0, 0, 0, 0, 0 },
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E2(L6470)
|
||||
{ 9, 0, 0, 0, 0, 0, 0 },
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E3(L6470)
|
||||
{ 10, 0, 0, 0, 0, 0, 0 },
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E4(L6470)
|
||||
{ 11, 0, 0, 0, 0, 0, 0 },
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E5(L6470)
|
||||
{ 12, 0, 0, 0, 0, 0, 0 }
|
||||
#endif
|
||||
};
|
||||
|
||||
inline void append_stepper_err(char * &p, const uint8_t stepper_index, const char * const err=NULL) {
|
||||
p += sprintf_P(p, PSTR("Stepper %c%c "), char(index_to_axis[stepper_index][0]), char(index_to_axis[stepper_index][1]));
|
||||
if (err) p += sprintf_P(p, err);
|
||||
}
|
||||
|
||||
void L6470_monitor_update(uint8_t stepper_index, uint16_t status) {
|
||||
if (spi_abort) return; // don't do anything if set_directions() has occurred
|
||||
uint8_t kval_hold;
|
||||
char temp_buf[120];
|
||||
char* p = &temp_buf[0];
|
||||
uint8_t j;
|
||||
for (j = 0; j < L6470::chain[0]; j++) // find the table for this stepper
|
||||
if (driver_L6470_data[j].driver_index == stepper_index) break;
|
||||
|
||||
driver_L6470_data[j].driver_status = status;
|
||||
uint16_t _status = ~status; // all error bits are active low
|
||||
|
||||
if (status == 0 || status == 0xFFFF) { // com problem
|
||||
if (driver_L6470_data[j].com_counter == 0) { // warn user when it first happens
|
||||
driver_L6470_data[j].com_counter++;
|
||||
append_stepper_err(p, stepper_index, PSTR(" - communications lost\n"));
|
||||
L6470_ECHO(temp_buf);
|
||||
}
|
||||
else {
|
||||
driver_L6470_data[j].com_counter++;
|
||||
if (driver_L6470_data[j].com_counter > 240) { // remind of com problem about every 2 minutes
|
||||
driver_L6470_data[j].com_counter = 1;
|
||||
append_stepper_err(p, stepper_index, PSTR(" - still no communications\n"));
|
||||
L6470_ECHO(temp_buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (driver_L6470_data[j].com_counter) { // comms re-established
|
||||
driver_L6470_data[j].com_counter = 0;
|
||||
append_stepper_err(p, stepper_index, PSTR(" - communications re-established\n.. setting all drivers to default values\n"));
|
||||
L6470_ECHO(temp_buf);
|
||||
init_to_defaults();
|
||||
}
|
||||
else {
|
||||
// no com problems - do the usual checks
|
||||
if (_status & L6470_ERROR_MASK) {
|
||||
append_stepper_err(p, stepper_index);
|
||||
|
||||
if (status & STATUS_HIZ) { // the driver has shut down HiZ is active high
|
||||
driver_L6470_data[j].is_hi_Z = true;
|
||||
p += sprintf_P(p, PSTR("%cIS SHUT DOWN"), ' ');
|
||||
// if (_status & STATUS_TH_SD) { // strange - TH_SD never seems to go active, must be implied by the HiZ and TH_WRN
|
||||
if (_status & STATUS_TH_WRN) { // over current shutdown
|
||||
p += sprintf_P(p, PSTR("%cdue to over temperature"), ' ');
|
||||
driver_L6470_data[j].is_ot = true;
|
||||
kval_hold = get_param(stepper_index, L6470_KVAL_HOLD) - 2 * KVAL_HOLD_STEP_DOWN;
|
||||
set_param(stepper_index, L6470_KVAL_HOLD, kval_hold); // reduce KVAL_HOLD
|
||||
p += sprintf_P(p, PSTR(" - KVAL_HOLD reduced by %d to %d"), 2 * KVAL_HOLD_STEP_DOWN, kval_hold); // let user know
|
||||
}
|
||||
else
|
||||
driver_L6470_data[j].is_ot = false;
|
||||
}
|
||||
else {
|
||||
driver_L6470_data[j].is_hi_Z = false;
|
||||
|
||||
if (_status & STATUS_TH_WRN) { // have an over temperature warning
|
||||
driver_L6470_data[j].is_otw = true;
|
||||
driver_L6470_data[j].otw_counter++;
|
||||
kval_hold = get_param(stepper_index, L6470_KVAL_HOLD);
|
||||
if (driver_L6470_data[j].otw_counter > 4) { // otw present for 2 - 2.5 seconds, reduce KVAL_HOLD
|
||||
kval_hold -= KVAL_HOLD_STEP_DOWN;
|
||||
set_param(stepper_index, L6470_KVAL_HOLD, kval_hold); // reduce KVAL_HOLD
|
||||
p += sprintf_P(p, PSTR(" - KVAL_HOLD reduced by %d to %d"), KVAL_HOLD_STEP_DOWN, kval_hold); // let user know
|
||||
driver_L6470_data[j].otw_counter = 0;
|
||||
driver_L6470_data[j].is_otw = true;
|
||||
}
|
||||
else if (driver_L6470_data[j].otw_counter)
|
||||
p += sprintf_P(p, PSTR("%c- thermal warning"), ' '); // warn user
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef L6470_STOP_ON_ERROR
|
||||
if (_status & (STATUS_UVLO | STATUS_TH_WRN | STATUS_TH_SD))
|
||||
kill(temp_buf);
|
||||
#endif
|
||||
|
||||
|
||||
#if ENABLED(L6470_CHITCHAT)
|
||||
|
||||
if (_status & STATUS_OCD)
|
||||
p += sprintf_P(p, PSTR("%c over current"), ' ');
|
||||
|
||||
if (_status & (STATUS_STEP_LOSS_A | STATUS_STEP_LOSS_B))
|
||||
p += sprintf_P(p, PSTR("%c stall"), ' ');
|
||||
|
||||
if (_status & STATUS_UVLO)
|
||||
p += sprintf_P(p, PSTR("%c under voltage lock out"), ' ');
|
||||
|
||||
p += sprintf_P(p, PSTR("%c\n"), ' ');
|
||||
#endif
|
||||
|
||||
L6470_ECHOLN(temp_buf); // print the error message
|
||||
}
|
||||
else {
|
||||
driver_L6470_data[j].is_ot = false;
|
||||
driver_L6470_data[j].otw_counter = 0; //clear out warning indicators
|
||||
driver_L6470_data[j].is_otw = false;
|
||||
|
||||
} // end usual checks
|
||||
} // comms established but have errors
|
||||
} // comms re-established
|
||||
} // end L6470_monitor_update()
|
||||
|
||||
#define MONITOR_L6470_DRIVE(Q) L6470_monitor_update(Q, stepper##Q.getStatus())
|
||||
|
||||
void L6470_Marlin::monitor_driver() {
|
||||
static millis_t next_cOT = 0;
|
||||
if (ELAPSED(millis(), next_cOT)) {
|
||||
next_cOT = millis() + 500;
|
||||
|
||||
spi_active = true; // let set_directions() know we're in the middle of a series of SPI transfers
|
||||
|
||||
#if AXIS_DRIVER_TYPE_X(L6470)
|
||||
MONITOR_L6470_DRIVE(X);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Y(L6470)
|
||||
MONITOR_L6470_DRIVE(Y);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Z(L6470)
|
||||
MONITOR_L6470_DRIVE(Z);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_X2(L6470)
|
||||
MONITOR_L6470_DRIVE(X2);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Y2(L6470)
|
||||
MONITOR_L6470_DRIVE(Y2);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Z2(L6470)
|
||||
MONITOR_L6470_DRIVE(Z2);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Z3(L6470)
|
||||
MONITOR_L6470_DRIVE(Z3);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E0(L6470)
|
||||
MONITOR_L6470_DRIVE(E0);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E1(L6470)
|
||||
MONITOR_L6470_DRIVE(E1);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E2(L6470)
|
||||
MONITOR_L6470_DRIVE(E2);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E3(L6470)
|
||||
MONITOR_L6470_DRIVE(E3);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E4(L6470)
|
||||
MONITOR_L6470_DRIVE(E4);
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_E5(L6470)
|
||||
MONITOR_L6470_DRIVE(E5);
|
||||
#endif
|
||||
|
||||
#if ENABLED(L6470_DEBUG)
|
||||
if (report_L6470_status) L6470_EOL();
|
||||
#endif
|
||||
|
||||
spi_active = false; // done with all SPI transfers - clear handshake flags
|
||||
spi_abort = false;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // MONITOR_L6470_DRIVER_STATUS
|
||||
|
||||
#endif // HAS_DRIVER(L6470)
|
@ -0,0 +1,93 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2018 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#include <L6470.h>
|
||||
|
||||
#if ENABLED(L6470_CHITCHAT)
|
||||
#define L6470_EOL() SERIAL_EOL()
|
||||
#define L6470_CHAR(C) SERIAL_CHAR(C)
|
||||
#define L6470_ECHO(V) SERIAL_ECHO(V)
|
||||
#define L6470_ECHOLN(V) SERIAL_ECHOLN(V)
|
||||
#define L6470_ECHOPGM(S) SERIAL_ECHOPGM(S)
|
||||
#define L6470_ECHOLNPGM(S) SERIAL_ECHOLNPGM(S)
|
||||
#define L6470_ECHOPAIR(S,V) SERIAL_ECHOPAIR(S,V)
|
||||
#define L6470_ECHOLNPAIR(S,V) SERIAL_ECHOLNPAIR(S,V)
|
||||
#else
|
||||
#define L6470_EOL() NOOP
|
||||
#define L6470_CHAR(C) NOOP
|
||||
#define L6470_ECHO(V) NOOP
|
||||
#define L6470_ECHOLN(V) NOOP
|
||||
#define L6470_ECHOPGM(S) NOOP
|
||||
#define L6470_ECHOLNPGM(S) NOOP
|
||||
#define L6470_ECHOPAIR(S,V) NOOP
|
||||
#define L6470_ECHOLNPAIR(S,V) NOOP
|
||||
#endif
|
||||
|
||||
#define L6470_GETPARAM(P,Q) stepper##Q.GetParam(P)
|
||||
|
||||
#define MAX_L6470 (7 + MAX_EXTRUDERS) // Maximum number of axes in Marlin
|
||||
|
||||
#define L6470_ERROR_MASK (STATUS_UVLO | STATUS_TH_WRN | STATUS_TH_SD | STATUS_OCD | STATUS_STEP_LOSS_A | STATUS_STEP_LOSS_B)
|
||||
#define dSPIN_STEP_CLOCK_FWD dSPIN_STEP_CLOCK
|
||||
#define dSPIN_STEP_CLOCK_REV dSPIN_STEP_CLOCK+1
|
||||
#define HAS_L6470_EXTRUDER ( AXIS_DRIVER_TYPE_E0(L6470) || AXIS_DRIVER_TYPE_E1(L6470) || AXIS_DRIVER_TYPE_E2(L6470) \
|
||||
|| AXIS_DRIVER_TYPE_E3(L6470) || AXIS_DRIVER_TYPE_E4(L6470) || AXIS_DRIVER_TYPE_E5(L6470) )
|
||||
|
||||
class L6470_Marlin {
|
||||
public:
|
||||
static bool index_to_dir[MAX_L6470];
|
||||
static uint8_t axis_xref[MAX_L6470];
|
||||
static char index_to_axis[MAX_L6470][3];
|
||||
static uint8_t dir_commands[MAX_L6470];
|
||||
|
||||
// flags to guarantee graceful switch if stepper interrupts L6470 SPI transfer
|
||||
static volatile bool spi_abort;
|
||||
static bool spi_active;
|
||||
|
||||
L6470_Marlin() {}
|
||||
|
||||
static uint16_t get_status(const uint8_t axis);
|
||||
|
||||
static uint32_t get_param(uint8_t axis, uint8_t param);
|
||||
|
||||
static void set_param(uint8_t axis, uint8_t param, uint32_t value);
|
||||
|
||||
static bool get_user_input(uint8_t &driver_count, uint8_t axis_index[3], char axis_mon[3][3],
|
||||
float &position_max, float &position_min, float &final_feedrate, uint8_t &kval_hold,
|
||||
bool over_current_flag, uint8_t &OCD_TH_val, uint8_t &STALL_TH_val, uint16_t &over_current_threshold);
|
||||
|
||||
static void error_status_decode(const uint16_t status, const uint8_t axis);
|
||||
|
||||
static void monitor_driver();
|
||||
|
||||
static void init();
|
||||
static void init_to_defaults();
|
||||
|
||||
static void say_axis(const uint8_t axis, const bool label=true);
|
||||
|
||||
private:
|
||||
void populate_chain_array();
|
||||
};
|
||||
|
||||
extern L6470_Marlin L6470;
|
Loading…
Reference in New Issue