committed by
Scott Lahteine
95 changed files with 7844 additions and 1429 deletions
@ -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,105 @@ |
|||
Arduino-6470 library revision 0.7.0 or above is required. |
|||
|
|||
This software can be used with any L647x chip and the powerSTEP01. L647x and powerSTEP01 devices can not be mixed within a system. A flag in the library must be set to enable use of a powerSTEP01. |
|||
|
|||
These devices use voltage PWMs to drive the stepper phases. Phase current is not directly controlled. Each microstep corresponds to a particular PWM duty cycle. The KVAL\_HOLD register scales the PWM duty cycle. |
|||
|
|||
This software assumes that all L6470 drivers are in one SPI daisy chain. |
|||
|
|||
``` {.gcode} |
|||
The hardware setup is: |
|||
|
|||
MOSI from controller tied to SDI on the first device |
|||
|
|||
SDO of the first device is tied to SDI of the next device |
|||
|
|||
SDO of the last device is tied to MISO of the controller |
|||
|
|||
all devices share the same SCK, SS\_PIN and RESET\_PIN |
|||
|
|||
Each L6470 passes the data it saw on its SDI to its neighbor on the **NEXT** SPI cycle (8 bit delay). |
|||
|
|||
Each L6470 acts on the **last** SPI data it saw when the SS\_PIN **goes high**. |
|||
``` |
|||
|
|||
The L6470 drivers operate in STEP\_CLOCK mode. In this mode the direction and enable are done via SPI commands and the phase currents are changed in response to step pulses (generated in the usual way). |
|||
|
|||
There are two different SPI routines used. |
|||
|
|||
- **uint8\_t** L6470\_Transfer(uint8\_t data, int \_SSPin, const uint8\_t chain\_position) is used to setup the chips and by the maintenance/status code. This code uses the Arduino-6470 library. |
|||
|
|||
- **void** L6470\_Transfer(uint8\_t L6470\_buf[], const uint8\_t length) is used by the set\_directions() routine to send the direction/enable commands. The library is NOT used by this code. |
|||
|
|||
**HARDWARE/SOFTWARE interaction** |
|||
|
|||
Powering up a stepper and setting the direction are done by the same command. Can't do one without the other. |
|||
|
|||
**All** directions are set **every time** a new block is popped off the queue by the stepper ISR. |
|||
|
|||
SPI transfers, when setting the directions, are minimized by using arrays and a SPI routine dedicated to this function. L6470 library calls are not used. For N L6470 drivers, this results in a N byte transfer. If library calls were used then N\*N bytes would be sent. |
|||
|
|||
**Power up (reset) sequence:** |
|||
|
|||
1. Stepper objects are created before the **setup()** entry point is reached. |
|||
|
|||
2. After the **setup()** entry point is reached and before the steppers are initialized, L6470\_init() is called to do the following |
|||
|
|||
3. If present, the hardware reset is pulsed. |
|||
|
|||
4. The L6470\_chain array is populated during **setup()**. This array is used to tell where in the SPI stream the commands/data for an stepper is positioned. |
|||
|
|||
5. The L6470 soft SPI pins are initialized. |
|||
|
|||
6. The L6470 chips are initialized during **setup()**. They can be re-initialized using the **L6470\_init\_to\_defaults()** function |
|||
|
|||
The steppers are **NOT** powered up during this sequence. |
|||
|
|||
**L6470\_chain** array |
|||
|
|||
This array is used by all routines that transmit SPI data. |
|||
|
|||
``` {.gcode} |
|||
Location 0 - number of drivers in chain |
|||
|
|||
Location 1 - axis index for first device in the chain (closest to MOSI) |
|||
|
|||
… |
|||
|
|||
Location N - axis index for last device in the N device long chain (closest to MISO) |
|||
``` |
|||
|
|||
**Direction set and enable** |
|||
|
|||
The DIR\_WRITE macros for the L6470 drivers are written so that the standard X, Y, Z and extruder logic used by the set\_directions() routine is not altered. These macros write the correct forward/reverse command to the corresponding location in the array *L6470\_dir\_commands*. |
|||
|
|||
At the end of the set\_directions() routine, the array *L6470\_chain* is used to grab the corresponding direction/enable commands out of the array *L6470\_dir\_commands* and put them in the correct sequence in the array *L6470\_buf*. Array *L6470\_buf* is then passed to the **void** L6470\_Transfer function which actually sends the data to the devices. |
|||
|
|||
**Utilities and misc** |
|||
|
|||
The **absolute position** registers should accurately reflect Marlin’s stepper position counts. They are set to zero during initialization. G28 sets them to the Marlin counts for the corresponding axis after homing. NOTE – these registers are often the negative of the Marlin counts. This is because the Marlin counts reflect the logical direction while the registers reflect the stepper direction. The register contents are displayed via the M114 D command. |
|||
|
|||
The **L6470\_monitor** feature reads the status of each device every half second. It will report if there are any error conditions present or if communications has been lost/restored. The KVAL\_HOLD value is reduced every 2 – 2.5 seconds if the thermal warning or thermal shutdown conditions are present. |
|||
|
|||
**M122** displays the settings of most of the bits in the status register plus a couple of other items. |
|||
|
|||
**M906** can be used to set the KVAL\_HOLD register one driver at a time. If a setting is not included with the command then the contents of the registers that affect the phase current/voltage are displayed. |
|||
|
|||
**M916, M917 & M918** |
|||
|
|||
These utilities are used to tune the system. They can get you in the ballpark for acceptable jerk, acceleration, top speed and KVAL\_HOLD settings. In general they seem to provide an overly optimistic KVAL\_HOLD setting because of the lag between setting KVAL\_HOLD and the driver reaching final temperature. Enabling the **L6470\_monitor** feature during prints will provide the **final useful KVAL\_HOLD setting**. |
|||
|
|||
The amount of power needed to move the stepper without skipping steps increases as jerk, acceleration and top speed increase. The power dissipated by the driver increases as the power to the stepper increases. The net result is a balancing act between jerk, acceleration, top speed and power dissipated by the driver. |
|||
|
|||
**M916 -** Increases KVAL\_HOLD while moving one axis until get thermal warning. 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. |
|||
|
|||
**M917 -** Find minimum current thresholds. This is done by doing the following steps while moving an axis: |
|||
|
|||
1. Decrease OCD current until overcurrent error |
|||
|
|||
2. Increase OCD until overcurrent error goes away |
|||
|
|||
3. Decrease stall threshold until stall error |
|||
|
|||
4. Increase stall until stall error goes away |
|||
|
|||
**M918 -** Increase speed until error or max feedrate achieved. |
@ -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