Merge upstream changes from Marlin 2.1.1
This commit is contained in:
104
Marlin/src/feature/adc/adc_mcp3426.cpp
Normal file
104
Marlin/src/feature/adc/adc_mcp3426.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2021 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* adc_mcp3426.cpp - library for MicroChip MCP3426 I2C A/D converter
|
||||
*
|
||||
* For implementation details, please take a look at the datasheet:
|
||||
* https://www.microchip.com/en-us/product/MCP3426
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(HAS_MCP3426_ADC)
|
||||
|
||||
#include "adc_mcp3426.h"
|
||||
|
||||
// Read the ADC value from MCP342X on a specific channel
|
||||
int16_t MCP3426::ReadValue(uint8_t channel, uint8_t gain, uint8_t address) {
|
||||
Error = false;
|
||||
|
||||
#if PINS_EXIST(I2C_SCL, I2C_SDA) && DISABLED(SOFT_I2C_EEPROM)
|
||||
Wire.setSDA(pin_t(I2C_SDA_PIN));
|
||||
Wire.setSCL(pin_t(I2C_SCL_PIN));
|
||||
#endif
|
||||
|
||||
Wire.begin(); // No address joins the BUS as the master
|
||||
|
||||
Wire.beginTransmission(I2C_ADDRESS(address));
|
||||
|
||||
// Continuous Conversion Mode, 16 bit, Channel 1, Gain x4
|
||||
// 26 = 0b00011000
|
||||
// RXXCSSGG
|
||||
// R = Ready Bit
|
||||
// XX = Channel (00=1, 01=2, 10=3 (MCP3428), 11=4 (MCP3428))
|
||||
// C = Conversion Mode Bit (1= Continuous Conversion Mode (Default))
|
||||
// SS = Sample rate, 10=15 samples per second @ 16 bits
|
||||
// GG = Gain 00 =x1
|
||||
uint8_t controlRegister = 0b00011000;
|
||||
|
||||
if (channel == 2) controlRegister |= 0b00100000; // Select channel 2
|
||||
|
||||
if (gain == 2)
|
||||
controlRegister |= 0b00000001;
|
||||
else if (gain == 4)
|
||||
controlRegister |= 0b00000010;
|
||||
else if (gain == 8)
|
||||
controlRegister |= 0b00000011;
|
||||
|
||||
Wire.write(controlRegister);
|
||||
if (Wire.endTransmission() != 0) {
|
||||
Error = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const uint8_t len = 3;
|
||||
uint8_t buffer[len] = {};
|
||||
|
||||
do {
|
||||
Wire.requestFrom(I2C_ADDRESS(address), len);
|
||||
if (Wire.available() != len) {
|
||||
Error = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < len; ++i)
|
||||
buffer[i] = Wire.read();
|
||||
|
||||
// Is conversion ready, if not loop around again
|
||||
} while ((buffer[2] & 0x80) != 0);
|
||||
|
||||
union TwoBytesToInt16 {
|
||||
uint8_t bytes[2];
|
||||
int16_t integervalue;
|
||||
};
|
||||
TwoBytesToInt16 ConversionUnion;
|
||||
|
||||
ConversionUnion.bytes[1] = buffer[0];
|
||||
ConversionUnion.bytes[0] = buffer[1];
|
||||
|
||||
return ConversionUnion.integervalue;
|
||||
}
|
||||
|
||||
MCP3426 mcp3426;
|
||||
|
||||
#endif // HAS_MCP3426_ADC
|
38
Marlin/src/feature/adc/adc_mcp3426.h
Normal file
38
Marlin/src/feature/adc/adc_mcp3426.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2021 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
|
||||
|
||||
/**
|
||||
* Arduino library for MicroChip MCP3426 I2C A/D converter.
|
||||
* https://www.microchip.com/en-us/product/MCP3426
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <Wire.h>
|
||||
|
||||
class MCP3426 {
|
||||
public:
|
||||
int16_t ReadValue(uint8_t channel, uint8_t gain, uint8_t address);
|
||||
bool Error;
|
||||
};
|
||||
|
||||
extern MCP3426 mcp3426;
|
@@ -54,6 +54,18 @@ void Babystep::add_mm(const AxisEnum axis, const_float_t mm) {
|
||||
add_steps(axis, mm * planner.settings.axis_steps_per_mm[axis]);
|
||||
}
|
||||
|
||||
#if ENABLED(BD_SENSOR)
|
||||
void Babystep::set_mm(const AxisEnum axis, const_float_t mm) {
|
||||
//if (DISABLED(BABYSTEP_WITHOUT_HOMING) && axes_should_home(_BV(axis))) return;
|
||||
const int16_t distance = mm * planner.settings.axis_steps_per_mm[axis];
|
||||
accum = distance; // Count up babysteps for the UI
|
||||
steps[BS_AXIS_IND(axis)] = distance;
|
||||
TERN_(BABYSTEP_DISPLAY_TOTAL, axis_total[BS_TOTAL_IND(axis)] = distance);
|
||||
TERN_(BABYSTEP_ALWAYS_AVAILABLE, gcode.reset_stepper_timeout());
|
||||
TERN_(INTEGRATED_BABYSTEPPING, if (has_steps()) stepper.initiateBabystepping());
|
||||
}
|
||||
#endif
|
||||
|
||||
void Babystep::add_steps(const AxisEnum axis, const int16_t distance) {
|
||||
if (DISABLED(BABYSTEP_WITHOUT_HOMING) && axes_should_home(_BV(axis))) return;
|
||||
|
||||
|
@@ -54,7 +54,7 @@ public:
|
||||
|
||||
#if ENABLED(BABYSTEP_DISPLAY_TOTAL)
|
||||
static int16_t axis_total[BS_TOTAL_IND(Z_AXIS) + 1]; // Total babysteps since G28
|
||||
static inline void reset_total(const AxisEnum axis) {
|
||||
static void reset_total(const AxisEnum axis) {
|
||||
if (TERN1(BABYSTEP_XY, axis == Z_AXIS))
|
||||
axis_total[BS_TOTAL_IND(axis)] = 0;
|
||||
}
|
||||
@@ -63,7 +63,11 @@ public:
|
||||
static void add_steps(const AxisEnum axis, const int16_t distance);
|
||||
static void add_mm(const AxisEnum axis, const_float_t mm);
|
||||
|
||||
static inline bool has_steps() {
|
||||
#if ENABLED(BD_SENSOR)
|
||||
static void set_mm(const AxisEnum axis, const_float_t mm);
|
||||
#endif
|
||||
|
||||
static bool has_steps() {
|
||||
return steps[BS_AXIS_IND(X_AXIS)] || steps[BS_AXIS_IND(Y_AXIS)] || steps[BS_AXIS_IND(Z_AXIS)];
|
||||
}
|
||||
|
||||
@@ -71,7 +75,7 @@ public:
|
||||
// Called by the Temperature or Stepper ISR to
|
||||
// apply accumulated babysteps to the axes.
|
||||
//
|
||||
static inline void task() {
|
||||
static void task() {
|
||||
LOOP_LE_N(i, BS_AXIS_IND(Z_AXIS)) step_axis(BS_AXIS(i));
|
||||
}
|
||||
|
||||
|
@@ -29,6 +29,9 @@
|
||||
#include "../module/motion.h"
|
||||
#include "../module/planner.h"
|
||||
|
||||
axis_bits_t Backlash::last_direction_bits;
|
||||
xyz_long_t Backlash::residual_error{0};
|
||||
|
||||
#ifdef BACKLASH_DISTANCE_MM
|
||||
#if ENABLED(BACKLASH_GCODE)
|
||||
xyz_float_t Backlash::distance_mm = BACKLASH_DISTANCE_MM;
|
||||
@@ -38,7 +41,7 @@
|
||||
#endif
|
||||
|
||||
#if ENABLED(BACKLASH_GCODE)
|
||||
uint8_t Backlash::correction = (BACKLASH_CORRECTION) * 0xFF;
|
||||
uint8_t Backlash::correction = (BACKLASH_CORRECTION) * all_on;
|
||||
#ifdef BACKLASH_SMOOTHING_MM
|
||||
float Backlash::smoothing_mm = BACKLASH_SMOOTHING_MM;
|
||||
#endif
|
||||
@@ -61,10 +64,9 @@ Backlash backlash;
|
||||
*/
|
||||
|
||||
void Backlash::add_correction_steps(const int32_t &da, const int32_t &db, const int32_t &dc, const axis_bits_t dm, block_t * const block) {
|
||||
static axis_bits_t last_direction_bits;
|
||||
axis_bits_t changed_dir = last_direction_bits ^ dm;
|
||||
// Ignore direction change unless steps are taken in that direction
|
||||
#if DISABLED(CORE_BACKLASH) || ENABLED(MARKFORGED_XY)
|
||||
#if DISABLED(CORE_BACKLASH) || EITHER(MARKFORGED_XY, MARKFORGED_YX)
|
||||
if (!da) CBI(changed_dir, X_AXIS);
|
||||
if (!db) CBI(changed_dir, Y_AXIS);
|
||||
if (!dc) CBI(changed_dir, Z_AXIS);
|
||||
@@ -83,7 +85,7 @@ void Backlash::add_correction_steps(const int32_t &da, const int32_t &db, const
|
||||
#endif
|
||||
last_direction_bits ^= changed_dir;
|
||||
|
||||
if (correction == 0) return;
|
||||
if (!correction && !residual_error) return;
|
||||
|
||||
#ifdef BACKLASH_SMOOTHING_MM
|
||||
// The segment proportion is a value greater than 0.0 indicating how much residual_error
|
||||
@@ -91,39 +93,28 @@ void Backlash::add_correction_steps(const int32_t &da, const int32_t &db, const
|
||||
// smoothing distance. Since the computation of this proportion involves a floating point
|
||||
// division, defer computation until needed.
|
||||
float segment_proportion = 0;
|
||||
|
||||
// Residual error carried forward across multiple segments, so correction can be applied
|
||||
// to segments where there is no direction change.
|
||||
static xyz_long_t residual_error{0};
|
||||
#else
|
||||
// No direction change, no correction.
|
||||
if (!changed_dir) return;
|
||||
// No leftover residual error from segment to segment
|
||||
xyz_long_t residual_error{0};
|
||||
#endif
|
||||
|
||||
const float f_corr = float(correction) / 255.0f;
|
||||
const float f_corr = float(correction) / all_on;
|
||||
|
||||
LOOP_LINEAR_AXES(axis) {
|
||||
LOOP_NUM_AXES(axis) {
|
||||
if (distance_mm[axis]) {
|
||||
const bool reversing = TEST(dm,axis);
|
||||
const bool reverse = TEST(dm, axis);
|
||||
|
||||
// When an axis changes direction, add axis backlash to the residual error
|
||||
if (TEST(changed_dir, axis))
|
||||
residual_error[axis] += (reversing ? -f_corr : f_corr) * distance_mm[axis] * planner.settings.axis_steps_per_mm[axis];
|
||||
residual_error[axis] += (reverse ? -f_corr : f_corr) * distance_mm[axis] * planner.settings.axis_steps_per_mm[axis];
|
||||
|
||||
// Decide how much of the residual error to correct in this segment
|
||||
int32_t error_correction = residual_error[axis];
|
||||
if (reverse != (error_correction < 0))
|
||||
error_correction = 0; // Don't take up any backlash in this segment, as it would subtract steps
|
||||
|
||||
#ifdef BACKLASH_SMOOTHING_MM
|
||||
if (error_correction && smoothing_mm != 0) {
|
||||
// Take up a portion of the residual_error in this segment, but only when
|
||||
// the current segment travels in the same direction as the correction
|
||||
if (reversing == (error_correction < 0)) {
|
||||
if (segment_proportion == 0) segment_proportion = _MIN(1.0f, block->millimeters / smoothing_mm);
|
||||
error_correction = CEIL(segment_proportion * error_correction);
|
||||
}
|
||||
else
|
||||
error_correction = 0; // Don't take up any backlash in this segment, as it would subtract steps
|
||||
// Take up a portion of the residual_error in this segment
|
||||
if (segment_proportion == 0) segment_proportion = _MIN(1.0f, block->millimeters / smoothing_mm);
|
||||
error_correction = CEIL(segment_proportion * error_correction);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -153,6 +144,57 @@ void Backlash::add_correction_steps(const int32_t &da, const int32_t &db, const
|
||||
}
|
||||
}
|
||||
|
||||
int32_t Backlash::get_applied_steps(const AxisEnum axis) {
|
||||
if (axis >= NUM_AXES) return 0;
|
||||
|
||||
const bool reverse = TEST(last_direction_bits, axis);
|
||||
|
||||
const int32_t residual_error_axis = residual_error[axis];
|
||||
|
||||
// At startup it is assumed the last move was forwards. So the applied
|
||||
// steps will always be a non-positive number.
|
||||
|
||||
if (!reverse) return -residual_error_axis;
|
||||
|
||||
const float f_corr = float(correction) / all_on;
|
||||
const int32_t full_error_axis = -f_corr * distance_mm[axis] * planner.settings.axis_steps_per_mm[axis];
|
||||
return full_error_axis - residual_error_axis;
|
||||
}
|
||||
|
||||
class Backlash::StepAdjuster {
|
||||
private:
|
||||
xyz_long_t applied_steps;
|
||||
public:
|
||||
StepAdjuster() {
|
||||
LOOP_NUM_AXES(axis) applied_steps[axis] = backlash.get_applied_steps((AxisEnum)axis);
|
||||
}
|
||||
~StepAdjuster() {
|
||||
// after backlash compensation parameter changes, ensure applied step count does not change
|
||||
LOOP_NUM_AXES(axis) residual_error[axis] += backlash.get_applied_steps((AxisEnum)axis) - applied_steps[axis];
|
||||
}
|
||||
};
|
||||
|
||||
#if ENABLED(BACKLASH_GCODE)
|
||||
|
||||
void Backlash::set_correction_uint8(const uint8_t v) {
|
||||
StepAdjuster adjuster;
|
||||
correction = v;
|
||||
}
|
||||
|
||||
void Backlash::set_distance_mm(const AxisEnum axis, const float v) {
|
||||
StepAdjuster adjuster;
|
||||
distance_mm[axis] = v;
|
||||
}
|
||||
|
||||
#ifdef BACKLASH_SMOOTHING_MM
|
||||
void Backlash::set_smoothing_mm(const float v) {
|
||||
StepAdjuster adjuster;
|
||||
smoothing_mm = v;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if ENABLED(MEASURE_BACKLASH_WHEN_PROBING)
|
||||
|
||||
#include "../module/probe.h"
|
||||
|
@@ -24,21 +24,22 @@
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
#include "../module/planner.h"
|
||||
|
||||
constexpr uint8_t all_on = 0xFF, all_off = 0x00;
|
||||
|
||||
class Backlash {
|
||||
public:
|
||||
static constexpr uint8_t all_on = 0xFF, all_off = 0x00;
|
||||
|
||||
private:
|
||||
static axis_bits_t last_direction_bits;
|
||||
static xyz_long_t residual_error;
|
||||
|
||||
#if ENABLED(BACKLASH_GCODE)
|
||||
static xyz_float_t distance_mm;
|
||||
static uint8_t correction;
|
||||
static xyz_float_t distance_mm;
|
||||
#ifdef BACKLASH_SMOOTHING_MM
|
||||
static float smoothing_mm;
|
||||
#endif
|
||||
|
||||
static inline void set_correction(const_float_t v) { correction = _MAX(0, _MIN(1.0, v)) * all_on; }
|
||||
static inline float get_correction() { return float(ui8_to_percent(correction)) / 100.0f; }
|
||||
#else
|
||||
static constexpr uint8_t correction = (BACKLASH_CORRECTION) * 0xFF;
|
||||
static constexpr uint8_t correction = (BACKLASH_CORRECTION) * all_on;
|
||||
static const xyz_float_t distance_mm;
|
||||
#ifdef BACKLASH_SMOOTHING_MM
|
||||
static constexpr float smoothing_mm = BACKLASH_SMOOTHING_MM;
|
||||
@@ -46,14 +47,14 @@ public:
|
||||
#endif
|
||||
|
||||
#if ENABLED(MEASURE_BACKLASH_WHEN_PROBING)
|
||||
private:
|
||||
static xyz_float_t measured_mm;
|
||||
static xyz_uint8_t measured_count;
|
||||
public:
|
||||
static void measure_with_probe();
|
||||
static xyz_float_t measured_mm;
|
||||
static xyz_uint8_t measured_count;
|
||||
#endif
|
||||
|
||||
static inline float get_measurement(const AxisEnum a) {
|
||||
class StepAdjuster;
|
||||
|
||||
public:
|
||||
static float get_measurement(const AxisEnum a) {
|
||||
UNUSED(a);
|
||||
// Return the measurement averaged over all readings
|
||||
return TERN(MEASURE_BACKLASH_WHEN_PROBING
|
||||
@@ -62,16 +63,34 @@ public:
|
||||
);
|
||||
}
|
||||
|
||||
static inline bool has_measurement(const AxisEnum a) {
|
||||
static bool has_measurement(const AxisEnum a) {
|
||||
UNUSED(a);
|
||||
return TERN0(MEASURE_BACKLASH_WHEN_PROBING, measured_count[a] > 0);
|
||||
}
|
||||
|
||||
static inline bool has_any_measurement() {
|
||||
static bool has_any_measurement() {
|
||||
return has_measurement(X_AXIS) || has_measurement(Y_AXIS) || has_measurement(Z_AXIS);
|
||||
}
|
||||
|
||||
void add_correction_steps(const int32_t &da, const int32_t &db, const int32_t &dc, const axis_bits_t dm, block_t * const block);
|
||||
static void add_correction_steps(const int32_t &da, const int32_t &db, const int32_t &dc, const axis_bits_t dm, block_t * const block);
|
||||
static int32_t get_applied_steps(const AxisEnum axis);
|
||||
|
||||
#if ENABLED(BACKLASH_GCODE)
|
||||
static void set_correction_uint8(const uint8_t v);
|
||||
static uint8_t get_correction_uint8() { return correction; }
|
||||
static void set_correction(const float v) { set_correction_uint8(_MAX(0, _MIN(1.0, v)) * all_on + 0.5f); }
|
||||
static float get_correction() { return float(get_correction_uint8()) / all_on; }
|
||||
static void set_distance_mm(const AxisEnum axis, const float v);
|
||||
static float get_distance_mm(const AxisEnum axis) {return distance_mm[axis];}
|
||||
#ifdef BACKLASH_SMOOTHING_MM
|
||||
static void set_smoothing_mm(const float v);
|
||||
static float get_smoothing_mm() {return smoothing_mm;}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if ENABLED(MEASURE_BACKLASH_WHEN_PROBING)
|
||||
static void measure_with_probe();
|
||||
#endif
|
||||
};
|
||||
|
||||
extern Backlash backlash;
|
||||
|
@@ -35,14 +35,19 @@
|
||||
#include "../../../lcd/extui/ui_api.h"
|
||||
#endif
|
||||
|
||||
xy_pos_t bilinear_grid_spacing, bilinear_start;
|
||||
xy_float_t bilinear_grid_factor;
|
||||
bed_mesh_t z_values;
|
||||
LevelingBilinear bedlevel;
|
||||
|
||||
xy_pos_t LevelingBilinear::grid_spacing,
|
||||
LevelingBilinear::grid_start;
|
||||
xy_float_t LevelingBilinear::grid_factor;
|
||||
bed_mesh_t LevelingBilinear::z_values;
|
||||
xy_pos_t LevelingBilinear::cached_rel;
|
||||
xy_int8_t LevelingBilinear::cached_g;
|
||||
|
||||
/**
|
||||
* Extrapolate a single point from its neighbors
|
||||
*/
|
||||
static void extrapolate_one_point(const uint8_t x, const uint8_t y, const int8_t xdir, const int8_t ydir) {
|
||||
void LevelingBilinear::extrapolate_one_point(const uint8_t x, const uint8_t y, const int8_t xdir, const int8_t ydir) {
|
||||
if (!isnan(z_values[x][y])) return;
|
||||
if (DEBUGGING(LEVELING)) {
|
||||
DEBUG_ECHOPGM("Extrapolate [");
|
||||
@@ -92,11 +97,26 @@ static void extrapolate_one_point(const uint8_t x, const uint8_t y, const int8_t
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void LevelingBilinear::reset() {
|
||||
grid_start.reset();
|
||||
grid_spacing.reset();
|
||||
GRID_LOOP(x, y) {
|
||||
z_values[x][y] = NAN;
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(x, y, 0));
|
||||
}
|
||||
}
|
||||
|
||||
void LevelingBilinear::set_grid(const xy_pos_t& _grid_spacing, const xy_pos_t& _grid_start) {
|
||||
grid_spacing = _grid_spacing;
|
||||
grid_start = _grid_start;
|
||||
grid_factor = grid_spacing.reciprocal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill in the unprobed points (corners of circular print surface)
|
||||
* using linear extrapolation, away from the center.
|
||||
*/
|
||||
void extrapolate_unprobed_bed_level() {
|
||||
void LevelingBilinear::extrapolate_unprobed_bed_level() {
|
||||
#ifdef HALF_IN_X
|
||||
constexpr uint8_t ctrx2 = 0, xend = GRID_MAX_POINTS_X - 1;
|
||||
#else
|
||||
@@ -131,35 +151,31 @@ void extrapolate_unprobed_bed_level() {
|
||||
#endif
|
||||
extrapolate_one_point(x2, y2, -1, -1); // right-above - -
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void print_bilinear_leveling_grid() {
|
||||
void LevelingBilinear::print_leveling_grid(const bed_mesh_t* _z_values /*= NULL*/) {
|
||||
// print internal grid(s) or just the one passed as a parameter
|
||||
SERIAL_ECHOLNPGM("Bilinear Leveling Grid:");
|
||||
print_2d_array(GRID_MAX_POINTS_X, GRID_MAX_POINTS_Y, 3,
|
||||
[](const uint8_t ix, const uint8_t iy) { return z_values[ix][iy]; }
|
||||
);
|
||||
print_2d_array(GRID_MAX_POINTS_X, GRID_MAX_POINTS_Y, 3, _z_values ? *_z_values[0] : z_values[0]);
|
||||
|
||||
#if ENABLED(ABL_BILINEAR_SUBDIVISION)
|
||||
if (!_z_values) {
|
||||
SERIAL_ECHOLNPGM("Subdivided with CATMULL ROM Leveling Grid:");
|
||||
print_2d_array(ABL_GRID_POINTS_VIRT_X, ABL_GRID_POINTS_VIRT_Y, 5, z_values_virt[0]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ENABLED(ABL_BILINEAR_SUBDIVISION)
|
||||
|
||||
#define ABL_GRID_POINTS_VIRT_X GRID_MAX_CELLS_X * (BILINEAR_SUBDIVISIONS) + 1
|
||||
#define ABL_GRID_POINTS_VIRT_Y GRID_MAX_CELLS_Y * (BILINEAR_SUBDIVISIONS) + 1
|
||||
#define ABL_TEMP_POINTS_X (GRID_MAX_POINTS_X + 2)
|
||||
#define ABL_TEMP_POINTS_Y (GRID_MAX_POINTS_Y + 2)
|
||||
float z_values_virt[ABL_GRID_POINTS_VIRT_X][ABL_GRID_POINTS_VIRT_Y];
|
||||
xy_pos_t bilinear_grid_spacing_virt;
|
||||
xy_float_t bilinear_grid_factor_virt;
|
||||
|
||||
void print_bilinear_leveling_grid_virt() {
|
||||
SERIAL_ECHOLNPGM("Subdivided with CATMULL ROM Leveling Grid:");
|
||||
print_2d_array(ABL_GRID_POINTS_VIRT_X, ABL_GRID_POINTS_VIRT_Y, 5,
|
||||
[](const uint8_t ix, const uint8_t iy) { return z_values_virt[ix][iy]; }
|
||||
);
|
||||
}
|
||||
float LevelingBilinear::z_values_virt[ABL_GRID_POINTS_VIRT_X][ABL_GRID_POINTS_VIRT_Y];
|
||||
xy_pos_t LevelingBilinear::grid_spacing_virt;
|
||||
xy_float_t LevelingBilinear::grid_factor_virt;
|
||||
|
||||
#define LINEAR_EXTRAPOLATION(E, I) ((E) * 2 - (I))
|
||||
float bed_level_virt_coord(const uint8_t x, const uint8_t y) {
|
||||
float LevelingBilinear::bed_level_virt_coord(const uint8_t x, const uint8_t y) {
|
||||
uint8_t ep = 0, ip = 1;
|
||||
if (x > (GRID_MAX_POINTS_X) + 1 || y > (GRID_MAX_POINTS_Y) + 1) {
|
||||
// The requested point requires extrapolating two points beyond the mesh.
|
||||
@@ -204,7 +220,7 @@ void print_bilinear_leveling_grid() {
|
||||
return z_values[x - 1][y - 1];
|
||||
}
|
||||
|
||||
static float bed_level_virt_cmr(const float p[4], const uint8_t i, const float t) {
|
||||
float LevelingBilinear::bed_level_virt_cmr(const float p[4], const uint8_t i, const float t) {
|
||||
return (
|
||||
p[i-1] * -t * sq(1 - t)
|
||||
+ p[i] * (2 - 5 * sq(t) + 3 * t * sq(t))
|
||||
@@ -213,7 +229,7 @@ void print_bilinear_leveling_grid() {
|
||||
) * 0.5f;
|
||||
}
|
||||
|
||||
static float bed_level_virt_2cmr(const uint8_t x, const uint8_t y, const_float_t tx, const_float_t ty) {
|
||||
float LevelingBilinear::bed_level_virt_2cmr(const uint8_t x, const uint8_t y, const_float_t tx, const_float_t ty) {
|
||||
float row[4], column[4];
|
||||
LOOP_L_N(i, 4) {
|
||||
LOOP_L_N(j, 4) {
|
||||
@@ -224,9 +240,9 @@ void print_bilinear_leveling_grid() {
|
||||
return bed_level_virt_cmr(row, 1, tx);
|
||||
}
|
||||
|
||||
void bed_level_virt_interpolate() {
|
||||
bilinear_grid_spacing_virt = bilinear_grid_spacing / (BILINEAR_SUBDIVISIONS);
|
||||
bilinear_grid_factor_virt = bilinear_grid_spacing_virt.reciprocal();
|
||||
void LevelingBilinear::bed_level_virt_interpolate() {
|
||||
grid_spacing_virt = grid_spacing / (BILINEAR_SUBDIVISIONS);
|
||||
grid_factor_virt = grid_spacing_virt.reciprocal();
|
||||
LOOP_L_N(y, GRID_MAX_POINTS_Y)
|
||||
LOOP_L_N(x, GRID_MAX_POINTS_X)
|
||||
LOOP_L_N(ty, BILINEAR_SUBDIVISIONS)
|
||||
@@ -242,40 +258,42 @@ void print_bilinear_leveling_grid() {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ABL_BILINEAR_SUBDIVISION
|
||||
|
||||
// Refresh after other values have been updated
|
||||
void refresh_bed_level() {
|
||||
bilinear_grid_factor = bilinear_grid_spacing.reciprocal();
|
||||
void LevelingBilinear::refresh_bed_level() {
|
||||
TERN_(ABL_BILINEAR_SUBDIVISION, bed_level_virt_interpolate());
|
||||
cached_rel.x = cached_rel.y = -999.999;
|
||||
cached_g.x = cached_g.y = -99;
|
||||
}
|
||||
|
||||
#if ENABLED(ABL_BILINEAR_SUBDIVISION)
|
||||
#define ABL_BG_SPACING(A) bilinear_grid_spacing_virt.A
|
||||
#define ABL_BG_FACTOR(A) bilinear_grid_factor_virt.A
|
||||
#define ABL_BG_SPACING(A) grid_spacing_virt.A
|
||||
#define ABL_BG_FACTOR(A) grid_factor_virt.A
|
||||
#define ABL_BG_POINTS_X ABL_GRID_POINTS_VIRT_X
|
||||
#define ABL_BG_POINTS_Y ABL_GRID_POINTS_VIRT_Y
|
||||
#define ABL_BG_GRID(X,Y) z_values_virt[X][Y]
|
||||
#else
|
||||
#define ABL_BG_SPACING(A) bilinear_grid_spacing.A
|
||||
#define ABL_BG_FACTOR(A) bilinear_grid_factor.A
|
||||
#define ABL_BG_SPACING(A) grid_spacing.A
|
||||
#define ABL_BG_FACTOR(A) grid_factor.A
|
||||
#define ABL_BG_POINTS_X GRID_MAX_POINTS_X
|
||||
#define ABL_BG_POINTS_Y GRID_MAX_POINTS_Y
|
||||
#define ABL_BG_GRID(X,Y) z_values[X][Y]
|
||||
#endif
|
||||
|
||||
// Get the Z adjustment for non-linear bed leveling
|
||||
float bilinear_z_offset(const xy_pos_t &raw) {
|
||||
float LevelingBilinear::get_z_correction(const xy_pos_t &raw) {
|
||||
|
||||
static float z1, d2, z3, d4, L, D;
|
||||
|
||||
static xy_pos_t prev { -999.999, -999.999 }, ratio;
|
||||
static xy_pos_t ratio;
|
||||
|
||||
// Whole units for the grid line indices. Constrained within bounds.
|
||||
static xy_int8_t thisg, nextg, lastg { -99, -99 };
|
||||
static xy_int8_t thisg, nextg;
|
||||
|
||||
// XY relative to the probed area
|
||||
xy_pos_t rel = raw - bilinear_start.asFloat();
|
||||
xy_pos_t rel = raw - grid_start.asFloat();
|
||||
|
||||
#if ENABLED(EXTRAPOLATE_BEYOND_GRID)
|
||||
#define FAR_EDGE_OR_BOX 2 // Keep using the last grid box
|
||||
@@ -283,8 +301,8 @@ float bilinear_z_offset(const xy_pos_t &raw) {
|
||||
#define FAR_EDGE_OR_BOX 1 // Just use the grid far edge
|
||||
#endif
|
||||
|
||||
if (prev.x != rel.x) {
|
||||
prev.x = rel.x;
|
||||
if (cached_rel.x != rel.x) {
|
||||
cached_rel.x = rel.x;
|
||||
ratio.x = rel.x * ABL_BG_FACTOR(x);
|
||||
const float gx = constrain(FLOOR(ratio.x), 0, ABL_BG_POINTS_X - (FAR_EDGE_OR_BOX));
|
||||
ratio.x -= gx; // Subtract whole to get the ratio within the grid box
|
||||
@@ -298,10 +316,10 @@ float bilinear_z_offset(const xy_pos_t &raw) {
|
||||
nextg.x = _MIN(thisg.x + 1, ABL_BG_POINTS_X - 1);
|
||||
}
|
||||
|
||||
if (prev.y != rel.y || lastg.x != thisg.x) {
|
||||
if (cached_rel.y != rel.y || cached_g.x != thisg.x) {
|
||||
|
||||
if (prev.y != rel.y) {
|
||||
prev.y = rel.y;
|
||||
if (cached_rel.y != rel.y) {
|
||||
cached_rel.y = rel.y;
|
||||
ratio.y = rel.y * ABL_BG_FACTOR(y);
|
||||
const float gy = constrain(FLOOR(ratio.y), 0, ABL_BG_POINTS_Y - (FAR_EDGE_OR_BOX));
|
||||
ratio.y -= gy;
|
||||
@@ -315,8 +333,8 @@ float bilinear_z_offset(const xy_pos_t &raw) {
|
||||
nextg.y = _MIN(thisg.y + 1, ABL_BG_POINTS_Y - 1);
|
||||
}
|
||||
|
||||
if (lastg != thisg) {
|
||||
lastg = thisg;
|
||||
if (cached_g != thisg) {
|
||||
cached_g = thisg;
|
||||
// Z at the box corners
|
||||
z1 = ABL_BG_GRID(thisg.x, thisg.y); // left-front
|
||||
d2 = ABL_BG_GRID(thisg.x, nextg.y) - z1; // left-back (delta)
|
||||
@@ -336,8 +354,8 @@ float bilinear_z_offset(const xy_pos_t &raw) {
|
||||
/*
|
||||
static float last_offset = 0;
|
||||
if (ABS(last_offset - offset) > 0.2) {
|
||||
SERIAL_ECHOLNPGM("Sudden Shift at x=", rel.x, " / ", bilinear_grid_spacing.x, " -> thisg.x=", thisg.x);
|
||||
SERIAL_ECHOLNPGM(" y=", rel.y, " / ", bilinear_grid_spacing.y, " -> thisg.y=", thisg.y);
|
||||
SERIAL_ECHOLNPGM("Sudden Shift at x=", rel.x, " / ", grid_spacing.x, " -> thisg.x=", thisg.x);
|
||||
SERIAL_ECHOLNPGM(" y=", rel.y, " / ", grid_spacing.y, " -> thisg.y=", thisg.y);
|
||||
SERIAL_ECHOLNPGM(" ratio.x=", ratio.x, " ratio.y=", ratio.y);
|
||||
SERIAL_ECHOLNPGM(" z1=", z1, " z2=", z2, " z3=", z3, " z4=", z4);
|
||||
SERIAL_ECHOLNPGM(" L=", L, " R=", R, " offset=", offset);
|
||||
@@ -350,13 +368,13 @@ float bilinear_z_offset(const xy_pos_t &raw) {
|
||||
|
||||
#if IS_CARTESIAN && DISABLED(SEGMENT_LEVELED_MOVES)
|
||||
|
||||
#define CELL_INDEX(A,V) ((V - bilinear_start.A) * ABL_BG_FACTOR(A))
|
||||
#define CELL_INDEX(A,V) ((V - grid_start.A) * ABL_BG_FACTOR(A))
|
||||
|
||||
/**
|
||||
* Prepare a bilinear-leveled linear move on Cartesian,
|
||||
* splitting the move where it crosses grid borders.
|
||||
*/
|
||||
void bilinear_line_to_destination(const_feedRate_t scaled_fr_mm_s, uint16_t x_splits, uint16_t y_splits) {
|
||||
void LevelingBilinear::line_to_destination(const_feedRate_t scaled_fr_mm_s, uint16_t x_splits, uint16_t y_splits) {
|
||||
// Get current and destination cells for this line
|
||||
xy_int_t c1 { CELL_INDEX(x, current_position.x), CELL_INDEX(y, current_position.y) },
|
||||
c2 { CELL_INDEX(x, destination.x), CELL_INDEX(y, destination.y) };
|
||||
@@ -384,7 +402,7 @@ float bilinear_z_offset(const xy_pos_t &raw) {
|
||||
// Split on the X grid line
|
||||
CBI(x_splits, gc.x);
|
||||
end = destination;
|
||||
destination.x = bilinear_start.x + ABL_BG_SPACING(x) * gc.x;
|
||||
destination.x = grid_start.x + ABL_BG_SPACING(x) * gc.x;
|
||||
normalized_dist = (destination.x - current_position.x) / (end.x - current_position.x);
|
||||
destination.y = LINE_SEGMENT_END(y);
|
||||
}
|
||||
@@ -393,7 +411,7 @@ float bilinear_z_offset(const xy_pos_t &raw) {
|
||||
// Split on the Y grid line
|
||||
CBI(y_splits, gc.y);
|
||||
end = destination;
|
||||
destination.y = bilinear_start.y + ABL_BG_SPACING(y) * gc.y;
|
||||
destination.y = grid_start.y + ABL_BG_SPACING(y) * gc.y;
|
||||
normalized_dist = (destination.y - current_position.y) / (end.y - current_position.y);
|
||||
destination.x = LINE_SEGMENT_END(x);
|
||||
}
|
||||
@@ -409,11 +427,11 @@ float bilinear_z_offset(const xy_pos_t &raw) {
|
||||
destination.e = LINE_SEGMENT_END(e);
|
||||
|
||||
// Do the split and look for more borders
|
||||
bilinear_line_to_destination(scaled_fr_mm_s, x_splits, y_splits);
|
||||
line_to_destination(scaled_fr_mm_s, x_splits, y_splits);
|
||||
|
||||
// Restore destination from stack
|
||||
destination = end;
|
||||
bilinear_line_to_destination(scaled_fr_mm_s, x_splits, y_splits);
|
||||
line_to_destination(scaled_fr_mm_s, x_splits, y_splits);
|
||||
}
|
||||
|
||||
#endif // IS_CARTESIAN && !SEGMENT_LEVELED_MOVES
|
70
Marlin/src/feature/bedlevel/abl/bbl.h
Normal file
70
Marlin/src/feature/bedlevel/abl/bbl.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* 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/MarlinConfigPre.h"
|
||||
|
||||
class LevelingBilinear {
|
||||
public:
|
||||
static bed_mesh_t z_values;
|
||||
static xy_pos_t grid_spacing, grid_start;
|
||||
|
||||
private:
|
||||
static xy_float_t grid_factor;
|
||||
static xy_pos_t cached_rel;
|
||||
static xy_int8_t cached_g;
|
||||
|
||||
static void extrapolate_one_point(const uint8_t x, const uint8_t y, const int8_t xdir, const int8_t ydir);
|
||||
|
||||
#if ENABLED(ABL_BILINEAR_SUBDIVISION)
|
||||
#define ABL_GRID_POINTS_VIRT_X (GRID_MAX_CELLS_X * (BILINEAR_SUBDIVISIONS) + 1)
|
||||
#define ABL_GRID_POINTS_VIRT_Y (GRID_MAX_CELLS_Y * (BILINEAR_SUBDIVISIONS) + 1)
|
||||
|
||||
static float z_values_virt[ABL_GRID_POINTS_VIRT_X][ABL_GRID_POINTS_VIRT_Y];
|
||||
static xy_pos_t grid_spacing_virt;
|
||||
static xy_float_t grid_factor_virt;
|
||||
|
||||
static float bed_level_virt_coord(const uint8_t x, const uint8_t y);
|
||||
static float bed_level_virt_cmr(const float p[4], const uint8_t i, const float t);
|
||||
static float bed_level_virt_2cmr(const uint8_t x, const uint8_t y, const_float_t tx, const_float_t ty);
|
||||
static void bed_level_virt_interpolate();
|
||||
#endif
|
||||
|
||||
public:
|
||||
static void reset();
|
||||
static void set_grid(const xy_pos_t& _grid_spacing, const xy_pos_t& _grid_start);
|
||||
static void extrapolate_unprobed_bed_level();
|
||||
static void print_leveling_grid(const bed_mesh_t* _z_values = NULL);
|
||||
static void refresh_bed_level();
|
||||
static bool has_mesh() { return !!grid_spacing.x; }
|
||||
static bool mesh_is_valid() { return has_mesh(); }
|
||||
static float get_mesh_x(const uint8_t i) { return grid_start.x + i * grid_spacing.x; }
|
||||
static float get_mesh_y(const uint8_t j) { return grid_start.y + j * grid_spacing.y; }
|
||||
static float get_z_correction(const xy_pos_t &raw);
|
||||
static constexpr float get_z_offset() { return 0.0f; }
|
||||
|
||||
#if IS_CARTESIAN && DISABLED(SEGMENT_LEVELED_MOVES)
|
||||
static void line_to_destination(const_feedRate_t scaled_fr_mm_s, uint16_t x_splits=0xFFFF, uint16_t y_splits=0xFFFF);
|
||||
#endif
|
||||
};
|
||||
|
||||
extern LevelingBilinear bedlevel;
|
195
Marlin/src/feature/bedlevel/bdl/bdl.cpp
Normal file
195
Marlin/src/feature/bedlevel/bdl/bdl.cpp
Normal file
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2022 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(BD_SENSOR)
|
||||
|
||||
#include "../../../MarlinCore.h"
|
||||
#include "../../../gcode/gcode.h"
|
||||
#include "../../../module/settings.h"
|
||||
#include "../../../module/motion.h"
|
||||
#include "../../../module/planner.h"
|
||||
#include "../../../module/stepper.h"
|
||||
#include "../../../module/probe.h"
|
||||
#include "../../../module/temperature.h"
|
||||
#include "../../../module/endstops.h"
|
||||
#include "../../babystep.h"
|
||||
|
||||
// I2C software Master library for segment bed heating and bed distance sensor
|
||||
#include <Panda_segmentBed_I2C.h>
|
||||
|
||||
#include "bdl.h"
|
||||
BDS_Leveling bdl;
|
||||
|
||||
//#define DEBUG_OUT_BD
|
||||
|
||||
// M102 S-5 Read raw Calibrate data
|
||||
// M102 S-6 Start Calibrate
|
||||
// M102 S4 Set the adjustable Z height value (e.g., 'M102 S4' means it will do adjusting while the Z height <= 0.4mm , disable with 'M102 S0'.)
|
||||
// M102 S-1 Read sensor information
|
||||
|
||||
#define MAX_BD_HEIGHT 4.0f
|
||||
#define CMD_START_READ_CALIBRATE_DATA 1017
|
||||
#define CMD_END_READ_CALIBRATE_DATA 1018
|
||||
#define CMD_START_CALIBRATE 1019
|
||||
#define CMD_END_CALIBRATE 1021
|
||||
#define CMD_READ_VERSION 1016
|
||||
|
||||
I2C_SegmentBED BD_I2C_SENSOR;
|
||||
|
||||
#define BD_SENSOR_I2C_ADDR 0x3C
|
||||
|
||||
int8_t BDS_Leveling::config_state;
|
||||
uint8_t BDS_Leveling::homing;
|
||||
|
||||
void BDS_Leveling::echo_name() { SERIAL_ECHOPGM("Bed Distance Leveling"); }
|
||||
|
||||
void BDS_Leveling::init(uint8_t _sda, uint8_t _scl, uint16_t delay_s) {
|
||||
int ret = BD_I2C_SENSOR.i2c_init(_sda, _scl, BD_SENSOR_I2C_ADDR, delay_s);
|
||||
if (ret != 1) SERIAL_ECHOLNPGM("BD_I2C_SENSOR Init Fail return code:", ret);
|
||||
config_state = 0;
|
||||
}
|
||||
|
||||
float BDS_Leveling::read() {
|
||||
const uint16_t tmp = BD_I2C_SENSOR.BD_i2c_read();
|
||||
float BD_z = NAN;
|
||||
if (BD_I2C_SENSOR.BD_Check_OddEven(tmp) && (tmp & 0x3FF) < 1020)
|
||||
BD_z = (tmp & 0x3FF) / 100.0f;
|
||||
return BD_z;
|
||||
}
|
||||
|
||||
void BDS_Leveling::process() {
|
||||
//if (config_state == 0) return;
|
||||
static millis_t next_check_ms = 0; // starting at T=0
|
||||
static float z_pose = 0.0f;
|
||||
const millis_t ms = millis();
|
||||
if (ELAPSED(ms, next_check_ms)) { // timed out (or first run)
|
||||
next_check_ms = ms + (config_state < 0 ? 1000 : 100); // check at 1Hz or 10Hz
|
||||
|
||||
unsigned short tmp = 0;
|
||||
const float cur_z = planner.get_axis_position_mm(Z_AXIS); //current_position.z
|
||||
static float old_cur_z = cur_z,
|
||||
old_buf_z = current_position.z;
|
||||
|
||||
tmp = BD_I2C_SENSOR.BD_i2c_read();
|
||||
if (BD_I2C_SENSOR.BD_Check_OddEven(tmp) && (tmp & 0x3FF) < 1020) {
|
||||
const float z_sensor = (tmp & 0x3FF) / 100.0f;
|
||||
if (cur_z < 0) config_state = 0;
|
||||
//float abs_z = current_position.z > cur_z ? (current_position.z - cur_z) : (cur_z - current_position.z);
|
||||
if ( cur_z < config_state * 0.1f
|
||||
&& config_state > 0
|
||||
&& old_cur_z == cur_z
|
||||
&& old_buf_z == current_position.z
|
||||
&& z_sensor < (MAX_BD_HEIGHT)
|
||||
) {
|
||||
babystep.set_mm(Z_AXIS, cur_z - z_sensor);
|
||||
#if ENABLED(DEBUG_OUT_BD)
|
||||
SERIAL_ECHOLNPGM("BD:", z_sensor, ", Z:", cur_z, "|", current_position.z);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
babystep.set_mm(Z_AXIS, 0);
|
||||
//if (old_cur_z <= cur_z) Z_DIR_WRITE(!INVERT_Z_DIR);
|
||||
stepper.set_directions();
|
||||
}
|
||||
old_cur_z = cur_z;
|
||||
old_buf_z = current_position.z;
|
||||
endstops.bdp_state_update(z_sensor <= 0.01f);
|
||||
//endstops.update();
|
||||
}
|
||||
else
|
||||
stepper.set_directions();
|
||||
|
||||
#if ENABLED(DEBUG_OUT_BD)
|
||||
SERIAL_ECHOLNPGM("BD:", tmp & 0x3FF, ", Z:", cur_z, "|", current_position.z);
|
||||
if (BD_I2C_SENSOR.BD_Check_OddEven(tmp) == 0) SERIAL_ECHOLNPGM("errorCRC");
|
||||
#endif
|
||||
|
||||
if ((tmp & 0x3FF) > 1020) {
|
||||
BD_I2C_SENSOR.BD_i2c_stop();
|
||||
safe_delay(10);
|
||||
}
|
||||
|
||||
// read raw calibrate data
|
||||
if (config_state == -5) {
|
||||
BD_I2C_SENSOR.BD_i2c_write(CMD_START_READ_CALIBRATE_DATA);
|
||||
safe_delay(1000);
|
||||
|
||||
for (int i = 0; i < MAX_BD_HEIGHT * 10; i++) {
|
||||
tmp = BD_I2C_SENSOR.BD_i2c_read();
|
||||
SERIAL_ECHOLNPGM("Calibrate data:", i, ",", tmp & 0x3FF, ", check:", BD_I2C_SENSOR.BD_Check_OddEven(tmp));
|
||||
safe_delay(500);
|
||||
}
|
||||
config_state = 0;
|
||||
BD_I2C_SENSOR.BD_i2c_write(CMD_END_READ_CALIBRATE_DATA);
|
||||
safe_delay(500);
|
||||
}
|
||||
else if (config_state <= -6) { // Start Calibrate
|
||||
safe_delay(100);
|
||||
if (config_state == -6) {
|
||||
//BD_I2C_SENSOR.BD_i2c_write(1019); // begin calibrate
|
||||
//delay(1000);
|
||||
gcode.stepper_inactive_time = SEC_TO_MS(60 * 5);
|
||||
gcode.process_subcommands_now(F("M17 Z"));
|
||||
gcode.process_subcommands_now(F("G1 Z0.0"));
|
||||
z_pose = 0;
|
||||
safe_delay(1000);
|
||||
BD_I2C_SENSOR.BD_i2c_write(CMD_START_CALIBRATE); // Begin calibrate
|
||||
SERIAL_ECHOLNPGM("Begin calibrate");
|
||||
safe_delay(2000);
|
||||
config_state = -7;
|
||||
}
|
||||
else if (planner.get_axis_position_mm(Z_AXIS) < 10.0f) {
|
||||
if (z_pose >= MAX_BD_HEIGHT) {
|
||||
BD_I2C_SENSOR.BD_i2c_write(CMD_END_CALIBRATE); // End calibrate
|
||||
SERIAL_ECHOLNPGM("End calibrate data");
|
||||
z_pose = 7;
|
||||
config_state = 0;
|
||||
safe_delay(1000);
|
||||
}
|
||||
else {
|
||||
float tmp_k = 0;
|
||||
char tmp_1[30];
|
||||
sprintf_P(tmp_1, PSTR("G1 Z%d.%d"), int(z_pose), int(int(z_pose * 10) % 10));
|
||||
gcode.process_subcommands_now(tmp_1);
|
||||
|
||||
SERIAL_ECHO(tmp_1);
|
||||
SERIAL_ECHOLNPGM(" ,Z:", current_position.z);
|
||||
|
||||
while (tmp_k < (z_pose - 0.1f)) {
|
||||
tmp_k = planner.get_axis_position_mm(Z_AXIS);
|
||||
safe_delay(1);
|
||||
}
|
||||
safe_delay(800);
|
||||
tmp = (z_pose + 0.0001f) * 10;
|
||||
BD_I2C_SENSOR.BD_i2c_write(tmp);
|
||||
SERIAL_ECHOLNPGM("w:", tmp, ",Zpose:", z_pose);
|
||||
z_pose += 0.1001f;
|
||||
//queue.enqueue_now_P(PSTR("G90"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BD_SENSOR
|
36
Marlin/src/feature/bedlevel/bdl/bdl.h
Normal file
36
Marlin/src/feature/bedlevel/bdl/bdl.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2022 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 <stdint.h>
|
||||
|
||||
class BDS_Leveling {
|
||||
public:
|
||||
static int8_t config_state;
|
||||
static uint8_t homing;
|
||||
static void echo_name();
|
||||
static void init(uint8_t _sda, uint8_t _scl, uint16_t delay_s);
|
||||
static void process();
|
||||
static float read();
|
||||
};
|
||||
|
||||
extern BDS_Leveling bdl;
|
@@ -47,14 +47,11 @@
|
||||
#endif
|
||||
|
||||
bool leveling_is_valid() {
|
||||
return TERN1(MESH_BED_LEVELING, mbl.has_mesh())
|
||||
&& TERN1(AUTO_BED_LEVELING_BILINEAR, !!bilinear_grid_spacing.x)
|
||||
&& TERN1(AUTO_BED_LEVELING_UBL, ubl.mesh_is_valid());
|
||||
return TERN1(HAS_MESH, bedlevel.mesh_is_valid());
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn bed leveling on or off, fixing the current
|
||||
* position as-needed.
|
||||
* Turn bed leveling on or off, correcting the current position.
|
||||
*
|
||||
* Disable: Current position = physical position
|
||||
* Enable: Current position = "unleveled" physical position
|
||||
@@ -65,30 +62,25 @@ void set_bed_leveling_enabled(const bool enable/*=true*/) {
|
||||
|
||||
if (can_change && enable != planner.leveling_active) {
|
||||
|
||||
auto _report_leveling = []{
|
||||
if (DEBUGGING(LEVELING)) {
|
||||
if (planner.leveling_active)
|
||||
DEBUG_POS("Leveling ON", current_position);
|
||||
else
|
||||
DEBUG_POS("Leveling OFF", current_position);
|
||||
}
|
||||
};
|
||||
|
||||
_report_leveling();
|
||||
planner.synchronize();
|
||||
|
||||
#if ENABLED(AUTO_BED_LEVELING_BILINEAR)
|
||||
// Force bilinear_z_offset to re-calculate next time
|
||||
const xyz_pos_t reset { -9999.999, -9999.999, 0 };
|
||||
(void)bilinear_z_offset(reset);
|
||||
#endif
|
||||
|
||||
if (planner.leveling_active) { // leveling from on to off
|
||||
if (DEBUGGING(LEVELING)) DEBUG_POS("Leveling ON", current_position);
|
||||
// change unleveled current_position to physical current_position without moving steppers.
|
||||
planner.apply_leveling(current_position);
|
||||
planner.leveling_active = false; // disable only AFTER calling apply_leveling
|
||||
if (DEBUGGING(LEVELING)) DEBUG_POS("...Now OFF", current_position);
|
||||
}
|
||||
else { // leveling from off to on
|
||||
if (DEBUGGING(LEVELING)) DEBUG_POS("Leveling OFF", current_position);
|
||||
planner.leveling_active = true; // enable BEFORE calling unapply_leveling, otherwise ignored
|
||||
// change physical current_position to unleveled current_position without moving steppers.
|
||||
planner.unapply_leveling(current_position);
|
||||
if (DEBUGGING(LEVELING)) DEBUG_POS("...Now ON", current_position);
|
||||
}
|
||||
// Get the corrected leveled / unleveled position
|
||||
planner.apply_modifiers(current_position); // Physical position with all modifiers
|
||||
planner.leveling_active ^= true; // Toggle leveling between apply and unapply
|
||||
planner.unapply_modifiers(current_position); // Logical position with modifiers removed
|
||||
|
||||
sync_plan_position();
|
||||
_report_leveling();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,23 +114,9 @@ TemporaryBedLevelingState::TemporaryBedLevelingState(const bool enable) : saved(
|
||||
*/
|
||||
void reset_bed_level() {
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("reset_bed_level");
|
||||
#if ENABLED(AUTO_BED_LEVELING_UBL)
|
||||
ubl.reset();
|
||||
#else
|
||||
set_bed_leveling_enabled(false);
|
||||
#if ENABLED(MESH_BED_LEVELING)
|
||||
mbl.reset();
|
||||
#elif ENABLED(AUTO_BED_LEVELING_BILINEAR)
|
||||
bilinear_start.reset();
|
||||
bilinear_grid_spacing.reset();
|
||||
GRID_LOOP(x, y) {
|
||||
z_values[x][y] = NAN;
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(x, y, 0));
|
||||
}
|
||||
#elif ABL_PLANAR
|
||||
planner.bed_level_matrix.set_to_identity();
|
||||
#endif
|
||||
#endif
|
||||
IF_DISABLED(AUTO_BED_LEVELING_UBL, set_bed_leveling_enabled(false));
|
||||
TERN_(HAS_MESH, bedlevel.reset());
|
||||
TERN_(ABL_PLANAR, planner.bed_level_matrix.set_to_identity());
|
||||
}
|
||||
|
||||
#if EITHER(AUTO_BED_LEVELING_BILINEAR, MESH_BED_LEVELING)
|
||||
@@ -156,7 +134,7 @@ void reset_bed_level() {
|
||||
/**
|
||||
* Print calibration results for plotting or manual frame adjustment.
|
||||
*/
|
||||
void print_2d_array(const uint8_t sx, const uint8_t sy, const uint8_t precision, element_2d_fn fn) {
|
||||
void print_2d_array(const uint8_t sx, const uint8_t sy, const uint8_t precision, const float *values) {
|
||||
#ifndef SCAD_MESH_OUTPUT
|
||||
LOOP_L_N(x, sx) {
|
||||
serial_spaces(precision + (x < 10 ? 3 : 2));
|
||||
@@ -176,7 +154,7 @@ void reset_bed_level() {
|
||||
#endif
|
||||
LOOP_L_N(x, sx) {
|
||||
SERIAL_CHAR(' ');
|
||||
const float offset = fn(x, y);
|
||||
const float offset = values[x * sy + y];
|
||||
if (!isnan(offset)) {
|
||||
if (offset >= 0) SERIAL_CHAR('+');
|
||||
SERIAL_ECHO_F(offset, int(precision));
|
||||
|
@@ -62,16 +62,13 @@ class TemporaryBedLevelingState {
|
||||
typedef float bed_mesh_t[GRID_MAX_POINTS_X][GRID_MAX_POINTS_Y];
|
||||
|
||||
#if ENABLED(AUTO_BED_LEVELING_BILINEAR)
|
||||
#include "abl/abl.h"
|
||||
#include "abl/bbl.h"
|
||||
#elif ENABLED(AUTO_BED_LEVELING_UBL)
|
||||
#include "ubl/ubl.h"
|
||||
#elif ENABLED(MESH_BED_LEVELING)
|
||||
#include "mbl/mesh_bed_leveling.h"
|
||||
#endif
|
||||
|
||||
#define Z_VALUES(X,Y) Z_VALUES_ARR[X][Y]
|
||||
#define _GET_MESH_POS(M) { _GET_MESH_X(M.a), _GET_MESH_Y(M.b) }
|
||||
|
||||
#if EITHER(AUTO_BED_LEVELING_BILINEAR, MESH_BED_LEVELING)
|
||||
|
||||
#include <stdint.h>
|
||||
@@ -81,7 +78,7 @@ class TemporaryBedLevelingState {
|
||||
/**
|
||||
* Print calibration results for plotting or manual frame adjustment.
|
||||
*/
|
||||
void print_2d_array(const uint8_t sx, const uint8_t sy, const uint8_t precision, element_2d_fn fn);
|
||||
void print_2d_array(const uint8_t sx, const uint8_t sy, const uint8_t precision, const float *values);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -92,7 +89,7 @@ class TemporaryBedLevelingState {
|
||||
bool valid() const { return pos.x >= 0 && pos.y >= 0; }
|
||||
#if ENABLED(AUTO_BED_LEVELING_UBL)
|
||||
xy_pos_t meshpos() {
|
||||
return { ubl.mesh_index_to_xpos(pos.x), ubl.mesh_index_to_ypos(pos.y) };
|
||||
return { bedlevel.get_mesh_x(pos.x), bedlevel.get_mesh_y(pos.y) };
|
||||
}
|
||||
#endif
|
||||
operator xy_int8_t&() { return pos; }
|
||||
|
@@ -32,7 +32,7 @@
|
||||
#include "../../../lcd/extui/ui_api.h"
|
||||
#endif
|
||||
|
||||
mesh_bed_leveling mbl;
|
||||
mesh_bed_leveling bedlevel;
|
||||
|
||||
float mesh_bed_leveling::z_offset,
|
||||
mesh_bed_leveling::z_values[GRID_MAX_POINTS_X][GRID_MAX_POINTS_Y],
|
||||
@@ -125,9 +125,7 @@
|
||||
void mesh_bed_leveling::report_mesh() {
|
||||
SERIAL_ECHOPAIR_F(STRINGIFY(GRID_MAX_POINTS_X) "x" STRINGIFY(GRID_MAX_POINTS_Y) " mesh. Z offset: ", z_offset, 5);
|
||||
SERIAL_ECHOLNPGM("\nMeasured points:");
|
||||
print_2d_array(GRID_MAX_POINTS_X, GRID_MAX_POINTS_Y, 5,
|
||||
[](const uint8_t ix, const uint8_t iy) { return z_values[ix][iy]; }
|
||||
);
|
||||
print_2d_array(GRID_MAX_POINTS_X, GRID_MAX_POINTS_Y, 5, z_values[0]);
|
||||
}
|
||||
|
||||
#endif // MESH_BED_LEVELING
|
||||
|
@@ -34,9 +34,6 @@ enum MeshLevelingState : char {
|
||||
|
||||
#define MESH_X_DIST (float(MESH_MAX_X - (MESH_MIN_X)) / (GRID_MAX_CELLS_X))
|
||||
#define MESH_Y_DIST (float(MESH_MAX_Y - (MESH_MIN_Y)) / (GRID_MAX_CELLS_Y))
|
||||
#define _GET_MESH_X(I) mbl.index_to_xpos[I]
|
||||
#define _GET_MESH_Y(J) mbl.index_to_ypos[J]
|
||||
#define Z_VALUES_ARR mbl.z_values
|
||||
|
||||
class mesh_bed_leveling {
|
||||
public:
|
||||
@@ -56,9 +53,11 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool mesh_is_valid() { return has_mesh(); }
|
||||
|
||||
static void set_z(const int8_t px, const int8_t py, const_float_t z) { z_values[px][py] = z; }
|
||||
|
||||
static inline void zigzag(const int8_t index, int8_t &px, int8_t &py) {
|
||||
static void zigzag(const int8_t index, int8_t &px, int8_t &py) {
|
||||
px = index % (GRID_MAX_POINTS_X);
|
||||
py = index / (GRID_MAX_POINTS_X);
|
||||
if (py & 1) px = (GRID_MAX_POINTS_X) - 1 - px; // Zig zag
|
||||
@@ -70,6 +69,9 @@ public:
|
||||
set_z(px, py, z);
|
||||
}
|
||||
|
||||
static float get_mesh_x(const uint8_t i) { return index_to_xpos[i]; }
|
||||
static float get_mesh_y(const uint8_t i) { return index_to_ypos[i]; }
|
||||
|
||||
static int8_t cell_index_x(const_float_t x) {
|
||||
int8_t cx = (x - (MESH_MIN_X)) * RECIPROCAL(MESH_X_DIST);
|
||||
return constrain(cx, 0, GRID_MAX_CELLS_X - 1);
|
||||
@@ -78,10 +80,10 @@ public:
|
||||
int8_t cy = (y - (MESH_MIN_Y)) * RECIPROCAL(MESH_Y_DIST);
|
||||
return constrain(cy, 0, GRID_MAX_CELLS_Y - 1);
|
||||
}
|
||||
static inline xy_int8_t cell_indexes(const_float_t x, const_float_t y) {
|
||||
static xy_int8_t cell_indexes(const_float_t x, const_float_t y) {
|
||||
return { cell_index_x(x), cell_index_y(y) };
|
||||
}
|
||||
static inline xy_int8_t cell_indexes(const xy_pos_t &xy) { return cell_indexes(xy.x, xy.y); }
|
||||
static xy_int8_t cell_indexes(const xy_pos_t &xy) { return cell_indexes(xy.x, xy.y); }
|
||||
|
||||
static int8_t probe_index_x(const_float_t x) {
|
||||
int8_t px = (x - (MESH_MIN_X) + 0.5f * (MESH_X_DIST)) * RECIPROCAL(MESH_X_DIST);
|
||||
@@ -91,10 +93,10 @@ public:
|
||||
int8_t py = (y - (MESH_MIN_Y) + 0.5f * (MESH_Y_DIST)) * RECIPROCAL(MESH_Y_DIST);
|
||||
return WITHIN(py, 0, (GRID_MAX_POINTS_Y) - 1) ? py : -1;
|
||||
}
|
||||
static inline xy_int8_t probe_indexes(const_float_t x, const_float_t y) {
|
||||
static xy_int8_t probe_indexes(const_float_t x, const_float_t y) {
|
||||
return { probe_index_x(x), probe_index_y(y) };
|
||||
}
|
||||
static inline xy_int8_t probe_indexes(const xy_pos_t &xy) { return probe_indexes(xy.x, xy.y); }
|
||||
static xy_int8_t probe_indexes(const xy_pos_t &xy) { return probe_indexes(xy.x, xy.y); }
|
||||
|
||||
static float calc_z0(const_float_t a0, const_float_t a1, const_float_t z1, const_float_t a2, const_float_t z2) {
|
||||
const float delta_z = (z2 - z1) / (a2 - a1),
|
||||
@@ -102,12 +104,9 @@ public:
|
||||
return z1 + delta_a * delta_z;
|
||||
}
|
||||
|
||||
static float get_z(const xy_pos_t &pos
|
||||
OPTARG(ENABLE_LEVELING_FADE_HEIGHT, const_float_t factor=1.0f)
|
||||
) {
|
||||
#if DISABLED(ENABLE_LEVELING_FADE_HEIGHT)
|
||||
constexpr float factor = 1.0f;
|
||||
#endif
|
||||
static float get_z_offset() { return z_offset; }
|
||||
|
||||
static float get_z_correction(const xy_pos_t &pos) {
|
||||
const xy_int8_t ind = cell_indexes(pos);
|
||||
const float x1 = index_to_xpos[ind.x], x2 = index_to_xpos[ind.x+1],
|
||||
y1 = index_to_xpos[ind.y], y2 = index_to_xpos[ind.y+1],
|
||||
@@ -115,7 +114,7 @@ public:
|
||||
z2 = calc_z0(pos.x, x1, z_values[ind.x][ind.y+1], x2, z_values[ind.x+1][ind.y+1]),
|
||||
zf = calc_z0(pos.y, y1, z1, y2, z2);
|
||||
|
||||
return z_offset + zf * factor;
|
||||
return zf;
|
||||
}
|
||||
|
||||
#if IS_CARTESIAN && DISABLED(SEGMENT_LEVELED_MOVES)
|
||||
@@ -123,4 +122,4 @@ public:
|
||||
#endif
|
||||
};
|
||||
|
||||
extern mesh_bed_leveling mbl;
|
||||
extern mesh_bed_leveling bedlevel;
|
||||
|
@@ -26,7 +26,7 @@
|
||||
|
||||
#include "../bedlevel.h"
|
||||
|
||||
unified_bed_leveling ubl;
|
||||
unified_bed_leveling bedlevel;
|
||||
|
||||
#include "../../../MarlinCore.h"
|
||||
#include "../../../gcode/gcode.h"
|
||||
@@ -180,10 +180,8 @@ void unified_bed_leveling::display_map(const uint8_t map_type) {
|
||||
SERIAL_EOL();
|
||||
serial_echo_column_labels(eachsp - 2);
|
||||
}
|
||||
else {
|
||||
SERIAL_ECHOPGM(" for ");
|
||||
SERIAL_ECHOPGM_P(csv ? PSTR("CSV:\n") : PSTR("LCD:\n"));
|
||||
}
|
||||
else
|
||||
SERIAL_ECHOPGM(" for ", csv ? F("CSV:\n") : F("LCD:\n"));
|
||||
|
||||
// Add XY probe offset from extruder because probe.probe_at_point() subtracts them when
|
||||
// moving to the XY position to be measured. This ensures better agreement between
|
||||
@@ -213,10 +211,10 @@ void unified_bed_leveling::display_map(const uint8_t map_type) {
|
||||
// TODO: Display on Graphical LCD
|
||||
}
|
||||
else if (isnan(f))
|
||||
SERIAL_ECHOPGM_P(human ? PSTR(" . ") : PSTR("NAN"));
|
||||
SERIAL_ECHOF(human ? F(" . ") : F("NAN"));
|
||||
else if (human || csv) {
|
||||
if (human && f >= 0.0) SERIAL_CHAR(f > 0 ? '+' : ' '); // Display sign also for positive numbers (' ' for 0)
|
||||
SERIAL_ECHO_F(f, 3); // Positive: 5 digits, Negative: 6 digits
|
||||
if (human && f >= 0) SERIAL_CHAR(f > 0 ? '+' : ' '); // Display sign also for positive numbers (' ' for 0)
|
||||
SERIAL_DECIMAL(f); // Positive: 5 digits, Negative: 6 digits
|
||||
}
|
||||
if (csv && i < (GRID_MAX_POINTS_X) - 1) SERIAL_CHAR('\t');
|
||||
|
||||
@@ -281,10 +279,10 @@ bool unified_bed_leveling::sanity_check() {
|
||||
}
|
||||
#endif
|
||||
|
||||
process_subcommands_now_P(G28_STR); // Home
|
||||
process_subcommands_now_P(PSTR(ALIGN_GCODE "\n" // Align multi z axis if available
|
||||
PROBE_GCODE "\n" // Build mesh with available hardware
|
||||
"G29P3\nG29P3")); // Ensure mesh is complete by running smart fill twice
|
||||
process_subcommands_now(FPSTR(G28_STR)); // Home
|
||||
process_subcommands_now(F(ALIGN_GCODE "\n" // Align multi z axis if available
|
||||
PROBE_GCODE "\n" // Build mesh with available hardware
|
||||
"G29P3\nG29P3")); // Ensure mesh is complete by running smart fill twice
|
||||
|
||||
if (parser.seenval('S')) {
|
||||
char umw_gcode[32];
|
||||
@@ -292,9 +290,9 @@ bool unified_bed_leveling::sanity_check() {
|
||||
queue.inject(umw_gcode);
|
||||
}
|
||||
|
||||
process_subcommands_now_P(PSTR("G29A\nG29F10\n" // Set UBL Active & Fade 10
|
||||
"M140S0\nM104S0\n" // Turn off heaters
|
||||
"M500")); // Store settings
|
||||
process_subcommands_now(F("G29A\nG29F10\n" // Set UBL Active & Fade 10
|
||||
"M140S0\nM104S0\n" // Turn off heaters
|
||||
"M500")); // Store settings
|
||||
}
|
||||
|
||||
#endif // UBL_MESH_WIZARD
|
||||
|
@@ -70,20 +70,19 @@ private:
|
||||
static void move_z_with_encoder(const_float_t multiplier);
|
||||
static float measure_point_with_encoder();
|
||||
static float measure_business_card_thickness();
|
||||
static void manually_probe_remaining_mesh(const xy_pos_t&, const_float_t , const_float_t , const bool) _O0;
|
||||
static void fine_tune_mesh(const xy_pos_t &pos, const bool do_ubl_mesh_map) _O0;
|
||||
static void manually_probe_remaining_mesh(const xy_pos_t&, const_float_t , const_float_t , const bool) __O0;
|
||||
static void fine_tune_mesh(const xy_pos_t &pos, const bool do_ubl_mesh_map) __O0;
|
||||
#endif
|
||||
|
||||
static bool G29_parse_parameters() _O0;
|
||||
static bool G29_parse_parameters() __O0;
|
||||
static void shift_mesh_height();
|
||||
static void probe_entire_mesh(const xy_pos_t &near, const bool do_ubl_mesh_map, const bool stow_probe, const bool do_furthest) _O0;
|
||||
static void probe_entire_mesh(const xy_pos_t &near, const bool do_ubl_mesh_map, const bool stow_probe, const bool do_furthest) __O0;
|
||||
static void tilt_mesh_based_on_3pts(const_float_t z1, const_float_t z2, const_float_t z3);
|
||||
static void tilt_mesh_based_on_probed_grid(const bool do_ubl_mesh_map);
|
||||
static bool smart_fill_one(const uint8_t x, const uint8_t y, const int8_t xdir, const int8_t ydir);
|
||||
static inline bool smart_fill_one(const xy_uint8_t &pos, const xy_uint8_t &dir) {
|
||||
static bool smart_fill_one(const xy_uint8_t &pos, const xy_uint8_t &dir) {
|
||||
return smart_fill_one(pos.x, pos.y, dir.x, dir.y);
|
||||
}
|
||||
static void smart_fill_mesh();
|
||||
|
||||
#if ENABLED(UBL_DEVEL_DEBUGGING)
|
||||
static void g29_what_command();
|
||||
@@ -98,17 +97,18 @@ public:
|
||||
static void report_state();
|
||||
static void save_ubl_active_state_and_disable();
|
||||
static void restore_ubl_active_state_and_leave();
|
||||
static void display_map(const uint8_t) _O0;
|
||||
static mesh_index_pair find_closest_mesh_point_of_type(const MeshPointType, const xy_pos_t&, const bool=false, MeshFlags *done_flags=nullptr) _O0;
|
||||
static mesh_index_pair find_furthest_invalid_mesh_point() _O0;
|
||||
static void display_map(const uint8_t) __O0;
|
||||
static mesh_index_pair find_closest_mesh_point_of_type(const MeshPointType, const xy_pos_t&, const bool=false, MeshFlags *done_flags=nullptr) __O0;
|
||||
static mesh_index_pair find_furthest_invalid_mesh_point() __O0;
|
||||
static void reset();
|
||||
static void invalidate();
|
||||
static void set_all_mesh_points_to_value(const_float_t value);
|
||||
static void adjust_mesh_to_mean(const bool cflag, const_float_t value);
|
||||
static bool sanity_check();
|
||||
static void smart_fill_mesh();
|
||||
|
||||
static void G29() _O0; // O0 for no optimization
|
||||
static void smart_fill_wlsf(const_float_t ) _O2; // O2 gives smaller code than Os on A2560
|
||||
static void G29() __O0; // O0 for no optimization
|
||||
static void smart_fill_wlsf(const_float_t ) __O2; // O2 gives smaller code than Os on A2560
|
||||
|
||||
static int8_t storage_slot;
|
||||
|
||||
@@ -120,11 +120,11 @@ public:
|
||||
static const float _mesh_index_to_xpos[GRID_MAX_POINTS_X],
|
||||
_mesh_index_to_ypos[GRID_MAX_POINTS_Y];
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
#if HAS_MARLINUI_MENU
|
||||
static bool lcd_map_control;
|
||||
static void steppers_were_disabled();
|
||||
#else
|
||||
static inline void steppers_were_disabled() {}
|
||||
static void steppers_were_disabled() {}
|
||||
#endif
|
||||
|
||||
static volatile int16_t encoder_diff; // Volatile because buttons may change it at interrupt time
|
||||
@@ -157,10 +157,10 @@ public:
|
||||
return constrain(cell_index_y_raw(y), 0, GRID_MAX_CELLS_Y - 1);
|
||||
}
|
||||
|
||||
static inline xy_int8_t cell_indexes(const_float_t x, const_float_t y) {
|
||||
static xy_int8_t cell_indexes(const_float_t x, const_float_t y) {
|
||||
return { cell_index_x(x), cell_index_y(y) };
|
||||
}
|
||||
static inline xy_int8_t cell_indexes(const xy_pos_t &xy) { return cell_indexes(xy.x, xy.y); }
|
||||
static xy_int8_t cell_indexes(const xy_pos_t &xy) { return cell_indexes(xy.x, xy.y); }
|
||||
|
||||
static int8_t closest_x_index(const_float_t x) {
|
||||
const int8_t px = (x - (MESH_MIN_X) + (MESH_X_DIST) * 0.5) * RECIPROCAL(MESH_X_DIST);
|
||||
@@ -170,7 +170,7 @@ public:
|
||||
const int8_t py = (y - (MESH_MIN_Y) + (MESH_Y_DIST) * 0.5) * RECIPROCAL(MESH_Y_DIST);
|
||||
return WITHIN(py, 0, (GRID_MAX_POINTS_Y) - 1) ? py : -1;
|
||||
}
|
||||
static inline xy_int8_t closest_indexes(const xy_pos_t &xy) {
|
||||
static xy_int8_t closest_indexes(const xy_pos_t &xy) {
|
||||
return { closest_x_index(xy.x), closest_y_index(xy.y) };
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ public:
|
||||
* z_correction_for_x_on_horizontal_mesh_line is an optimization for
|
||||
* the case where the printer is making a vertical line that only crosses horizontal mesh lines.
|
||||
*/
|
||||
static inline float z_correction_for_x_on_horizontal_mesh_line(const_float_t rx0, const int x1_i, const int yi) {
|
||||
static float z_correction_for_x_on_horizontal_mesh_line(const_float_t rx0, const int x1_i, const int yi) {
|
||||
if (!WITHIN(x1_i, 0, (GRID_MAX_POINTS_X) - 1) || !WITHIN(yi, 0, (GRID_MAX_POINTS_Y) - 1)) {
|
||||
|
||||
if (DEBUGGING(LEVELING)) {
|
||||
@@ -215,7 +215,7 @@ public:
|
||||
return _UBL_OUTER_Z_RAISE;
|
||||
}
|
||||
|
||||
const float xratio = (rx0 - mesh_index_to_xpos(x1_i)) * RECIPROCAL(MESH_X_DIST),
|
||||
const float xratio = (rx0 - get_mesh_x(x1_i)) * RECIPROCAL(MESH_X_DIST),
|
||||
z1 = z_values[x1_i][yi];
|
||||
|
||||
return z1 + xratio * (z_values[_MIN(x1_i, (GRID_MAX_POINTS_X) - 2) + 1][yi] - z1); // Don't allow x1_i+1 to be past the end of the array
|
||||
@@ -226,7 +226,7 @@ public:
|
||||
//
|
||||
// See comments above for z_correction_for_x_on_horizontal_mesh_line
|
||||
//
|
||||
static inline float z_correction_for_y_on_vertical_mesh_line(const_float_t ry0, const int xi, const int y1_i) {
|
||||
static float z_correction_for_y_on_vertical_mesh_line(const_float_t ry0, const int xi, const int y1_i) {
|
||||
if (!WITHIN(xi, 0, (GRID_MAX_POINTS_X) - 1) || !WITHIN(y1_i, 0, (GRID_MAX_POINTS_Y) - 1)) {
|
||||
|
||||
if (DEBUGGING(LEVELING)) {
|
||||
@@ -238,7 +238,7 @@ public:
|
||||
return _UBL_OUTER_Z_RAISE;
|
||||
}
|
||||
|
||||
const float yratio = (ry0 - mesh_index_to_ypos(y1_i)) * RECIPROCAL(MESH_Y_DIST),
|
||||
const float yratio = (ry0 - get_mesh_y(y1_i)) * RECIPROCAL(MESH_Y_DIST),
|
||||
z1 = z_values[xi][y1_i];
|
||||
|
||||
return z1 + yratio * (z_values[xi][_MIN(y1_i, (GRID_MAX_POINTS_Y) - 2) + 1] - z1); // Don't allow y1_i+1 to be past the end of the array
|
||||
@@ -264,16 +264,17 @@ public:
|
||||
return UBL_Z_RAISE_WHEN_OFF_MESH;
|
||||
#endif
|
||||
|
||||
const uint8_t mx = _MIN(cx, (GRID_MAX_POINTS_X) - 2) + 1, my = _MIN(cy, (GRID_MAX_POINTS_Y) - 2) + 1;
|
||||
const float z1 = calc_z0(rx0, mesh_index_to_xpos(cx), z_values[cx][cy], mesh_index_to_xpos(cx + 1), z_values[mx][cy]);
|
||||
const float z2 = calc_z0(rx0, mesh_index_to_xpos(cx), z_values[cx][my], mesh_index_to_xpos(cx + 1), z_values[mx][my]);
|
||||
float z0 = calc_z0(ry0, mesh_index_to_ypos(cy), z1, mesh_index_to_ypos(cy + 1), z2);
|
||||
const uint8_t mx = _MIN(cx, (GRID_MAX_POINTS_X) - 2) + 1, my = _MIN(cy, (GRID_MAX_POINTS_Y) - 2) + 1,
|
||||
x0 = get_mesh_x(cx), x1 = get_mesh_x(cx + 1);
|
||||
const float z1 = calc_z0(rx0, x0, z_values[cx][cy], x1, z_values[mx][cy]),
|
||||
z2 = calc_z0(rx0, x0, z_values[cx][my], x1, z_values[mx][my]);
|
||||
float z0 = calc_z0(ry0, get_mesh_y(cy), z1, get_mesh_y(cy + 1), z2);
|
||||
|
||||
if (isnan(z0)) { // if part of the Mesh is undefined, it will show up as NAN
|
||||
z0 = 0.0; // in ubl.z_values[][] and propagate through the
|
||||
// calculations. If our correction is NAN, we throw it out
|
||||
// because part of the Mesh is undefined and we don't have the
|
||||
// information we need to complete the height correction.
|
||||
if (isnan(z0)) { // If part of the Mesh is undefined, it will show up as NAN
|
||||
z0 = 0.0; // in z_values[][] and propagate through the calculations.
|
||||
// If our correction is NAN, we throw it out because part of
|
||||
// the Mesh is undefined and we don't have the information
|
||||
// needed to complete the height correction.
|
||||
|
||||
if (DEBUGGING(MESH_ADJUST)) DEBUG_ECHOLNPGM("??? Yikes! NAN in ");
|
||||
}
|
||||
@@ -285,12 +286,14 @@ public:
|
||||
|
||||
return z0;
|
||||
}
|
||||
static inline float get_z_correction(const xy_pos_t &pos) { return get_z_correction(pos.x, pos.y); }
|
||||
static float get_z_correction(const xy_pos_t &pos) { return get_z_correction(pos.x, pos.y); }
|
||||
|
||||
static inline float mesh_index_to_xpos(const uint8_t i) {
|
||||
static constexpr float get_z_offset() { return 0.0f; }
|
||||
|
||||
static float get_mesh_x(const uint8_t i) {
|
||||
return i < (GRID_MAX_POINTS_X) ? pgm_read_float(&_mesh_index_to_xpos[i]) : MESH_MIN_X + i * (MESH_X_DIST);
|
||||
}
|
||||
static inline float mesh_index_to_ypos(const uint8_t i) {
|
||||
static float get_mesh_y(const uint8_t i) {
|
||||
return i < (GRID_MAX_POINTS_Y) ? pgm_read_float(&_mesh_index_to_ypos[i]) : MESH_MIN_Y + i * (MESH_Y_DIST);
|
||||
}
|
||||
|
||||
@@ -300,18 +303,14 @@ public:
|
||||
static void line_to_destination_cartesian(const_feedRate_t scaled_fr_mm_s, const uint8_t e);
|
||||
#endif
|
||||
|
||||
static inline bool mesh_is_valid() {
|
||||
static bool mesh_is_valid() {
|
||||
GRID_LOOP(x, y) if (isnan(z_values[x][y])) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}; // class unified_bed_leveling
|
||||
|
||||
extern unified_bed_leveling ubl;
|
||||
|
||||
#define _GET_MESH_X(I) ubl.mesh_index_to_xpos(I)
|
||||
#define _GET_MESH_Y(J) ubl.mesh_index_to_ypos(J)
|
||||
#define Z_VALUES_ARR ubl.z_values
|
||||
extern unified_bed_leveling bedlevel;
|
||||
|
||||
// Prevent debugging propagating to other files
|
||||
#include "../../../core/debug_out.h"
|
||||
|
@@ -31,7 +31,6 @@
|
||||
#include "../../../libs/hex_print.h"
|
||||
#include "../../../module/settings.h"
|
||||
#include "../../../lcd/marlinui.h"
|
||||
#include "../../../module/stepper.h"
|
||||
#include "../../../module/planner.h"
|
||||
#include "../../../module/motion.h"
|
||||
#include "../../../module/probe.h"
|
||||
@@ -57,7 +56,7 @@
|
||||
|
||||
#define UBL_G29_P31
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
#if HAS_MARLINUI_MENU
|
||||
|
||||
bool unified_bed_leveling::lcd_map_control = false;
|
||||
|
||||
@@ -316,7 +315,43 @@ void unified_bed_leveling::G29() {
|
||||
planner.synchronize();
|
||||
// Send 'N' to force homing before G29 (internal only)
|
||||
if (axes_should_home() || parser.seen_test('N')) gcode.home_all_axes();
|
||||
TERN_(HAS_MULTI_HOTEND, if (active_extruder) tool_change(0));
|
||||
TERN_(HAS_MULTI_HOTEND, if (active_extruder != 0) tool_change(0, true));
|
||||
|
||||
// Position bed horizontally and Z probe vertically.
|
||||
#if defined(SAFE_BED_LEVELING_START_X) || defined(SAFE_BED_LEVELING_START_Y) || defined(SAFE_BED_LEVELING_START_Z) \
|
||||
|| defined(SAFE_BED_LEVELING_START_I) || defined(SAFE_BED_LEVELING_START_J) || defined(SAFE_BED_LEVELING_START_K) \
|
||||
|| defined(SAFE_BED_LEVELING_START_U) || defined(SAFE_BED_LEVELING_START_V) || defined(SAFE_BED_LEVELING_START_W)
|
||||
xyze_pos_t safe_position = current_position;
|
||||
#ifdef SAFE_BED_LEVELING_START_X
|
||||
safe_position.x = SAFE_BED_LEVELING_START_X;
|
||||
#endif
|
||||
#ifdef SAFE_BED_LEVELING_START_Y
|
||||
safe_position.y = SAFE_BED_LEVELING_START_Y;
|
||||
#endif
|
||||
#ifdef SAFE_BED_LEVELING_START_Z
|
||||
safe_position.z = SAFE_BED_LEVELING_START_Z;
|
||||
#endif
|
||||
#ifdef SAFE_BED_LEVELING_START_I
|
||||
safe_position.i = SAFE_BED_LEVELING_START_I;
|
||||
#endif
|
||||
#ifdef SAFE_BED_LEVELING_START_J
|
||||
safe_position.j = SAFE_BED_LEVELING_START_J;
|
||||
#endif
|
||||
#ifdef SAFE_BED_LEVELING_START_K
|
||||
safe_position.k = SAFE_BED_LEVELING_START_K;
|
||||
#endif
|
||||
#ifdef SAFE_BED_LEVELING_START_U
|
||||
safe_position.u = SAFE_BED_LEVELING_START_U;
|
||||
#endif
|
||||
#ifdef SAFE_BED_LEVELING_START_V
|
||||
safe_position.v = SAFE_BED_LEVELING_START_V;
|
||||
#endif
|
||||
#ifdef SAFE_BED_LEVELING_START_W
|
||||
safe_position.w = SAFE_BED_LEVELING_START_W;
|
||||
#endif
|
||||
|
||||
do_blocking_move_to(safe_position);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Invalidate one or more nearby mesh points, possibly all.
|
||||
@@ -346,13 +381,14 @@ void unified_bed_leveling::G29() {
|
||||
|
||||
if (parser.seen('Q')) {
|
||||
const int16_t test_pattern = parser.has_value() ? parser.value_int() : -99;
|
||||
if (!WITHIN(test_pattern, -1, 2)) {
|
||||
SERIAL_ECHOLNPGM("Invalid test_pattern value. (-1 to 2)\n");
|
||||
if (!WITHIN(test_pattern, TERN0(UBL_DEVEL_DEBUGGING, -1), 2)) {
|
||||
SERIAL_ECHOLNPGM("?Invalid (Q) test pattern. (" TERN(UBL_DEVEL_DEBUGGING, "-1", "0") " to 2)\n");
|
||||
return;
|
||||
}
|
||||
SERIAL_ECHOLNPGM("Loading test_pattern values.\n");
|
||||
SERIAL_ECHOLNPGM("Applying test pattern.\n");
|
||||
switch (test_pattern) {
|
||||
|
||||
default:
|
||||
case -1: TERN_(UBL_DEVEL_DEBUGGING, g29_eeprom_dump()); break;
|
||||
|
||||
case 0:
|
||||
@@ -366,13 +402,13 @@ void unified_bed_leveling::G29() {
|
||||
|
||||
case 1:
|
||||
LOOP_L_N(x, GRID_MAX_POINTS_X) { // Create a diagonal line several Mesh cells thick that is raised
|
||||
const uint8_t x2 = x + (x < (GRID_MAX_POINTS_Y) - 1 ? 1 : -1);
|
||||
z_values[x][x] += 9.999f;
|
||||
z_values[x][x + (x < (GRID_MAX_POINTS_Y) - 1) ? 1 : -1] += 9.999f; // We want the altered line several mesh points thick
|
||||
z_values[x][x2] += 9.999f; // We want the altered line several mesh points thick
|
||||
#if ENABLED(EXTENSIBLE_UI)
|
||||
ExtUI::onMeshUpdate(x, x, z_values[x][x]);
|
||||
ExtUI::onMeshUpdate(x, (x + (x < (GRID_MAX_POINTS_Y) - 1) ? 1 : -1), z_values[x][x + (x < (GRID_MAX_POINTS_Y) - 1) ? 1 : -1]);
|
||||
ExtUI::onMeshUpdate(x, (x2), z_values[x][x2]);
|
||||
#endif
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -442,7 +478,7 @@ void unified_bed_leveling::G29() {
|
||||
#endif // HAS_BED_PROBE
|
||||
|
||||
case 2: {
|
||||
#if HAS_LCD_MENU
|
||||
#if HAS_MARLINUI_MENU
|
||||
//
|
||||
// Manually Probe Mesh in areas that can't be reached by the probe
|
||||
//
|
||||
@@ -554,7 +590,7 @@ void unified_bed_leveling::G29() {
|
||||
}
|
||||
|
||||
case 4: // Fine Tune (i.e., Edit) the Mesh
|
||||
#if HAS_LCD_MENU
|
||||
#if HAS_MARLINUI_MENU
|
||||
fine_tune_mesh(param.XY_pos, parser.seen_test('T'));
|
||||
#else
|
||||
SERIAL_ECHOLNPGM("?P4 is only available when an LCD is present.");
|
||||
@@ -645,7 +681,7 @@ void unified_bed_leveling::G29() {
|
||||
|
||||
LEAVE:
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
#if HAS_MARLINUI_MENU
|
||||
ui.reset_alert_level();
|
||||
ui.quick_feedback();
|
||||
ui.reset_status();
|
||||
@@ -656,13 +692,13 @@ void unified_bed_leveling::G29() {
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Z Probe End Script: ", Z_PROBE_END_SCRIPT);
|
||||
if (probe_deployed) {
|
||||
planner.synchronize();
|
||||
gcode.process_subcommands_now_P(PSTR(Z_PROBE_END_SCRIPT));
|
||||
gcode.process_subcommands_now(F(Z_PROBE_END_SCRIPT));
|
||||
}
|
||||
#else
|
||||
UNUSED(probe_deployed);
|
||||
#endif
|
||||
|
||||
TERN_(HAS_MULTI_HOTEND, tool_change(old_tool_index));
|
||||
TERN_(HAS_MULTI_HOTEND, if (old_tool_index != 0) tool_change(old_tool_index));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -724,7 +760,9 @@ void unified_bed_leveling::shift_mesh_height() {
|
||||
void unified_bed_leveling::probe_entire_mesh(const xy_pos_t &nearby, const bool do_ubl_mesh_map, const bool stow_probe, const bool do_furthest) {
|
||||
probe.deploy(); // Deploy before ui.capture() to allow for PAUSE_BEFORE_DEPLOY_STOW
|
||||
|
||||
TERN_(HAS_LCD_MENU, ui.capture());
|
||||
TERN_(HAS_MARLINUI_MENU, ui.capture());
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onLevelingStart());
|
||||
TERN_(DWIN_LCD_PROUI, DWIN_LevelingStart());
|
||||
|
||||
save_ubl_active_state_and_disable(); // No bed level correction so only raw data is obtained
|
||||
uint8_t count = GRID_MAX_POINTS;
|
||||
@@ -736,9 +774,9 @@ void unified_bed_leveling::shift_mesh_height() {
|
||||
|
||||
const uint8_t point_num = (GRID_MAX_POINTS - count) + 1;
|
||||
SERIAL_ECHOLNPGM("Probing mesh point ", point_num, "/", GRID_MAX_POINTS, ".");
|
||||
TERN_(HAS_STATUS_MESSAGE, ui.status_printf_P(0, PSTR(S_FMT " %i/%i"), GET_TEXT(MSG_PROBING_POINT), point_num, int(GRID_MAX_POINTS)));
|
||||
TERN_(HAS_STATUS_MESSAGE, ui.status_printf(0, F(S_FMT " %i/%i"), GET_TEXT(MSG_PROBING_POINT), point_num, int(GRID_MAX_POINTS)));
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
#if HAS_MARLINUI_MENU
|
||||
if (ui.button_pressed()) {
|
||||
ui.quick_feedback(false); // Preserve button state for click-and-hold
|
||||
SERIAL_ECHOLNPGM("\nMesh only partially populated.\n");
|
||||
@@ -746,6 +784,7 @@ void unified_bed_leveling::shift_mesh_height() {
|
||||
ui.quick_feedback();
|
||||
ui.release();
|
||||
probe.stow(); // Release UI before stow to allow for PAUSE_BEFORE_DEPLOY_STOW
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onLevelingDone());
|
||||
return restore_ubl_active_state_and_leave();
|
||||
}
|
||||
#endif
|
||||
@@ -773,9 +812,9 @@ void unified_bed_leveling::shift_mesh_height() {
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(best.pos, ExtUI::G29_FINISH));
|
||||
|
||||
// Release UI during stow to allow for PAUSE_BEFORE_DEPLOY_STOW
|
||||
TERN_(HAS_LCD_MENU, ui.release());
|
||||
TERN_(HAS_MARLINUI_MENU, ui.release());
|
||||
probe.stow();
|
||||
TERN_(HAS_LCD_MENU, ui.capture());
|
||||
TERN_(HAS_MARLINUI_MENU, ui.capture());
|
||||
|
||||
probe.move_z_after_probing();
|
||||
|
||||
@@ -785,20 +824,23 @@ void unified_bed_leveling::shift_mesh_height() {
|
||||
constrain(nearby.x - probe.offset_xy.x, MESH_MIN_X, MESH_MAX_X),
|
||||
constrain(nearby.y - probe.offset_xy.y, MESH_MIN_Y, MESH_MAX_Y)
|
||||
);
|
||||
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onLevelingDone());
|
||||
TERN_(DWIN_LCD_PROUI, DWIN_LevelingDone());
|
||||
}
|
||||
|
||||
#endif // HAS_BED_PROBE
|
||||
|
||||
void set_message_with_feedback(PGM_P const msg_P) {
|
||||
#if HAS_LCD_MENU
|
||||
ui.set_status_P(msg_P);
|
||||
void set_message_with_feedback(FSTR_P const fstr) {
|
||||
#if HAS_MARLINUI_MENU
|
||||
ui.set_status(fstr);
|
||||
ui.quick_feedback();
|
||||
#else
|
||||
UNUSED(msg_P);
|
||||
UNUSED(fstr);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
#if HAS_MARLINUI_MENU
|
||||
|
||||
typedef void (*clickFunc_t)();
|
||||
|
||||
@@ -850,7 +892,7 @@ void set_message_with_feedback(PGM_P const msg_P) {
|
||||
planner.synchronize();
|
||||
|
||||
SERIAL_ECHOPGM("Place shim under nozzle");
|
||||
LCD_MESSAGEPGM(MSG_UBL_BC_INSERT);
|
||||
LCD_MESSAGE(MSG_UBL_BC_INSERT);
|
||||
ui.return_to_status();
|
||||
echo_and_take_a_measurement();
|
||||
|
||||
@@ -859,7 +901,7 @@ void set_message_with_feedback(PGM_P const msg_P) {
|
||||
planner.synchronize();
|
||||
|
||||
SERIAL_ECHOPGM("Remove shim");
|
||||
LCD_MESSAGEPGM(MSG_UBL_BC_REMOVE);
|
||||
LCD_MESSAGE(MSG_UBL_BC_REMOVE);
|
||||
echo_and_take_a_measurement();
|
||||
|
||||
const float z2 = measure_point_with_encoder();
|
||||
@@ -884,6 +926,7 @@ void set_message_with_feedback(PGM_P const msg_P) {
|
||||
*/
|
||||
void unified_bed_leveling::manually_probe_remaining_mesh(const xy_pos_t &pos, const_float_t z_clearance, const_float_t thick, const bool do_ubl_mesh_map) {
|
||||
ui.capture();
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onLevelingStart());
|
||||
|
||||
save_ubl_active_state_and_disable(); // No bed level correction so only raw data is obtained
|
||||
do_blocking_move_to_xy_z(current_position, z_clearance);
|
||||
@@ -897,15 +940,11 @@ void set_message_with_feedback(PGM_P const msg_P) {
|
||||
// It doesn't matter if the probe can't reach the NAN location. This is a manual probe.
|
||||
if (!location.valid()) continue;
|
||||
|
||||
const xyz_pos_t ppos = {
|
||||
mesh_index_to_xpos(lpos.x),
|
||||
mesh_index_to_ypos(lpos.y),
|
||||
z_clearance
|
||||
};
|
||||
const xyz_pos_t ppos = { get_mesh_x(lpos.x), get_mesh_y(lpos.y), z_clearance };
|
||||
|
||||
if (!position_is_reachable(ppos)) break; // SHOULD NOT OCCUR (find_closest_mesh_point only returns reachable points)
|
||||
|
||||
LCD_MESSAGEPGM(MSG_UBL_MOVING_TO_NEXT);
|
||||
LCD_MESSAGE(MSG_UBL_MOVING_TO_NEXT);
|
||||
|
||||
do_blocking_move_to(ppos);
|
||||
do_z_clearance(z_clearance);
|
||||
@@ -917,11 +956,11 @@ void set_message_with_feedback(PGM_P const msg_P) {
|
||||
|
||||
if (parser.seen_test('B')) {
|
||||
SERIAL_ECHOPGM("Place Shim & Measure");
|
||||
LCD_MESSAGEPGM(MSG_UBL_BC_INSERT);
|
||||
LCD_MESSAGE(MSG_UBL_BC_INSERT);
|
||||
}
|
||||
else {
|
||||
SERIAL_ECHOPGM("Measure");
|
||||
LCD_MESSAGEPGM(MSG_UBL_BC_INSERT2);
|
||||
LCD_MESSAGE(MSG_UBL_BC_INSERT2);
|
||||
}
|
||||
|
||||
const float z_step = 0.01f; // 0.01mm per encoder tick, occasionally step
|
||||
@@ -947,6 +986,8 @@ void set_message_with_feedback(PGM_P const msg_P) {
|
||||
|
||||
restore_ubl_active_state_and_leave();
|
||||
do_blocking_move_to_xy_z(pos, Z_CLEARANCE_DEPLOY_PROBE);
|
||||
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onLevelingDone());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -974,7 +1015,7 @@ void set_message_with_feedback(PGM_P const msg_P) {
|
||||
|
||||
save_ubl_active_state_and_disable();
|
||||
|
||||
LCD_MESSAGEPGM(MSG_UBL_FINE_TUNE_MESH);
|
||||
LCD_MESSAGE(MSG_UBL_FINE_TUNE_MESH);
|
||||
ui.capture(); // Take over control of the LCD encoder
|
||||
|
||||
do_blocking_move_to_xy_z(pos, Z_CLEARANCE_BETWEEN_PROBES); // Move to the given XY with probe clearance
|
||||
@@ -994,11 +1035,7 @@ void set_message_with_feedback(PGM_P const msg_P) {
|
||||
|
||||
done_flags.mark(lpos); // Mark this location as 'adjusted' so a new
|
||||
// location is used on the next loop
|
||||
const xyz_pos_t raw = {
|
||||
mesh_index_to_xpos(lpos.x),
|
||||
mesh_index_to_ypos(lpos.y),
|
||||
Z_CLEARANCE_BETWEEN_PROBES
|
||||
};
|
||||
const xyz_pos_t raw = { get_mesh_x(lpos.x), get_mesh_y(lpos.y), Z_CLEARANCE_BETWEEN_PROBES };
|
||||
|
||||
if (!position_is_reachable(raw)) break; // SHOULD NOT OCCUR (find_closest_mesh_point_of_type only returns reachable)
|
||||
|
||||
@@ -1039,7 +1076,7 @@ void set_message_with_feedback(PGM_P const msg_P) {
|
||||
if (_click_and_hold([]{
|
||||
ui.return_to_status();
|
||||
do_z_clearance(Z_CLEARANCE_BETWEEN_PROBES);
|
||||
set_message_with_feedback(GET_TEXT(MSG_EDITING_STOPPED));
|
||||
set_message_with_feedback(GET_TEXT_F(MSG_EDITING_STOPPED));
|
||||
})) break;
|
||||
|
||||
// TODO: Disable leveling here so the Z value becomes the 'native' Z value.
|
||||
@@ -1060,7 +1097,7 @@ void set_message_with_feedback(PGM_P const msg_P) {
|
||||
|
||||
do_blocking_move_to_xy_z(pos, Z_CLEARANCE_BETWEEN_PROBES);
|
||||
|
||||
LCD_MESSAGEPGM(MSG_UBL_DONE_EDITING_MESH);
|
||||
LCD_MESSAGE(MSG_UBL_DONE_EDITING_MESH);
|
||||
SERIAL_ECHOLNPGM("Done Editing Mesh");
|
||||
|
||||
if (lcd_map_control)
|
||||
@@ -1069,7 +1106,7 @@ void set_message_with_feedback(PGM_P const msg_P) {
|
||||
ui.return_to_status();
|
||||
}
|
||||
|
||||
#endif // HAS_LCD_MENU
|
||||
#endif // HAS_MARLINUI_MENU
|
||||
|
||||
/**
|
||||
* Parse and validate most G29 parameters, store for use by G29 functions.
|
||||
@@ -1077,7 +1114,7 @@ void set_message_with_feedback(PGM_P const msg_P) {
|
||||
bool unified_bed_leveling::G29_parse_parameters() {
|
||||
bool err_flag = false;
|
||||
|
||||
set_message_with_feedback(GET_TEXT(MSG_UBL_DOING_G29));
|
||||
set_message_with_feedback(GET_TEXT_F(MSG_UBL_DOING_G29));
|
||||
|
||||
param.C_constant = 0;
|
||||
param.R_repetition = 0;
|
||||
@@ -1200,7 +1237,7 @@ void unified_bed_leveling::save_ubl_active_state_and_disable() {
|
||||
ubl_state_recursion_chk++;
|
||||
if (ubl_state_recursion_chk != 1) {
|
||||
SERIAL_ECHOLNPGM("save_ubl_active_state_and_disabled() called multiple times in a row.");
|
||||
set_message_with_feedback(GET_TEXT(MSG_UBL_SAVE_ERROR));
|
||||
set_message_with_feedback(GET_TEXT_F(MSG_UBL_SAVE_ERROR));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@@ -1209,15 +1246,16 @@ void unified_bed_leveling::save_ubl_active_state_and_disable() {
|
||||
}
|
||||
|
||||
void unified_bed_leveling::restore_ubl_active_state_and_leave() {
|
||||
TERN_(HAS_LCD_MENU, ui.release());
|
||||
TERN_(HAS_MARLINUI_MENU, ui.release());
|
||||
#if ENABLED(UBL_DEVEL_DEBUGGING)
|
||||
if (--ubl_state_recursion_chk) {
|
||||
SERIAL_ECHOLNPGM("restore_ubl_active_state_and_leave() called too many times.");
|
||||
set_message_with_feedback(GET_TEXT(MSG_UBL_RESTORE_ERROR));
|
||||
set_message_with_feedback(GET_TEXT_F(MSG_UBL_RESTORE_ERROR));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
set_bed_leveling_enabled(ubl_state_at_invocation);
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onLevelingDone());
|
||||
}
|
||||
|
||||
mesh_index_pair unified_bed_leveling::find_furthest_invalid_mesh_point() {
|
||||
@@ -1230,7 +1268,7 @@ mesh_index_pair unified_bed_leveling::find_furthest_invalid_mesh_point() {
|
||||
if (!isnan(z_values[i][j])) continue; // Skip valid mesh points
|
||||
|
||||
// Skip unreachable points
|
||||
if (!probe.can_reach(mesh_index_to_xpos(i), mesh_index_to_ypos(j)))
|
||||
if (!probe.can_reach(get_mesh_x(i), get_mesh_y(j)))
|
||||
continue;
|
||||
|
||||
found_a_NAN = true;
|
||||
@@ -1282,11 +1320,11 @@ mesh_index_pair unified_bed_leveling::find_furthest_invalid_mesh_point() {
|
||||
|
||||
static bool test_func(uint8_t i, uint8_t j, void *data) {
|
||||
find_closest_t *d = (find_closest_t*)data;
|
||||
if ( d->type == CLOSEST || d->type == (isnan(ubl.z_values[i][j]) ? INVALID : REAL)
|
||||
if ( d->type == CLOSEST || d->type == (isnan(bedlevel.z_values[i][j]) ? INVALID : REAL)
|
||||
|| (d->type == SET_IN_BITMAP && !d->done_flags->marked(i, j))
|
||||
) {
|
||||
// Found a Mesh Point of the specified type!
|
||||
const xy_pos_t mpos = { ubl.mesh_index_to_xpos(i), ubl.mesh_index_to_ypos(j) };
|
||||
const xy_pos_t mpos = { bedlevel.get_mesh_x(i), bedlevel.get_mesh_y(j) };
|
||||
|
||||
// If using the probe as the reference there are some unreachable locations.
|
||||
// Also for round beds, there are grid points outside the bed the nozzle can't reach.
|
||||
@@ -1330,7 +1368,7 @@ mesh_index_pair unified_bed_leveling::find_closest_mesh_point_of_type(const Mesh
|
||||
|| (type == SET_IN_BITMAP && !done_flags->marked(i, j))
|
||||
) {
|
||||
// Found a Mesh Point of the specified type!
|
||||
const xy_pos_t mpos = { mesh_index_to_xpos(i), mesh_index_to_ypos(j) };
|
||||
const xy_pos_t mpos = { get_mesh_x(i), get_mesh_y(j) };
|
||||
|
||||
// If using the probe as the reference there are some unreachable locations.
|
||||
// Also for round beds, there are grid points outside the bed the nozzle can't reach.
|
||||
@@ -1386,10 +1424,10 @@ typedef struct { uint8_t sx, ex, sy, ey; bool yfirst; } smart_fill_info;
|
||||
|
||||
void unified_bed_leveling::smart_fill_mesh() {
|
||||
static const smart_fill_info
|
||||
info0 PROGMEM = { 0, GRID_MAX_POINTS_X, 0, GRID_MAX_POINTS_Y - 2, false }, // Bottom of the mesh looking up
|
||||
info1 PROGMEM = { 0, GRID_MAX_POINTS_X, GRID_MAX_POINTS_Y - 1, 0, false }, // Top of the mesh looking down
|
||||
info2 PROGMEM = { 0, GRID_MAX_POINTS_X - 2, 0, GRID_MAX_POINTS_Y, true }, // Left side of the mesh looking right
|
||||
info3 PROGMEM = { GRID_MAX_POINTS_X - 1, 0, 0, GRID_MAX_POINTS_Y, true }; // Right side of the mesh looking left
|
||||
info0 PROGMEM = { 0, GRID_MAX_POINTS_X, 0, (GRID_MAX_POINTS_Y) - 2, false }, // Bottom of the mesh looking up
|
||||
info1 PROGMEM = { 0, GRID_MAX_POINTS_X, (GRID_MAX_POINTS_Y) - 1, 0, false }, // Top of the mesh looking down
|
||||
info2 PROGMEM = { 0, (GRID_MAX_POINTS_X) - 2, 0, GRID_MAX_POINTS_Y, true }, // Left side of the mesh looking right
|
||||
info3 PROGMEM = { (GRID_MAX_POINTS_X) - 1, 0, 0, GRID_MAX_POINTS_Y, true }; // Right side of the mesh looking left
|
||||
static const smart_fill_info * const info[] PROGMEM = { &info0, &info1, &info2, &info3 };
|
||||
|
||||
LOOP_L_N(i, COUNT(info)) {
|
||||
@@ -1438,7 +1476,7 @@ void unified_bed_leveling::smart_fill_mesh() {
|
||||
|
||||
if (do_3_pt_leveling) {
|
||||
SERIAL_ECHOLNPGM("Tilting mesh (1/3)");
|
||||
TERN_(HAS_STATUS_MESSAGE, ui.status_printf_P(0, PSTR(S_FMT " 1/3"), GET_TEXT(MSG_LCD_TILTING_MESH)));
|
||||
TERN_(HAS_STATUS_MESSAGE, ui.status_printf(0, F(S_FMT " 1/3"), GET_TEXT(MSG_LCD_TILTING_MESH)));
|
||||
|
||||
measured_z = probe.probe_at_point(points[0], PROBE_PT_RAISE, param.V_verbosity);
|
||||
if (isnan(measured_z))
|
||||
@@ -1457,7 +1495,7 @@ void unified_bed_leveling::smart_fill_mesh() {
|
||||
|
||||
if (!abort_flag) {
|
||||
SERIAL_ECHOLNPGM("Tilting mesh (2/3)");
|
||||
TERN_(HAS_STATUS_MESSAGE, ui.status_printf_P(0, PSTR(S_FMT " 2/3"), GET_TEXT(MSG_LCD_TILTING_MESH)));
|
||||
TERN_(HAS_STATUS_MESSAGE, ui.status_printf(0, F(S_FMT " 2/3"), GET_TEXT(MSG_LCD_TILTING_MESH)));
|
||||
|
||||
measured_z = probe.probe_at_point(points[1], PROBE_PT_RAISE, param.V_verbosity);
|
||||
#ifdef VALIDATE_MESH_TILT
|
||||
@@ -1477,7 +1515,7 @@ void unified_bed_leveling::smart_fill_mesh() {
|
||||
|
||||
if (!abort_flag) {
|
||||
SERIAL_ECHOLNPGM("Tilting mesh (3/3)");
|
||||
TERN_(HAS_STATUS_MESSAGE, ui.status_printf_P(0, PSTR(S_FMT " 3/3"), GET_TEXT(MSG_LCD_TILTING_MESH)));
|
||||
TERN_(HAS_STATUS_MESSAGE, ui.status_printf(0, F(S_FMT " 3/3"), GET_TEXT(MSG_LCD_TILTING_MESH)));
|
||||
|
||||
measured_z = probe.probe_at_point(points[2], PROBE_PT_LAST_STOW, param.V_verbosity);
|
||||
#ifdef VALIDATE_MESH_TILT
|
||||
@@ -1518,7 +1556,7 @@ void unified_bed_leveling::smart_fill_mesh() {
|
||||
|
||||
if (!abort_flag) {
|
||||
SERIAL_ECHOLNPGM("Tilting mesh point ", point_num, "/", total_points, "\n");
|
||||
TERN_(HAS_STATUS_MESSAGE, ui.status_printf_P(0, PSTR(S_FMT " %i/%i"), GET_TEXT(MSG_LCD_TILTING_MESH), point_num, total_points));
|
||||
TERN_(HAS_STATUS_MESSAGE, ui.status_printf(0, F(S_FMT " %i/%i"), GET_TEXT(MSG_LCD_TILTING_MESH), point_num, total_points));
|
||||
|
||||
measured_z = probe.probe_at_point(rpos, parser.seen_test('E') ? PROBE_PT_STOW : PROBE_PT_RAISE, param.V_verbosity); // TODO: Needs error handling
|
||||
|
||||
@@ -1578,9 +1616,7 @@ void unified_bed_leveling::smart_fill_mesh() {
|
||||
matrix_3x3 rotation = matrix_3x3::create_look_at(vector_3(lsf_results.A, lsf_results.B, 1));
|
||||
|
||||
GRID_LOOP(i, j) {
|
||||
float mx = mesh_index_to_xpos(i),
|
||||
my = mesh_index_to_ypos(j),
|
||||
mz = z_values[i][j];
|
||||
float mx = get_mesh_x(i), my = get_mesh_y(j), mz = z_values[i][j];
|
||||
|
||||
if (DEBUGGING(LEVELING)) {
|
||||
DEBUG_ECHOPAIR_F("before rotation = [", mx, 7);
|
||||
@@ -1609,7 +1645,7 @@ void unified_bed_leveling::smart_fill_mesh() {
|
||||
}
|
||||
|
||||
if (DEBUGGING(LEVELING)) {
|
||||
rotation.debug(PSTR("rotation matrix:\n"));
|
||||
rotation.debug(F("rotation matrix:\n"));
|
||||
DEBUG_ECHOPAIR_F("LSF Results A=", lsf_results.A, 7);
|
||||
DEBUG_ECHOPAIR_F(" B=", lsf_results.B, 7);
|
||||
DEBUG_ECHOLNPAIR_F(" D=", lsf_results.D, 7);
|
||||
@@ -1636,14 +1672,14 @@ void unified_bed_leveling::smart_fill_mesh() {
|
||||
auto normed = [&](const xy_pos_t &pos, const_float_t zadd) {
|
||||
return normal.x * pos.x + normal.y * pos.y + zadd;
|
||||
};
|
||||
auto debug_pt = [](PGM_P const pre, const xy_pos_t &pos, const_float_t zadd) {
|
||||
d_from(); SERIAL_ECHOPGM_P(pre);
|
||||
auto debug_pt = [](FSTR_P const pre, const xy_pos_t &pos, const_float_t zadd) {
|
||||
d_from(); SERIAL_ECHOF(pre);
|
||||
DEBUG_ECHO_F(normed(pos, zadd), 6);
|
||||
DEBUG_ECHOLNPAIR_F(" Z error = ", zadd - get_z_correction(pos), 6);
|
||||
};
|
||||
debug_pt(PSTR("1st point: "), probe_pt[0], normal.z * z1);
|
||||
debug_pt(PSTR("2nd point: "), probe_pt[1], normal.z * z2);
|
||||
debug_pt(PSTR("3rd point: "), probe_pt[2], normal.z * z3);
|
||||
debug_pt(F("1st point: "), probe_pt[0], normal.z * z1);
|
||||
debug_pt(F("2nd point: "), probe_pt[1], normal.z * z2);
|
||||
debug_pt(F("3rd point: "), probe_pt[2], normal.z * z3);
|
||||
d_from(); DEBUG_ECHOPGM("safe home with Z=");
|
||||
DEBUG_ECHOLNPAIR_F("0 : ", normed(safe_homing_xy, 0), 6);
|
||||
d_from(); DEBUG_ECHOPGM("safe home with Z=");
|
||||
@@ -1677,18 +1713,18 @@ void unified_bed_leveling::smart_fill_mesh() {
|
||||
|
||||
xy_pos_t ppos;
|
||||
LOOP_L_N(ix, GRID_MAX_POINTS_X) {
|
||||
ppos.x = mesh_index_to_xpos(ix);
|
||||
ppos.x = get_mesh_x(ix);
|
||||
LOOP_L_N(iy, GRID_MAX_POINTS_Y) {
|
||||
ppos.y = mesh_index_to_ypos(iy);
|
||||
ppos.y = get_mesh_y(iy);
|
||||
if (isnan(z_values[ix][iy])) {
|
||||
// undefined mesh point at (ppos.x,ppos.y), compute weighted LSF from original valid mesh points.
|
||||
incremental_LSF_reset(&lsf_results);
|
||||
xy_pos_t rpos;
|
||||
LOOP_L_N(jx, GRID_MAX_POINTS_X) {
|
||||
rpos.x = mesh_index_to_xpos(jx);
|
||||
rpos.x = get_mesh_x(jx);
|
||||
LOOP_L_N(jy, GRID_MAX_POINTS_Y) {
|
||||
if (TEST(bitmap[jx], jy)) {
|
||||
rpos.y = mesh_index_to_ypos(jy);
|
||||
rpos.y = get_mesh_y(jy);
|
||||
const float rz = z_values[jx][jy],
|
||||
w = 1.0f + weight_scaled / (rpos - ppos).magnitude();
|
||||
incremental_WLSF(&lsf_results, rpos, rz, w);
|
||||
@@ -1747,7 +1783,7 @@ void unified_bed_leveling::smart_fill_mesh() {
|
||||
|
||||
SERIAL_ECHOPGM("X-Axis Mesh Points at: ");
|
||||
LOOP_L_N(i, GRID_MAX_POINTS_X) {
|
||||
SERIAL_ECHO_F(LOGICAL_X_POSITION(mesh_index_to_xpos(i)), 3);
|
||||
SERIAL_ECHO_F(LOGICAL_X_POSITION(get_mesh_x(i)), 3);
|
||||
SERIAL_ECHOPGM(" ");
|
||||
serial_delay(25);
|
||||
}
|
||||
@@ -1755,7 +1791,7 @@ void unified_bed_leveling::smart_fill_mesh() {
|
||||
|
||||
SERIAL_ECHOPGM("Y-Axis Mesh Points at: ");
|
||||
LOOP_L_N(i, GRID_MAX_POINTS_Y) {
|
||||
SERIAL_ECHO_F(LOGICAL_Y_POSITION(mesh_index_to_ypos(i)), 3);
|
||||
SERIAL_ECHO_F(LOGICAL_Y_POSITION(get_mesh_y(i)), 3);
|
||||
SERIAL_ECHOPGM(" ");
|
||||
serial_delay(25);
|
||||
}
|
||||
|
@@ -26,7 +26,6 @@
|
||||
|
||||
#include "../bedlevel.h"
|
||||
#include "../../../module/planner.h"
|
||||
#include "../../../module/stepper.h"
|
||||
#include "../../../module/motion.h"
|
||||
|
||||
#if ENABLED(DELTA)
|
||||
@@ -36,8 +35,18 @@
|
||||
#include "../../../MarlinCore.h"
|
||||
#include <math.h>
|
||||
|
||||
//#define DEBUG_UBL_MOTION
|
||||
#define DEBUG_OUT ENABLED(DEBUG_UBL_MOTION)
|
||||
#include "../../../core/debug_out.h"
|
||||
|
||||
#if !UBL_SEGMENTED
|
||||
|
||||
// TODO: The first and last parts of a move might result in very short segment(s)
|
||||
// after getting split on the cell boundary, so moves like that should not
|
||||
// get split. This will be most common for moves that start/end near the
|
||||
// corners of cells. To fix the issue, simply check if the start/end of the line
|
||||
// is very close to a cell boundary in advance and don't split the line there.
|
||||
|
||||
void unified_bed_leveling::line_to_destination_cartesian(const_feedRate_t scaled_fr_mm_s, const uint8_t extruder) {
|
||||
/**
|
||||
* Much of the nozzle movement will be within the same cell. So we will do as little computation
|
||||
@@ -76,8 +85,8 @@
|
||||
#endif
|
||||
|
||||
// The distance is always MESH_X_DIST so multiply by the constant reciprocal.
|
||||
const float xratio = (end.x - mesh_index_to_xpos(iend.x)) * RECIPROCAL(MESH_X_DIST),
|
||||
yratio = (end.y - mesh_index_to_ypos(iend.y)) * RECIPROCAL(MESH_Y_DIST),
|
||||
const float xratio = (end.x - get_mesh_x(iend.x)) * RECIPROCAL(MESH_X_DIST),
|
||||
yratio = (end.y - get_mesh_y(iend.y)) * RECIPROCAL(MESH_Y_DIST),
|
||||
z1 = z_values[iend.x][iend.y ] + xratio * (z_values[iend.x + 1][iend.y ] - z_values[iend.x][iend.y ]),
|
||||
z2 = z_values[iend.x][iend.y + 1] + xratio * (z_values[iend.x + 1][iend.y + 1] - z_values[iend.x][iend.y + 1]);
|
||||
|
||||
@@ -139,7 +148,7 @@
|
||||
icell.y += ineg.y; // Line going down? Just go to the bottom.
|
||||
while (icell.y != iend.y + ineg.y) {
|
||||
icell.y += iadd.y;
|
||||
const float next_mesh_line_y = mesh_index_to_ypos(icell.y);
|
||||
const float next_mesh_line_y = get_mesh_y(icell.y);
|
||||
|
||||
/**
|
||||
* Skip the calculations for an infinite slope.
|
||||
@@ -155,7 +164,7 @@
|
||||
// Replace NAN corrections with 0.0 to prevent NAN propagation.
|
||||
if (isnan(z0)) z0 = 0.0;
|
||||
|
||||
dest.y = mesh_index_to_ypos(icell.y);
|
||||
dest.y = get_mesh_y(icell.y);
|
||||
|
||||
/**
|
||||
* Without this check, it's possible to generate a zero length move, as in the case where
|
||||
@@ -176,7 +185,9 @@
|
||||
dest.z += z0;
|
||||
planner.buffer_segment(dest, scaled_fr_mm_s, extruder);
|
||||
|
||||
} //else printf("FIRST MOVE PRUNED ");
|
||||
}
|
||||
else
|
||||
DEBUG_ECHOLNPGM("[ubl] skip Y segment");
|
||||
}
|
||||
|
||||
// At the final destination? Usually not, but when on a Y Mesh Line it's completed.
|
||||
@@ -196,7 +207,7 @@
|
||||
|
||||
while (icell.x != iend.x + ineg.x) {
|
||||
icell.x += iadd.x;
|
||||
dest.x = mesh_index_to_xpos(icell.x);
|
||||
dest.x = get_mesh_x(icell.x);
|
||||
dest.y = ratio * dest.x + c; // Calculate Y at the next X mesh line
|
||||
|
||||
float z0 = z_correction_for_y_on_vertical_mesh_line(dest.y, icell.x, icell.y)
|
||||
@@ -225,7 +236,9 @@
|
||||
dest.z += z0;
|
||||
if (!planner.buffer_segment(dest, scaled_fr_mm_s, extruder)) break;
|
||||
|
||||
} //else printf("FIRST MOVE PRUNED ");
|
||||
}
|
||||
else
|
||||
DEBUG_ECHOLNPGM("[ubl] skip Y segment");
|
||||
}
|
||||
|
||||
if (xy_pos_t(current_position) != xy_pos_t(end))
|
||||
@@ -245,8 +258,8 @@
|
||||
|
||||
while (cnt) {
|
||||
|
||||
const float next_mesh_line_x = mesh_index_to_xpos(icell.x + iadd.x),
|
||||
next_mesh_line_y = mesh_index_to_ypos(icell.y + iadd.y);
|
||||
const float next_mesh_line_x = get_mesh_x(icell.x + iadd.x),
|
||||
next_mesh_line_y = get_mesh_y(icell.y + iadd.y);
|
||||
|
||||
dest.y = ratio * next_mesh_line_x + c; // Calculate Y at the next X mesh line
|
||||
dest.x = (next_mesh_line_y - c) / ratio; // Calculate X at the next Y mesh line
|
||||
@@ -340,7 +353,7 @@
|
||||
* Returns true if did NOT move, false if moved (requires current_position update).
|
||||
*/
|
||||
|
||||
bool _O2 unified_bed_leveling::line_to_destination_segmented(const_feedRate_t scaled_fr_mm_s) {
|
||||
bool __O2 unified_bed_leveling::line_to_destination_segmented(const_feedRate_t scaled_fr_mm_s) {
|
||||
|
||||
if (!position_is_reachable(destination)) // fail if moving outside reachable boundary
|
||||
return true; // did not move, so current_position still accurate
|
||||
@@ -360,11 +373,12 @@
|
||||
#endif
|
||||
|
||||
NOLESS(segments, 1U); // Must have at least one segment
|
||||
const float inv_segments = 1.0f / segments, // Reciprocal to save calculation
|
||||
segment_xyz_mm = SQRT(cart_xy_mm_2 + sq(total.z)) * inv_segments; // Length of each segment
|
||||
const float inv_segments = 1.0f / segments; // Reciprocal to save calculation
|
||||
|
||||
// Add hints to help optimize the move
|
||||
PlannerHints hints(SQRT(cart_xy_mm_2 + sq(total.z)) * inv_segments); // Length of each segment
|
||||
#if ENABLED(SCARA_FEEDRATE_SCALING)
|
||||
const float inv_duration = scaled_fr_mm_s / segment_xyz_mm;
|
||||
hints.inv_duration = scaled_fr_mm_s / hints.millimeters;
|
||||
#endif
|
||||
|
||||
xyze_float_t diff = total * inv_segments;
|
||||
@@ -378,13 +392,9 @@
|
||||
if (!planner.leveling_active || !planner.leveling_active_at_z(destination.z)) {
|
||||
while (--segments) {
|
||||
raw += diff;
|
||||
planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, segment_xyz_mm
|
||||
OPTARG(SCARA_FEEDRATE_SCALING, inv_duration)
|
||||
);
|
||||
planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, hints);
|
||||
}
|
||||
planner.buffer_line(destination, scaled_fr_mm_s, active_extruder, segment_xyz_mm
|
||||
OPTARG(SCARA_FEEDRATE_SCALING, inv_duration)
|
||||
);
|
||||
planner.buffer_line(destination, scaled_fr_mm_s, active_extruder, hints);
|
||||
return false; // Did not set current from destination
|
||||
}
|
||||
|
||||
@@ -423,7 +433,7 @@
|
||||
if (isnan(z_x0y1)) z_x0y1 = 0; // in order to avoid isnan tests per cell,
|
||||
if (isnan(z_x1y1)) z_x1y1 = 0; // thus guessing zero for undefined points
|
||||
|
||||
const xy_pos_t pos = { mesh_index_to_xpos(icell.x), mesh_index_to_ypos(icell.y) };
|
||||
const xy_pos_t pos = { get_mesh_x(icell.x), get_mesh_y(icell.y) };
|
||||
xy_pos_t cell = raw - pos;
|
||||
|
||||
const float z_xmy0 = (z_x1y0 - z_x0y0) * RECIPROCAL(MESH_X_DIST), // z slope per x along y0 (lower left to lower right)
|
||||
@@ -450,13 +460,10 @@
|
||||
if (--segments == 0) raw = destination; // if this is last segment, use destination for exact
|
||||
|
||||
const float z_cxcy = (z_cxy0 + z_cxym * cell.y) // interpolated mesh z height along cell.x at cell.y
|
||||
#if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
|
||||
* fade_scaling_factor // apply fade factor to interpolated mesh height
|
||||
#endif
|
||||
;
|
||||
TERN_(ENABLE_LEVELING_FADE_HEIGHT, * fade_scaling_factor); // apply fade factor to interpolated height
|
||||
|
||||
const float oldz = raw.z; raw.z += z_cxcy;
|
||||
planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, segment_xyz_mm OPTARG(SCARA_FEEDRATE_SCALING, inv_duration) );
|
||||
planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, hints);
|
||||
raw.z = oldz;
|
||||
|
||||
if (segments == 0) // done with last segment
|
||||
|
@@ -28,7 +28,12 @@
|
||||
|
||||
BLTouch bltouch;
|
||||
|
||||
bool BLTouch::last_written_mode; // Initialized by settings.load, 0 = Open Drain; 1 = 5V Drain
|
||||
bool BLTouch::od_5v_mode; // Initialized by settings.load, 0 = Open Drain; 1 = 5V Drain
|
||||
#ifdef BLTOUCH_HS_MODE
|
||||
bool BLTouch::high_speed_mode; // Initialized by settings.load, 0 = Low Speed; 1 = High Speed
|
||||
#else
|
||||
constexpr bool BLTouch::high_speed_mode;
|
||||
#endif
|
||||
|
||||
#include "../module/servo.h"
|
||||
#include "../module/probe.h"
|
||||
@@ -40,7 +45,7 @@ void stop();
|
||||
|
||||
bool BLTouch::command(const BLTCommand cmd, const millis_t &ms) {
|
||||
if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPGM("BLTouch Command :", cmd);
|
||||
MOVE_SERVO(Z_PROBE_SERVO_NR, cmd);
|
||||
servo[Z_PROBE_SERVO_NR].move(cmd);
|
||||
safe_delay(_MAX(ms, (uint32_t)BLTOUCH_DELAY)); // BLTOUCH_DELAY is also the *minimum* delay
|
||||
return triggered();
|
||||
}
|
||||
@@ -63,18 +68,17 @@ void BLTouch::init(const bool set_voltage/*=false*/) {
|
||||
|
||||
#else
|
||||
|
||||
if (DEBUGGING(LEVELING)) {
|
||||
DEBUG_ECHOLNPGM("last_written_mode - ", last_written_mode);
|
||||
DEBUG_ECHOLNPGM("config mode - "
|
||||
#if ENABLED(BLTOUCH_SET_5V_MODE)
|
||||
"BLTOUCH_SET_5V_MODE"
|
||||
#else
|
||||
"OD"
|
||||
#endif
|
||||
);
|
||||
}
|
||||
#ifdef DEBUG_OUT
|
||||
if (DEBUGGING(LEVELING)) {
|
||||
PGMSTR(mode0, "OD");
|
||||
PGMSTR(mode1, "5V");
|
||||
DEBUG_ECHOPGM("BLTouch Mode: ");
|
||||
DEBUG_ECHOPGM_P(bltouch.od_5v_mode ? mode1 : mode0);
|
||||
DEBUG_ECHOLNPGM(" (Default " TERN(BLTOUCH_SET_5V_MODE, "5V", "OD") ")");
|
||||
}
|
||||
#endif
|
||||
|
||||
const bool should_set = last_written_mode != ENABLED(BLTOUCH_SET_5V_MODE);
|
||||
const bool should_set = od_5v_mode != ENABLED(BLTOUCH_SET_5V_MODE);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -107,11 +111,8 @@ bool BLTouch::deploy_proc() {
|
||||
// Last attempt to DEPLOY
|
||||
if (_deploy_query_alarm()) {
|
||||
// The deploy might have failed or the probe is actually triggered (nozzle too low?) again
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("BLTouch Recovery Failed");
|
||||
|
||||
SERIAL_ERROR_MSG(STR_STOP_BLTOUCH); // Tell the user something is wrong, needs action
|
||||
stop(); // but it's not too bad, no need to kill, allow restart
|
||||
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("BLTouch Deploy Failed");
|
||||
probe.probe_error_stop(); // Something is wrong, needs action, but not too bad, allow restart
|
||||
return true; // Tell our caller we goofed in case he cares to know
|
||||
}
|
||||
}
|
||||
@@ -149,12 +150,8 @@ bool BLTouch::stow_proc() {
|
||||
// But one more STOW will catch that
|
||||
// Last attempt to STOW
|
||||
if (_stow_query_alarm()) { // so if there is now STILL an ALARM condition:
|
||||
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("BLTouch Recovery Failed");
|
||||
|
||||
SERIAL_ERROR_MSG(STR_STOP_BLTOUCH); // Tell the user something is wrong, needs action
|
||||
stop(); // but it's not too bad, no need to kill, allow restart
|
||||
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("BLTouch Stow Failed");
|
||||
probe.probe_error_stop(); // Something is wrong, needs action, but not too bad, allow restart
|
||||
return true; // Tell our caller we goofed in case he cares to know
|
||||
}
|
||||
}
|
||||
@@ -193,7 +190,7 @@ void BLTouch::mode_conv_proc(const bool M5V) {
|
||||
_mode_store();
|
||||
if (M5V) _set_5V_mode(); else _set_OD_mode();
|
||||
_stow();
|
||||
last_written_mode = M5V;
|
||||
od_5v_mode = M5V;
|
||||
}
|
||||
|
||||
#endif // BLTOUCH
|
||||
|
@@ -23,10 +23,6 @@
|
||||
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
|
||||
#if DISABLED(BLTOUCH_HS_MODE)
|
||||
#define BLTOUCH_SLOW_MODE 1
|
||||
#endif
|
||||
|
||||
// BLTouch commands are sent as servo angles
|
||||
typedef unsigned char BLTCommand;
|
||||
|
||||
@@ -70,8 +66,17 @@ typedef unsigned char BLTCommand;
|
||||
|
||||
class BLTouch {
|
||||
public:
|
||||
|
||||
static void init(const bool set_voltage=false);
|
||||
static bool last_written_mode; // Initialized by settings.load, 0 = Open Drain; 1 = 5V Drain
|
||||
static bool od_5v_mode; // Initialized by settings.load, 0 = Open Drain; 1 = 5V Drain
|
||||
|
||||
#ifdef BLTOUCH_HS_MODE
|
||||
static bool high_speed_mode; // Initialized by settings.load, 0 = Low Speed; 1 = High Speed
|
||||
#else
|
||||
static constexpr bool high_speed_mode = false;
|
||||
#endif
|
||||
|
||||
static float z_extra_clearance() { return high_speed_mode ? 7 : 0; }
|
||||
|
||||
// DEPLOY and STOW are wrapped for error handling - these are used by homing and by probing
|
||||
static bool deploy() { return deploy_proc(); }
|
||||
|
@@ -46,7 +46,7 @@ void CancelObject::set_active_object(const int8_t obj) {
|
||||
|
||||
#if BOTH(HAS_STATUS_MESSAGE, CANCEL_OBJECTS_REPORTING)
|
||||
if (active_object >= 0)
|
||||
ui.status_printf_P(0, PSTR(S_FMT " %i"), GET_TEXT(MSG_PRINTING_OBJECT), int(active_object));
|
||||
ui.status_printf(0, F(S_FMT " %i"), GET_TEXT(MSG_PRINTING_OBJECT), int(active_object));
|
||||
else
|
||||
ui.reset_status();
|
||||
#endif
|
||||
|
@@ -32,10 +32,10 @@ public:
|
||||
static void cancel_object(const int8_t obj);
|
||||
static void uncancel_object(const int8_t obj);
|
||||
static void report();
|
||||
static inline bool is_canceled(const int8_t obj) { return TEST(canceled, obj); }
|
||||
static inline void clear_active_object() { set_active_object(-1); }
|
||||
static inline void cancel_active_object() { cancel_object(active_object); }
|
||||
static inline void reset() { canceled = 0x0000; object_count = 0; clear_active_object(); }
|
||||
static bool is_canceled(const int8_t obj) { return TEST(canceled, obj); }
|
||||
static void clear_active_object() { set_active_object(-1); }
|
||||
static void cancel_active_object() { cancel_object(active_object); }
|
||||
static void reset() { canceled = 0x0000; object_count = 0; clear_active_object(); }
|
||||
};
|
||||
|
||||
extern CancelObject cancelable;
|
||||
|
@@ -39,7 +39,6 @@ CaseLight caselight;
|
||||
bool CaseLight::on = CASE_LIGHT_DEFAULT_ON;
|
||||
|
||||
#if CASE_LIGHT_IS_COLOR_LED
|
||||
#include "leds/leds.h"
|
||||
constexpr uint8_t init_case_light[] = CASE_LIGHT_DEFAULT_COLOR;
|
||||
LEDColor CaseLight::color = { init_case_light[0], init_case_light[1], init_case_light[2] OPTARG(HAS_WHITE_LED, init_case_light[3]) };
|
||||
#endif
|
||||
@@ -65,12 +64,22 @@ void CaseLight::update(const bool sflag) {
|
||||
#endif
|
||||
|
||||
#if CASE_LIGHT_IS_COLOR_LED
|
||||
leds.set_color(LEDColor(color.r, color.g, color.b OPTARG(HAS_WHITE_LED, color.w), n10ct));
|
||||
#if ENABLED(CASE_LIGHT_USE_NEOPIXEL)
|
||||
if (on)
|
||||
// Use current color of (NeoPixel) leds and new brightness level
|
||||
leds.set_color(LEDColor(leds.color.r, leds.color.g, leds.color.b OPTARG(HAS_WHITE_LED, leds.color.w) OPTARG(NEOPIXEL_LED, n10ct)));
|
||||
else
|
||||
// Switch off leds
|
||||
leds.set_off();
|
||||
#else
|
||||
// Use CaseLight color (CASE_LIGHT_DEFAULT_COLOR) and new brightness level
|
||||
leds.set_color(LEDColor(color.r, color.g, color.b OPTARG(HAS_WHITE_LED, color.w) OPTARG(NEOPIXEL_LED, n10ct)));
|
||||
#endif
|
||||
#else // !CASE_LIGHT_IS_COLOR_LED
|
||||
|
||||
#if CASELIGHT_USES_BRIGHTNESS
|
||||
if (pin_is_pwm())
|
||||
analogWrite(pin_t(CASE_LIGHT_PIN), (
|
||||
hal.set_pwm_duty(pin_t(CASE_LIGHT_PIN), (
|
||||
#if CASE_LIGHT_MAX_PWM == 255
|
||||
n10ct
|
||||
#else
|
||||
|
@@ -27,10 +27,6 @@
|
||||
#include "leds/leds.h" // for LEDColor
|
||||
#endif
|
||||
|
||||
#if NONE(CASE_LIGHT_NO_BRIGHTNESS, CASE_LIGHT_IS_COLOR_LED) || ENABLED(CASE_LIGHT_USE_NEOPIXEL)
|
||||
#define CASELIGHT_USES_BRIGHTNESS 1
|
||||
#endif
|
||||
|
||||
class CaseLight {
|
||||
public:
|
||||
static bool on;
|
||||
@@ -49,8 +45,8 @@ public:
|
||||
}
|
||||
|
||||
static void update(const bool sflag);
|
||||
static inline void update_brightness() { update(false); }
|
||||
static inline void update_enabled() { update(true); }
|
||||
static void update_brightness() { update(false); }
|
||||
static void update_enabled() { update(true); }
|
||||
|
||||
#if ENABLED(CASE_LIGHT_IS_COLOR_LED)
|
||||
private:
|
||||
|
@@ -58,7 +58,7 @@ void ControllerFan::update() {
|
||||
// - At least one stepper driver is enabled
|
||||
// - The heated bed is enabled
|
||||
// - TEMP_SENSOR_BOARD is reporting >= CONTROLLER_FAN_MIN_BOARD_TEMP
|
||||
const ena_mask_t axis_mask = TERN(CONTROLLER_FAN_USE_Z_ONLY, _BV(Z_AXIS), ~TERN0(CONTROLLER_FAN_IGNORE_Z, _BV(Z_AXIS)));
|
||||
const ena_mask_t axis_mask = TERN(CONTROLLER_FAN_USE_Z_ONLY, _BV(Z_AXIS), (ena_mask_t)~TERN0(CONTROLLER_FAN_IGNORE_Z, _BV(Z_AXIS)));
|
||||
if ( (stepper.axis_enabled.bits & axis_mask)
|
||||
|| TERN0(HAS_HEATED_BED, thermalManager.temp_bed.soft_pwm_amount > 0)
|
||||
|| TERN0(HAS_CONTROLLER_FAN_MIN_BOARD_TEMP, thermalManager.wholeDegBoard() >= CONTROLLER_FAN_MIN_BOARD_TEMP)
|
||||
@@ -72,9 +72,14 @@ void ControllerFan::update() {
|
||||
? settings.active_speed : settings.idle_speed
|
||||
);
|
||||
|
||||
// Allow digital or PWM fan output (see M42 handling)
|
||||
WRITE(CONTROLLER_FAN_PIN, speed);
|
||||
analogWrite(pin_t(CONTROLLER_FAN_PIN), speed);
|
||||
#if ENABLED(FAN_SOFT_PWM)
|
||||
thermalManager.soft_pwm_controller_speed = speed;
|
||||
#else
|
||||
if (PWM_PIN(CONTROLLER_FAN_PIN))
|
||||
hal.set_pwm_duty(pin_t(CONTROLLER_FAN_PIN), speed);
|
||||
else
|
||||
WRITE(CONTROLLER_FAN_PIN, speed > 0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -60,9 +60,9 @@ class ControllerFan {
|
||||
#else
|
||||
static const controllerFan_settings_t &settings;
|
||||
#endif
|
||||
static inline bool state() { return speed > 0; }
|
||||
static inline void init() { reset(); }
|
||||
static inline void reset() { TERN_(CONTROLLER_FAN_EDITABLE, settings = controllerFan_defaults); }
|
||||
static bool state() { return speed > 0; }
|
||||
static void init() { reset(); }
|
||||
static void reset() { TERN_(CONTROLLER_FAN_EDITABLE, settings = controllerFan_defaults); }
|
||||
static void setup();
|
||||
static void update();
|
||||
};
|
||||
|
@@ -11,7 +11,6 @@
|
||||
#include "dac_dac084s085.h"
|
||||
|
||||
#include "../../MarlinCore.h"
|
||||
#include "../../module/stepper.h"
|
||||
#include "../../HAL/shared/Delay.h"
|
||||
|
||||
dac084s085::dac084s085() { }
|
||||
|
@@ -29,7 +29,6 @@
|
||||
#if HAS_MOTOR_CURRENT_DAC
|
||||
|
||||
#include "stepper_dac.h"
|
||||
#include "../../MarlinCore.h" // for SP_X_LBL...
|
||||
|
||||
bool dac_present = false;
|
||||
constexpr xyze_uint8_t dac_order = DAC_STEPPER_ORDER;
|
||||
@@ -60,7 +59,7 @@ int StepperDAC::init() {
|
||||
}
|
||||
|
||||
void StepperDAC::set_current_value(const uint8_t channel, uint16_t val) {
|
||||
if (!dac_present) return;
|
||||
if (!(dac_present && channel < LOGICAL_AXES)) return;
|
||||
|
||||
NOMORE(val, uint16_t(DAC_STEPPER_MAX));
|
||||
|
||||
@@ -85,13 +84,11 @@ void StepperDAC::print_values() {
|
||||
if (!dac_present) return;
|
||||
SERIAL_ECHO_MSG("Stepper current values in % (Amps):");
|
||||
SERIAL_ECHO_START();
|
||||
SERIAL_ECHOPGM_P(SP_X_LBL, dac_perc(X_AXIS), PSTR(" ("), dac_amps(X_AXIS), PSTR(")"));
|
||||
#if HAS_Y_AXIS
|
||||
SERIAL_ECHOPGM_P(SP_Y_LBL, dac_perc(Y_AXIS), PSTR(" ("), dac_amps(Y_AXIS), PSTR(")"));
|
||||
#endif
|
||||
#if HAS_Z_AXIS
|
||||
SERIAL_ECHOPGM_P(SP_Z_LBL, dac_perc(Z_AXIS), PSTR(" ("), dac_amps(Z_AXIS), PSTR(")"));
|
||||
#endif
|
||||
LOOP_LOGICAL_AXES(a) {
|
||||
SERIAL_CHAR(' ', IAXIS_CHAR(a), ':');
|
||||
SERIAL_ECHO(dac_perc(a));
|
||||
SERIAL_ECHOPGM_P(PSTR(" ("), dac_amps(AxisEnum(a)), PSTR(")"));
|
||||
}
|
||||
#if HAS_EXTRUDERS
|
||||
SERIAL_ECHOLNPGM_P(SP_E_LBL, dac_perc(E_AXIS), PSTR(" ("), dac_amps(E_AXIS), PSTR(")"));
|
||||
#endif
|
||||
|
@@ -31,9 +31,13 @@
|
||||
|
||||
// Settings for the I2C based DIGIPOT (MCP4018) based on WT150
|
||||
|
||||
#define DIGIPOT_A4988_Rsx 0.250
|
||||
#define DIGIPOT_A4988_Vrefmax 1.666
|
||||
#define DIGIPOT_MCP4018_MAX_VALUE 127
|
||||
#ifndef DIGIPOT_A4988_Rsx
|
||||
#define DIGIPOT_A4988_Rsx 0.250
|
||||
#endif
|
||||
#ifndef DIGIPOT_A4988_Vrefmax
|
||||
#define DIGIPOT_A4988_Vrefmax 1.666
|
||||
#endif
|
||||
#define DIGIPOT_MCP4018_MAX_VALUE 127
|
||||
|
||||
#define DIGIPOT_A4988_Itripmax(Vref) ((Vref) / (8.0 * DIGIPOT_A4988_Rsx))
|
||||
|
||||
|
@@ -52,13 +52,13 @@ namespace DirectStepping {
|
||||
volatile bool SerialPageManager<Cfg>::fatal_error;
|
||||
|
||||
template<typename Cfg>
|
||||
volatile PageState SerialPageManager<Cfg>::page_states[Cfg::NUM_PAGES];
|
||||
volatile PageState SerialPageManager<Cfg>::page_states[Cfg::PAGE_COUNT];
|
||||
|
||||
template<typename Cfg>
|
||||
volatile bool SerialPageManager<Cfg>::page_states_dirty;
|
||||
|
||||
template<typename Cfg>
|
||||
uint8_t SerialPageManager<Cfg>::pages[Cfg::NUM_PAGES][Cfg::PAGE_SIZE];
|
||||
uint8_t SerialPageManager<Cfg>::pages[Cfg::PAGE_COUNT][Cfg::PAGE_SIZE];
|
||||
|
||||
template<typename Cfg>
|
||||
uint8_t SerialPageManager<Cfg>::checksum;
|
||||
@@ -74,7 +74,7 @@ namespace DirectStepping {
|
||||
|
||||
template <typename Cfg>
|
||||
void SerialPageManager<Cfg>::init() {
|
||||
for (int i = 0 ; i < Cfg::NUM_PAGES ; i++)
|
||||
for (int i = 0 ; i < Cfg::PAGE_COUNT ; i++)
|
||||
page_states[i] = PageState::FREE;
|
||||
|
||||
fatal_error = false;
|
||||
@@ -143,14 +143,16 @@ namespace DirectStepping {
|
||||
// special case for 8-bit, check if rolled back to 0
|
||||
if (Cfg::DIRECTIONAL || !write_page_size) { // full 256 bytes
|
||||
if (write_byte_idx) return true;
|
||||
} else {
|
||||
if (write_byte_idx < write_page_size) return true;
|
||||
}
|
||||
} else if (Cfg::DIRECTIONAL) {
|
||||
if (write_byte_idx != Cfg::PAGE_SIZE) return true;
|
||||
} else {
|
||||
if (write_byte_idx < write_page_size) return true;
|
||||
else if (write_byte_idx < write_page_size)
|
||||
return true;
|
||||
}
|
||||
else if (Cfg::DIRECTIONAL) {
|
||||
if (write_byte_idx != Cfg::PAGE_SIZE)
|
||||
return true;
|
||||
}
|
||||
else if (write_byte_idx < write_page_size)
|
||||
return true;
|
||||
|
||||
state = State::CHECKSUM;
|
||||
return true;
|
||||
@@ -161,11 +163,10 @@ namespace DirectStepping {
|
||||
return true;
|
||||
}
|
||||
case State::UNFAIL:
|
||||
if (c == 0) {
|
||||
if (c == 0)
|
||||
set_page_state(write_page_idx, PageState::FREE);
|
||||
} else {
|
||||
else
|
||||
fatal_error = true;
|
||||
}
|
||||
state = State::MONITOR;
|
||||
return true;
|
||||
}
|
||||
@@ -174,7 +175,7 @@ namespace DirectStepping {
|
||||
template <typename Cfg>
|
||||
void SerialPageManager<Cfg>::write_responses() {
|
||||
if (fatal_error) {
|
||||
kill(GET_TEXT(MSG_BAD_PAGE));
|
||||
kill(GET_TEXT_F(MSG_BAD_PAGE));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -183,10 +184,10 @@ namespace DirectStepping {
|
||||
|
||||
SERIAL_CHAR(Cfg::CONTROL_CHAR);
|
||||
constexpr int state_bits = 2;
|
||||
constexpr int n_bytes = Cfg::NUM_PAGES >> state_bits;
|
||||
constexpr int n_bytes = Cfg::PAGE_COUNT >> state_bits;
|
||||
volatile uint8_t bits_b[n_bytes] = { 0 };
|
||||
|
||||
for (page_idx_t i = 0 ; i < Cfg::NUM_PAGES ; i++) {
|
||||
for (page_idx_t i = 0 ; i < Cfg::PAGE_COUNT ; i++) {
|
||||
bits_b[i >> state_bits] |= page_states[i] << ((i * state_bits) & 0x7);
|
||||
}
|
||||
|
||||
|
@@ -68,10 +68,10 @@ namespace DirectStepping {
|
||||
static State state;
|
||||
static volatile bool fatal_error;
|
||||
|
||||
static volatile PageState page_states[Cfg::NUM_PAGES];
|
||||
static volatile PageState page_states[Cfg::PAGE_COUNT];
|
||||
static volatile bool page_states_dirty;
|
||||
|
||||
static uint8_t pages[Cfg::NUM_PAGES][Cfg::PAGE_SIZE];
|
||||
static uint8_t pages[Cfg::PAGE_COUNT][Cfg::PAGE_SIZE];
|
||||
static uint8_t checksum;
|
||||
static write_byte_idx_t write_byte_idx;
|
||||
static page_idx_t write_page_idx;
|
||||
@@ -87,8 +87,8 @@ namespace DirectStepping {
|
||||
struct config_t {
|
||||
static constexpr char CONTROL_CHAR = '!';
|
||||
|
||||
static constexpr int NUM_PAGES = num_pages;
|
||||
static constexpr int NUM_AXES = num_axes;
|
||||
static constexpr int PAGE_COUNT = num_pages;
|
||||
static constexpr int AXIS_COUNT = num_axes;
|
||||
static constexpr int BITS_SEGMENT = bits_segment;
|
||||
static constexpr int DIRECTIONAL = dir ? 1 : 0;
|
||||
static constexpr int SEGMENTS = segments;
|
||||
@@ -96,10 +96,10 @@ namespace DirectStepping {
|
||||
static constexpr int NUM_SEGMENTS = _BV(BITS_SEGMENT);
|
||||
static constexpr int SEGMENT_STEPS = _BV(BITS_SEGMENT - DIRECTIONAL) - 1;
|
||||
static constexpr int TOTAL_STEPS = SEGMENT_STEPS * SEGMENTS;
|
||||
static constexpr int PAGE_SIZE = (NUM_AXES * BITS_SEGMENT * SEGMENTS) / 8;
|
||||
static constexpr int PAGE_SIZE = (AXIS_COUNT * BITS_SEGMENT * SEGMENTS) / 8;
|
||||
|
||||
typedef typename TypeSelector<(PAGE_SIZE>256), uint16_t, uint8_t>::type write_byte_idx_t;
|
||||
typedef typename TypeSelector<(NUM_PAGES>256), uint16_t, uint8_t>::type page_idx_t;
|
||||
typedef typename TypeSelector<(PAGE_COUNT>256), uint16_t, uint8_t>::type page_idx_t;
|
||||
};
|
||||
|
||||
template <uint8_t num_pages>
|
||||
|
@@ -41,7 +41,9 @@ extern bool wait_for_user, wait_for_heatup;
|
||||
void quickresume_stepper();
|
||||
#endif
|
||||
|
||||
void HAL_reboot();
|
||||
#if ENABLED(SOFT_RESET_VIA_SERIAL)
|
||||
void HAL_reboot();
|
||||
#endif
|
||||
|
||||
class EmergencyParser {
|
||||
|
||||
@@ -199,7 +201,7 @@ public:
|
||||
case EP_M112: killed_by_M112 = true; break;
|
||||
case EP_M410: quickstop_by_M410 = true; break;
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
case EP_M876SN: host_response_handler(M876_reason); break;
|
||||
case EP_M876SN: hostui.handle_response(M876_reason); break;
|
||||
#endif
|
||||
#if ENABLED(REALTIME_REPORTING_COMMANDS)
|
||||
case EP_GRBL_STATUS: report_current_position_moving(); break;
|
||||
|
236
Marlin/src/feature/easythreed_ui.cpp
Normal file
236
Marlin/src/feature/easythreed_ui.cpp
Normal file
@@ -0,0 +1,236 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
|
||||
#if ENABLED(EASYTHREED_UI)
|
||||
|
||||
#include "easythreed_ui.h"
|
||||
#include "pause.h"
|
||||
#include "../module/temperature.h"
|
||||
#include "../module/printcounter.h"
|
||||
#include "../sd/cardreader.h"
|
||||
#include "../gcode/queue.h"
|
||||
#include "../module/motion.h"
|
||||
#include "../module/planner.h"
|
||||
#include "../MarlinCore.h"
|
||||
|
||||
EasythreedUI easythreed_ui;
|
||||
|
||||
#define BTN_DEBOUNCE_MS 20
|
||||
|
||||
void EasythreedUI::init() {
|
||||
SET_INPUT_PULLUP(BTN_HOME); SET_OUTPUT(BTN_HOME_GND);
|
||||
SET_INPUT_PULLUP(BTN_FEED); SET_OUTPUT(BTN_FEED_GND);
|
||||
SET_INPUT_PULLUP(BTN_RETRACT); SET_OUTPUT(BTN_RETRACT_GND);
|
||||
SET_INPUT_PULLUP(BTN_PRINT);
|
||||
SET_OUTPUT(EASYTHREED_LED_PIN);
|
||||
}
|
||||
|
||||
void EasythreedUI::run() {
|
||||
blinkLED();
|
||||
loadButton();
|
||||
printButton();
|
||||
}
|
||||
|
||||
enum LEDInterval : uint16_t {
|
||||
LED_OFF = 0,
|
||||
LED_ON = 4000,
|
||||
LED_BLINK_0 = 2500,
|
||||
LED_BLINK_1 = 1500,
|
||||
LED_BLINK_2 = 1000,
|
||||
LED_BLINK_3 = 800,
|
||||
LED_BLINK_4 = 500,
|
||||
LED_BLINK_5 = 300,
|
||||
LED_BLINK_6 = 150,
|
||||
LED_BLINK_7 = 50
|
||||
};
|
||||
|
||||
uint16_t blink_interval_ms = LED_ON; // Status LED on Start button
|
||||
|
||||
void EasythreedUI::blinkLED() {
|
||||
static millis_t prev_blink_interval_ms = 0, blink_start_ms = 0;
|
||||
|
||||
if (blink_interval_ms == LED_OFF) { WRITE(EASYTHREED_LED_PIN, HIGH); return; } // OFF
|
||||
if (blink_interval_ms >= LED_ON) { WRITE(EASYTHREED_LED_PIN, LOW); return; } // ON
|
||||
|
||||
const millis_t ms = millis();
|
||||
if (prev_blink_interval_ms != blink_interval_ms) {
|
||||
prev_blink_interval_ms = blink_interval_ms;
|
||||
blink_start_ms = ms;
|
||||
}
|
||||
if (PENDING(ms, blink_start_ms + blink_interval_ms))
|
||||
WRITE(EASYTHREED_LED_PIN, LOW);
|
||||
else if (PENDING(ms, blink_start_ms + 2 * blink_interval_ms))
|
||||
WRITE(EASYTHREED_LED_PIN, HIGH);
|
||||
else
|
||||
blink_start_ms = ms;
|
||||
}
|
||||
|
||||
//
|
||||
// Filament Load/Unload Button
|
||||
// Load/Unload buttons are a 3 position switch with a common center ground.
|
||||
//
|
||||
void EasythreedUI::loadButton() {
|
||||
if (printingIsActive()) return;
|
||||
|
||||
enum FilamentStatus : uint8_t { FS_IDLE, FS_PRESS, FS_CHECK, FS_PROCEED };
|
||||
static uint8_t filament_status = FS_IDLE;
|
||||
static millis_t filament_time = 0;
|
||||
|
||||
switch (filament_status) {
|
||||
|
||||
case FS_IDLE:
|
||||
if (!READ(BTN_RETRACT) || !READ(BTN_FEED)) { // If feed/retract switch is toggled...
|
||||
filament_status++; // ...proceed to next test.
|
||||
filament_time = millis();
|
||||
}
|
||||
break;
|
||||
|
||||
case FS_PRESS:
|
||||
if (ELAPSED(millis(), filament_time + BTN_DEBOUNCE_MS)) { // After a short debounce delay...
|
||||
if (!READ(BTN_RETRACT) || !READ(BTN_FEED)) { // ...if switch still toggled...
|
||||
thermalManager.setTargetHotend(EXTRUDE_MINTEMP + 10, 0); // Start heating up
|
||||
blink_interval_ms = LED_BLINK_7; // Set the LED to blink fast
|
||||
filament_status++;
|
||||
}
|
||||
else
|
||||
filament_status = FS_IDLE; // Switch not toggled long enough
|
||||
}
|
||||
break;
|
||||
|
||||
case FS_CHECK:
|
||||
if (READ(BTN_RETRACT) && READ(BTN_FEED)) { // Switch in center position (stop)
|
||||
blink_interval_ms = LED_ON; // LED on steady
|
||||
filament_status = FS_IDLE;
|
||||
thermalManager.disable_all_heaters();
|
||||
}
|
||||
else if (thermalManager.hotEnoughToExtrude(0)) { // Is the hotend hot enough to move material?
|
||||
filament_status++; // Proceed to feed / retract.
|
||||
blink_interval_ms = LED_BLINK_5; // Blink ~3 times per second
|
||||
}
|
||||
break;
|
||||
|
||||
case FS_PROCEED: {
|
||||
// Feed or Retract just once. Hard abort all moves and return to idle on swicth release.
|
||||
static bool flag = false;
|
||||
if (READ(BTN_RETRACT) && READ(BTN_FEED)) { // Switch in center position (stop)
|
||||
flag = false; // Restore flag to false
|
||||
filament_status = FS_IDLE; // Go back to idle state
|
||||
quickstop_stepper(); // Hard-stop all the steppers ... now!
|
||||
thermalManager.disable_all_heaters(); // And disable all the heaters
|
||||
blink_interval_ms = LED_ON;
|
||||
}
|
||||
else if (!flag) {
|
||||
flag = true;
|
||||
queue.inject(!READ(BTN_RETRACT) ? F("G91\nG0 E10 F180\nG0 E-120 F180\nM104 S0") : F("G91\nG0 E100 F120\nM104 S0"));
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if HAS_STEPPER_RESET
|
||||
void disableStepperDrivers();
|
||||
#endif
|
||||
|
||||
//
|
||||
// Print Start/Pause/Resume Button
|
||||
//
|
||||
void EasythreedUI::printButton() {
|
||||
enum KeyStatus : uint8_t { KS_IDLE, KS_PRESS, KS_PROCEED };
|
||||
static uint8_t key_status = KS_IDLE;
|
||||
static millis_t key_time = 0;
|
||||
|
||||
enum PrintFlag : uint8_t { PF_START, PF_PAUSE, PF_RESUME };
|
||||
static PrintFlag print_key_flag = PF_START;
|
||||
|
||||
const millis_t ms = millis();
|
||||
|
||||
switch (key_status) {
|
||||
case KS_IDLE:
|
||||
if (!READ(BTN_PRINT)) { // Print/Pause/Resume button pressed?
|
||||
key_time = ms; // Save start time
|
||||
key_status++; // Go to debounce test
|
||||
}
|
||||
break;
|
||||
|
||||
case KS_PRESS:
|
||||
if (ELAPSED(ms, key_time + BTN_DEBOUNCE_MS)) // Wait for debounce interval to expire
|
||||
key_status = READ(BTN_PRINT) ? KS_IDLE : KS_PROCEED; // Proceed if still pressed
|
||||
break;
|
||||
|
||||
case KS_PROCEED:
|
||||
if (!READ(BTN_PRINT)) break; // Wait for the button to be released
|
||||
key_status = KS_IDLE; // Ready for the next press
|
||||
if (PENDING(ms, key_time + 1200 - BTN_DEBOUNCE_MS)) { // Register a press < 1.2 seconds
|
||||
switch (print_key_flag) {
|
||||
case PF_START: { // The "Print" button starts an SD card print
|
||||
if (printingIsActive()) break; // Already printing? (find another line that checks for 'is planner doing anything else right now?')
|
||||
blink_interval_ms = LED_BLINK_2; // Blink the indicator LED at 1 second intervals
|
||||
print_key_flag = PF_PAUSE; // The "Print" button now pauses the print
|
||||
card.mount(); // Force SD card to mount - now!
|
||||
if (!card.isMounted) { // Failed to mount?
|
||||
blink_interval_ms = LED_OFF; // Turn off LED
|
||||
print_key_flag = PF_START;
|
||||
return; // Bail out
|
||||
}
|
||||
card.ls(); // List all files to serial output
|
||||
const uint16_t filecnt = card.countFilesInWorkDir(); // Count printable files in cwd
|
||||
if (filecnt == 0) return; // None are printable?
|
||||
card.selectFileByIndex(filecnt); // Select the last file according to current sort options
|
||||
card.openAndPrintFile(card.filename); // Start printing it
|
||||
break;
|
||||
}
|
||||
case PF_PAUSE: { // Pause printing (not currently firing)
|
||||
if (!printingIsActive()) break;
|
||||
blink_interval_ms = LED_ON; // Set indicator to steady ON
|
||||
queue.inject(F("M25")); // Queue Pause
|
||||
print_key_flag = PF_RESUME; // The "Print" button now resumes the print
|
||||
break;
|
||||
}
|
||||
case PF_RESUME: { // Resume printing
|
||||
if (printingIsActive()) break;
|
||||
blink_interval_ms = LED_BLINK_2; // Blink the indicator LED at 1 second intervals
|
||||
queue.inject(F("M24")); // Queue resume
|
||||
print_key_flag = PF_PAUSE; // The "Print" button now pauses the print
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else { // Register a longer press
|
||||
if (print_key_flag == PF_START && !printingIsActive()) { // While not printing, this moves Z up 10mm
|
||||
blink_interval_ms = LED_ON;
|
||||
queue.inject(F("G91\nG0 Z10 F600\nG90")); // Raise Z soon after returning to main loop
|
||||
}
|
||||
else { // While printing, cancel print
|
||||
card.abortFilePrintSoon(); // There is a delay while the current steps play out
|
||||
blink_interval_ms = LED_OFF; // Turn off LED
|
||||
}
|
||||
planner.synchronize(); // Wait for commands already in the planner to finish
|
||||
TERN_(HAS_STEPPER_RESET, disableStepperDrivers()); // Disable all steppers - now!
|
||||
print_key_flag = PF_START; // The "Print" button now starts a new print
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif // EASYTHREED_UI
|
35
Marlin/src/feature/easythreed_ui.h
Normal file
35
Marlin/src/feature/easythreed_ui.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2021 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
|
||||
|
||||
class EasythreedUI {
|
||||
public:
|
||||
static void init();
|
||||
static void run();
|
||||
|
||||
private:
|
||||
static void blinkLED();
|
||||
static void loadButton();
|
||||
static void printButton();
|
||||
};
|
||||
|
||||
extern EasythreedUI easythreed_ui;
|
@@ -49,7 +49,7 @@ void I2CPositionEncoder::init(const uint8_t address, const AxisEnum axis) {
|
||||
|
||||
initialized = true;
|
||||
|
||||
SERIAL_ECHOLNPGM("Setting up encoder on ", AS_CHAR(axis_codes[encoderAxis]), " axis, addr = ", address);
|
||||
SERIAL_ECHOLNPGM("Setting up encoder on ", AS_CHAR(AXIS_CHAR(encoderAxis)), " axis, addr = ", address);
|
||||
|
||||
position = get_position();
|
||||
}
|
||||
@@ -67,7 +67,7 @@ void I2CPositionEncoder::update() {
|
||||
/*
|
||||
if (trusted) { //commented out as part of the note below
|
||||
trusted = false;
|
||||
SERIAL_ECHOLNPGM("Fault detected on ", AS_CHAR(axis_codes[encoderAxis]), " axis encoder. Disengaging error correction until module is trusted again.");
|
||||
SERIAL_ECHOLNPGM("Fault detected on ", AS_CHAR(AXIS_CHAR(encoderAxis)), " axis encoder. Disengaging error correction until module is trusted again.");
|
||||
}
|
||||
*/
|
||||
return;
|
||||
@@ -92,7 +92,7 @@ void I2CPositionEncoder::update() {
|
||||
if (millis() - lastErrorTime > I2CPE_TIME_TRUSTED) {
|
||||
trusted = true;
|
||||
|
||||
SERIAL_ECHOLNPGM("Untrusted encoder module on ", AS_CHAR(axis_codes[encoderAxis]), " axis has been fault-free for set duration, reinstating error correction.");
|
||||
SERIAL_ECHOLNPGM("Untrusted encoder module on ", AS_CHAR(AXIS_CHAR(encoderAxis)), " axis has been fault-free for set duration, reinstating error correction.");
|
||||
|
||||
//the encoder likely lost its place when the error occurred, so we'll reset and use the printer's
|
||||
//idea of where it the axis is to re-initialize
|
||||
@@ -153,7 +153,7 @@ void I2CPositionEncoder::update() {
|
||||
|
||||
#ifdef I2CPE_ERR_THRESH_ABORT
|
||||
if (ABS(error) > I2CPE_ERR_THRESH_ABORT * planner.settings.axis_steps_per_mm[encoderAxis]) {
|
||||
//kill(PSTR("Significant Error"));
|
||||
//kill(F("Significant Error"));
|
||||
SERIAL_ECHOLNPGM("Axis error over threshold, aborting!", error);
|
||||
safe_delay(5000);
|
||||
}
|
||||
@@ -172,7 +172,7 @@ void I2CPositionEncoder::update() {
|
||||
float sumP = 0;
|
||||
LOOP_L_N(i, I2CPE_ERR_PRST_ARRAY_SIZE) sumP += errPrst[i];
|
||||
const int32_t errorP = int32_t(sumP * RECIPROCAL(I2CPE_ERR_PRST_ARRAY_SIZE));
|
||||
SERIAL_CHAR(axis_codes[encoderAxis]);
|
||||
SERIAL_CHAR(AXIS_CHAR(encoderAxis));
|
||||
SERIAL_ECHOLNPGM(" : CORRECT ERR ", errorP * planner.mm_per_step[encoderAxis], "mm");
|
||||
babystep.add_steps(encoderAxis, -LROUND(errorP));
|
||||
errPrstIdx = 0;
|
||||
@@ -192,7 +192,7 @@ void I2CPositionEncoder::update() {
|
||||
if (ABS(error) > I2CPE_ERR_CNT_THRESH * planner.settings.axis_steps_per_mm[encoderAxis]) {
|
||||
const millis_t ms = millis();
|
||||
if (ELAPSED(ms, nextErrorCountTime)) {
|
||||
SERIAL_CHAR(axis_codes[encoderAxis]);
|
||||
SERIAL_CHAR(AXIS_CHAR(encoderAxis));
|
||||
SERIAL_ECHOLNPGM(" : LARGE ERR ", error, "; diffSum=", diffSum);
|
||||
errorCount++;
|
||||
nextErrorCountTime = ms + I2CPE_ERR_CNT_DEBOUNCE_MS;
|
||||
@@ -212,7 +212,7 @@ void I2CPositionEncoder::set_homed() {
|
||||
homed = trusted = true;
|
||||
|
||||
#ifdef I2CPE_DEBUG
|
||||
SERIAL_CHAR(axis_codes[encoderAxis]);
|
||||
SERIAL_CHAR(AXIS_CHAR(encoderAxis));
|
||||
SERIAL_ECHOLNPGM(" axis encoder homed, offset of ", zeroOffset, " ticks.");
|
||||
#endif
|
||||
}
|
||||
@@ -223,7 +223,7 @@ void I2CPositionEncoder::set_unhomed() {
|
||||
homed = trusted = false;
|
||||
|
||||
#ifdef I2CPE_DEBUG
|
||||
SERIAL_CHAR(axis_codes[encoderAxis]);
|
||||
SERIAL_CHAR(AXIS_CHAR(encoderAxis));
|
||||
SERIAL_ECHOLNPGM(" axis encoder unhomed.");
|
||||
#endif
|
||||
}
|
||||
@@ -231,8 +231,8 @@ void I2CPositionEncoder::set_unhomed() {
|
||||
bool I2CPositionEncoder::passes_test(const bool report) {
|
||||
if (report) {
|
||||
if (H != I2CPE_MAG_SIG_GOOD) SERIAL_ECHOPGM("Warning. ");
|
||||
SERIAL_CHAR(axis_codes[encoderAxis]);
|
||||
serial_ternary(H == I2CPE_MAG_SIG_BAD, PSTR(" axis "), PSTR("magnetic strip "), PSTR("encoder "));
|
||||
SERIAL_CHAR(AXIS_CHAR(encoderAxis));
|
||||
serial_ternary(H == I2CPE_MAG_SIG_BAD, F(" axis "), F("magnetic strip "), F("encoder "));
|
||||
switch (H) {
|
||||
case I2CPE_MAG_SIG_GOOD:
|
||||
case I2CPE_MAG_SIG_MID:
|
||||
@@ -252,7 +252,7 @@ float I2CPositionEncoder::get_axis_error_mm(const bool report) {
|
||||
error = ABS(diff) > 10000 ? 0 : diff; // Huge error is a bad reading
|
||||
|
||||
if (report) {
|
||||
SERIAL_CHAR(axis_codes[encoderAxis]);
|
||||
SERIAL_CHAR(AXIS_CHAR(encoderAxis));
|
||||
SERIAL_ECHOLNPGM(" axis target=", target, "mm; actual=", actual, "mm; err=", error, "mm");
|
||||
}
|
||||
|
||||
@@ -262,7 +262,7 @@ float I2CPositionEncoder::get_axis_error_mm(const bool report) {
|
||||
int32_t I2CPositionEncoder::get_axis_error_steps(const bool report) {
|
||||
if (!active) {
|
||||
if (report) {
|
||||
SERIAL_CHAR(axis_codes[encoderAxis]);
|
||||
SERIAL_CHAR(AXIS_CHAR(encoderAxis));
|
||||
SERIAL_ECHOLNPGM(" axis encoder not active!");
|
||||
}
|
||||
return 0;
|
||||
@@ -287,7 +287,7 @@ int32_t I2CPositionEncoder::get_axis_error_steps(const bool report) {
|
||||
errorPrev = error;
|
||||
|
||||
if (report) {
|
||||
SERIAL_CHAR(axis_codes[encoderAxis]);
|
||||
SERIAL_CHAR(AXIS_CHAR(encoderAxis));
|
||||
SERIAL_ECHOLNPGM(" axis target=", target, "; actual=", encoderCountInStepperTicksScaled, "; err=", error);
|
||||
}
|
||||
|
||||
@@ -337,7 +337,7 @@ bool I2CPositionEncoder::test_axis() {
|
||||
ec = false;
|
||||
|
||||
xyze_pos_t startCoord, endCoord;
|
||||
LOOP_LINEAR_AXES(a) {
|
||||
LOOP_NUM_AXES(a) {
|
||||
startCoord[a] = planner.get_axis_position_mm((AxisEnum)a);
|
||||
endCoord[a] = planner.get_axis_position_mm((AxisEnum)a);
|
||||
}
|
||||
@@ -395,7 +395,7 @@ void I2CPositionEncoder::calibrate_steps_mm(const uint8_t iter) {
|
||||
travelDistance = endDistance - startDistance;
|
||||
|
||||
xyze_pos_t startCoord, endCoord;
|
||||
LOOP_LINEAR_AXES(a) {
|
||||
LOOP_NUM_AXES(a) {
|
||||
startCoord[a] = planner.get_axis_position_mm((AxisEnum)a);
|
||||
endCoord[a] = planner.get_axis_position_mm((AxisEnum)a);
|
||||
}
|
||||
@@ -489,7 +489,7 @@ void I2CPositionEncodersMgr::init() {
|
||||
encoders[i].set_stepper_ticks(I2CPE_ENC_1_TICKS_REV);
|
||||
#endif
|
||||
#ifdef I2CPE_ENC_1_INVERT
|
||||
encoders[i].set_inverted(I2CPE_ENC_1_INVERT);
|
||||
encoders[i].set_inverted(ENABLED(I2CPE_ENC_1_INVERT));
|
||||
#endif
|
||||
#ifdef I2CPE_ENC_1_EC_METHOD
|
||||
encoders[i].set_ec_method(I2CPE_ENC_1_EC_METHOD);
|
||||
@@ -518,7 +518,7 @@ void I2CPositionEncodersMgr::init() {
|
||||
encoders[i].set_stepper_ticks(I2CPE_ENC_2_TICKS_REV);
|
||||
#endif
|
||||
#ifdef I2CPE_ENC_2_INVERT
|
||||
encoders[i].set_inverted(I2CPE_ENC_2_INVERT);
|
||||
encoders[i].set_inverted(ENABLED(I2CPE_ENC_2_INVERT));
|
||||
#endif
|
||||
#ifdef I2CPE_ENC_2_EC_METHOD
|
||||
encoders[i].set_ec_method(I2CPE_ENC_2_EC_METHOD);
|
||||
@@ -547,7 +547,7 @@ void I2CPositionEncodersMgr::init() {
|
||||
encoders[i].set_stepper_ticks(I2CPE_ENC_3_TICKS_REV);
|
||||
#endif
|
||||
#ifdef I2CPE_ENC_3_INVERT
|
||||
encoders[i].set_inverted(I2CPE_ENC_3_INVERT);
|
||||
encoders[i].set_inverted(ENABLED(I2CPE_ENC_3_INVERT));
|
||||
#endif
|
||||
#ifdef I2CPE_ENC_3_EC_METHOD
|
||||
encoders[i].set_ec_method(I2CPE_ENC_3_EC_METHOD);
|
||||
@@ -576,7 +576,7 @@ void I2CPositionEncodersMgr::init() {
|
||||
encoders[i].set_stepper_ticks(I2CPE_ENC_4_TICKS_REV);
|
||||
#endif
|
||||
#ifdef I2CPE_ENC_4_INVERT
|
||||
encoders[i].set_inverted(I2CPE_ENC_4_INVERT);
|
||||
encoders[i].set_inverted(ENABLED(I2CPE_ENC_4_INVERT));
|
||||
#endif
|
||||
#ifdef I2CPE_ENC_4_EC_METHOD
|
||||
encoders[i].set_ec_method(I2CPE_ENC_4_EC_METHOD);
|
||||
@@ -605,7 +605,7 @@ void I2CPositionEncodersMgr::init() {
|
||||
encoders[i].set_stepper_ticks(I2CPE_ENC_5_TICKS_REV);
|
||||
#endif
|
||||
#ifdef I2CPE_ENC_5_INVERT
|
||||
encoders[i].set_inverted(I2CPE_ENC_5_INVERT);
|
||||
encoders[i].set_inverted(ENABLED(I2CPE_ENC_5_INVERT));
|
||||
#endif
|
||||
#ifdef I2CPE_ENC_5_EC_METHOD
|
||||
encoders[i].set_ec_method(I2CPE_ENC_5_EC_METHOD);
|
||||
@@ -634,7 +634,7 @@ void I2CPositionEncodersMgr::init() {
|
||||
encoders[i].set_stepper_ticks(I2CPE_ENC_6_TICKS_REV);
|
||||
#endif
|
||||
#ifdef I2CPE_ENC_6_INVERT
|
||||
encoders[i].set_inverted(I2CPE_ENC_6_INVERT);
|
||||
encoders[i].set_inverted(ENABLED(I2CPE_ENC_6_INVERT));
|
||||
#endif
|
||||
#ifdef I2CPE_ENC_6_EC_METHOD
|
||||
encoders[i].set_ec_method(I2CPE_ENC_6_EC_METHOD);
|
||||
@@ -657,7 +657,7 @@ void I2CPositionEncodersMgr::report_position(const int8_t idx, const bool units,
|
||||
else {
|
||||
if (noOffset) {
|
||||
const int32_t raw_count = encoders[idx].get_raw_count();
|
||||
SERIAL_CHAR(axis_codes[encoders[idx].get_axis()], ' ');
|
||||
SERIAL_CHAR(AXIS_CHAR(encoders[idx).get_axis()], ' ');
|
||||
|
||||
for (uint8_t j = 31; j > 0; j--)
|
||||
SERIAL_ECHO((bool)(0x00000001 & (raw_count >> j)));
|
||||
@@ -712,7 +712,7 @@ void I2CPositionEncodersMgr::change_module_address(const uint8_t oldaddr, const
|
||||
// and enable it (it will likely have failed initialization on power-up, before the address change).
|
||||
const int8_t idx = idx_from_addr(newaddr);
|
||||
if (idx >= 0 && !encoders[idx].get_active()) {
|
||||
SERIAL_CHAR(axis_codes[encoders[idx].get_axis()]);
|
||||
SERIAL_CHAR(AXIS_CHAR(encoders[idx).get_axis()]);
|
||||
SERIAL_ECHOLNPGM(" axis encoder was not detected on printer startup. Trying again.");
|
||||
encoders[idx].set_active(encoders[idx].passes_test(true));
|
||||
}
|
||||
@@ -756,7 +756,7 @@ int8_t I2CPositionEncodersMgr::parse() {
|
||||
if (!parser.has_value()) {
|
||||
SERIAL_ECHOLNPGM("?A seen, but no address specified! [30-200]");
|
||||
return I2CPE_PARSE_ERR;
|
||||
};
|
||||
}
|
||||
|
||||
I2CPE_addr = parser.value_byte();
|
||||
if (!WITHIN(I2CPE_addr, 30, 200)) { // reserve the first 30 and last 55
|
||||
@@ -775,7 +775,7 @@ int8_t I2CPositionEncodersMgr::parse() {
|
||||
if (!parser.has_value()) {
|
||||
SERIAL_ECHOLNPGM("?I seen, but no index specified! [0-", I2CPE_ENCODER_CNT - 1, "]");
|
||||
return I2CPE_PARSE_ERR;
|
||||
};
|
||||
}
|
||||
|
||||
I2CPE_idx = parser.value_byte();
|
||||
if (I2CPE_idx >= I2CPE_ENCODER_CNT) {
|
||||
@@ -791,7 +791,7 @@ int8_t I2CPositionEncodersMgr::parse() {
|
||||
I2CPE_anyaxis = parser.seen_axis();
|
||||
|
||||
return I2CPE_PARSE_OK;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* M860: Report the position(s) of position encoder module(s).
|
||||
@@ -814,7 +814,7 @@ void I2CPositionEncodersMgr::M860() {
|
||||
|
||||
if (I2CPE_idx == 0xFF) {
|
||||
LOOP_LOGICAL_AXES(i) {
|
||||
if (!I2CPE_anyaxis || parser.seen_test(axis_codes[i])) {
|
||||
if (!I2CPE_anyaxis || parser.seen_test(AXIS_CHAR(i))) {
|
||||
const uint8_t idx = idx_from_axis(AxisEnum(i));
|
||||
if ((int8_t)idx >= 0) report_position(idx, hasU, hasO);
|
||||
}
|
||||
@@ -841,7 +841,7 @@ void I2CPositionEncodersMgr::M861() {
|
||||
|
||||
if (I2CPE_idx == 0xFF) {
|
||||
LOOP_LOGICAL_AXES(i) {
|
||||
if (!I2CPE_anyaxis || parser.seen(axis_codes[i])) {
|
||||
if (!I2CPE_anyaxis || parser.seen(AXIS_CHAR(i))) {
|
||||
const uint8_t idx = idx_from_axis(AxisEnum(i));
|
||||
if ((int8_t)idx >= 0) report_status(idx);
|
||||
}
|
||||
@@ -869,7 +869,7 @@ void I2CPositionEncodersMgr::M862() {
|
||||
|
||||
if (I2CPE_idx == 0xFF) {
|
||||
LOOP_LOGICAL_AXES(i) {
|
||||
if (!I2CPE_anyaxis || parser.seen(axis_codes[i])) {
|
||||
if (!I2CPE_anyaxis || parser.seen(AXIS_CHAR(i))) {
|
||||
const uint8_t idx = idx_from_axis(AxisEnum(i));
|
||||
if ((int8_t)idx >= 0) test_axis(idx);
|
||||
}
|
||||
@@ -900,7 +900,7 @@ void I2CPositionEncodersMgr::M863() {
|
||||
|
||||
if (I2CPE_idx == 0xFF) {
|
||||
LOOP_LOGICAL_AXES(i) {
|
||||
if (!I2CPE_anyaxis || parser.seen(axis_codes[i])) {
|
||||
if (!I2CPE_anyaxis || parser.seen(AXIS_CHAR(i))) {
|
||||
const uint8_t idx = idx_from_axis(AxisEnum(i));
|
||||
if ((int8_t)idx >= 0) calibrate_steps_mm(idx, iterations);
|
||||
}
|
||||
@@ -934,7 +934,7 @@ void I2CPositionEncodersMgr::M864() {
|
||||
if (!parser.has_value()) {
|
||||
SERIAL_ECHOLNPGM("?S seen, but no address specified! [30-200]");
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
newAddress = parser.value_byte();
|
||||
if (!WITHIN(newAddress, 30, 200)) {
|
||||
@@ -976,7 +976,7 @@ void I2CPositionEncodersMgr::M865() {
|
||||
|
||||
if (!I2CPE_addr) {
|
||||
LOOP_LOGICAL_AXES(i) {
|
||||
if (!I2CPE_anyaxis || parser.seen(axis_codes[i])) {
|
||||
if (!I2CPE_anyaxis || parser.seen(AXIS_CHAR(i))) {
|
||||
const uint8_t idx = idx_from_axis(AxisEnum(i));
|
||||
if ((int8_t)idx >= 0) report_module_firmware(encoders[idx].get_address());
|
||||
}
|
||||
@@ -1007,7 +1007,7 @@ void I2CPositionEncodersMgr::M866() {
|
||||
|
||||
if (I2CPE_idx == 0xFF) {
|
||||
LOOP_LOGICAL_AXES(i) {
|
||||
if (!I2CPE_anyaxis || parser.seen(axis_codes[i])) {
|
||||
if (!I2CPE_anyaxis || parser.seen(AXIS_CHAR(i))) {
|
||||
const uint8_t idx = idx_from_axis(AxisEnum(i));
|
||||
if ((int8_t)idx >= 0) {
|
||||
if (hasR)
|
||||
@@ -1045,7 +1045,7 @@ void I2CPositionEncodersMgr::M867() {
|
||||
|
||||
if (I2CPE_idx == 0xFF) {
|
||||
LOOP_LOGICAL_AXES(i) {
|
||||
if (!I2CPE_anyaxis || parser.seen(axis_codes[i])) {
|
||||
if (!I2CPE_anyaxis || parser.seen(AXIS_CHAR(i))) {
|
||||
const uint8_t idx = idx_from_axis(AxisEnum(i));
|
||||
if ((int8_t)idx >= 0) {
|
||||
const bool ena = onoff == -1 ? !encoders[I2CPE_idx].get_ec_enabled() : !!onoff;
|
||||
@@ -1081,7 +1081,7 @@ void I2CPositionEncodersMgr::M868() {
|
||||
|
||||
if (I2CPE_idx == 0xFF) {
|
||||
LOOP_LOGICAL_AXES(i) {
|
||||
if (!I2CPE_anyaxis || parser.seen(axis_codes[i])) {
|
||||
if (!I2CPE_anyaxis || parser.seen(AXIS_CHAR(i))) {
|
||||
const uint8_t idx = idx_from_axis(AxisEnum(i));
|
||||
if ((int8_t)idx >= 0) {
|
||||
if (newThreshold != -9999)
|
||||
@@ -1115,7 +1115,7 @@ void I2CPositionEncodersMgr::M869() {
|
||||
|
||||
if (I2CPE_idx == 0xFF) {
|
||||
LOOP_LOGICAL_AXES(i) {
|
||||
if (!I2CPE_anyaxis || parser.seen(axis_codes[i])) {
|
||||
if (!I2CPE_anyaxis || parser.seen(AXIS_CHAR(i))) {
|
||||
const uint8_t idx = idx_from_axis(AxisEnum(i));
|
||||
if ((int8_t)idx >= 0) report_error(idx);
|
||||
}
|
||||
|
@@ -261,32 +261,32 @@ class I2CPositionEncodersMgr {
|
||||
|
||||
static void report_error_count(const int8_t idx, const AxisEnum axis) {
|
||||
CHECK_IDX();
|
||||
SERIAL_ECHOLNPGM("Error count on ", AS_CHAR(axis_codes[axis]), " axis is ", encoders[idx].get_error_count());
|
||||
SERIAL_ECHOLNPGM("Error count on ", AS_CHAR(AXIS_CHAR(axis)), " axis is ", encoders[idx].get_error_count());
|
||||
}
|
||||
|
||||
static void reset_error_count(const int8_t idx, const AxisEnum axis) {
|
||||
CHECK_IDX();
|
||||
encoders[idx].set_error_count(0);
|
||||
SERIAL_ECHOLNPGM("Error count on ", AS_CHAR(axis_codes[axis]), " axis has been reset.");
|
||||
SERIAL_ECHOLNPGM("Error count on ", AS_CHAR(AXIS_CHAR(axis)), " axis has been reset.");
|
||||
}
|
||||
|
||||
static void enable_ec(const int8_t idx, const bool enabled, const AxisEnum axis) {
|
||||
CHECK_IDX();
|
||||
encoders[idx].set_ec_enabled(enabled);
|
||||
SERIAL_ECHOPGM("Error correction on ", AS_CHAR(axis_codes[axis]));
|
||||
SERIAL_ECHOPGM("Error correction on ", AS_CHAR(AXIS_CHAR(axis)));
|
||||
SERIAL_ECHO_TERNARY(encoders[idx].get_ec_enabled(), " axis is ", "en", "dis", "abled.\n");
|
||||
}
|
||||
|
||||
static void set_ec_threshold(const int8_t idx, const float newThreshold, const AxisEnum axis) {
|
||||
CHECK_IDX();
|
||||
encoders[idx].set_ec_threshold(newThreshold);
|
||||
SERIAL_ECHOLNPGM("Error correct threshold for ", AS_CHAR(axis_codes[axis]), " axis set to ", newThreshold, "mm.");
|
||||
SERIAL_ECHOLNPGM("Error correct threshold for ", AS_CHAR(AXIS_CHAR(axis)), " axis set to ", newThreshold, "mm.");
|
||||
}
|
||||
|
||||
static void get_ec_threshold(const int8_t idx, const AxisEnum axis) {
|
||||
CHECK_IDX();
|
||||
const float threshold = encoders[idx].get_ec_threshold();
|
||||
SERIAL_ECHOLNPGM("Error correct threshold for ", AS_CHAR(axis_codes[axis]), " axis is ", threshold, "mm.");
|
||||
SERIAL_ECHOLNPGM("Error correct threshold for ", AS_CHAR(AXIS_CHAR(axis)), " axis is ", threshold, "mm.");
|
||||
}
|
||||
|
||||
static int8_t idx_from_axis(const AxisEnum axis) {
|
||||
|
207
Marlin/src/feature/fancheck.cpp
Normal file
207
Marlin/src/feature/fancheck.cpp
Normal file
@@ -0,0 +1,207 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2021 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* fancheck.cpp - fan tachometer check
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#if HAS_FANCHECK
|
||||
|
||||
#include "fancheck.h"
|
||||
#include "../module/temperature.h"
|
||||
|
||||
#if HAS_AUTO_FAN && EXTRUDER_AUTO_FAN_SPEED != 255 && DISABLED(FOURWIRES_FANS)
|
||||
bool FanCheck::measuring = false;
|
||||
#endif
|
||||
Flags<TACHO_COUNT> FanCheck::tacho_state;
|
||||
uint16_t FanCheck::edge_counter[TACHO_COUNT];
|
||||
uint8_t FanCheck::rps[TACHO_COUNT];
|
||||
FanCheck::TachoError FanCheck::error = FanCheck::TachoError::NONE;
|
||||
bool FanCheck::enabled;
|
||||
|
||||
void FanCheck::init() {
|
||||
#define _TACHINIT(N) TERN(E##N##_FAN_TACHO_PULLUP, SET_INPUT_PULLUP, TERN(E##N##_FAN_TACHO_PULLDOWN, SET_INPUT_PULLDOWN, SET_INPUT))(E##N##_FAN_TACHO_PIN)
|
||||
#if HAS_E0_FAN_TACHO
|
||||
_TACHINIT(0);
|
||||
#endif
|
||||
#if HAS_E1_FAN_TACHO
|
||||
_TACHINIT(1);
|
||||
#endif
|
||||
#if HAS_E2_FAN_TACHO
|
||||
_TACHINIT(2);
|
||||
#endif
|
||||
#if HAS_E3_FAN_TACHO
|
||||
_TACHINIT(3);
|
||||
#endif
|
||||
#if HAS_E4_FAN_TACHO
|
||||
_TACHINIT(4);
|
||||
#endif
|
||||
#if HAS_E5_FAN_TACHO
|
||||
_TACHINIT(5);
|
||||
#endif
|
||||
#if HAS_E6_FAN_TACHO
|
||||
_TACHINIT(6);
|
||||
#endif
|
||||
#if HAS_E7_FAN_TACHO
|
||||
_TACHINIT(7);
|
||||
#endif
|
||||
}
|
||||
|
||||
void FanCheck::update_tachometers() {
|
||||
bool status;
|
||||
|
||||
#define _TACHO_CASE(N) case N: status = READ(E##N##_FAN_TACHO_PIN); break;
|
||||
LOOP_L_N(f, TACHO_COUNT) {
|
||||
switch (f) {
|
||||
#if HAS_E0_FAN_TACHO
|
||||
_TACHO_CASE(0)
|
||||
#endif
|
||||
#if HAS_E1_FAN_TACHO
|
||||
_TACHO_CASE(1)
|
||||
#endif
|
||||
#if HAS_E2_FAN_TACHO
|
||||
_TACHO_CASE(2)
|
||||
#endif
|
||||
#if HAS_E3_FAN_TACHO
|
||||
_TACHO_CASE(3)
|
||||
#endif
|
||||
#if HAS_E4_FAN_TACHO
|
||||
_TACHO_CASE(4)
|
||||
#endif
|
||||
#if HAS_E5_FAN_TACHO
|
||||
_TACHO_CASE(5)
|
||||
#endif
|
||||
#if HAS_E6_FAN_TACHO
|
||||
_TACHO_CASE(6)
|
||||
#endif
|
||||
#if HAS_E7_FAN_TACHO
|
||||
_TACHO_CASE(7)
|
||||
#endif
|
||||
default: continue;
|
||||
}
|
||||
|
||||
if (status != tacho_state[f]) {
|
||||
if (measuring) ++edge_counter[f];
|
||||
tacho_state.set(f, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FanCheck::compute_speed(uint16_t elapsedTime) {
|
||||
static uint8_t errors_count[TACHO_COUNT];
|
||||
static uint8_t fan_reported_errors_msk = 0;
|
||||
|
||||
uint8_t fan_error_msk = 0;
|
||||
LOOP_L_N(f, TACHO_COUNT) {
|
||||
switch (f) {
|
||||
TERN_(HAS_E0_FAN_TACHO, case 0:)
|
||||
TERN_(HAS_E1_FAN_TACHO, case 1:)
|
||||
TERN_(HAS_E2_FAN_TACHO, case 2:)
|
||||
TERN_(HAS_E3_FAN_TACHO, case 3:)
|
||||
TERN_(HAS_E4_FAN_TACHO, case 4:)
|
||||
TERN_(HAS_E5_FAN_TACHO, case 5:)
|
||||
TERN_(HAS_E6_FAN_TACHO, case 6:)
|
||||
TERN_(HAS_E7_FAN_TACHO, case 7:)
|
||||
// Compute fan speed
|
||||
rps[f] = edge_counter[f] * float(250) / elapsedTime;
|
||||
edge_counter[f] = 0;
|
||||
|
||||
// Check fan speed
|
||||
constexpr int8_t max_extruder_fan_errors = TERN(HAS_PWMFANCHECK, 10000, 5000) / Temperature::fan_update_interval_ms;
|
||||
|
||||
if (rps[f] >= 20 || TERN0(HAS_AUTO_FAN, thermalManager.autofan_speed[f] == 0))
|
||||
errors_count[f] = 0;
|
||||
else if (errors_count[f] < max_extruder_fan_errors)
|
||||
++errors_count[f];
|
||||
else if (enabled)
|
||||
SBI(fan_error_msk, f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Drop the error when all fans are ok
|
||||
if (!fan_error_msk && error == TachoError::REPORTED) error = TachoError::FIXED;
|
||||
|
||||
if (error == TachoError::FIXED && !printJobOngoing() && !printingIsPaused()) {
|
||||
error = TachoError::NONE; // if the issue has been fixed while the printer is idle, reenable immediately
|
||||
ui.reset_alert_level();
|
||||
}
|
||||
|
||||
if (fan_error_msk & ~fan_reported_errors_msk) {
|
||||
// Handle new faults only
|
||||
LOOP_L_N(f, TACHO_COUNT) if (TEST(fan_error_msk, f)) report_speed_error(f);
|
||||
}
|
||||
fan_reported_errors_msk = fan_error_msk;
|
||||
}
|
||||
|
||||
void FanCheck::report_speed_error(uint8_t fan) {
|
||||
if (printJobOngoing()) {
|
||||
if (error == TachoError::NONE) {
|
||||
if (thermalManager.degTargetHotend(fan) != 0) {
|
||||
kill(GET_TEXT_F(MSG_FAN_SPEED_FAULT));
|
||||
error = TachoError::REPORTED;
|
||||
}
|
||||
else
|
||||
error = TachoError::DETECTED; // Plans error for next processed command
|
||||
}
|
||||
}
|
||||
else if (!printingIsPaused()) {
|
||||
thermalManager.setTargetHotend(0, fan); // Always disable heating
|
||||
if (error == TachoError::NONE) error = TachoError::REPORTED;
|
||||
}
|
||||
|
||||
SERIAL_ERROR_MSG(STR_ERR_FANSPEED, fan);
|
||||
LCD_ALERTMESSAGE(MSG_FAN_SPEED_FAULT);
|
||||
}
|
||||
|
||||
void FanCheck::print_fan_states() {
|
||||
LOOP_L_N(s, 2) {
|
||||
LOOP_L_N(f, TACHO_COUNT) {
|
||||
switch (f) {
|
||||
TERN_(HAS_E0_FAN_TACHO, case 0:)
|
||||
TERN_(HAS_E1_FAN_TACHO, case 1:)
|
||||
TERN_(HAS_E2_FAN_TACHO, case 2:)
|
||||
TERN_(HAS_E3_FAN_TACHO, case 3:)
|
||||
TERN_(HAS_E4_FAN_TACHO, case 4:)
|
||||
TERN_(HAS_E5_FAN_TACHO, case 5:)
|
||||
TERN_(HAS_E6_FAN_TACHO, case 6:)
|
||||
TERN_(HAS_E7_FAN_TACHO, case 7:)
|
||||
SERIAL_ECHOPGM("E", f);
|
||||
if (s == 0)
|
||||
SERIAL_ECHOPGM(":", 60 * rps[f], " RPM ");
|
||||
else
|
||||
SERIAL_ECHOPGM("@:", TERN(HAS_AUTO_FAN, thermalManager.autofan_speed[f], 255), " ");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
SERIAL_EOL();
|
||||
}
|
||||
|
||||
#if ENABLED(AUTO_REPORT_FANS)
|
||||
AutoReporter<FanCheck::AutoReportFan> FanCheck::auto_reporter;
|
||||
void FanCheck::AutoReportFan::report() { print_fan_states(); }
|
||||
#endif
|
||||
|
||||
#endif // HAS_FANCHECK
|
89
Marlin/src/feature/fancheck.h
Normal file
89
Marlin/src/feature/fancheck.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2021 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"
|
||||
|
||||
#if HAS_FANCHECK
|
||||
|
||||
#include "../MarlinCore.h"
|
||||
#include "../lcd/marlinui.h"
|
||||
|
||||
#if ENABLED(AUTO_REPORT_FANS)
|
||||
#include "../libs/autoreport.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(PARK_HEAD_ON_PAUSE)
|
||||
#include "../gcode/queue.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* fancheck.h
|
||||
*/
|
||||
#define TACHO_COUNT TERN(HAS_E7_FAN_TACHO, 8, TERN(HAS_E6_FAN_TACHO, 7, TERN(HAS_E5_FAN_TACHO, 6, TERN(HAS_E4_FAN_TACHO, 5, TERN(HAS_E3_FAN_TACHO, 4, TERN(HAS_E2_FAN_TACHO, 3, TERN(HAS_E1_FAN_TACHO, 2, 1)))))))
|
||||
|
||||
class FanCheck {
|
||||
private:
|
||||
|
||||
enum class TachoError : uint8_t { NONE, DETECTED, REPORTED, FIXED };
|
||||
|
||||
#if HAS_PWMFANCHECK
|
||||
static bool measuring; // For future use (3 wires PWM controlled fans)
|
||||
#else
|
||||
static constexpr bool measuring = true;
|
||||
#endif
|
||||
static Flags<TACHO_COUNT> tacho_state;
|
||||
static uint16_t edge_counter[TACHO_COUNT];
|
||||
static uint8_t rps[TACHO_COUNT];
|
||||
static TachoError error;
|
||||
|
||||
static void report_speed_error(uint8_t fan);
|
||||
|
||||
public:
|
||||
|
||||
static bool enabled;
|
||||
|
||||
static void init();
|
||||
static void update_tachometers();
|
||||
static void compute_speed(uint16_t elapsedTime);
|
||||
static void print_fan_states();
|
||||
#if HAS_PWMFANCHECK
|
||||
static void toggle_measuring() { measuring = !measuring; }
|
||||
static bool is_measuring() { return measuring; }
|
||||
#endif
|
||||
|
||||
static void check_deferred_error() {
|
||||
if (error == TachoError::DETECTED) {
|
||||
error = TachoError::REPORTED;
|
||||
TERN(PARK_HEAD_ON_PAUSE, queue.inject(F("M125")), kill(GET_TEXT_F(MSG_FAN_SPEED_FAULT)));
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLED(AUTO_REPORT_FANS)
|
||||
struct AutoReportFan { static void report(); };
|
||||
static AutoReporter<AutoReportFan> auto_reporter;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern FanCheck fan_check;
|
||||
|
||||
#endif // HAS_FANCHECK
|
@@ -41,9 +41,9 @@ public:
|
||||
FilamentWidthSensor() { init(); }
|
||||
static void init();
|
||||
|
||||
static inline void enable(const bool ena) { enabled = ena; }
|
||||
static void enable(const bool ena) { enabled = ena; }
|
||||
|
||||
static inline void set_delay_cm(const uint8_t cm) {
|
||||
static void set_delay_cm(const uint8_t cm) {
|
||||
meas_delay_cm = _MIN(cm, MAX_MEASUREMENT_DELAY);
|
||||
}
|
||||
|
||||
@@ -67,18 +67,18 @@ public:
|
||||
}
|
||||
|
||||
// Convert raw measurement to mm
|
||||
static inline float raw_to_mm(const uint16_t v) { return v * 5.0f * RECIPROCAL(float(MAX_RAW_THERMISTOR_VALUE)); }
|
||||
static inline float raw_to_mm() { return raw_to_mm(raw); }
|
||||
static float raw_to_mm(const uint16_t v) { return v * float(ADC_VREF) * RECIPROCAL(float(MAX_RAW_THERMISTOR_VALUE)); }
|
||||
static float raw_to_mm() { return raw_to_mm(raw); }
|
||||
|
||||
// A scaled reading is ready
|
||||
// Divide to get to 0-16384 range since we used 1/128 IIR filter approach
|
||||
static inline void reading_ready() { raw = accum >> 10; }
|
||||
static void reading_ready() { raw = accum >> 10; }
|
||||
|
||||
// Update mm from the raw measurement
|
||||
static inline void update_measured_mm() { measured_mm = raw_to_mm(); }
|
||||
static void update_measured_mm() { measured_mm = raw_to_mm(); }
|
||||
|
||||
// Update ring buffer used to delay filament measurements
|
||||
static inline void advance_e(const_float_t e_move) {
|
||||
static void advance_e(const_float_t e_move) {
|
||||
|
||||
// Increment counters with the E distance
|
||||
e_count += e_move;
|
||||
@@ -106,7 +106,7 @@ public:
|
||||
}
|
||||
|
||||
// Dynamically set the volumetric multiplier based on the delayed width measurement.
|
||||
static inline void update_volumetric() {
|
||||
static void update_volumetric() {
|
||||
if (enabled) {
|
||||
int8_t read_index = index_r - meas_delay_cm;
|
||||
if (read_index < 0) read_index += MMD_CM; // Loop around buffer if needed
|
||||
|
@@ -34,7 +34,6 @@ FWRetract fwretract; // Single instance - this calls the constructor
|
||||
|
||||
#include "../module/motion.h"
|
||||
#include "../module/planner.h"
|
||||
#include "../module/stepper.h"
|
||||
|
||||
#include "../gcode/gcode.h"
|
||||
|
||||
@@ -45,7 +44,7 @@ FWRetract fwretract; // Single instance - this calls the constructor
|
||||
// private:
|
||||
|
||||
#if HAS_MULTI_EXTRUDER
|
||||
bool FWRetract::retracted_swap[EXTRUDERS]; // Which extruders are swap-retracted
|
||||
Flags<EXTRUDERS> FWRetract::retracted_swap; // Which extruders are swap-retracted
|
||||
#endif
|
||||
|
||||
// public:
|
||||
@@ -56,7 +55,7 @@ fwretract_settings_t FWRetract::settings; // M207 S F Z W, M208 S F
|
||||
bool FWRetract::autoretract_enabled; // M209 S - Autoretract switch
|
||||
#endif
|
||||
|
||||
bool FWRetract::retracted[EXTRUDERS]; // Which extruders are currently retracted
|
||||
Flags<EXTRUDERS> FWRetract::retracted; // Which extruders are currently retracted
|
||||
|
||||
float FWRetract::current_retract[EXTRUDERS], // Retract value used by planner
|
||||
FWRetract::current_hop;
|
||||
@@ -73,10 +72,10 @@ void FWRetract::reset() {
|
||||
settings.swap_retract_recover_feedrate_mm_s = RETRACT_RECOVER_FEEDRATE_SWAP;
|
||||
current_hop = 0.0;
|
||||
|
||||
LOOP_L_N(i, EXTRUDERS) {
|
||||
retracted[i] = false;
|
||||
E_TERN_(retracted_swap[i] = false);
|
||||
current_retract[i] = 0.0;
|
||||
retracted.reset();
|
||||
EXTRUDER_LOOP() {
|
||||
E_TERN_(retracted_swap.clear(e));
|
||||
current_retract[e] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,10 +110,10 @@ void FWRetract::retract(const bool retracting E_OPTARG(bool swapping/*=false*/))
|
||||
" swapping ", swapping,
|
||||
" active extruder ", active_extruder
|
||||
);
|
||||
LOOP_L_N(i, EXTRUDERS) {
|
||||
SERIAL_ECHOLNPGM("retracted[", i, "] ", AS_DIGIT(retracted[i]));
|
||||
EXTRUDER_LOOP() {
|
||||
SERIAL_ECHOLNPGM("retracted[", e, "] ", AS_DIGIT(retracted[e]));
|
||||
#if HAS_MULTI_EXTRUDER
|
||||
SERIAL_ECHOLNPGM("retracted_swap[", i, "] ", AS_DIGIT(retracted_swap[i]));
|
||||
SERIAL_ECHOLNPGM("retracted_swap[", e, "] ", AS_DIGIT(retracted_swap[e]));
|
||||
#endif
|
||||
}
|
||||
SERIAL_ECHOLNPGM("current_position.z ", current_position.z);
|
||||
@@ -173,21 +172,21 @@ void FWRetract::retract(const bool retracting E_OPTARG(bool swapping/*=false*/))
|
||||
|
||||
TERN_(RETRACT_SYNC_MIXING, mixer.T(old_mixing_tool)); // Restore original mixing tool
|
||||
|
||||
retracted[active_extruder] = retracting; // Active extruder now retracted / recovered
|
||||
retracted.set(active_extruder, retracting); // Active extruder now retracted / recovered
|
||||
|
||||
// If swap retract/recover update the retracted_swap flag too
|
||||
#if HAS_MULTI_EXTRUDER
|
||||
if (swapping) retracted_swap[active_extruder] = retracting;
|
||||
if (swapping) retracted_swap.set(active_extruder, retracting);
|
||||
#endif
|
||||
|
||||
/* // debugging
|
||||
SERIAL_ECHOLNPGM("retracting ", AS_DIGIT(retracting));
|
||||
SERIAL_ECHOLNPGM("swapping ", AS_DIGIT(swapping));
|
||||
SERIAL_ECHOLNPGM("active_extruder ", active_extruder);
|
||||
LOOP_L_N(i, EXTRUDERS) {
|
||||
SERIAL_ECHOLNPGM("retracted[", i, "] ", AS_DIGIT(retracted[i]));
|
||||
EXTRUDER_LOOP() {
|
||||
SERIAL_ECHOLNPGM("retracted[", e, "] ", AS_DIGIT(retracted[e]));
|
||||
#if HAS_MULTI_EXTRUDER
|
||||
SERIAL_ECHOLNPGM("retracted_swap[", i, "] ", AS_DIGIT(retracted_swap[i]));
|
||||
SERIAL_ECHOLNPGM("retracted_swap[", e, "] ", AS_DIGIT(retracted_swap[e]));
|
||||
#endif
|
||||
}
|
||||
SERIAL_ECHOLNPGM("current_position.z ", current_position.z);
|
||||
|
@@ -43,7 +43,7 @@ typedef struct {
|
||||
class FWRetract {
|
||||
private:
|
||||
#if HAS_MULTI_EXTRUDER
|
||||
static bool retracted_swap[EXTRUDERS]; // Which extruders are swap-retracted
|
||||
static Flags<EXTRUDERS> retracted_swap; // Which extruders are swap-retracted
|
||||
#endif
|
||||
|
||||
public:
|
||||
@@ -55,7 +55,7 @@ public:
|
||||
static constexpr bool autoretract_enabled = false;
|
||||
#endif
|
||||
|
||||
static bool retracted[EXTRUDERS]; // Which extruders are currently retracted
|
||||
static Flags<EXTRUDERS> retracted; // Which extruders are currently retracted
|
||||
static float current_retract[EXTRUDERS], // Retract value used by planner
|
||||
current_hop; // Hop value used by planner
|
||||
|
||||
@@ -63,9 +63,7 @@ public:
|
||||
|
||||
static void reset();
|
||||
|
||||
static void refresh_autoretract() {
|
||||
LOOP_L_N(i, EXTRUDERS) retracted[i] = false;
|
||||
}
|
||||
static void refresh_autoretract() { retracted.reset(); }
|
||||
|
||||
static void enable_autoretract(const bool enable) {
|
||||
#if ENABLED(FWRETRACT_AUTORETRACT)
|
||||
|
@@ -24,10 +24,10 @@
|
||||
|
||||
#if ENABLED(HOST_ACTION_COMMANDS)
|
||||
|
||||
#include "host_actions.h"
|
||||
|
||||
//#define DEBUG_HOST_ACTIONS
|
||||
|
||||
#include "host_actions.h"
|
||||
|
||||
#if ENABLED(ADVANCED_PAUSE_FEATURE)
|
||||
#include "pause.h"
|
||||
#include "../gcode/queue.h"
|
||||
@@ -37,37 +37,54 @@
|
||||
#include "runout.h"
|
||||
#endif
|
||||
|
||||
void host_action(PGM_P const pstr, const bool eol) {
|
||||
HostUI hostui;
|
||||
|
||||
void HostUI::action(FSTR_P const fstr, const bool eol) {
|
||||
PORT_REDIRECT(SerialMask::All);
|
||||
SERIAL_ECHOPGM("//action:");
|
||||
SERIAL_ECHOPGM_P(pstr);
|
||||
SERIAL_ECHOF(fstr);
|
||||
if (eol) SERIAL_EOL();
|
||||
}
|
||||
|
||||
#ifdef ACTION_ON_KILL
|
||||
void host_action_kill() { host_action(PSTR(ACTION_ON_KILL)); }
|
||||
void HostUI::kill() { action(F(ACTION_ON_KILL)); }
|
||||
#endif
|
||||
#ifdef ACTION_ON_PAUSE
|
||||
void host_action_pause(const bool eol/*=true*/) { host_action(PSTR(ACTION_ON_PAUSE), eol); }
|
||||
void HostUI::pause(const bool eol/*=true*/) { action(F(ACTION_ON_PAUSE), eol); }
|
||||
#endif
|
||||
#ifdef ACTION_ON_PAUSED
|
||||
void host_action_paused(const bool eol/*=true*/) { host_action(PSTR(ACTION_ON_PAUSED), eol); }
|
||||
void HostUI::paused(const bool eol/*=true*/) { action(F(ACTION_ON_PAUSED), eol); }
|
||||
#endif
|
||||
#ifdef ACTION_ON_RESUME
|
||||
void host_action_resume() { host_action(PSTR(ACTION_ON_RESUME)); }
|
||||
void HostUI::resume() { action(F(ACTION_ON_RESUME)); }
|
||||
#endif
|
||||
#ifdef ACTION_ON_RESUMED
|
||||
void host_action_resumed() { host_action(PSTR(ACTION_ON_RESUMED)); }
|
||||
void HostUI::resumed() { action(F(ACTION_ON_RESUMED)); }
|
||||
#endif
|
||||
#ifdef ACTION_ON_CANCEL
|
||||
void host_action_cancel() { host_action(PSTR(ACTION_ON_CANCEL)); }
|
||||
void HostUI::cancel() { action(F(ACTION_ON_CANCEL)); }
|
||||
#endif
|
||||
#ifdef ACTION_ON_START
|
||||
void host_action_start() { host_action(PSTR(ACTION_ON_START)); }
|
||||
void HostUI::start() { action(F(ACTION_ON_START)); }
|
||||
#endif
|
||||
|
||||
#if ENABLED(G29_RETRY_AND_RECOVER)
|
||||
#ifdef ACTION_ON_G29_RECOVER
|
||||
void HostUI::g29_recover() { action(F(ACTION_ON_G29_RECOVER)); }
|
||||
#endif
|
||||
#ifdef ACTION_ON_G29_FAILURE
|
||||
void HostUI::g29_failure() { action(F(ACTION_ON_G29_FAILURE)); }
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef SHUTDOWN_ACTION
|
||||
void HostUI::shutdown() { action(F(SHUTDOWN_ACTION)); }
|
||||
#endif
|
||||
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
|
||||
PromptReason HostUI::host_prompt_reason = PROMPT_NOT_DEFINED;
|
||||
|
||||
PGMSTR(CONTINUE_STR, "Continue");
|
||||
PGMSTR(DISMISS_STR, "Dismiss");
|
||||
|
||||
@@ -75,64 +92,64 @@ void host_action(PGM_P const pstr, const bool eol) {
|
||||
extern bool wait_for_user;
|
||||
#endif
|
||||
|
||||
PromptReason host_prompt_reason = PROMPT_NOT_DEFINED;
|
||||
|
||||
void host_action_notify(const char * const message) {
|
||||
void HostUI::notify(const char * const cstr) {
|
||||
PORT_REDIRECT(SerialMask::All);
|
||||
host_action(PSTR("notification "), false);
|
||||
SERIAL_ECHOLN(message);
|
||||
action(F("notification "), false);
|
||||
SERIAL_ECHOLN(cstr);
|
||||
}
|
||||
|
||||
void host_action_notify_P(PGM_P const message) {
|
||||
void HostUI::notify_P(PGM_P const pstr) {
|
||||
PORT_REDIRECT(SerialMask::All);
|
||||
host_action(PSTR("notification "), false);
|
||||
SERIAL_ECHOLNPGM_P(message);
|
||||
action(F("notification "), false);
|
||||
SERIAL_ECHOLNPGM_P(pstr);
|
||||
}
|
||||
|
||||
void host_action_prompt(PGM_P const ptype, const bool eol=true) {
|
||||
void HostUI::prompt(FSTR_P const ptype, const bool eol/*=true*/) {
|
||||
PORT_REDIRECT(SerialMask::All);
|
||||
host_action(PSTR("prompt_"), false);
|
||||
SERIAL_ECHOPGM_P(ptype);
|
||||
action(F("prompt_"), false);
|
||||
SERIAL_ECHOF(ptype);
|
||||
if (eol) SERIAL_EOL();
|
||||
}
|
||||
|
||||
void host_action_prompt_plus(PGM_P const ptype, PGM_P const pstr, const char extra_char='\0') {
|
||||
host_action_prompt(ptype, false);
|
||||
void HostUI::prompt_plus(FSTR_P const ptype, FSTR_P const fstr, const char extra_char/*='\0'*/) {
|
||||
prompt(ptype, false);
|
||||
PORT_REDIRECT(SerialMask::All);
|
||||
SERIAL_CHAR(' ');
|
||||
SERIAL_ECHOPGM_P(pstr);
|
||||
SERIAL_ECHOF(fstr);
|
||||
if (extra_char != '\0') SERIAL_CHAR(extra_char);
|
||||
SERIAL_EOL();
|
||||
}
|
||||
void host_action_prompt_begin(const PromptReason reason, PGM_P const pstr, const char extra_char/*='\0'*/) {
|
||||
host_action_prompt_end();
|
||||
void HostUI::prompt_begin(const PromptReason reason, FSTR_P const fstr, const char extra_char/*='\0'*/) {
|
||||
prompt_end();
|
||||
host_prompt_reason = reason;
|
||||
host_action_prompt_plus(PSTR("begin"), pstr, extra_char);
|
||||
prompt_plus(F("begin"), fstr, extra_char);
|
||||
}
|
||||
void host_action_prompt_button(PGM_P const pstr) { host_action_prompt_plus(PSTR("button"), pstr); }
|
||||
void host_action_prompt_end() { host_action_prompt(PSTR("end")); }
|
||||
void host_action_prompt_show() { host_action_prompt(PSTR("show")); }
|
||||
void HostUI::prompt_button(FSTR_P const fstr) { prompt_plus(F("button"), fstr); }
|
||||
void HostUI::prompt_end() { prompt(F("end")); }
|
||||
void HostUI::prompt_show() { prompt(F("show")); }
|
||||
|
||||
void _host_prompt_show(PGM_P const btn1/*=nullptr*/, PGM_P const btn2/*=nullptr*/) {
|
||||
if (btn1) host_action_prompt_button(btn1);
|
||||
if (btn2) host_action_prompt_button(btn2);
|
||||
host_action_prompt_show();
|
||||
void HostUI::_prompt_show(FSTR_P const btn1, FSTR_P const btn2) {
|
||||
if (btn1) prompt_button(btn1);
|
||||
if (btn2) prompt_button(btn2);
|
||||
prompt_show();
|
||||
}
|
||||
void host_prompt_do(const PromptReason reason, PGM_P const pstr, PGM_P const btn1/*=nullptr*/, PGM_P const btn2/*=nullptr*/) {
|
||||
host_action_prompt_begin(reason, pstr);
|
||||
_host_prompt_show(btn1, btn2);
|
||||
void HostUI::prompt_do(const PromptReason reason, FSTR_P const fstr, FSTR_P const btn1/*=nullptr*/, FSTR_P const btn2/*=nullptr*/) {
|
||||
prompt_begin(reason, fstr);
|
||||
_prompt_show(btn1, btn2);
|
||||
}
|
||||
void host_prompt_do(const PromptReason reason, PGM_P const pstr, const char extra_char, PGM_P const btn1/*=nullptr*/, PGM_P const btn2/*=nullptr*/) {
|
||||
host_action_prompt_begin(reason, pstr, extra_char);
|
||||
_host_prompt_show(btn1, btn2);
|
||||
void HostUI::prompt_do(const PromptReason reason, FSTR_P const fstr, const char extra_char, FSTR_P const btn1/*=nullptr*/, FSTR_P const btn2/*=nullptr*/) {
|
||||
prompt_begin(reason, fstr, extra_char);
|
||||
_prompt_show(btn1, btn2);
|
||||
}
|
||||
|
||||
void filament_load_host_prompt() {
|
||||
const bool disable_to_continue = TERN0(HAS_FILAMENT_SENSOR, runout.filament_ran_out);
|
||||
host_prompt_do(PROMPT_FILAMENT_RUNOUT, PSTR("Paused"), PSTR("PurgeMore"),
|
||||
disable_to_continue ? PSTR("DisableRunout") : CONTINUE_STR
|
||||
);
|
||||
}
|
||||
#if ENABLED(ADVANCED_PAUSE_FEATURE)
|
||||
void HostUI::filament_load_prompt() {
|
||||
const bool disable_to_continue = TERN0(HAS_FILAMENT_SENSOR, runout.filament_ran_out);
|
||||
prompt_do(PROMPT_FILAMENT_RUNOUT, F("Paused"), F("PurgeMore"),
|
||||
disable_to_continue ? F("DisableRunout") : FPSTR(CONTINUE_STR)
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// Handle responses from the host, such as:
|
||||
@@ -141,7 +158,7 @@ void host_action(PGM_P const pstr, const bool eol) {
|
||||
// - Resume Print response
|
||||
// - Dismissal of info
|
||||
//
|
||||
void host_response_handler(const uint8_t response) {
|
||||
void HostUI::handle_response(const uint8_t response) {
|
||||
const PromptReason hpr = host_prompt_reason;
|
||||
host_prompt_reason = PROMPT_NOT_DEFINED; // Reset now ahead of logic
|
||||
switch (hpr) {
|
||||
|
@@ -24,34 +24,8 @@
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
#include "../HAL/shared/Marduino.h"
|
||||
|
||||
void host_action(PGM_P const pstr, const bool eol=true);
|
||||
|
||||
#ifdef ACTION_ON_KILL
|
||||
void host_action_kill();
|
||||
#endif
|
||||
#ifdef ACTION_ON_PAUSE
|
||||
void host_action_pause(const bool eol=true);
|
||||
#endif
|
||||
#ifdef ACTION_ON_PAUSED
|
||||
void host_action_paused(const bool eol=true);
|
||||
#endif
|
||||
#ifdef ACTION_ON_RESUME
|
||||
void host_action_resume();
|
||||
#endif
|
||||
#ifdef ACTION_ON_RESUMED
|
||||
void host_action_resumed();
|
||||
#endif
|
||||
#ifdef ACTION_ON_CANCEL
|
||||
void host_action_cancel();
|
||||
#endif
|
||||
#ifdef ACTION_ON_START
|
||||
void host_action_start();
|
||||
#endif
|
||||
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
|
||||
extern const char CONTINUE_STR[], DISMISS_STR[];
|
||||
|
||||
enum PromptReason : uint8_t {
|
||||
PROMPT_NOT_DEFINED,
|
||||
PROMPT_FILAMENT_RUNOUT,
|
||||
@@ -61,21 +35,80 @@ void host_action(PGM_P const pstr, const bool eol=true);
|
||||
PROMPT_INFO
|
||||
};
|
||||
|
||||
extern PromptReason host_prompt_reason;
|
||||
|
||||
void host_response_handler(const uint8_t response);
|
||||
void host_action_notify(const char * const message);
|
||||
void host_action_notify_P(PGM_P const message);
|
||||
void host_action_prompt_begin(const PromptReason reason, PGM_P const pstr, const char extra_char='\0');
|
||||
void host_action_prompt_button(PGM_P const pstr);
|
||||
void host_action_prompt_end();
|
||||
void host_action_prompt_show();
|
||||
void host_prompt_do(const PromptReason reason, PGM_P const pstr, PGM_P const btn1=nullptr, PGM_P const btn2=nullptr);
|
||||
void host_prompt_do(const PromptReason reason, PGM_P const pstr, const char extra_char, PGM_P const btn1=nullptr, PGM_P const btn2=nullptr);
|
||||
inline void host_prompt_open(const PromptReason reason, PGM_P const pstr, PGM_P const btn1=nullptr, PGM_P const btn2=nullptr) {
|
||||
if (host_prompt_reason == PROMPT_NOT_DEFINED) host_prompt_do(reason, pstr, btn1, btn2);
|
||||
}
|
||||
|
||||
void filament_load_host_prompt();
|
||||
|
||||
#endif
|
||||
|
||||
class HostUI {
|
||||
public:
|
||||
|
||||
static void action(FSTR_P const fstr, const bool eol=true);
|
||||
|
||||
#ifdef ACTION_ON_KILL
|
||||
static void kill();
|
||||
#endif
|
||||
#ifdef ACTION_ON_PAUSE
|
||||
static void pause(const bool eol=true);
|
||||
#endif
|
||||
#ifdef ACTION_ON_PAUSED
|
||||
static void paused(const bool eol=true);
|
||||
#endif
|
||||
#ifdef ACTION_ON_RESUME
|
||||
static void resume();
|
||||
#endif
|
||||
#ifdef ACTION_ON_RESUMED
|
||||
static void resumed();
|
||||
#endif
|
||||
#ifdef ACTION_ON_CANCEL
|
||||
static void cancel();
|
||||
#endif
|
||||
#ifdef ACTION_ON_START
|
||||
static void start();
|
||||
#endif
|
||||
#ifdef SHUTDOWN_ACTION
|
||||
static void shutdown();
|
||||
#endif
|
||||
|
||||
#if ENABLED(G29_RETRY_AND_RECOVER)
|
||||
#ifdef ACTION_ON_G29_RECOVER
|
||||
static void g29_recover();
|
||||
#endif
|
||||
#ifdef ACTION_ON_G29_FAILURE
|
||||
static void g29_failure();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
private:
|
||||
static void prompt(FSTR_P const ptype, const bool eol=true);
|
||||
static void prompt_plus(FSTR_P const ptype, FSTR_P const fstr, const char extra_char='\0');
|
||||
static void prompt_show();
|
||||
static void _prompt_show(FSTR_P const btn1, FSTR_P const btn2);
|
||||
|
||||
public:
|
||||
static PromptReason host_prompt_reason;
|
||||
|
||||
static void handle_response(const uint8_t response);
|
||||
|
||||
static void notify_P(PGM_P const message);
|
||||
static void notify(FSTR_P const fmsg) { notify_P(FTOP(fmsg)); }
|
||||
static void notify(const char * const message);
|
||||
|
||||
static void prompt_begin(const PromptReason reason, FSTR_P const fstr, const char extra_char='\0');
|
||||
static void prompt_button(FSTR_P const fstr);
|
||||
static void prompt_end();
|
||||
static void prompt_do(const PromptReason reason, FSTR_P const pstr, FSTR_P const btn1=nullptr, FSTR_P const btn2=nullptr);
|
||||
static void prompt_do(const PromptReason reason, FSTR_P const pstr, const char extra_char, FSTR_P const btn1=nullptr, FSTR_P const btn2=nullptr);
|
||||
static void prompt_open(const PromptReason reason, FSTR_P const pstr, FSTR_P const btn1=nullptr, FSTR_P const btn2=nullptr) {
|
||||
if (host_prompt_reason == PROMPT_NOT_DEFINED) prompt_do(reason, pstr, btn1, btn2);
|
||||
}
|
||||
|
||||
#if ENABLED(ADVANCED_PAUSE_FEATURE)
|
||||
static void filament_load_prompt();
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
extern HostUI hostui;
|
||||
|
||||
extern const char CONTINUE_STR[], DISMISS_STR[];
|
||||
|
91
Marlin/src/feature/hotend_idle.cpp
Normal file
91
Marlin/src/feature/hotend_idle.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Hotend Idle Timeout
|
||||
* Prevent filament in the nozzle from charring and causing a critical jam.
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(HOTEND_IDLE_TIMEOUT)
|
||||
|
||||
#include "hotend_idle.h"
|
||||
#include "../gcode/gcode.h"
|
||||
|
||||
#include "../module/temperature.h"
|
||||
#include "../module/motion.h"
|
||||
#include "../module/planner.h"
|
||||
#include "../lcd/marlinui.h"
|
||||
|
||||
extern HotendIdleProtection hotend_idle;
|
||||
|
||||
millis_t HotendIdleProtection::next_protect_ms = 0;
|
||||
|
||||
void HotendIdleProtection::check_hotends(const millis_t &ms) {
|
||||
bool do_prot = false;
|
||||
HOTEND_LOOP() {
|
||||
const bool busy = (TERN0(HAS_RESUME_CONTINUE, wait_for_user) || planner.has_blocks_queued());
|
||||
if (thermalManager.degHotend(e) >= (HOTEND_IDLE_MIN_TRIGGER) && !busy) {
|
||||
do_prot = true; break;
|
||||
}
|
||||
}
|
||||
if (bool(next_protect_ms) != do_prot)
|
||||
next_protect_ms = do_prot ? ms + hp_interval : 0;
|
||||
}
|
||||
|
||||
void HotendIdleProtection::check_e_motion(const millis_t &ms) {
|
||||
static float old_e_position = 0;
|
||||
if (old_e_position != current_position.e) {
|
||||
old_e_position = current_position.e; // Track filament motion
|
||||
if (next_protect_ms) // If some heater is on then...
|
||||
next_protect_ms = ms + hp_interval; // ...delay the timeout till later
|
||||
}
|
||||
}
|
||||
|
||||
void HotendIdleProtection::check() {
|
||||
const millis_t ms = millis(); // Shared millis
|
||||
|
||||
check_hotends(ms); // Any hotends need protection?
|
||||
check_e_motion(ms); // Motion will protect them
|
||||
|
||||
// Hot and not moving for too long...
|
||||
if (next_protect_ms && ELAPSED(ms, next_protect_ms))
|
||||
timed_out();
|
||||
}
|
||||
|
||||
// Lower (but don't raise) hotend / bed temperatures
|
||||
void HotendIdleProtection::timed_out() {
|
||||
next_protect_ms = 0;
|
||||
SERIAL_ECHOLNPGM("Hotend Idle Timeout");
|
||||
LCD_MESSAGE(MSG_HOTEND_IDLE_TIMEOUT);
|
||||
HOTEND_LOOP() {
|
||||
if ((HOTEND_IDLE_NOZZLE_TARGET) < thermalManager.degTargetHotend(e))
|
||||
thermalManager.setTargetHotend(HOTEND_IDLE_NOZZLE_TARGET, e);
|
||||
}
|
||||
#if HAS_HEATED_BED
|
||||
if ((HOTEND_IDLE_BED_TARGET) < thermalManager.degTargetBed())
|
||||
thermalManager.setTargetBed(HOTEND_IDLE_BED_TARGET);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // HOTEND_IDLE_TIMEOUT
|
@@ -21,25 +21,17 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../../../inc/MarlinConfigPre.h"
|
||||
#include "../core/millis_t.h"
|
||||
|
||||
extern xy_pos_t bilinear_grid_spacing, bilinear_start;
|
||||
extern xy_float_t bilinear_grid_factor;
|
||||
extern bed_mesh_t z_values;
|
||||
float bilinear_z_offset(const xy_pos_t &raw);
|
||||
class HotendIdleProtection {
|
||||
public:
|
||||
static void check();
|
||||
private:
|
||||
static constexpr millis_t hp_interval = SEC_TO_MS(HOTEND_IDLE_TIMEOUT_SEC);
|
||||
static millis_t next_protect_ms;
|
||||
static void check_hotends(const millis_t &ms);
|
||||
static void check_e_motion(const millis_t &ms);
|
||||
static void timed_out();
|
||||
};
|
||||
|
||||
void extrapolate_unprobed_bed_level();
|
||||
void print_bilinear_leveling_grid();
|
||||
void refresh_bed_level();
|
||||
#if ENABLED(ABL_BILINEAR_SUBDIVISION)
|
||||
void print_bilinear_leveling_grid_virt();
|
||||
void bed_level_virt_interpolate();
|
||||
#endif
|
||||
|
||||
#if IS_CARTESIAN && DISABLED(SEGMENT_LEVELED_MOVES)
|
||||
void bilinear_line_to_destination(const_feedRate_t scaled_fr_mm_s, uint16_t x_splits=0xFFFF, uint16_t y_splits=0xFFFF);
|
||||
#endif
|
||||
|
||||
#define _GET_MESH_X(I) float(bilinear_start.x + (I) * bilinear_grid_spacing.x)
|
||||
#define _GET_MESH_Y(J) float(bilinear_start.y + (J) * bilinear_grid_spacing.y)
|
||||
#define Z_VALUES_ARR z_values
|
||||
extern HotendIdleProtection hotend_idle;
|
@@ -68,13 +68,13 @@ Joystick joystick;
|
||||
void Joystick::report() {
|
||||
SERIAL_ECHOPGM("Joystick");
|
||||
#if HAS_JOY_ADC_X
|
||||
SERIAL_ECHOPGM_P(SP_X_STR, JOY_X(x.raw));
|
||||
SERIAL_ECHOPGM_P(SP_X_STR, JOY_X(x.getraw()));
|
||||
#endif
|
||||
#if HAS_JOY_ADC_Y
|
||||
SERIAL_ECHOPGM_P(SP_Y_STR, JOY_Y(y.raw));
|
||||
SERIAL_ECHOPGM_P(SP_Y_STR, JOY_Y(y.getraw()));
|
||||
#endif
|
||||
#if HAS_JOY_ADC_Z
|
||||
SERIAL_ECHOPGM_P(SP_Z_STR, JOY_Z(z.raw));
|
||||
SERIAL_ECHOPGM_P(SP_Z_STR, JOY_Z(z.getraw()));
|
||||
#endif
|
||||
#if HAS_JOY_ADC_EN
|
||||
SERIAL_ECHO_TERNARY(READ(JOY_EN_PIN), " EN=", "HIGH (dis", "LOW (en", "abled)");
|
||||
@@ -91,29 +91,29 @@ Joystick joystick;
|
||||
if (READ(JOY_EN_PIN)) return;
|
||||
#endif
|
||||
|
||||
auto _normalize_joy = [](float &axis_jog, const int16_t raw, const int16_t (&joy_limits)[4]) {
|
||||
auto _normalize_joy = [](float &axis_jog, const raw_adc_t raw, const raw_adc_t (&joy_limits)[4]) {
|
||||
if (WITHIN(raw, joy_limits[0], joy_limits[3])) {
|
||||
// within limits, check deadzone
|
||||
if (raw > joy_limits[2])
|
||||
axis_jog = (raw - joy_limits[2]) / float(joy_limits[3] - joy_limits[2]);
|
||||
else if (raw < joy_limits[1])
|
||||
axis_jog = (raw - joy_limits[1]) / float(joy_limits[1] - joy_limits[0]); // negative value
|
||||
axis_jog = int16_t(raw - joy_limits[1]) / float(joy_limits[1] - joy_limits[0]); // negative value
|
||||
// Map normal to jog value via quadratic relationship
|
||||
axis_jog = SIGN(axis_jog) * sq(axis_jog);
|
||||
}
|
||||
};
|
||||
|
||||
#if HAS_JOY_ADC_X
|
||||
static constexpr int16_t joy_x_limits[4] = JOY_X_LIMITS;
|
||||
_normalize_joy(norm_jog.x, JOY_X(x.raw), joy_x_limits);
|
||||
static constexpr raw_adc_t joy_x_limits[4] = JOY_X_LIMITS;
|
||||
_normalize_joy(norm_jog.x, JOY_X(x.getraw()), joy_x_limits);
|
||||
#endif
|
||||
#if HAS_JOY_ADC_Y
|
||||
static constexpr int16_t joy_y_limits[4] = JOY_Y_LIMITS;
|
||||
_normalize_joy(norm_jog.y, JOY_Y(y.raw), joy_y_limits);
|
||||
static constexpr raw_adc_t joy_y_limits[4] = JOY_Y_LIMITS;
|
||||
_normalize_joy(norm_jog.y, JOY_Y(y.getraw()), joy_y_limits);
|
||||
#endif
|
||||
#if HAS_JOY_ADC_Z
|
||||
static constexpr int16_t joy_z_limits[4] = JOY_Z_LIMITS;
|
||||
_normalize_joy(norm_jog.z, JOY_Z(z.raw), joy_z_limits);
|
||||
static constexpr raw_adc_t joy_z_limits[4] = JOY_Z_LIMITS;
|
||||
_normalize_joy(norm_jog.z, JOY_Z(z.getraw()), joy_z_limits);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ Joystick joystick;
|
||||
// norm_jog values of [-1 .. 1] maps linearly to [-feedrate .. feedrate]
|
||||
xyz_float_t move_dist{0};
|
||||
float hypot2 = 0;
|
||||
LOOP_LINEAR_AXES(i) if (norm_jog[i]) {
|
||||
LOOP_NUM_AXES(i) if (norm_jog[i]) {
|
||||
move_dist[i] = seg_time * norm_jog[i] * TERN(EXTENSIBLE_UI, manual_feedrate_mm_s, planner.settings.max_feedrate_mm_s)[i];
|
||||
hypot2 += sq(move_dist[i]);
|
||||
}
|
||||
@@ -172,8 +172,9 @@ Joystick joystick;
|
||||
current_position += move_dist;
|
||||
apply_motion_limits(current_position);
|
||||
const float length = sqrt(hypot2);
|
||||
PlannerHints hints(length);
|
||||
injecting_now = true;
|
||||
planner.buffer_line(current_position, length / seg_time, active_extruder, length);
|
||||
planner.buffer_line(current_position, length / seg_time, active_extruder, hints);
|
||||
injecting_now = false;
|
||||
}
|
||||
}
|
||||
|
@@ -42,7 +42,7 @@
|
||||
#include "pca9533.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(CASE_LIGHT_USE_RGB_LED)
|
||||
#if EITHER(CASE_LIGHT_USE_RGB_LED, CASE_LIGHT_USE_NEOPIXEL)
|
||||
#include "../../feature/caselight.h"
|
||||
#endif
|
||||
|
||||
@@ -95,6 +95,10 @@ void LEDLights::set_color(const LEDColor &incol
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if BOTH(CASE_LIGHT_MENU, CASE_LIGHT_USE_NEOPIXEL)
|
||||
// Update brightness only if caselight is ON or switching leds off
|
||||
if (caselight.on || incol.is_off())
|
||||
#endif
|
||||
neo.set_brightness(incol.i);
|
||||
|
||||
#if ENABLED(NEOPIXEL_IS_SEQUENTIAL)
|
||||
@@ -106,6 +110,10 @@ void LEDLights::set_color(const LEDColor &incol
|
||||
}
|
||||
#endif
|
||||
|
||||
#if BOTH(CASE_LIGHT_MENU, CASE_LIGHT_USE_NEOPIXEL)
|
||||
// Update color only if caselight is ON or switching leds off
|
||||
if (caselight.on || incol.is_off())
|
||||
#endif
|
||||
neo.set_color(neocolor);
|
||||
|
||||
#endif
|
||||
@@ -121,11 +129,11 @@ void LEDLights::set_color(const LEDColor &incol
|
||||
|
||||
// This variant uses 3-4 separate pins for the RGB(W) components.
|
||||
// If the pins can do PWM then their intensity will be set.
|
||||
#define _UPDATE_RGBW(C,c) do { \
|
||||
if (PWM_PIN(RGB_LED_##C##_PIN)) \
|
||||
analogWrite(pin_t(RGB_LED_##C##_PIN), c); \
|
||||
else \
|
||||
WRITE(RGB_LED_##C##_PIN, c ? HIGH : LOW); \
|
||||
#define _UPDATE_RGBW(C,c) do { \
|
||||
if (PWM_PIN(RGB_LED_##C##_PIN)) \
|
||||
hal.set_pwm_duty(pin_t(RGB_LED_##C##_PIN), c); \
|
||||
else \
|
||||
WRITE(RGB_LED_##C##_PIN, c ? HIGH : LOW); \
|
||||
}while(0)
|
||||
#define UPDATE_RGBW(C,c) _UPDATE_RGBW(C, TERN1(CASE_LIGHT_USE_RGB_LED, caselight.on) ? incol.c : 0)
|
||||
UPDATE_RGBW(R,r); UPDATE_RGBW(G,g); UPDATE_RGBW(B,b);
|
||||
@@ -150,7 +158,7 @@ void LEDLights::set_color(const LEDColor &incol
|
||||
void LEDLights::toggle() { if (lights_on) set_off(); else update(); }
|
||||
#endif
|
||||
|
||||
#ifdef LED_BACKLIGHT_TIMEOUT
|
||||
#if LED_POWEROFF_TIMEOUT > 0
|
||||
|
||||
millis_t LEDLights::led_off_time; // = 0
|
||||
|
||||
@@ -170,9 +178,9 @@ void LEDLights::set_color(const LEDColor &incol
|
||||
|
||||
#if ENABLED(NEO2_COLOR_PRESETS)
|
||||
const LEDColor LEDLights2::defaultLEDColor = LEDColor(
|
||||
LED_USER_PRESET_RED, LED_USER_PRESET_GREEN, LED_USER_PRESET_BLUE
|
||||
OPTARG(HAS_WHITE_LED2, LED_USER_PRESET_WHITE)
|
||||
OPTARG(NEOPIXEL_LED, LED_USER_PRESET_BRIGHTNESS)
|
||||
NEO2_USER_PRESET_RED, NEO2_USER_PRESET_GREEN, NEO2_USER_PRESET_BLUE
|
||||
OPTARG(HAS_WHITE_LED2, NEO2_USER_PRESET_WHITE)
|
||||
OPTARG(NEOPIXEL_LED, NEO2_USER_PRESET_BRIGHTNESS)
|
||||
);
|
||||
#endif
|
||||
|
||||
|
@@ -54,6 +54,8 @@ typedef struct LEDColor {
|
||||
OPTARG(NEOPIXEL_LED, i(NEOPIXEL_BRIGHTNESS))
|
||||
{}
|
||||
|
||||
LEDColor(const LEDColor&) = default;
|
||||
|
||||
LEDColor(uint8_t r, uint8_t g, uint8_t b OPTARG(HAS_WHITE_LED, uint8_t w=0) OPTARG(NEOPIXEL_LED, uint8_t i=NEOPIXEL_BRIGHTNESS))
|
||||
: r(r), g(g), b(b) OPTARG(HAS_WHITE_LED, w(w)) OPTARG(NEOPIXEL_LED, i(i)) {}
|
||||
|
||||
@@ -68,11 +70,6 @@ typedef struct LEDColor {
|
||||
return *this;
|
||||
}
|
||||
|
||||
LEDColor& operator=(const LEDColor &right) {
|
||||
if (this != &right) memcpy(this, &right, sizeof(LEDColor));
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const LEDColor &right) {
|
||||
if (this == &right) return true;
|
||||
return 0 == memcmp(this, &right, sizeof(LEDColor));
|
||||
@@ -118,7 +115,7 @@ public:
|
||||
OPTARG(NEOPIXEL_IS_SEQUENTIAL, bool isSequence=false)
|
||||
);
|
||||
|
||||
static inline void set_color(uint8_t r, uint8_t g, uint8_t b
|
||||
static void set_color(uint8_t r, uint8_t g, uint8_t b
|
||||
OPTARG(HAS_WHITE_LED, uint8_t w=0)
|
||||
OPTARG(NEOPIXEL_LED, uint8_t i=NEOPIXEL_BRIGHTNESS)
|
||||
OPTARG(NEOPIXEL_IS_SEQUENTIAL, bool isSequence=false)
|
||||
@@ -126,23 +123,23 @@ public:
|
||||
set_color(LEDColor(r, g, b OPTARG(HAS_WHITE_LED, w) OPTARG(NEOPIXEL_LED, i)) OPTARG(NEOPIXEL_IS_SEQUENTIAL, isSequence));
|
||||
}
|
||||
|
||||
static inline void set_off() { set_color(LEDColorOff()); }
|
||||
static inline void set_green() { set_color(LEDColorGreen()); }
|
||||
static inline void set_white() { set_color(LEDColorWhite()); }
|
||||
static void set_off() { set_color(LEDColorOff()); }
|
||||
static void set_green() { set_color(LEDColorGreen()); }
|
||||
static void set_white() { set_color(LEDColorWhite()); }
|
||||
|
||||
#if ENABLED(LED_COLOR_PRESETS)
|
||||
static const LEDColor defaultLEDColor;
|
||||
static inline void set_default() { set_color(defaultLEDColor); }
|
||||
static inline void set_red() { set_color(LEDColorRed()); }
|
||||
static inline void set_orange() { set_color(LEDColorOrange()); }
|
||||
static inline void set_yellow() { set_color(LEDColorYellow()); }
|
||||
static inline void set_blue() { set_color(LEDColorBlue()); }
|
||||
static inline void set_indigo() { set_color(LEDColorIndigo()); }
|
||||
static inline void set_violet() { set_color(LEDColorViolet()); }
|
||||
static void set_default() { set_color(defaultLEDColor); }
|
||||
static void set_red() { set_color(LEDColorRed()); }
|
||||
static void set_orange() { set_color(LEDColorOrange()); }
|
||||
static void set_yellow() { set_color(LEDColorYellow()); }
|
||||
static void set_blue() { set_color(LEDColorBlue()); }
|
||||
static void set_indigo() { set_color(LEDColorIndigo()); }
|
||||
static void set_violet() { set_color(LEDColorViolet()); }
|
||||
#endif
|
||||
|
||||
#if ENABLED(PRINTER_EVENT_LEDS)
|
||||
static inline LEDColor get_color() { return lights_on ? color : LEDColorOff(); }
|
||||
static LEDColor get_color() { return lights_on ? color : LEDColorOff(); }
|
||||
#endif
|
||||
|
||||
#if ANY(LED_CONTROL_MENU, PRINTER_EVENT_LEDS, CASE_LIGHT_IS_COLOR_LED)
|
||||
@@ -154,15 +151,15 @@ public:
|
||||
static void toggle(); // swap "off" with color
|
||||
#endif
|
||||
#if EITHER(LED_CONTROL_MENU, CASE_LIGHT_USE_RGB_LED)
|
||||
static inline void update() { set_color(color); }
|
||||
static void update() { set_color(color); }
|
||||
#endif
|
||||
|
||||
#ifdef LED_BACKLIGHT_TIMEOUT
|
||||
#if LED_POWEROFF_TIMEOUT > 0
|
||||
private:
|
||||
static millis_t led_off_time;
|
||||
public:
|
||||
static inline void reset_timeout(const millis_t &ms) {
|
||||
led_off_time = ms + LED_BACKLIGHT_TIMEOUT;
|
||||
static void reset_timeout(const millis_t &ms) {
|
||||
led_off_time = ms + LED_POWEROFF_TIMEOUT;
|
||||
if (!lights_on) update();
|
||||
}
|
||||
static void update_timeout(const bool power_on);
|
||||
@@ -181,7 +178,7 @@ extern LEDLights leds;
|
||||
|
||||
static void set_color(const LEDColor &color);
|
||||
|
||||
static inline void set_color(uint8_t r, uint8_t g, uint8_t b
|
||||
static void set_color(uint8_t r, uint8_t g, uint8_t b
|
||||
OPTARG(HAS_WHITE_LED, uint8_t w=0)
|
||||
OPTARG(NEOPIXEL_LED, uint8_t i=NEOPIXEL_BRIGHTNESS)
|
||||
) {
|
||||
@@ -191,26 +188,26 @@ extern LEDLights leds;
|
||||
));
|
||||
}
|
||||
|
||||
static inline void set_off() { set_color(LEDColorOff()); }
|
||||
static inline void set_green() { set_color(LEDColorGreen()); }
|
||||
static inline void set_white() { set_color(LEDColorWhite()); }
|
||||
static void set_off() { set_color(LEDColorOff()); }
|
||||
static void set_green() { set_color(LEDColorGreen()); }
|
||||
static void set_white() { set_color(LEDColorWhite()); }
|
||||
|
||||
#if ENABLED(NEO2_COLOR_PRESETS)
|
||||
static const LEDColor defaultLEDColor;
|
||||
static inline void set_default() { set_color(defaultLEDColor); }
|
||||
static inline void set_red() { set_color(LEDColorRed()); }
|
||||
static inline void set_orange() { set_color(LEDColorOrange()); }
|
||||
static inline void set_yellow() { set_color(LEDColorYellow()); }
|
||||
static inline void set_blue() { set_color(LEDColorBlue()); }
|
||||
static inline void set_indigo() { set_color(LEDColorIndigo()); }
|
||||
static inline void set_violet() { set_color(LEDColorViolet()); }
|
||||
static void set_default() { set_color(defaultLEDColor); }
|
||||
static void set_red() { set_color(LEDColorRed()); }
|
||||
static void set_orange() { set_color(LEDColorOrange()); }
|
||||
static void set_yellow() { set_color(LEDColorYellow()); }
|
||||
static void set_blue() { set_color(LEDColorBlue()); }
|
||||
static void set_indigo() { set_color(LEDColorIndigo()); }
|
||||
static void set_violet() { set_color(LEDColorViolet()); }
|
||||
#endif
|
||||
|
||||
#if ENABLED(NEOPIXEL2_SEPARATE)
|
||||
static LEDColor color; // last non-off color
|
||||
static bool lights_on; // the last set color was "on"
|
||||
static void toggle(); // swap "off" with color
|
||||
static inline void update() { set_color(color); }
|
||||
static void update() { set_color(color); }
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@@ -35,7 +35,7 @@
|
||||
#endif
|
||||
|
||||
Marlin_NeoPixel neo;
|
||||
int8_t Marlin_NeoPixel::neoindex;
|
||||
pixel_index_t Marlin_NeoPixel::neoindex;
|
||||
|
||||
Adafruit_NeoPixel Marlin_NeoPixel::adaneo1(NEOPIXEL_PIXELS, NEOPIXEL_PIN, NEOPIXEL_TYPE + NEO_KHZ800);
|
||||
#if CONJOINED_NEOPIXEL
|
||||
@@ -44,14 +44,14 @@ Adafruit_NeoPixel Marlin_NeoPixel::adaneo1(NEOPIXEL_PIXELS, NEOPIXEL_PIN, NEOPIX
|
||||
|
||||
#ifdef NEOPIXEL_BKGD_INDEX_FIRST
|
||||
|
||||
void Marlin_NeoPixel::set_background_color(uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
|
||||
for (int background_led = NEOPIXEL_BKGD_INDEX_FIRST; background_led <= NEOPIXEL_BKGD_INDEX_LAST; background_led++)
|
||||
void Marlin_NeoPixel::set_background_color(const uint8_t r, const uint8_t g, const uint8_t b, const uint8_t w) {
|
||||
for (int background_led = NEOPIXEL_BKGD_INDEX_FIRST; background_led <= NEOPIXEL_BKGD_INDEX_LAST; background_led++)
|
||||
set_pixel_color(background_led, adaneo1.Color(r, g, b, w));
|
||||
}
|
||||
|
||||
void Marlin_NeoPixel::reset_background_color() {
|
||||
constexpr uint8_t background_color[4] = NEOPIXEL_BKGD_COLOR;
|
||||
set_background_color(background_color[0], background_color[1], background_color[2], background_color[3]);
|
||||
set_background_color(background_color);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -108,7 +108,7 @@ void Marlin_NeoPixel::init() {
|
||||
set_color(adaneo1.Color
|
||||
TERN(LED_USER_PRESET_STARTUP,
|
||||
(LED_USER_PRESET_RED, LED_USER_PRESET_GREEN, LED_USER_PRESET_BLUE, LED_USER_PRESET_WHITE),
|
||||
(0, 0, 0, 0))
|
||||
(255, 255, 255, 255))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ void Marlin_NeoPixel::init() {
|
||||
|
||||
Marlin_NeoPixel2 neo2;
|
||||
|
||||
int8_t Marlin_NeoPixel2::neoindex;
|
||||
pixel_index_t Marlin_NeoPixel2::neoindex;
|
||||
Adafruit_NeoPixel Marlin_NeoPixel2::adaneo(NEOPIXEL2_PIXELS, NEOPIXEL2_PIN, NEOPIXEL2_TYPE);
|
||||
|
||||
void Marlin_NeoPixel2::set_color(const uint32_t color) {
|
||||
|
@@ -63,7 +63,13 @@
|
||||
#endif
|
||||
|
||||
// ------------------------
|
||||
// Function prototypes
|
||||
// Types
|
||||
// ------------------------
|
||||
|
||||
typedef IF<(TERN0(NEOPIXEL_LED, NEOPIXEL_PIXELS > 127)), int16_t, int8_t>::type pixel_index_t;
|
||||
|
||||
// ------------------------
|
||||
// Classes
|
||||
// ------------------------
|
||||
|
||||
class Marlin_NeoPixel {
|
||||
@@ -74,7 +80,7 @@ private:
|
||||
#endif
|
||||
|
||||
public:
|
||||
static int8_t neoindex;
|
||||
static pixel_index_t neoindex;
|
||||
|
||||
static void init();
|
||||
static void set_color_startup(const uint32_t c);
|
||||
@@ -82,16 +88,17 @@ public:
|
||||
static void set_color(const uint32_t c);
|
||||
|
||||
#ifdef NEOPIXEL_BKGD_INDEX_FIRST
|
||||
static void set_background_color(uint8_t r, uint8_t g, uint8_t b, uint8_t w);
|
||||
static void set_background_color(const uint8_t r, const uint8_t g, const uint8_t b, const uint8_t w);
|
||||
static void set_background_color(const uint8_t (&rgbw)[4]) { set_background_color(rgbw[0], rgbw[1], rgbw[2], rgbw[3]); }
|
||||
static void reset_background_color();
|
||||
#endif
|
||||
|
||||
static inline void begin() {
|
||||
static void begin() {
|
||||
adaneo1.begin();
|
||||
TERN_(CONJOINED_NEOPIXEL, adaneo2.begin());
|
||||
}
|
||||
|
||||
static inline void set_pixel_color(const uint16_t n, const uint32_t c) {
|
||||
static void set_pixel_color(const uint16_t n, const uint32_t c) {
|
||||
#if ENABLED(NEOPIXEL2_INSERIES)
|
||||
if (n >= NEOPIXEL_PIXELS) adaneo2.setPixelColor(n - (NEOPIXEL_PIXELS), c);
|
||||
else adaneo1.setPixelColor(n, c);
|
||||
@@ -101,12 +108,12 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void set_brightness(const uint8_t b) {
|
||||
static void set_brightness(const uint8_t b) {
|
||||
adaneo1.setBrightness(b);
|
||||
TERN_(CONJOINED_NEOPIXEL, adaneo2.setBrightness(b));
|
||||
}
|
||||
|
||||
static inline void show() {
|
||||
static void show() {
|
||||
// Some platforms cannot maintain PWM output when NeoPixel disables interrupts for long durations.
|
||||
TERN_(HAS_PAUSE_SERVO_OUTPUT, PAUSE_SERVO_OUTPUT());
|
||||
adaneo1.show();
|
||||
@@ -122,11 +129,18 @@ public:
|
||||
}
|
||||
|
||||
// Accessors
|
||||
static inline uint16_t pixels() { return adaneo1.numPixels() * TERN1(NEOPIXEL2_INSERIES, 2); }
|
||||
static uint16_t pixels() { return adaneo1.numPixels() * TERN1(NEOPIXEL2_INSERIES, 2); }
|
||||
|
||||
static inline uint8_t brightness() { return adaneo1.getBrightness(); }
|
||||
static uint32_t pixel_color(const uint16_t n) {
|
||||
#if ENABLED(NEOPIXEL2_INSERIES)
|
||||
if (n >= NEOPIXEL_PIXELS) return adaneo2.getPixelColor(n - (NEOPIXEL_PIXELS));
|
||||
#endif
|
||||
return adaneo1.getPixelColor(n);
|
||||
}
|
||||
|
||||
static inline uint32_t Color(uint8_t r, uint8_t g, uint8_t b OPTARG(HAS_WHITE_LED, uint8_t w)) {
|
||||
static uint8_t brightness() { return adaneo1.getBrightness(); }
|
||||
|
||||
static uint32_t Color(uint8_t r, uint8_t g, uint8_t b OPTARG(HAS_WHITE_LED, uint8_t w)) {
|
||||
return adaneo1.Color(r, g, b OPTARG(HAS_WHITE_LED, w));
|
||||
}
|
||||
};
|
||||
@@ -150,25 +164,26 @@ extern Marlin_NeoPixel neo;
|
||||
static Adafruit_NeoPixel adaneo;
|
||||
|
||||
public:
|
||||
static int8_t neoindex;
|
||||
static pixel_index_t neoindex;
|
||||
|
||||
static void init();
|
||||
static void set_color_startup(const uint32_t c);
|
||||
|
||||
static void set_color(const uint32_t c);
|
||||
|
||||
static inline void begin() { adaneo.begin(); }
|
||||
static inline void set_pixel_color(const uint16_t n, const uint32_t c) { adaneo.setPixelColor(n, c); }
|
||||
static inline void set_brightness(const uint8_t b) { adaneo.setBrightness(b); }
|
||||
static inline void show() {
|
||||
static void begin() { adaneo.begin(); }
|
||||
static void set_pixel_color(const uint16_t n, const uint32_t c) { adaneo.setPixelColor(n, c); }
|
||||
static void set_brightness(const uint8_t b) { adaneo.setBrightness(b); }
|
||||
static void show() {
|
||||
adaneo.show();
|
||||
adaneo.setPin(NEOPIXEL2_PIN);
|
||||
}
|
||||
|
||||
// Accessors
|
||||
static inline uint16_t pixels() { return adaneo.numPixels();}
|
||||
static inline uint8_t brightness() { return adaneo.getBrightness(); }
|
||||
static inline uint32_t Color(uint8_t r, uint8_t g, uint8_t b OPTARG(HAS_WHITE_LED2, uint8_t w)) {
|
||||
static uint16_t pixels() { return adaneo.numPixels();}
|
||||
static uint32_t pixel_color(const uint16_t n) { return adaneo.getPixelColor(n); }
|
||||
static uint8_t brightness() { return adaneo.getBrightness(); }
|
||||
static uint32_t Color(uint8_t r, uint8_t g, uint8_t b OPTARG(HAS_WHITE_LED2, uint8_t w)) {
|
||||
return adaneo.Color(r, g, b OPTARG(HAS_WHITE_LED2, w));
|
||||
}
|
||||
};
|
||||
|
@@ -36,32 +36,32 @@ private:
|
||||
static bool leds_off_after_print;
|
||||
#endif
|
||||
|
||||
static inline void set_done() { TERN(LED_COLOR_PRESETS, leds.set_default(), leds.set_off()); }
|
||||
static void set_done() { TERN(LED_COLOR_PRESETS, leds.set_default(), leds.set_off()); }
|
||||
|
||||
public:
|
||||
#if HAS_TEMP_HOTEND
|
||||
static inline LEDColor onHotendHeatingStart() { old_intensity = 0; return leds.get_color(); }
|
||||
static LEDColor onHotendHeatingStart() { old_intensity = 0; return leds.get_color(); }
|
||||
static void onHotendHeating(const celsius_t start, const celsius_t current, const celsius_t target);
|
||||
#endif
|
||||
|
||||
#if HAS_HEATED_BED
|
||||
static inline LEDColor onBedHeatingStart() { old_intensity = 127; return leds.get_color(); }
|
||||
static LEDColor onBedHeatingStart() { old_intensity = 127; return leds.get_color(); }
|
||||
static void onBedHeating(const celsius_t start, const celsius_t current, const celsius_t target);
|
||||
#endif
|
||||
|
||||
#if HAS_HEATED_CHAMBER
|
||||
static inline LEDColor onChamberHeatingStart() { old_intensity = 127; return leds.get_color(); }
|
||||
static LEDColor onChamberHeatingStart() { old_intensity = 127; return leds.get_color(); }
|
||||
static void onChamberHeating(const celsius_t start, const celsius_t current, const celsius_t target);
|
||||
#endif
|
||||
|
||||
#if HAS_TEMP_HOTEND || HAS_HEATED_BED || HAS_HEATED_CHAMBER
|
||||
static inline void onHeatingDone() { leds.set_white(); }
|
||||
static inline void onPidTuningDone(LEDColor c) { leds.set_color(c); }
|
||||
static void onHeatingDone() { leds.set_white(); }
|
||||
static void onPidTuningDone(LEDColor c) { leds.set_color(c); }
|
||||
#endif
|
||||
|
||||
#if ENABLED(SDSUPPORT)
|
||||
|
||||
static inline void onPrintCompleted() {
|
||||
static void onPrintCompleted() {
|
||||
leds.set_green();
|
||||
#if HAS_LEDS_OFF_FLAG
|
||||
leds_off_after_print = true;
|
||||
@@ -71,7 +71,7 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void onResumeAfterWait() {
|
||||
static void onResumeAfterWait() {
|
||||
#if HAS_LEDS_OFF_FLAG
|
||||
if (leds_off_after_print) {
|
||||
set_done();
|
||||
|
@@ -44,7 +44,6 @@
|
||||
#include "max7219.h"
|
||||
|
||||
#include "../module/planner.h"
|
||||
#include "../module/stepper.h"
|
||||
#include "../MarlinCore.h"
|
||||
#include "../HAL/shared/Delay.h"
|
||||
|
||||
@@ -52,6 +51,7 @@
|
||||
#define HAS_SIDE_BY_SIDE 1
|
||||
#endif
|
||||
|
||||
#define _ROT ((MAX7219_ROTATE + 360) % 360)
|
||||
#if _ROT == 0 || _ROT == 180
|
||||
#define MAX7219_X_LEDS TERN(HAS_SIDE_BY_SIDE, 8, MAX7219_LINES)
|
||||
#define MAX7219_Y_LEDS TERN(HAS_SIDE_BY_SIDE, MAX7219_LINES, 8)
|
||||
@@ -62,6 +62,15 @@
|
||||
#error "MAX7219_ROTATE must be a multiple of +/- 90°."
|
||||
#endif
|
||||
|
||||
#ifdef MAX7219_DEBUG_PROFILE
|
||||
CodeProfiler::Mode CodeProfiler::mode = ACCUMULATE_AVERAGE;
|
||||
uint8_t CodeProfiler::instance_count = 0;
|
||||
uint32_t CodeProfiler::last_calc_time = micros();
|
||||
uint8_t CodeProfiler::time_fraction = 0;
|
||||
uint32_t CodeProfiler::total_time = 0;
|
||||
uint16_t CodeProfiler::call_count = 0;
|
||||
#endif
|
||||
|
||||
Max7219 max7219;
|
||||
|
||||
uint8_t Max7219::led_line[MAX7219_LINES]; // = { 0 };
|
||||
@@ -69,7 +78,7 @@ uint8_t Max7219::suspended; // = 0;
|
||||
|
||||
#define LINE_REG(Q) (max7219_reg_digit0 + ((Q) & 0x7))
|
||||
|
||||
#if _ROT == 0 || _ROT == 270
|
||||
#if (_ROT == 0 || _ROT == 270) == DISABLED(MAX7219_REVERSE_EACH)
|
||||
#define _LED_BIT(Q) (7 - ((Q) & 0x7))
|
||||
#else
|
||||
#define _LED_BIT(Q) ((Q) & 0x7)
|
||||
@@ -124,11 +133,10 @@ uint8_t Max7219::suspended; // = 0;
|
||||
#define SIG_DELAY() DELAY_NS(250)
|
||||
#endif
|
||||
|
||||
void Max7219::error(const char * const func, const int32_t v1, const int32_t v2/*=-1*/) {
|
||||
void Max7219::error(FSTR_P const func, const int32_t v1, const int32_t v2/*=-1*/) {
|
||||
#if ENABLED(MAX7219_ERRORS)
|
||||
SERIAL_ECHOPGM("??? Max7219::");
|
||||
SERIAL_ECHOPGM_P(func);
|
||||
SERIAL_CHAR('(');
|
||||
SERIAL_ECHOF(func, AS_CHAR('('));
|
||||
SERIAL_ECHO(v1);
|
||||
if (v2 > 0) SERIAL_ECHOPGM(", ", v2);
|
||||
SERIAL_CHAR(')');
|
||||
@@ -267,26 +275,27 @@ void Max7219::set(const uint8_t line, const uint8_t bits) {
|
||||
#endif // MAX7219_NUMERIC
|
||||
|
||||
// Modify a single LED bit and send the changed line
|
||||
void Max7219::led_set(const uint8_t x, const uint8_t y, const bool on) {
|
||||
if (x >= MAX7219_X_LEDS || y >= MAX7219_Y_LEDS) return error(PSTR("led_set"), x, y);
|
||||
void Max7219::led_set(const uint8_t x, const uint8_t y, const bool on, uint8_t * const rcm/*=nullptr*/) {
|
||||
if (x >= MAX7219_X_LEDS || y >= MAX7219_Y_LEDS) return error(F("led_set"), x, y);
|
||||
if (BIT_7219(x, y) == on) return;
|
||||
XOR_7219(x, y);
|
||||
refresh_unit_line(LED_IND(x, y));
|
||||
if (rcm != nullptr) *rcm |= _BV(LED_IND(x, y) & 0x07);
|
||||
}
|
||||
|
||||
void Max7219::led_on(const uint8_t x, const uint8_t y) {
|
||||
if (x >= MAX7219_X_LEDS || y >= MAX7219_Y_LEDS) return error(PSTR("led_on"), x, y);
|
||||
led_set(x, y, true);
|
||||
void Max7219::led_on(const uint8_t x, const uint8_t y, uint8_t * const rcm/*=nullptr*/) {
|
||||
if (x >= MAX7219_X_LEDS || y >= MAX7219_Y_LEDS) return error(F("led_on"), x, y);
|
||||
led_set(x, y, true, rcm);
|
||||
}
|
||||
|
||||
void Max7219::led_off(const uint8_t x, const uint8_t y) {
|
||||
if (x >= MAX7219_X_LEDS || y >= MAX7219_Y_LEDS) return error(PSTR("led_off"), x, y);
|
||||
led_set(x, y, false);
|
||||
void Max7219::led_off(const uint8_t x, const uint8_t y, uint8_t * const rcm/*=nullptr*/) {
|
||||
if (x >= MAX7219_X_LEDS || y >= MAX7219_Y_LEDS) return error(F("led_off"), x, y);
|
||||
led_set(x, y, false, rcm);
|
||||
}
|
||||
|
||||
void Max7219::led_toggle(const uint8_t x, const uint8_t y) {
|
||||
if (x >= MAX7219_X_LEDS || y >= MAX7219_Y_LEDS) return error(PSTR("led_toggle"), x, y);
|
||||
led_set(x, y, !BIT_7219(x, y));
|
||||
void Max7219::led_toggle(const uint8_t x, const uint8_t y, uint8_t * const rcm/*=nullptr*/) {
|
||||
if (x >= MAX7219_X_LEDS || y >= MAX7219_Y_LEDS) return error(F("led_toggle"), x, y);
|
||||
led_set(x, y, !BIT_7219(x, y), rcm);
|
||||
}
|
||||
|
||||
void Max7219::send_row(const uint8_t row) {
|
||||
@@ -328,13 +337,13 @@ void Max7219::fill() {
|
||||
}
|
||||
|
||||
void Max7219::clear_row(const uint8_t row) {
|
||||
if (row >= MAX7219_Y_LEDS) return error(PSTR("clear_row"), row);
|
||||
if (row >= MAX7219_Y_LEDS) return error(F("clear_row"), row);
|
||||
LOOP_L_N(x, MAX7219_X_LEDS) CLR_7219(x, row);
|
||||
send_row(row);
|
||||
}
|
||||
|
||||
void Max7219::clear_column(const uint8_t col) {
|
||||
if (col >= MAX7219_X_LEDS) return error(PSTR("set_column"), col);
|
||||
if (col >= MAX7219_X_LEDS) return error(F("set_column"), col);
|
||||
LOOP_L_N(y, MAX7219_Y_LEDS) CLR_7219(col, y);
|
||||
send_column(col);
|
||||
}
|
||||
@@ -345,7 +354,7 @@ void Max7219::clear_column(const uint8_t col) {
|
||||
* once with a single call to the function (if rotated 90° or 270°).
|
||||
*/
|
||||
void Max7219::set_row(const uint8_t row, const uint32_t val) {
|
||||
if (row >= MAX7219_Y_LEDS) return error(PSTR("set_row"), row);
|
||||
if (row >= MAX7219_Y_LEDS) return error(F("set_row"), row);
|
||||
uint32_t mask = _BV32(MAX7219_X_LEDS - 1);
|
||||
LOOP_L_N(x, MAX7219_X_LEDS) {
|
||||
if (val & mask) SET_7219(x, row); else CLR_7219(x, row);
|
||||
@@ -360,7 +369,7 @@ void Max7219::set_row(const uint8_t row, const uint32_t val) {
|
||||
* once with a single call to the function (if rotated 0° or 180°).
|
||||
*/
|
||||
void Max7219::set_column(const uint8_t col, const uint32_t val) {
|
||||
if (col >= MAX7219_X_LEDS) return error(PSTR("set_column"), col);
|
||||
if (col >= MAX7219_X_LEDS) return error(F("set_column"), col);
|
||||
uint32_t mask = _BV32(MAX7219_Y_LEDS - 1);
|
||||
LOOP_L_N(y, MAX7219_Y_LEDS) {
|
||||
if (val & mask) SET_7219(col, y); else CLR_7219(col, y);
|
||||
@@ -371,56 +380,56 @@ void Max7219::set_column(const uint8_t col, const uint32_t val) {
|
||||
|
||||
void Max7219::set_rows_16bits(const uint8_t y, uint32_t val) {
|
||||
#if MAX7219_X_LEDS == 8
|
||||
if (y > MAX7219_Y_LEDS - 2) return error(PSTR("set_rows_16bits"), y, val);
|
||||
if (y > MAX7219_Y_LEDS - 2) return error(F("set_rows_16bits"), y, val);
|
||||
set_row(y + 1, val); val >>= 8;
|
||||
set_row(y + 0, val);
|
||||
#else // at least 16 bits on each row
|
||||
if (y > MAX7219_Y_LEDS - 1) return error(PSTR("set_rows_16bits"), y, val);
|
||||
if (y > MAX7219_Y_LEDS - 1) return error(F("set_rows_16bits"), y, val);
|
||||
set_row(y, val);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Max7219::set_rows_32bits(const uint8_t y, uint32_t val) {
|
||||
#if MAX7219_X_LEDS == 8
|
||||
if (y > MAX7219_Y_LEDS - 4) return error(PSTR("set_rows_32bits"), y, val);
|
||||
if (y > MAX7219_Y_LEDS - 4) return error(F("set_rows_32bits"), y, val);
|
||||
set_row(y + 3, val); val >>= 8;
|
||||
set_row(y + 2, val); val >>= 8;
|
||||
set_row(y + 1, val); val >>= 8;
|
||||
set_row(y + 0, val);
|
||||
#elif MAX7219_X_LEDS == 16
|
||||
if (y > MAX7219_Y_LEDS - 2) return error(PSTR("set_rows_32bits"), y, val);
|
||||
if (y > MAX7219_Y_LEDS - 2) return error(F("set_rows_32bits"), y, val);
|
||||
set_row(y + 1, val); val >>= 16;
|
||||
set_row(y + 0, val);
|
||||
#else // at least 24 bits on each row. In the 3 matrix case, just display the low 24 bits
|
||||
if (y > MAX7219_Y_LEDS - 1) return error(PSTR("set_rows_32bits"), y, val);
|
||||
if (y > MAX7219_Y_LEDS - 1) return error(F("set_rows_32bits"), y, val);
|
||||
set_row(y, val);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Max7219::set_columns_16bits(const uint8_t x, uint32_t val) {
|
||||
#if MAX7219_Y_LEDS == 8
|
||||
if (x > MAX7219_X_LEDS - 2) return error(PSTR("set_columns_16bits"), x, val);
|
||||
if (x > MAX7219_X_LEDS - 2) return error(F("set_columns_16bits"), x, val);
|
||||
set_column(x + 0, val); val >>= 8;
|
||||
set_column(x + 1, val);
|
||||
#else // at least 16 bits in each column
|
||||
if (x > MAX7219_X_LEDS - 1) return error(PSTR("set_columns_16bits"), x, val);
|
||||
if (x > MAX7219_X_LEDS - 1) return error(F("set_columns_16bits"), x, val);
|
||||
set_column(x, val);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Max7219::set_columns_32bits(const uint8_t x, uint32_t val) {
|
||||
#if MAX7219_Y_LEDS == 8
|
||||
if (x > MAX7219_X_LEDS - 4) return error(PSTR("set_rows_32bits"), x, val);
|
||||
if (x > MAX7219_X_LEDS - 4) return error(F("set_rows_32bits"), x, val);
|
||||
set_column(x + 3, val); val >>= 8;
|
||||
set_column(x + 2, val); val >>= 8;
|
||||
set_column(x + 1, val); val >>= 8;
|
||||
set_column(x + 0, val);
|
||||
#elif MAX7219_Y_LEDS == 16
|
||||
if (x > MAX7219_X_LEDS - 2) return error(PSTR("set_rows_32bits"), x, val);
|
||||
if (x > MAX7219_X_LEDS - 2) return error(F("set_rows_32bits"), x, val);
|
||||
set_column(x + 1, val); val >>= 16;
|
||||
set_column(x + 0, val);
|
||||
#else // at least 24 bits on each row. In the 3 matrix case, just display the low 24 bits
|
||||
if (x > MAX7219_X_LEDS - 1) return error(PSTR("set_rows_32bits"), x, val);
|
||||
if (x > MAX7219_X_LEDS - 1) return error(F("set_rows_32bits"), x, val);
|
||||
set_column(x, val);
|
||||
#endif
|
||||
}
|
||||
@@ -449,7 +458,7 @@ void Max7219::register_setup() {
|
||||
pulse_load(); // Tell the chips to load the clocked out data
|
||||
}
|
||||
|
||||
#ifdef MAX7219_INIT_TEST
|
||||
#if MAX7219_INIT_TEST
|
||||
|
||||
uint8_t test_mode = 0;
|
||||
millis_t next_patt_ms;
|
||||
@@ -537,13 +546,9 @@ void Max7219::init() {
|
||||
|
||||
register_setup();
|
||||
|
||||
LOOP_LE_N(i, 7) { // Empty registers to turn all LEDs off
|
||||
led_line[i] = 0x00;
|
||||
send(max7219_reg_digit0 + i, 0);
|
||||
pulse_load(); // Tell the chips to load the clocked out data
|
||||
}
|
||||
clear();
|
||||
|
||||
#ifdef MAX7219_INIT_TEST
|
||||
#if MAX7219_INIT_TEST
|
||||
start_test_pattern();
|
||||
#endif
|
||||
}
|
||||
@@ -555,41 +560,55 @@ void Max7219::init() {
|
||||
*/
|
||||
|
||||
// Apply changes to update a marker
|
||||
void Max7219::mark16(const uint8_t pos, const uint8_t v1, const uint8_t v2) {
|
||||
void Max7219::mark16(const uint8_t pos, const uint8_t v1, const uint8_t v2, uint8_t * const rcm/*=nullptr*/) {
|
||||
#if MAX7219_X_LEDS > 8 // At least 16 LEDs on the X-Axis. Use single line.
|
||||
led_off(v1 & 0xF, pos);
|
||||
led_on(v2 & 0xF, pos);
|
||||
led_off(v1 & 0xF, pos, rcm);
|
||||
led_on(v2 & 0xF, pos, rcm);
|
||||
#elif MAX7219_Y_LEDS > 8 // At least 16 LEDs on the Y-Axis. Use a single column.
|
||||
led_off(pos, v1 & 0xF);
|
||||
led_on(pos, v2 & 0xF);
|
||||
led_off(pos, v1 & 0xF, rcm);
|
||||
led_on(pos, v2 & 0xF, rcm);
|
||||
#else // Single 8x8 LED matrix. Use two lines to get 16 LEDs.
|
||||
led_off(v1 & 0x7, pos + (v1 >= 8));
|
||||
led_on(v2 & 0x7, pos + (v2 >= 8));
|
||||
led_off(v1 & 0x7, pos + (v1 >= 8), rcm);
|
||||
led_on(v2 & 0x7, pos + (v2 >= 8), rcm);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Apply changes to update a tail-to-head range
|
||||
void Max7219::range16(const uint8_t y, const uint8_t ot, const uint8_t nt, const uint8_t oh, const uint8_t nh) {
|
||||
void Max7219::range16(const uint8_t y, const uint8_t ot, const uint8_t nt, const uint8_t oh,
|
||||
const uint8_t nh, uint8_t * const rcm/*=nullptr*/) {
|
||||
#if MAX7219_X_LEDS > 8 // At least 16 LEDs on the X-Axis. Use single line.
|
||||
if (ot != nt) for (uint8_t n = ot & 0xF; n != (nt & 0xF) && n != (nh & 0xF); n = (n + 1) & 0xF)
|
||||
led_off(n & 0xF, y);
|
||||
led_off(n & 0xF, y, rcm);
|
||||
if (oh != nh) for (uint8_t n = (oh + 1) & 0xF; n != ((nh + 1) & 0xF); n = (n + 1) & 0xF)
|
||||
led_on(n & 0xF, y);
|
||||
led_on(n & 0xF, y, rcm);
|
||||
#elif MAX7219_Y_LEDS > 8 // At least 16 LEDs on the Y-Axis. Use a single column.
|
||||
if (ot != nt) for (uint8_t n = ot & 0xF; n != (nt & 0xF) && n != (nh & 0xF); n = (n + 1) & 0xF)
|
||||
led_off(y, n & 0xF);
|
||||
led_off(y, n & 0xF, rcm);
|
||||
if (oh != nh) for (uint8_t n = (oh + 1) & 0xF; n != ((nh + 1) & 0xF); n = (n + 1) & 0xF)
|
||||
led_on(y, n & 0xF);
|
||||
led_on(y, n & 0xF, rcm);
|
||||
#else // Single 8x8 LED matrix. Use two lines to get 16 LEDs.
|
||||
if (ot != nt) for (uint8_t n = ot & 0xF; n != (nt & 0xF) && n != (nh & 0xF); n = (n + 1) & 0xF)
|
||||
led_off(n & 0x7, y + (n >= 8));
|
||||
led_off(n & 0x7, y + (n >= 8), rcm);
|
||||
if (oh != nh) for (uint8_t n = (oh + 1) & 0xF; n != ((nh + 1) & 0xF); n = (n + 1) & 0xF)
|
||||
led_on(n & 0x7, y + (n >= 8));
|
||||
led_on(n & 0x7, y + (n >= 8), rcm);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Apply changes to update a quantity
|
||||
void Max7219::quantity16(const uint8_t pos, const uint8_t ov, const uint8_t nv) {
|
||||
void Max7219::quantity(const uint8_t pos, const uint8_t ov, const uint8_t nv, uint8_t * const rcm/*=nullptr*/) {
|
||||
for (uint8_t i = _MIN(nv, ov); i < _MAX(nv, ov); i++)
|
||||
led_set(
|
||||
#if MAX7219_X_LEDS >= MAX7219_Y_LEDS
|
||||
i, pos // Single matrix or multiple matrices in Landscape
|
||||
#else
|
||||
pos, i // Multiple matrices in Portrait
|
||||
#endif
|
||||
, nv >= ov
|
||||
, rcm
|
||||
);
|
||||
}
|
||||
|
||||
void Max7219::quantity16(const uint8_t pos, const uint8_t ov, const uint8_t nv, uint8_t * const rcm/*=nullptr*/) {
|
||||
for (uint8_t i = _MIN(nv, ov); i < _MAX(nv, ov); i++)
|
||||
led_set(
|
||||
#if MAX7219_X_LEDS > 8 // At least 16 LEDs on the X-Axis. Use single line.
|
||||
@@ -600,6 +619,7 @@ void Max7219::quantity16(const uint8_t pos, const uint8_t ov, const uint8_t nv)
|
||||
i >> 1, pos + (i & 1)
|
||||
#endif
|
||||
, nv >= ov
|
||||
, rcm
|
||||
);
|
||||
}
|
||||
|
||||
@@ -637,16 +657,20 @@ void Max7219::idle_tasks() {
|
||||
register_setup();
|
||||
}
|
||||
|
||||
#ifdef MAX7219_INIT_TEST
|
||||
#if MAX7219_INIT_TEST
|
||||
if (test_mode) {
|
||||
run_test_pattern();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// suspend updates and record which lines have changed for batching later
|
||||
suspended++;
|
||||
uint8_t row_change_mask = 0x00;
|
||||
|
||||
#if ENABLED(MAX7219_DEBUG_PRINTER_ALIVE)
|
||||
if (do_blink) {
|
||||
led_toggle(MAX7219_X_LEDS - 1, MAX7219_Y_LEDS - 1);
|
||||
led_toggle(MAX7219_X_LEDS - 1, MAX7219_Y_LEDS - 1, &row_change_mask);
|
||||
next_blink = ms + 1000;
|
||||
}
|
||||
#endif
|
||||
@@ -656,7 +680,7 @@ void Max7219::idle_tasks() {
|
||||
static int16_t last_head_cnt = 0xF, last_tail_cnt = 0xF;
|
||||
|
||||
if (last_head_cnt != head || last_tail_cnt != tail) {
|
||||
range16(MAX7219_DEBUG_PLANNER_HEAD, last_tail_cnt, tail, last_head_cnt, head);
|
||||
range16(MAX7219_DEBUG_PLANNER_HEAD, last_tail_cnt, tail, last_head_cnt, head, &row_change_mask);
|
||||
last_head_cnt = head;
|
||||
last_tail_cnt = tail;
|
||||
}
|
||||
@@ -666,7 +690,7 @@ void Max7219::idle_tasks() {
|
||||
#ifdef MAX7219_DEBUG_PLANNER_HEAD
|
||||
static int16_t last_head_cnt = 0x1;
|
||||
if (last_head_cnt != head) {
|
||||
mark16(MAX7219_DEBUG_PLANNER_HEAD, last_head_cnt, head);
|
||||
mark16(MAX7219_DEBUG_PLANNER_HEAD, last_head_cnt, head, &row_change_mask);
|
||||
last_head_cnt = head;
|
||||
}
|
||||
#endif
|
||||
@@ -674,7 +698,7 @@ void Max7219::idle_tasks() {
|
||||
#ifdef MAX7219_DEBUG_PLANNER_TAIL
|
||||
static int16_t last_tail_cnt = 0x1;
|
||||
if (last_tail_cnt != tail) {
|
||||
mark16(MAX7219_DEBUG_PLANNER_TAIL, last_tail_cnt, tail);
|
||||
mark16(MAX7219_DEBUG_PLANNER_TAIL, last_tail_cnt, tail, &row_change_mask);
|
||||
last_tail_cnt = tail;
|
||||
}
|
||||
#endif
|
||||
@@ -685,11 +709,26 @@ void Max7219::idle_tasks() {
|
||||
static int16_t last_depth = 0;
|
||||
const int16_t current_depth = (head - tail + BLOCK_BUFFER_SIZE) & (BLOCK_BUFFER_SIZE - 1) & 0xF;
|
||||
if (current_depth != last_depth) {
|
||||
quantity16(MAX7219_DEBUG_PLANNER_QUEUE, last_depth, current_depth);
|
||||
quantity16(MAX7219_DEBUG_PLANNER_QUEUE, last_depth, current_depth, &row_change_mask);
|
||||
last_depth = current_depth;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MAX7219_DEBUG_PROFILE
|
||||
static uint8_t last_time_fraction = 0;
|
||||
const uint8_t current_time_fraction = (uint16_t(CodeProfiler::get_time_fraction()) * MAX7219_NUMBER_UNITS + 8) / 16;
|
||||
if (current_time_fraction != last_time_fraction) {
|
||||
quantity(MAX7219_DEBUG_PROFILE, last_time_fraction, current_time_fraction, &row_change_mask);
|
||||
last_time_fraction = current_time_fraction;
|
||||
}
|
||||
#endif
|
||||
|
||||
// batch line updates
|
||||
suspended--;
|
||||
if (!suspended)
|
||||
LOOP_L_N(i, 8) if (row_change_mask & _BV(i))
|
||||
refresh_line(i);
|
||||
|
||||
// After resume() automatically do a refresh()
|
||||
if (suspended == 0x80) {
|
||||
suspended = 0;
|
||||
|
@@ -42,10 +42,11 @@
|
||||
* a Max7219_Set_Row(). The opposite is true for rotations of 0 or 180 degrees.
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#ifndef MAX7219_ROTATE
|
||||
#define MAX7219_ROTATE 0
|
||||
#endif
|
||||
#define _ROT ((MAX7219_ROTATE + 360) % 360)
|
||||
|
||||
#ifndef MAX7219_NUMBER_UNITS
|
||||
#define MAX7219_NUMBER_UNITS 1
|
||||
@@ -71,6 +72,67 @@
|
||||
#define max7219_reg_shutdown 0x0C
|
||||
#define max7219_reg_displayTest 0x0F
|
||||
|
||||
#ifdef MAX7219_DEBUG_PROFILE
|
||||
// This class sums up the amount of time for which its instances exist.
|
||||
// By default there is one instantiated for the duration of the idle()
|
||||
// function. But an instance can be created in any code block to measure
|
||||
// the time spent from the point of instantiation until the CPU leaves
|
||||
// block. Be careful about having multiple instances of CodeProfiler as
|
||||
// it does not guard against double counting. In general mixing ISR and
|
||||
// non-ISR use will require critical sections but note that mode setting
|
||||
// is atomic so the total or average times can safely be read if you set
|
||||
// mode to FREEZE first.
|
||||
class CodeProfiler {
|
||||
public:
|
||||
enum Mode : uint8_t { ACCUMULATE_AVERAGE, ACCUMULATE_TOTAL, FREEZE };
|
||||
|
||||
private:
|
||||
static Mode mode;
|
||||
static uint8_t instance_count;
|
||||
static uint32_t last_calc_time;
|
||||
static uint32_t total_time;
|
||||
static uint8_t time_fraction;
|
||||
static uint16_t call_count;
|
||||
|
||||
uint32_t start_time;
|
||||
|
||||
public:
|
||||
CodeProfiler() : start_time(micros()) { instance_count++; }
|
||||
~CodeProfiler() {
|
||||
instance_count--;
|
||||
if (mode == FREEZE) return;
|
||||
|
||||
call_count++;
|
||||
|
||||
const uint32_t now = micros();
|
||||
total_time += now - start_time;
|
||||
|
||||
if (mode == ACCUMULATE_TOTAL) return;
|
||||
|
||||
// update time_fraction every hundred milliseconds
|
||||
if (instance_count == 0 && ELAPSED(now, last_calc_time + 100000)) {
|
||||
time_fraction = total_time * 128 / (now - last_calc_time);
|
||||
last_calc_time = now;
|
||||
total_time = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void set_mode(Mode _mode) { mode = _mode; }
|
||||
static void reset() {
|
||||
time_fraction = 0;
|
||||
last_calc_time = micros();
|
||||
total_time = 0;
|
||||
call_count = 0;
|
||||
}
|
||||
// returns fraction of total time which was measured, scaled from 0 to 128
|
||||
static uint8_t get_time_fraction() { return time_fraction; }
|
||||
// returns total time in microseconds
|
||||
static uint32_t get_total_time() { return total_time; }
|
||||
|
||||
static uint16_t get_call_count() { return call_count; }
|
||||
};
|
||||
#endif
|
||||
|
||||
class Max7219 {
|
||||
public:
|
||||
static uint8_t led_line[MAX7219_LINES];
|
||||
@@ -86,13 +148,13 @@ public:
|
||||
static void send(const uint8_t reg, const uint8_t data);
|
||||
|
||||
// Refresh all units
|
||||
static inline void refresh() { for (uint8_t i = 0; i < 8; i++) refresh_line(i); }
|
||||
static void refresh() { for (uint8_t i = 0; i < 8; i++) refresh_line(i); }
|
||||
|
||||
// Suspend / resume updates to the LED unit
|
||||
// Use these methods to speed up multiple changes
|
||||
// or to apply updates from interrupt context.
|
||||
static inline void suspend() { suspended++; }
|
||||
static inline void resume() { suspended--; suspended |= 0x80; }
|
||||
static void suspend() { suspended++; }
|
||||
static void resume() { suspended--; suspended |= 0x80; }
|
||||
|
||||
// Update a single native line on all units
|
||||
static void refresh_line(const uint8_t line);
|
||||
@@ -108,10 +170,10 @@ public:
|
||||
#endif
|
||||
|
||||
// Set a single LED by XY coordinate
|
||||
static void led_set(const uint8_t x, const uint8_t y, const bool on);
|
||||
static void led_on(const uint8_t x, const uint8_t y);
|
||||
static void led_off(const uint8_t x, const uint8_t y);
|
||||
static void led_toggle(const uint8_t x, const uint8_t y);
|
||||
static void led_set(const uint8_t x, const uint8_t y, const bool on, uint8_t * const rcm=nullptr);
|
||||
static void led_on(const uint8_t x, const uint8_t y, uint8_t * const rcm=nullptr);
|
||||
static void led_off(const uint8_t x, const uint8_t y, uint8_t * const rcm=nullptr);
|
||||
static void led_toggle(const uint8_t x, const uint8_t y, uint8_t * const rcm=nullptr);
|
||||
|
||||
// Set all LEDs in a single column
|
||||
static void set_column(const uint8_t col, const uint32_t val);
|
||||
@@ -140,16 +202,17 @@ public:
|
||||
|
||||
private:
|
||||
static uint8_t suspended;
|
||||
static void error(const char * const func, const int32_t v1, const int32_t v2=-1);
|
||||
static void error(FSTR_P const func, const int32_t v1, const int32_t v2=-1);
|
||||
static void noop();
|
||||
static void set(const uint8_t line, const uint8_t bits);
|
||||
static void send_row(const uint8_t row);
|
||||
static void send_column(const uint8_t col);
|
||||
static void mark16(const uint8_t y, const uint8_t v1, const uint8_t v2);
|
||||
static void range16(const uint8_t y, const uint8_t ot, const uint8_t nt, const uint8_t oh, const uint8_t nh);
|
||||
static void quantity16(const uint8_t y, const uint8_t ov, const uint8_t nv);
|
||||
static void mark16(const uint8_t y, const uint8_t v1, const uint8_t v2, uint8_t * const rcm=nullptr);
|
||||
static void range16(const uint8_t y, const uint8_t ot, const uint8_t nt, const uint8_t oh, const uint8_t nh, uint8_t * const rcm=nullptr);
|
||||
static void quantity(const uint8_t y, const uint8_t ov, const uint8_t nv, uint8_t * const rcm=nullptr);
|
||||
static void quantity16(const uint8_t y, const uint8_t ov, const uint8_t nv, uint8_t * const rcm=nullptr);
|
||||
|
||||
#ifdef MAX7219_INIT_TEST
|
||||
#if MAX7219_INIT_TEST
|
||||
static void test_pattern();
|
||||
static void run_test_pattern();
|
||||
static void start_test_pattern();
|
||||
|
@@ -26,7 +26,7 @@
|
||||
* Algorithm & Implementation: Scott Mudge - mail@scottmudge.com
|
||||
* Date: Dec. 2020
|
||||
*
|
||||
* Character Frequencies from ~30 MB of comment-stripped gcode:
|
||||
* Character Frequencies from ~30 MB of comment-stripped G-code:
|
||||
* '1' -> 4451136 '4' -> 1353273 '\n' -> 1087683 '-' -> 90242
|
||||
* '0' -> 4253577 '9' -> 1352147 'G' -> 1075806 'Z' -> 34109
|
||||
* ' ' -> 3053297 '3' -> 1262929 'X' -> 975742 'M' -> 11879
|
||||
@@ -169,10 +169,9 @@ void MeatPack::handle_command(const MeatPack_Command c) {
|
||||
void MeatPack::report_state() {
|
||||
// NOTE: if any configuration vars are added below, the outgoing sync text for host plugin
|
||||
// should not contain the "PV' substring, as this is used to indicate protocol version
|
||||
SERIAL_ECHOPGM("[MP] ");
|
||||
SERIAL_ECHOPGM(MeatPack_ProtocolVersion " ");
|
||||
SERIAL_ECHOPGM("[MP] " MeatPack_ProtocolVersion " ");
|
||||
serialprint_onoff(TEST(state, MPConfig_Bit_Active));
|
||||
SERIAL_ECHOPGM_P(TEST(state, MPConfig_Bit_NoSpaces) ? PSTR(" NSP\n") : PSTR(" ESP\n"));
|
||||
SERIAL_ECHOF(TEST(state, MPConfig_Bit_NoSpaces) ? F(" NSP\n") : F(" ESP\n"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -29,7 +29,7 @@
|
||||
* Specifically optimized for 3D printing G-Code, this is a zero-cost data compression method
|
||||
* which packs ~180-190% more data into the same amount of bytes going to the CNC controller.
|
||||
* As a majority of G-Code can be represented by a restricted alphabet, I performed histogram
|
||||
* analysis on a wide variety of 3D printing gcode samples, and found ~93% of all gcode could
|
||||
* analysis on a wide variety of 3D printing G-code samples, and found ~93% of all G-code could
|
||||
* be represented by the same 15-character alphabet.
|
||||
*
|
||||
* This allowed me to design a system of packing 2 8-bit characters into a single byte, assuming
|
||||
@@ -38,7 +38,7 @@
|
||||
*
|
||||
* Combined with some logic to allow commingling of full-width characters outside of this 15-
|
||||
* character alphabet (at the cost of an extra 8-bits per full-width character), and by stripping
|
||||
* out unnecessary comments, the end result is gcode which is roughly half the original size.
|
||||
* out unnecessary comments, the end result is G-code which is roughly half the original size.
|
||||
*
|
||||
* Why did I do this? I noticed micro-stuttering and other data-bottleneck issues while printing
|
||||
* objects with high curvature, especially at high speeds. There is also the issue of the limited
|
||||
|
@@ -63,7 +63,7 @@ void Mixer::normalize(const uint8_t tool_index) {
|
||||
#ifdef MIXER_NORMALIZER_DEBUG
|
||||
SERIAL_ECHOPGM("Mixer: Old relation : [ ");
|
||||
MIXER_STEPPER_LOOP(i) {
|
||||
SERIAL_ECHO_F(collector[i] / csum, 3);
|
||||
SERIAL_DECIMAL(collector[i] / csum);
|
||||
SERIAL_CHAR(' ');
|
||||
}
|
||||
SERIAL_ECHOLNPGM("]");
|
||||
|
@@ -126,7 +126,7 @@ class Mixer {
|
||||
|
||||
static mixer_perc_t mix[MIXING_STEPPERS]; // Scratch array for the Mix in proportion to 100
|
||||
|
||||
static inline void copy_mix_to_color(mixer_comp_t (&tcolor)[MIXING_STEPPERS]) {
|
||||
static void copy_mix_to_color(mixer_comp_t (&tcolor)[MIXING_STEPPERS]) {
|
||||
// Scale each component to the largest one in terms of COLOR_A_MASK
|
||||
// So the largest component will be COLOR_A_MASK and the other will be in proportion to it
|
||||
const float scale = (COLOR_A_MASK) * RECIPROCAL(_MAX(
|
||||
@@ -145,7 +145,7 @@ class Mixer {
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void update_mix_from_vtool(const uint8_t j=selected_vtool) {
|
||||
static void update_mix_from_vtool(const uint8_t j=selected_vtool) {
|
||||
float ctot = 0;
|
||||
MIXER_STEPPER_LOOP(i) ctot += color[j][i];
|
||||
//MIXER_STEPPER_LOOP(i) mix[i] = 100.0f * color[j][i] / ctot;
|
||||
@@ -165,7 +165,7 @@ class Mixer {
|
||||
#if HAS_DUAL_MIXING
|
||||
|
||||
// Update the virtual tool from an edited mix
|
||||
static inline void update_vtool_from_mix() {
|
||||
static void update_vtool_from_mix() {
|
||||
copy_mix_to_color(color[selected_vtool]);
|
||||
TERN_(GRADIENT_MIX, refresh_gradient());
|
||||
// MIXER_STEPPER_LOOP(i) collector[i] = mix[i];
|
||||
@@ -182,7 +182,7 @@ class Mixer {
|
||||
// Update the current mix from the gradient for a given Z
|
||||
static void update_gradient_for_z(const_float_t z);
|
||||
static void update_gradient_for_planner_z();
|
||||
static inline void gradient_control(const_float_t z) {
|
||||
static void gradient_control(const_float_t z) {
|
||||
if (gradient.enabled) {
|
||||
if (z >= gradient.end_z)
|
||||
T(gradient.end_vtool);
|
||||
@@ -191,7 +191,7 @@ class Mixer {
|
||||
}
|
||||
}
|
||||
|
||||
static inline void update_mix_from_gradient() {
|
||||
static void update_mix_from_gradient() {
|
||||
float ctot = 0;
|
||||
MIXER_STEPPER_LOOP(i) ctot += gradient.color[i];
|
||||
MIXER_STEPPER_LOOP(i) mix[i] = (mixer_perc_t)CEIL(100.0f * gradient.color[i] / ctot);
|
||||
|
@@ -24,9 +24,9 @@
|
||||
|
||||
#if HAS_PRUSA_MMU1
|
||||
|
||||
#include "../MarlinCore.h"
|
||||
#include "../module/planner.h"
|
||||
#include "../module/stepper.h"
|
||||
#include "../../MarlinCore.h"
|
||||
#include "../../module/planner.h"
|
||||
#include "../../module/stepper.h"
|
||||
|
||||
void mmu_init() {
|
||||
SET_OUTPUT(E_MUX0_PIN);
|
@@ -51,7 +51,7 @@ When done, the MMU sends
|
||||
|
||||
- MMU => 'ok\n'
|
||||
|
||||
We don't wait for a response here but immediately continue with the next gcode which should
|
||||
We don't wait for a response here but immediately continue with the next G-code which should
|
||||
be one or more extruder moves to feed the filament into the hotend.
|
||||
|
||||
|
@@ -54,7 +54,7 @@ MMU2 mmu2;
|
||||
#define MMU_CMD_TIMEOUT 45000UL // 45s timeout for mmu commands (except P0)
|
||||
#define MMU_P0_TIMEOUT 3000UL // Timeout for P0 command: 3seconds
|
||||
|
||||
#define MMU2_COMMAND(S) tx_str_P(PSTR(S "\n"))
|
||||
#define MMU2_COMMAND(S) tx_str(F(S "\n"))
|
||||
|
||||
#if ENABLED(MMU_EXTRUDER_SENSOR)
|
||||
uint8_t mmu_idl_sens = 0;
|
||||
@@ -143,6 +143,11 @@ uint8_t MMU2::get_current_tool() {
|
||||
#define FILAMENT_PRESENT() (READ(FIL_RUNOUT1_PIN) != FIL_RUNOUT1_STATE)
|
||||
#endif
|
||||
|
||||
void mmu2_attn_buzz(const bool two=false) {
|
||||
BUZZ(200, 404);
|
||||
if (two) { BUZZ(10, 0); BUZZ(200, 404); }
|
||||
}
|
||||
|
||||
void MMU2::mmu_loop() {
|
||||
|
||||
switch (state) {
|
||||
@@ -229,17 +234,17 @@ void MMU2::mmu_loop() {
|
||||
if (cmd) {
|
||||
if (WITHIN(cmd, MMU_CMD_T0, MMU_CMD_T0 + EXTRUDERS - 1)) {
|
||||
// tool change
|
||||
int filament = cmd - MMU_CMD_T0;
|
||||
const int filament = cmd - MMU_CMD_T0;
|
||||
DEBUG_ECHOLNPGM("MMU <= T", filament);
|
||||
tx_printf_P(PSTR("T%d\n"), filament);
|
||||
tx_printf(F("T%d\n"), filament);
|
||||
TERN_(MMU_EXTRUDER_SENSOR, mmu_idl_sens = 1); // enable idler sensor, if any
|
||||
state = 3; // wait for response
|
||||
}
|
||||
else if (WITHIN(cmd, MMU_CMD_L0, MMU_CMD_L0 + EXTRUDERS - 1)) {
|
||||
// load
|
||||
int filament = cmd - MMU_CMD_L0;
|
||||
const int filament = cmd - MMU_CMD_L0;
|
||||
DEBUG_ECHOLNPGM("MMU <= L", filament);
|
||||
tx_printf_P(PSTR("L%d\n"), filament);
|
||||
tx_printf(F("L%d\n"), filament);
|
||||
state = 3; // wait for response
|
||||
}
|
||||
else if (cmd == MMU_CMD_C0) {
|
||||
@@ -257,9 +262,9 @@ void MMU2::mmu_loop() {
|
||||
}
|
||||
else if (WITHIN(cmd, MMU_CMD_E0, MMU_CMD_E0 + EXTRUDERS - 1)) {
|
||||
// eject filament
|
||||
int filament = cmd - MMU_CMD_E0;
|
||||
const int filament = cmd - MMU_CMD_E0;
|
||||
DEBUG_ECHOLNPGM("MMU <= E", filament);
|
||||
tx_printf_P(PSTR("E%d\n"), filament);
|
||||
tx_printf(F("E%d\n"), filament);
|
||||
state = 3; // wait for response
|
||||
}
|
||||
else if (cmd == MMU_CMD_R0) {
|
||||
@@ -270,9 +275,9 @@ void MMU2::mmu_loop() {
|
||||
}
|
||||
else if (WITHIN(cmd, MMU_CMD_F0, MMU_CMD_F0 + EXTRUDERS - 1)) {
|
||||
// filament type
|
||||
int filament = cmd - MMU_CMD_F0;
|
||||
const int filament = cmd - MMU_CMD_F0;
|
||||
DEBUG_ECHOLNPGM("MMU <= F", filament, " ", cmd_arg);
|
||||
tx_printf_P(PSTR("F%d %d\n"), filament, cmd_arg);
|
||||
tx_printf(F("F%d %d\n"), filament, cmd_arg);
|
||||
state = 3; // wait for response
|
||||
}
|
||||
|
||||
@@ -356,13 +361,15 @@ void MMU2::mmu_loop() {
|
||||
*/
|
||||
bool MMU2::rx_start() {
|
||||
// check for start message
|
||||
return rx_str_P(PSTR("start\n"));
|
||||
return rx_str(F("start\n"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the data received ends with the given string.
|
||||
*/
|
||||
bool MMU2::rx_str_P(const char *str) {
|
||||
bool MMU2::rx_str(FSTR_P fstr) {
|
||||
PGM_P pstr = FTOP(fstr);
|
||||
|
||||
uint8_t i = strlen(rx_buffer);
|
||||
|
||||
while (MMU2_SERIAL.available()) {
|
||||
@@ -375,14 +382,14 @@ bool MMU2::rx_str_P(const char *str) {
|
||||
}
|
||||
rx_buffer[i] = '\0';
|
||||
|
||||
uint8_t len = strlen_P(str);
|
||||
uint8_t len = strlen_P(pstr);
|
||||
|
||||
if (i < len) return false;
|
||||
|
||||
str += len;
|
||||
pstr += len;
|
||||
|
||||
while (len--) {
|
||||
char c0 = pgm_read_byte(str--), c1 = rx_buffer[i--];
|
||||
char c0 = pgm_read_byte(pstr--), c1 = rx_buffer[i--];
|
||||
if (c0 == c1) continue;
|
||||
if (c0 == '\r' && c1 == '\n') continue; // match cr as lf
|
||||
if (c0 == '\n' && c1 == '\r') continue; // match lf as cr
|
||||
@@ -394,19 +401,19 @@ bool MMU2::rx_str_P(const char *str) {
|
||||
/**
|
||||
* Transfer data to MMU, no argument
|
||||
*/
|
||||
void MMU2::tx_str_P(const char *str) {
|
||||
void MMU2::tx_str(FSTR_P fstr) {
|
||||
clear_rx_buffer();
|
||||
uint8_t len = strlen_P(str);
|
||||
LOOP_L_N(i, len) MMU2_SERIAL.write(pgm_read_byte(str++));
|
||||
PGM_P pstr = FTOP(fstr);
|
||||
while (const char c = pgm_read_byte(pstr)) { MMU2_SERIAL.write(c); pstr++; }
|
||||
prev_request = millis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfer data to MMU, single argument
|
||||
*/
|
||||
void MMU2::tx_printf_P(const char *format, int argument = -1) {
|
||||
void MMU2::tx_printf(FSTR_P format, int argument = -1) {
|
||||
clear_rx_buffer();
|
||||
uint8_t len = sprintf_P(tx_buffer, format, argument);
|
||||
const uint8_t len = sprintf_P(tx_buffer, FTOP(format), argument);
|
||||
LOOP_L_N(i, len) MMU2_SERIAL.write(tx_buffer[i]);
|
||||
prev_request = millis();
|
||||
}
|
||||
@@ -414,9 +421,9 @@ void MMU2::tx_printf_P(const char *format, int argument = -1) {
|
||||
/**
|
||||
* Transfer data to MMU, two arguments
|
||||
*/
|
||||
void MMU2::tx_printf_P(const char *format, int argument1, int argument2) {
|
||||
void MMU2::tx_printf(FSTR_P format, int argument1, int argument2) {
|
||||
clear_rx_buffer();
|
||||
uint8_t len = sprintf_P(tx_buffer, format, argument1, argument2);
|
||||
const uint8_t len = sprintf_P(tx_buffer, FTOP(format), argument1, argument2);
|
||||
LOOP_L_N(i, len) MMU2_SERIAL.write(tx_buffer[i]);
|
||||
prev_request = millis();
|
||||
}
|
||||
@@ -433,7 +440,7 @@ void MMU2::clear_rx_buffer() {
|
||||
* Check if we received 'ok' from MMU
|
||||
*/
|
||||
bool MMU2::rx_ok() {
|
||||
if (rx_str_P(PSTR("ok\n"))) {
|
||||
if (rx_str(F("ok\n"))) {
|
||||
prev_P0_request = millis();
|
||||
return true;
|
||||
}
|
||||
@@ -446,12 +453,12 @@ bool MMU2::rx_ok() {
|
||||
void MMU2::check_version() {
|
||||
if (buildnr < MMU_REQUIRED_FW_BUILDNR) {
|
||||
SERIAL_ERROR_MSG("Invalid MMU2 firmware. Version >= " STRINGIFY(MMU_REQUIRED_FW_BUILDNR) " required.");
|
||||
kill(GET_TEXT(MSG_KILL_MMU2_FIRMWARE));
|
||||
kill(GET_TEXT_F(MSG_KILL_MMU2_FIRMWARE));
|
||||
}
|
||||
}
|
||||
|
||||
static void mmu2_not_responding() {
|
||||
LCD_MESSAGEPGM(MSG_MMU2_NOT_RESPONDING);
|
||||
LCD_MESSAGE(MSG_MMU2_NOT_RESPONDING);
|
||||
BUZZ(100, 659);
|
||||
BUZZ(200, 698);
|
||||
BUZZ(100, 659);
|
||||
@@ -487,7 +494,7 @@ static void mmu2_not_responding() {
|
||||
if (index != extruder) {
|
||||
|
||||
stepper.disable_extruder();
|
||||
ui.status_printf_P(0, GET_TEXT(MSG_MMU2_LOADING_FILAMENT), int(index + 1));
|
||||
ui.status_printf(0, GET_TEXT_F(MSG_MMU2_LOADING_FILAMENT), int(index + 1));
|
||||
|
||||
command(MMU_CMD_T0 + index);
|
||||
manage_response(true, true);
|
||||
@@ -523,7 +530,7 @@ static void mmu2_not_responding() {
|
||||
while (!thermalManager.wait_for_hotend(active_extruder, false)) safe_delay(100);
|
||||
load_filament_to_nozzle(index);
|
||||
#else
|
||||
BUZZ(400, 40);
|
||||
ERR_BUZZ();
|
||||
#endif
|
||||
} break;
|
||||
|
||||
@@ -542,7 +549,7 @@ static void mmu2_not_responding() {
|
||||
active_extruder = 0;
|
||||
}
|
||||
#else
|
||||
BUZZ(400, 40);
|
||||
ERR_BUZZ();
|
||||
#endif
|
||||
} break;
|
||||
|
||||
@@ -573,7 +580,7 @@ static void mmu2_not_responding() {
|
||||
command(MMU_CMD_U0);
|
||||
manage_response(true, true);
|
||||
}
|
||||
ui.status_printf_P(0, GET_TEXT(MSG_MMU2_LOADING_FILAMENT), int(index + 1));
|
||||
ui.status_printf(0, GET_TEXT_F(MSG_MMU2_LOADING_FILAMENT), int(index + 1));
|
||||
mmu_loading_flag = true;
|
||||
command(MMU_CMD_T0 + index);
|
||||
manage_response(true, true);
|
||||
@@ -611,7 +618,7 @@ static void mmu2_not_responding() {
|
||||
while (!thermalManager.wait_for_hotend(active_extruder, false)) safe_delay(100);
|
||||
load_filament_to_nozzle(index);
|
||||
#else
|
||||
BUZZ(400, 40);
|
||||
ERR_BUZZ();
|
||||
#endif
|
||||
} break;
|
||||
|
||||
@@ -631,7 +638,7 @@ static void mmu2_not_responding() {
|
||||
extruder = index;
|
||||
active_extruder = 0;
|
||||
#else
|
||||
BUZZ(400, 40);
|
||||
ERR_BUZZ();
|
||||
#endif
|
||||
} break;
|
||||
|
||||
@@ -671,7 +678,7 @@ static void mmu2_not_responding() {
|
||||
|
||||
if (index != extruder) {
|
||||
stepper.disable_extruder();
|
||||
ui.status_printf_P(0, GET_TEXT(MSG_MMU2_LOADING_FILAMENT), int(index + 1));
|
||||
ui.status_printf(0, GET_TEXT_F(MSG_MMU2_LOADING_FILAMENT), int(index + 1));
|
||||
command(MMU_CMD_T0 + index);
|
||||
manage_response(true, true);
|
||||
command(MMU_CMD_C0);
|
||||
@@ -705,7 +712,7 @@ static void mmu2_not_responding() {
|
||||
while (!thermalManager.wait_for_hotend(active_extruder, false)) safe_delay(100);
|
||||
load_filament_to_nozzle(index);
|
||||
#else
|
||||
BUZZ(400, 40);
|
||||
ERR_BUZZ();
|
||||
#endif
|
||||
} break;
|
||||
|
||||
@@ -724,7 +731,7 @@ static void mmu2_not_responding() {
|
||||
extruder = index;
|
||||
active_extruder = 0;
|
||||
#else
|
||||
BUZZ(400, 40);
|
||||
ERR_BUZZ();
|
||||
#endif
|
||||
} break;
|
||||
|
||||
@@ -808,26 +815,27 @@ void MMU2::manage_response(const bool move_axes, const bool turn_off_nozzle) {
|
||||
|
||||
if (turn_off_nozzle && resume_hotend_temp) {
|
||||
thermalManager.setTargetHotend(resume_hotend_temp, active_extruder);
|
||||
LCD_MESSAGEPGM(MSG_HEATING);
|
||||
BUZZ(200, 40);
|
||||
LCD_MESSAGE(MSG_HEATING);
|
||||
ERR_BUZZ();
|
||||
|
||||
while (!thermalManager.wait_for_hotend(active_extruder, false)) safe_delay(1000);
|
||||
}
|
||||
|
||||
if (move_axes && all_axes_homed()) {
|
||||
LCD_MESSAGEPGM(MSG_MMU2_RESUMING);
|
||||
BUZZ(198, 404); BUZZ(4, 0); BUZZ(198, 404);
|
||||
LCD_MESSAGE(MSG_MMU2_RESUMING);
|
||||
mmu2_attn_buzz(true);
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
||||
|
||||
if (move_axes && all_axes_homed()) {
|
||||
// Move XY to starting position, then Z
|
||||
do_blocking_move_to_xy(resume_position, feedRate_t(NOZZLE_PARK_XY_FEEDRATE));
|
||||
|
||||
// Move Z_AXIS to saved position
|
||||
do_blocking_move_to_z(resume_position.z, feedRate_t(NOZZLE_PARK_Z_FEEDRATE));
|
||||
}
|
||||
else {
|
||||
BUZZ(198, 404); BUZZ(4, 0); BUZZ(198, 404);
|
||||
LCD_MESSAGEPGM(MSG_MMU2_RESUMING);
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -842,7 +850,7 @@ void MMU2::set_filament_type(const uint8_t index, const uint8_t filamentType) {
|
||||
}
|
||||
|
||||
void MMU2::filament_runout() {
|
||||
queue.inject_P(PSTR(MMU2_FILAMENT_RUNOUT_SCRIPT));
|
||||
queue.inject(F(MMU2_FILAMENT_RUNOUT_SCRIPT));
|
||||
planner.synchronize();
|
||||
}
|
||||
|
||||
@@ -853,7 +861,7 @@ void MMU2::filament_runout() {
|
||||
if (cmd == MMU_CMD_NONE && last_cmd == MMU_CMD_C0) {
|
||||
if (present && !mmu2s_triggered) {
|
||||
DEBUG_ECHOLNPGM("MMU <= 'A'");
|
||||
tx_str_P(PSTR("A\n"));
|
||||
tx_str(F("A\n"));
|
||||
}
|
||||
// Slowly spin the extruder during C0
|
||||
else {
|
||||
@@ -896,7 +904,7 @@ void MMU2::load_filament(const uint8_t index) {
|
||||
|
||||
command(MMU_CMD_L0 + index);
|
||||
manage_response(false, false);
|
||||
BUZZ(200, 404);
|
||||
mmu2_attn_buzz();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -907,8 +915,8 @@ bool MMU2::load_filament_to_nozzle(const uint8_t index) {
|
||||
if (!_enabled) return false;
|
||||
|
||||
if (thermalManager.tooColdToExtrude(active_extruder)) {
|
||||
BUZZ(200, 404);
|
||||
LCD_ALERTMESSAGEPGM(MSG_HOTEND_TOO_COLD);
|
||||
mmu2_attn_buzz();
|
||||
LCD_ALERTMESSAGE(MSG_HOTEND_TOO_COLD);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -922,7 +930,7 @@ bool MMU2::load_filament_to_nozzle(const uint8_t index) {
|
||||
extruder = index;
|
||||
active_extruder = 0;
|
||||
load_to_nozzle();
|
||||
BUZZ(200, 404);
|
||||
mmu2_attn_buzz();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
@@ -931,7 +939,7 @@ bool MMU2::load_filament_to_nozzle(const uint8_t index) {
|
||||
* Load filament to nozzle of multimaterial printer
|
||||
*
|
||||
* This function is used only after T? (user select filament) and M600 (change filament).
|
||||
* It is not used after T0 .. T4 command (select filament), in such case, gcode is responsible for loading
|
||||
* It is not used after T0 .. T4 command (select filament), in such case, G-code is responsible for loading
|
||||
* filament to nozzle.
|
||||
*/
|
||||
void MMU2::load_to_nozzle() {
|
||||
@@ -943,12 +951,12 @@ bool MMU2::eject_filament(const uint8_t index, const bool recover) {
|
||||
if (!_enabled) return false;
|
||||
|
||||
if (thermalManager.tooColdToExtrude(active_extruder)) {
|
||||
BUZZ(200, 404);
|
||||
LCD_ALERTMESSAGEPGM(MSG_HOTEND_TOO_COLD);
|
||||
mmu2_attn_buzz();
|
||||
LCD_ALERTMESSAGE(MSG_HOTEND_TOO_COLD);
|
||||
return false;
|
||||
}
|
||||
|
||||
LCD_MESSAGEPGM(MSG_MMU2_EJECTING_FILAMENT);
|
||||
LCD_MESSAGE(MSG_MMU2_EJECTING_FILAMENT);
|
||||
|
||||
stepper.enable_extruder();
|
||||
current_position.e -= MMU2_FILAMENTCHANGE_EJECT_FEED;
|
||||
@@ -958,13 +966,12 @@ bool MMU2::eject_filament(const uint8_t index, const bool recover) {
|
||||
manage_response(false, false);
|
||||
|
||||
if (recover) {
|
||||
LCD_MESSAGEPGM(MSG_MMU2_EJECT_RECOVER);
|
||||
BUZZ(200, 404);
|
||||
TERN_(HOST_PROMPT_SUPPORT, host_prompt_do(PROMPT_USER_CONTINUE, PSTR("MMU2 Eject Recover"), CONTINUE_STR));
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired_P(PSTR("MMU2 Eject Recover")));
|
||||
LCD_MESSAGE(MSG_MMU2_EJECT_RECOVER);
|
||||
mmu2_attn_buzz();
|
||||
TERN_(HOST_PROMPT_SUPPORT, hostui.prompt_do(PROMPT_USER_CONTINUE, F("MMU2 Eject Recover"), FPSTR(CONTINUE_STR)));
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired(F("MMU2 Eject Recover")));
|
||||
TERN_(HAS_RESUME_CONTINUE, wait_for_user_response());
|
||||
BUZZ(200, 404);
|
||||
BUZZ(200, 404);
|
||||
mmu2_attn_buzz(true);
|
||||
|
||||
command(MMU_CMD_R0);
|
||||
manage_response(false, false);
|
||||
@@ -977,7 +984,7 @@ bool MMU2::eject_filament(const uint8_t index, const bool recover) {
|
||||
|
||||
set_runout_valid(false);
|
||||
|
||||
BUZZ(200, 404);
|
||||
mmu2_attn_buzz();
|
||||
|
||||
stepper.disable_extruder();
|
||||
|
||||
@@ -992,8 +999,8 @@ bool MMU2::unload() {
|
||||
if (!_enabled) return false;
|
||||
|
||||
if (thermalManager.tooColdToExtrude(active_extruder)) {
|
||||
BUZZ(200, 404);
|
||||
LCD_ALERTMESSAGEPGM(MSG_HOTEND_TOO_COLD);
|
||||
mmu2_attn_buzz();
|
||||
LCD_ALERTMESSAGE(MSG_HOTEND_TOO_COLD);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1003,7 +1010,7 @@ bool MMU2::unload() {
|
||||
command(MMU_CMD_U0);
|
||||
manage_response(false, true);
|
||||
|
||||
BUZZ(200, 404);
|
||||
mmu2_attn_buzz();
|
||||
|
||||
// no active tool
|
||||
extruder = MMU2_NO_TOOL;
|
||||
@@ -1024,8 +1031,7 @@ void MMU2::execute_extruder_sequence(const E_Step * sequence, int steps) {
|
||||
const float es = pgm_read_float(&(step->extrude));
|
||||
const feedRate_t fr_mm_m = pgm_read_float(&(step->feedRate));
|
||||
|
||||
DEBUG_ECHO_START();
|
||||
DEBUG_ECHOLNPGM("E step ", es, "/", fr_mm_m);
|
||||
DEBUG_ECHO_MSG("E step ", es, "/", fr_mm_m);
|
||||
|
||||
current_position.e += es;
|
||||
line_to_current_position(MMM_TO_MMS(fr_mm_m));
|
@@ -43,7 +43,7 @@ public:
|
||||
|
||||
static void init();
|
||||
static void reset();
|
||||
static inline bool enabled() { return _enabled; }
|
||||
static bool enabled() { return _enabled; }
|
||||
static void mmu_loop();
|
||||
static void tool_change(const uint8_t index);
|
||||
static void tool_change(const char *special);
|
||||
@@ -57,10 +57,10 @@ public:
|
||||
static bool eject_filament(const uint8_t index, const bool recover);
|
||||
|
||||
private:
|
||||
static bool rx_str_P(const char *str);
|
||||
static void tx_str_P(const char *str);
|
||||
static void tx_printf_P(const char *format, const int argument);
|
||||
static void tx_printf_P(const char *format, const int argument1, const int argument2);
|
||||
static bool rx_str(FSTR_P fstr);
|
||||
static void tx_str(FSTR_P fstr);
|
||||
static void tx_printf(FSTR_P ffmt, const int argument);
|
||||
static void tx_printf(FSTR_P ffmt, const int argument1, const int argument2);
|
||||
static void clear_rx_buffer();
|
||||
|
||||
static bool rx_ok();
|
||||
@@ -99,7 +99,7 @@ private:
|
||||
static millis_t prev_request, prev_P0_request;
|
||||
static char rx_buffer[MMU_RX_SIZE], tx_buffer[MMU_TX_SIZE];
|
||||
|
||||
static inline void set_runout_valid(const bool valid) {
|
||||
static void set_runout_valid(const bool valid) {
|
||||
finda_runout_valid = valid;
|
||||
#if HAS_FILAMENT_SENSOR
|
||||
if (valid) runout.reset();
|
@@ -40,7 +40,7 @@ uint32_t Password::value, Password::value_entry;
|
||||
//
|
||||
void Password::lock_machine() {
|
||||
is_locked = true;
|
||||
TERN_(HAS_LCD_MENU, authenticate_user(ui.status_screen, screen_password_entry));
|
||||
TERN_(HAS_MARLINUI_MENU, authenticate_user(ui.status_screen, screen_password_entry));
|
||||
}
|
||||
|
||||
//
|
||||
@@ -55,7 +55,7 @@ void Password::authentication_check() {
|
||||
is_locked = true;
|
||||
SERIAL_ECHOLNPGM(STR_WRONG_PASSWORD);
|
||||
}
|
||||
TERN_(HAS_LCD_MENU, authentication_done());
|
||||
TERN_(HAS_MARLINUI_MENU, authentication_done());
|
||||
}
|
||||
|
||||
#endif // PASSWORD_FEATURE
|
||||
|
@@ -33,7 +33,7 @@ public:
|
||||
static void lock_machine();
|
||||
static void authentication_check();
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
#if HAS_MARLINUI_MENU
|
||||
static void access_menu_password();
|
||||
static void authentication_done();
|
||||
static void media_gatekeeper();
|
||||
|
@@ -35,11 +35,18 @@
|
||||
#include "../gcode/gcode.h"
|
||||
#include "../module/motion.h"
|
||||
#include "../module/planner.h"
|
||||
#include "../module/stepper.h"
|
||||
#include "../module/printcounter.h"
|
||||
#include "../module/temperature.h"
|
||||
#include "../core/serial.h"
|
||||
|
||||
#if HAS_EXTRUDERS
|
||||
#include "../module/stepper.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(AUTO_BED_LEVELING_UBL)
|
||||
#include "bedlevel/bedlevel.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(FWRETRACT)
|
||||
#include "fwretract.h"
|
||||
#endif
|
||||
@@ -54,13 +61,13 @@
|
||||
|
||||
#if ENABLED(EXTENSIBLE_UI)
|
||||
#include "../lcd/extui/ui_api.h"
|
||||
#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED)
|
||||
#include "../lcd/e3v2/enhanced/dwin.h"
|
||||
#elif ENABLED(DWIN_LCD_PROUI)
|
||||
#include "../lcd/e3v2/proui/dwin.h"
|
||||
#endif
|
||||
|
||||
#include "../lcd/marlinui.h"
|
||||
|
||||
#if HAS_BUZZER
|
||||
#if HAS_SOUND
|
||||
#include "../libs/buzzer.h"
|
||||
#endif
|
||||
|
||||
@@ -95,10 +102,10 @@ fil_change_settings_t fc_settings[EXTRUDERS];
|
||||
#define _PMSG(L) L##_LCD
|
||||
#endif
|
||||
|
||||
#if HAS_BUZZER
|
||||
#if HAS_SOUND
|
||||
static void impatient_beep(const int8_t max_beep_count, const bool restart=false) {
|
||||
|
||||
if (TERN0(HAS_LCD_MENU, pause_mode == PAUSE_MODE_PAUSE_PRINT)) return;
|
||||
if (TERN0(HAS_MARLINUI_MENU, pause_mode == PAUSE_MODE_PAUSE_PRINT)) return;
|
||||
|
||||
static millis_t next_buzz = 0;
|
||||
static int8_t runout_beep = 0;
|
||||
@@ -195,11 +202,11 @@ bool load_filament(const_float_t slow_load_length/*=0*/, const_float_t fast_load
|
||||
KEEPALIVE_STATE(PAUSED_FOR_USER);
|
||||
wait_for_user = true; // LCD click or M108 will clear this
|
||||
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired_P(PSTR("Load Filament")));
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired(F("Load Filament")));
|
||||
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
const char tool = '0' + TERN0(MULTI_FILAMENT_SENSOR, active_extruder);
|
||||
host_prompt_do(PROMPT_USER_CONTINUE, PSTR("Load Filament T"), tool, CONTINUE_STR);
|
||||
hostui.prompt_do(PROMPT_USER_CONTINUE, F("Load Filament T"), tool, FPSTR(CONTINUE_STR));
|
||||
#endif
|
||||
|
||||
while (wait_for_user) {
|
||||
@@ -253,9 +260,8 @@ bool load_filament(const_float_t slow_load_length/*=0*/, const_float_t fast_load
|
||||
|
||||
if (show_lcd) ui.pause_show_message(PAUSE_MESSAGE_PURGE);
|
||||
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired_P(GET_TEXT(MSG_FILAMENT_CHANGE_PURGE)));
|
||||
TERN_(HOST_PROMPT_SUPPORT, host_prompt_do(PROMPT_USER_CONTINUE, GET_TEXT(MSG_FILAMENT_CHANGE_PURGE), CONTINUE_STR));
|
||||
TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_Popup_Confirm(ICON_BLTouch, GET_TEXT(MSG_FILAMENT_CHANGE_PURGE), CONTINUE_STR));
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired(GET_TEXT_F(MSG_FILAMENT_CHANGE_PURGE)));
|
||||
TERN_(HOST_PROMPT_SUPPORT, hostui.prompt_do(PROMPT_USER_CONTINUE, GET_TEXT_F(MSG_FILAMENT_CHANGE_PURGE), FPSTR(CONTINUE_STR)));
|
||||
wait_for_user = true; // A click or M108 breaks the purge_length loop
|
||||
for (float purge_count = purge_length; purge_count > 0 && wait_for_user; --purge_count)
|
||||
unscaled_e_move(1, ADVANCED_PAUSE_PURGE_FEEDRATE);
|
||||
@@ -272,14 +278,14 @@ bool load_filament(const_float_t slow_load_length/*=0*/, const_float_t fast_load
|
||||
unscaled_e_move(purge_length, ADVANCED_PAUSE_PURGE_FEEDRATE);
|
||||
}
|
||||
|
||||
TERN_(HOST_PROMPT_SUPPORT, filament_load_host_prompt()); // Initiate another host prompt.
|
||||
TERN_(HOST_PROMPT_SUPPORT, hostui.filament_load_prompt()); // Initiate another host prompt.
|
||||
|
||||
#if M600_PURGE_MORE_RESUMABLE
|
||||
if (show_lcd) {
|
||||
// Show "Purge More" / "Resume" menu and wait for reply
|
||||
KEEPALIVE_STATE(PAUSED_FOR_USER);
|
||||
wait_for_user = false;
|
||||
#if EITHER(HAS_LCD_MENU, DWIN_CREALITY_LCD_ENHANCED)
|
||||
#if EITHER(HAS_MARLINUI_MENU, DWIN_LCD_PROUI)
|
||||
ui.pause_show_message(PAUSE_MESSAGE_OPTION); // Also sets PAUSE_RESPONSE_WAIT_FOR
|
||||
#else
|
||||
pause_menu_response = PAUSE_RESPONSE_WAIT_FOR;
|
||||
@@ -293,7 +299,7 @@ bool load_filament(const_float_t slow_load_length/*=0*/, const_float_t fast_load
|
||||
} while (TERN0(M600_PURGE_MORE_RESUMABLE, pause_menu_response == PAUSE_RESPONSE_EXTRUDE_MORE));
|
||||
|
||||
#endif
|
||||
TERN_(HOST_PROMPT_SUPPORT, host_action_prompt_end());
|
||||
TERN_(HOST_PROMPT_SUPPORT, hostui.prompt_end());
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -399,13 +405,14 @@ bool pause_print(const_float_t retract, const xyz_pos_t &park_point, const bool
|
||||
|
||||
#if ENABLED(HOST_ACTION_COMMANDS)
|
||||
#ifdef ACTION_ON_PAUSED
|
||||
host_action_paused();
|
||||
hostui.paused();
|
||||
#elif defined(ACTION_ON_PAUSE)
|
||||
host_action_pause();
|
||||
hostui.pause();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
TERN_(HOST_PROMPT_SUPPORT, host_prompt_open(PROMPT_INFO, PSTR("Pause"), DISMISS_STR));
|
||||
TERN_(HOST_PROMPT_SUPPORT, hostui.prompt_open(PROMPT_INFO, F("Pause"), FPSTR(DISMISS_STR)));
|
||||
TERN_(DWIN_LCD_PROUI, DWIN_Print_Pause());
|
||||
|
||||
// Indicate that the printer is paused
|
||||
++did_pause_print;
|
||||
@@ -443,7 +450,15 @@ bool pause_print(const_float_t retract, const xyz_pos_t &park_point, const bool
|
||||
// Initial retract before move to filament change position
|
||||
if (retract && thermalManager.hotEnoughToExtrude(active_extruder)) {
|
||||
DEBUG_ECHOLNPGM("... retract:", retract);
|
||||
|
||||
#if ENABLED(AUTO_BED_LEVELING_UBL)
|
||||
const bool leveling_was_enabled = planner.leveling_active; // save leveling state
|
||||
set_bed_leveling_enabled(false); // turn off leveling
|
||||
#endif
|
||||
|
||||
unscaled_e_move(retract, PAUSE_PARK_RETRACT_FEEDRATE);
|
||||
|
||||
TERN_(AUTO_BED_LEVELING_UBL, set_bed_leveling_enabled(leveling_was_enabled)); // restore leveling
|
||||
}
|
||||
|
||||
// If axes don't need to home then the nozzle can park
|
||||
@@ -488,7 +503,7 @@ void show_continue_prompt(const bool is_reload) {
|
||||
|
||||
ui.pause_show_message(is_reload ? PAUSE_MESSAGE_INSERT : PAUSE_MESSAGE_WAITING);
|
||||
SERIAL_ECHO_START();
|
||||
SERIAL_ECHOPGM_P(is_reload ? PSTR(_PMSG(STR_FILAMENT_CHANGE_INSERT) "\n") : PSTR(_PMSG(STR_FILAMENT_CHANGE_WAIT) "\n"));
|
||||
SERIAL_ECHOF(is_reload ? F(_PMSG(STR_FILAMENT_CHANGE_INSERT) "\n") : F(_PMSG(STR_FILAMENT_CHANGE_WAIT) "\n"));
|
||||
}
|
||||
|
||||
void wait_for_confirmation(const bool is_reload/*=false*/, const int8_t max_beep_count/*=0*/ DXC_ARGS) {
|
||||
@@ -514,8 +529,8 @@ void wait_for_confirmation(const bool is_reload/*=false*/, const int8_t max_beep
|
||||
|
||||
// Wait for filament insert by user and press button
|
||||
KEEPALIVE_STATE(PAUSED_FOR_USER);
|
||||
TERN_(HOST_PROMPT_SUPPORT, host_prompt_do(PROMPT_USER_CONTINUE, GET_TEXT(MSG_NOZZLE_PARKED), CONTINUE_STR));
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired_P(GET_TEXT(MSG_NOZZLE_PARKED)));
|
||||
TERN_(HOST_PROMPT_SUPPORT, hostui.prompt_do(PROMPT_USER_CONTINUE, GET_TEXT_F(MSG_NOZZLE_PARKED), FPSTR(CONTINUE_STR)));
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired(GET_TEXT_F(MSG_NOZZLE_PARKED)));
|
||||
wait_for_user = true; // LCD click or M108 will clear this
|
||||
while (wait_for_user) {
|
||||
impatient_beep(max_beep_count);
|
||||
@@ -530,17 +545,17 @@ void wait_for_confirmation(const bool is_reload/*=false*/, const int8_t max_beep
|
||||
ui.pause_show_message(PAUSE_MESSAGE_HEAT);
|
||||
SERIAL_ECHO_MSG(_PMSG(STR_FILAMENT_CHANGE_HEAT));
|
||||
|
||||
TERN_(HOST_PROMPT_SUPPORT, host_prompt_do(PROMPT_USER_CONTINUE, GET_TEXT(MSG_HEATER_TIMEOUT), GET_TEXT(MSG_REHEAT)));
|
||||
TERN_(HOST_PROMPT_SUPPORT, hostui.prompt_do(PROMPT_USER_CONTINUE, GET_TEXT_F(MSG_HEATER_TIMEOUT), GET_TEXT_F(MSG_REHEAT)));
|
||||
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired_P(GET_TEXT(MSG_HEATER_TIMEOUT)));
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired(GET_TEXT_F(MSG_HEATER_TIMEOUT)));
|
||||
|
||||
TERN_(HAS_RESUME_CONTINUE, wait_for_user_response(0, true)); // Wait for LCD click or M108
|
||||
|
||||
TERN_(HOST_PROMPT_SUPPORT, host_prompt_do(PROMPT_INFO, GET_TEXT(MSG_REHEATING)));
|
||||
TERN_(HOST_PROMPT_SUPPORT, hostui.prompt_do(PROMPT_INFO, GET_TEXT_F(MSG_REHEATING)));
|
||||
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onStatusChanged_P(GET_TEXT(MSG_REHEATING)));
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onStatusChanged(GET_TEXT_F(MSG_REHEATING)));
|
||||
|
||||
TERN_(DWIN_CREALITY_LCD_ENHANCED, ui.set_status_P(GET_TEXT(MSG_REHEATING)));
|
||||
TERN_(DWIN_LCD_PROUI, LCD_MESSAGE(MSG_REHEATING));
|
||||
|
||||
// Re-enable the heaters if they timed out
|
||||
HOTEND_LOOP() thermalManager.reset_hotend_idle_timer(e);
|
||||
@@ -556,9 +571,9 @@ void wait_for_confirmation(const bool is_reload/*=false*/, const int8_t max_beep
|
||||
|
||||
HOTEND_LOOP() thermalManager.heater_idle[e].start(nozzle_timeout);
|
||||
|
||||
TERN_(HOST_PROMPT_SUPPORT, host_prompt_do(PROMPT_USER_CONTINUE, GET_TEXT(MSG_REHEATDONE), CONTINUE_STR));
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired_P(GET_TEXT(MSG_REHEATDONE)));
|
||||
TERN_(DWIN_CREALITY_LCD_ENHANCED, ui.set_status_P(GET_TEXT(MSG_REHEATDONE)));
|
||||
TERN_(HOST_PROMPT_SUPPORT, hostui.prompt_do(PROMPT_USER_CONTINUE, GET_TEXT_F(MSG_REHEATDONE), FPSTR(CONTINUE_STR)));
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired(GET_TEXT_F(MSG_REHEATDONE)));
|
||||
TERN_(DWIN_LCD_PROUI, LCD_MESSAGE(MSG_REHEATDONE));
|
||||
|
||||
IF_DISABLED(PAUSE_REHEAT_FAST_RESUME, wait_for_user = true);
|
||||
|
||||
@@ -655,9 +670,16 @@ void resume_print(const_float_t slow_load_length/*=0*/, const_float_t fast_load_
|
||||
prepare_internal_move_to_destination(NOZZLE_PARK_Z_FEEDRATE);
|
||||
}
|
||||
|
||||
#if ENABLED(AUTO_BED_LEVELING_UBL)
|
||||
const bool leveling_was_enabled = planner.leveling_active; // save leveling state
|
||||
set_bed_leveling_enabled(false); // turn off leveling
|
||||
#endif
|
||||
|
||||
// Unretract
|
||||
unscaled_e_move(PAUSE_PARK_RETRACT_LENGTH, feedRate_t(PAUSE_PARK_RETRACT_FEEDRATE));
|
||||
|
||||
TERN_(AUTO_BED_LEVELING_UBL, set_bed_leveling_enabled(leveling_was_enabled)); // restore leveling
|
||||
|
||||
// Intelligent resuming
|
||||
#if ENABLED(FWRETRACT)
|
||||
// If retracted before goto pause
|
||||
@@ -667,8 +689,9 @@ void resume_print(const_float_t slow_load_length/*=0*/, const_float_t fast_load_
|
||||
|
||||
// If resume_position is negative
|
||||
if (resume_position.e < 0) unscaled_e_move(resume_position.e, feedRate_t(PAUSE_PARK_RETRACT_FEEDRATE));
|
||||
#if ADVANCED_PAUSE_RESUME_PRIME != 0
|
||||
unscaled_e_move(ADVANCED_PAUSE_RESUME_PRIME, feedRate_t(ADVANCED_PAUSE_PURGE_FEEDRATE));
|
||||
#ifdef ADVANCED_PAUSE_RESUME_PRIME
|
||||
if (ADVANCED_PAUSE_RESUME_PRIME != 0)
|
||||
unscaled_e_move(ADVANCED_PAUSE_RESUME_PRIME, feedRate_t(ADVANCED_PAUSE_PURGE_FEEDRATE));
|
||||
#endif
|
||||
|
||||
// Now all extrusion positions are resumed and ready to be confirmed
|
||||
@@ -678,14 +701,14 @@ void resume_print(const_float_t slow_load_length/*=0*/, const_float_t fast_load_
|
||||
ui.pause_show_message(PAUSE_MESSAGE_STATUS);
|
||||
|
||||
#ifdef ACTION_ON_RESUMED
|
||||
host_action_resumed();
|
||||
hostui.resumed();
|
||||
#elif defined(ACTION_ON_RESUME)
|
||||
host_action_resume();
|
||||
hostui.resume();
|
||||
#endif
|
||||
|
||||
--did_pause_print;
|
||||
|
||||
TERN_(HOST_PROMPT_SUPPORT, host_prompt_open(PROMPT_INFO, PSTR("Resuming"), DISMISS_STR));
|
||||
TERN_(HOST_PROMPT_SUPPORT, hostui.prompt_open(PROMPT_INFO, F("Resuming"), FPSTR(DISMISS_STR)));
|
||||
|
||||
// Resume the print job timer if it was running
|
||||
if (print_job_timer.isPaused()) print_job_timer.start();
|
||||
@@ -705,9 +728,13 @@ void resume_print(const_float_t slow_load_length/*=0*/, const_float_t fast_load_
|
||||
|
||||
TERN_(HAS_FILAMENT_SENSOR, runout.reset());
|
||||
|
||||
TERN_(HAS_STATUS_MESSAGE, ui.reset_status());
|
||||
TERN_(HAS_LCD_MENU, ui.return_to_status());
|
||||
TERN_(DWIN_CREALITY_LCD_ENHANCED, HMI_ReturnScreen());
|
||||
#if ENABLED(DWIN_LCD_PROUI)
|
||||
DWIN_Print_Resume();
|
||||
HMI_ReturnScreen();
|
||||
#else
|
||||
ui.reset_status();
|
||||
ui.return_to_status();
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // ADVANCED_PAUSE_FEATURE
|
||||
|
@@ -73,17 +73,10 @@ extern fil_change_settings_t fc_settings[EXTRUDERS];
|
||||
|
||||
extern uint8_t did_pause_print;
|
||||
|
||||
#if ENABLED(DUAL_X_CARRIAGE)
|
||||
#define DXC_PARAMS , const int8_t DXC_ext=-1
|
||||
#define DXC_ARGS , const int8_t DXC_ext
|
||||
#define DXC_PASS , DXC_ext
|
||||
#define DXC_SAY , " dxc:", int(DXC_ext)
|
||||
#else
|
||||
#define DXC_PARAMS
|
||||
#define DXC_ARGS
|
||||
#define DXC_PASS
|
||||
#define DXC_SAY
|
||||
#endif
|
||||
#define DXC_PARAMS OPTARG(DUAL_X_CARRIAGE, const int8_t DXC_ext=-1)
|
||||
#define DXC_ARGS OPTARG(DUAL_X_CARRIAGE, const int8_t DXC_ext)
|
||||
#define DXC_PASS OPTARG(DUAL_X_CARRIAGE, DXC_ext)
|
||||
#define DXC_SAY OPTARG(DUAL_X_CARRIAGE, " dxc:", int(DXC_ext))
|
||||
|
||||
// Pause the print. If unload_length is set, do a Filament Unload
|
||||
bool pause_print(
|
||||
|
@@ -24,10 +24,14 @@
|
||||
* power.cpp - power control
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
|
||||
#if EITHER(PSU_CONTROL, AUTO_POWER_CONTROL)
|
||||
|
||||
#include "power.h"
|
||||
#include "../module/stepper.h"
|
||||
#include "../module/planner.h"
|
||||
#include "../module/stepper/indirection.h" // for restore_stepper_drivers
|
||||
#include "../module/temperature.h"
|
||||
#include "../MarlinCore.h"
|
||||
|
||||
#if ENABLED(PS_OFF_SOUND)
|
||||
@@ -38,12 +42,11 @@
|
||||
#include "../gcode/gcode.h"
|
||||
#endif
|
||||
|
||||
#if EITHER(PSU_CONTROL, AUTO_POWER_CONTROL)
|
||||
|
||||
Power powerManager;
|
||||
bool Power::psu_on;
|
||||
|
||||
#if ENABLED(AUTO_POWER_CONTROL)
|
||||
#include "../module/stepper.h"
|
||||
#include "../module/temperature.h"
|
||||
|
||||
#if BOTH(USE_CONTROLLER_FAN, AUTO_POWER_CONTROLLERFAN)
|
||||
@@ -75,6 +78,10 @@ void Power::power_on() {
|
||||
|
||||
if (psu_on) return;
|
||||
|
||||
#if EITHER(POWER_OFF_TIMER, POWER_OFF_WAIT_FOR_COOLDOWN)
|
||||
cancelAutoPowerOff();
|
||||
#endif
|
||||
|
||||
OUT_WRITE(PS_ON_PIN, PSU_ACTIVE_STATE);
|
||||
psu_on = true;
|
||||
safe_delay(PSU_POWERUP_DELAY);
|
||||
@@ -82,20 +89,23 @@ void Power::power_on() {
|
||||
TERN_(HAS_TRINAMIC_CONFIG, safe_delay(PSU_POWERUP_DELAY));
|
||||
|
||||
#ifdef PSU_POWERUP_GCODE
|
||||
GcodeSuite::process_subcommands_now_P(PSTR(PSU_POWERUP_GCODE));
|
||||
gcode.process_subcommands_now(F(PSU_POWERUP_GCODE));
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Power off if the power is currently on.
|
||||
* Processes any PSU_POWEROFF_GCODE and makes a PS_OFF_SOUND if enabled.
|
||||
*
|
||||
*/
|
||||
void Power::power_off() {
|
||||
SERIAL_ECHOLNPGM(STR_POWEROFF);
|
||||
|
||||
TERN_(HAS_SUICIDE, suicide());
|
||||
|
||||
if (!psu_on) return;
|
||||
|
||||
#ifdef PSU_POWEROFF_GCODE
|
||||
GcodeSuite::process_subcommands_now_P(PSTR(PSU_POWEROFF_GCODE));
|
||||
gcode.process_subcommands_now(F(PSU_POWEROFF_GCODE));
|
||||
#endif
|
||||
|
||||
#if ENABLED(PS_OFF_SOUND)
|
||||
@@ -104,8 +114,57 @@ void Power::power_off() {
|
||||
|
||||
OUT_WRITE(PS_ON_PIN, !PSU_ACTIVE_STATE);
|
||||
psu_on = false;
|
||||
|
||||
#if EITHER(POWER_OFF_TIMER, POWER_OFF_WAIT_FOR_COOLDOWN)
|
||||
cancelAutoPowerOff();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if EITHER(AUTO_POWER_CONTROL, POWER_OFF_WAIT_FOR_COOLDOWN)
|
||||
|
||||
bool Power::is_cooling_needed() {
|
||||
#if HAS_HOTEND && AUTO_POWER_E_TEMP
|
||||
HOTEND_LOOP() if (thermalManager.degHotend(e) >= (AUTO_POWER_E_TEMP)) return true;
|
||||
#endif
|
||||
|
||||
#if HAS_HEATED_CHAMBER && AUTO_POWER_CHAMBER_TEMP
|
||||
if (thermalManager.degChamber() >= (AUTO_POWER_CHAMBER_TEMP)) return true;
|
||||
#endif
|
||||
|
||||
#if HAS_COOLER && AUTO_POWER_COOLER_TEMP
|
||||
if (thermalManager.degCooler() >= (AUTO_POWER_COOLER_TEMP)) return true;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if EITHER(POWER_OFF_TIMER, POWER_OFF_WAIT_FOR_COOLDOWN)
|
||||
|
||||
#if ENABLED(POWER_OFF_TIMER)
|
||||
millis_t Power::power_off_time = 0;
|
||||
void Power::setPowerOffTimer(const millis_t delay_ms) { power_off_time = millis() + delay_ms; }
|
||||
#endif
|
||||
|
||||
#if ENABLED(POWER_OFF_WAIT_FOR_COOLDOWN)
|
||||
bool Power::power_off_on_cooldown = false;
|
||||
void Power::setPowerOffOnCooldown(const bool ena) { power_off_on_cooldown = ena; }
|
||||
#endif
|
||||
|
||||
void Power::cancelAutoPowerOff() {
|
||||
TERN_(POWER_OFF_TIMER, power_off_time = 0);
|
||||
TERN_(POWER_OFF_WAIT_FOR_COOLDOWN, power_off_on_cooldown = false);
|
||||
}
|
||||
|
||||
void Power::checkAutoPowerOff() {
|
||||
if (TERN1(POWER_OFF_TIMER, !power_off_time) && TERN1(POWER_OFF_WAIT_FOR_COOLDOWN, !power_off_on_cooldown)) return;
|
||||
if (TERN0(POWER_OFF_WAIT_FOR_COOLDOWN, power_off_on_cooldown && is_cooling_needed())) return;
|
||||
if (TERN0(POWER_OFF_TIMER, power_off_time && PENDING(millis(), power_off_time))) return;
|
||||
power_off();
|
||||
}
|
||||
|
||||
#endif // POWER_OFF_TIMER || POWER_OFF_WAIT_FOR_COOLDOWN
|
||||
|
||||
#if ENABLED(AUTO_POWER_CONTROL)
|
||||
|
||||
@@ -149,19 +208,7 @@ void Power::power_off() {
|
||||
|
||||
if (TERN0(HAS_HEATED_BED, thermalManager.degTargetBed() > 0 || thermalManager.temp_bed.soft_pwm_amount > 0)) return true;
|
||||
|
||||
#if HAS_HOTEND && AUTO_POWER_E_TEMP
|
||||
HOTEND_LOOP() if (thermalManager.degHotend(e) >= (AUTO_POWER_E_TEMP)) return true;
|
||||
#endif
|
||||
|
||||
#if HAS_HEATED_CHAMBER && AUTO_POWER_CHAMBER_TEMP
|
||||
if (thermalManager.degChamber() >= (AUTO_POWER_CHAMBER_TEMP)) return true;
|
||||
#endif
|
||||
|
||||
#if HAS_COOLER && AUTO_POWER_COOLER_TEMP
|
||||
if (thermalManager.degCooler() >= (AUTO_POWER_COOLER_TEMP)) return true;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
return is_cooling_needed();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,7 +240,6 @@ void Power::power_off() {
|
||||
|
||||
/**
|
||||
* Power off with a delay. Power off is triggered by check() after the delay.
|
||||
*
|
||||
*/
|
||||
void Power::power_off_soon() {
|
||||
lastPowerOn = millis() - SEC_TO_MS(POWER_TIMEOUT) + SEC_TO_MS(POWER_OFF_DELAY);
|
||||
|
@@ -25,7 +25,7 @@
|
||||
* power.h - power control
|
||||
*/
|
||||
|
||||
#if ENABLED(AUTO_POWER_CONTROL)
|
||||
#if EITHER(AUTO_POWER_CONTROL, POWER_OFF_TIMER)
|
||||
#include "../core/millis_t.h"
|
||||
#endif
|
||||
|
||||
@@ -37,20 +37,36 @@ class Power {
|
||||
static void power_on();
|
||||
static void power_off();
|
||||
|
||||
#if ENABLED(AUTO_POWER_CONTROL) && POWER_OFF_DELAY > 0
|
||||
static void power_off_soon();
|
||||
#else
|
||||
static inline void power_off_soon() { power_off(); }
|
||||
#endif
|
||||
#if EITHER(POWER_OFF_TIMER, POWER_OFF_WAIT_FOR_COOLDOWN)
|
||||
#if ENABLED(POWER_OFF_TIMER)
|
||||
static millis_t power_off_time;
|
||||
static void setPowerOffTimer(const millis_t delay_ms);
|
||||
#endif
|
||||
#if ENABLED(POWER_OFF_WAIT_FOR_COOLDOWN)
|
||||
static bool power_off_on_cooldown;
|
||||
static void setPowerOffOnCooldown(const bool ena);
|
||||
#endif
|
||||
static void cancelAutoPowerOff();
|
||||
static void checkAutoPowerOff();
|
||||
#endif
|
||||
|
||||
#if ENABLED(AUTO_POWER_CONTROL)
|
||||
static void check(const bool pause);
|
||||
#if ENABLED(AUTO_POWER_CONTROL) && POWER_OFF_DELAY > 0
|
||||
static void power_off_soon();
|
||||
#else
|
||||
static void power_off_soon() { power_off(); }
|
||||
#endif
|
||||
|
||||
private:
|
||||
static millis_t lastPowerOn;
|
||||
static bool is_power_needed();
|
||||
#if ENABLED(AUTO_POWER_CONTROL)
|
||||
static void check(const bool pause);
|
||||
|
||||
#endif
|
||||
private:
|
||||
static millis_t lastPowerOn;
|
||||
static bool is_power_needed();
|
||||
static bool is_cooling_needed();
|
||||
#elif ENABLED(POWER_OFF_WAIT_FOR_COOLDOWN)
|
||||
private:
|
||||
static bool is_cooling_needed();
|
||||
#endif
|
||||
};
|
||||
|
||||
extern Power powerManager;
|
||||
|
@@ -26,7 +26,7 @@
|
||||
|
||||
#include "power_monitor.h"
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
#if HAS_MARLINUI_MENU
|
||||
#include "../lcd/marlinui.h"
|
||||
#include "../lcd/lcdprint.h"
|
||||
#endif
|
||||
@@ -53,7 +53,7 @@ PowerMonitor power_monitor; // Single instance - this calls the constructor
|
||||
void PowerMonitor::draw_current() {
|
||||
const float amps = getAmps();
|
||||
lcd_put_u8str(amps < 100 ? ftostr31ns(amps) : ui16tostr4rj((uint16_t)amps));
|
||||
lcd_put_wchar('A');
|
||||
lcd_put_lchar('A');
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -61,7 +61,7 @@ PowerMonitor power_monitor; // Single instance - this calls the constructor
|
||||
void PowerMonitor::draw_voltage() {
|
||||
const float volts = getVolts();
|
||||
lcd_put_u8str(volts < 100 ? ftostr31ns(volts) : ui16tostr4rj((uint16_t)volts));
|
||||
lcd_put_wchar('V');
|
||||
lcd_put_lchar('V');
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -69,7 +69,7 @@ PowerMonitor power_monitor; // Single instance - this calls the constructor
|
||||
void PowerMonitor::draw_power() {
|
||||
const float power = getPower();
|
||||
lcd_put_u8str(power < 100 ? ftostr31ns(power) : ui16tostr4rj((uint16_t)power));
|
||||
lcd_put_wchar('W');
|
||||
lcd_put_lchar('W');
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@@ -32,7 +32,7 @@ struct pm_lpf_t {
|
||||
uint32_t filter_buf;
|
||||
float value;
|
||||
void add_sample(const uint16_t sample) {
|
||||
filter_buf = filter_buf - (filter_buf >> K_VALUE) + (uint32_t(sample) << K_SCALE);
|
||||
filter_buf += (uint32_t(sample) << K_SCALE) - (filter_buf >> K_VALUE);
|
||||
}
|
||||
void capture() {
|
||||
value = filter_buf * (SCALE * (1.0f / (1UL << (PM_K_VALUE + PM_K_SCALE))));
|
||||
|
@@ -54,6 +54,10 @@ uint32_t PrintJobRecovery::cmd_sdpos, // = 0
|
||||
#include "../module/temperature.h"
|
||||
#include "../core/serial.h"
|
||||
|
||||
#if HOMING_Z_WITH_PROBE
|
||||
#include "../module/probe.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(FWRETRACT)
|
||||
#include "fwretract.h"
|
||||
#endif
|
||||
@@ -104,13 +108,18 @@ void PrintJobRecovery::changed() {
|
||||
*
|
||||
* If a saved state exists send 'M1000 S' to initiate job recovery.
|
||||
*/
|
||||
void PrintJobRecovery::check() {
|
||||
bool PrintJobRecovery::check() {
|
||||
//if (!card.isMounted()) card.mount();
|
||||
bool success = false;
|
||||
if (card.isMounted()) {
|
||||
load();
|
||||
if (!valid()) return cancel();
|
||||
queue.inject_P(PSTR("M1000S"));
|
||||
success = valid();
|
||||
if (!success)
|
||||
cancel();
|
||||
else
|
||||
queue.inject(F("M1000S"));
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,7 +139,7 @@ void PrintJobRecovery::load() {
|
||||
(void)file.read(&info, sizeof(info));
|
||||
close();
|
||||
}
|
||||
debug(PSTR("Load"));
|
||||
debug(F("Load"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,7 +187,8 @@ void PrintJobRecovery::save(const bool force/*=false*/, const float zraise/*=POW
|
||||
info.valid_foot = info.valid_head;
|
||||
|
||||
// Machine state
|
||||
info.current_position = current_position;
|
||||
// info.sdpos and info.current_position are pre-filled from the Stepper ISR
|
||||
|
||||
info.feedrate = uint16_t(MMS_TO_MMM(feedrate_mm_s));
|
||||
info.zraise = zraise;
|
||||
info.flag.raised = raised; // Was Z raised before power-off?
|
||||
@@ -191,7 +201,7 @@ void PrintJobRecovery::save(const bool force/*=false*/, const float zraise/*=POW
|
||||
#if DISABLED(NO_VOLUMETRICS)
|
||||
info.flag.volumetric_enabled = parser.volumetric_enabled;
|
||||
#if HAS_MULTI_EXTRUDER
|
||||
for (int8_t e = 0; e < EXTRUDERS; e++) info.filament_size[e] = planner.filament_size[e];
|
||||
EXTRUDER_LOOP() info.filament_size[e] = planner.filament_size[e];
|
||||
#else
|
||||
if (parser.volumetric_enabled) info.filament_size[0] = planner.filament_size[active_extruder];
|
||||
#endif
|
||||
@@ -244,7 +254,7 @@ void PrintJobRecovery::save(const bool force/*=false*/, const float zraise/*=POW
|
||||
|
||||
#if POWER_LOSS_RETRACT_LEN
|
||||
// Retract filament now
|
||||
gcode.process_subcommands_now_P(PSTR("G1 F3000 E-" STRINGIFY(POWER_LOSS_RETRACT_LEN)));
|
||||
gcode.process_subcommands_now(F("G1 F3000 E-" STRINGIFY(POWER_LOSS_RETRACT_LEN)));
|
||||
#endif
|
||||
|
||||
#if POWER_LOSS_ZRAISE
|
||||
@@ -265,6 +275,10 @@ void PrintJobRecovery::save(const bool force/*=false*/, const float zraise/*=POW
|
||||
|
||||
#endif
|
||||
|
||||
#endif // POWER_LOSS_PIN
|
||||
|
||||
#if PIN_EXISTS(POWER_LOSS) || ENABLED(DEBUG_POWER_LOSS_RECOVERY)
|
||||
|
||||
/**
|
||||
* An outage was detected by a sensor pin.
|
||||
* - If not SD printing, let the machine turn off on its own with no "KILL" screen
|
||||
@@ -273,7 +287,7 @@ void PrintJobRecovery::save(const bool force/*=false*/, const float zraise/*=POW
|
||||
* - If backup power is available Retract E and Raise Z
|
||||
* - Go to the KILL screen
|
||||
*/
|
||||
void PrintJobRecovery::_outage() {
|
||||
void PrintJobRecovery::_outage(TERN_(DEBUG_POWER_LOSS_RECOVERY, const bool simulated/*=false*/)) {
|
||||
#if ENABLED(BACKUP_POWER_SUPPLY)
|
||||
static bool lock = false;
|
||||
if (lock) return; // No re-entrance from idle() during retract_and_lift()
|
||||
@@ -301,17 +315,23 @@ void PrintJobRecovery::save(const bool force/*=false*/, const float zraise/*=POW
|
||||
retract_and_lift(zraise);
|
||||
#endif
|
||||
|
||||
kill(GET_TEXT(MSG_OUTAGE_RECOVERY));
|
||||
if (TERN0(DEBUG_POWER_LOSS_RECOVERY, simulated)) {
|
||||
card.fileHasFinished();
|
||||
current_position.reset();
|
||||
sync_plan_position();
|
||||
}
|
||||
else
|
||||
kill(GET_TEXT_F(MSG_OUTAGE_RECOVERY));
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif // POWER_LOSS_PIN || DEBUG_POWER_LOSS_RECOVERY
|
||||
|
||||
/**
|
||||
* Save the recovery info the recovery file
|
||||
*/
|
||||
void PrintJobRecovery::write() {
|
||||
|
||||
debug(PSTR("Write"));
|
||||
debug(F("Write"));
|
||||
|
||||
open(false);
|
||||
file.seekSet(0);
|
||||
@@ -337,7 +357,7 @@ void PrintJobRecovery::resume() {
|
||||
|
||||
#if HAS_LEVELING
|
||||
// Make sure leveling is off before any G92 and G28
|
||||
gcode.process_subcommands_now_P(PSTR("M420 S0 Z0"));
|
||||
gcode.process_subcommands_now(F("M420 S0 Z0"));
|
||||
#endif
|
||||
|
||||
#if HAS_HEATED_BED
|
||||
@@ -373,7 +393,7 @@ void PrintJobRecovery::resume() {
|
||||
// establish the current position as best we can.
|
||||
//
|
||||
|
||||
gcode.process_subcommands_now_P(PSTR("G92.9E0")); // Reset E to 0
|
||||
gcode.process_subcommands_now(F("G92.9E0")); // Reset E to 0
|
||||
|
||||
#if Z_HOME_TO_MAX
|
||||
|
||||
@@ -390,14 +410,12 @@ void PrintJobRecovery::resume() {
|
||||
|
||||
#if ENABLED(POWER_LOSS_RECOVER_ZHOME) && defined(POWER_LOSS_ZHOME_POS)
|
||||
#define HOMING_Z_DOWN 1
|
||||
#else
|
||||
#define HOME_XY_ONLY 1
|
||||
#endif
|
||||
|
||||
float z_now = info.flag.raised ? z_raised : z_print;
|
||||
|
||||
// Reset E to 0 and set Z to the real position
|
||||
#if HOME_XY_ONLY
|
||||
#if !HOMING_Z_DOWN
|
||||
// Set Z to the real position
|
||||
sprintf_P(cmd, PSTR("G92.9Z%s"), dtostrf(z_now, 1, 3, str_1));
|
||||
gcode.process_subcommands_now(cmd);
|
||||
#endif
|
||||
@@ -409,15 +427,15 @@ void PrintJobRecovery::resume() {
|
||||
gcode.process_subcommands_now(cmd);
|
||||
}
|
||||
|
||||
// Home XY with no Z raise, and also home Z here if Z isn't homing down below.
|
||||
gcode.process_subcommands_now_P(PSTR("G28R0" TERN_(HOME_XY_ONLY, "XY"))); // No raise during G28
|
||||
// Home XY with no Z raise
|
||||
gcode.process_subcommands_now(F("G28R0XY")); // No raise during G28
|
||||
|
||||
#endif
|
||||
|
||||
#if HOMING_Z_DOWN
|
||||
// Move to a safe XY position and home Z while avoiding the print.
|
||||
constexpr xy_pos_t p = POWER_LOSS_ZHOME_POS;
|
||||
sprintf_P(cmd, PSTR("G1X%sY%sF1000\nG28Z"), dtostrf(p.x, 1, 3, str_1), dtostrf(p.y, 1, 3, str_2));
|
||||
const xy_pos_t p = xy_pos_t(POWER_LOSS_ZHOME_POS) TERN_(HOMING_Z_WITH_PROBE, - probe.offset_xy);
|
||||
sprintf_P(cmd, PSTR("G1X%sY%sF1000\nG28HZ"), dtostrf(p.x, 1, 3, str_1), dtostrf(p.y, 1, 3, str_2));
|
||||
gcode.process_subcommands_now(cmd);
|
||||
#endif
|
||||
|
||||
@@ -431,7 +449,7 @@ void PrintJobRecovery::resume() {
|
||||
sprintf_P(cmd, PSTR("M420S%cZ%s"), '0' + (char)info.flag.leveling, dtostrf(info.fade, 1, 1, str_1));
|
||||
gcode.process_subcommands_now(cmd);
|
||||
|
||||
#if HOME_XY_ONLY
|
||||
#if !HOMING_Z_DOWN
|
||||
// The physical Z was adjusted at power-off so undo the M420S1 correction to Z with G92.9.
|
||||
sprintf_P(cmd, PSTR("G92.9Z%s"), dtostrf(z_now, 1, 1, str_1));
|
||||
gcode.process_subcommands_now(cmd);
|
||||
@@ -448,7 +466,7 @@ void PrintJobRecovery::resume() {
|
||||
// Recover volumetric extrusion state
|
||||
#if DISABLED(NO_VOLUMETRICS)
|
||||
#if HAS_MULTI_EXTRUDER
|
||||
for (int8_t e = 0; e < EXTRUDERS; e++) {
|
||||
EXTRUDER_LOOP() {
|
||||
sprintf_P(cmd, PSTR("M200T%iD%s"), e, dtostrf(info.filament_size[e], 1, 3, str_1));
|
||||
gcode.process_subcommands_now(cmd);
|
||||
}
|
||||
@@ -498,10 +516,10 @@ void PrintJobRecovery::resume() {
|
||||
|
||||
// Restore retract and hop state from an active `G10` command
|
||||
#if ENABLED(FWRETRACT)
|
||||
LOOP_L_N(e, EXTRUDERS) {
|
||||
EXTRUDER_LOOP() {
|
||||
if (info.retract[e] != 0.0) {
|
||||
fwretract.current_retract[e] = info.retract[e];
|
||||
fwretract.retracted[e] = true;
|
||||
fwretract.retracted.set(e);
|
||||
}
|
||||
}
|
||||
fwretract.current_hop = info.retract_hop;
|
||||
@@ -513,17 +531,17 @@ void PrintJobRecovery::resume() {
|
||||
|
||||
// Un-retract if there was a retract at outage
|
||||
#if ENABLED(BACKUP_POWER_SUPPLY) && POWER_LOSS_RETRACT_LEN > 0
|
||||
gcode.process_subcommands_now_P(PSTR("G1E" STRINGIFY(POWER_LOSS_RETRACT_LEN) "F3000"));
|
||||
gcode.process_subcommands_now(F("G1F3000E" STRINGIFY(POWER_LOSS_RETRACT_LEN)));
|
||||
#endif
|
||||
|
||||
// Additional purge on resume if configured
|
||||
#if POWER_LOSS_PURGE_LEN
|
||||
sprintf_P(cmd, PSTR("G1 E%d F3000"), (POWER_LOSS_PURGE_LEN) + (POWER_LOSS_RETRACT_LEN));
|
||||
sprintf_P(cmd, PSTR("G1F3000E%d"), (POWER_LOSS_PURGE_LEN) + (POWER_LOSS_RETRACT_LEN));
|
||||
gcode.process_subcommands_now(cmd);
|
||||
#endif
|
||||
|
||||
#if ENABLED(NOZZLE_CLEAN_FEATURE)
|
||||
gcode.process_subcommands_now_P(PSTR("G12"));
|
||||
gcode.process_subcommands_now(F("G12"));
|
||||
#endif
|
||||
|
||||
// Move back over to the saved XY
|
||||
@@ -549,7 +567,7 @@ void PrintJobRecovery::resume() {
|
||||
TERN_(HAS_HOME_OFFSET, home_offset = info.home_offset);
|
||||
TERN_(HAS_POSITION_SHIFT, position_shift = info.position_shift);
|
||||
#if HAS_HOME_OFFSET || HAS_POSITION_SHIFT
|
||||
LOOP_LINEAR_AXES(i) update_workspace_offset((AxisEnum)i);
|
||||
LOOP_NUM_AXES(i) update_workspace_offset((AxisEnum)i);
|
||||
#endif
|
||||
|
||||
// Relative axis modes
|
||||
@@ -575,8 +593,8 @@ void PrintJobRecovery::resume() {
|
||||
|
||||
#if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
|
||||
|
||||
void PrintJobRecovery::debug(PGM_P const prefix) {
|
||||
DEBUG_ECHOPGM_P(prefix);
|
||||
void PrintJobRecovery::debug(FSTR_P const prefix) {
|
||||
DEBUG_ECHOF(prefix);
|
||||
DEBUG_ECHOLNPGM(" Job Recovery Info...\nvalid_head:", info.valid_head, " valid_foot:", info.valid_foot);
|
||||
if (info.valid_head) {
|
||||
if (info.valid_head == info.valid_foot) {
|
||||
@@ -599,7 +617,7 @@ void PrintJobRecovery::resume() {
|
||||
|
||||
#if HAS_HOME_OFFSET
|
||||
DEBUG_ECHOPGM("home_offset: ");
|
||||
LOOP_LINEAR_AXES(i) {
|
||||
LOOP_NUM_AXES(i) {
|
||||
if (i) DEBUG_CHAR(',');
|
||||
DEBUG_DECIMAL(info.home_offset[i]);
|
||||
}
|
||||
@@ -608,7 +626,7 @@ void PrintJobRecovery::resume() {
|
||||
|
||||
#if HAS_POSITION_SHIFT
|
||||
DEBUG_ECHOPGM("position_shift: ");
|
||||
LOOP_LINEAR_AXES(i) {
|
||||
LOOP_NUM_AXES(i) {
|
||||
if (i) DEBUG_CHAR(',');
|
||||
DEBUG_DECIMAL(info.position_shift[i]);
|
||||
}
|
||||
@@ -621,7 +639,7 @@ void PrintJobRecovery::resume() {
|
||||
|
||||
#if DISABLED(NO_VOLUMETRICS)
|
||||
DEBUG_ECHOPGM("filament_size:");
|
||||
LOOP_L_N(i, EXTRUDERS) DEBUG_ECHOLNPGM(" ", info.filament_size[i]);
|
||||
EXTRUDER_LOOP() DEBUG_ECHOLNPGM(" ", info.filament_size[e]);
|
||||
DEBUG_EOL();
|
||||
#endif
|
||||
|
||||
@@ -653,7 +671,7 @@ void PrintJobRecovery::resume() {
|
||||
|
||||
#if ENABLED(FWRETRACT)
|
||||
DEBUG_ECHOPGM("retract: ");
|
||||
for (int8_t e = 0; e < EXTRUDERS; e++) {
|
||||
EXTRUDER_LOOP() {
|
||||
DEBUG_ECHO(info.retract[e]);
|
||||
if (e < EXTRUDERS - 1) DEBUG_CHAR(',');
|
||||
}
|
||||
|
@@ -152,7 +152,7 @@ class PrintJobRecovery {
|
||||
static void init();
|
||||
static void prepare();
|
||||
|
||||
static inline void setup() {
|
||||
static void setup() {
|
||||
#if PIN_EXISTS(POWER_LOSS)
|
||||
#if ENABLED(POWER_LOSS_PULLUP)
|
||||
SET_INPUT_PULLUP(POWER_LOSS_PIN);
|
||||
@@ -165,28 +165,28 @@ class PrintJobRecovery {
|
||||
}
|
||||
|
||||
// Track each command's file offsets
|
||||
static inline uint32_t command_sdpos() { return sdpos[queue_index_r]; }
|
||||
static inline void commit_sdpos(const uint8_t index_w) { sdpos[index_w] = cmd_sdpos; }
|
||||
static uint32_t command_sdpos() { return sdpos[queue_index_r]; }
|
||||
static void commit_sdpos(const uint8_t index_w) { sdpos[index_w] = cmd_sdpos; }
|
||||
|
||||
static bool enabled;
|
||||
static void enable(const bool onoff);
|
||||
static void changed();
|
||||
|
||||
static inline bool exists() { return card.jobRecoverFileExists(); }
|
||||
static inline void open(const bool read) { card.openJobRecoveryFile(read); }
|
||||
static inline void close() { file.close(); }
|
||||
static bool exists() { return card.jobRecoverFileExists(); }
|
||||
static void open(const bool read) { card.openJobRecoveryFile(read); }
|
||||
static void close() { file.close(); }
|
||||
|
||||
static void check();
|
||||
static bool check();
|
||||
static void resume();
|
||||
static void purge();
|
||||
|
||||
static inline void cancel() { purge(); IF_DISABLED(NO_SD_AUTOSTART, card.autofile_begin()); }
|
||||
static void cancel() { purge(); }
|
||||
|
||||
static void load();
|
||||
static void save(const bool force=ENABLED(SAVE_EACH_CMD_MODE), const float zraise=POWER_LOSS_ZRAISE, const bool raised=false);
|
||||
|
||||
#if PIN_EXISTS(POWER_LOSS)
|
||||
static inline void outage() {
|
||||
static void outage() {
|
||||
static constexpr uint8_t OUTAGE_THRESHOLD = 3;
|
||||
static uint8_t outage_counter = 0;
|
||||
if (enabled && READ(POWER_LOSS_PIN) == POWER_LOSS_STATE) {
|
||||
@@ -199,14 +199,14 @@ class PrintJobRecovery {
|
||||
#endif
|
||||
|
||||
// The referenced file exists
|
||||
static inline bool interrupted_file_exists() { return card.fileExists(info.sd_filename); }
|
||||
static bool interrupted_file_exists() { return card.fileExists(info.sd_filename); }
|
||||
|
||||
static inline bool valid() { return info.valid() && interrupted_file_exists(); }
|
||||
static bool valid() { return info.valid() && interrupted_file_exists(); }
|
||||
|
||||
#if ENABLED(DEBUG_POWER_LOSS_RECOVERY)
|
||||
static void debug(PGM_P const prefix);
|
||||
static void debug(FSTR_P const prefix);
|
||||
#else
|
||||
static inline void debug(PGM_P const) {}
|
||||
static void debug(FSTR_P const) {}
|
||||
#endif
|
||||
|
||||
private:
|
||||
@@ -216,9 +216,9 @@ class PrintJobRecovery {
|
||||
static void retract_and_lift(const_float_t zraise);
|
||||
#endif
|
||||
|
||||
#if PIN_EXISTS(POWER_LOSS)
|
||||
#if PIN_EXISTS(POWER_LOSS) || ENABLED(DEBUG_POWER_LOSS_RECOVERY)
|
||||
friend class GcodeSuite;
|
||||
static void _outage();
|
||||
static void _outage(TERN_(DEBUG_POWER_LOSS_RECOVERY, const bool simulated=false));
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@@ -22,36 +22,54 @@
|
||||
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
|
||||
#if ENABLED(PROBE_TEMP_COMPENSATION)
|
||||
#if HAS_PTC
|
||||
|
||||
//#define DEBUG_PTC // Print extra debug output with 'M871'
|
||||
|
||||
#include "probe_temp_comp.h"
|
||||
#include <math.h>
|
||||
#include "../module/temperature.h"
|
||||
|
||||
ProbeTempComp temp_comp;
|
||||
ProbeTempComp ptc;
|
||||
|
||||
int16_t ProbeTempComp::z_offsets_probe[cali_info_init[TSI_PROBE].measurements], // = {0}
|
||||
ProbeTempComp::z_offsets_bed[cali_info_init[TSI_BED].measurements]; // = {0}
|
||||
#if ENABLED(PTC_PROBE)
|
||||
constexpr int16_t z_offsets_probe_default[PTC_PROBE_COUNT] = PTC_PROBE_ZOFFS;
|
||||
int16_t ProbeTempComp::z_offsets_probe[PTC_PROBE_COUNT] = PTC_PROBE_ZOFFS;
|
||||
#endif
|
||||
|
||||
#if ENABLED(USE_TEMP_EXT_COMPENSATION)
|
||||
int16_t ProbeTempComp::z_offsets_ext[cali_info_init[TSI_EXT].measurements]; // = {0}
|
||||
#if ENABLED(PTC_BED)
|
||||
constexpr int16_t z_offsets_bed_default[PTC_BED_COUNT] = PTC_BED_ZOFFS;
|
||||
int16_t ProbeTempComp::z_offsets_bed[PTC_BED_COUNT] = PTC_BED_ZOFFS;
|
||||
#endif
|
||||
|
||||
#if ENABLED(PTC_HOTEND)
|
||||
constexpr int16_t z_offsets_hotend_default[PTC_HOTEND_COUNT] = PTC_HOTEND_ZOFFS;
|
||||
int16_t ProbeTempComp::z_offsets_hotend[PTC_HOTEND_COUNT] = PTC_HOTEND_ZOFFS;
|
||||
#endif
|
||||
|
||||
int16_t *ProbeTempComp::sensor_z_offsets[TSI_COUNT] = {
|
||||
ProbeTempComp::z_offsets_probe, ProbeTempComp::z_offsets_bed
|
||||
OPTARG(USE_TEMP_EXT_COMPENSATION, ProbeTempComp::z_offsets_ext)
|
||||
#if ENABLED(PTC_PROBE)
|
||||
ProbeTempComp::z_offsets_probe,
|
||||
#endif
|
||||
#if ENABLED(PTC_BED)
|
||||
ProbeTempComp::z_offsets_bed,
|
||||
#endif
|
||||
#if ENABLED(PTC_HOTEND)
|
||||
ProbeTempComp::z_offsets_hotend,
|
||||
#endif
|
||||
};
|
||||
|
||||
const temp_calib_t ProbeTempComp::cali_info[TSI_COUNT] = {
|
||||
cali_info_init[TSI_PROBE], cali_info_init[TSI_BED]
|
||||
OPTARG(USE_TEMP_EXT_COMPENSATION, cali_info_init[TSI_EXT])
|
||||
};
|
||||
|
||||
constexpr xyz_pos_t ProbeTempComp::park_point;
|
||||
constexpr xy_pos_t ProbeTempComp::measure_point;
|
||||
constexpr celsius_t ProbeTempComp::probe_calib_bed_temp;
|
||||
constexpr temp_calib_t ProbeTempComp::cali_info[TSI_COUNT];
|
||||
|
||||
uint8_t ProbeTempComp::calib_idx; // = 0
|
||||
float ProbeTempComp::init_measurement; // = 0.0
|
||||
bool ProbeTempComp::enabled = true;
|
||||
|
||||
void ProbeTempComp::reset() {
|
||||
TERN_(PTC_PROBE, LOOP_L_N(i, PTC_PROBE_COUNT) z_offsets_probe[i] = z_offsets_probe_default[i]);
|
||||
TERN_(PTC_BED, LOOP_L_N(i, PTC_BED_COUNT) z_offsets_bed[i] = z_offsets_bed_default[i]);
|
||||
TERN_(PTC_HOTEND, LOOP_L_N(i, PTC_HOTEND_COUNT) z_offsets_hotend[i] = z_offsets_hotend_default[i]);
|
||||
}
|
||||
|
||||
void ProbeTempComp::clear_offsets(const TempSensorID tsi) {
|
||||
LOOP_L_N(i, cali_info[tsi].measurements)
|
||||
@@ -69,19 +87,26 @@ void ProbeTempComp::print_offsets() {
|
||||
LOOP_L_N(s, TSI_COUNT) {
|
||||
celsius_t temp = cali_info[s].start_temp;
|
||||
for (int16_t i = -1; i < cali_info[s].measurements; ++i) {
|
||||
SERIAL_ECHOPGM_P(s == TSI_BED ? PSTR("Bed") :
|
||||
#if ENABLED(USE_TEMP_EXT_COMPENSATION)
|
||||
s == TSI_EXT ? PSTR("Extruder") :
|
||||
#endif
|
||||
PSTR("Probe")
|
||||
SERIAL_ECHOF(
|
||||
TERN_(PTC_BED, s == TSI_BED ? F("Bed") :)
|
||||
TERN_(PTC_HOTEND, s == TSI_EXT ? F("Extruder") :)
|
||||
F("Probe")
|
||||
);
|
||||
SERIAL_ECHOLNPGM(
|
||||
" temp: ", temp,
|
||||
"C; Offset: ", i < 0 ? 0.0f : sensor_z_offsets[s][i], " um"
|
||||
);
|
||||
temp += cali_info[s].temp_res;
|
||||
temp += cali_info[s].temp_resolution;
|
||||
}
|
||||
}
|
||||
#if ENABLED(DEBUG_PTC)
|
||||
float meas[4] = { 0, 0, 0, 0 };
|
||||
compensate_measurement(TSI_PROBE, 27.5, meas[0]);
|
||||
compensate_measurement(TSI_PROBE, 32.5, meas[1]);
|
||||
compensate_measurement(TSI_PROBE, 77.5, meas[2]);
|
||||
compensate_measurement(TSI_PROBE, 82.5, meas[3]);
|
||||
SERIAL_ECHOLNPGM("DEBUG_PTC 27.5:", meas[0], " 32.5:", meas[1], " 77.5:", meas[2], " 82.5:", meas[3]);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ProbeTempComp::prepare_new_calibration(const_float_t init_meas_z) {
|
||||
@@ -90,28 +115,20 @@ void ProbeTempComp::prepare_new_calibration(const_float_t init_meas_z) {
|
||||
}
|
||||
|
||||
void ProbeTempComp::push_back_new_measurement(const TempSensorID tsi, const_float_t meas_z) {
|
||||
switch (tsi) {
|
||||
case TSI_PROBE:
|
||||
case TSI_BED:
|
||||
//case TSI_EXT:
|
||||
if (calib_idx >= cali_info[tsi].measurements) return;
|
||||
sensor_z_offsets[tsi][calib_idx++] = static_cast<int16_t>(meas_z * 1000.0f - init_measurement * 1000.0f);
|
||||
default: break;
|
||||
}
|
||||
if (calib_idx >= cali_info[tsi].measurements) return;
|
||||
sensor_z_offsets[tsi][calib_idx++] = static_cast<int16_t>((meas_z - init_measurement) * 1000.0f);
|
||||
}
|
||||
|
||||
bool ProbeTempComp::finish_calibration(const TempSensorID tsi) {
|
||||
if (tsi != TSI_PROBE && tsi != TSI_BED) return false;
|
||||
|
||||
if (calib_idx < 3) {
|
||||
SERIAL_ECHOLNPGM("!Insufficient measurements (min. 3).");
|
||||
if (!calib_idx) {
|
||||
SERIAL_ECHOLNPGM("!No measurements.");
|
||||
clear_offsets(tsi);
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t measurements = cali_info[tsi].measurements;
|
||||
const celsius_t start_temp = cali_info[tsi].start_temp,
|
||||
res_temp = cali_info[tsi].temp_res;
|
||||
res_temp = cali_info[tsi].temp_resolution;
|
||||
int16_t * const data = sensor_z_offsets[tsi];
|
||||
|
||||
// Extrapolate
|
||||
@@ -120,16 +137,15 @@ bool ProbeTempComp::finish_calibration(const TempSensorID tsi) {
|
||||
SERIAL_ECHOLNPGM("Got ", calib_idx, " measurements. ");
|
||||
if (linear_regression(tsi, k, d)) {
|
||||
SERIAL_ECHOPGM("Applying linear extrapolation");
|
||||
calib_idx--;
|
||||
for (; calib_idx < measurements; ++calib_idx) {
|
||||
const celsius_float_t temp = start_temp + float(calib_idx) * res_temp;
|
||||
const celsius_float_t temp = start_temp + float(calib_idx + 1) * res_temp;
|
||||
data[calib_idx] = static_cast<int16_t>(k * temp + d);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Simply use the last measured value for higher temperatures
|
||||
SERIAL_ECHOPGM("Failed to extrapolate");
|
||||
const int16_t last_val = data[calib_idx];
|
||||
const int16_t last_val = data[calib_idx-1];
|
||||
for (; calib_idx < measurements; ++calib_idx)
|
||||
data[calib_idx] = last_val;
|
||||
}
|
||||
@@ -147,7 +163,7 @@ bool ProbeTempComp::finish_calibration(const TempSensorID tsi) {
|
||||
// Restrict the max. offset difference between two probings
|
||||
if (calib_idx > 0 && ABS(data[calib_idx - 1] - data[calib_idx]) > 800) {
|
||||
SERIAL_ECHOLNPGM("!Invalid Z-offset between two probings detected (0-0.8).");
|
||||
clear_offsets(TSI_PROBE);
|
||||
clear_offsets(tsi);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -155,56 +171,60 @@ bool ProbeTempComp::finish_calibration(const TempSensorID tsi) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProbeTempComp::compensate_measurement(const TempSensorID tsi, const celsius_t temp, float &meas_z) {
|
||||
if (WITHIN(temp, cali_info[tsi].start_temp, cali_info[tsi].end_temp))
|
||||
meas_z -= get_offset_for_temperature(tsi, temp);
|
||||
void ProbeTempComp::apply_compensation(float &meas_z) {
|
||||
if (!enabled) return;
|
||||
TERN_(PTC_BED, compensate_measurement(TSI_BED, thermalManager.degBed(), meas_z));
|
||||
TERN_(PTC_PROBE, compensate_measurement(TSI_PROBE, thermalManager.degProbe(), meas_z));
|
||||
TERN_(PTC_HOTEND, compensate_measurement(TSI_EXT, thermalManager.degHotend(0), meas_z));
|
||||
}
|
||||
|
||||
float ProbeTempComp::get_offset_for_temperature(const TempSensorID tsi, const celsius_t temp) {
|
||||
void ProbeTempComp::compensate_measurement(const TempSensorID tsi, const celsius_t temp, float &meas_z) {
|
||||
const uint8_t measurements = cali_info[tsi].measurements;
|
||||
const celsius_t start_temp = cali_info[tsi].start_temp,
|
||||
res_temp = cali_info[tsi].temp_res;
|
||||
res_temp = cali_info[tsi].temp_resolution,
|
||||
end_temp = start_temp + measurements * res_temp;
|
||||
const int16_t * const data = sensor_z_offsets[tsi];
|
||||
|
||||
auto point = [&](uint8_t i) -> xy_float_t {
|
||||
return xy_float_t({ static_cast<float>(start_temp) + i * res_temp, static_cast<float>(data[i]) });
|
||||
// Given a data index, return { celsius, zoffset } in the form { x, y }
|
||||
auto tpoint = [&](uint8_t i) -> xy_float_t {
|
||||
return xy_float_t({ static_cast<float>(start_temp) + i * res_temp, i ? static_cast<float>(data[i - 1]) : 0.0f });
|
||||
};
|
||||
|
||||
// Interpolate Z based on a temperature being within a given range
|
||||
auto linear_interp = [](const_float_t x, xy_float_t p1, xy_float_t p2) {
|
||||
return (p2.y - p1.y) / (p2.x - p2.y) * (x - p1.x) + p1.y;
|
||||
// zoffs1 + zoffset_per_toffset * toffset
|
||||
return p1.y + (p2.y - p1.y) / (p2.x - p1.x) * (x - p1.x);
|
||||
};
|
||||
|
||||
// Linear interpolation
|
||||
uint8_t idx = static_cast<uint8_t>((temp - start_temp) / res_temp);
|
||||
|
||||
// offset in µm
|
||||
float offset = 0.0f;
|
||||
|
||||
#if !defined(PTC_LINEAR_EXTRAPOLATION) || PTC_LINEAR_EXTRAPOLATION <= 0
|
||||
if (idx < 0)
|
||||
offset = 0.0f;
|
||||
else if (idx > measurements - 2)
|
||||
offset = static_cast<float>(data[measurements - 1]);
|
||||
#if PTC_LINEAR_EXTRAPOLATION
|
||||
if (temp < start_temp)
|
||||
offset = linear_interp(temp, tpoint(0), tpoint(PTC_LINEAR_EXTRAPOLATION));
|
||||
else if (temp >= end_temp)
|
||||
offset = linear_interp(temp, tpoint(measurements - PTC_LINEAR_EXTRAPOLATION), tpoint(measurements));
|
||||
#else
|
||||
if (idx < 0)
|
||||
offset = linear_interp(temp, point(0), point(PTC_LINEAR_EXTRAPOLATION));
|
||||
else if (idx > measurements - 2)
|
||||
offset = linear_interp(temp, point(measurements - PTC_LINEAR_EXTRAPOLATION - 1), point(measurements - 1));
|
||||
if (temp < start_temp)
|
||||
offset = 0.0f;
|
||||
else if (temp >= end_temp)
|
||||
offset = static_cast<float>(data[measurements - 1]);
|
||||
#endif
|
||||
else
|
||||
offset = linear_interp(temp, point(idx), point(idx + 1));
|
||||
else {
|
||||
// Linear interpolation
|
||||
const int8_t idx = static_cast<int8_t>((temp - start_temp) / res_temp);
|
||||
offset = linear_interp(temp, tpoint(idx), tpoint(idx + 1));
|
||||
}
|
||||
|
||||
// return offset in mm
|
||||
return offset / 1000.0f;
|
||||
// convert offset to mm and apply it
|
||||
meas_z -= offset / 1000.0f;
|
||||
}
|
||||
|
||||
bool ProbeTempComp::linear_regression(const TempSensorID tsi, float &k, float &d) {
|
||||
if (tsi != TSI_PROBE && tsi != TSI_BED) return false;
|
||||
|
||||
if (!WITHIN(calib_idx, 2, cali_info[tsi].measurements)) return false;
|
||||
if (!WITHIN(calib_idx, 1, cali_info[tsi].measurements)) return false;
|
||||
|
||||
const celsius_t start_temp = cali_info[tsi].start_temp,
|
||||
res_temp = cali_info[tsi].temp_res;
|
||||
res_temp = cali_info[tsi].temp_resolution;
|
||||
const int16_t * const data = sensor_z_offsets[tsi];
|
||||
|
||||
float sum_x = start_temp,
|
||||
@@ -234,4 +254,4 @@ bool ProbeTempComp::linear_regression(const TempSensorID tsi, float &k, float &d
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // PROBE_TEMP_COMPENSATION
|
||||
#endif // HAS_PTC
|
||||
|
@@ -24,19 +24,22 @@
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
enum TempSensorID : uint8_t {
|
||||
TSI_PROBE,
|
||||
TSI_BED,
|
||||
#if ENABLED(USE_TEMP_EXT_COMPENSATION)
|
||||
#if ENABLED(PTC_PROBE)
|
||||
TSI_PROBE,
|
||||
#endif
|
||||
#if ENABLED(PTC_BED)
|
||||
TSI_BED,
|
||||
#endif
|
||||
#if ENABLED(PTC_HOTEND)
|
||||
TSI_EXT,
|
||||
#endif
|
||||
TSI_COUNT
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t measurements; // Max. number of measurements to be stored (35 - 80°C)
|
||||
celsius_t temp_res, // Resolution in °C between measurements
|
||||
start_temp, // Base measurement; z-offset == 0
|
||||
end_temp;
|
||||
uint8_t measurements; // Max. number of measurements to be stored (35 - 80°C)
|
||||
celsius_t temp_resolution, // Resolution in °C between measurements
|
||||
start_temp; // Base measurement; z-offset == 0
|
||||
} temp_calib_t;
|
||||
|
||||
/**
|
||||
@@ -45,89 +48,55 @@ typedef struct {
|
||||
* measurement errors/shifts due to changed temperature.
|
||||
*/
|
||||
|
||||
// Probe temperature calibration constants
|
||||
#ifndef PTC_SAMPLE_COUNT
|
||||
#define PTC_SAMPLE_COUNT 10
|
||||
#endif
|
||||
#ifndef PTC_SAMPLE_RES
|
||||
#define PTC_SAMPLE_RES 5
|
||||
#endif
|
||||
#ifndef PTC_SAMPLE_START
|
||||
#define PTC_SAMPLE_START 30
|
||||
#endif
|
||||
#define PTC_SAMPLE_END (PTC_SAMPLE_START + (PTC_SAMPLE_COUNT) * PTC_SAMPLE_RES)
|
||||
|
||||
// Bed temperature calibration constants
|
||||
#ifndef BTC_PROBE_TEMP
|
||||
#define BTC_PROBE_TEMP 30
|
||||
#endif
|
||||
#ifndef BTC_SAMPLE_COUNT
|
||||
#define BTC_SAMPLE_COUNT 10
|
||||
#endif
|
||||
#ifndef BTC_SAMPLE_RES
|
||||
#define BTC_SAMPLE_RES 5
|
||||
#endif
|
||||
#ifndef BTC_SAMPLE_START
|
||||
#define BTC_SAMPLE_START 60
|
||||
#endif
|
||||
#define BTC_SAMPLE_END (BTC_SAMPLE_START + (BTC_SAMPLE_COUNT) * BTC_SAMPLE_RES)
|
||||
|
||||
#ifndef PTC_PROBE_HEATING_OFFSET
|
||||
#define PTC_PROBE_HEATING_OFFSET 0.5f
|
||||
#endif
|
||||
|
||||
#ifndef PTC_PROBE_RAISE
|
||||
#define PTC_PROBE_RAISE 10
|
||||
#endif
|
||||
|
||||
static constexpr temp_calib_t cali_info_init[TSI_COUNT] = {
|
||||
{ PTC_SAMPLE_COUNT, PTC_SAMPLE_RES, PTC_SAMPLE_START, PTC_SAMPLE_END }, // Probe
|
||||
{ BTC_SAMPLE_COUNT, BTC_SAMPLE_RES, BTC_SAMPLE_START, BTC_SAMPLE_END }, // Bed
|
||||
#if ENABLED(USE_TEMP_EXT_COMPENSATION)
|
||||
{ 20, 5, 180, 180 + 5 * 20 } // Extruder
|
||||
#endif
|
||||
};
|
||||
|
||||
class ProbeTempComp {
|
||||
public:
|
||||
|
||||
static const temp_calib_t cali_info[TSI_COUNT];
|
||||
static constexpr temp_calib_t cali_info[TSI_COUNT] = {
|
||||
#if ENABLED(PTC_PROBE)
|
||||
{ PTC_PROBE_COUNT, PTC_PROBE_RES, PTC_PROBE_START }, // Probe
|
||||
#endif
|
||||
#if ENABLED(PTC_BED)
|
||||
{ PTC_BED_COUNT, PTC_BED_RES, PTC_BED_START }, // Bed
|
||||
#endif
|
||||
#if ENABLED(PTC_HOTEND)
|
||||
{ PTC_HOTEND_COUNT, PTC_HOTEND_RES, PTC_HOTEND_START }, // Extruder
|
||||
#endif
|
||||
};
|
||||
|
||||
// Where to park nozzle to wait for probe cooldown
|
||||
static constexpr xyz_pos_t park_point = PTC_PARK_POS;
|
||||
|
||||
// XY coordinates of nozzle for probing the bed
|
||||
static constexpr xy_pos_t measure_point = PTC_PROBE_POS; // Coordinates to probe
|
||||
//measure_point = { 12.0f, 7.3f }; // Coordinates for the MK52 magnetic heatbed
|
||||
|
||||
static constexpr celsius_t probe_calib_bed_temp = BED_MAX_TARGET, // Bed temperature while calibrating probe
|
||||
bed_calib_probe_temp = BTC_PROBE_TEMP; // Probe temperature while calibrating bed
|
||||
|
||||
static int16_t *sensor_z_offsets[TSI_COUNT],
|
||||
z_offsets_probe[cali_info_init[TSI_PROBE].measurements], // (µm)
|
||||
z_offsets_bed[cali_info_init[TSI_BED].measurements]; // (µm)
|
||||
|
||||
#if ENABLED(USE_TEMP_EXT_COMPENSATION)
|
||||
static int16_t z_offsets_ext[cali_info_init[TSI_EXT].measurements]; // (µm)
|
||||
static int16_t *sensor_z_offsets[TSI_COUNT];
|
||||
#if ENABLED(PTC_PROBE)
|
||||
static int16_t z_offsets_probe[PTC_PROBE_COUNT]; // (µm)
|
||||
#endif
|
||||
#if ENABLED(PTC_BED)
|
||||
static int16_t z_offsets_bed[PTC_BED_COUNT]; // (µm)
|
||||
#endif
|
||||
#if ENABLED(PTC_HOTEND)
|
||||
static int16_t z_offsets_hotend[PTC_HOTEND_COUNT]; // (µm)
|
||||
#endif
|
||||
|
||||
static inline void reset_index() { calib_idx = 0; };
|
||||
static inline uint8_t get_index() { return calib_idx; }
|
||||
static void clear_offsets(const TempSensorID tsi);
|
||||
static inline void clear_all_offsets() {
|
||||
clear_offsets(TSI_BED);
|
||||
clear_offsets(TSI_PROBE);
|
||||
TERN_(USE_TEMP_EXT_COMPENSATION, clear_offsets(TSI_EXT));
|
||||
static void reset_index() { calib_idx = 0; };
|
||||
static uint8_t get_index() { return calib_idx; }
|
||||
static void reset();
|
||||
static void clear_all_offsets() {
|
||||
TERN_(PTC_PROBE, clear_offsets(TSI_PROBE));
|
||||
TERN_(PTC_BED, clear_offsets(TSI_BED));
|
||||
TERN_(PTC_HOTEND, clear_offsets(TSI_EXT));
|
||||
}
|
||||
static bool set_offset(const TempSensorID tsi, const uint8_t idx, const int16_t offset);
|
||||
static void print_offsets();
|
||||
static void prepare_new_calibration(const_float_t init_meas_z);
|
||||
static void push_back_new_measurement(const TempSensorID tsi, const_float_t meas_z);
|
||||
static bool finish_calibration(const TempSensorID tsi);
|
||||
static void compensate_measurement(const TempSensorID tsi, const celsius_t temp, float &meas_z);
|
||||
static void set_enabled(const bool ena) { enabled = ena; }
|
||||
|
||||
// Apply all temperature compensation adjustments
|
||||
static void apply_compensation(float &meas_z);
|
||||
|
||||
private:
|
||||
static uint8_t calib_idx;
|
||||
static bool enabled;
|
||||
|
||||
static void clear_offsets(const TempSensorID tsi);
|
||||
|
||||
/**
|
||||
* Base value. Temperature compensation values will be deltas
|
||||
@@ -135,13 +104,13 @@ class ProbeTempComp {
|
||||
*/
|
||||
static float init_measurement;
|
||||
|
||||
static float get_offset_for_temperature(const TempSensorID tsi, const celsius_t temp);
|
||||
|
||||
/**
|
||||
* Fit a linear function in measured temperature offsets
|
||||
* to allow generating values of higher temperatures.
|
||||
*/
|
||||
static bool linear_regression(const TempSensorID tsi, float &k, float &d);
|
||||
|
||||
static void compensate_measurement(const TempSensorID tsi, const celsius_t temp, float &meas_z);
|
||||
};
|
||||
|
||||
extern ProbeTempComp temp_comp;
|
||||
extern ProbeTempComp ptc;
|
||||
|
@@ -38,8 +38,8 @@ private:
|
||||
static repeat_marker_t marker[MAX_REPEAT_NESTING];
|
||||
static uint8_t index;
|
||||
public:
|
||||
static inline void reset() { index = 0; }
|
||||
static inline bool is_active() {
|
||||
static void reset() { index = 0; }
|
||||
static bool is_active() {
|
||||
LOOP_L_N(i, index) if (marker[i].counter) return true;
|
||||
return false;
|
||||
}
|
||||
|
@@ -68,8 +68,8 @@ bool FilamentMonitorBase::enabled = true,
|
||||
|
||||
#if ENABLED(EXTENSIBLE_UI)
|
||||
#include "../lcd/extui/ui_api.h"
|
||||
#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED)
|
||||
#include "../lcd/e3v2/enhanced/dwin.h"
|
||||
#elif ENABLED(DWIN_LCD_PROUI)
|
||||
#include "../lcd/e3v2/proui/dwin.h"
|
||||
#endif
|
||||
|
||||
void event_filament_runout(const uint8_t extruder) {
|
||||
@@ -88,7 +88,7 @@ void event_filament_runout(const uint8_t extruder) {
|
||||
#endif
|
||||
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onFilamentRunout(ExtUI::getTool(extruder)));
|
||||
TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_FilamentRunout(extruder));
|
||||
TERN_(DWIN_LCD_PROUI, DWIN_FilamentRunout(extruder));
|
||||
|
||||
#if ANY(HOST_PROMPT_SUPPORT, HOST_ACTION_COMMANDS, MULTI_FILAMENT_SENSOR)
|
||||
const char tool = '0' + TERN0(MULTI_FILAMENT_SENSOR, extruder);
|
||||
@@ -96,8 +96,7 @@ void event_filament_runout(const uint8_t extruder) {
|
||||
|
||||
//action:out_of_filament
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
host_action_prompt_begin(PROMPT_FILAMENT_RUNOUT, PSTR("FilamentRunout T"), tool);
|
||||
host_action_prompt_show();
|
||||
hostui.prompt_do(PROMPT_FILAMENT_RUNOUT, F("FilamentRunout T"), tool); //action:out_of_filament
|
||||
#endif
|
||||
|
||||
const bool run_runout_script = !runout.host_handling;
|
||||
@@ -109,18 +108,18 @@ void event_filament_runout(const uint8_t extruder) {
|
||||
|| TERN0(ADVANCED_PAUSE_FEATURE, strstr(FILAMENT_RUNOUT_SCRIPT, "M25"))
|
||||
)
|
||||
) {
|
||||
host_action_paused(false);
|
||||
hostui.paused(false);
|
||||
}
|
||||
else {
|
||||
// Legacy Repetier command for use until newer version supports standard dialog
|
||||
// To be removed later when pause command also triggers dialog
|
||||
#ifdef ACTION_ON_FILAMENT_RUNOUT
|
||||
host_action(PSTR(ACTION_ON_FILAMENT_RUNOUT " T"), false);
|
||||
hostui.action(F(ACTION_ON_FILAMENT_RUNOUT " T"), false);
|
||||
SERIAL_CHAR(tool);
|
||||
SERIAL_EOL();
|
||||
#endif
|
||||
|
||||
host_action_pause(false);
|
||||
hostui.pause(false);
|
||||
}
|
||||
SERIAL_ECHOPGM(" " ACTION_REASON_ON_FILAMENT_RUNOUT " ");
|
||||
SERIAL_CHAR(tool);
|
||||
@@ -140,7 +139,7 @@ void event_filament_runout(const uint8_t extruder) {
|
||||
SERIAL_ECHOPGM("Runout Command: ");
|
||||
SERIAL_ECHOLNPGM(FILAMENT_RUNOUT_SCRIPT);
|
||||
#endif
|
||||
queue.inject_P(PSTR(FILAMENT_RUNOUT_SCRIPT));
|
||||
queue.inject(F(FILAMENT_RUNOUT_SCRIPT));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@@ -83,30 +83,30 @@ class TFilamentMonitor : public FilamentMonitorBase {
|
||||
static sensor_t sensor;
|
||||
|
||||
public:
|
||||
static inline void setup() {
|
||||
static void setup() {
|
||||
sensor.setup();
|
||||
reset();
|
||||
}
|
||||
|
||||
static inline void reset() {
|
||||
static void reset() {
|
||||
filament_ran_out = false;
|
||||
response.reset();
|
||||
}
|
||||
|
||||
// Call this method when filament is present,
|
||||
// so the response can reset its counter.
|
||||
static inline void filament_present(const uint8_t extruder) {
|
||||
static void filament_present(const uint8_t extruder) {
|
||||
response.filament_present(extruder);
|
||||
}
|
||||
|
||||
#if HAS_FILAMENT_RUNOUT_DISTANCE
|
||||
static inline float& runout_distance() { return response.runout_distance_mm; }
|
||||
static inline void set_runout_distance(const_float_t mm) { response.runout_distance_mm = mm; }
|
||||
static float& runout_distance() { return response.runout_distance_mm; }
|
||||
static void set_runout_distance(const_float_t mm) { response.runout_distance_mm = mm; }
|
||||
#endif
|
||||
|
||||
// Handle a block completion. RunoutResponseDelayed uses this to
|
||||
// add up the length of filament moved while the filament is out.
|
||||
static inline void block_completed(const block_t * const b) {
|
||||
static void block_completed(const block_t * const b) {
|
||||
if (enabled) {
|
||||
response.block_completed(b);
|
||||
sensor.block_completed(b);
|
||||
@@ -114,7 +114,7 @@ class TFilamentMonitor : public FilamentMonitorBase {
|
||||
}
|
||||
|
||||
// Give the response a chance to update its counter.
|
||||
static inline void run() {
|
||||
static void run() {
|
||||
if (enabled && !filament_ran_out && (printingIsActive() || did_pause_print)) {
|
||||
TERN_(HAS_FILAMENT_RUNOUT_DISTANCE, cli()); // Prevent RunoutResponseDelayed::block_completed from accumulating here
|
||||
response.run();
|
||||
@@ -168,12 +168,12 @@ class FilamentSensorBase {
|
||||
* Called by FilamentSensorSwitch::run when filament is detected.
|
||||
* Called by FilamentSensorEncoder::block_completed when motion is detected.
|
||||
*/
|
||||
static inline void filament_present(const uint8_t extruder) {
|
||||
static void filament_present(const uint8_t extruder) {
|
||||
runout.filament_present(extruder); // ...which calls response.filament_present(extruder)
|
||||
}
|
||||
|
||||
public:
|
||||
static inline void setup() {
|
||||
static void setup() {
|
||||
#define _INIT_RUNOUT_PIN(P,S,U,D) do{ if (ENABLED(U)) SET_INPUT_PULLUP(P); else if (ENABLED(D)) SET_INPUT_PULLDOWN(P); else SET_INPUT(P); }while(0)
|
||||
#define INIT_RUNOUT_PIN(N) _INIT_RUNOUT_PIN(FIL_RUNOUT##N##_PIN, FIL_RUNOUT##N##_STATE, FIL_RUNOUT##N##_PULLUP, FIL_RUNOUT##N##_PULLDOWN)
|
||||
#if NUM_RUNOUT_SENSORS >= 1
|
||||
@@ -205,14 +205,14 @@ class FilamentSensorBase {
|
||||
}
|
||||
|
||||
// Return a bitmask of runout pin states
|
||||
static inline uint8_t poll_runout_pins() {
|
||||
static uint8_t poll_runout_pins() {
|
||||
#define _OR_RUNOUT(N) | (READ(FIL_RUNOUT##N##_PIN) ? _BV((N) - 1) : 0)
|
||||
return (0 REPEAT_1(NUM_RUNOUT_SENSORS, _OR_RUNOUT));
|
||||
#undef _OR_RUNOUT
|
||||
}
|
||||
|
||||
// Return a bitmask of runout flag states (1 bits always indicates runout)
|
||||
static inline uint8_t poll_runout_states() {
|
||||
static uint8_t poll_runout_states() {
|
||||
return poll_runout_pins() ^ uint8_t(0
|
||||
#if NUM_RUNOUT_SENSORS >= 1
|
||||
| (FIL_RUNOUT1_STATE ? 0 : _BV(1 - 1))
|
||||
@@ -254,7 +254,7 @@ class FilamentSensorBase {
|
||||
private:
|
||||
static uint8_t motion_detected;
|
||||
|
||||
static inline void poll_motion_sensor() {
|
||||
static void poll_motion_sensor() {
|
||||
static uint8_t old_state;
|
||||
const uint8_t new_state = poll_runout_pins(),
|
||||
change = old_state ^ new_state;
|
||||
@@ -273,7 +273,7 @@ class FilamentSensorBase {
|
||||
}
|
||||
|
||||
public:
|
||||
static inline void block_completed(const block_t * const b) {
|
||||
static void block_completed(const block_t * const b) {
|
||||
// If the sensor wheel has moved since the last call to
|
||||
// this method reset the runout counter for the extruder.
|
||||
if (TEST(motion_detected, b->extruder))
|
||||
@@ -283,7 +283,7 @@ class FilamentSensorBase {
|
||||
motion_detected = 0;
|
||||
}
|
||||
|
||||
static inline void run() { poll_motion_sensor(); }
|
||||
static void run() { poll_motion_sensor(); }
|
||||
};
|
||||
|
||||
#else
|
||||
@@ -294,7 +294,7 @@ class FilamentSensorBase {
|
||||
*/
|
||||
class FilamentSensorSwitch : public FilamentSensorBase {
|
||||
private:
|
||||
static inline bool poll_runout_state(const uint8_t extruder) {
|
||||
static bool poll_runout_state(const uint8_t extruder) {
|
||||
const uint8_t runout_states = poll_runout_states();
|
||||
#if MULTI_FILAMENT_SENSOR
|
||||
if ( !TERN0(DUAL_X_CARRIAGE, idex_is_duplicating())
|
||||
@@ -307,9 +307,9 @@ class FilamentSensorBase {
|
||||
}
|
||||
|
||||
public:
|
||||
static inline void block_completed(const block_t * const) {}
|
||||
static void block_completed(const block_t * const) {}
|
||||
|
||||
static inline void run() {
|
||||
static void run() {
|
||||
LOOP_L_N(s, NUM_RUNOUT_SENSORS) {
|
||||
const bool out = poll_runout_state(s);
|
||||
if (!out) filament_present(s);
|
||||
@@ -317,7 +317,7 @@ class FilamentSensorBase {
|
||||
static uint8_t was_out; // = 0
|
||||
if (out != TEST(was_out, s)) {
|
||||
TBI(was_out, s);
|
||||
SERIAL_ECHOLNPGM_P(PSTR("Filament Sensor "), '0' + s, out ? PSTR(" OUT") : PSTR(" IN"));
|
||||
SERIAL_ECHOLNF(F("Filament Sensor "), AS_DIGIT(s), out ? F(" OUT") : F(" IN"));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -341,34 +341,34 @@ class FilamentSensorBase {
|
||||
public:
|
||||
static float runout_distance_mm;
|
||||
|
||||
static inline void reset() {
|
||||
static void reset() {
|
||||
LOOP_L_N(i, NUM_RUNOUT_SENSORS) filament_present(i);
|
||||
}
|
||||
|
||||
static inline void run() {
|
||||
static void run() {
|
||||
#if ENABLED(FILAMENT_RUNOUT_SENSOR_DEBUG)
|
||||
static millis_t t = 0;
|
||||
const millis_t ms = millis();
|
||||
if (ELAPSED(ms, t)) {
|
||||
t = millis() + 1000UL;
|
||||
LOOP_L_N(i, NUM_RUNOUT_SENSORS)
|
||||
SERIAL_ECHOPGM_P(i ? PSTR(", ") : PSTR("Remaining mm: "), runout_mm_countdown[i]);
|
||||
SERIAL_ECHOF(i ? F(", ") : F("Remaining mm: "), runout_mm_countdown[i]);
|
||||
SERIAL_EOL();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint8_t has_run_out() {
|
||||
static uint8_t has_run_out() {
|
||||
uint8_t runout_flags = 0;
|
||||
LOOP_L_N(i, NUM_RUNOUT_SENSORS) if (runout_mm_countdown[i] < 0) SBI(runout_flags, i);
|
||||
return runout_flags;
|
||||
}
|
||||
|
||||
static inline void filament_present(const uint8_t extruder) {
|
||||
static void filament_present(const uint8_t extruder) {
|
||||
runout_mm_countdown[extruder] = runout_distance_mm;
|
||||
}
|
||||
|
||||
static inline void block_completed(const block_t * const b) {
|
||||
static void block_completed(const block_t * const b) {
|
||||
if (b->steps.x || b->steps.y || b->steps.z || did_pause_print) { // Allow pause purge move to re-trigger runout state
|
||||
// Only trigger on extrusion with XYZ movement to allow filament change and retract/recover.
|
||||
const uint8_t e = b->extruder;
|
||||
@@ -389,23 +389,23 @@ class FilamentSensorBase {
|
||||
static int8_t runout_count[NUM_RUNOUT_SENSORS];
|
||||
|
||||
public:
|
||||
static inline void reset() {
|
||||
static void reset() {
|
||||
LOOP_L_N(i, NUM_RUNOUT_SENSORS) filament_present(i);
|
||||
}
|
||||
|
||||
static inline void run() {
|
||||
static void run() {
|
||||
LOOP_L_N(i, NUM_RUNOUT_SENSORS) if (runout_count[i] >= 0) runout_count[i]--;
|
||||
}
|
||||
|
||||
static inline uint8_t has_run_out() {
|
||||
static uint8_t has_run_out() {
|
||||
uint8_t runout_flags = 0;
|
||||
LOOP_L_N(i, NUM_RUNOUT_SENSORS) if (runout_count[i] < 0) SBI(runout_flags, i);
|
||||
return runout_flags;
|
||||
}
|
||||
|
||||
static inline void block_completed(const block_t * const) { }
|
||||
static void block_completed(const block_t * const) { }
|
||||
|
||||
static inline void filament_present(const uint8_t extruder) {
|
||||
static void filament_present(const uint8_t extruder) {
|
||||
runout_count[extruder] = runout_threshold;
|
||||
}
|
||||
};
|
||||
|
@@ -27,65 +27,29 @@
|
||||
#include "solenoid.h"
|
||||
|
||||
#include "../module/motion.h" // for active_extruder
|
||||
|
||||
// PARKING_EXTRUDER options alter the default behavior of solenoids, this ensures compliance of M380-381
|
||||
|
||||
#if ENABLED(PARKING_EXTRUDER)
|
||||
#include "../module/tool_change.h"
|
||||
#endif
|
||||
|
||||
#define HAS_SOLENOID(N) (HAS_SOLENOID_##N && (ENABLED(MANUAL_SOLENOID_CONTROL) || N < EXTRUDERS))
|
||||
#include "../module/tool_change.h"
|
||||
|
||||
// Used primarily with MANUAL_SOLENOID_CONTROL
|
||||
static void set_solenoid(const uint8_t num, const bool active) {
|
||||
const uint8_t value = active ? PE_MAGNET_ON_STATE : !PE_MAGNET_ON_STATE;
|
||||
static void set_solenoid(const uint8_t num, const uint8_t state) {
|
||||
#define _SOL_CASE(N) case N: TERN_(HAS_SOLENOID_##N, OUT_WRITE(SOL##N##_PIN, state)); break;
|
||||
switch (num) {
|
||||
case 0: OUT_WRITE(SOL0_PIN, value); break;
|
||||
#if HAS_SOLENOID(1)
|
||||
case 1: OUT_WRITE(SOL1_PIN, value); break;
|
||||
#endif
|
||||
#if HAS_SOLENOID(2)
|
||||
case 2: OUT_WRITE(SOL2_PIN, value); break;
|
||||
#endif
|
||||
#if HAS_SOLENOID(3)
|
||||
case 3: OUT_WRITE(SOL3_PIN, value); break;
|
||||
#endif
|
||||
#if HAS_SOLENOID(4)
|
||||
case 4: OUT_WRITE(SOL4_PIN, value); break;
|
||||
#endif
|
||||
#if HAS_SOLENOID(5)
|
||||
case 5: OUT_WRITE(SOL5_PIN, value); break;
|
||||
#endif
|
||||
REPEAT(8, _SOL_CASE)
|
||||
default: SERIAL_ECHO_MSG(STR_INVALID_SOLENOID); break;
|
||||
}
|
||||
|
||||
#if ENABLED(PARKING_EXTRUDER)
|
||||
if (!active && active_extruder == num) // If active extruder's solenoid is disabled, carriage is considered parked
|
||||
if (state == LOW && active_extruder == num) // If active extruder's solenoid is disabled, carriage is considered parked
|
||||
parking_extruder_set_parked(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
void enable_solenoid(const uint8_t num) { set_solenoid(num, true); }
|
||||
void disable_solenoid(const uint8_t num) { set_solenoid(num, false); }
|
||||
void enable_solenoid_on_active_extruder() { enable_solenoid(active_extruder); }
|
||||
// PARKING_EXTRUDER options alter the default behavior of solenoids to ensure compliance of M380-381
|
||||
void enable_solenoid(const uint8_t num) { set_solenoid(num, TERN1(PARKING_EXTRUDER, PE_MAGNET_ON_STATE)); }
|
||||
void disable_solenoid(const uint8_t num) { set_solenoid(num, TERN0(PARKING_EXTRUDER, !PE_MAGNET_ON_STATE)); }
|
||||
|
||||
void disable_all_solenoids() {
|
||||
disable_solenoid(0);
|
||||
#if HAS_SOLENOID(1)
|
||||
disable_solenoid(1);
|
||||
#endif
|
||||
#if HAS_SOLENOID(2)
|
||||
disable_solenoid(2);
|
||||
#endif
|
||||
#if HAS_SOLENOID(3)
|
||||
disable_solenoid(3);
|
||||
#endif
|
||||
#if HAS_SOLENOID(4)
|
||||
disable_solenoid(4);
|
||||
#endif
|
||||
#if HAS_SOLENOID(5)
|
||||
disable_solenoid(5);
|
||||
#endif
|
||||
#define _SOL_DISABLE(N) TERN_(HAS_SOLENOID_##N, disable_solenoid(N));
|
||||
REPEAT(8, _SOL_DISABLE)
|
||||
}
|
||||
|
||||
#endif // EXT_SOLENOID || MANUAL_SOLENOID_CONTROL
|
||||
|
@@ -21,7 +21,6 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
void enable_solenoid_on_active_extruder();
|
||||
void disable_all_solenoids();
|
||||
void enable_solenoid(const uint8_t num);
|
||||
void disable_solenoid(const uint8_t num);
|
||||
|
@@ -39,17 +39,26 @@
|
||||
#endif
|
||||
|
||||
SpindleLaser cutter;
|
||||
uint8_t SpindleLaser::power;
|
||||
#if ENABLED(LASER_FEATURE)
|
||||
cutter_test_pulse_t SpindleLaser::testPulse = 50; // Test fire Pulse time ms value.
|
||||
#endif
|
||||
bool SpindleLaser::isReady; // Ready to apply power setting from the UI to OCR
|
||||
cutter_power_t SpindleLaser::menuPower, // Power set via LCD menu in PWM, PERCENT, or RPM
|
||||
SpindleLaser::unitPower; // LCD status power in PWM, PERCENT, or RPM
|
||||
bool SpindleLaser::enable_state; // Virtual enable state, controls enable pin if present and or apply power if > 0
|
||||
uint8_t SpindleLaser::power, // Actual power output 0-255 ocr or "0 = off" > 0 = "on"
|
||||
SpindleLaser::last_power_applied; // = 0 // Basic power state tracking
|
||||
|
||||
#if ENABLED(MARLIN_DEV_MODE)
|
||||
cutter_frequency_t SpindleLaser::frequency; // PWM frequency setting; range: 2K - 50K
|
||||
#if ENABLED(LASER_FEATURE)
|
||||
cutter_test_pulse_t SpindleLaser::testPulse = 50; // (ms) Test fire pulse default duration
|
||||
uint8_t SpindleLaser::last_block_power; // = 0 // Track power changes for dynamic inline power
|
||||
feedRate_t SpindleLaser::feedrate_mm_m = 1500,
|
||||
SpindleLaser::last_feedrate_mm_m; // = 0 // (mm/min) Track feedrate changes for dynamic power
|
||||
#endif
|
||||
|
||||
bool SpindleLaser::isReadyForUI = false; // Ready to apply power setting from the UI to OCR
|
||||
CutterMode SpindleLaser::cutter_mode = CUTTER_MODE_STANDARD; // Default is standard mode
|
||||
|
||||
constexpr cutter_cpower_t SpindleLaser::power_floor;
|
||||
cutter_power_t SpindleLaser::menuPower = 0, // Power value via LCD menu in PWM, PERCENT, or RPM based on configured format set by CUTTER_POWER_UNIT.
|
||||
SpindleLaser::unitPower = 0; // Unit power is in PWM, PERCENT, or RPM based on CUTTER_POWER_UNIT.
|
||||
|
||||
cutter_frequency_t SpindleLaser::frequency; // PWM frequency setting; range: 2K - 50K
|
||||
|
||||
#define SPINDLE_LASER_PWM_OFF TERN(SPINDLE_LASER_PWM_INVERT, 255, 0)
|
||||
|
||||
/**
|
||||
@@ -57,20 +66,20 @@ cutter_power_t SpindleLaser::menuPower, // Power s
|
||||
*/
|
||||
void SpindleLaser::init() {
|
||||
#if ENABLED(SPINDLE_SERVO)
|
||||
MOVE_SERVO(SPINDLE_SERVO_NR, SPINDLE_SERVO_MIN);
|
||||
#else
|
||||
servo[SPINDLE_SERVO_NR].move(SPINDLE_SERVO_MIN);
|
||||
#elif PIN_EXISTS(SPINDLE_LASER_ENA)
|
||||
OUT_WRITE(SPINDLE_LASER_ENA_PIN, !SPINDLE_LASER_ACTIVE_STATE); // Init spindle to off
|
||||
#endif
|
||||
#if ENABLED(SPINDLE_CHANGE_DIR)
|
||||
OUT_WRITE(SPINDLE_DIR_PIN, SPINDLE_INVERT_DIR ? 255 : 0); // Init rotation to clockwise (M3)
|
||||
OUT_WRITE(SPINDLE_DIR_PIN, SPINDLE_INVERT_DIR); // Init rotation to clockwise (M3)
|
||||
#endif
|
||||
#if ENABLED(HAL_CAN_SET_PWM_FREQ) && SPINDLE_LASER_FREQUENCY
|
||||
frequency = SPINDLE_LASER_FREQUENCY;
|
||||
hal.set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), SPINDLE_LASER_FREQUENCY);
|
||||
#endif
|
||||
#if ENABLED(SPINDLE_LASER_USE_PWM)
|
||||
SET_PWM(SPINDLE_LASER_PWM_PIN);
|
||||
analogWrite(pin_t(SPINDLE_LASER_PWM_PIN), SPINDLE_LASER_PWM_OFF); // Set to lowest speed
|
||||
#endif
|
||||
#if ENABLED(HAL_CAN_SET_PWM_FREQ) && defined(SPINDLE_LASER_FREQUENCY)
|
||||
set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), SPINDLE_LASER_FREQUENCY);
|
||||
TERN_(MARLIN_DEV_MODE, frequency = SPINDLE_LASER_FREQUENCY);
|
||||
hal.set_pwm_duty(pin_t(SPINDLE_LASER_PWM_PIN), SPINDLE_LASER_PWM_OFF); // Set to lowest speed
|
||||
#endif
|
||||
#if ENABLED(AIR_EVACUATION)
|
||||
OUT_WRITE(AIR_EVACUATION_PIN, !AIR_EVACUATION_ACTIVE); // Init Vacuum/Blower OFF
|
||||
@@ -78,9 +87,7 @@ void SpindleLaser::init() {
|
||||
#if ENABLED(AIR_ASSIST)
|
||||
OUT_WRITE(AIR_ASSIST_PIN, !AIR_ASSIST_ACTIVE); // Init Air Assist OFF
|
||||
#endif
|
||||
#if ENABLED(I2C_AMMETER)
|
||||
ammeter.init(); // Init I2C Ammeter
|
||||
#endif
|
||||
TERN_(I2C_AMMETER, ammeter.init()); // Init I2C Ammeter
|
||||
}
|
||||
|
||||
#if ENABLED(SPINDLE_LASER_USE_PWM)
|
||||
@@ -90,56 +97,63 @@ void SpindleLaser::init() {
|
||||
* @param ocr Power value
|
||||
*/
|
||||
void SpindleLaser::_set_ocr(const uint8_t ocr) {
|
||||
#if NEEDS_HARDWARE_PWM && SPINDLE_LASER_FREQUENCY
|
||||
set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), TERN(MARLIN_DEV_MODE, frequency, SPINDLE_LASER_FREQUENCY));
|
||||
set_pwm_duty(pin_t(SPINDLE_LASER_PWM_PIN), ocr ^ SPINDLE_LASER_PWM_OFF);
|
||||
#else
|
||||
analogWrite(pin_t(SPINDLE_LASER_PWM_PIN), ocr ^ SPINDLE_LASER_PWM_OFF);
|
||||
#if ENABLED(HAL_CAN_SET_PWM_FREQ) && SPINDLE_LASER_FREQUENCY
|
||||
hal.set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), frequency);
|
||||
#endif
|
||||
hal.set_pwm_duty(pin_t(SPINDLE_LASER_PWM_PIN), ocr ^ SPINDLE_LASER_PWM_OFF);
|
||||
}
|
||||
|
||||
void SpindleLaser::set_ocr(const uint8_t ocr) {
|
||||
WRITE(SPINDLE_LASER_ENA_PIN, SPINDLE_LASER_ACTIVE_STATE); // Cutter ON
|
||||
#if PIN_EXISTS(SPINDLE_LASER_ENA)
|
||||
WRITE(SPINDLE_LASER_ENA_PIN, SPINDLE_LASER_ACTIVE_STATE); // Cutter ON
|
||||
#endif
|
||||
_set_ocr(ocr);
|
||||
}
|
||||
|
||||
void SpindleLaser::ocr_off() {
|
||||
WRITE(SPINDLE_LASER_ENA_PIN, !SPINDLE_LASER_ACTIVE_STATE); // Cutter OFF
|
||||
#if PIN_EXISTS(SPINDLE_LASER_ENA)
|
||||
WRITE(SPINDLE_LASER_ENA_PIN, !SPINDLE_LASER_ACTIVE_STATE); // Cutter OFF
|
||||
#endif
|
||||
_set_ocr(0);
|
||||
}
|
||||
#endif // SPINDLE_LASER_USE_PWM
|
||||
|
||||
/**
|
||||
* Apply power for laser/spindle
|
||||
* Apply power for Laser or Spindle
|
||||
*
|
||||
* Apply cutter power value for PWM, Servo, and on/off pin.
|
||||
*
|
||||
* @param opwr Power value. Range 0 to MAX. When 0 disable spindle/laser.
|
||||
* @param opwr Power value. Range 0 to MAX.
|
||||
*/
|
||||
void SpindleLaser::apply_power(const uint8_t opwr) {
|
||||
static uint8_t last_power_applied = 0;
|
||||
if (opwr == last_power_applied) return;
|
||||
last_power_applied = opwr;
|
||||
power = opwr;
|
||||
#if ENABLED(SPINDLE_LASER_USE_PWM)
|
||||
if (cutter.unitPower == 0 && CUTTER_UNIT_IS(RPM)) {
|
||||
ocr_off();
|
||||
isReady = false;
|
||||
}
|
||||
else if (ENABLED(CUTTER_POWER_RELATIVE) || enabled()) {
|
||||
set_ocr(power);
|
||||
isReady = true;
|
||||
}
|
||||
else {
|
||||
ocr_off();
|
||||
isReady = false;
|
||||
}
|
||||
#elif ENABLED(SPINDLE_SERVO)
|
||||
MOVE_SERVO(SPINDLE_SERVO_NR, power);
|
||||
#else
|
||||
WRITE(SPINDLE_LASER_ENA_PIN, enabled() ? SPINDLE_LASER_ACTIVE_STATE : !SPINDLE_LASER_ACTIVE_STATE);
|
||||
isReady = true;
|
||||
#endif
|
||||
if (enabled() || opwr == 0) { // 0 check allows us to disable where no ENA pin exists
|
||||
// Test and set the last power used to improve performance
|
||||
if (opwr == last_power_applied) return;
|
||||
last_power_applied = opwr;
|
||||
// Handle PWM driven or just simple on/off
|
||||
#if ENABLED(SPINDLE_LASER_USE_PWM)
|
||||
if (CUTTER_UNIT_IS(RPM) && unitPower == 0)
|
||||
ocr_off();
|
||||
else if (ENABLED(CUTTER_POWER_RELATIVE) || enabled() || opwr == 0) {
|
||||
set_ocr(opwr);
|
||||
isReadyForUI = true;
|
||||
}
|
||||
else
|
||||
ocr_off();
|
||||
#elif ENABLED(SPINDLE_SERVO)
|
||||
MOVE_SERVO(SPINDLE_SERVO_NR, power);
|
||||
#else
|
||||
WRITE(SPINDLE_LASER_ENA_PIN, enabled() ? SPINDLE_LASER_ACTIVE_STATE : !SPINDLE_LASER_ACTIVE_STATE);
|
||||
isReadyForUI = true;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
#if PIN_EXISTS(SPINDLE_LASER_ENA)
|
||||
WRITE(SPINDLE_LASER_ENA_PIN, !SPINDLE_LASER_ACTIVE_STATE);
|
||||
#endif
|
||||
isReadyForUI = false; // Only used for UI display updates.
|
||||
TERN_(SPINDLE_LASER_USE_PWM, ocr_off());
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLED(SPINDLE_CHANGE_DIR)
|
||||
@@ -163,8 +177,8 @@ void SpindleLaser::apply_power(const uint8_t opwr) {
|
||||
|
||||
#if ENABLED(AIR_ASSIST)
|
||||
// Enable / disable air assist
|
||||
void SpindleLaser::air_assist_enable() { WRITE(AIR_ASSIST_PIN, AIR_ASSIST_PIN); } // Turn ON
|
||||
void SpindleLaser::air_assist_disable() { WRITE(AIR_ASSIST_PIN, !AIR_ASSIST_PIN); } // Turn OFF
|
||||
void SpindleLaser::air_assist_enable() { WRITE(AIR_ASSIST_PIN, AIR_ASSIST_ACTIVE); } // Turn ON
|
||||
void SpindleLaser::air_assist_disable() { WRITE(AIR_ASSIST_PIN, !AIR_ASSIST_ACTIVE); } // Turn OFF
|
||||
void SpindleLaser::air_assist_toggle() { TOGGLE(AIR_ASSIST_PIN); } // Toggle state
|
||||
#endif
|
||||
|
||||
|
@@ -30,98 +30,102 @@
|
||||
|
||||
#include "spindle_laser_types.h"
|
||||
|
||||
#if USE_BEEPER
|
||||
#if HAS_BEEPER
|
||||
#include "../libs/buzzer.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(LASER_POWER_INLINE)
|
||||
#include "../module/planner.h"
|
||||
#endif
|
||||
// Inline laser power
|
||||
#include "../module/planner.h"
|
||||
|
||||
#define PCT_TO_PWM(X) ((X) * 255 / 100)
|
||||
#define PCT_TO_SERVO(X) ((X) * 180 / 100)
|
||||
|
||||
#ifndef SPEED_POWER_INTERCEPT
|
||||
#define SPEED_POWER_INTERCEPT 0
|
||||
#endif
|
||||
|
||||
// #define _MAP(N,S1,S2,D1,D2) ((N)*_MAX((D2)-(D1),0)/_MAX((S2)-(S1),1)+(D1))
|
||||
// Laser/Cutter operation mode
|
||||
enum CutterMode : int8_t {
|
||||
CUTTER_MODE_ERROR = -1,
|
||||
CUTTER_MODE_STANDARD, // M3 power is applied directly and waits for planner moves to sync.
|
||||
CUTTER_MODE_CONTINUOUS, // M3 or G1/2/3 move power is controlled within planner blocks, set with 'M3 I', cleared with 'M5 I'.
|
||||
CUTTER_MODE_DYNAMIC // M4 laser power is proportional to the feed rate, set with 'M4 I', cleared with 'M5 I'.
|
||||
};
|
||||
|
||||
class SpindleLaser {
|
||||
public:
|
||||
static constexpr float
|
||||
min_pct = TERN(CUTTER_POWER_RELATIVE, 0, TERN(SPINDLE_FEATURE, round(100.0f * (SPEED_POWER_MIN) / (SPEED_POWER_MAX)), SPEED_POWER_MIN)),
|
||||
max_pct = TERN(SPINDLE_FEATURE, 100, SPEED_POWER_MAX);
|
||||
static CutterMode cutter_mode;
|
||||
|
||||
static const inline uint8_t pct_to_ocr(const_float_t pct) { return uint8_t(PCT_TO_PWM(pct)); }
|
||||
static constexpr uint8_t pct_to_ocr(const_float_t pct) { return uint8_t(PCT_TO_PWM(pct)); }
|
||||
|
||||
// cpower = configured values (e.g., SPEED_POWER_MAX)
|
||||
|
||||
// Convert configured power range to a percentage
|
||||
static const inline uint8_t cpwr_to_pct(const cutter_cpower_t cpwr) {
|
||||
constexpr cutter_cpower_t power_floor = TERN(CUTTER_POWER_RELATIVE, SPEED_POWER_MIN, 0),
|
||||
power_range = SPEED_POWER_MAX - power_floor;
|
||||
return cpwr ? round(100.0f * (cpwr - power_floor) / power_range) : 0;
|
||||
static constexpr cutter_cpower_t power_floor = TERN(CUTTER_POWER_RELATIVE, SPEED_POWER_MIN, 0);
|
||||
static constexpr uint8_t cpwr_to_pct(const cutter_cpower_t cpwr) {
|
||||
return cpwr ? round(100.0f * (cpwr - power_floor) / (SPEED_POWER_MAX - power_floor)) : 0;
|
||||
}
|
||||
|
||||
// Convert a cpower (e.g., SPEED_POWER_STARTUP) to unit power (upwr, upower),
|
||||
// which can be PWM, Percent, Servo angle, or RPM (rel/abs).
|
||||
static const inline cutter_power_t cpwr_to_upwr(const cutter_cpower_t cpwr) { // STARTUP power to Unit power
|
||||
const cutter_power_t upwr = (
|
||||
// Convert config defines from RPM to %, angle or PWM when in Spindle mode
|
||||
// and convert from PERCENT to PWM when in Laser mode
|
||||
static constexpr cutter_power_t cpwr_to_upwr(const cutter_cpower_t cpwr) { // STARTUP power to Unit power
|
||||
return (
|
||||
#if ENABLED(SPINDLE_FEATURE)
|
||||
// Spindle configured values are in RPM
|
||||
// Spindle configured define values are in RPM
|
||||
#if CUTTER_UNIT_IS(RPM)
|
||||
cpwr // to RPM
|
||||
#elif CUTTER_UNIT_IS(PERCENT) // to PCT
|
||||
cpwr_to_pct(cpwr)
|
||||
#elif CUTTER_UNIT_IS(SERVO) // to SERVO angle
|
||||
PCT_TO_SERVO(cpwr_to_pct(cpwr))
|
||||
#else // to PWM
|
||||
PCT_TO_PWM(cpwr_to_pct(cpwr))
|
||||
cpwr // to same
|
||||
#elif CUTTER_UNIT_IS(PERCENT)
|
||||
cpwr_to_pct(cpwr) // to Percent
|
||||
#elif CUTTER_UNIT_IS(SERVO)
|
||||
PCT_TO_SERVO(cpwr_to_pct(cpwr)) // to SERVO angle
|
||||
#else
|
||||
PCT_TO_PWM(cpwr_to_pct(cpwr)) // to PWM
|
||||
#endif
|
||||
#else
|
||||
// Laser configured values are in PCT
|
||||
// Laser configured define values are in Percent
|
||||
#if CUTTER_UNIT_IS(PWM255)
|
||||
PCT_TO_PWM(cpwr)
|
||||
PCT_TO_PWM(cpwr) // to PWM
|
||||
#else
|
||||
cpwr // to RPM/PCT
|
||||
cpwr // to same
|
||||
#endif
|
||||
#endif
|
||||
);
|
||||
return upwr;
|
||||
}
|
||||
|
||||
static const cutter_power_t mpower_min() { return cpwr_to_upwr(SPEED_POWER_MIN); }
|
||||
static const cutter_power_t mpower_max() { return cpwr_to_upwr(SPEED_POWER_MAX); }
|
||||
static constexpr cutter_power_t mpower_min() { return cpwr_to_upwr(SPEED_POWER_MIN); }
|
||||
static constexpr cutter_power_t mpower_max() { return cpwr_to_upwr(SPEED_POWER_MAX); }
|
||||
|
||||
#if ENABLED(LASER_FEATURE)
|
||||
static cutter_test_pulse_t testPulse; // Test fire Pulse ms value
|
||||
static cutter_test_pulse_t testPulse; // (ms) Test fire pulse duration
|
||||
static uint8_t last_block_power; // Track power changes for dynamic power
|
||||
|
||||
static feedRate_t feedrate_mm_m, last_feedrate_mm_m; // (mm/min) Track feedrate changes for dynamic power
|
||||
static bool laser_feedrate_changed() {
|
||||
const bool changed = last_feedrate_mm_m != feedrate_mm_m;
|
||||
if (changed) last_feedrate_mm_m = feedrate_mm_m;
|
||||
return changed;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool isReady; // Ready to apply power setting from the UI to OCR
|
||||
static uint8_t power;
|
||||
static bool isReadyForUI; // Ready to apply power setting from the UI to OCR
|
||||
static bool enable_state;
|
||||
static uint8_t power,
|
||||
last_power_applied; // Basic power state tracking
|
||||
|
||||
#if ENABLED(MARLIN_DEV_MODE)
|
||||
static cutter_frequency_t frequency; // Set PWM frequency; range: 2K-50K
|
||||
#endif
|
||||
static cutter_frequency_t frequency; // Set PWM frequency; range: 2K-50K
|
||||
|
||||
static cutter_power_t menuPower, // Power as set via LCD menu in PWM, Percentage or RPM
|
||||
unitPower; // Power as displayed status in PWM, Percentage or RPM
|
||||
|
||||
static void init();
|
||||
|
||||
#if ENABLED(MARLIN_DEV_MODE)
|
||||
static inline void refresh_frequency() { set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), frequency); }
|
||||
#if ENABLED(HAL_CAN_SET_PWM_FREQ) && SPINDLE_LASER_FREQUENCY
|
||||
static void refresh_frequency() { hal.set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), frequency); }
|
||||
#endif
|
||||
|
||||
// Modifying this function should update everywhere
|
||||
static inline bool enabled(const cutter_power_t opwr) { return opwr > 0; }
|
||||
static inline bool enabled() { return enabled(power); }
|
||||
static bool enabled(const cutter_power_t opwr) { return opwr > 0; }
|
||||
static bool enabled() { return enable_state; }
|
||||
|
||||
static void apply_power(const uint8_t inpow);
|
||||
|
||||
FORCE_INLINE static void refresh() { apply_power(power); }
|
||||
FORCE_INLINE static void set_power(const uint8_t upwr) { power = upwr; refresh(); }
|
||||
|
||||
#if ENABLED(SPINDLE_LASER_USE_PWM)
|
||||
|
||||
@@ -132,13 +136,12 @@ public:
|
||||
public:
|
||||
|
||||
static void set_ocr(const uint8_t ocr);
|
||||
static inline void ocr_set_power(const uint8_t ocr) { power = ocr; set_ocr(ocr); }
|
||||
static void ocr_off();
|
||||
|
||||
/**
|
||||
* Update output for power->OCR translation
|
||||
*/
|
||||
static inline uint8_t upower_to_ocr(const cutter_power_t upwr) {
|
||||
static uint8_t upower_to_ocr(const cutter_power_t upwr) {
|
||||
return uint8_t(
|
||||
#if CUTTER_UNIT_IS(PWM255)
|
||||
upwr
|
||||
@@ -150,201 +153,179 @@ public:
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct power to configured range
|
||||
*/
|
||||
static inline cutter_power_t power_to_range(const cutter_power_t pwr) {
|
||||
return power_to_range(pwr, _CUTTER_POWER(CUTTER_POWER_UNIT));
|
||||
}
|
||||
|
||||
static inline cutter_power_t power_to_range(const cutter_power_t pwr, const uint8_t pwrUnit) {
|
||||
if (pwr <= 0) return 0;
|
||||
cutter_power_t upwr;
|
||||
switch (pwrUnit) {
|
||||
case _CUTTER_POWER_PWM255:
|
||||
upwr = cutter_power_t(
|
||||
(pwr < pct_to_ocr(min_pct)) ? pct_to_ocr(min_pct) // Use minimum if set below
|
||||
: (pwr > pct_to_ocr(max_pct)) ? pct_to_ocr(max_pct) // Use maximum if set above
|
||||
: pwr
|
||||
);
|
||||
break;
|
||||
case _CUTTER_POWER_PERCENT:
|
||||
upwr = cutter_power_t(
|
||||
(pwr < min_pct) ? min_pct // Use minimum if set below
|
||||
: (pwr > max_pct) ? max_pct // Use maximum if set above
|
||||
: pwr // PCT
|
||||
);
|
||||
break;
|
||||
case _CUTTER_POWER_RPM:
|
||||
upwr = cutter_power_t(
|
||||
(pwr < SPEED_POWER_MIN) ? SPEED_POWER_MIN // Use minimum if set below
|
||||
: (pwr > SPEED_POWER_MAX) ? SPEED_POWER_MAX // Use maximum if set above
|
||||
: pwr // Calculate OCR value
|
||||
);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
return upwr;
|
||||
}
|
||||
#endif // SPINDLE_LASER_USE_PWM
|
||||
|
||||
/**
|
||||
* Enable/Disable spindle/laser
|
||||
* @param enable true = enable; false = disable
|
||||
* Correct power to configured range
|
||||
*/
|
||||
static inline void set_enabled(const bool enable) {
|
||||
uint8_t value = 0;
|
||||
if (enable) {
|
||||
#if ENABLED(SPINDLE_LASER_USE_PWM)
|
||||
if (power)
|
||||
value = power;
|
||||
else if (unitPower)
|
||||
value = upower_to_ocr(cpwr_to_upwr(SPEED_POWER_STARTUP));
|
||||
#else
|
||||
value = 255;
|
||||
#endif
|
||||
static cutter_power_t power_to_range(const cutter_power_t pwr, const uint8_t pwrUnit=_CUTTER_POWER(CUTTER_POWER_UNIT)) {
|
||||
static constexpr float
|
||||
min_pct = TERN(CUTTER_POWER_RELATIVE, 0, TERN(SPINDLE_FEATURE, round(100.0f * (SPEED_POWER_MIN) / (SPEED_POWER_MAX)), SPEED_POWER_MIN)),
|
||||
max_pct = TERN(SPINDLE_FEATURE, 100, SPEED_POWER_MAX);
|
||||
if (pwr <= 0) return 0;
|
||||
cutter_power_t upwr;
|
||||
switch (pwrUnit) {
|
||||
case _CUTTER_POWER_PWM255: { // PWM
|
||||
const uint8_t pmin = pct_to_ocr(min_pct), pmax = pct_to_ocr(max_pct);
|
||||
upwr = cutter_power_t(constrain(pwr, pmin, pmax));
|
||||
} break;
|
||||
case _CUTTER_POWER_PERCENT: // Percent
|
||||
upwr = cutter_power_t(constrain(pwr, min_pct, max_pct));
|
||||
break;
|
||||
case _CUTTER_POWER_RPM: // Calculate OCR value
|
||||
upwr = cutter_power_t(constrain(pwr, SPEED_POWER_MIN, SPEED_POWER_MAX));
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
set_power(value);
|
||||
return upwr;
|
||||
}
|
||||
|
||||
static inline void disable() { isReady = false; set_enabled(false); }
|
||||
|
||||
/**
|
||||
* Wait for spindle to spin up or spin down
|
||||
* Enable Laser or Spindle output.
|
||||
* It's important to prevent changing the power output value during inline cutter operation.
|
||||
* Inline power is adjusted in the planner to support LASER_TRAP_POWER and CUTTER_MODE_DYNAMIC mode.
|
||||
*
|
||||
* @param on true = state to on; false = state to off.
|
||||
* This method accepts one of the following control states:
|
||||
*
|
||||
* - For CUTTER_MODE_STANDARD the cutter power is either full on/off or ocr-based and it will apply
|
||||
* SPEED_POWER_STARTUP if no value is assigned.
|
||||
*
|
||||
* - For CUTTER_MODE_CONTINUOUS inline and power remains where last set and the cutter output enable flag is set.
|
||||
*
|
||||
* - CUTTER_MODE_DYNAMIC is also inline-based and it just sets the enable output flag.
|
||||
*
|
||||
* - For CUTTER_MODE_ERROR set the output enable_state flag directly and set power to 0 for any mode.
|
||||
* This mode allows a global power shutdown action to occur.
|
||||
*/
|
||||
static inline void power_delay(const bool on) {
|
||||
#if DISABLED(LASER_POWER_INLINE)
|
||||
safe_delay(on ? SPINDLE_LASER_POWERUP_DELAY : SPINDLE_LASER_POWERDOWN_DELAY);
|
||||
static void set_enabled(bool enable) {
|
||||
switch (cutter_mode) {
|
||||
case CUTTER_MODE_STANDARD:
|
||||
apply_power(enable ? TERN(SPINDLE_LASER_USE_PWM, (power ?: (unitPower ? upower_to_ocr(cpwr_to_upwr(SPEED_POWER_STARTUP)) : 0)), 255) : 0);
|
||||
break;
|
||||
case CUTTER_MODE_CONTINUOUS:
|
||||
TERN_(LASER_FEATURE, set_inline_enabled(enable));
|
||||
break;
|
||||
case CUTTER_MODE_DYNAMIC:
|
||||
TERN_(LASER_FEATURE, set_inline_enabled(enable));
|
||||
break;
|
||||
case CUTTER_MODE_ERROR: // Error mode, no enable and kill power.
|
||||
enable = false;
|
||||
apply_power(0);
|
||||
}
|
||||
#if SPINDLE_LASER_ENA_PIN
|
||||
WRITE(SPINDLE_LASER_ENA_PIN, enable ? SPINDLE_LASER_ACTIVE_STATE : !SPINDLE_LASER_ACTIVE_STATE);
|
||||
#endif
|
||||
enable_state = enable;
|
||||
}
|
||||
|
||||
static void disable() { isReadyForUI = false; set_enabled(false); }
|
||||
|
||||
// Wait for spindle/laser to startup or shutdown
|
||||
static void power_delay(const bool on) {
|
||||
safe_delay(on ? SPINDLE_LASER_POWERUP_DELAY : SPINDLE_LASER_POWERDOWN_DELAY);
|
||||
}
|
||||
|
||||
#if ENABLED(SPINDLE_CHANGE_DIR)
|
||||
static void set_reverse(const bool reverse);
|
||||
static bool is_reverse() { return READ(SPINDLE_DIR_PIN) == SPINDLE_INVERT_DIR; }
|
||||
#else
|
||||
static inline void set_reverse(const bool) {}
|
||||
static void set_reverse(const bool) {}
|
||||
static bool is_reverse() { return false; }
|
||||
#endif
|
||||
|
||||
#if ENABLED(AIR_EVACUATION)
|
||||
static void air_evac_enable(); // Turn On Cutter Vacuum or Laser Blower motor
|
||||
static void air_evac_disable(); // Turn Off Cutter Vacuum or Laser Blower motor
|
||||
static void air_evac_toggle(); // Toggle Cutter Vacuum or Laser Blower motor
|
||||
static inline bool air_evac_state() { // Get current state
|
||||
static void air_evac_enable(); // Turn On Cutter Vacuum or Laser Blower motor
|
||||
static void air_evac_disable(); // Turn Off Cutter Vacuum or Laser Blower motor
|
||||
static void air_evac_toggle(); // Toggle Cutter Vacuum or Laser Blower motor
|
||||
static bool air_evac_state() { // Get current state
|
||||
return (READ(AIR_EVACUATION_PIN) == AIR_EVACUATION_ACTIVE);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLED(AIR_ASSIST)
|
||||
static void air_assist_enable(); // Turn on air assist
|
||||
static void air_assist_disable(); // Turn off air assist
|
||||
static void air_assist_toggle(); // Toggle air assist
|
||||
static inline bool air_assist_state() { // Get current state
|
||||
static void air_assist_enable(); // Turn on air assist
|
||||
static void air_assist_disable(); // Turn off air assist
|
||||
static void air_assist_toggle(); // Toggle air assist
|
||||
static bool air_assist_state() { // Get current state
|
||||
return (READ(AIR_ASSIST_PIN) == AIR_ASSIST_ACTIVE);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
static inline void enable_with_dir(const bool reverse) {
|
||||
isReady = true;
|
||||
const uint8_t ocr = TERN(SPINDLE_LASER_USE_PWM, upower_to_ocr(menuPower), 255);
|
||||
if (menuPower)
|
||||
power = ocr;
|
||||
else
|
||||
menuPower = cpwr_to_upwr(SPEED_POWER_STARTUP);
|
||||
unitPower = menuPower;
|
||||
set_reverse(reverse);
|
||||
set_enabled(true);
|
||||
}
|
||||
FORCE_INLINE static void enable_forward() { enable_with_dir(false); }
|
||||
FORCE_INLINE static void enable_reverse() { enable_with_dir(true); }
|
||||
FORCE_INLINE static void enable_same_dir() { enable_with_dir(is_reverse()); }
|
||||
#if HAS_MARLINUI_MENU
|
||||
|
||||
#if ENABLED(SPINDLE_FEATURE)
|
||||
static void enable_with_dir(const bool reverse) {
|
||||
isReadyForUI = true;
|
||||
const uint8_t ocr = TERN(SPINDLE_LASER_USE_PWM, upower_to_ocr(menuPower), 255);
|
||||
if (menuPower)
|
||||
power = ocr;
|
||||
else
|
||||
menuPower = cpwr_to_upwr(SPEED_POWER_STARTUP);
|
||||
unitPower = menuPower;
|
||||
set_reverse(reverse);
|
||||
set_enabled(true);
|
||||
}
|
||||
FORCE_INLINE static void enable_forward() { enable_with_dir(false); }
|
||||
FORCE_INLINE static void enable_reverse() { enable_with_dir(true); }
|
||||
FORCE_INLINE static void enable_same_dir() { enable_with_dir(is_reverse()); }
|
||||
#endif // SPINDLE_FEATURE
|
||||
|
||||
#if ENABLED(SPINDLE_LASER_USE_PWM)
|
||||
static inline void update_from_mpower() {
|
||||
if (isReady) power = upower_to_ocr(menuPower);
|
||||
static void update_from_mpower() {
|
||||
if (isReadyForUI) power = upower_to_ocr(menuPower);
|
||||
unitPower = menuPower;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLED(LASER_FEATURE)
|
||||
// Toggle the laser on/off with menuPower. Apply SPEED_POWER_STARTUP if it was 0 on entry.
|
||||
static void menu_set_enabled(const bool state) {
|
||||
set_enabled(state);
|
||||
if (state) {
|
||||
if (!menuPower) menuPower = cpwr_to_upwr(SPEED_POWER_STARTUP);
|
||||
power = upower_to_ocr(menuPower);
|
||||
apply_power(power);
|
||||
} else
|
||||
apply_power(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test fire the laser using the testPulse ms duration
|
||||
* Also fires with any PWM power that was previous set
|
||||
* If not set defaults to 80% power
|
||||
*/
|
||||
static inline void test_fire_pulse() {
|
||||
TERN_(USE_BEEPER, buzzer.tone(30, 3000));
|
||||
enable_forward(); // Turn Laser on (Spindle speak but same funct)
|
||||
delay(testPulse); // Delay for time set by user in pulse ms menu screen.
|
||||
disable(); // Turn laser off
|
||||
static void test_fire_pulse() {
|
||||
BUZZ(30, 3000);
|
||||
cutter_mode = CUTTER_MODE_STANDARD; // Menu needs standard mode.
|
||||
menu_set_enabled(true); // Laser On
|
||||
delay(testPulse); // Delay for time set by user in pulse ms menu screen.
|
||||
menu_set_enabled(false); // Laser Off
|
||||
}
|
||||
#endif
|
||||
#endif // LASER_FEATURE
|
||||
|
||||
#endif // HAS_LCD_MENU
|
||||
#endif // HAS_MARLINUI_MENU
|
||||
|
||||
#if ENABLED(LASER_POWER_INLINE)
|
||||
/**
|
||||
* Inline power adds extra fields to the planner block
|
||||
* to handle laser power and scale to movement speed.
|
||||
*/
|
||||
#if ENABLED(LASER_FEATURE)
|
||||
|
||||
// Force disengage planner power control
|
||||
static inline void inline_disable() {
|
||||
isReady = false;
|
||||
unitPower = 0;
|
||||
planner.laser_inline.status.isPlanned = false;
|
||||
planner.laser_inline.status.isEnabled = false;
|
||||
planner.laser_inline.power = 0;
|
||||
// Dynamic mode rate calculation
|
||||
static uint8_t calc_dynamic_power() {
|
||||
if (feedrate_mm_m > 65535) return 255; // Too fast, go always on
|
||||
uint16_t rate = uint16_t(feedrate_mm_m); // 16 bits from the G-code parser float input
|
||||
rate >>= 8; // Take the G-code input e.g. F40000 and shift off the lower bits to get an OCR value from 1-255
|
||||
return uint8_t(rate);
|
||||
}
|
||||
|
||||
// Inline modes of all other functions; all enable planner inline power control
|
||||
static inline void set_inline_enabled(const bool enable) {
|
||||
if (enable)
|
||||
inline_power(255);
|
||||
else {
|
||||
isReady = false;
|
||||
unitPower = menuPower = 0;
|
||||
planner.laser_inline.status.isPlanned = false;
|
||||
TERN(SPINDLE_LASER_USE_PWM, inline_ocr_power, inline_power)(0);
|
||||
}
|
||||
}
|
||||
static void set_inline_enabled(const bool enable) { planner.laser_inline.status.isEnabled = enable; }
|
||||
|
||||
// Set the power for subsequent movement blocks
|
||||
static void inline_power(const cutter_power_t upwr) {
|
||||
unitPower = menuPower = upwr;
|
||||
#if ENABLED(SPINDLE_LASER_USE_PWM)
|
||||
#if ENABLED(SPEED_POWER_RELATIVE) && !CUTTER_UNIT_IS(RPM) // relative mode does not turn laser off at 0, except for RPM
|
||||
planner.laser_inline.status.isEnabled = true;
|
||||
planner.laser_inline.power = upower_to_ocr(upwr);
|
||||
isReady = true;
|
||||
#else
|
||||
inline_ocr_power(upower_to_ocr(upwr));
|
||||
#endif
|
||||
#else
|
||||
planner.laser_inline.status.isEnabled = enabled(upwr);
|
||||
planner.laser_inline.power = upwr;
|
||||
isReady = enabled(upwr);
|
||||
#endif
|
||||
static void inline_power(const cutter_power_t cpwr) {
|
||||
TERN(SPINDLE_LASER_USE_PWM, power = planner.laser_inline.power = cpwr, planner.laser_inline.power = cpwr > 0 ? 255 : 0);
|
||||
}
|
||||
|
||||
static inline void inline_direction(const bool) { /* never */ }
|
||||
#endif // LASER_FEATURE
|
||||
|
||||
#if ENABLED(SPINDLE_LASER_USE_PWM)
|
||||
static inline void inline_ocr_power(const uint8_t ocrpwr) {
|
||||
isReady = ocrpwr > 0;
|
||||
planner.laser_inline.status.isEnabled = ocrpwr > 0;
|
||||
planner.laser_inline.power = ocrpwr;
|
||||
}
|
||||
#endif
|
||||
#endif // LASER_POWER_INLINE
|
||||
|
||||
static inline void kill() {
|
||||
TERN_(LASER_POWER_INLINE, inline_disable());
|
||||
disable();
|
||||
}
|
||||
static void kill() { disable(); }
|
||||
};
|
||||
|
||||
extern SpindleLaser cutter;
|
||||
|
@@ -28,12 +28,34 @@
|
||||
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
|
||||
#define MSG_CUTTER(M) _MSG_CUTTER(M)
|
||||
|
||||
#ifndef SPEED_POWER_INTERCEPT
|
||||
#define SPEED_POWER_INTERCEPT 0
|
||||
#endif
|
||||
#if ENABLED(SPINDLE_FEATURE)
|
||||
#define _MSG_CUTTER(M) MSG_SPINDLE_##M
|
||||
#ifndef SPEED_POWER_MIN
|
||||
#define SPEED_POWER_MIN 5000
|
||||
#endif
|
||||
#ifndef SPEED_POWER_MAX
|
||||
#define SPEED_POWER_MAX 30000
|
||||
#endif
|
||||
#ifndef SPEED_POWER_STARTUP
|
||||
#define SPEED_POWER_STARTUP 25000
|
||||
#endif
|
||||
#else
|
||||
#define _MSG_CUTTER(M) MSG_LASER_##M
|
||||
#ifndef SPEED_POWER_MIN
|
||||
#define SPEED_POWER_MIN 0
|
||||
#endif
|
||||
#ifndef SPEED_POWER_MAX
|
||||
#define SPEED_POWER_MAX 255
|
||||
#endif
|
||||
#ifndef SPEED_POWER_STARTUP
|
||||
#define SPEED_POWER_STARTUP 255
|
||||
#endif
|
||||
#endif
|
||||
#define MSG_CUTTER(M) _MSG_CUTTER(M)
|
||||
|
||||
typedef IF<(SPEED_POWER_MAX > 255), uint16_t, uint8_t>::type cutter_cpower_t;
|
||||
|
||||
@@ -52,12 +74,10 @@ typedef IF<(SPEED_POWER_MAX > 255), uint16_t, uint8_t>::type cutter_cpower_t;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef uint16_t cutter_frequency_t;
|
||||
|
||||
#if ENABLED(LASER_FEATURE)
|
||||
typedef uint16_t cutter_test_pulse_t;
|
||||
#define CUTTER_MENU_PULSE_TYPE uint16_3
|
||||
#endif
|
||||
|
||||
#if ENABLED(MARLIN_DEV_MODE)
|
||||
typedef uint16_t cutter_frequency_t;
|
||||
#define CUTTER_MENU_FREQUENCY_TYPE uint16_5
|
||||
#endif
|
||||
|
@@ -28,11 +28,11 @@
|
||||
|
||||
static uint32_t axis_plug_backward = 0;
|
||||
|
||||
void stepper_driver_backward_error(PGM_P str) {
|
||||
void stepper_driver_backward_error(FSTR_P const fstr) {
|
||||
SERIAL_ERROR_START();
|
||||
SERIAL_ECHOPGM_P(str);
|
||||
SERIAL_ECHOF(fstr);
|
||||
SERIAL_ECHOLNPGM(" driver is backward!");
|
||||
ui.status_printf_P(2, PSTR(S_FMT S_FMT), str, GET_TEXT(MSG_DRIVER_BACKWARD));
|
||||
ui.status_printf(2, F(S_FMT S_FMT), FTOP(fstr), GET_TEXT(MSG_DRIVER_BACKWARD));
|
||||
}
|
||||
|
||||
void stepper_driver_backward_check() {
|
||||
@@ -45,7 +45,7 @@ void stepper_driver_backward_check() {
|
||||
delay(20); \
|
||||
if (READ(AXIS##_ENABLE_PIN) == false) { \
|
||||
SBI(axis_plug_backward, BIT); \
|
||||
stepper_driver_backward_error(PSTR(STRINGIFY(AXIS))); \
|
||||
stepper_driver_backward_error(F(STRINGIFY(AXIS))); \
|
||||
} \
|
||||
}while(0)
|
||||
|
||||
@@ -65,15 +65,18 @@ void stepper_driver_backward_check() {
|
||||
TEST_BACKWARD(I, 8);
|
||||
TEST_BACKWARD(J, 9);
|
||||
TEST_BACKWARD(K, 10);
|
||||
TEST_BACKWARD(U, 11);
|
||||
TEST_BACKWARD(V, 12);
|
||||
TEST_BACKWARD(W, 13);
|
||||
|
||||
TEST_BACKWARD(E0, 11);
|
||||
TEST_BACKWARD(E1, 12);
|
||||
TEST_BACKWARD(E2, 13);
|
||||
TEST_BACKWARD(E3, 14);
|
||||
TEST_BACKWARD(E4, 15);
|
||||
TEST_BACKWARD(E5, 16);
|
||||
TEST_BACKWARD(E6, 17);
|
||||
TEST_BACKWARD(E7, 18);
|
||||
TEST_BACKWARD(E0, 14);
|
||||
TEST_BACKWARD(E1, 15);
|
||||
TEST_BACKWARD(E2, 16);
|
||||
TEST_BACKWARD(E3, 17);
|
||||
TEST_BACKWARD(E4, 18);
|
||||
TEST_BACKWARD(E5, 19);
|
||||
TEST_BACKWARD(E6, 20);
|
||||
TEST_BACKWARD(E7, 21);
|
||||
|
||||
if (!axis_plug_backward)
|
||||
WRITE(SAFE_POWER_PIN, HIGH);
|
||||
@@ -82,12 +85,12 @@ void stepper_driver_backward_check() {
|
||||
void stepper_driver_backward_report() {
|
||||
if (!axis_plug_backward) return;
|
||||
|
||||
auto _report_if_backward = [](PGM_P axis, uint8_t bit) {
|
||||
auto _report_if_backward = [](FSTR_P const axis, uint8_t bit) {
|
||||
if (TEST(axis_plug_backward, bit))
|
||||
stepper_driver_backward_error(axis);
|
||||
};
|
||||
|
||||
#define REPORT_BACKWARD(axis, bit) TERN_(HAS_##axis##_ENABLE, _report_if_backward(PSTR(STRINGIFY(axis)), bit))
|
||||
#define REPORT_BACKWARD(axis, bit) TERN_(HAS_##axis##_ENABLE, _report_if_backward(F(STRINGIFY(axis)), bit))
|
||||
|
||||
REPORT_BACKWARD(X, 0);
|
||||
REPORT_BACKWARD(X2, 1);
|
||||
@@ -103,15 +106,18 @@ void stepper_driver_backward_report() {
|
||||
REPORT_BACKWARD(I, 8);
|
||||
REPORT_BACKWARD(J, 9);
|
||||
REPORT_BACKWARD(K, 10);
|
||||
REPORT_BACKWARD(U, 11);
|
||||
REPORT_BACKWARD(V, 12);
|
||||
REPORT_BACKWARD(W, 13);
|
||||
|
||||
REPORT_BACKWARD(E0, 11);
|
||||
REPORT_BACKWARD(E1, 12);
|
||||
REPORT_BACKWARD(E2, 13);
|
||||
REPORT_BACKWARD(E3, 14);
|
||||
REPORT_BACKWARD(E4, 15);
|
||||
REPORT_BACKWARD(E5, 16);
|
||||
REPORT_BACKWARD(E6, 17);
|
||||
REPORT_BACKWARD(E7, 18);
|
||||
REPORT_BACKWARD(E0, 14);
|
||||
REPORT_BACKWARD(E1, 15);
|
||||
REPORT_BACKWARD(E2, 16);
|
||||
REPORT_BACKWARD(E3, 17);
|
||||
REPORT_BACKWARD(E4, 18);
|
||||
REPORT_BACKWARD(E5, 19);
|
||||
REPORT_BACKWARD(E6, 20);
|
||||
REPORT_BACKWARD(E7, 21);
|
||||
}
|
||||
|
||||
#endif // HAS_DRIVER_SAFE_POWER_PROTECT
|
||||
|
@@ -33,17 +33,12 @@
|
||||
#include "../gcode/gcode.h"
|
||||
|
||||
#if ENABLED(TMC_DEBUG)
|
||||
#include "../module/planner.h"
|
||||
#include "../libs/hex_print.h"
|
||||
#if ENABLED(MONITOR_DRIVER_STATUS)
|
||||
static uint16_t report_tmc_status_interval; // = 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
#include "../module/stepper.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Check for over temperature or short to ground error flags.
|
||||
* Report and log warning of overtemperature condition.
|
||||
@@ -212,7 +207,7 @@
|
||||
if (data.is_ot) SERIAL_ECHOLNPGM("overtemperature");
|
||||
if (data.is_s2g) SERIAL_ECHOLNPGM("coil short circuit");
|
||||
TERN_(TMC_DEBUG, tmc_report_all());
|
||||
kill(PSTR("Driver error"));
|
||||
kill(F("Driver error"));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -421,16 +416,26 @@
|
||||
if (monitor_tmc_driver(stepperI, need_update_error_counters, need_debug_reporting))
|
||||
step_current_down(stepperI);
|
||||
#endif
|
||||
|
||||
#if AXIS_IS_TMC(J)
|
||||
if (monitor_tmc_driver(stepperJ, need_update_error_counters, need_debug_reporting))
|
||||
step_current_down(stepperJ);
|
||||
#endif
|
||||
|
||||
#if AXIS_IS_TMC(K)
|
||||
if (monitor_tmc_driver(stepperK, need_update_error_counters, need_debug_reporting))
|
||||
step_current_down(stepperK);
|
||||
#endif
|
||||
#if AXIS_IS_TMC(U)
|
||||
if (monitor_tmc_driver(stepperU, need_update_error_counters, need_debug_reporting))
|
||||
step_current_down(stepperU);
|
||||
#endif
|
||||
#if AXIS_IS_TMC(V)
|
||||
if (monitor_tmc_driver(stepperV, need_update_error_counters, need_debug_reporting))
|
||||
step_current_down(stepperV);
|
||||
#endif
|
||||
#if AXIS_IS_TMC(W)
|
||||
if (monitor_tmc_driver(stepperW, need_update_error_counters, need_debug_reporting))
|
||||
step_current_down(stepperW);
|
||||
#endif
|
||||
|
||||
#if AXIS_IS_TMC(E0)
|
||||
(void)monitor_tmc_driver(stepperE0, need_update_error_counters, need_debug_reporting);
|
||||
@@ -472,12 +477,8 @@
|
||||
void tmc_set_report_interval(const uint16_t update_interval) {
|
||||
if ((report_tmc_status_interval = update_interval))
|
||||
SERIAL_ECHOLNPGM("axis:pwm_scale"
|
||||
#if HAS_STEALTHCHOP
|
||||
"/curr_scale"
|
||||
#endif
|
||||
#if HAS_STALLGUARD
|
||||
"/mech_load"
|
||||
#endif
|
||||
TERN_(HAS_STEALTHCHOP, "/curr_scale")
|
||||
TERN_(HAS_STALLGUARD, "/mech_load")
|
||||
"|flags|warncount"
|
||||
);
|
||||
}
|
||||
@@ -561,7 +562,7 @@
|
||||
};
|
||||
|
||||
template<class TMC>
|
||||
static void print_vsense(TMC &st) { SERIAL_ECHOPGM_P(st.vsense() ? PSTR("1=.18") : PSTR("0=.325")); }
|
||||
static void print_vsense(TMC &st) { SERIAL_ECHOF(st.vsense() ? F("1=.18") : F("0=.325")); }
|
||||
|
||||
#if HAS_DRIVER(TMC2130) || HAS_DRIVER(TMC5130)
|
||||
static void _tmc_status(TMC2130Stepper &st, const TMC_debug_enum i) {
|
||||
@@ -732,7 +733,7 @@
|
||||
SERIAL_ECHO(st.cs());
|
||||
SERIAL_ECHOPGM("/31");
|
||||
break;
|
||||
case TMC_VSENSE: SERIAL_ECHOPGM_P(st.vsense() ? PSTR("1=.165") : PSTR("0=.310")); break;
|
||||
case TMC_VSENSE: SERIAL_ECHOF(st.vsense() ? F("1=.165") : F("0=.310")); break;
|
||||
case TMC_MICROSTEPS: SERIAL_ECHO(st.microsteps()); break;
|
||||
//case TMC_OTPW: serialprint_truefalse(st.otpw()); break;
|
||||
//case TMC_OTPW_TRIGGERED: serialprint_truefalse(st.getOTPW()); break;
|
||||
@@ -815,6 +816,15 @@
|
||||
#if AXIS_IS_TMC(K)
|
||||
if (k) tmc_status(stepperK, n);
|
||||
#endif
|
||||
#if AXIS_IS_TMC(U)
|
||||
if (u) tmc_status(stepperU, n);
|
||||
#endif
|
||||
#if AXIS_IS_TMC(V)
|
||||
if (v) tmc_status(stepperV, n);
|
||||
#endif
|
||||
#if AXIS_IS_TMC(W)
|
||||
if (w) tmc_status(stepperW, n);
|
||||
#endif
|
||||
|
||||
if (TERN0(HAS_EXTRUDERS, e)) {
|
||||
#if AXIS_IS_TMC(E0)
|
||||
@@ -889,6 +899,15 @@
|
||||
#if AXIS_IS_TMC(K)
|
||||
if (k) tmc_parse_drv_status(stepperK, n);
|
||||
#endif
|
||||
#if AXIS_IS_TMC(U)
|
||||
if (u) tmc_parse_drv_status(stepperU, n);
|
||||
#endif
|
||||
#if AXIS_IS_TMC(V)
|
||||
if (v) tmc_parse_drv_status(stepperV, n);
|
||||
#endif
|
||||
#if AXIS_IS_TMC(W)
|
||||
if (w) tmc_parse_drv_status(stepperW, n);
|
||||
#endif
|
||||
|
||||
if (TERN0(HAS_EXTRUDERS, e)) {
|
||||
#if AXIS_IS_TMC(E0)
|
||||
@@ -1094,6 +1113,15 @@
|
||||
#if AXIS_IS_TMC(K)
|
||||
if (k) tmc_get_registers(stepperK, n);
|
||||
#endif
|
||||
#if AXIS_IS_TMC(U)
|
||||
if (u) tmc_get_registers(stepperU, n);
|
||||
#endif
|
||||
#if AXIS_IS_TMC(V)
|
||||
if (v) tmc_get_registers(stepperV, n);
|
||||
#endif
|
||||
#if AXIS_IS_TMC(W)
|
||||
if (w) tmc_get_registers(stepperW, n);
|
||||
#endif
|
||||
|
||||
if (TERN0(HAS_EXTRUDERS, e)) {
|
||||
#if AXIS_IS_TMC(E0)
|
||||
@@ -1184,69 +1212,6 @@
|
||||
|
||||
#endif // USE_SENSORLESS
|
||||
|
||||
#if HAS_TMC_SPI
|
||||
#define SET_CS_PIN(st) OUT_WRITE(st##_CS_PIN, HIGH)
|
||||
void tmc_init_cs_pins() {
|
||||
#if AXIS_HAS_SPI(X)
|
||||
SET_CS_PIN(X);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(Y)
|
||||
SET_CS_PIN(Y);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(Z)
|
||||
SET_CS_PIN(Z);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(X2)
|
||||
SET_CS_PIN(X2);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(Y2)
|
||||
SET_CS_PIN(Y2);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(Z2)
|
||||
SET_CS_PIN(Z2);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(Z3)
|
||||
SET_CS_PIN(Z3);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(Z4)
|
||||
SET_CS_PIN(Z4);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(I)
|
||||
SET_CS_PIN(I);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(J)
|
||||
SET_CS_PIN(J);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(K)
|
||||
SET_CS_PIN(K);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(E0)
|
||||
SET_CS_PIN(E0);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(E1)
|
||||
SET_CS_PIN(E1);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(E2)
|
||||
SET_CS_PIN(E2);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(E3)
|
||||
SET_CS_PIN(E3);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(E4)
|
||||
SET_CS_PIN(E4);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(E5)
|
||||
SET_CS_PIN(E5);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(E6)
|
||||
SET_CS_PIN(E6);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(E7)
|
||||
SET_CS_PIN(E7);
|
||||
#endif
|
||||
}
|
||||
#endif // HAS_TMC_SPI
|
||||
|
||||
template<typename TMC>
|
||||
static bool test_connection(TMC &st) {
|
||||
SERIAL_ECHOPGM("Testing ");
|
||||
@@ -1256,15 +1221,14 @@ static bool test_connection(TMC &st) {
|
||||
|
||||
if (test_result > 0) SERIAL_ECHOPGM("Error: All ");
|
||||
|
||||
const char *stat;
|
||||
FSTR_P stat;
|
||||
switch (test_result) {
|
||||
default:
|
||||
case 0: stat = PSTR("OK"); break;
|
||||
case 1: stat = PSTR("HIGH"); break;
|
||||
case 2: stat = PSTR("LOW"); break;
|
||||
case 0: stat = F("OK"); break;
|
||||
case 1: stat = F("HIGH"); break;
|
||||
case 2: stat = F("LOW"); break;
|
||||
}
|
||||
SERIAL_ECHOPGM_P(stat);
|
||||
SERIAL_EOL();
|
||||
SERIAL_ECHOLNF(stat);
|
||||
|
||||
return test_result;
|
||||
}
|
||||
@@ -1314,6 +1278,15 @@ void test_tmc_connection(LOGICAL_AXIS_ARGS(const bool)) {
|
||||
#if AXIS_IS_TMC(K)
|
||||
if (k) axis_connection += test_connection(stepperK);
|
||||
#endif
|
||||
#if AXIS_IS_TMC(U)
|
||||
if (u) axis_connection += test_connection(stepperU);
|
||||
#endif
|
||||
#if AXIS_IS_TMC(V)
|
||||
if (v) axis_connection += test_connection(stepperV);
|
||||
#endif
|
||||
#if AXIS_IS_TMC(W)
|
||||
if (w) axis_connection += test_connection(stepperW);
|
||||
#endif
|
||||
|
||||
if (TERN0(HAS_EXTRUDERS, e)) {
|
||||
#if AXIS_IS_TMC(E0)
|
||||
@@ -1342,7 +1315,79 @@ void test_tmc_connection(LOGICAL_AXIS_ARGS(const bool)) {
|
||||
#endif
|
||||
}
|
||||
|
||||
if (axis_connection) LCD_MESSAGEPGM(MSG_ERROR_TMC);
|
||||
if (axis_connection) LCD_MESSAGE(MSG_ERROR_TMC);
|
||||
}
|
||||
|
||||
#endif // HAS_TRINAMIC_CONFIG
|
||||
|
||||
#if HAS_TMC_SPI
|
||||
#define SET_CS_PIN(st) OUT_WRITE(st##_CS_PIN, HIGH)
|
||||
void tmc_init_cs_pins() {
|
||||
#if AXIS_HAS_SPI(X)
|
||||
SET_CS_PIN(X);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(Y)
|
||||
SET_CS_PIN(Y);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(Z)
|
||||
SET_CS_PIN(Z);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(X2)
|
||||
SET_CS_PIN(X2);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(Y2)
|
||||
SET_CS_PIN(Y2);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(Z2)
|
||||
SET_CS_PIN(Z2);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(Z3)
|
||||
SET_CS_PIN(Z3);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(Z4)
|
||||
SET_CS_PIN(Z4);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(I)
|
||||
SET_CS_PIN(I);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(J)
|
||||
SET_CS_PIN(J);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(K)
|
||||
SET_CS_PIN(K);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(U)
|
||||
SET_CS_PIN(U);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(V)
|
||||
SET_CS_PIN(V);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(W)
|
||||
SET_CS_PIN(W);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(E0)
|
||||
SET_CS_PIN(E0);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(E1)
|
||||
SET_CS_PIN(E1);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(E2)
|
||||
SET_CS_PIN(E2);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(E3)
|
||||
SET_CS_PIN(E3);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(E4)
|
||||
SET_CS_PIN(E4);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(E5)
|
||||
SET_CS_PIN(E5);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(E6)
|
||||
SET_CS_PIN(E6);
|
||||
#endif
|
||||
#if AXIS_HAS_SPI(E7)
|
||||
SET_CS_PIN(E7);
|
||||
#endif
|
||||
}
|
||||
#endif // HAS_TMC_SPI
|
||||
|
@@ -45,6 +45,12 @@ constexpr uint16_t _tmc_thrs(const uint16_t msteps, const uint32_t thrs, const u
|
||||
return 12650000UL * msteps / (256 * thrs * spmm);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint8_t toff;
|
||||
int8_t hend;
|
||||
uint8_t hstrt;
|
||||
} chopper_timing_t;
|
||||
|
||||
template<char AXIS_LETTER, char DRIVER_ID>
|
||||
class TMCStorage {
|
||||
protected:
|
||||
@@ -58,13 +64,13 @@ class TMCStorage {
|
||||
uint8_t otpw_count = 0,
|
||||
error_count = 0;
|
||||
bool flag_otpw = false;
|
||||
inline bool getOTPW() { return flag_otpw; }
|
||||
inline void clear_otpw() { flag_otpw = 0; }
|
||||
bool getOTPW() { return flag_otpw; }
|
||||
void clear_otpw() { flag_otpw = 0; }
|
||||
#endif
|
||||
|
||||
inline uint16_t getMilliamps() { return val_mA; }
|
||||
uint16_t getMilliamps() { return val_mA; }
|
||||
|
||||
inline void printLabel() {
|
||||
void printLabel() {
|
||||
SERIAL_CHAR(AXIS_LETTER);
|
||||
if (DRIVER_ID > '0') SERIAL_CHAR(DRIVER_ID);
|
||||
}
|
||||
@@ -91,55 +97,61 @@ class TMCMarlin : public TMC, public TMCStorage<AXIS_LETTER, DRIVER_ID> {
|
||||
TMCMarlin(const uint16_t CS, const float RS, const uint16_t pinMOSI, const uint16_t pinMISO, const uint16_t pinSCK, const uint8_t axis_chain_index) :
|
||||
TMC(CS, RS, pinMOSI, pinMISO, pinSCK, axis_chain_index)
|
||||
{}
|
||||
inline uint16_t rms_current() { return TMC::rms_current(); }
|
||||
inline void rms_current(uint16_t mA) {
|
||||
uint16_t rms_current() { return TMC::rms_current(); }
|
||||
void rms_current(uint16_t mA) {
|
||||
this->val_mA = mA;
|
||||
TMC::rms_current(mA);
|
||||
}
|
||||
inline void rms_current(const uint16_t mA, const float mult) {
|
||||
void rms_current(const uint16_t mA, const float mult) {
|
||||
this->val_mA = mA;
|
||||
TMC::rms_current(mA, mult);
|
||||
}
|
||||
inline uint16_t get_microstep_counter() { return TMC::MSCNT(); }
|
||||
uint16_t get_microstep_counter() { return TMC::MSCNT(); }
|
||||
|
||||
#if HAS_STEALTHCHOP
|
||||
inline bool get_stealthChop() { return this->en_pwm_mode(); }
|
||||
inline bool get_stored_stealthChop() { return this->stored.stealthChop_enabled; }
|
||||
inline void refresh_stepping_mode() { this->en_pwm_mode(this->stored.stealthChop_enabled); }
|
||||
inline void set_stealthChop(const bool stch) { this->stored.stealthChop_enabled = stch; refresh_stepping_mode(); }
|
||||
inline bool toggle_stepping_mode() { set_stealthChop(!this->stored.stealthChop_enabled); return get_stealthChop(); }
|
||||
bool get_stealthChop() { return this->en_pwm_mode(); }
|
||||
bool get_stored_stealthChop() { return this->stored.stealthChop_enabled; }
|
||||
void refresh_stepping_mode() { this->en_pwm_mode(this->stored.stealthChop_enabled); }
|
||||
void set_stealthChop(const bool stch) { this->stored.stealthChop_enabled = stch; refresh_stepping_mode(); }
|
||||
bool toggle_stepping_mode() { set_stealthChop(!this->stored.stealthChop_enabled); return get_stealthChop(); }
|
||||
#endif
|
||||
|
||||
void set_chopper_times(const chopper_timing_t &ct) {
|
||||
TMC::toff(ct.toff);
|
||||
TMC::hysteresis_end(ct.hend);
|
||||
TMC::hysteresis_start(ct.hstrt);
|
||||
}
|
||||
|
||||
#if ENABLED(HYBRID_THRESHOLD)
|
||||
uint32_t get_pwm_thrs() {
|
||||
return _tmc_thrs(this->microsteps(), this->TPWMTHRS(), planner.settings.axis_steps_per_mm[AXIS_ID]);
|
||||
}
|
||||
void set_pwm_thrs(const uint32_t thrs) {
|
||||
TMC::TPWMTHRS(_tmc_thrs(this->microsteps(), thrs, planner.settings.axis_steps_per_mm[AXIS_ID]));
|
||||
TERN_(HAS_LCD_MENU, this->stored.hybrid_thrs = thrs);
|
||||
TERN_(HAS_MARLINUI_MENU, this->stored.hybrid_thrs = thrs);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if USE_SENSORLESS
|
||||
inline int16_t homing_threshold() { return TMC::sgt(); }
|
||||
int16_t homing_threshold() { return TMC::sgt(); }
|
||||
void homing_threshold(int16_t sgt_val) {
|
||||
sgt_val = (int16_t)constrain(sgt_val, sgt_min, sgt_max);
|
||||
TMC::sgt(sgt_val);
|
||||
TERN_(HAS_LCD_MENU, this->stored.homing_thrs = sgt_val);
|
||||
TERN_(HAS_MARLINUI_MENU, this->stored.homing_thrs = sgt_val);
|
||||
}
|
||||
#if ENABLED(SPI_ENDSTOPS)
|
||||
bool test_stall_status();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
inline void refresh_stepper_current() { rms_current(this->val_mA); }
|
||||
#if HAS_MARLINUI_MENU
|
||||
void refresh_stepper_current() { rms_current(this->val_mA); }
|
||||
|
||||
#if ENABLED(HYBRID_THRESHOLD)
|
||||
inline void refresh_hybrid_thrs() { set_pwm_thrs(this->stored.hybrid_thrs); }
|
||||
void refresh_hybrid_thrs() { set_pwm_thrs(this->stored.hybrid_thrs); }
|
||||
#endif
|
||||
#if USE_SENSORLESS
|
||||
inline void refresh_homing_thrs() { homing_threshold(this->stored.homing_thrs); }
|
||||
void refresh_homing_thrs() { homing_threshold(this->stored.homing_thrs); }
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -161,39 +173,45 @@ class TMCMarlin<TMC2208Stepper, AXIS_LETTER, DRIVER_ID, AXIS_ID> : public TMC220
|
||||
{}
|
||||
|
||||
uint16_t rms_current() { return TMC2208Stepper::rms_current(); }
|
||||
inline void rms_current(const uint16_t mA) {
|
||||
void rms_current(const uint16_t mA) {
|
||||
this->val_mA = mA;
|
||||
TMC2208Stepper::rms_current(mA);
|
||||
}
|
||||
inline void rms_current(const uint16_t mA, const float mult) {
|
||||
void rms_current(const uint16_t mA, const float mult) {
|
||||
this->val_mA = mA;
|
||||
TMC2208Stepper::rms_current(mA, mult);
|
||||
}
|
||||
inline uint16_t get_microstep_counter() { return TMC2208Stepper::MSCNT(); }
|
||||
uint16_t get_microstep_counter() { return TMC2208Stepper::MSCNT(); }
|
||||
|
||||
#if HAS_STEALTHCHOP
|
||||
inline bool get_stealthChop() { return !this->en_spreadCycle(); }
|
||||
inline bool get_stored_stealthChop() { return this->stored.stealthChop_enabled; }
|
||||
inline void refresh_stepping_mode() { this->en_spreadCycle(!this->stored.stealthChop_enabled); }
|
||||
inline void set_stealthChop(const bool stch) { this->stored.stealthChop_enabled = stch; refresh_stepping_mode(); }
|
||||
inline bool toggle_stepping_mode() { set_stealthChop(!this->stored.stealthChop_enabled); return get_stealthChop(); }
|
||||
bool get_stealthChop() { return !this->en_spreadCycle(); }
|
||||
bool get_stored_stealthChop() { return this->stored.stealthChop_enabled; }
|
||||
void refresh_stepping_mode() { this->en_spreadCycle(!this->stored.stealthChop_enabled); }
|
||||
void set_stealthChop(const bool stch) { this->stored.stealthChop_enabled = stch; refresh_stepping_mode(); }
|
||||
bool toggle_stepping_mode() { set_stealthChop(!this->stored.stealthChop_enabled); return get_stealthChop(); }
|
||||
#endif
|
||||
|
||||
void set_chopper_times(const chopper_timing_t &ct) {
|
||||
TMC2208Stepper::toff(ct.toff);
|
||||
TMC2208Stepper::hysteresis_end(ct.hend);
|
||||
TMC2208Stepper::hysteresis_start(ct.hstrt);
|
||||
}
|
||||
|
||||
#if ENABLED(HYBRID_THRESHOLD)
|
||||
uint32_t get_pwm_thrs() {
|
||||
return _tmc_thrs(this->microsteps(), this->TPWMTHRS(), planner.settings.axis_steps_per_mm[AXIS_ID]);
|
||||
}
|
||||
void set_pwm_thrs(const uint32_t thrs) {
|
||||
TMC2208Stepper::TPWMTHRS(_tmc_thrs(this->microsteps(), thrs, planner.settings.axis_steps_per_mm[AXIS_ID]));
|
||||
TERN_(HAS_LCD_MENU, this->stored.hybrid_thrs = thrs);
|
||||
TERN_(HAS_MARLINUI_MENU, this->stored.hybrid_thrs = thrs);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
inline void refresh_stepper_current() { rms_current(this->val_mA); }
|
||||
#if HAS_MARLINUI_MENU
|
||||
void refresh_stepper_current() { rms_current(this->val_mA); }
|
||||
|
||||
#if ENABLED(HYBRID_THRESHOLD)
|
||||
inline void refresh_hybrid_thrs() { set_pwm_thrs(this->stored.hybrid_thrs); }
|
||||
void refresh_hybrid_thrs() { set_pwm_thrs(this->stored.hybrid_thrs); }
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
@@ -209,50 +227,56 @@ class TMCMarlin<TMC2209Stepper, AXIS_LETTER, DRIVER_ID, AXIS_ID> : public TMC220
|
||||
{}
|
||||
uint8_t get_address() { return slave_address; }
|
||||
uint16_t rms_current() { return TMC2209Stepper::rms_current(); }
|
||||
inline void rms_current(const uint16_t mA) {
|
||||
void rms_current(const uint16_t mA) {
|
||||
this->val_mA = mA;
|
||||
TMC2209Stepper::rms_current(mA);
|
||||
}
|
||||
inline void rms_current(const uint16_t mA, const float mult) {
|
||||
void rms_current(const uint16_t mA, const float mult) {
|
||||
this->val_mA = mA;
|
||||
TMC2209Stepper::rms_current(mA, mult);
|
||||
}
|
||||
inline uint16_t get_microstep_counter() { return TMC2209Stepper::MSCNT(); }
|
||||
uint16_t get_microstep_counter() { return TMC2209Stepper::MSCNT(); }
|
||||
|
||||
#if HAS_STEALTHCHOP
|
||||
inline bool get_stealthChop() { return !this->en_spreadCycle(); }
|
||||
inline bool get_stored_stealthChop() { return this->stored.stealthChop_enabled; }
|
||||
inline void refresh_stepping_mode() { this->en_spreadCycle(!this->stored.stealthChop_enabled); }
|
||||
inline void set_stealthChop(const bool stch) { this->stored.stealthChop_enabled = stch; refresh_stepping_mode(); }
|
||||
inline bool toggle_stepping_mode() { set_stealthChop(!this->stored.stealthChop_enabled); return get_stealthChop(); }
|
||||
bool get_stealthChop() { return !this->en_spreadCycle(); }
|
||||
bool get_stored_stealthChop() { return this->stored.stealthChop_enabled; }
|
||||
void refresh_stepping_mode() { this->en_spreadCycle(!this->stored.stealthChop_enabled); }
|
||||
void set_stealthChop(const bool stch) { this->stored.stealthChop_enabled = stch; refresh_stepping_mode(); }
|
||||
bool toggle_stepping_mode() { set_stealthChop(!this->stored.stealthChop_enabled); return get_stealthChop(); }
|
||||
#endif
|
||||
|
||||
void set_chopper_times(const chopper_timing_t &ct) {
|
||||
TMC2209Stepper::toff(ct.toff);
|
||||
TMC2209Stepper::hysteresis_end(ct.hend);
|
||||
TMC2209Stepper::hysteresis_start(ct.hstrt);
|
||||
}
|
||||
|
||||
#if ENABLED(HYBRID_THRESHOLD)
|
||||
uint32_t get_pwm_thrs() {
|
||||
return _tmc_thrs(this->microsteps(), this->TPWMTHRS(), planner.settings.axis_steps_per_mm[AXIS_ID]);
|
||||
}
|
||||
void set_pwm_thrs(const uint32_t thrs) {
|
||||
TMC2209Stepper::TPWMTHRS(_tmc_thrs(this->microsteps(), thrs, planner.settings.axis_steps_per_mm[AXIS_ID]));
|
||||
TERN_(HAS_LCD_MENU, this->stored.hybrid_thrs = thrs);
|
||||
TERN_(HAS_MARLINUI_MENU, this->stored.hybrid_thrs = thrs);
|
||||
}
|
||||
#endif
|
||||
#if USE_SENSORLESS
|
||||
inline int16_t homing_threshold() { return TMC2209Stepper::SGTHRS(); }
|
||||
int16_t homing_threshold() { return TMC2209Stepper::SGTHRS(); }
|
||||
void homing_threshold(int16_t sgt_val) {
|
||||
sgt_val = (int16_t)constrain(sgt_val, sgt_min, sgt_max);
|
||||
TMC2209Stepper::SGTHRS(sgt_val);
|
||||
TERN_(HAS_LCD_MENU, this->stored.homing_thrs = sgt_val);
|
||||
TERN_(HAS_MARLINUI_MENU, this->stored.homing_thrs = sgt_val);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
inline void refresh_stepper_current() { rms_current(this->val_mA); }
|
||||
#if HAS_MARLINUI_MENU
|
||||
void refresh_stepper_current() { rms_current(this->val_mA); }
|
||||
|
||||
#if ENABLED(HYBRID_THRESHOLD)
|
||||
inline void refresh_hybrid_thrs() { set_pwm_thrs(this->stored.hybrid_thrs); }
|
||||
void refresh_hybrid_thrs() { set_pwm_thrs(this->stored.hybrid_thrs); }
|
||||
#endif
|
||||
#if USE_SENSORLESS
|
||||
inline void refresh_homing_thrs() { homing_threshold(this->stored.homing_thrs); }
|
||||
void refresh_homing_thrs() { homing_threshold(this->stored.homing_thrs); }
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -269,27 +293,33 @@ class TMCMarlin<TMC2660Stepper, AXIS_LETTER, DRIVER_ID, AXIS_ID> : public TMC266
|
||||
TMCMarlin(const uint16_t CS, const float RS, const uint16_t pinMOSI, const uint16_t pinMISO, const uint16_t pinSCK, const uint8_t) :
|
||||
TMC2660Stepper(CS, RS, pinMOSI, pinMISO, pinSCK)
|
||||
{}
|
||||
inline uint16_t rms_current() { return TMC2660Stepper::rms_current(); }
|
||||
inline void rms_current(const uint16_t mA) {
|
||||
uint16_t rms_current() { return TMC2660Stepper::rms_current(); }
|
||||
void rms_current(const uint16_t mA) {
|
||||
this->val_mA = mA;
|
||||
TMC2660Stepper::rms_current(mA);
|
||||
}
|
||||
inline uint16_t get_microstep_counter() { return TMC2660Stepper::mstep(); }
|
||||
uint16_t get_microstep_counter() { return TMC2660Stepper::mstep(); }
|
||||
|
||||
void set_chopper_times(const chopper_timing_t &ct) {
|
||||
TMC2660Stepper::toff(ct.toff);
|
||||
TMC2660Stepper::hysteresis_end(ct.hend);
|
||||
TMC2660Stepper::hysteresis_start(ct.hstrt);
|
||||
}
|
||||
|
||||
#if USE_SENSORLESS
|
||||
inline int16_t homing_threshold() { return TMC2660Stepper::sgt(); }
|
||||
int16_t homing_threshold() { return TMC2660Stepper::sgt(); }
|
||||
void homing_threshold(int16_t sgt_val) {
|
||||
sgt_val = (int16_t)constrain(sgt_val, sgt_min, sgt_max);
|
||||
TMC2660Stepper::sgt(sgt_val);
|
||||
TERN_(HAS_LCD_MENU, this->stored.homing_thrs = sgt_val);
|
||||
TERN_(HAS_MARLINUI_MENU, this->stored.homing_thrs = sgt_val);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAS_LCD_MENU
|
||||
inline void refresh_stepper_current() { rms_current(this->val_mA); }
|
||||
#if HAS_MARLINUI_MENU
|
||||
void refresh_stepper_current() { rms_current(this->val_mA); }
|
||||
|
||||
#if USE_SENSORLESS
|
||||
inline void refresh_homing_thrs() { homing_threshold(this->stored.homing_thrs); }
|
||||
void refresh_homing_thrs() { homing_threshold(this->stored.homing_thrs); }
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -297,43 +327,6 @@ class TMCMarlin<TMC2660Stepper, AXIS_LETTER, DRIVER_ID, AXIS_ID> : public TMC266
|
||||
sgt_max = 63;
|
||||
};
|
||||
|
||||
template<typename TMC>
|
||||
void tmc_print_current(TMC &st) {
|
||||
st.printLabel();
|
||||
SERIAL_ECHOLNPGM(" driver current: ", st.getMilliamps());
|
||||
}
|
||||
|
||||
#if ENABLED(MONITOR_DRIVER_STATUS)
|
||||
template<typename TMC>
|
||||
void tmc_report_otpw(TMC &st) {
|
||||
st.printLabel();
|
||||
SERIAL_ECHOPGM(" temperature prewarn triggered: ");
|
||||
serialprint_truefalse(st.getOTPW());
|
||||
SERIAL_EOL();
|
||||
}
|
||||
template<typename TMC>
|
||||
void tmc_clear_otpw(TMC &st) {
|
||||
st.clear_otpw();
|
||||
st.printLabel();
|
||||
SERIAL_ECHOLNPGM(" prewarn flag cleared");
|
||||
}
|
||||
#endif
|
||||
#if ENABLED(HYBRID_THRESHOLD)
|
||||
template<typename TMC>
|
||||
void tmc_print_pwmthrs(TMC &st) {
|
||||
st.printLabel();
|
||||
SERIAL_ECHOLNPGM(" stealthChop max speed: ", st.get_pwm_thrs());
|
||||
}
|
||||
#endif
|
||||
#if USE_SENSORLESS
|
||||
template<typename TMC>
|
||||
void tmc_print_sgt(TMC &st) {
|
||||
st.printLabel();
|
||||
SERIAL_ECHOPGM(" homing sensitivity: ");
|
||||
SERIAL_PRINTLN(st.homing_threshold(), PrintBase::Dec);
|
||||
}
|
||||
#endif
|
||||
|
||||
void monitor_tmc_drivers();
|
||||
void test_tmc_connection(LOGICAL_AXIS_DECL(const bool, true));
|
||||
|
||||
@@ -355,7 +348,7 @@ void test_tmc_connection(LOGICAL_AXIS_DECL(const bool, true));
|
||||
#if USE_SENSORLESS
|
||||
|
||||
// Track enabled status of stealthChop and only re-enable where applicable
|
||||
struct sensorless_t { bool LINEAR_AXIS_ARGS(), x2, y2, z2, z3, z4; };
|
||||
struct sensorless_t { bool NUM_AXIS_ARGS(), x2, y2, z2, z3, z4; };
|
||||
|
||||
#if ENABLED(IMPROVE_HOMING_RELIABILITY)
|
||||
extern millis_t sg_guard_period;
|
||||
@@ -389,8 +382,8 @@ void test_tmc_connection(LOGICAL_AXIS_DECL(const bool, true));
|
||||
|
||||
#endif // USE_SENSORLESS
|
||||
|
||||
#endif // HAS_TRINAMIC_CONFIG
|
||||
|
||||
#if HAS_TMC_SPI
|
||||
void tmc_init_cs_pins();
|
||||
#endif
|
||||
|
||||
#endif // HAS_TRINAMIC_CONFIG
|
||||
|
@@ -28,13 +28,24 @@
|
||||
|
||||
#include <Wire.h>
|
||||
|
||||
#include "../libs/hex_print.h"
|
||||
|
||||
TWIBus i2c;
|
||||
|
||||
TWIBus::TWIBus() {
|
||||
#if I2C_SLAVE_ADDRESS == 0
|
||||
Wire.begin(); // No address joins the BUS as the master
|
||||
|
||||
#if PINS_EXIST(I2C_SCL, I2C_SDA) && DISABLED(SOFT_I2C_EEPROM)
|
||||
Wire.setSDA(pin_t(I2C_SDA_PIN));
|
||||
Wire.setSCL(pin_t(I2C_SCL_PIN));
|
||||
#endif
|
||||
|
||||
Wire.begin(); // No address joins the BUS as the master
|
||||
|
||||
#else
|
||||
Wire.begin(I2C_SLAVE_ADDRESS); // Join the bus as a slave
|
||||
|
||||
Wire.begin(I2C_SLAVE_ADDRESS); // Join the bus as a slave
|
||||
|
||||
#endif
|
||||
reset();
|
||||
}
|
||||
@@ -45,33 +56,32 @@ void TWIBus::reset() {
|
||||
}
|
||||
|
||||
void TWIBus::address(const uint8_t adr) {
|
||||
if (!WITHIN(adr, 8, 127)) {
|
||||
if (!WITHIN(adr, 8, 127))
|
||||
SERIAL_ECHO_MSG("Bad I2C address (8-127)");
|
||||
}
|
||||
|
||||
addr = adr;
|
||||
|
||||
debug(PSTR("address"), adr);
|
||||
debug(F("address"), adr);
|
||||
}
|
||||
|
||||
void TWIBus::addbyte(const char c) {
|
||||
if (buffer_s >= COUNT(buffer)) return;
|
||||
buffer[buffer_s++] = c;
|
||||
debug(PSTR("addbyte"), c);
|
||||
debug(F("addbyte"), c);
|
||||
}
|
||||
|
||||
void TWIBus::addbytes(char src[], uint8_t bytes) {
|
||||
debug(PSTR("addbytes"), bytes);
|
||||
debug(F("addbytes"), bytes);
|
||||
while (bytes--) addbyte(*src++);
|
||||
}
|
||||
|
||||
void TWIBus::addstring(char str[]) {
|
||||
debug(PSTR("addstring"), str);
|
||||
debug(F("addstring"), str);
|
||||
while (char c = *str++) addbyte(c);
|
||||
}
|
||||
|
||||
void TWIBus::send() {
|
||||
debug(PSTR("send"), addr);
|
||||
debug(F("send"), addr);
|
||||
|
||||
Wire.beginTransmission(I2C_ADDRESS(addr));
|
||||
Wire.write(buffer, buffer_s);
|
||||
@@ -81,21 +91,60 @@ void TWIBus::send() {
|
||||
}
|
||||
|
||||
// static
|
||||
void TWIBus::echoprefix(uint8_t bytes, const char pref[], uint8_t adr) {
|
||||
void TWIBus::echoprefix(uint8_t bytes, FSTR_P const pref, uint8_t adr) {
|
||||
SERIAL_ECHO_START();
|
||||
SERIAL_ECHOPGM_P(pref);
|
||||
SERIAL_ECHOF(pref);
|
||||
SERIAL_ECHOPGM(": from:", adr, " bytes:", bytes, " data:");
|
||||
}
|
||||
|
||||
// static
|
||||
void TWIBus::echodata(uint8_t bytes, const char pref[], uint8_t adr) {
|
||||
void TWIBus::echodata(uint8_t bytes, FSTR_P const pref, uint8_t adr, const uint8_t style/*=0*/) {
|
||||
union TwoBytesToInt16 { uint8_t bytes[2]; int16_t integervalue; };
|
||||
TwoBytesToInt16 ConversionUnion;
|
||||
|
||||
echoprefix(bytes, pref, adr);
|
||||
while (bytes-- && Wire.available()) SERIAL_CHAR(Wire.read());
|
||||
|
||||
while (bytes-- && Wire.available()) {
|
||||
int value = Wire.read();
|
||||
switch (style) {
|
||||
|
||||
// Style 1, HEX DUMP
|
||||
case 1:
|
||||
SERIAL_CHAR(hex_nybble((value & 0xF0) >> 4));
|
||||
SERIAL_CHAR(hex_nybble(value & 0x0F));
|
||||
if (bytes) SERIAL_CHAR(' ');
|
||||
break;
|
||||
|
||||
// Style 2, signed two byte integer (int16)
|
||||
case 2:
|
||||
if (bytes == 1)
|
||||
ConversionUnion.bytes[1] = (uint8_t)value;
|
||||
else if (bytes == 0) {
|
||||
ConversionUnion.bytes[0] = (uint8_t)value;
|
||||
// Output value in base 10 (standard decimal)
|
||||
SERIAL_ECHO(ConversionUnion.integervalue);
|
||||
}
|
||||
break;
|
||||
|
||||
// Style 3, unsigned byte, base 10 (uint8)
|
||||
case 3:
|
||||
SERIAL_ECHO(value);
|
||||
if (bytes) SERIAL_CHAR(' ');
|
||||
break;
|
||||
|
||||
// Default style (zero), raw serial output
|
||||
default:
|
||||
// This can cause issues with some serial consoles, Pronterface is an example where things go wrong
|
||||
SERIAL_CHAR(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SERIAL_EOL();
|
||||
}
|
||||
|
||||
void TWIBus::echobuffer(const char pref[], uint8_t adr) {
|
||||
echoprefix(buffer_s, pref, adr);
|
||||
void TWIBus::echobuffer(FSTR_P const prefix, uint8_t adr) {
|
||||
echoprefix(buffer_s, prefix, adr);
|
||||
LOOP_L_N(i, buffer_s) SERIAL_CHAR(buffer[i]);
|
||||
SERIAL_EOL();
|
||||
}
|
||||
@@ -103,22 +152,22 @@ void TWIBus::echobuffer(const char pref[], uint8_t adr) {
|
||||
bool TWIBus::request(const uint8_t bytes) {
|
||||
if (!addr) return false;
|
||||
|
||||
debug(PSTR("request"), bytes);
|
||||
debug(F("request"), bytes);
|
||||
|
||||
// requestFrom() is a blocking function
|
||||
if (Wire.requestFrom(I2C_ADDRESS(addr), bytes) == 0) {
|
||||
debug("request fail", I2C_ADDRESS(addr));
|
||||
debug(F("request fail"), I2C_ADDRESS(addr));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TWIBus::relay(const uint8_t bytes) {
|
||||
debug(PSTR("relay"), bytes);
|
||||
void TWIBus::relay(const uint8_t bytes, const uint8_t style/*=0*/) {
|
||||
debug(F("relay"), bytes);
|
||||
|
||||
if (request(bytes))
|
||||
echodata(bytes, PSTR("i2c-reply"), addr);
|
||||
echodata(bytes, F("i2c-reply"), addr, style);
|
||||
}
|
||||
|
||||
uint8_t TWIBus::capture(char *dst, const uint8_t bytes) {
|
||||
@@ -127,7 +176,7 @@ uint8_t TWIBus::capture(char *dst, const uint8_t bytes) {
|
||||
while (count < bytes && Wire.available())
|
||||
dst[count++] = Wire.read();
|
||||
|
||||
debug(PSTR("capture"), count);
|
||||
debug(F("capture"), count);
|
||||
|
||||
return count;
|
||||
}
|
||||
@@ -140,12 +189,12 @@ void TWIBus::flush() {
|
||||
#if I2C_SLAVE_ADDRESS > 0
|
||||
|
||||
void TWIBus::receive(uint8_t bytes) {
|
||||
debug(PSTR("receive"), bytes);
|
||||
echodata(bytes, PSTR("i2c-receive"), 0);
|
||||
debug(F("receive"), bytes);
|
||||
echodata(bytes, F("i2c-receive"), 0);
|
||||
}
|
||||
|
||||
void TWIBus::reply(char str[]/*=nullptr*/) {
|
||||
debug(PSTR("reply"), str);
|
||||
debug(F("reply"), str);
|
||||
|
||||
if (str) {
|
||||
reset();
|
||||
@@ -170,18 +219,16 @@ void TWIBus::flush() {
|
||||
#if ENABLED(DEBUG_TWIBUS)
|
||||
|
||||
// static
|
||||
void TWIBus::prefix(const char func[]) {
|
||||
SERIAL_ECHOPGM("TWIBus::");
|
||||
SERIAL_ECHOPGM_P(func);
|
||||
SERIAL_ECHOPGM(": ");
|
||||
void TWIBus::prefix(FSTR_P const func) {
|
||||
SERIAL_ECHOPGM("TWIBus::", func, ": ");
|
||||
}
|
||||
void TWIBus::debug(const char func[], uint32_t adr) {
|
||||
void TWIBus::debug(FSTR_P const func, uint32_t adr) {
|
||||
if (DEBUGGING(INFO)) { prefix(func); SERIAL_ECHOLN(adr); }
|
||||
}
|
||||
void TWIBus::debug(const char func[], char c) {
|
||||
void TWIBus::debug(FSTR_P const func, char c) {
|
||||
if (DEBUGGING(INFO)) { prefix(func); SERIAL_ECHOLN(c); }
|
||||
}
|
||||
void TWIBus::debug(const char func[], char str[]) {
|
||||
void TWIBus::debug(FSTR_P const func, char str[]) {
|
||||
if (DEBUGGING(INFO)) { prefix(func); SERIAL_ECHOLN(str); }
|
||||
}
|
||||
|
||||
|
@@ -142,7 +142,7 @@ class TWIBus {
|
||||
*
|
||||
* @param bytes the number of bytes to request
|
||||
*/
|
||||
static void echoprefix(uint8_t bytes, const char prefix[], uint8_t adr);
|
||||
static void echoprefix(uint8_t bytes, FSTR_P const prefix, uint8_t adr);
|
||||
|
||||
/**
|
||||
* @brief Echo data on the bus to serial
|
||||
@@ -150,8 +150,9 @@ class TWIBus {
|
||||
* to serial in a parser-friendly format.
|
||||
*
|
||||
* @param bytes the number of bytes to request
|
||||
* @param style Output format for the bytes, 0 = Raw byte [default], 1 = Hex characters, 2 = uint16_t
|
||||
*/
|
||||
static void echodata(uint8_t bytes, const char prefix[], uint8_t adr);
|
||||
static void echodata(uint8_t bytes, FSTR_P const prefix, uint8_t adr, const uint8_t style=0);
|
||||
|
||||
/**
|
||||
* @brief Echo data in the buffer to serial
|
||||
@@ -160,7 +161,7 @@ class TWIBus {
|
||||
*
|
||||
* @param bytes the number of bytes to request
|
||||
*/
|
||||
void echobuffer(const char prefix[], uint8_t adr);
|
||||
void echobuffer(FSTR_P const prefix, uint8_t adr);
|
||||
|
||||
/**
|
||||
* @brief Request data from the slave device and wait.
|
||||
@@ -192,10 +193,11 @@ class TWIBus {
|
||||
* @brief Request data from the slave device, echo to serial.
|
||||
* @details Request a number of bytes from a slave device and output
|
||||
* the returned data to serial in a parser-friendly format.
|
||||
* @style Output format for the bytes, 0 = raw byte [default], 1 = Hex characters, 2 = uint16_t
|
||||
*
|
||||
* @param bytes the number of bytes to request
|
||||
*/
|
||||
void relay(const uint8_t bytes);
|
||||
void relay(const uint8_t bytes, const uint8_t style=0);
|
||||
|
||||
#if I2C_SLAVE_ADDRESS > 0
|
||||
|
||||
@@ -237,17 +239,16 @@ class TWIBus {
|
||||
* @brief Prints a debug message
|
||||
* @details Prints a simple debug message "TWIBus::function: value"
|
||||
*/
|
||||
static void prefix(const char func[]);
|
||||
static void debug(const char func[], uint32_t adr);
|
||||
static void debug(const char func[], char c);
|
||||
static void debug(const char func[], char adr[]);
|
||||
static inline void debug(const char func[], uint8_t v) { debug(func, (uint32_t)v); }
|
||||
static void prefix(FSTR_P const func);
|
||||
static void debug(FSTR_P const func, uint32_t adr);
|
||||
static void debug(FSTR_P const func, char c);
|
||||
static void debug(FSTR_P const func, char adr[]);
|
||||
#else
|
||||
static inline void debug(const char[], uint32_t) {}
|
||||
static inline void debug(const char[], char) {}
|
||||
static inline void debug(const char[], char[]) {}
|
||||
static inline void debug(const char[], uint8_t) {}
|
||||
static void debug(FSTR_P const, uint32_t) {}
|
||||
static void debug(FSTR_P const, char) {}
|
||||
static void debug(FSTR_P const, char[]) {}
|
||||
#endif
|
||||
static void debug(FSTR_P const func, uint8_t v) { debug(func, (uint32_t)v); }
|
||||
};
|
||||
|
||||
extern TWIBus i2c;
|
||||
|
67
Marlin/src/feature/x_twist.cpp
Normal file
67
Marlin/src/feature/x_twist.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(X_AXIS_TWIST_COMPENSATION)
|
||||
|
||||
#include "x_twist.h"
|
||||
#include "../module/probe.h"
|
||||
|
||||
XATC xatc;
|
||||
|
||||
bool XATC::enabled;
|
||||
float XATC::spacing, XATC::start;
|
||||
xatc_array_t XATC::z_offset; // Initialized by settings.load()
|
||||
|
||||
void XATC::reset() {
|
||||
constexpr float xzo[] = XATC_Z_OFFSETS;
|
||||
static_assert(COUNT(xzo) == XATC_MAX_POINTS, "XATC_Z_OFFSETS is the wrong size.");
|
||||
COPY(z_offset, xzo);
|
||||
start = probe.min_x();
|
||||
spacing = (probe.max_x() - start) / (XATC_MAX_POINTS - 1);
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
void XATC::print_points() {
|
||||
SERIAL_ECHOLNPGM(" X-Twist Correction:");
|
||||
LOOP_L_N(x, XATC_MAX_POINTS) {
|
||||
SERIAL_CHAR(' ');
|
||||
if (!isnan(z_offset[x]))
|
||||
serial_offset(z_offset[x]);
|
||||
else
|
||||
LOOP_L_N(i, 6) SERIAL_CHAR(i ? '=' : ' ');
|
||||
}
|
||||
SERIAL_EOL();
|
||||
}
|
||||
|
||||
float lerp(const_float_t t, const_float_t a, const_float_t b) { return a + t * (b - a); }
|
||||
|
||||
float XATC::compensation(const xy_pos_t &raw) {
|
||||
if (!enabled) return 0;
|
||||
if (NEAR_ZERO(spacing)) return 0;
|
||||
float t = (raw.x - start) / spacing;
|
||||
const int i = constrain(FLOOR(t), 0, XATC_MAX_POINTS - 2);
|
||||
t -= i;
|
||||
return lerp(t, z_offset[i], z_offset[i + 1]);
|
||||
}
|
||||
|
||||
#endif // X_AXIS_TWIST_COMPENSATION
|
40
Marlin/src/feature/x_twist.h
Normal file
40
Marlin/src/feature/x_twist.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2021 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/MarlinConfigPre.h"
|
||||
|
||||
typedef float xatc_array_t[XATC_MAX_POINTS];
|
||||
|
||||
class XATC {
|
||||
static bool enabled;
|
||||
public:
|
||||
static float spacing, start;
|
||||
static xatc_array_t z_offset;
|
||||
|
||||
static void reset();
|
||||
static void set_enabled(const bool ena) { enabled = ena; }
|
||||
static float compensation(const xy_pos_t &raw);
|
||||
static void print_points();
|
||||
};
|
||||
|
||||
extern XATC xatc;
|
@@ -33,35 +33,35 @@
|
||||
|
||||
ZStepperAlign z_stepper_align;
|
||||
|
||||
xy_pos_t ZStepperAlign::xy[NUM_Z_STEPPER_DRIVERS];
|
||||
xy_pos_t ZStepperAlign::xy[NUM_Z_STEPPERS];
|
||||
|
||||
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
|
||||
xy_pos_t ZStepperAlign::stepper_xy[NUM_Z_STEPPER_DRIVERS];
|
||||
#if HAS_Z_STEPPER_ALIGN_STEPPER_XY
|
||||
xy_pos_t ZStepperAlign::stepper_xy[NUM_Z_STEPPERS];
|
||||
#endif
|
||||
|
||||
void ZStepperAlign::reset_to_default() {
|
||||
#ifdef Z_STEPPER_ALIGN_XY
|
||||
|
||||
constexpr xy_pos_t xy_init[] = Z_STEPPER_ALIGN_XY;
|
||||
static_assert(COUNT(xy_init) == NUM_Z_STEPPER_DRIVERS,
|
||||
static_assert(COUNT(xy_init) == NUM_Z_STEPPERS,
|
||||
"Z_STEPPER_ALIGN_XY requires "
|
||||
#if NUM_Z_STEPPER_DRIVERS == 4
|
||||
#if NUM_Z_STEPPERS == 4
|
||||
"four {X,Y} entries (Z, Z2, Z3, and Z4)."
|
||||
#elif NUM_Z_STEPPER_DRIVERS == 3
|
||||
#elif NUM_Z_STEPPERS == 3
|
||||
"three {X,Y} entries (Z, Z2, and Z3)."
|
||||
#else
|
||||
"two {X,Y} entries (Z and Z2)."
|
||||
#endif
|
||||
);
|
||||
|
||||
#define VALIDATE_ALIGN_POINT(N) static_assert(N >= NUM_Z_STEPPER_DRIVERS || Probe::build_time::can_reach(xy_init[N]), \
|
||||
#define VALIDATE_ALIGN_POINT(N) static_assert(N >= NUM_Z_STEPPERS || Probe::build_time::can_reach(xy_init[N]), \
|
||||
"Z_STEPPER_ALIGN_XY point " STRINGIFY(N) " is not reachable with the default NOZZLE_TO_PROBE offset and PROBING_MARGIN.")
|
||||
VALIDATE_ALIGN_POINT(0); VALIDATE_ALIGN_POINT(1); VALIDATE_ALIGN_POINT(2); VALIDATE_ALIGN_POINT(3);
|
||||
|
||||
#else // !Z_STEPPER_ALIGN_XY
|
||||
|
||||
const xy_pos_t xy_init[] = {
|
||||
#if NUM_Z_STEPPER_DRIVERS >= 3 // First probe point...
|
||||
#if NUM_Z_STEPPERS >= 3 // First probe point...
|
||||
#if !Z_STEPPERS_ORIENTATION
|
||||
{ probe.min_x(), probe.min_y() }, // SW
|
||||
#elif Z_STEPPERS_ORIENTATION == 1
|
||||
@@ -73,7 +73,7 @@ void ZStepperAlign::reset_to_default() {
|
||||
#else
|
||||
#error "Z_STEPPERS_ORIENTATION must be from 0 to 3 (first point SW, NW, NE, SE)."
|
||||
#endif
|
||||
#if NUM_Z_STEPPER_DRIVERS == 4 // 3 more points...
|
||||
#if NUM_Z_STEPPERS == 4 // 3 more points...
|
||||
#if !Z_STEPPERS_ORIENTATION
|
||||
{ probe.min_x(), probe.max_y() }, { probe.max_x(), probe.max_y() }, { probe.max_x(), probe.min_y() } // SW
|
||||
#elif Z_STEPPERS_ORIENTATION == 1
|
||||
@@ -103,14 +103,14 @@ void ZStepperAlign::reset_to_default() {
|
||||
|
||||
COPY(xy, xy_init);
|
||||
|
||||
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
|
||||
#if HAS_Z_STEPPER_ALIGN_STEPPER_XY
|
||||
constexpr xy_pos_t stepper_xy_init[] = Z_STEPPER_ALIGN_STEPPER_XY;
|
||||
static_assert(
|
||||
COUNT(stepper_xy_init) == NUM_Z_STEPPER_DRIVERS,
|
||||
COUNT(stepper_xy_init) == NUM_Z_STEPPERS,
|
||||
"Z_STEPPER_ALIGN_STEPPER_XY requires "
|
||||
#if NUM_Z_STEPPER_DRIVERS == 4
|
||||
#if NUM_Z_STEPPERS == 4
|
||||
"four {X,Y} entries (Z, Z2, Z3, and Z4)."
|
||||
#elif NUM_Z_STEPPER_DRIVERS == 3
|
||||
#elif NUM_Z_STEPPERS == 3
|
||||
"three {X,Y} entries (Z, Z2, and Z3)."
|
||||
#endif
|
||||
);
|
||||
|
@@ -29,10 +29,10 @@
|
||||
|
||||
class ZStepperAlign {
|
||||
public:
|
||||
static xy_pos_t xy[NUM_Z_STEPPER_DRIVERS];
|
||||
static xy_pos_t xy[NUM_Z_STEPPERS];
|
||||
|
||||
#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS)
|
||||
static xy_pos_t stepper_xy[NUM_Z_STEPPER_DRIVERS];
|
||||
#if HAS_Z_STEPPER_ALIGN_STEPPER_XY
|
||||
static xy_pos_t stepper_xy[NUM_Z_STEPPERS];
|
||||
#endif
|
||||
|
||||
static void reset_to_default();
|
||||
|
Reference in New Issue
Block a user