Merge upstream changes from Marlin 2.1.1
This commit is contained in:
@@ -48,17 +48,17 @@
|
||||
#ifdef __STM32F1__
|
||||
#define SDA_IN() do{ PIN_MAP[IIC_EEPROM_SDA].gpio_device->regs->CRH &= 0XFFFF0FFF; PIN_MAP[IIC_EEPROM_SDA].gpio_device->regs->CRH |= 8 << 12; }while(0)
|
||||
#define SDA_OUT() do{ PIN_MAP[IIC_EEPROM_SDA].gpio_device->regs->CRH &= 0XFFFF0FFF; PIN_MAP[IIC_EEPROM_SDA].gpio_device->regs->CRH |= 3 << 12; }while(0)
|
||||
#elif STM32F1
|
||||
#elif defined(STM32F1) || defined(STM32F4)
|
||||
#define SDA_IN() SET_INPUT(IIC_EEPROM_SDA)
|
||||
#define SDA_OUT() SET_OUTPUT(IIC_EEPROM_SDA)
|
||||
#endif
|
||||
|
||||
// IO ops
|
||||
#define IIC_SCL_0() WRITE(IIC_EEPROM_SCL, LOW)
|
||||
#define IIC_SCL_1() WRITE(IIC_EEPROM_SCL, HIGH)
|
||||
#define IIC_SDA_0() WRITE(IIC_EEPROM_SDA, LOW)
|
||||
#define IIC_SDA_1() WRITE(IIC_EEPROM_SDA, HIGH)
|
||||
#define READ_SDA() READ(IIC_EEPROM_SDA)
|
||||
#define IIC_SCL_0() WRITE(IIC_EEPROM_SCL, LOW)
|
||||
#define IIC_SCL_1() WRITE(IIC_EEPROM_SCL, HIGH)
|
||||
#define IIC_SDA_0() WRITE(IIC_EEPROM_SDA, LOW)
|
||||
#define IIC_SDA_1() WRITE(IIC_EEPROM_SDA, HIGH)
|
||||
#define READ_SDA() READ(IIC_EEPROM_SDA)
|
||||
|
||||
//
|
||||
// Simple IIC interface via libmaple
|
||||
|
@@ -1,998 +0,0 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* The monitor_driver routines are a close copy of the TMC code
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if HAS_L64XX
|
||||
|
||||
#include "L64XX_Marlin.h"
|
||||
|
||||
L64XX_Marlin L64xxManager;
|
||||
|
||||
#include "../../module/stepper/indirection.h"
|
||||
#include "../../gcode/gcode.h"
|
||||
#include "../../module/planner.h"
|
||||
#include "../../HAL/shared/Delay.h"
|
||||
|
||||
static const char LINEAR_AXIS_LIST(
|
||||
str_X[] PROGMEM = "X ", str_Y[] PROGMEM = "Y ", str_Z[] PROGMEM = "Z ",
|
||||
str_I[] PROGMEM = AXIS4_STR " ", str_J[] PROGMEM = AXIS5_STR " ", str_K[] PROGMEM = AXIS6_STR " "
|
||||
),
|
||||
str_X2[] PROGMEM = "X2", str_Y2[] PROGMEM = "Y2",
|
||||
str_Z2[] PROGMEM = "Z2", str_Z3[] PROGMEM = "Z3", str_Z4[] PROGMEM = "Z4",
|
||||
LIST_N(EXTRUDERS,
|
||||
str_E0[] PROGMEM = "E0", str_E1[] PROGMEM = "E1",
|
||||
str_E2[] PROGMEM = "E2", str_E3[] PROGMEM = "E3",
|
||||
str_E4[] PROGMEM = "E4", str_E5[] PROGMEM = "E5",
|
||||
str_E6[] PROGMEM = "E6", str_E7[] PROGMEM = "E7"
|
||||
)
|
||||
;
|
||||
|
||||
#define _EN_ITEM(N) , str_E##N
|
||||
PGM_P const L64XX_Marlin::index_to_axis[] PROGMEM = {
|
||||
LINEAR_AXIS_LIST(str_X, str_Y, str_Z, str_I, str_J, str_K),
|
||||
str_X2, str_Y2, str_Z2, str_Z3, str_Z4
|
||||
REPEAT(E_STEPPERS, _EN_ITEM)
|
||||
};
|
||||
#undef _EN_ITEM
|
||||
|
||||
#define DEBUG_OUT ENABLED(L6470_CHITCHAT)
|
||||
#include "../../core/debug_out.h"
|
||||
|
||||
void echo_yes_no(const bool yes) { DEBUG_ECHOPGM_P(yes ? PSTR(" YES") : PSTR(" NO ")); UNUSED(yes); }
|
||||
|
||||
uint8_t L64XX_Marlin::dir_commands[MAX_L64XX]; // array to hold direction command for each driver
|
||||
|
||||
#define _EN_ITEM(N) , INVERT_E##N##_DIR
|
||||
const uint8_t L64XX_Marlin::index_to_dir[MAX_L64XX] = {
|
||||
LINEAR_AXIS_LIST(INVERT_X_DIR, INVERT_Y_DIR, INVERT_Z_DIR, INVERT_I_DIR, INVERT_J_DIR, INVERT_K_DIR)
|
||||
, (INVERT_X_DIR) ^ BOTH(X_DUAL_STEPPER_DRIVERS, INVERT_X2_VS_X_DIR) // X2
|
||||
, (INVERT_Y_DIR) ^ BOTH(Y_DUAL_STEPPER_DRIVERS, INVERT_Y2_VS_Y_DIR) // Y2
|
||||
, (INVERT_Z_DIR) ^ ENABLED(INVERT_Z2_VS_Z_DIR) // Z2
|
||||
, (INVERT_Z_DIR) ^ ENABLED(INVERT_Z3_VS_Z_DIR) // Z3
|
||||
, (INVERT_Z_DIR) ^ ENABLED(INVERT_Z4_VS_Z_DIR) // Z4
|
||||
REPEAT(E_STEPPERS, _EN_ITEM)
|
||||
};
|
||||
#undef _EN_ITEM
|
||||
|
||||
volatile uint8_t L64XX_Marlin::spi_abort = false;
|
||||
uint8_t L64XX_Marlin::spi_active = false;
|
||||
|
||||
L64XX_Marlin::L64XX_shadow_t L64XX_Marlin::shadow;
|
||||
|
||||
//uint32_t UVLO_ADC = 0x0400; // ADC undervoltage event
|
||||
|
||||
void L6470_populate_chain_array() {
|
||||
|
||||
#define _L6470_INIT_SPI(Q) do{ stepper##Q.set_chain_info(Q, Q##_CHAIN_POS); }while(0)
|
||||
|
||||
#if AXIS_IS_L64XX(X)
|
||||
_L6470_INIT_SPI(X);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(X2)
|
||||
_L6470_INIT_SPI(X2);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Y)
|
||||
_L6470_INIT_SPI(Y);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Y2)
|
||||
_L6470_INIT_SPI(Y2);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Z)
|
||||
_L6470_INIT_SPI(Z);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Z2)
|
||||
_L6470_INIT_SPI(Z2);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Z3)
|
||||
_L6470_INIT_SPI(Z3);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Z4)
|
||||
_L6470_INIT_SPI(Z4);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E0)
|
||||
_L6470_INIT_SPI(E0);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E1)
|
||||
_L6470_INIT_SPI(E1);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E2)
|
||||
_L6470_INIT_SPI(E2);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E3)
|
||||
_L6470_INIT_SPI(E3);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E4)
|
||||
_L6470_INIT_SPI(E4);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E5)
|
||||
_L6470_INIT_SPI(E5);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E6)
|
||||
_L6470_INIT_SPI(E6);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E7)
|
||||
_L6470_INIT_SPI(E7);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Some status bit positions & definitions differ per driver.
|
||||
* Copy info to known locations to simplfy check/display logic.
|
||||
* 1. Copy stepper status
|
||||
* 2. Copy status bit definitions
|
||||
* 3. Copy status layout
|
||||
* 4. Make all error bits active low (as needed)
|
||||
*/
|
||||
uint16_t L64XX_Marlin::get_stepper_status(L64XX &st) {
|
||||
shadow.STATUS_AXIS_RAW = st.getStatus();
|
||||
shadow.STATUS_AXIS = shadow.STATUS_AXIS_RAW;
|
||||
shadow.STATUS_AXIS_LAYOUT = st.L6470_status_layout;
|
||||
shadow.AXIS_OCD_TH_MAX = st.OCD_TH_MAX;
|
||||
shadow.AXIS_STALL_TH_MAX = st.STALL_TH_MAX;
|
||||
shadow.AXIS_OCD_CURRENT_CONSTANT_INV = st.OCD_CURRENT_CONSTANT_INV;
|
||||
shadow.AXIS_STALL_CURRENT_CONSTANT_INV = st.STALL_CURRENT_CONSTANT_INV;
|
||||
shadow.L6470_AXIS_CONFIG = st.L64XX_CONFIG;
|
||||
shadow.L6470_AXIS_STATUS = st.L64XX_STATUS;
|
||||
shadow.STATUS_AXIS_OCD = st.STATUS_OCD;
|
||||
shadow.STATUS_AXIS_SCK_MOD = st.STATUS_SCK_MOD;
|
||||
shadow.STATUS_AXIS_STEP_LOSS_A = st.STATUS_STEP_LOSS_A;
|
||||
shadow.STATUS_AXIS_STEP_LOSS_B = st.STATUS_STEP_LOSS_B;
|
||||
shadow.STATUS_AXIS_TH_SD = st.STATUS_TH_SD;
|
||||
shadow.STATUS_AXIS_TH_WRN = st.STATUS_TH_WRN;
|
||||
shadow.STATUS_AXIS_UVLO = st.STATUS_UVLO;
|
||||
shadow.STATUS_AXIS_WRONG_CMD = st.STATUS_WRONG_CMD;
|
||||
shadow.STATUS_AXIS_CMD_ERR = st.STATUS_CMD_ERR;
|
||||
shadow.STATUS_AXIS_NOTPERF_CMD = st.STATUS_NOTPERF_CMD;
|
||||
|
||||
switch (shadow.STATUS_AXIS_LAYOUT) {
|
||||
case L6470_STATUS_LAYOUT: { // L6470
|
||||
shadow.L6470_ERROR_MASK = shadow.STATUS_AXIS_UVLO | shadow.STATUS_AXIS_TH_WRN | shadow.STATUS_AXIS_TH_SD | shadow.STATUS_AXIS_OCD | shadow.STATUS_AXIS_STEP_LOSS_A | shadow.STATUS_AXIS_STEP_LOSS_B;
|
||||
shadow.STATUS_AXIS ^= (shadow.STATUS_AXIS_WRONG_CMD | shadow.STATUS_AXIS_NOTPERF_CMD); // invert just error bits that are active high
|
||||
break;
|
||||
}
|
||||
case L6474_STATUS_LAYOUT: { // L6474
|
||||
shadow.L6470_ERROR_MASK = shadow.STATUS_AXIS_UVLO | shadow.STATUS_AXIS_TH_WRN | shadow.STATUS_AXIS_TH_SD | shadow.STATUS_AXIS_OCD ;
|
||||
shadow.STATUS_AXIS ^= (shadow.STATUS_AXIS_WRONG_CMD | shadow.STATUS_AXIS_NOTPERF_CMD); // invert just error bits that are active high
|
||||
break;
|
||||
}
|
||||
case L6480_STATUS_LAYOUT: { // L6480 & powerSTEP01
|
||||
shadow.L6470_ERROR_MASK = shadow.STATUS_AXIS_UVLO | shadow.STATUS_AXIS_TH_WRN | shadow.STATUS_AXIS_TH_SD | shadow.STATUS_AXIS_OCD | shadow.STATUS_AXIS_STEP_LOSS_A | shadow.STATUS_AXIS_STEP_LOSS_B;
|
||||
shadow.STATUS_AXIS ^= (shadow.STATUS_AXIS_CMD_ERR | shadow.STATUS_AXIS_TH_WRN | shadow.STATUS_AXIS_TH_SD); // invert just error bits that are active high
|
||||
break;
|
||||
}
|
||||
}
|
||||
return shadow.STATUS_AXIS;
|
||||
}
|
||||
|
||||
|
||||
void L64XX_Marlin::init() { // Set up SPI and then init chips
|
||||
ENABLE_RESET_L64XX_CHIPS(LOW); // hardware reset of drivers
|
||||
DELAY_US(100);
|
||||
ENABLE_RESET_L64XX_CHIPS(HIGH);
|
||||
DELAY_US(1000); // need about 650µs for the chip(s) to fully start up
|
||||
L6470_populate_chain_array(); // Set up array to control where in the SPI transfer sequence a particular stepper's data goes
|
||||
|
||||
spi_init(); // Since L64XX SPI pins are unset we must init SPI here
|
||||
|
||||
init_to_defaults(); // init the chips
|
||||
}
|
||||
|
||||
uint16_t L64XX_Marlin::get_status(const L64XX_axis_t axis) {
|
||||
|
||||
#define STATUS_L6470(Q) get_stepper_status(stepper##Q)
|
||||
|
||||
switch (axis) {
|
||||
default: break;
|
||||
#if AXIS_IS_L64XX(X)
|
||||
case X : return STATUS_L6470(X);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Y)
|
||||
case Y : return STATUS_L6470(Y);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Z)
|
||||
case Z : return STATUS_L6470(Z);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(X2)
|
||||
case X2: return STATUS_L6470(X2);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Y2)
|
||||
case Y2: return STATUS_L6470(Y2);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Z2)
|
||||
case Z2: return STATUS_L6470(Z2);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Z3)
|
||||
case Z3: return STATUS_L6470(Z3);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Z4)
|
||||
case Z4: return STATUS_L6470(Z4);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E0)
|
||||
case E0: return STATUS_L6470(E0);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E1)
|
||||
case E1: return STATUS_L6470(E1);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E2)
|
||||
case E2: return STATUS_L6470(E2);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E3)
|
||||
case E3: return STATUS_L6470(E3);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E4)
|
||||
case E4: return STATUS_L6470(E4);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E5)
|
||||
case E5: return STATUS_L6470(E5);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E6)
|
||||
case E6: return STATUS_L6470(E6);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E7)
|
||||
case E7: return STATUS_L6470(E7);
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0; // Not needed but kills a compiler warning
|
||||
}
|
||||
|
||||
uint32_t L64XX_Marlin::get_param(const L64XX_axis_t axis, const uint8_t param) {
|
||||
|
||||
#define GET_L6470_PARAM(Q) L6470_GETPARAM(param, Q)
|
||||
|
||||
switch (axis) {
|
||||
default: break;
|
||||
#if AXIS_IS_L64XX(X)
|
||||
case X : return GET_L6470_PARAM(X);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Y)
|
||||
case Y : return GET_L6470_PARAM(Y);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Z)
|
||||
case Z : return GET_L6470_PARAM(Z);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(X2)
|
||||
case X2: return GET_L6470_PARAM(X2);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Y2)
|
||||
case Y2: return GET_L6470_PARAM(Y2);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Z2)
|
||||
case Z2: return GET_L6470_PARAM(Z2);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Z3)
|
||||
case Z3: return GET_L6470_PARAM(Z3);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Z4)
|
||||
case Z4: return GET_L6470_PARAM(Z4);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E0)
|
||||
case E0: return GET_L6470_PARAM(E0);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E1)
|
||||
case E1: return GET_L6470_PARAM(E1);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E2)
|
||||
case E2: return GET_L6470_PARAM(E2);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E3)
|
||||
case E3: return GET_L6470_PARAM(E3);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E4)
|
||||
case E4: return GET_L6470_PARAM(E4);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E5)
|
||||
case E5: return GET_L6470_PARAM(E5);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E6)
|
||||
case E6: return GET_L6470_PARAM(E6);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E7)
|
||||
case E7: return GET_L6470_PARAM(E7);
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0; // not needed but kills a compiler warning
|
||||
}
|
||||
|
||||
void L64XX_Marlin::set_param(const L64XX_axis_t axis, const uint8_t param, const uint32_t value) {
|
||||
|
||||
#define SET_L6470_PARAM(Q) stepper##Q.SetParam(param, value)
|
||||
|
||||
switch (axis) {
|
||||
default: break;
|
||||
#if AXIS_IS_L64XX(X)
|
||||
case X : SET_L6470_PARAM(X); break;
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Y)
|
||||
case Y : SET_L6470_PARAM(Y); break;
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Z)
|
||||
case Z : SET_L6470_PARAM(Z); break;
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(I)
|
||||
case I : SET_L6470_PARAM(I); break;
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(J)
|
||||
case J : SET_L6470_PARAM(J); break;
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(K)
|
||||
case K : SET_L6470_PARAM(K); break;
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(X2)
|
||||
case X2: SET_L6470_PARAM(X2); break;
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Y2)
|
||||
case Y2: SET_L6470_PARAM(Y2); break;
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Z2)
|
||||
case Z2: SET_L6470_PARAM(Z2); break;
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Z3)
|
||||
case Z3: SET_L6470_PARAM(Z3); break;
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Z4)
|
||||
case Z4: SET_L6470_PARAM(Z4); break;
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E0)
|
||||
case E0: SET_L6470_PARAM(E0); break;
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E1)
|
||||
case E1: SET_L6470_PARAM(E1); break;
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E2)
|
||||
case E2: SET_L6470_PARAM(E2); break;
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E3)
|
||||
case E3: SET_L6470_PARAM(E3); break;
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E4)
|
||||
case E4: SET_L6470_PARAM(E4); break;
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E5)
|
||||
case E5: SET_L6470_PARAM(E5); break;
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E6)
|
||||
case E6: SET_L6470_PARAM(E6); break;
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E7)
|
||||
case E7: SET_L6470_PARAM(E7); break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
inline void echo_min_max(const char a, const_float_t min, const_float_t max) {
|
||||
DEBUG_CHAR(' '); DEBUG_CHAR(a);
|
||||
DEBUG_ECHOLNPGM(" min = ", min, " max = ", max);
|
||||
}
|
||||
inline void echo_oct_used(const_float_t oct, const uint8_t stall) {
|
||||
DEBUG_ECHOPGM("over_current_threshold used : ", oct);
|
||||
DEBUG_ECHOPGM_P(stall ? PSTR(" (Stall") : PSTR(" (OCD"));
|
||||
DEBUG_ECHOLNPGM(" threshold)");
|
||||
}
|
||||
inline void err_out_of_bounds() { DEBUG_ECHOLNPGM("Test aborted - motion out of bounds"); }
|
||||
|
||||
uint8_t L64XX_Marlin::get_user_input(uint8_t &driver_count, L64XX_axis_t axis_index[3], char axis_mon[3][3],
|
||||
float &position_max, float &position_min, float &final_feedrate, uint8_t &kval_hold,
|
||||
uint8_t 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()) {
|
||||
DEBUG_ECHOLNPGM("Test aborted - home all before running this command");
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t found_displacement = false;
|
||||
LOOP_LOGICAL_AXES(i) if (uint16_t _displacement = parser.intval(axis_codes[i])) {
|
||||
found_displacement = true;
|
||||
displacement = _displacement;
|
||||
uint8_t axis_offset = parser.byteval('J');
|
||||
axis_mon[0][0] = axis_codes[i]; // Axis first character, one of XYZE
|
||||
const bool single_or_e = axis_offset >= 2 || axis_mon[0][0] == 'E',
|
||||
one_or_more = !single_or_e && axis_offset == 0;
|
||||
uint8_t driver_count_local = 0; // Can't use "driver_count" directly as a subscript because it's passed by reference
|
||||
if (single_or_e) // Single axis, E0, or E1
|
||||
axis_mon[0][1] = axis_offset + '0'; // Index given by 'J' parameter
|
||||
|
||||
if (single_or_e || one_or_more) {
|
||||
for (j = 0; j < MAX_L64XX; j++) { // Count up the drivers on this axis
|
||||
PGM_P str = (PGM_P)pgm_read_ptr(&index_to_axis[j]); // Get a PGM_P from progmem
|
||||
const char c = pgm_read_byte(str); // Get a char from progmem
|
||||
if (axis_mon[0][0] == c) { // For each stepper on this axis...
|
||||
char *mon = axis_mon[driver_count_local];
|
||||
*mon++ = c; // Copy the 3 letter axis name
|
||||
*mon++ = pgm_read_byte(&str[1]); // to the axis_mon array
|
||||
*mon = pgm_read_byte(&str[2]);
|
||||
axis_index[driver_count_local] = (L64XX_axis_t)j; // And store the L64XX axis index
|
||||
driver_count_local++;
|
||||
}
|
||||
}
|
||||
if (one_or_more) driver_count = driver_count_local;
|
||||
}
|
||||
break; // only take first axis found
|
||||
}
|
||||
|
||||
if (!found_displacement) {
|
||||
DEBUG_ECHOLNPGM("Test aborted - AXIS with displacement is required");
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Position calcs & checks
|
||||
//
|
||||
|
||||
const float LOGICAL_AXIS_LIST(
|
||||
E_center = current_position.e,
|
||||
X_center = LOGICAL_X_POSITION(current_position.x),
|
||||
Y_center = LOGICAL_Y_POSITION(current_position.y),
|
||||
Z_center = LOGICAL_Z_POSITION(current_position.z),
|
||||
I_center = LOGICAL_I_POSITION(current_position.i),
|
||||
J_center = LOGICAL_J_POSITION(current_position.j),
|
||||
K_center = LOGICAL_K_POSITION(current_position.k)
|
||||
);
|
||||
|
||||
switch (axis_mon[0][0]) {
|
||||
default: position_max = position_min = 0; break;
|
||||
|
||||
case 'X': {
|
||||
position_min = X_center - displacement;
|
||||
position_max = X_center + displacement;
|
||||
echo_min_max('X', position_min, position_max);
|
||||
if (TERN0(HAS_ENDSTOPS, position_min < (X_MIN_POS) || position_max > (X_MAX_POS))) {
|
||||
err_out_of_bounds();
|
||||
return true;
|
||||
}
|
||||
} break;
|
||||
|
||||
#if HAS_Y_AXIS
|
||||
case 'Y': {
|
||||
position_min = Y_center - displacement;
|
||||
position_max = Y_center + displacement;
|
||||
echo_min_max('Y', position_min, position_max);
|
||||
if (TERN0(HAS_ENDSTOPS, position_min < (Y_MIN_POS) || position_max > (Y_MAX_POS))) {
|
||||
err_out_of_bounds();
|
||||
return true;
|
||||
}
|
||||
} break;
|
||||
#endif
|
||||
|
||||
#if HAS_Z_AXIS
|
||||
case 'Z': {
|
||||
position_min = Z_center - displacement;
|
||||
position_max = Z_center + displacement;
|
||||
echo_min_max('Z', position_min, position_max);
|
||||
if (TERN0(HAS_ENDSTOPS, position_min < (Z_MIN_POS) || position_max > (Z_MAX_POS))) {
|
||||
err_out_of_bounds();
|
||||
return true;
|
||||
}
|
||||
} break;
|
||||
#endif
|
||||
|
||||
#if LINEAR_AXES >= 4
|
||||
case AXIS4_NAME: {
|
||||
position_min = I_center - displacement;
|
||||
position_max = I_center + displacement;
|
||||
echo_min_max(AXIS4_NAME, position_min, position_max);
|
||||
if (TERN0(HAS_ENDSTOPS, position_min < (I_MIN_POS) || position_max > (I_MAX_POS))) {
|
||||
err_out_of_bounds();
|
||||
return true;
|
||||
}
|
||||
} break;
|
||||
#endif
|
||||
|
||||
#if LINEAR_AXES >= 5
|
||||
case AXIS5_NAME: {
|
||||
position_min = J_center - displacement;
|
||||
position_max = J_center + displacement;
|
||||
echo_min_max(AXIS5_NAME, position_min, position_max);
|
||||
if (TERN1(HAS_ENDSTOPS, position_min < (J_MIN_POS) || position_max > (J_MAX_POS))) {
|
||||
err_out_of_bounds();
|
||||
return true;
|
||||
}
|
||||
} break;
|
||||
#endif
|
||||
|
||||
#if LINEAR_AXES >= 6
|
||||
case AXIS6_NAME: {
|
||||
position_min = K_center - displacement;
|
||||
position_max = K_center + displacement;
|
||||
echo_min_max(AXIS6_NAME, position_min, position_max);
|
||||
if (TERN2(HAS_ENDSTOPS, position_min < (K_MIN_POS) || position_max > (K_MAX_POS))) {
|
||||
err_out_of_bounds();
|
||||
return true;
|
||||
}
|
||||
} break;
|
||||
#endif
|
||||
|
||||
#if HAS_EXTRUDERS
|
||||
case 'E': {
|
||||
position_min = E_center - displacement;
|
||||
position_max = E_center + displacement;
|
||||
echo_min_max('E', position_min, position_max);
|
||||
} break;
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// Work on the drivers
|
||||
//
|
||||
|
||||
LOOP_L_N(k, driver_count) {
|
||||
uint8_t not_found = true;
|
||||
for (j = 1; j <= L64XX::chain[0]; j++) {
|
||||
PGM_P const str = (PGM_P)pgm_read_ptr(&index_to_axis[L64XX::chain[j]]);
|
||||
if (pgm_read_byte(&str[0]) == axis_mon[k][0] && pgm_read_byte(&str[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) {
|
||||
DEBUG_ECHOLNPGM("Test aborted - not a L6470 axis");
|
||||
return true;
|
||||
}
|
||||
|
||||
DEBUG_ECHOPGM("Monitoring:");
|
||||
for (j = 0; j < driver_count; j++) DEBUG_ECHOPGM(" ", axis_mon[j]);
|
||||
DEBUG_EOL();
|
||||
|
||||
// now have a list of driver(s) to monitor
|
||||
|
||||
//
|
||||
// TVAL & kVAL_HOLD checks & settings
|
||||
//
|
||||
const L64XX_shadow_t &sh = shadow;
|
||||
get_status(axis_index[0]); // populate shadow array
|
||||
|
||||
if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT) { // L6474 - use TVAL
|
||||
uint16_t TVAL_current = parser.ushortval('T');
|
||||
if (TVAL_current) {
|
||||
uint8_t TVAL_count = (TVAL_current / sh.AXIS_STALL_CURRENT_CONSTANT_INV) - 1;
|
||||
LIMIT(TVAL_count, 0, sh.AXIS_STALL_TH_MAX);
|
||||
for (j = 0; j < driver_count; j++)
|
||||
set_param(axis_index[j], L6474_TVAL, TVAL_count);
|
||||
}
|
||||
// only print the tval from one of the drivers
|
||||
kval_hold = get_param(axis_index[0], L6474_TVAL);
|
||||
DEBUG_ECHOLNPGM("TVAL current (mA) = ", (kval_hold + 1) * sh.AXIS_STALL_CURRENT_CONSTANT_INV);
|
||||
}
|
||||
else {
|
||||
kval_hold = parser.byteval('K');
|
||||
if (kval_hold) {
|
||||
DEBUG_ECHOLNPGM("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);
|
||||
DEBUG_ECHOLNPGM("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;
|
||||
}
|
||||
|
||||
DEBUG_ECHOLNPGM("over_current_threshold specified: ", over_current_threshold);
|
||||
if (!(sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT)) echo_oct_used((STALL_TH_val_local + 1) * 31.25, true);
|
||||
echo_oct_used((OCD_TH_val_local + 1) * 375, 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);
|
||||
|
||||
if (!(sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT)) 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
|
||||
}
|
||||
|
||||
void L64XX_Marlin::say_axis(const L64XX_axis_t axis, const uint8_t label/*=true*/) {
|
||||
if (label) SERIAL_ECHOPGM("AXIS:");
|
||||
const char * const str = L64xxManager.index_to_axis[axis];
|
||||
SERIAL_CHAR(' ', str[0], str[1], ' ');
|
||||
}
|
||||
|
||||
#if ENABLED(L6470_CHITCHAT)
|
||||
|
||||
// Assumes status bits have been inverted
|
||||
void L64XX_Marlin::error_status_decode(const uint16_t status, const L64XX_axis_t axis,
|
||||
const uint16_t _status_axis_th_sd, const uint16_t _status_axis_th_wrn,
|
||||
const uint16_t _status_axis_step_loss_a, const uint16_t _status_axis_step_loss_b,
|
||||
const uint16_t _status_axis_ocd, const uint8_t _status_axis_layout
|
||||
) {
|
||||
say_axis(axis);
|
||||
DEBUG_ECHOPGM(" THERMAL: ");
|
||||
DEBUG_ECHOPGM_P((status & _status_axis_th_sd) ? PSTR("SHUTDOWN") : (status & _status_axis_th_wrn) ? PSTR("WARNING ") : PSTR("OK "));
|
||||
DEBUG_ECHOPGM(" OVERCURRENT: ");
|
||||
echo_yes_no((status & _status_axis_ocd) != 0);
|
||||
if (!(_status_axis_layout == L6474_STATUS_LAYOUT)) { // L6474 doesn't have these bits
|
||||
DEBUG_ECHOPGM(" STALL: ");
|
||||
echo_yes_no((status & (_status_axis_step_loss_a | _status_axis_step_loss_b)) != 0);
|
||||
}
|
||||
DEBUG_EOL();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////
|
||||
//// MONITOR_L6470_DRIVER_STATUS routines
|
||||
////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if ENABLED(MONITOR_L6470_DRIVER_STATUS)
|
||||
|
||||
bool L64XX_Marlin::monitor_paused = false; // Flag to skip monitor during M122, M906, M916, M917, M918, etc.
|
||||
|
||||
struct L6470_driver_data {
|
||||
L64XX_axis_t driver_index;
|
||||
uint32_t driver_status;
|
||||
uint8_t is_otw;
|
||||
uint8_t otw_counter;
|
||||
uint8_t is_ot;
|
||||
uint8_t is_hi_Z;
|
||||
uint8_t com_counter;
|
||||
};
|
||||
|
||||
L6470_driver_data driver_L6470_data[] = {
|
||||
#if AXIS_IS_L64XX(X)
|
||||
{ X, 0, 0, 0, 0, 0, 0 },
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Y)
|
||||
{ Y, 0, 0, 0, 0, 0, 0 },
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Z)
|
||||
{ Z, 0, 0, 0, 0, 0, 0 },
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(I)
|
||||
{ I, 0, 0, 0, 0, 0, 0 },
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(J)
|
||||
{ J, 0, 0, 0, 0, 0, 0 },
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(K)
|
||||
{ K, 0, 0, 0, 0, 0, 0 },
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(X2)
|
||||
{ X2, 0, 0, 0, 0, 0, 0 },
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Y2)
|
||||
{ Y2, 0, 0, 0, 0, 0, 0 },
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Z2)
|
||||
{ Z2, 0, 0, 0, 0, 0, 0 },
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Z3)
|
||||
{ Z3, 0, 0, 0, 0, 0, 0 },
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Z4)
|
||||
{ Z4, 0, 0, 0, 0, 0, 0 },
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E0)
|
||||
{ E0, 0, 0, 0, 0, 0, 0 },
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E1)
|
||||
{ E1, 0, 0, 0, 0, 0, 0 },
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E2)
|
||||
{ E2, 0, 0, 0, 0, 0, 0 },
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E3)
|
||||
{ E3, 0, 0, 0, 0, 0, 0 },
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E4)
|
||||
{ E4, 0, 0, 0, 0, 0, 0 },
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E5)
|
||||
{ E5, 0, 0, 0, 0, 0, 0 }
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E6)
|
||||
{ E6, 0, 0, 0, 0, 0, 0 }
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E7)
|
||||
{ E7, 0, 0, 0, 0, 0, 0 }
|
||||
#endif
|
||||
};
|
||||
|
||||
void L64XX_Marlin::append_stepper_err(char* &p, const uint8_t stepper_index, const char * const err/*=nullptr*/) {
|
||||
PGM_P const str = (PGM_P)pgm_read_ptr(&index_to_axis[stepper_index]);
|
||||
p += sprintf_P(p, PSTR("Stepper %c%c "), pgm_read_byte(&str[0]), pgm_read_byte(&str[1]));
|
||||
if (err) p += sprintf_P(p, err);
|
||||
}
|
||||
|
||||
void L64XX_Marlin::monitor_update(L64XX_axis_t stepper_index) {
|
||||
if (spi_abort) return; // don't do anything if set_directions() has occurred
|
||||
const L64XX_shadow_t &sh = shadow;
|
||||
get_status(stepper_index); // get stepper status and details
|
||||
uint16_t status = sh.STATUS_AXIS;
|
||||
uint8_t kval_hold, tval;
|
||||
char temp_buf[120], *p = temp_buf;
|
||||
uint8_t j;
|
||||
for (j = 0; j < L64XX::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"));
|
||||
DEBUG_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"));
|
||||
DEBUG_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"));
|
||||
DEBUG_ECHO(temp_buf);
|
||||
init_to_defaults();
|
||||
}
|
||||
else {
|
||||
// no com problems - do the usual checks
|
||||
if (_status & sh.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 & sh.STATUS_AXIS_TH_SD) { // strange - TH_SD never seems to go active, must be implied by the HiZ and TH_WRN
|
||||
if (_status & sh.STATUS_AXIS_TH_WRN) { // over current shutdown
|
||||
p += sprintf_P(p, PSTR("%cdue to over temperature"), ' ');
|
||||
driver_L6470_data[j].is_ot = true;
|
||||
if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT) { // L6474
|
||||
tval = get_param(stepper_index, L6474_TVAL) - 2 * KVAL_HOLD_STEP_DOWN;
|
||||
set_param(stepper_index, L6474_TVAL, tval); // reduce TVAL
|
||||
p += sprintf_P(p, PSTR(" - TVAL reduced by %d to %d mA"), uint16_t (2 * KVAL_HOLD_STEP_DOWN * sh.AXIS_STALL_CURRENT_CONSTANT_INV), uint16_t ((tval + 1) * sh.AXIS_STALL_CURRENT_CONSTANT_INV)); // let user know
|
||||
}
|
||||
else {
|
||||
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 & sh.STATUS_AXIS_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
|
||||
driver_L6470_data[j].otw_counter = 0;
|
||||
driver_L6470_data[j].is_otw = true;
|
||||
if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT) { // L6474
|
||||
tval = get_param(stepper_index, L6474_TVAL) - KVAL_HOLD_STEP_DOWN;
|
||||
set_param(stepper_index, L6474_TVAL, tval); // reduce TVAL
|
||||
p += sprintf_P(p, PSTR(" - TVAL reduced by %d to %d mA"), uint16_t (KVAL_HOLD_STEP_DOWN * sh.AXIS_STALL_CURRENT_CONSTANT_INV), uint16_t ((tval + 1) * sh.AXIS_STALL_CURRENT_CONSTANT_INV)); // let user know
|
||||
}
|
||||
else {
|
||||
kval_hold = get_param(stepper_index, L6470_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
|
||||
}
|
||||
}
|
||||
else if (driver_L6470_data[j].otw_counter)
|
||||
p += sprintf_P(p, PSTR("%c- thermal warning"), ' '); // warn user
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLED(L6470_STOP_ON_ERROR)
|
||||
if (_status & (sh.STATUS_AXIS_UVLO | sh.STATUS_AXIS_TH_WRN | sh.STATUS_AXIS_TH_SD))
|
||||
kill(temp_buf);
|
||||
#endif
|
||||
|
||||
#if ENABLED(L6470_CHITCHAT)
|
||||
if (_status & sh.STATUS_AXIS_OCD)
|
||||
p += sprintf_P(p, PSTR("%c over current"), ' ');
|
||||
|
||||
if (_status & (sh.STATUS_AXIS_STEP_LOSS_A | sh.STATUS_AXIS_STEP_LOSS_B))
|
||||
p += sprintf_P(p, PSTR("%c stall"), ' ');
|
||||
|
||||
if (_status & sh.STATUS_AXIS_UVLO)
|
||||
p += sprintf_P(p, PSTR("%c under voltage lock out"), ' ');
|
||||
|
||||
p += sprintf_P(p, PSTR("%c\n"), ' ');
|
||||
#endif
|
||||
|
||||
DEBUG_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 monitor_update()
|
||||
|
||||
|
||||
void L64XX_Marlin::monitor_driver() {
|
||||
static millis_t next_cOT = 0;
|
||||
if (ELAPSED(millis(), next_cOT)) {
|
||||
next_cOT = millis() + 500;
|
||||
|
||||
if (!monitor_paused) { // Skip during M122, M906, M916, M917 or M918 (could steal status result from test)
|
||||
|
||||
spi_active = true; // Tell set_directions() a series of SPI transfers is underway
|
||||
|
||||
#if AXIS_IS_L64XX(X)
|
||||
monitor_update(X);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Y)
|
||||
monitor_update(Y);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Z)
|
||||
monitor_update(Z);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(I)
|
||||
monitor_update(I);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(J)
|
||||
monitor_update(J);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(K)
|
||||
monitor_update(K);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(X2)
|
||||
monitor_update(X2);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Y2)
|
||||
monitor_update(Y2);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Z2)
|
||||
monitor_update(Z2);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Z3)
|
||||
monitor_update(Z3);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(Z4)
|
||||
monitor_update(Z4);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E0)
|
||||
monitor_update(E0);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E1)
|
||||
monitor_update(E1);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E2)
|
||||
monitor_update(E2);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E3)
|
||||
monitor_update(E3);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E4)
|
||||
monitor_update(E4);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E5)
|
||||
monitor_update(E5);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E6)
|
||||
monitor_update(E6);
|
||||
#endif
|
||||
#if AXIS_IS_L64XX(E7)
|
||||
monitor_update(E7);
|
||||
#endif
|
||||
|
||||
if (TERN0(L6470_DEBUG, report_L6470_status)) DEBUG_EOL();
|
||||
|
||||
spi_active = false; // done with all SPI transfers - clear handshake flags
|
||||
spi_abort = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // MONITOR_L6470_DRIVER_STATUS
|
||||
|
||||
#endif // HAS_L64XX
|
@@ -1,141 +0,0 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#include <L6470.h>
|
||||
#if !(L6470_LIBRARY_VERSION >= 0x000800)
|
||||
#error 'L6470_LIBRARY_VERSION 0x000800 or later required'
|
||||
#endif
|
||||
|
||||
#define L6470_GETPARAM(P,Q) stepper##Q.GetParam(P)
|
||||
|
||||
#define dSPIN_STEP_CLOCK 0x58
|
||||
#define dSPIN_STEP_CLOCK_FWD dSPIN_STEP_CLOCK
|
||||
#define dSPIN_STEP_CLOCK_REV dSPIN_STEP_CLOCK+1
|
||||
#define HAS_L64XX_EXTRUDER (AXIS_IS_L64XX(E0) || AXIS_IS_L64XX(E1) || AXIS_IS_L64XX(E2) || AXIS_IS_L64XX(E3) || AXIS_IS_L64XX(E4) || AXIS_IS_L64XX(E5) || AXIS_IS_L64XX(E6) || AXIS_IS_L64XX(E7))
|
||||
|
||||
#define _EN_ITEM(N) , E##N
|
||||
enum L64XX_axis_t : uint8_t { LINEAR_AXIS_LIST(X, Y, Z, I, J, K), X2, Y2, Z2, Z3, Z4 REPEAT(E_STEPPERS, _EN_ITEM), MAX_L64XX };
|
||||
#undef _EN_ITEM
|
||||
|
||||
class L64XX_Marlin : public L64XXHelper {
|
||||
public:
|
||||
static PGM_P const index_to_axis[MAX_L64XX];
|
||||
|
||||
static const uint8_t index_to_dir[MAX_L64XX];
|
||||
|
||||
static uint8_t dir_commands[MAX_L64XX];
|
||||
|
||||
// Flags to guarantee graceful switch if stepper interrupts L6470 SPI transfer
|
||||
static volatile uint8_t spi_abort;
|
||||
static uint8_t spi_active;
|
||||
|
||||
L64XX_Marlin() {}
|
||||
|
||||
static void init();
|
||||
static void init_to_defaults();
|
||||
|
||||
static uint16_t get_stepper_status(L64XX &st);
|
||||
|
||||
static uint16_t get_status(const L64XX_axis_t axis);
|
||||
|
||||
static uint32_t get_param(const L64XX_axis_t axis, const uint8_t param);
|
||||
|
||||
static void set_param(const L64XX_axis_t axis, const uint8_t param, const uint32_t value);
|
||||
|
||||
//static void send_command(const L64XX_axis_t axis, uint8_t command);
|
||||
|
||||
static uint8_t get_user_input(uint8_t &driver_count, L64XX_axis_t axis_index[3], char axis_mon[3][3],
|
||||
float &position_max, float &position_min, float &final_feedrate, uint8_t &kval_hold,
|
||||
uint8_t over_current_flag, uint8_t &OCD_TH_val, uint8_t &STALL_TH_val, uint16_t &over_current_threshold);
|
||||
|
||||
static void transfer(uint8_t L6470_buf[], const uint8_t length);
|
||||
|
||||
static void say_axis(const L64XX_axis_t axis, const uint8_t label=true);
|
||||
#if ENABLED(L6470_CHITCHAT)
|
||||
static void error_status_decode(
|
||||
const uint16_t status, const L64XX_axis_t axis,
|
||||
const uint16_t _status_axis_th_sd, const uint16_t _status_axis_th_wrn,
|
||||
const uint16_t _status_axis_step_loss_a, const uint16_t _status_axis_step_loss_b,
|
||||
const uint16_t _status_axis_ocd, const uint8_t _status_axis_layout
|
||||
);
|
||||
#else
|
||||
FORCE_INLINE static void error_status_decode(
|
||||
const uint16_t, const L64XX_axis_t,
|
||||
const uint16_t, const uint16_t,
|
||||
const uint16_t, const uint16_t,
|
||||
const uint16_t, const uint8_t
|
||||
){}
|
||||
#endif
|
||||
|
||||
// ~40 bytes SRAM to simplify status decode routines
|
||||
typedef struct {
|
||||
uint8_t STATUS_AXIS_LAYOUT; // Copy of L6470_status_layout
|
||||
uint8_t AXIS_OCD_TH_MAX; // Size of OCD_TH field
|
||||
uint8_t AXIS_STALL_TH_MAX; // Size of STALL_TH field
|
||||
float AXIS_OCD_CURRENT_CONSTANT_INV; // mA per count
|
||||
float AXIS_STALL_CURRENT_CONSTANT_INV; // mA per count
|
||||
uint8_t L6470_AXIS_CONFIG, // Address of the CONFIG register
|
||||
L6470_AXIS_STATUS; // Address of the STATUS register
|
||||
uint16_t L6470_ERROR_MASK, // STATUS_UVLO | STATUS_TH_WRN | STATUS_TH_SD | STATUS_OCD | STATUS_STEP_LOSS_A | STATUS_STEP_LOSS_B
|
||||
L6474_ERROR_MASK, // STATUS_UVLO | STATUS_TH_WRN | STATUS_TH_SD | STATUS_OCD
|
||||
STATUS_AXIS_RAW, // Copy of status register contents
|
||||
STATUS_AXIS, // Copy of status register contents but with all error bits active low
|
||||
STATUS_AXIS_OCD, // Overcurrent detected bit position
|
||||
STATUS_AXIS_SCK_MOD, // Step clock mode is active bit position
|
||||
STATUS_AXIS_STEP_LOSS_A, // Stall detected on A bridge bit position
|
||||
STATUS_AXIS_STEP_LOSS_B, // Stall detected on B bridge bit position
|
||||
STATUS_AXIS_TH_SD, // Thermal shutdown bit position
|
||||
STATUS_AXIS_TH_WRN, // Thermal warning bit position
|
||||
STATUS_AXIS_UVLO, // Undervoltage lockout is active bit position
|
||||
STATUS_AXIS_WRONG_CMD, // Last command not valid bit position
|
||||
STATUS_AXIS_CMD_ERR, // Command error bit position
|
||||
STATUS_AXIS_NOTPERF_CMD; // Last command not performed bit position
|
||||
} L64XX_shadow_t;
|
||||
|
||||
static L64XX_shadow_t shadow;
|
||||
|
||||
#if ENABLED(MONITOR_L6470_DRIVER_STATUS)
|
||||
static bool monitor_paused;
|
||||
static inline void pause_monitor(const bool p) { monitor_paused = p; }
|
||||
static void monitor_update(L64XX_axis_t stepper_index);
|
||||
static void monitor_driver();
|
||||
#else
|
||||
static inline void pause_monitor(const bool) {}
|
||||
#endif
|
||||
|
||||
//protected:
|
||||
// L64XXHelper methods
|
||||
static void spi_init();
|
||||
static uint8_t transfer_single(uint8_t data, int16_t ss_pin);
|
||||
static uint8_t transfer_chain(uint8_t data, int16_t ss_pin, uint8_t chain_position);
|
||||
|
||||
private:
|
||||
static void append_stepper_err(char* &p, const uint8_t stepper_index, const char * const err=nullptr);
|
||||
|
||||
};
|
||||
|
||||
void echo_yes_no(const bool yes);
|
||||
|
||||
extern L64XX_Marlin L64xxManager;
|
@@ -1,98 +0,0 @@
|
||||
### L64XX Stepper Driver
|
||||
|
||||
*Arduino-L6470* library revision 0.8.0 or above is required.
|
||||
|
||||
This software can be used with the L6470, L6474, L6480 and the powerSTEP01 (collectively referred to as "L64xx" from now on). Different drivers can be mixed within a system.
|
||||
|
||||
These devices use voltage PWMs to drive the stepper phases. On the L6474 the phase current is controlled by the `TVAL` register. On all the other drivers the phase current is indirectly controlled via the `KVAL_HOLD` register which scales the PWM duty cycle.
|
||||
|
||||
This software assumes that all drivers are in one SPI daisy chain.
|
||||
|
||||
### Hardware Setup
|
||||
|
||||
- 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_PIN` and `SS_PIN` pins. The user must supply a macro to control the `RESET_PIN`(s).
|
||||
|
||||
- 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 L6474 uses the standard STEP DIR interface. Phase currents are changed in response to step pulses. The direction is set by the DIR pin. Instead of an ENA pin, stepper power is controlled with SPI commands.
|
||||
|
||||
The other drivers operate in `STEP_CLOCK` mode. In this mode the Direction / Enable functions are done with SPI commands and the phase currents are changed in response to STEP pulses.
|
||||
|
||||
### Hardware / Software Interaction
|
||||
|
||||
Except for the L6474, powering up a stepper and setting the direction are done by the same command. You 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.
|
||||
|
||||
When setting direction, SPI transfers are minimized by using arrays and a specialized SPI method. *Arduino-L6470* library calls are not used. For N L64xx drivers, this results in N bytes transferred. If library calls were used then N<sup>2</sup> bytes would be sent.
|
||||
|
||||
### Power-up (Reset) Sequence
|
||||
|
||||
- Stepper objects are instantiated before the `setup()` entry point is reached.
|
||||
|
||||
- In `setup()` (before stepper drivers are initialized) the `L6470_init()` method is called to do the following:
|
||||
|
||||
- If present, pulse the hardware reset pin.
|
||||
|
||||
- Populate the `L6470_chain` array, which maps positions in the SPI stream to commands/data for L64XX stepper drivers.
|
||||
|
||||
- Initialize the L64XX Software SPI pin states.
|
||||
|
||||
- Initialize L64XX drivers. They may be reset later by a call to `L6470_init_to_defaults()`.
|
||||
|
||||
The steppers are **NOT** powered up (enabled) during this sequence.
|
||||
|
||||
### `L6470_chain` array
|
||||
|
||||
This array is used by all routines that transmit SPI data. For a chain with N devices, the array contains:
|
||||
|
||||
Index|Value
|
||||
-----|-----
|
||||
0|Number of drivers in chain
|
||||
1|Axis index of the first device in the chain (closest to MOSI)
|
||||
...|
|
||||
N|Axis index of the last device chain (closest to MISO)
|
||||
|
||||
### Set Direction and Enable
|
||||
|
||||
The `DIR_WRITE` macros for the L64xx 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`. On the L6474 the array the command used just enables the stepper because direction is set by the DIR pin.
|
||||
|
||||
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, etc.
|
||||
|
||||
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 (`TVAL` on L6474) 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 (`TVAL` on L6474). In general they seem to provide an overly optimistic `KVAL_HOLD` (`TVAL`) setting because of the lag between setting `KVAL_HOLD` (`TVAL`) and the driver reaching final temperature. Enabling the `L6470_monitor` feature during prints will provide the **final useful setting**.
|
||||
|
||||
The amount of power needed to move the stepper without skipping steps increases as jerk, acceleration, top speed, and micro-steps 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, micro-steps, and power dissipated by the driver.
|
||||
|
||||
**M916** - Increases `KVAL_HOLD` (`TVAL`) while moving one axis until a thermal warning is generated. This routine is also useful for determining the approximate `KVAL_HOLD` (`TVAL`) where the stepper stops losing steps. The sound will get noticeably quieter as it stops losing steps.
|
||||
|
||||
**M917** - Find minimum current thresholds. This is accomplished 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 (not available on the L6474).
|
||||
|
||||
4. Increase stall until stall error goes away (not available on the L6474).
|
||||
|
||||
**M918** - Increase speed until error or max feedrate achieved.
|
@@ -40,31 +40,27 @@
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
// Useful for RTD debugging.
|
||||
//#define MAX31865_DEBUG
|
||||
//#define MAX31865_DEBUG_SPI
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#if HAS_MAX31865 && !USE_ADAFRUIT_MAX31865
|
||||
|
||||
//#include <SoftwareSPI.h> // TODO: switch to SPIclass/SoftSPI
|
||||
#include "MAX31865.h"
|
||||
|
||||
#ifndef MAX31865_MIN_SAMPLING_TIME_MSEC
|
||||
#define MAX31865_MIN_SAMPLING_TIME_MSEC 0
|
||||
#endif
|
||||
|
||||
#define DEBUG_OUT ENABLED(DEBUG_MAX31865)
|
||||
#include "../core/debug_out.h"
|
||||
|
||||
// The maximum speed the MAX31865 can do is 5 MHz
|
||||
SPISettings MAX31865::spiConfig = SPISettings(
|
||||
#if defined(TARGET_LPC1768)
|
||||
SPI_QUARTER_SPEED
|
||||
#elif defined(ARDUINO_ARCH_STM32)
|
||||
SPI_CLOCK_DIV4
|
||||
#else
|
||||
500000
|
||||
#endif
|
||||
, MSBFIRST
|
||||
, SPI_MODE_1 // CPOL0 CPHA1
|
||||
TERN(TARGET_LPC1768, SPI_QUARTER_SPEED, TERN(ARDUINO_ARCH_STM32, SPI_CLOCK_DIV4, 500000)),
|
||||
MSBFIRST,
|
||||
SPI_MODE1 // CPOL0 CPHA1
|
||||
);
|
||||
|
||||
#ifndef LARGE_PINMAP
|
||||
#if DISABLED(LARGE_PINMAP)
|
||||
|
||||
/**
|
||||
* Create the interface object using software (bitbang) SPI for PIN values
|
||||
@@ -76,10 +72,10 @@ SPISettings MAX31865::spiConfig = SPISettings(
|
||||
* @param spi_clk the SPI clock pin to use
|
||||
*/
|
||||
MAX31865::MAX31865(int8_t spi_cs, int8_t spi_mosi, int8_t spi_miso, int8_t spi_clk) {
|
||||
_cs = spi_cs;
|
||||
_mosi = spi_mosi;
|
||||
_miso = spi_miso;
|
||||
_sclk = spi_clk;
|
||||
cselPin = spi_cs;
|
||||
mosiPin = spi_mosi;
|
||||
misoPin = spi_miso;
|
||||
sclkPin = spi_clk;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,11 +85,11 @@ SPISettings MAX31865::spiConfig = SPISettings(
|
||||
* @param spi_cs the SPI CS pin to use along with the default SPI device
|
||||
*/
|
||||
MAX31865::MAX31865(int8_t spi_cs) {
|
||||
_cs = spi_cs;
|
||||
_sclk = _miso = _mosi = -1;
|
||||
cselPin = spi_cs;
|
||||
sclkPin = misoPin = mosiPin = -1;
|
||||
}
|
||||
|
||||
#else
|
||||
#else // LARGE_PINMAP
|
||||
|
||||
/**
|
||||
* Create the interface object using software (bitbang) SPI for PIN values
|
||||
@@ -106,13 +102,11 @@ SPISettings MAX31865::spiConfig = SPISettings(
|
||||
* @param spi_clk the SPI clock pin to use
|
||||
* @param pin_mapping set to 1 for positive pin values
|
||||
*/
|
||||
MAX31865::MAX31865(uint32_t spi_cs, uint32_t spi_mosi,
|
||||
uint32_t spi_miso, uint32_t spi_clk,
|
||||
uint8_t pin_mapping) {
|
||||
_cs = spi_cs;
|
||||
_mosi = spi_mosi;
|
||||
_miso = spi_miso;
|
||||
_sclk = spi_clk;
|
||||
MAX31865::MAX31865(uint32_t spi_cs, uint32_t spi_mosi, uint32_t spi_miso, uint32_t spi_clk, uint8_t pin_mapping) {
|
||||
cselPin = spi_cs;
|
||||
mosiPin = spi_mosi;
|
||||
misoPin = spi_miso;
|
||||
sclkPin = spi_clk;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,156 +118,247 @@ SPISettings MAX31865::spiConfig = SPISettings(
|
||||
* @param pin_mapping set to 1 for positive pin values
|
||||
*/
|
||||
MAX31865::MAX31865(uint32_t spi_cs, uint8_t pin_mapping) {
|
||||
_cs = spi_cs;
|
||||
_sclk = _miso = _mosi = -1UL; //-1UL or 0xFFFFFFFF or 4294967295
|
||||
cselPin = spi_cs;
|
||||
sclkPin = misoPin = mosiPin = -1UL; //-1UL or 0xFFFFFFFF or 4294967295
|
||||
}
|
||||
|
||||
#endif // LARGE_PINMAP
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Instance & Class methods
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the SPI interface and set the number of RTD wires used
|
||||
*
|
||||
* @param wires The number of wires in enum format. Can be MAX31865_2WIRE, MAX31865_3WIRE, or MAX31865_4WIRE.
|
||||
* @param zero The resistance of the RTD at 0 degC, in ohms.
|
||||
* @param ref The resistance of the reference resistor, in ohms.
|
||||
* @param wires The number of wires as an enum: MAX31865_2WIRE, MAX31865_3WIRE, or MAX31865_4WIRE.
|
||||
* @param zero_res The resistance of the RTD at 0°C, in ohms.
|
||||
* @param ref_res The resistance of the reference resistor, in ohms.
|
||||
* @param wire_res The resistance of the wire connecting the sensor to the RTD, in ohms.
|
||||
*/
|
||||
void MAX31865::begin(max31865_numwires_t wires, float zero, float ref) {
|
||||
Rzero = zero;
|
||||
Rref = ref;
|
||||
void MAX31865::begin(max31865_numwires_t wires, const_float_t zero_res, const_float_t ref_res, const_float_t wire_res) {
|
||||
resNormalizer = 100.0f / zero_res; // reciprocal of resistance, scaled by 100
|
||||
refRes = ref_res;
|
||||
wireRes = wire_res;
|
||||
|
||||
OUT_WRITE(_cs, HIGH);
|
||||
pinMode(cselPin, OUTPUT);
|
||||
digitalWrite(cselPin, HIGH);
|
||||
|
||||
if (_sclk != TERN(LARGE_PINMAP, -1UL, -1)) {
|
||||
// Define pin modes for Software SPI
|
||||
#ifdef MAX31865_DEBUG
|
||||
SERIAL_ECHOLN("Initializing MAX31865 Software SPI");
|
||||
#endif
|
||||
|
||||
OUT_WRITE(_sclk, LOW);
|
||||
SET_OUTPUT(_mosi);
|
||||
SET_INPUT(_miso);
|
||||
}
|
||||
if (sclkPin != TERN(LARGE_PINMAP, -1UL, 255))
|
||||
softSpiInit(); // Define pin modes for Software SPI
|
||||
else {
|
||||
// Start and configure hardware SPI
|
||||
#ifdef MAX31865_DEBUG
|
||||
SERIAL_ECHOLN("Initializing MAX31865 Hardware SPI");
|
||||
#endif
|
||||
|
||||
SPI.begin();
|
||||
DEBUG_ECHOLNPGM("Init MAX31865 Hardware SPI");
|
||||
SPI.begin(); // Start and configure hardware SPI
|
||||
}
|
||||
|
||||
setWires(wires);
|
||||
enableBias(false);
|
||||
autoConvert(false);
|
||||
clearFault();
|
||||
initFixedFlags(wires);
|
||||
|
||||
#ifdef MAX31865_DEBUG_SPI
|
||||
#ifndef LARGE_PINMAP
|
||||
SERIAL_ECHOLNPGM(
|
||||
"Regular begin call with _cs: ", _cs,
|
||||
" _miso: ", _miso,
|
||||
" _sclk: ", _sclk,
|
||||
" _mosi: ", _mosi
|
||||
);
|
||||
#else
|
||||
SERIAL_ECHOLNPGM(
|
||||
"LARGE_PINMAP begin call with _cs: ", _cs,
|
||||
" _miso: ", _miso,
|
||||
" _sclk: ", _sclk,
|
||||
" _mosi: ", _mosi
|
||||
);
|
||||
#endif // LARGE_PINMAP
|
||||
DEBUG_ECHOLNPGM("MAX31865 Regs: CFG ", readRegister8(MAX31865_CONFIG_REG),
|
||||
"|RTD ", readRegister16(MAX31865_RTDMSB_REG),
|
||||
"|HTHRS ", readRegister16(MAX31865_HFAULTMSB_REG),
|
||||
"|LTHRS ", readRegister16(MAX31865_LFAULTMSB_REG),
|
||||
"|FLT ", readRegister8(MAX31865_FAULTSTAT_REG));
|
||||
|
||||
SERIAL_ECHOLNPGM("config: ", readRegister8(MAX31856_CONFIG_REG));
|
||||
SERIAL_EOL();
|
||||
#endif // MAX31865_DEBUG_SPI
|
||||
// fault detection cycle seems to initialize the sensor better
|
||||
runAutoFaultDetectionCycle(); // also initializes flags
|
||||
|
||||
if (lastFault)
|
||||
SERIAL_ECHOLNPGM("MAX31865 init fault ", lastFault);
|
||||
|
||||
writeRegister16(MAX31865_HFAULTMSB_REG, 0xFFFF);
|
||||
writeRegister16(MAX31865_LFAULTMSB_REG, 0);
|
||||
|
||||
#if ENABLED(MAX31865_USE_AUTO_MODE) // make a proper first read to initialize _lastRead
|
||||
|
||||
uint16_t rtd = readRegister16(MAX31865_RTDMSB_REG);
|
||||
|
||||
#if MAX31865_IGNORE_INITIAL_FAULTY_READS > 0
|
||||
rtd = fixFault(rtd);
|
||||
#endif
|
||||
|
||||
if (rtd & 1) {
|
||||
lastRead = 0xFFFF; // some invalid value
|
||||
lastFault = readRegister8(MAX31865_FAULTSTAT_REG);
|
||||
clearFault(); // also clears the bias voltage flag, so no further action is required
|
||||
|
||||
DEBUG_ECHOLNPGM("MAX31865 read fault: ", rtd);
|
||||
}
|
||||
else {
|
||||
DEBUG_ECHOLNPGM("RTD MSB:", (rtd >> 8), " RTD LSB:", (rtd & 0x00FF));
|
||||
lastRead = rtd;
|
||||
TERN_(MAX31865_USE_READ_ERROR_DETECTION, lastReadStamp = millis());
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
enableBias();
|
||||
DELAY_US(2000); // according to the datasheet, 10.5τ+1msec (see below)
|
||||
oneShot();
|
||||
DELAY_US(63000);
|
||||
uint16_t rtd = readRegister16(MAX31865_RTDMSB_REG);
|
||||
|
||||
#if MAX31865_IGNORE_INITIAL_FAULTY_READS > 0
|
||||
rtd = fixFault(rtd);
|
||||
#endif
|
||||
|
||||
if (rtd & 1) {
|
||||
lastRead = 0xFFFF; // some invalid value
|
||||
lastFault = readRegister8(MAX31865_FAULTSTAT_REG);
|
||||
clearFault(); // also clears the bias voltage flag, so no further action is required
|
||||
|
||||
DEBUG_ECHOLNPGM("MAX31865 read fault: ", rtd);
|
||||
}
|
||||
else {
|
||||
DEBUG_ECHOLNPGM("RTD MSB:", (rtd >> 8), " RTD LSB:", (rtd & 0x00FF));
|
||||
|
||||
resetFlags();
|
||||
|
||||
lastRead = rtd;
|
||||
nextEvent = SETUP_BIAS_VOLTAGE;
|
||||
millis_t now = millis();
|
||||
nextEventStamp = now + MAX31865_MIN_SAMPLING_TIME_MSEC;
|
||||
|
||||
TERN_(MAX31865_USE_READ_ERROR_DETECTION, lastReadStamp = now);
|
||||
}
|
||||
|
||||
#endif // MAX31865_USE_AUTO_MODE
|
||||
|
||||
DEBUG_ECHOLNPGM(
|
||||
TERN(LARGE_PINMAP, "LARGE_PINMAP", "Regular")
|
||||
" begin call with cselPin: ", cselPin,
|
||||
" misoPin: ", misoPin,
|
||||
" sclkPin: ", sclkPin,
|
||||
" mosiPin: ", mosiPin,
|
||||
" config: ", readRegister8(MAX31865_CONFIG_REG)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the raw 8-bit FAULTSTAT register
|
||||
* Return and clear the last fault value
|
||||
*
|
||||
* @return The raw unsigned 8-bit FAULT status register
|
||||
* @return The raw unsigned 8-bit FAULT status register or spike fault
|
||||
*/
|
||||
uint8_t MAX31865::readFault() {
|
||||
return readRegister8(MAX31856_FAULTSTAT_REG);
|
||||
uint8_t r = lastFault;
|
||||
lastFault = 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all faults in FAULTSTAT.
|
||||
* Clear last fault
|
||||
*/
|
||||
void MAX31865::clearFault() {
|
||||
setConfig(MAX31856_CONFIG_FAULTSTAT, 1);
|
||||
setConfig(MAX31865_CONFIG_FAULTSTAT, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether we want to have continuous conversions (50/60 Hz)
|
||||
*
|
||||
* @param b If true, auto conversion is enabled
|
||||
* Reset flags
|
||||
*/
|
||||
void MAX31865::autoConvert(bool b) {
|
||||
setConfig(MAX31856_CONFIG_MODEAUTO, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether we want filter out 50Hz noise or 60Hz noise
|
||||
*
|
||||
* @param b If true, 50Hz noise is filtered, else 60Hz(default)
|
||||
*/
|
||||
void MAX31865::enable50HzFilter(bool b) {
|
||||
setConfig(MAX31856_CONFIG_FILT50HZ, b);
|
||||
void MAX31865::resetFlags() {
|
||||
writeRegister8(MAX31865_CONFIG_REG, stdFlags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the bias voltage on the RTD sensor
|
||||
*
|
||||
* @param b If true bias is enabled, else disabled
|
||||
*/
|
||||
void MAX31865::enableBias(bool b) {
|
||||
setConfig(MAX31856_CONFIG_BIAS, b);
|
||||
|
||||
// From the datasheet:
|
||||
// Note that if VBIAS is off (to reduce supply current between conversions), any filter
|
||||
// capacitors at the RTDIN inputs need to charge before an accurate conversion can be
|
||||
// performed. Therefore, enable VBIAS and wait at least 10.5 time constants of the input
|
||||
// RC network plus an additional 1ms before initiating the conversion.
|
||||
if (b)
|
||||
DELAY_US(11500); //11.5ms
|
||||
void MAX31865::enableBias() {
|
||||
setConfig(MAX31865_CONFIG_BIAS, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a one-shot temperature reading.
|
||||
*/
|
||||
void MAX31865::oneShot() {
|
||||
setConfig(MAX31856_CONFIG_1SHOT, 1);
|
||||
setConfig(MAX31865_CONFIG_1SHOT | MAX31865_CONFIG_BIAS, 1);
|
||||
}
|
||||
|
||||
// From the datasheet:
|
||||
// Note that a single conversion requires approximately 52ms in 60Hz filter
|
||||
// mode or 62.5ms in 50Hz filter mode to complete. 1-Shot is a self-clearing bit.
|
||||
// TODO: switch this out depending on the filter mode.
|
||||
DELAY_US(65000); // 65ms
|
||||
void MAX31865::runAutoFaultDetectionCycle() {
|
||||
writeRegister8(MAX31865_CONFIG_REG, (stdFlags & 0x11) | 0x84 ); // cfg reg = 100X010Xb
|
||||
DELAY_US(600);
|
||||
for (int i = 0; i < 10 && (readRegister8(MAX31865_CONFIG_REG) & 0xC) > 0; i++) DELAY_US(100); // Fault det completes when bits 2 and 3 are zero (or after 10 tries)
|
||||
readFault();
|
||||
clearFault();
|
||||
}
|
||||
|
||||
/**
|
||||
* How many wires we have in our RTD setup, can be MAX31865_2WIRE,
|
||||
* MAX31865_3WIRE, or MAX31865_4WIRE
|
||||
* Set a value in the configuration register.
|
||||
*
|
||||
* @param config 8-bit value for the config item
|
||||
* @param enable whether to enable or disable the value
|
||||
*/
|
||||
void MAX31865::setConfig(uint8_t config, bool enable) {
|
||||
uint8_t t = stdFlags;
|
||||
if (enable)
|
||||
t |= config;
|
||||
else
|
||||
t &= ~config;
|
||||
writeRegister8(MAX31865_CONFIG_REG, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize standard flags with flags that will not change during operation (Hz, polling mode and no. of wires)
|
||||
*
|
||||
* @param wires The number of wires in enum format
|
||||
*/
|
||||
void MAX31865::setWires(max31865_numwires_t wires) {
|
||||
uint8_t t = readRegister8(MAX31856_CONFIG_REG);
|
||||
void MAX31865::initFixedFlags(max31865_numwires_t wires) {
|
||||
|
||||
// set config-defined flags (same for all sensors)
|
||||
stdFlags = TERN(MAX31865_50HZ_FILTER, MAX31865_CONFIG_FILT50HZ, MAX31865_CONFIG_FILT60HZ) |
|
||||
TERN(MAX31865_USE_AUTO_MODE, MAX31865_CONFIG_MODEAUTO | MAX31865_CONFIG_BIAS, MAX31865_CONFIG_MODEOFF);
|
||||
|
||||
if (wires == MAX31865_3WIRE)
|
||||
t |= MAX31856_CONFIG_3WIRE;
|
||||
else // 2 or 4 wire
|
||||
t &= ~MAX31856_CONFIG_3WIRE;
|
||||
writeRegister8(MAX31856_CONFIG_REG, t);
|
||||
stdFlags |= MAX31865_CONFIG_3WIRE; // 3 wire
|
||||
else
|
||||
stdFlags &= ~MAX31865_CONFIG_3WIRE; // 2 or 4 wire
|
||||
}
|
||||
|
||||
#if MAX31865_IGNORE_INITIAL_FAULTY_READS > 0
|
||||
|
||||
inline uint16_t MAX31865::fixFault(uint16_t rtd) {
|
||||
if (!ignore_faults || !(rtd & 1))
|
||||
return rtd;
|
||||
|
||||
ignore_faults--;
|
||||
clearFault();
|
||||
|
||||
DEBUG_ECHOLNPGM("MAX31865 ignoring fault ", (MAX31865_IGNORE_INITIAL_FAULTY_READS) - ignore_faults);
|
||||
|
||||
return rtd & ~1; // 0xFFFE
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
inline uint16_t MAX31865::readRawImmediate() {
|
||||
uint16_t rtd = readRegister16(MAX31865_RTDMSB_REG);
|
||||
DEBUG_ECHOLNPGM("MAX31865 RTD MSB:", (rtd >> 8), " LSB:", (rtd & 0x00FF));
|
||||
|
||||
#if MAX31865_IGNORE_INITIAL_FAULTY_READS > 0
|
||||
rtd = fixFault(rtd);
|
||||
#endif
|
||||
|
||||
if (rtd & 1) {
|
||||
lastFault = readRegister8(MAX31865_FAULTSTAT_REG);
|
||||
lastRead |= 1;
|
||||
clearFault(); // also clears the bias voltage flag, so no further action is required
|
||||
DEBUG_ECHOLNPGM("MAX31865 read fault: ", lastFault);
|
||||
}
|
||||
else {
|
||||
TERN_(MAX31865_USE_READ_ERROR_DETECTION, const millis_t ms = millis());
|
||||
if (TERN0(MAX31865_USE_READ_ERROR_DETECTION, ABS((int)(lastRead - rtd)) > 500 && PENDING(ms, lastReadStamp + 1000))) {
|
||||
// If 2 readings within 1s differ too much (~20°C) it's a read error.
|
||||
lastFault = 0x01;
|
||||
lastRead |= 1;
|
||||
DEBUG_ECHOLNPGM("MAX31865 read error: ", rtd);
|
||||
}
|
||||
else {
|
||||
lastRead = rtd;
|
||||
TERN_(MAX31865_USE_READ_ERROR_DETECTION, lastReadStamp = ms);
|
||||
}
|
||||
}
|
||||
|
||||
return rtd;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -283,33 +368,59 @@ void MAX31865::setWires(max31865_numwires_t wires) {
|
||||
* @return The raw unsigned 16-bit register value with ERROR bit attached, NOT temperature!
|
||||
*/
|
||||
uint16_t MAX31865::readRaw() {
|
||||
clearFault();
|
||||
enableBias(true);
|
||||
|
||||
oneShot();
|
||||
uint16_t rtd = readRegister16(MAX31856_RTDMSB_REG);
|
||||
#if ENABLED(MAX31865_USE_AUTO_MODE)
|
||||
|
||||
readRawImmediate();
|
||||
|
||||
#else
|
||||
|
||||
const millis_t ms = millis();
|
||||
|
||||
if (PENDING(ms, nextEventStamp)) {
|
||||
DEBUG_ECHOLNPGM("MAX31865 waiting for event ", nextEvent);
|
||||
return lastRead;
|
||||
}
|
||||
|
||||
switch (nextEvent) {
|
||||
case SETUP_BIAS_VOLTAGE:
|
||||
enableBias();
|
||||
nextEventStamp = ms + 2; // wait at least 10.5*τ (τ = 100nF*430Ω max for PT100 / 10nF*4.3ΚΩ for PT1000 = 43μsec) + 1msec
|
||||
nextEvent = SETUP_1_SHOT_MODE;
|
||||
DEBUG_ECHOLNPGM("MAX31865 bias voltage enabled");
|
||||
break;
|
||||
|
||||
case SETUP_1_SHOT_MODE:
|
||||
oneShot();
|
||||
nextEventStamp = ms + TERN(MAX31865_50HZ_FILTER, 63, 52); // wait at least 52msec for 60Hz (63msec for 50Hz) before reading RTD register
|
||||
nextEvent = READ_RTD_REG;
|
||||
DEBUG_ECHOLNPGM("MAX31865 1 shot mode enabled");
|
||||
break;
|
||||
|
||||
case READ_RTD_REG:
|
||||
|
||||
if (!(readRawImmediate() & 1)) // if clearFault() was not invoked, need to clear the bias voltage and 1-shot flags
|
||||
resetFlags();
|
||||
|
||||
nextEvent = SETUP_BIAS_VOLTAGE;
|
||||
nextEventStamp = ms + (MAX31865_MIN_SAMPLING_TIME_MSEC); // next step should not occur within less than MAX31865_MIN_SAMPLING_TIME_MSEC from the last one
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef MAX31865_DEBUG
|
||||
SERIAL_ECHOLNPGM("RTD MSB:", (rtd >> 8), " RTD LSB:", (rtd & 0x00FF));
|
||||
#endif
|
||||
|
||||
// Disable the bias to lower power dissipation between reads.
|
||||
// If the ref resistor heats up, the temperature reading will be skewed.
|
||||
enableBias(false);
|
||||
|
||||
return rtd;
|
||||
return lastRead;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate and return the resistance value of the connected RTD.
|
||||
*
|
||||
* @param refResistor The value of the matching reference resistor, usually 430 or 4300
|
||||
* @return The raw RTD resistance value, NOT temperature!
|
||||
*/
|
||||
float MAX31865::readResistance() {
|
||||
// Strip the error bit (D0) and convert to a float ratio.
|
||||
// less precise method: (readRaw() * Rref) >> 16
|
||||
return (((readRaw() >> 1) / 32768.0f) * Rref);
|
||||
// less precise method: (readRaw() * refRes) >> 16
|
||||
return ((readRaw() * RECIPROCAL(65536.0f)) * refRes - wireRes);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -326,66 +437,77 @@ float MAX31865::temperature() {
|
||||
*
|
||||
* @return Temperature in C
|
||||
*/
|
||||
float MAX31865::temperature(uint16_t adcVal) {
|
||||
return temperature(((adcVal) / 32768.0f) * Rref);
|
||||
float MAX31865::temperature(const uint16_t adc_val) {
|
||||
return temperature(((adc_val) * RECIPROCAL(32768.0f)) * refRes - wireRes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the temperature in C from the RTD resistance.
|
||||
* Uses the technique outlined in this PDF:
|
||||
* http://www.analog.com/media/en/technical-documentation/application-notes/AN709_0.pdf
|
||||
*
|
||||
* @param Rrtd the resistance value in ohms
|
||||
* @return the temperature in degC
|
||||
* @param rtd_res the resistance value in ohms
|
||||
* @return the temperature in °C
|
||||
*/
|
||||
float MAX31865::temperature(float Rrtd) {
|
||||
float temp = (RTD_Z1 + sqrt(RTD_Z2 + (RTD_Z3 * Rrtd))) / RTD_Z4;
|
||||
float MAX31865::temperature(float rtd_res) {
|
||||
|
||||
rtd_res *= resNormalizer; // normalize to 100 ohm
|
||||
|
||||
// Constants for calculating temperature from the measured RTD resistance.
|
||||
// http://www.analog.com/media/en/technical-documentation/application-notes/AN709_0.pdf
|
||||
constexpr float RTD_Z1 = -0.0039083,
|
||||
RTD_Z2 = +1.758480889e-5,
|
||||
RTD_Z3 = -2.31e-8,
|
||||
RTD_Z4 = -1.155e-6;
|
||||
|
||||
// Callender-Van Dusen equation
|
||||
float temp = (RTD_Z1 + sqrt(RTD_Z2 + (RTD_Z3 * rtd_res))) * RECIPROCAL(RTD_Z4);
|
||||
|
||||
// From the PDF...
|
||||
//
|
||||
// The previous equation is valid only for temperatures of 0°C and above.
|
||||
// The equation for RRTD(t) that defines negative temperature behavior is a
|
||||
// fourth-order polynomial (after expanding the third term) and is quite
|
||||
// impractical to solve for a single expression of temperature as a function
|
||||
// of resistance.
|
||||
// of resistance. So here we use a Linear Approximation instead.
|
||||
//
|
||||
if (temp < 0) {
|
||||
Rrtd = (Rrtd / Rzero) * 100; // normalize to 100 ohm
|
||||
float rpoly = Rrtd;
|
||||
#ifndef MAX31865_APPROX
|
||||
#define MAX31865_APPROX 5
|
||||
#endif
|
||||
|
||||
temp = -242.02 + (2.2228 * rpoly);
|
||||
rpoly *= Rrtd; // square
|
||||
temp += 2.5859e-3 * rpoly;
|
||||
rpoly *= Rrtd; // ^3
|
||||
temp -= 4.8260e-6 * rpoly;
|
||||
rpoly *= Rrtd; // ^4
|
||||
temp -= 2.8183e-8 * rpoly;
|
||||
rpoly *= Rrtd; // ^5
|
||||
temp += 1.5243e-10 * rpoly;
|
||||
constexpr float RTD_C[] = {
|
||||
#if MAX31865_APPROX == 5
|
||||
-242.02, +2.2228, +2.5859e-3, -4.8260e-6, -2.8183e-8, +1.5243e-10
|
||||
#elif MAX31865_APPROX == 4
|
||||
-241.96, +2.2163, +2.8541e-3, -9.9121e-6, -1.7152e-8
|
||||
#elif MAX31865_APPROX == 3
|
||||
-242.09, +2.2276, +2.5178e-3, -5.8620e-6
|
||||
#else
|
||||
-242.97, +2.2838, +1.4727e-3
|
||||
#endif
|
||||
};
|
||||
|
||||
float rpoly = rtd_res;
|
||||
temp = RTD_C[0];
|
||||
temp += rpoly * RTD_C[1];
|
||||
rpoly *= rtd_res; temp += rpoly * RTD_C[2];
|
||||
if (MAX31865_APPROX >= 3) { rpoly *= rtd_res; temp += rpoly * RTD_C[3]; }
|
||||
if (MAX31865_APPROX >= 4) { rpoly *= rtd_res; temp += rpoly * RTD_C[4]; }
|
||||
if (MAX31865_APPROX >= 5) { rpoly *= rtd_res; temp += rpoly * RTD_C[5]; }
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
//
|
||||
// private:
|
||||
//
|
||||
|
||||
|
||||
/**
|
||||
* Set a value in the configuration register.
|
||||
*
|
||||
* @param config 8-bit value for the config item
|
||||
* @param enable whether to enable or disable the value
|
||||
* MAX31865 SPI Timing constants
|
||||
* See MAX31865 datasheet (https://datasheets.maximintegrated.com/en/ds/MAX31865.pdf)
|
||||
* All timings in nsec, minimum values.
|
||||
*/
|
||||
void MAX31865::setConfig(uint8_t config, bool enable) {
|
||||
uint8_t t = readRegister8(MAX31856_CONFIG_REG);
|
||||
if (enable)
|
||||
t |= config;
|
||||
else
|
||||
t &= ~config; // disable
|
||||
writeRegister8(MAX31856_CONFIG_REG, t);
|
||||
}
|
||||
|
||||
#define MAX31865_SPI_TIMING_TCC 400 // CS to SCLK setup
|
||||
#define MAX31865_SPI_TIMING_TDC 35 // Data to SCLK setup
|
||||
#define MAX31865_SPI_TIMING_TCL 100 // SCK half period
|
||||
#define MAX31865_SPI_TIMING_TCCH 100 // SCK to CS hold
|
||||
#define MAX31865_SPI_TIMING_TCWH 400 // CS inactive time (min)
|
||||
|
||||
/**
|
||||
* Read a single byte from the specified register address.
|
||||
@@ -396,7 +518,6 @@ void MAX31865::setConfig(uint8_t config, bool enable) {
|
||||
uint8_t MAX31865::readRegister8(uint8_t addr) {
|
||||
uint8_t ret = 0;
|
||||
readRegisterN(addr, &ret, 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -407,14 +528,9 @@ uint8_t MAX31865::readRegister8(uint8_t addr) {
|
||||
* @return both register contents as a single 16-bit int
|
||||
*/
|
||||
uint16_t MAX31865::readRegister16(uint8_t addr) {
|
||||
uint8_t buffer[2] = {0, 0};
|
||||
uint8_t buffer[2] = { 0 };
|
||||
readRegisterN(addr, buffer, 2);
|
||||
|
||||
uint16_t ret = buffer[0];
|
||||
ret <<= 8;
|
||||
ret |= buffer[1];
|
||||
|
||||
return ret;
|
||||
return uint16_t(buffer[0]) << 8 | buffer[1];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -425,27 +541,26 @@ uint16_t MAX31865::readRegister16(uint8_t addr) {
|
||||
* @param n the number of bytes to read
|
||||
*/
|
||||
void MAX31865::readRegisterN(uint8_t addr, uint8_t buffer[], uint8_t n) {
|
||||
addr &= 0x7F; // make sure top bit is not set
|
||||
if (_sclk == TERN(LARGE_PINMAP, -1UL, -1))
|
||||
SPI.beginTransaction(spiConfig);
|
||||
else
|
||||
WRITE(_sclk, LOW);
|
||||
|
||||
WRITE(_cs, LOW);
|
||||
spixfer(addr);
|
||||
addr &= 0x7F; // make sure top bit is not set
|
||||
|
||||
spiBeginTransaction();
|
||||
spiTransfer(addr);
|
||||
|
||||
while (n--) {
|
||||
buffer[0] = spixfer(0xFF);
|
||||
#ifdef MAX31865_DEBUG_SPI
|
||||
SERIAL_ECHOLNPGM("buffer read ", n, " data: ", buffer[0]);
|
||||
#endif
|
||||
buffer[0] = spiTransfer(0xFF);
|
||||
buffer++;
|
||||
}
|
||||
|
||||
if (_sclk == TERN(LARGE_PINMAP, -1UL, -1))
|
||||
SPI.endTransaction();
|
||||
spiEndTransaction();
|
||||
}
|
||||
|
||||
WRITE(_cs, HIGH);
|
||||
void MAX31865::writeRegister16(uint8_t addr, uint16_t data) {
|
||||
spiBeginTransaction();
|
||||
spiTransfer(addr | 0x80); // make sure top bit is set
|
||||
spiTransfer(data >> 8);
|
||||
spiTransfer(data & 0xFF);
|
||||
spiEndTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -455,20 +570,33 @@ void MAX31865::readRegisterN(uint8_t addr, uint8_t buffer[], uint8_t n) {
|
||||
* @param data the data to write
|
||||
*/
|
||||
void MAX31865::writeRegister8(uint8_t addr, uint8_t data) {
|
||||
if (_sclk == TERN(LARGE_PINMAP, -1UL, -1))
|
||||
spiBeginTransaction();
|
||||
spiTransfer(addr | 0x80); // make sure top bit is set
|
||||
spiTransfer(data);
|
||||
spiEndTransaction();
|
||||
}
|
||||
|
||||
void MAX31865::spiBeginTransaction() {
|
||||
digitalWrite(sclkPin, LOW); // ensure CPOL0
|
||||
DELAY_NS_VAR(MAX31865_SPI_TIMING_TCWH); // ensure minimum time of CS inactivity after previous operation
|
||||
digitalWrite(cselPin, LOW);
|
||||
DELAY_NS_VAR(MAX31865_SPI_TIMING_TCC);
|
||||
|
||||
if (sclkPin == TERN(LARGE_PINMAP, -1UL, 255))
|
||||
SPI.beginTransaction(spiConfig);
|
||||
else
|
||||
WRITE(_sclk, LOW);
|
||||
digitalWrite(sclkPin, HIGH);
|
||||
}
|
||||
|
||||
WRITE(_cs, LOW);
|
||||
|
||||
spixfer(addr | 0x80); // make sure top bit is set
|
||||
spixfer(data);
|
||||
|
||||
if (_sclk == TERN(LARGE_PINMAP, -1UL, -1))
|
||||
void MAX31865::spiEndTransaction() {
|
||||
if (sclkPin == TERN(LARGE_PINMAP, -1UL, 255))
|
||||
SPI.endTransaction();
|
||||
else
|
||||
digitalWrite(sclkPin, LOW);
|
||||
|
||||
WRITE(_cs, HIGH);
|
||||
DELAY_NS_VAR(MAX31865_SPI_TIMING_TCCH);
|
||||
|
||||
digitalWrite(cselPin, HIGH);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -480,21 +608,31 @@ void MAX31865::writeRegister8(uint8_t addr, uint8_t data) {
|
||||
* @param x an 8-bit chunk of data to write
|
||||
* @return the 8-bit response
|
||||
*/
|
||||
uint8_t MAX31865::spixfer(uint8_t x) {
|
||||
if (_sclk == TERN(LARGE_PINMAP, -1UL, -1))
|
||||
uint8_t MAX31865::spiTransfer(uint8_t x) {
|
||||
if (sclkPin == TERN(LARGE_PINMAP, -1UL, 255))
|
||||
return SPI.transfer(x);
|
||||
|
||||
uint8_t reply = 0;
|
||||
for (int i = 7; i >= 0; i--) {
|
||||
digitalWrite(mosiPin, x & _BV(i));
|
||||
DELAY_NS_VAR(MAX31865_SPI_TIMING_TDC);
|
||||
digitalWrite(sclkPin, LOW);
|
||||
DELAY_NS_VAR(MAX31865_SPI_TIMING_TCL - MAX31865_SPI_TIMING_TDC);
|
||||
reply <<= 1;
|
||||
WRITE(_sclk, HIGH);
|
||||
WRITE(_mosi, x & (1 << i));
|
||||
WRITE(_sclk, LOW);
|
||||
if (READ(_miso))
|
||||
reply |= 1;
|
||||
if (digitalRead(misoPin)) reply |= 1;
|
||||
DELAY_NS_VAR(MAX31865_SPI_TIMING_TDC);
|
||||
digitalWrite(sclkPin, HIGH);
|
||||
DELAY_NS_VAR(MAX31865_SPI_TIMING_TCL - MAX31865_SPI_TIMING_TDC);
|
||||
}
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
void MAX31865::softSpiInit() {
|
||||
DEBUG_ECHOLNPGM("Initializing MAX31865 Software SPI");
|
||||
pinMode(sclkPin, OUTPUT);
|
||||
digitalWrite(sclkPin, LOW);
|
||||
pinMode(mosiPin, OUTPUT);
|
||||
pinMode(misoPin, INPUT);
|
||||
}
|
||||
|
||||
#endif // HAS_MAX31865 && !USE_ADAFRUIT_MAX31865
|
||||
|
@@ -41,28 +41,30 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
//#define DEBUG_MAX31865
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
#include "../HAL/shared/Delay.h"
|
||||
#include HAL_PATH(../HAL, MarlinSPI.h)
|
||||
|
||||
#define MAX31856_CONFIG_REG 0x00
|
||||
#define MAX31856_CONFIG_BIAS 0x80
|
||||
#define MAX31856_CONFIG_MODEAUTO 0x40
|
||||
#define MAX31856_CONFIG_MODEOFF 0x00
|
||||
#define MAX31856_CONFIG_1SHOT 0x20
|
||||
#define MAX31856_CONFIG_3WIRE 0x10
|
||||
#define MAX31856_CONFIG_24WIRE 0x00
|
||||
#define MAX31856_CONFIG_FAULTSTAT 0x02
|
||||
#define MAX31856_CONFIG_FILT50HZ 0x01
|
||||
#define MAX31856_CONFIG_FILT60HZ 0x00
|
||||
#define MAX31865_CONFIG_REG 0x00
|
||||
#define MAX31865_CONFIG_BIAS 0x80
|
||||
#define MAX31865_CONFIG_MODEAUTO 0x40
|
||||
#define MAX31865_CONFIG_MODEOFF 0x00
|
||||
#define MAX31865_CONFIG_1SHOT 0x20
|
||||
#define MAX31865_CONFIG_3WIRE 0x10
|
||||
#define MAX31865_CONFIG_24WIRE 0x00
|
||||
#define MAX31865_CONFIG_FAULTSTAT 0x02
|
||||
#define MAX31865_CONFIG_FILT50HZ 0x01
|
||||
#define MAX31865_CONFIG_FILT60HZ 0x00
|
||||
|
||||
#define MAX31856_RTDMSB_REG 0x01
|
||||
#define MAX31856_RTDLSB_REG 0x02
|
||||
#define MAX31856_HFAULTMSB_REG 0x03
|
||||
#define MAX31856_HFAULTLSB_REG 0x04
|
||||
#define MAX31856_LFAULTMSB_REG 0x05
|
||||
#define MAX31856_LFAULTLSB_REG 0x06
|
||||
#define MAX31856_FAULTSTAT_REG 0x07
|
||||
#define MAX31865_RTDMSB_REG 0x01
|
||||
#define MAX31865_RTDLSB_REG 0x02
|
||||
#define MAX31865_HFAULTMSB_REG 0x03
|
||||
#define MAX31865_HFAULTLSB_REG 0x04
|
||||
#define MAX31865_LFAULTMSB_REG 0x05
|
||||
#define MAX31865_LFAULTLSB_REG 0x06
|
||||
#define MAX31865_FAULTSTAT_REG 0x07
|
||||
|
||||
#define MAX31865_FAULT_HIGHTHRESH 0x80 // D7
|
||||
#define MAX31865_FAULT_LOWTHRESH 0x40 // D6
|
||||
@@ -71,26 +73,49 @@
|
||||
#define MAX31865_FAULT_RTDINLOW 0x08 // D3
|
||||
#define MAX31865_FAULT_OVUV 0x04 // D2
|
||||
|
||||
// http://www.analog.com/media/en/technical-documentation/application-notes/AN709_0.pdf
|
||||
// constants for calculating temperature from the measured RTD resistance.
|
||||
#define RTD_Z1 -0.0039083
|
||||
#define RTD_Z2 0.00001758480889
|
||||
#define RTD_Z3 -0.0000000231
|
||||
#define RTD_Z4 -0.000001155
|
||||
|
||||
typedef enum max31865_numwires {
|
||||
MAX31865_2WIRE = 0,
|
||||
MAX31865_3WIRE = 1,
|
||||
MAX31865_4WIRE = 0
|
||||
} max31865_numwires_t;
|
||||
|
||||
#if DISABLED(MAX31865_USE_AUTO_MODE)
|
||||
typedef enum one_shot_event : uint8_t {
|
||||
SETUP_BIAS_VOLTAGE,
|
||||
SETUP_1_SHOT_MODE,
|
||||
READ_RTD_REG
|
||||
} one_shot_event_t;
|
||||
#endif
|
||||
|
||||
/* Interface class for the MAX31865 RTD Sensor reader */
|
||||
class MAX31865 {
|
||||
private:
|
||||
static SPISettings spiConfig;
|
||||
|
||||
TERN(LARGE_PINMAP, uint32_t, uint8_t) _sclk, _miso, _mosi, _cs;
|
||||
float Rzero, Rref;
|
||||
TERN(LARGE_PINMAP, uint32_t, uint8_t) sclkPin, misoPin, mosiPin, cselPin;
|
||||
|
||||
uint16_t spiDelay;
|
||||
|
||||
float resNormalizer, refRes, wireRes;
|
||||
|
||||
#if ENABLED(MAX31865_USE_READ_ERROR_DETECTION)
|
||||
millis_t lastReadStamp = 0;
|
||||
#endif
|
||||
|
||||
uint16_t lastRead = 0;
|
||||
uint8_t lastFault = 0;
|
||||
|
||||
#if DISABLED(MAX31865_USE_AUTO_MODE)
|
||||
millis_t nextEventStamp;
|
||||
one_shot_event_t nextEvent;
|
||||
#endif
|
||||
|
||||
#ifdef MAX31865_IGNORE_INITIAL_FAULTY_READS
|
||||
uint8_t ignore_faults = MAX31865_IGNORE_INITIAL_FAULTY_READS;
|
||||
uint16_t fixFault(uint16_t rtd);
|
||||
#endif
|
||||
|
||||
uint8_t stdFlags = 0;
|
||||
|
||||
void setConfig(uint8_t config, bool enable);
|
||||
|
||||
@@ -99,10 +124,26 @@ private:
|
||||
uint16_t readRegister16(uint8_t addr);
|
||||
|
||||
void writeRegister8(uint8_t addr, uint8_t reg);
|
||||
uint8_t spixfer(uint8_t addr);
|
||||
void writeRegister16(uint8_t addr, uint16_t reg);
|
||||
|
||||
void softSpiInit();
|
||||
void spiBeginTransaction();
|
||||
uint8_t spiTransfer(uint8_t addr);
|
||||
void spiEndTransaction();
|
||||
|
||||
void initFixedFlags(max31865_numwires_t wires);
|
||||
|
||||
void enable50HzFilter(bool b);
|
||||
void enableBias();
|
||||
void oneShot();
|
||||
void resetFlags();
|
||||
|
||||
uint16_t readRawImmediate();
|
||||
|
||||
void runAutoFaultDetectionCycle();
|
||||
|
||||
public:
|
||||
#ifdef LARGE_PINMAP
|
||||
#if ENABLED(LARGE_PINMAP)
|
||||
MAX31865(uint32_t spi_cs, uint8_t pin_mapping);
|
||||
MAX31865(uint32_t spi_cs, uint32_t spi_mosi, uint32_t spi_miso,
|
||||
uint32_t spi_clk, uint8_t pin_mapping);
|
||||
@@ -112,20 +153,14 @@ public:
|
||||
int8_t spi_clk);
|
||||
#endif
|
||||
|
||||
void begin(max31865_numwires_t wires, float zero, float ref);
|
||||
void begin(max31865_numwires_t wires, const_float_t zero_res, const_float_t ref_res, const_float_t wire_res);
|
||||
|
||||
uint8_t readFault();
|
||||
void clearFault();
|
||||
|
||||
void setWires(max31865_numwires_t wires);
|
||||
void autoConvert(bool b);
|
||||
void enable50HzFilter(bool b);
|
||||
void enableBias(bool b);
|
||||
void oneShot();
|
||||
|
||||
uint16_t readRaw();
|
||||
float readResistance();
|
||||
float temperature();
|
||||
float temperature(uint16_t adcVal);
|
||||
float temperature(float Rrtd);
|
||||
float temperature(const uint16_t adc_val);
|
||||
float temperature(float rtd_res);
|
||||
};
|
||||
|
@@ -133,7 +133,7 @@ uint16_t W25QXXFlash::W25QXX_ReadID(void) {
|
||||
return Temp;
|
||||
}
|
||||
|
||||
void W25QXXFlash::SPI_FLASH_WriteEnable(void) {
|
||||
void W25QXXFlash::SPI_FLASH_WriteEnable() {
|
||||
// Select the FLASH: Chip Select low
|
||||
SPI_FLASH_CS_L();
|
||||
// Send "Write Enable" instruction
|
||||
@@ -151,7 +151,7 @@ void W25QXXFlash::SPI_FLASH_WriteEnable(void) {
|
||||
* Output : None
|
||||
* Return : None
|
||||
*******************************************************************************/
|
||||
void W25QXXFlash::SPI_FLASH_WaitForWriteEnd(void) {
|
||||
void W25QXXFlash::SPI_FLASH_WaitForWriteEnd() {
|
||||
uint8_t FLASH_Status = 0;
|
||||
|
||||
// Select the FLASH: Chip Select low
|
||||
@@ -216,7 +216,7 @@ void W25QXXFlash::SPI_FLASH_BlockErase(uint32_t BlockAddr) {
|
||||
* Output : None
|
||||
* Return : None
|
||||
*******************************************************************************/
|
||||
void W25QXXFlash::SPI_FLASH_BulkErase(void) {
|
||||
void W25QXXFlash::SPI_FLASH_BulkErase() {
|
||||
// Send write enable instruction
|
||||
SPI_FLASH_WriteEnable();
|
||||
|
||||
|
@@ -61,11 +61,11 @@ public:
|
||||
static void spi_flash_Send(uint8_t b);
|
||||
static void spi_flash_SendBlock(uint8_t token, const uint8_t *buf);
|
||||
static uint16_t W25QXX_ReadID(void);
|
||||
static void SPI_FLASH_WriteEnable(void);
|
||||
static void SPI_FLASH_WaitForWriteEnd(void);
|
||||
static void SPI_FLASH_WriteEnable();
|
||||
static void SPI_FLASH_WaitForWriteEnd();
|
||||
static void SPI_FLASH_SectorErase(uint32_t SectorAddr);
|
||||
static void SPI_FLASH_BlockErase(uint32_t BlockAddr);
|
||||
static void SPI_FLASH_BulkErase(void);
|
||||
static void SPI_FLASH_BulkErase();
|
||||
static void SPI_FLASH_PageWrite(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
|
||||
static void SPI_FLASH_BufferWrite(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
|
||||
static void SPI_FLASH_BufferRead(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead);
|
||||
|
@@ -44,7 +44,7 @@ struct AutoReporter {
|
||||
next_report_ms = ms + SEC_TO_MS(report_interval);
|
||||
PORT_REDIRECT(report_port_mask);
|
||||
Helper::report();
|
||||
//PORT_RESTORE();
|
||||
PORT_RESTORE();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@@ -30,7 +30,7 @@
|
||||
*/
|
||||
|
||||
#define FORCE_INLINE __attribute__((always_inline)) inline
|
||||
#define _O3 __attribute__((optimize("O3")))
|
||||
#define __O3 __attribute__((optimize("O3")))
|
||||
|
||||
template <uint8_t uid, uint8_t size>
|
||||
struct BresenhamCfg { static constexpr uint8_t UID = uid, SIZE = size; };
|
||||
@@ -114,9 +114,9 @@ public:
|
||||
if (tick1(index)) { value[index] += dir[index]; back(index); }
|
||||
}
|
||||
|
||||
FORCE_INLINE static void tick1() _O3 { for (uint8_t i = 0; i < Cfg::SIZE; i++) (void)tick1(i); }
|
||||
FORCE_INLINE static void tick1() __O3 { for (uint8_t i = 0; i < Cfg::SIZE; i++) (void)tick1(i); }
|
||||
|
||||
FORCE_INLINE static void tick() _O3 { for (uint8_t i = 0; i < Cfg::SIZE; i++) (void)tick(i); }
|
||||
FORCE_INLINE static void tick() __O3 { for (uint8_t i = 0; i < Cfg::SIZE; i++) (void)tick(i); }
|
||||
|
||||
static void report(const uint8_t index) {
|
||||
if (index < Cfg::SIZE) {
|
||||
|
@@ -22,7 +22,7 @@
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#if USE_BEEPER
|
||||
#if HAS_BEEPER
|
||||
|
||||
#include "buzzer.h"
|
||||
#include "../module/temperature.h"
|
||||
@@ -45,17 +45,17 @@ Buzzer buzzer;
|
||||
* @param frequency Frequency of the tone in hertz
|
||||
*/
|
||||
void Buzzer::tone(const uint16_t duration, const uint16_t frequency/*=0*/) {
|
||||
if (!ui.buzzer_enabled) return;
|
||||
if (!ui.sound_on) return;
|
||||
while (buffer.isFull()) {
|
||||
tick();
|
||||
thermalManager.manage_heater();
|
||||
thermalManager.task();
|
||||
}
|
||||
tone_t tone = { duration, frequency };
|
||||
buffer.enqueue(tone);
|
||||
}
|
||||
|
||||
void Buzzer::tick() {
|
||||
if (!ui.buzzer_enabled) return;
|
||||
if (!ui.sound_on) return;
|
||||
const millis_t now = millis();
|
||||
|
||||
if (!state.endtime) {
|
||||
@@ -81,4 +81,4 @@ void Buzzer::tick() {
|
||||
else if (ELAPSED(now, state.endtime)) reset();
|
||||
}
|
||||
|
||||
#endif // USE_BEEPER
|
||||
#endif // HAS_BEEPER
|
||||
|
@@ -23,7 +23,7 @@
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#if USE_BEEPER
|
||||
#if HAS_BEEPER
|
||||
|
||||
#include "circularqueue.h"
|
||||
|
||||
@@ -61,23 +61,11 @@
|
||||
*/
|
||||
FORCE_INLINE static void invert() { TOGGLE(BEEPER_PIN); }
|
||||
|
||||
/**
|
||||
* @brief Turn off a digital PIN
|
||||
* @details Alias of digitalWrite(PIN, LOW) using FastIO
|
||||
*/
|
||||
FORCE_INLINE static void off() { WRITE(BEEPER_PIN, LOW); }
|
||||
|
||||
/**
|
||||
* @brief Turn on a digital PIN
|
||||
* @details Alias of digitalWrite(PIN, HIGH) using FastIO
|
||||
*/
|
||||
FORCE_INLINE static void on() { WRITE(BEEPER_PIN, HIGH); }
|
||||
|
||||
/**
|
||||
* @brief Resets the state of the class
|
||||
* @details Brings the class state to a known one.
|
||||
*/
|
||||
static inline void reset() {
|
||||
static void reset() {
|
||||
off();
|
||||
state.endtime = 0;
|
||||
}
|
||||
@@ -86,11 +74,25 @@
|
||||
/**
|
||||
* @brief Init Buzzer
|
||||
*/
|
||||
static inline void init() {
|
||||
static void init() {
|
||||
SET_OUTPUT(BEEPER_PIN);
|
||||
reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Turn on a digital PIN
|
||||
* @details Alias of digitalWrite(PIN, HIGH) using FastIO
|
||||
*/
|
||||
FORCE_INLINE static void on() { WRITE(BEEPER_PIN, HIGH); }
|
||||
|
||||
/**
|
||||
* @brief Turn off a digital PIN
|
||||
* @details Alias of digitalWrite(PIN, LOW) using FastIO
|
||||
*/
|
||||
FORCE_INLINE static void off() { WRITE(BEEPER_PIN, LOW); }
|
||||
|
||||
static void click(const uint16_t duration) { on(); delay(duration); off(); }
|
||||
|
||||
/**
|
||||
* @brief Add a tone to the queue
|
||||
* @details Adds a tone_t structure to the ring buffer, will block IO if the
|
||||
@@ -115,9 +117,9 @@
|
||||
// Buzz directly via the BEEPER pin tone queue
|
||||
#define BUZZ(d,f) buzzer.tone(d, f)
|
||||
|
||||
#elif HAS_BUZZER
|
||||
#elif USE_MARLINUI_BUZZER
|
||||
|
||||
// Buzz indirectly via the MarlinUI instance
|
||||
// Use MarlinUI for a buzzer on the LCD
|
||||
#include "../lcd/marlinui.h"
|
||||
#define BUZZ(d,f) ui.buzz(d,f)
|
||||
|
||||
@@ -127,3 +129,7 @@
|
||||
#define BUZZ(d,f) NOOP
|
||||
|
||||
#endif
|
||||
|
||||
#define ERR_BUZZ() BUZZ(400, 40);
|
||||
#define OKAY_BUZZ() do{ BUZZ(100, 659); BUZZ(10, 0); BUZZ(100, 698); }while(0)
|
||||
#define DONE_BUZZ(OK) do{ if (OK) OKAY_BUZZ(); else ERR_BUZZ(); }while(0)
|
||||
|
@@ -106,8 +106,8 @@ struct duration_t {
|
||||
return this->value;
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#if GCC_VERSION <= 50000
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wformat-overflow"
|
||||
#endif
|
||||
|
||||
@@ -127,11 +127,11 @@ struct duration_t {
|
||||
* 59s
|
||||
*/
|
||||
char* toString(char * const buffer) const {
|
||||
int y = this->year(),
|
||||
d = this->day() % 365,
|
||||
h = this->hour() % 24,
|
||||
m = this->minute() % 60,
|
||||
s = this->second() % 60;
|
||||
const uint16_t y = this->year(),
|
||||
d = this->day() % 365,
|
||||
h = this->hour() % 24,
|
||||
m = this->minute() % 60,
|
||||
s = this->second() % 60;
|
||||
|
||||
if (y) sprintf_P(buffer, PSTR("%iy %id %ih %im %is"), y, d, h, m, s);
|
||||
else if (d) sprintf_P(buffer, PSTR("%id %ih %im %is"), d, h, m, s);
|
||||
@@ -149,28 +149,32 @@ struct duration_t {
|
||||
*
|
||||
* Output examples:
|
||||
* 123456789 (strlen)
|
||||
* 12'34
|
||||
* 99:59
|
||||
* 11d 12:33
|
||||
*/
|
||||
uint8_t toDigital(char *buffer, bool with_days=false) const {
|
||||
uint16_t h = uint16_t(this->hour()),
|
||||
m = uint16_t(this->minute() % 60UL);
|
||||
const uint16_t h = uint16_t(this->hour()),
|
||||
m = uint16_t(this->minute() % 60UL);
|
||||
if (with_days) {
|
||||
uint16_t d = this->day();
|
||||
sprintf_P(buffer, PSTR("%hud %02hu:%02hu"), d, h % 24, m);
|
||||
const uint16_t d = this->day();
|
||||
sprintf_P(buffer, PSTR("%hud %02hu:%02hu"), d, h % 24, m); // 1d 23:45
|
||||
return d >= 10 ? 9 : 8;
|
||||
}
|
||||
else if (!h) {
|
||||
const uint16_t s = uint16_t(this->second() % 60UL);
|
||||
sprintf_P(buffer, PSTR("%02hu'%02hu"), m, s); // 12'34
|
||||
return 5;
|
||||
}
|
||||
else if (h < 100) {
|
||||
sprintf_P(buffer, PSTR("%02hu:%02hu"), h, m);
|
||||
sprintf_P(buffer, PSTR("%02hu:%02hu"), h, m); // 12:34
|
||||
return 5;
|
||||
}
|
||||
else {
|
||||
sprintf_P(buffer, PSTR("%hu:%02hu"), h, m);
|
||||
sprintf_P(buffer, PSTR("%hu:%02hu"), h, m); // 123:45
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
|
||||
#if GCC_VERSION <= 50000
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
#pragma GCC diagnostic pop
|
||||
};
|
||||
|
@@ -27,7 +27,7 @@
|
||||
// Utility functions to create and print hex strings as nybble, byte, and word.
|
||||
//
|
||||
|
||||
FORCE_INLINE char hex_nybble(const uint8_t n) {
|
||||
constexpr char hex_nybble(const uint8_t n) {
|
||||
return (n & 0xF) + ((n & 0xF) < 10 ? '0' : 'A' - 10);
|
||||
}
|
||||
char* hex_byte(const uint8_t b);
|
||||
|
@@ -161,7 +161,7 @@ Nozzle nozzle;
|
||||
void Nozzle::clean(const uint8_t &pattern, const uint8_t &strokes, const_float_t radius, const uint8_t &objects, const uint8_t cleans) {
|
||||
xyz_pos_t start[HOTENDS] = NOZZLE_CLEAN_START_POINT, end[HOTENDS] = NOZZLE_CLEAN_END_POINT, middle[HOTENDS] = NOZZLE_CLEAN_CIRCLE_MIDDLE;
|
||||
|
||||
const uint8_t arrPos = ANY(SINGLENOZZLE, MIXING_EXTRUDER) ? 0 : active_extruder;
|
||||
const uint8_t arrPos = EITHER(SINGLENOZZLE, MIXING_EXTRUDER) ? 0 : active_extruder;
|
||||
|
||||
#if NOZZLE_CLEAN_MIN_TEMP > 20
|
||||
if (thermalManager.degTargetHotend(arrPos) < NOZZLE_CLEAN_MIN_TEMP) {
|
||||
@@ -254,11 +254,18 @@ Nozzle nozzle;
|
||||
break;
|
||||
}
|
||||
|
||||
do_blocking_move_to_xy(
|
||||
TERN(NOZZLE_PARK_Y_ONLY, current_position, park).x,
|
||||
TERN(NOZZLE_PARK_X_ONLY, current_position, park).y,
|
||||
fr_xy
|
||||
);
|
||||
#ifndef NOZZLE_PARK_MOVE
|
||||
#define NOZZLE_PARK_MOVE 0
|
||||
#endif
|
||||
switch (NOZZLE_PARK_MOVE) {
|
||||
case 0: do_blocking_move_to_xy(park, fr_xy); break;
|
||||
case 1: do_blocking_move_to_x(park.x, fr_xy); break;
|
||||
case 2: do_blocking_move_to_y(park.y, fr_xy); break;
|
||||
case 3: do_blocking_move_to_x(park.x, fr_xy);
|
||||
do_blocking_move_to_y(park.y, fr_xy); break;
|
||||
case 4: do_blocking_move_to_y(park.y, fr_xy);
|
||||
do_blocking_move_to_x(park.x, fr_xy); break;
|
||||
}
|
||||
|
||||
report_current_position();
|
||||
}
|
||||
|
@@ -41,7 +41,7 @@ class Nozzle {
|
||||
* @param end xyz_pos_t defining the ending point
|
||||
* @param strokes number of strokes to execute
|
||||
*/
|
||||
static void stroke(const xyz_pos_t &start, const xyz_pos_t &end, const uint8_t &strokes) _Os;
|
||||
static void stroke(const xyz_pos_t &start, const xyz_pos_t &end, const uint8_t &strokes) __Os;
|
||||
|
||||
/**
|
||||
* @brief Zig-zag clean pattern
|
||||
@@ -52,7 +52,7 @@ class Nozzle {
|
||||
* @param strokes number of strokes to execute
|
||||
* @param objects number of objects to create
|
||||
*/
|
||||
static void zigzag(const xyz_pos_t &start, const xyz_pos_t &end, const uint8_t &strokes, const uint8_t &objects) _Os;
|
||||
static void zigzag(const xyz_pos_t &start, const xyz_pos_t &end, const uint8_t &strokes, const uint8_t &objects) __Os;
|
||||
|
||||
/**
|
||||
* @brief Circular clean pattern
|
||||
@@ -62,7 +62,7 @@ class Nozzle {
|
||||
* @param strokes number of strokes to execute
|
||||
* @param radius radius of circle
|
||||
*/
|
||||
static void circle(const xyz_pos_t &start, const xyz_pos_t &middle, const uint8_t &strokes, const_float_t radius) _Os;
|
||||
static void circle(const xyz_pos_t &start, const xyz_pos_t &middle, const uint8_t &strokes, const_float_t radius) __Os;
|
||||
|
||||
#endif // NOZZLE_CLEAN_FEATURE
|
||||
|
||||
@@ -77,14 +77,14 @@ class Nozzle {
|
||||
* @param pattern one of the available patterns
|
||||
* @param argument depends on the cleaning pattern
|
||||
*/
|
||||
static void clean(const uint8_t &pattern, const uint8_t &strokes, const_float_t radius, const uint8_t &objects, const uint8_t cleans) _Os;
|
||||
static void clean(const uint8_t &pattern, const uint8_t &strokes, const_float_t radius, const uint8_t &objects, const uint8_t cleans) __Os;
|
||||
|
||||
#endif // NOZZLE_CLEAN_FEATURE
|
||||
|
||||
#if ENABLED(NOZZLE_PARK_FEATURE)
|
||||
|
||||
static float park_mode_0_height(const_float_t park_z) _Os;
|
||||
static void park(const uint8_t z_action, const xyz_pos_t &park=NOZZLE_PARK_POINT) _Os;
|
||||
static float park_mode_0_height(const_float_t park_z) __Os;
|
||||
static void park(const uint8_t z_action, const xyz_pos_t &park=NOZZLE_PARK_POINT) __Os;
|
||||
|
||||
#endif
|
||||
};
|
||||
|
@@ -377,10 +377,10 @@ const char* ftostr53sign(const_float_t f) {
|
||||
return conv;
|
||||
}
|
||||
|
||||
// Convert unsigned float to string with ____4.5, __34.5, _234.5, 1234.5 format
|
||||
const char* ftostr51rj(const_float_t f) {
|
||||
// Convert unsigned float to string with ____5.6, ___45.6, __345.6, _2345.6, 12345.6 format
|
||||
const char* ftostr61rj(const_float_t f) {
|
||||
const long i = UINTFLOAT(f, 1);
|
||||
conv[0] = ' ';
|
||||
conv[0] = RJDIGIT(i, 100000);
|
||||
conv[1] = RJDIGIT(i, 10000);
|
||||
conv[2] = RJDIGIT(i, 1000);
|
||||
conv[3] = RJDIGIT(i, 100);
|
||||
|
@@ -113,8 +113,8 @@ const char* ftostr52sign(const_float_t x);
|
||||
// Convert signed float to string with +12.345 format
|
||||
const char* ftostr53sign(const_float_t f);
|
||||
|
||||
// Convert unsigned float to string with 1234.5 format omitting trailing zeros
|
||||
const char* ftostr51rj(const_float_t x);
|
||||
// Convert unsigned float to string with 12345.6 format omitting trailing zeros
|
||||
const char* ftostr61rj(const_float_t x);
|
||||
|
||||
// Convert float to rj string with 123 or -12 format
|
||||
FORCE_INLINE const char* ftostr3(const_float_t x) { return i16tostr3rj(int16_t(x + (x < 0 ? -0.5f : 0.5f))); }
|
||||
|
@@ -34,7 +34,7 @@ millis_t Stopwatch::startTimestamp;
|
||||
millis_t Stopwatch::stopTimestamp;
|
||||
|
||||
bool Stopwatch::stop() {
|
||||
Stopwatch::debug(PSTR("stop"));
|
||||
debug(F("stop"));
|
||||
|
||||
if (isRunning() || isPaused()) {
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onPrintTimerStopped());
|
||||
@@ -46,7 +46,7 @@ bool Stopwatch::stop() {
|
||||
}
|
||||
|
||||
bool Stopwatch::pause() {
|
||||
Stopwatch::debug(PSTR("pause"));
|
||||
debug(F("pause"));
|
||||
|
||||
if (isRunning()) {
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onPrintTimerPaused());
|
||||
@@ -58,7 +58,7 @@ bool Stopwatch::pause() {
|
||||
}
|
||||
|
||||
bool Stopwatch::start() {
|
||||
Stopwatch::debug(PSTR("start"));
|
||||
debug(F("start"));
|
||||
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onPrintTimerStarted());
|
||||
|
||||
@@ -74,14 +74,14 @@ bool Stopwatch::start() {
|
||||
}
|
||||
|
||||
void Stopwatch::resume(const millis_t with_time) {
|
||||
Stopwatch::debug(PSTR("resume"));
|
||||
debug(F("resume"));
|
||||
|
||||
reset();
|
||||
if ((accumulator = with_time)) state = RUNNING;
|
||||
}
|
||||
|
||||
void Stopwatch::reset() {
|
||||
Stopwatch::debug(PSTR("reset"));
|
||||
debug(F("reset"));
|
||||
|
||||
state = STOPPED;
|
||||
startTimestamp = 0;
|
||||
@@ -95,12 +95,8 @@ millis_t Stopwatch::duration() {
|
||||
|
||||
#if ENABLED(DEBUG_STOPWATCH)
|
||||
|
||||
void Stopwatch::debug(const char func[]) {
|
||||
if (DEBUGGING(INFO)) {
|
||||
SERIAL_ECHOPGM("Stopwatch::");
|
||||
SERIAL_ECHOPGM_P(func);
|
||||
SERIAL_ECHOLNPGM("()");
|
||||
}
|
||||
void Stopwatch::debug(FSTR_P const func) {
|
||||
if (DEBUGGING(INFO)) SERIAL_ECHOLNPGM("Stopwatch::", func, "()");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -21,14 +21,11 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
// Print debug messages with M111 S2 (Uses 156 bytes of PROGMEM)
|
||||
//#define DEBUG_STOPWATCH
|
||||
|
||||
#include "../core/macros.h" // for FORCE_INLINE
|
||||
|
||||
#include <stdint.h>
|
||||
typedef uint32_t millis_t;
|
||||
|
||||
/**
|
||||
* @brief Stopwatch class
|
||||
* @details This class acts as a timer proving stopwatch functionality including
|
||||
@@ -56,7 +53,7 @@ class Stopwatch {
|
||||
* @return true on success
|
||||
*/
|
||||
static bool stop();
|
||||
static inline bool abort() { return stop(); } // Alias by default
|
||||
static bool abort() { return stop(); } // Alias by default
|
||||
|
||||
/**
|
||||
* @brief Pause the stopwatch
|
||||
@@ -113,11 +110,11 @@ class Stopwatch {
|
||||
* @brief Print a debug message
|
||||
* @details Print a simple debug message "Stopwatch::function"
|
||||
*/
|
||||
static void debug(const char func[]);
|
||||
static void debug(FSTR_P const);
|
||||
|
||||
#else
|
||||
|
||||
static inline void debug(const char[]) {}
|
||||
static void debug(FSTR_P const) {}
|
||||
|
||||
#endif
|
||||
};
|
||||
|
@@ -75,8 +75,8 @@ void vector_3::apply_rotation(const matrix_3x3 &matrix) {
|
||||
matrix.vectors[0].z * _x + matrix.vectors[1].z * _y + matrix.vectors[2].z * _z };
|
||||
}
|
||||
|
||||
void vector_3::debug(PGM_P const title) {
|
||||
SERIAL_ECHOPGM_P(title);
|
||||
void vector_3::debug(FSTR_P const title) {
|
||||
SERIAL_ECHOF(title);
|
||||
SERIAL_ECHOPAIR_F_P(SP_X_STR, x, 6);
|
||||
SERIAL_ECHOPAIR_F_P(SP_Y_STR, y, 6);
|
||||
SERIAL_ECHOLNPAIR_F_P(SP_Z_STR, z, 6);
|
||||
@@ -100,14 +100,14 @@ void matrix_3x3::set_to_identity() {
|
||||
|
||||
// Create a matrix from 3 vector_3 inputs
|
||||
matrix_3x3 matrix_3x3::create_from_rows(const vector_3 &row_0, const vector_3 &row_1, const vector_3 &row_2) {
|
||||
//row_0.debug(PSTR("row_0"));
|
||||
//row_1.debug(PSTR("row_1"));
|
||||
//row_2.debug(PSTR("row_2"));
|
||||
//row_0.debug(F("row_0"));
|
||||
//row_1.debug(F("row_1"));
|
||||
//row_2.debug(F("row_2"));
|
||||
matrix_3x3 new_matrix;
|
||||
new_matrix.vectors[0] = row_0;
|
||||
new_matrix.vectors[1] = row_1;
|
||||
new_matrix.vectors[2] = row_2;
|
||||
//new_matrix.debug(PSTR("new_matrix"));
|
||||
//new_matrix.debug(F("new_matrix"));
|
||||
return new_matrix;
|
||||
}
|
||||
|
||||
@@ -117,14 +117,14 @@ matrix_3x3 matrix_3x3::create_look_at(const vector_3 &target) {
|
||||
x_row = vector_3(1, 0, -target.x / target.z).get_normal(),
|
||||
y_row = vector_3::cross(z_row, x_row).get_normal();
|
||||
|
||||
// x_row.debug(PSTR("x_row"));
|
||||
// y_row.debug(PSTR("y_row"));
|
||||
// z_row.debug(PSTR("z_row"));
|
||||
// x_row.debug(F("x_row"));
|
||||
// y_row.debug(F("y_row"));
|
||||
// z_row.debug(F("z_row"));
|
||||
|
||||
// create the matrix already correctly transposed
|
||||
matrix_3x3 rot = matrix_3x3::create_from_rows(x_row, y_row, z_row);
|
||||
|
||||
// rot.debug(PSTR("rot"));
|
||||
// rot.debug(F("rot"));
|
||||
return rot;
|
||||
}
|
||||
|
||||
@@ -137,12 +137,11 @@ matrix_3x3 matrix_3x3::transpose(const matrix_3x3 &original) {
|
||||
return new_matrix;
|
||||
}
|
||||
|
||||
void matrix_3x3::debug(PGM_P const title) {
|
||||
if (title) SERIAL_ECHOLNPGM_P(title);
|
||||
void matrix_3x3::debug(FSTR_P const title) {
|
||||
if (title) SERIAL_ECHOLNF(title);
|
||||
LOOP_L_N(i, 3) {
|
||||
LOOP_L_N(j, 3) {
|
||||
if (vectors[i][j] >= 0.0) SERIAL_CHAR('+');
|
||||
SERIAL_ECHO_F(vectors[i][j], 6);
|
||||
serial_offset(vectors[i][j], 2);
|
||||
SERIAL_CHAR(' ');
|
||||
}
|
||||
SERIAL_EOL();
|
||||
|
@@ -78,7 +78,7 @@ struct vector_3 {
|
||||
operator xy_float_t() { return xy_float_t({ x, y }); }
|
||||
operator xyz_float_t() { return xyz_float_t({ x, y, z }); }
|
||||
|
||||
void debug(PGM_P const title);
|
||||
void debug(FSTR_P const title);
|
||||
};
|
||||
|
||||
struct matrix_3x3 {
|
||||
@@ -91,7 +91,7 @@ struct matrix_3x3 {
|
||||
|
||||
void set_to_identity();
|
||||
|
||||
void debug(PGM_P const title);
|
||||
void debug(FSTR_P const title);
|
||||
|
||||
void apply_rotation_xyz(float &x, float &y, float &z);
|
||||
};
|
||||
|
Reference in New Issue
Block a user