Ender3 - Marlin 2.1.2.4 build (#16)
This commit was merged in pull request #16.
This commit is contained in:
@@ -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
|
||||
@@ -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;
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* 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(I2C_AMMETER)
|
||||
|
||||
#include "ammeter.h"
|
||||
|
||||
#ifndef I2C_AMMETER_IMAX
|
||||
#define I2C_AMMETER_IMAX 0.500 // Calibration range 500 Milliamps
|
||||
#endif
|
||||
|
||||
INA226 ina;
|
||||
|
||||
Ammeter ammeter;
|
||||
|
||||
float Ammeter::scale;
|
||||
float Ammeter::current;
|
||||
|
||||
void Ammeter::init() {
|
||||
ina.begin();
|
||||
ina.configure(INA226_AVERAGES_16, INA226_BUS_CONV_TIME_1100US, INA226_SHUNT_CONV_TIME_1100US, INA226_MODE_SHUNT_BUS_CONT);
|
||||
ina.calibrate(I2C_AMMETER_SHUNT_RESISTOR, I2C_AMMETER_IMAX);
|
||||
}
|
||||
|
||||
float Ammeter::read() {
|
||||
scale = 1;
|
||||
current = ina.readShuntCurrent();
|
||||
if (current <= 0.0001f) current = 0; // Clean up least-significant-bit amplification errors
|
||||
if (current < 0.1f) scale = 1000;
|
||||
return current * scale;
|
||||
}
|
||||
|
||||
#endif // I2C_AMMETER
|
||||
39
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/ammeter.h
Normal file
39
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/ammeter.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 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"
|
||||
|
||||
#include <Wire.h>
|
||||
#include <INA226.h>
|
||||
|
||||
class Ammeter {
|
||||
private:
|
||||
static float scale;
|
||||
|
||||
public:
|
||||
static float current;
|
||||
static void init();
|
||||
static float read();
|
||||
};
|
||||
|
||||
extern Ammeter ammeter;
|
||||
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(BABYSTEPPING)
|
||||
|
||||
#include "babystep.h"
|
||||
#include "../MarlinCore.h"
|
||||
#include "../module/motion.h" // for axes_should_home()
|
||||
#include "../module/planner.h" // for axis_steps_per_mm[]
|
||||
#include "../module/stepper.h"
|
||||
|
||||
#if ENABLED(BABYSTEP_ALWAYS_AVAILABLE)
|
||||
#include "../gcode/gcode.h"
|
||||
#endif
|
||||
|
||||
Babystep babystep;
|
||||
|
||||
volatile int16_t Babystep::steps[BS_AXIS_IND(Z_AXIS) + 1];
|
||||
#if ENABLED(BABYSTEP_DISPLAY_TOTAL)
|
||||
int16_t Babystep::axis_total[BS_TOTAL_IND(Z_AXIS) + 1];
|
||||
#endif
|
||||
int16_t Babystep::accum;
|
||||
|
||||
void Babystep::step_axis(const AxisEnum axis) {
|
||||
const int16_t curTodo = steps[BS_AXIS_IND(axis)]; // get rid of volatile for performance
|
||||
if (curTodo) {
|
||||
stepper.do_babystep((AxisEnum)axis, curTodo > 0);
|
||||
if (curTodo > 0) steps[BS_AXIS_IND(axis)]--; else steps[BS_AXIS_IND(axis)]++;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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 // BABYSTEPPING
|
||||
86
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/babystep.h
Normal file
86
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/babystep.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* 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"
|
||||
|
||||
#if ENABLED(INTEGRATED_BABYSTEPPING)
|
||||
#define BABYSTEPS_PER_SEC 1000UL
|
||||
#define BABYSTEP_TICKS ((STEPPER_TIMER_RATE) / (BABYSTEPS_PER_SEC))
|
||||
#else
|
||||
#define BABYSTEPS_PER_SEC 976UL
|
||||
#define BABYSTEP_TICKS ((TEMP_TIMER_RATE) / (BABYSTEPS_PER_SEC))
|
||||
#endif
|
||||
|
||||
#if ANY(IS_CORE, BABYSTEP_XY, I2C_POSITION_ENCODERS)
|
||||
#define BS_AXIS_IND(A) A
|
||||
#define BS_AXIS(I) AxisEnum(I)
|
||||
#else
|
||||
#define BS_AXIS_IND(A) 0
|
||||
#define BS_AXIS(I) Z_AXIS
|
||||
#endif
|
||||
|
||||
#if ENABLED(BABYSTEP_DISPLAY_TOTAL)
|
||||
#if ENABLED(BABYSTEP_XY)
|
||||
#define BS_TOTAL_IND(A) A
|
||||
#else
|
||||
#define BS_TOTAL_IND(A) 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
class Babystep {
|
||||
public:
|
||||
static volatile int16_t steps[BS_AXIS_IND(Z_AXIS) + 1];
|
||||
static int16_t accum; // Total babysteps in current edit
|
||||
|
||||
#if ENABLED(BABYSTEP_DISPLAY_TOTAL)
|
||||
static int16_t axis_total[BS_TOTAL_IND(Z_AXIS) + 1]; // Total babysteps since G28
|
||||
static void reset_total(const AxisEnum axis) {
|
||||
if (TERN1(BABYSTEP_XY, axis == Z_AXIS))
|
||||
axis_total[BS_TOTAL_IND(axis)] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void add_steps(const AxisEnum axis, const int16_t distance);
|
||||
static void add_mm(const AxisEnum axis, const_float_t mm);
|
||||
|
||||
#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)];
|
||||
}
|
||||
|
||||
//
|
||||
// Called by the Temperature or Stepper ISR to
|
||||
// apply accumulated babysteps to the axes.
|
||||
//
|
||||
static void task() {
|
||||
for (uint8_t i = 0; i <= BS_AXIS_IND(Z_AXIS); ++i) step_axis(BS_AXIS(i));
|
||||
}
|
||||
|
||||
private:
|
||||
static void step_axis(const AxisEnum axis);
|
||||
};
|
||||
|
||||
extern Babystep babystep;
|
||||
217
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/backlash.cpp
Normal file
217
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/backlash.cpp
Normal file
@@ -0,0 +1,217 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
|
||||
#if ENABLED(BACKLASH_COMPENSATION)
|
||||
|
||||
#include "backlash.h"
|
||||
|
||||
#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;
|
||||
#else
|
||||
const xyz_float_t Backlash::distance_mm = BACKLASH_DISTANCE_MM;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if ENABLED(BACKLASH_GCODE)
|
||||
uint8_t Backlash::correction = (BACKLASH_CORRECTION) * all_on;
|
||||
#ifdef BACKLASH_SMOOTHING_MM
|
||||
float Backlash::smoothing_mm = BACKLASH_SMOOTHING_MM;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if ENABLED(MEASURE_BACKLASH_WHEN_PROBING)
|
||||
xyz_float_t Backlash::measured_mm{0};
|
||||
xyz_uint8_t Backlash::measured_count{0};
|
||||
#endif
|
||||
|
||||
Backlash backlash;
|
||||
|
||||
/**
|
||||
* To minimize seams in the printed part, backlash correction only adds
|
||||
* steps to the current segment (instead of creating a new segment, which
|
||||
* causes discontinuities and print artifacts).
|
||||
*
|
||||
* With a non-zero BACKLASH_SMOOTHING_MM value the backlash correction is
|
||||
* spread over multiple segments, smoothing out artifacts even more.
|
||||
*/
|
||||
|
||||
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) {
|
||||
axis_bits_t changed_dir = last_direction_bits ^ dm;
|
||||
// Ignore direction change unless steps are taken in that direction
|
||||
#if DISABLED(CORE_BACKLASH) || ANY(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);
|
||||
#elif CORE_IS_XY
|
||||
if (!(da + db)) CBI(changed_dir, X_AXIS);
|
||||
if (!(da - db)) CBI(changed_dir, Y_AXIS);
|
||||
if (!dc) CBI(changed_dir, Z_AXIS);
|
||||
#elif CORE_IS_XZ
|
||||
if (!(da + dc)) CBI(changed_dir, X_AXIS);
|
||||
if (!(da - dc)) CBI(changed_dir, Z_AXIS);
|
||||
if (!db) CBI(changed_dir, Y_AXIS);
|
||||
#elif CORE_IS_YZ
|
||||
if (!(db + dc)) CBI(changed_dir, Y_AXIS);
|
||||
if (!(db - dc)) CBI(changed_dir, Z_AXIS);
|
||||
if (!da) CBI(changed_dir, X_AXIS);
|
||||
#endif
|
||||
last_direction_bits ^= changed_dir;
|
||||
|
||||
if (!correction && !residual_error) return;
|
||||
|
||||
#ifdef BACKLASH_SMOOTHING_MM
|
||||
// The segment proportion is a value greater than 0.0 indicating how much residual_error
|
||||
// is corrected for in this segment. The contribution is based on segment length and the
|
||||
// smoothing distance. Since the computation of this proportion involves a floating point
|
||||
// division, defer computation until needed.
|
||||
float segment_proportion = 0;
|
||||
#endif
|
||||
|
||||
const float f_corr = float(correction) / all_on;
|
||||
|
||||
LOOP_NUM_AXES(axis) {
|
||||
if (distance_mm[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] += (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
|
||||
if (segment_proportion == 0) segment_proportion = _MIN(1.0f, block->millimeters / smoothing_mm);
|
||||
error_correction = CEIL(segment_proportion * error_correction);
|
||||
}
|
||||
#endif
|
||||
|
||||
// This correction reduces the residual error and adds block steps
|
||||
if (error_correction) {
|
||||
block->steps[axis] += ABS(error_correction);
|
||||
#if ENABLED(CORE_BACKLASH)
|
||||
switch (axis) {
|
||||
case CORE_AXIS_1:
|
||||
//block->steps[CORE_AXIS_2] += influence_distance_mm[axis] * planner.settings.axis_steps_per_mm[CORE_AXIS_2];
|
||||
//SERIAL_ECHOLNPGM("CORE_AXIS_1 dir change. distance=", distance_mm[axis], " r.err=", residual_error[axis],
|
||||
// " da=", da, " db=", db, " block->steps[axis]=", block->steps[axis], " err_corr=", error_correction);
|
||||
break;
|
||||
case CORE_AXIS_2:
|
||||
//block->steps[CORE_AXIS_1] += influence_distance_mm[axis] * planner.settings.axis_steps_per_mm[CORE_AXIS_1];;
|
||||
//SERIAL_ECHOLNPGM("CORE_AXIS_2 dir change. distance=", distance_mm[axis], " r.err=", residual_error[axis],
|
||||
// " da=", da, " db=", db, " block->steps[axis]=", block->steps[axis], " err_corr=", error_correction);
|
||||
break;
|
||||
case NORMAL_AXIS: break;
|
||||
}
|
||||
residual_error[axis] = 0; // No residual_error needed for next CORE block, I think...
|
||||
#else
|
||||
residual_error[axis] -= error_correction;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
// Measure Z backlash by raising nozzle in increments until probe deactivates
|
||||
void Backlash::measure_with_probe() {
|
||||
if (measured_count.z == 255) return;
|
||||
|
||||
const float start_height = current_position.z;
|
||||
while (current_position.z < (start_height + BACKLASH_MEASUREMENT_LIMIT) && PROBE_TRIGGERED())
|
||||
do_blocking_move_to_z(current_position.z + BACKLASH_MEASUREMENT_RESOLUTION, MMM_TO_MMS(BACKLASH_MEASUREMENT_FEEDRATE));
|
||||
|
||||
// The backlash from all probe points is averaged, so count the number of measurements
|
||||
measured_mm.z += current_position.z - start_height;
|
||||
measured_count.z++;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif // BACKLASH_COMPENSATION
|
||||
96
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/backlash.h
Normal file
96
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/backlash.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* 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"
|
||||
#include "../module/planner.h"
|
||||
|
||||
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 uint8_t correction;
|
||||
static xyz_float_t distance_mm;
|
||||
#ifdef BACKLASH_SMOOTHING_MM
|
||||
static float smoothing_mm;
|
||||
#endif
|
||||
#else
|
||||
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;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if ENABLED(MEASURE_BACKLASH_WHEN_PROBING)
|
||||
static xyz_float_t measured_mm;
|
||||
static xyz_uint8_t measured_count;
|
||||
#endif
|
||||
|
||||
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
|
||||
, measured_count[a] > 0 ? measured_mm[a] / measured_count[a] : 0
|
||||
, 0
|
||||
);
|
||||
}
|
||||
|
||||
static bool has_measurement(const AxisEnum a) {
|
||||
UNUSED(a);
|
||||
return TERN0(MEASURE_BACKLASH_WHEN_PROBING, measured_count[a] > 0);
|
||||
}
|
||||
|
||||
static bool has_any_measurement() {
|
||||
return has_measurement(X_AXIS) || has_measurement(Y_AXIS) || has_measurement(Z_AXIS);
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
|
||||
#if ENABLED(BARICUDA)
|
||||
|
||||
#include "baricuda.h"
|
||||
|
||||
uint8_t baricuda_valve_pressure = 0,
|
||||
baricuda_e_to_p_pressure = 0;
|
||||
|
||||
#endif // BARICUDA
|
||||
25
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/baricuda.h
Normal file
25
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/baricuda.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
extern uint8_t baricuda_valve_pressure,
|
||||
baricuda_e_to_p_pressure;
|
||||
@@ -0,0 +1,439 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(AUTO_BED_LEVELING_BILINEAR)
|
||||
|
||||
#include "../bedlevel.h"
|
||||
|
||||
#include "../../../module/motion.h"
|
||||
|
||||
#define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
#include "../../../core/debug_out.h"
|
||||
|
||||
#if ENABLED(EXTENSIBLE_UI)
|
||||
#include "../../../lcd/extui/ui_api.h"
|
||||
#endif
|
||||
|
||||
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
|
||||
*/
|
||||
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 [");
|
||||
if (x < 10) DEBUG_CHAR(' ');
|
||||
DEBUG_ECHO(x);
|
||||
DEBUG_CHAR(xdir ? (xdir > 0 ? '+' : '-') : ' ');
|
||||
DEBUG_CHAR(' ');
|
||||
if (y < 10) DEBUG_CHAR(' ');
|
||||
DEBUG_ECHO(y);
|
||||
DEBUG_CHAR(ydir ? (ydir > 0 ? '+' : '-') : ' ');
|
||||
DEBUG_ECHOLNPGM("]");
|
||||
}
|
||||
|
||||
// Get X neighbors, Y neighbors, and XY neighbors
|
||||
const uint8_t x1 = x + xdir, y1 = y + ydir, x2 = x1 + xdir, y2 = y1 + ydir;
|
||||
float a1 = z_values[x1][y ], a2 = z_values[x2][y ],
|
||||
b1 = z_values[x ][y1], b2 = z_values[x ][y2],
|
||||
c1 = z_values[x1][y1], c2 = z_values[x2][y2];
|
||||
|
||||
// Treat far unprobed points as zero, near as equal to far
|
||||
if (isnan(a2)) a2 = 0.0;
|
||||
if (isnan(a1)) a1 = a2;
|
||||
if (isnan(b2)) b2 = 0.0;
|
||||
if (isnan(b1)) b1 = b2;
|
||||
if (isnan(c2)) c2 = 0.0;
|
||||
if (isnan(c1)) c1 = c2;
|
||||
|
||||
const float a = 2 * a1 - a2, b = 2 * b1 - b2, c = 2 * c1 - c2;
|
||||
|
||||
// Take the average instead of the median
|
||||
z_values[x][y] = (a + b + c) / 3.0;
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(x, y, z_values[x][y]));
|
||||
|
||||
// Median is robust (ignores outliers).
|
||||
// z_values[x][y] = (a < b) ? ((b < c) ? b : (c < a) ? a : c)
|
||||
// : ((c < b) ? b : (a < c) ? a : c);
|
||||
}
|
||||
|
||||
//Enable this if your SCARA uses 180° of total area
|
||||
//#define EXTRAPOLATE_FROM_EDGE
|
||||
|
||||
#if ENABLED(EXTRAPOLATE_FROM_EDGE)
|
||||
#if (GRID_MAX_POINTS_X) < (GRID_MAX_POINTS_Y)
|
||||
#define HALF_IN_X
|
||||
#elif (GRID_MAX_POINTS_Y) < (GRID_MAX_POINTS_X)
|
||||
#define HALF_IN_Y
|
||||
#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 LevelingBilinear::extrapolate_unprobed_bed_level() {
|
||||
#ifdef HALF_IN_X
|
||||
constexpr uint8_t ctrx2 = 0, xend = GRID_MAX_POINTS_X - 1;
|
||||
#else
|
||||
constexpr uint8_t ctrx1 = (GRID_MAX_CELLS_X) / 2, // left-of-center
|
||||
ctrx2 = (GRID_MAX_POINTS_X) / 2, // right-of-center
|
||||
xend = ctrx1;
|
||||
#endif
|
||||
|
||||
#ifdef HALF_IN_Y
|
||||
constexpr uint8_t ctry2 = 0, yend = GRID_MAX_POINTS_Y - 1;
|
||||
#else
|
||||
constexpr uint8_t ctry1 = (GRID_MAX_CELLS_Y) / 2, // top-of-center
|
||||
ctry2 = (GRID_MAX_POINTS_Y) / 2, // bottom-of-center
|
||||
yend = ctry1;
|
||||
#endif
|
||||
|
||||
for (uint8_t xo = 0; xo <= xend; ++xo)
|
||||
for (uint8_t yo = 0; yo <= yend; ++yo) {
|
||||
uint8_t x2 = ctrx2 + xo, y2 = ctry2 + yo;
|
||||
#ifndef HALF_IN_X
|
||||
const uint8_t x1 = ctrx1 - xo;
|
||||
#endif
|
||||
#ifndef HALF_IN_Y
|
||||
const uint8_t y1 = ctry1 - yo;
|
||||
#ifndef HALF_IN_X
|
||||
extrapolate_one_point(x1, y1, +1, +1); // left-below + +
|
||||
#endif
|
||||
extrapolate_one_point(x2, y1, -1, +1); // right-below - +
|
||||
#endif
|
||||
#ifndef HALF_IN_X
|
||||
extrapolate_one_point(x1, y2, +1, -1); // left-above + -
|
||||
#endif
|
||||
extrapolate_one_point(x2, y2, -1, -1); // right-above - -
|
||||
}
|
||||
}
|
||||
|
||||
void LevelingBilinear::print_leveling_grid(const bed_mesh_t* _z_values/*=nullptr*/) {
|
||||
// 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, _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_TEMP_POINTS_X (GRID_MAX_POINTS_X + 2)
|
||||
#define ABL_TEMP_POINTS_Y (GRID_MAX_POINTS_Y + 2)
|
||||
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 LevelingBilinear::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.
|
||||
// These values are only requested for the edges of the mesh, which are always an actual mesh point,
|
||||
// and do not require interpolation. When interpolation is not needed, this "Mesh + 2" point is
|
||||
// cancelled out in virt_cmr and does not impact the result. Return 0.0 rather than
|
||||
// making this function more complex by extrapolating two points.
|
||||
return 0.0;
|
||||
}
|
||||
if (!x || x == ABL_TEMP_POINTS_X - 1) {
|
||||
if (x) {
|
||||
ep = (GRID_MAX_POINTS_X) - 1;
|
||||
ip = GRID_MAX_CELLS_X - 1;
|
||||
}
|
||||
if (WITHIN(y, 1, ABL_TEMP_POINTS_Y - 2))
|
||||
return LINEAR_EXTRAPOLATION(
|
||||
z_values[ep][y - 1],
|
||||
z_values[ip][y - 1]
|
||||
);
|
||||
else
|
||||
return LINEAR_EXTRAPOLATION(
|
||||
virt_coord(ep + 1, y),
|
||||
virt_coord(ip + 1, y)
|
||||
);
|
||||
}
|
||||
if (!y || y == ABL_TEMP_POINTS_Y - 1) {
|
||||
if (y) {
|
||||
ep = (GRID_MAX_POINTS_Y) - 1;
|
||||
ip = GRID_MAX_CELLS_Y - 1;
|
||||
}
|
||||
if (WITHIN(x, 1, ABL_TEMP_POINTS_X - 2))
|
||||
return LINEAR_EXTRAPOLATION(
|
||||
z_values[x - 1][ep],
|
||||
z_values[x - 1][ip]
|
||||
);
|
||||
else
|
||||
return LINEAR_EXTRAPOLATION(
|
||||
virt_coord(x, ep + 1),
|
||||
virt_coord(x, ip + 1)
|
||||
);
|
||||
}
|
||||
return z_values[x - 1][y - 1];
|
||||
}
|
||||
|
||||
float LevelingBilinear::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))
|
||||
+ p[i+1] * t * (1 + 4 * t - 3 * sq(t))
|
||||
- p[i+2] * sq(t) * (1 - t)
|
||||
) * 0.5f;
|
||||
}
|
||||
|
||||
float LevelingBilinear::virt_2cmr(const uint8_t x, const uint8_t y, const_float_t tx, const_float_t ty) {
|
||||
float row[4], column[4];
|
||||
for (uint8_t i = 0; i < 4; ++i) {
|
||||
for (uint8_t j = 0; j < 4; ++j) {
|
||||
column[j] = virt_coord(i + x - 1, j + y - 1);
|
||||
}
|
||||
row[i] = virt_cmr(column, 1, ty);
|
||||
}
|
||||
return virt_cmr(row, 1, tx);
|
||||
}
|
||||
|
||||
void LevelingBilinear::subdivide_mesh() {
|
||||
grid_spacing_virt = grid_spacing / (BILINEAR_SUBDIVISIONS);
|
||||
grid_factor_virt = grid_spacing_virt.reciprocal();
|
||||
for (uint8_t y = 0; y < GRID_MAX_POINTS_Y; ++y)
|
||||
for (uint8_t x = 0; x < GRID_MAX_POINTS_X; ++x)
|
||||
for (uint8_t ty = 0; ty < BILINEAR_SUBDIVISIONS; ++ty)
|
||||
for (uint8_t tx = 0; tx < BILINEAR_SUBDIVISIONS; ++tx) {
|
||||
if ((ty && y == (GRID_MAX_POINTS_Y) - 1) || (tx && x == (GRID_MAX_POINTS_X) - 1))
|
||||
continue;
|
||||
z_values_virt[x * (BILINEAR_SUBDIVISIONS) + tx][y * (BILINEAR_SUBDIVISIONS) + ty] =
|
||||
virt_2cmr(
|
||||
x + 1,
|
||||
y + 1,
|
||||
(float)tx / (BILINEAR_SUBDIVISIONS),
|
||||
(float)ty / (BILINEAR_SUBDIVISIONS)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ABL_BILINEAR_SUBDIVISION
|
||||
|
||||
// Refresh after other values have been updated
|
||||
void LevelingBilinear::refresh_bed_level() {
|
||||
TERN_(ABL_BILINEAR_SUBDIVISION, subdivide_mesh());
|
||||
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) 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) 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 LevelingBilinear::get_z_correction(const xy_pos_t &raw) {
|
||||
|
||||
static float z1, d2, z3, d4, L, D;
|
||||
|
||||
static xy_pos_t ratio;
|
||||
|
||||
// Whole units for the grid line indices. Constrained within bounds.
|
||||
static xy_int8_t thisg, nextg;
|
||||
|
||||
// XY relative to the probed area
|
||||
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
|
||||
#else
|
||||
#define FAR_EDGE_OR_BOX 1 // Just use the grid far edge
|
||||
#endif
|
||||
|
||||
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
|
||||
|
||||
#if DISABLED(EXTRAPOLATE_BEYOND_GRID)
|
||||
// Beyond the grid maintain height at grid edges
|
||||
NOLESS(ratio.x, 0); // Never <0 (>1 is ok when nextg.x==thisg.x)
|
||||
#endif
|
||||
|
||||
thisg.x = gx;
|
||||
nextg.x = _MIN(thisg.x + 1, ABL_BG_POINTS_X - 1);
|
||||
}
|
||||
|
||||
if (cached_rel.y != rel.y || cached_g.x != thisg.x) {
|
||||
|
||||
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;
|
||||
|
||||
#if DISABLED(EXTRAPOLATE_BEYOND_GRID)
|
||||
// Beyond the grid maintain height at grid edges
|
||||
NOLESS(ratio.y, 0); // Never < 0.0. (> 1.0 is ok when nextg.y==thisg.y.)
|
||||
#endif
|
||||
|
||||
thisg.y = gy;
|
||||
nextg.y = _MIN(thisg.y + 1, ABL_BG_POINTS_Y - 1);
|
||||
}
|
||||
|
||||
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)
|
||||
z3 = ABL_BG_GRID(nextg.x, thisg.y); // right-front
|
||||
d4 = ABL_BG_GRID(nextg.x, nextg.y) - z3; // right-back (delta)
|
||||
}
|
||||
|
||||
// Bilinear interpolate. Needed since rel.y or thisg.x has changed.
|
||||
L = z1 + d2 * ratio.y; // Linear interp. LF -> LB
|
||||
const float R = z3 + d4 * ratio.y; // Linear interp. RF -> RB
|
||||
|
||||
D = R - L;
|
||||
}
|
||||
|
||||
const float offset = L + ratio.x * D; // the offset almost always changes
|
||||
|
||||
/*
|
||||
static float last_offset = 0;
|
||||
if (ABS(last_offset - offset) > 0.2) {
|
||||
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);
|
||||
}
|
||||
last_offset = offset;
|
||||
//*/
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
#if IS_CARTESIAN && DISABLED(SEGMENT_LEVELED_MOVES)
|
||||
|
||||
#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 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) };
|
||||
LIMIT(c1.x, 0, ABL_BG_POINTS_X - 2);
|
||||
LIMIT(c1.y, 0, ABL_BG_POINTS_Y - 2);
|
||||
LIMIT(c2.x, 0, ABL_BG_POINTS_X - 2);
|
||||
LIMIT(c2.y, 0, ABL_BG_POINTS_Y - 2);
|
||||
|
||||
// Start and end in the same cell? No split needed.
|
||||
if (c1 == c2) {
|
||||
current_position = destination;
|
||||
line_to_current_position(scaled_fr_mm_s);
|
||||
return;
|
||||
}
|
||||
|
||||
#define LINE_SEGMENT_END(A) (current_position.A + (destination.A - current_position.A) * normalized_dist)
|
||||
|
||||
float normalized_dist;
|
||||
xyze_pos_t end;
|
||||
const xy_int8_t gc { _MAX(c1.x, c2.x), _MAX(c1.y, c2.y) };
|
||||
|
||||
// Crosses on the X and not already split on this X?
|
||||
// The x_splits flags are insurance against rounding errors.
|
||||
if (c2.x != c1.x && TEST(x_splits, gc.x)) {
|
||||
// Split on the X grid line
|
||||
CBI(x_splits, gc.x);
|
||||
end = destination;
|
||||
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);
|
||||
}
|
||||
// Crosses on the Y and not already split on this Y?
|
||||
else if (c2.y != c1.y && TEST(y_splits, gc.y)) {
|
||||
// Split on the Y grid line
|
||||
CBI(y_splits, gc.y);
|
||||
end = destination;
|
||||
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);
|
||||
}
|
||||
else {
|
||||
// Must already have been split on these border(s)
|
||||
// This should be a rare case.
|
||||
current_position = destination;
|
||||
line_to_current_position(scaled_fr_mm_s);
|
||||
return;
|
||||
}
|
||||
|
||||
destination.z = LINE_SEGMENT_END(z);
|
||||
destination.e = LINE_SEGMENT_END(e);
|
||||
|
||||
// Do the split and look for more borders
|
||||
line_to_destination(scaled_fr_mm_s, x_splits, y_splits);
|
||||
|
||||
// Restore destination from stack
|
||||
destination = end;
|
||||
line_to_destination(scaled_fr_mm_s, x_splits, y_splits);
|
||||
}
|
||||
|
||||
#endif // IS_CARTESIAN && !SEGMENT_LEVELED_MOVES
|
||||
|
||||
#endif // AUTO_BED_LEVELING_BILINEAR
|
||||
@@ -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 virt_coord(const uint8_t x, const uint8_t y);
|
||||
static float virt_cmr(const float p[4], const uint8_t i, const float t);
|
||||
static float virt_2cmr(const uint8_t x, const uint8_t y, const_float_t tx, const_float_t ty);
|
||||
static void subdivide_mesh();
|
||||
#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=nullptr);
|
||||
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;
|
||||
@@ -0,0 +1,196 @@
|
||||
/**
|
||||
* 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 ENABLED(BABYSTEPPING)
|
||||
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.apply_directions();
|
||||
}
|
||||
#endif
|
||||
old_cur_z = cur_z;
|
||||
old_buf_z = current_position.z;
|
||||
endstops.bdp_state_update(z_sensor <= 0.01f);
|
||||
//endstops.update();
|
||||
}
|
||||
else
|
||||
stepper.apply_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);
|
||||
TERN_(HAS_DISABLE_IDLE_AXES, 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
|
||||
@@ -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;
|
||||
@@ -0,0 +1,218 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if HAS_LEVELING
|
||||
|
||||
#include "bedlevel.h"
|
||||
#include "../../module/planner.h"
|
||||
|
||||
#if ANY(MESH_BED_LEVELING, PROBE_MANUALLY)
|
||||
#include "../../module/motion.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(PROBE_MANUALLY)
|
||||
bool g29_in_progress = false;
|
||||
#endif
|
||||
|
||||
#if ENABLED(LCD_BED_LEVELING)
|
||||
#include "../../lcd/marlinui.h"
|
||||
#endif
|
||||
|
||||
#define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
#include "../../core/debug_out.h"
|
||||
|
||||
#if ENABLED(EXTENSIBLE_UI)
|
||||
#include "../../lcd/extui/ui_api.h"
|
||||
#endif
|
||||
|
||||
bool leveling_is_valid() {
|
||||
return TERN1(HAS_MESH, bedlevel.mesh_is_valid());
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn bed leveling on or off, correcting the current position.
|
||||
*
|
||||
* Disable: Current position = physical position
|
||||
* Enable: Current position = "unleveled" physical position
|
||||
*/
|
||||
void set_bed_leveling_enabled(const bool enable/*=true*/) {
|
||||
DEBUG_SECTION(log_sble, "set_bed_leveling_enabled", DEBUGGING(LEVELING));
|
||||
|
||||
const bool can_change = TERN1(AUTO_BED_LEVELING_BILINEAR, !enable || leveling_is_valid());
|
||||
|
||||
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();
|
||||
|
||||
// Get the corrected leveled / unleveled position
|
||||
planner.apply_modifiers(current_position, true); // Physical position with all modifiers
|
||||
planner.leveling_active ^= true; // Toggle leveling between apply and unapply
|
||||
planner.unapply_modifiers(current_position, true); // Logical position with modifiers removed
|
||||
|
||||
sync_plan_position();
|
||||
_report_leveling();
|
||||
}
|
||||
}
|
||||
|
||||
TemporaryBedLevelingState::TemporaryBedLevelingState(const bool enable) : saved(planner.leveling_active) {
|
||||
set_bed_leveling_enabled(enable);
|
||||
}
|
||||
|
||||
#if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
|
||||
|
||||
void set_z_fade_height(const_float_t zfh, const bool do_report/*=true*/) {
|
||||
|
||||
if (planner.z_fade_height == zfh) return;
|
||||
|
||||
const bool leveling_was_active = planner.leveling_active;
|
||||
set_bed_leveling_enabled(false);
|
||||
|
||||
planner.set_z_fade_height(zfh);
|
||||
|
||||
if (leveling_was_active) {
|
||||
const xyz_pos_t oldpos = current_position;
|
||||
set_bed_leveling_enabled(true);
|
||||
if (do_report && oldpos != current_position)
|
||||
report_current_position();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ENABLE_LEVELING_FADE_HEIGHT
|
||||
|
||||
/**
|
||||
* Reset calibration results to zero.
|
||||
*/
|
||||
void reset_bed_level() {
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("reset_bed_level");
|
||||
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 ANY(AUTO_BED_LEVELING_BILINEAR, MESH_BED_LEVELING)
|
||||
|
||||
/**
|
||||
* Enable to produce output in JSON format suitable
|
||||
* for SCAD or JavaScript mesh visualizers.
|
||||
*
|
||||
* Visualize meshes in OpenSCAD using the included script.
|
||||
*
|
||||
* buildroot/shared/scripts/MarlinMesh.scad
|
||||
*/
|
||||
//#define SCAD_MESH_OUTPUT
|
||||
|
||||
/**
|
||||
* 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, const float *values) {
|
||||
#ifndef SCAD_MESH_OUTPUT
|
||||
for (uint8_t x = 0; x < sx; ++x) {
|
||||
serial_spaces(precision + (x < 10 ? 3 : 2));
|
||||
SERIAL_ECHO(x);
|
||||
}
|
||||
SERIAL_EOL();
|
||||
#endif
|
||||
#ifdef SCAD_MESH_OUTPUT
|
||||
SERIAL_ECHOLNPGM("measured_z = ["); // open 2D array
|
||||
#endif
|
||||
for (uint8_t y = 0; y < sy; ++y) {
|
||||
#ifdef SCAD_MESH_OUTPUT
|
||||
SERIAL_ECHOPGM(" ["); // open sub-array
|
||||
#else
|
||||
if (y < 10) SERIAL_CHAR(' ');
|
||||
SERIAL_ECHO(y);
|
||||
#endif
|
||||
for (uint8_t x = 0; x < sx; ++x) {
|
||||
SERIAL_CHAR(' ');
|
||||
const float offset = values[x * sy + y];
|
||||
if (!isnan(offset)) {
|
||||
if (offset >= 0) SERIAL_CHAR('+');
|
||||
SERIAL_ECHO_F(offset, int(precision));
|
||||
}
|
||||
else {
|
||||
#ifdef SCAD_MESH_OUTPUT
|
||||
for (uint8_t i = 3; i < precision + 3; i++)
|
||||
SERIAL_CHAR(' ');
|
||||
SERIAL_ECHOPGM("NAN");
|
||||
#else
|
||||
for (uint8_t i = 0; i < precision + 3; ++i)
|
||||
SERIAL_CHAR(i ? '=' : ' ');
|
||||
#endif
|
||||
}
|
||||
#ifdef SCAD_MESH_OUTPUT
|
||||
if (x < sx - 1) SERIAL_CHAR(',');
|
||||
#endif
|
||||
}
|
||||
#ifdef SCAD_MESH_OUTPUT
|
||||
SERIAL_ECHOPGM(" ]"); // close sub-array
|
||||
if (y < sy - 1) SERIAL_CHAR(',');
|
||||
#endif
|
||||
SERIAL_EOL();
|
||||
}
|
||||
#ifdef SCAD_MESH_OUTPUT
|
||||
SERIAL_ECHOPGM("];"); // close 2D array
|
||||
#endif
|
||||
SERIAL_EOL();
|
||||
}
|
||||
|
||||
#endif // AUTO_BED_LEVELING_BILINEAR || MESH_BED_LEVELING
|
||||
|
||||
#if ANY(MESH_BED_LEVELING, PROBE_MANUALLY)
|
||||
|
||||
void _manual_goto_xy(const xy_pos_t &pos) {
|
||||
|
||||
// Get the resting Z position for after the XY move
|
||||
#ifdef MANUAL_PROBE_START_Z
|
||||
constexpr float finalz = _MAX(0, MANUAL_PROBE_START_Z); // If a MANUAL_PROBE_START_Z value is set, always respect it
|
||||
#else
|
||||
#warning "It's recommended to set some MANUAL_PROBE_START_Z value for manual leveling."
|
||||
#endif
|
||||
#if Z_CLEARANCE_BETWEEN_MANUAL_PROBES > 0 // A probe/obstacle clearance exists so there is a raise:
|
||||
#ifndef MANUAL_PROBE_START_Z
|
||||
const float finalz = current_position.z; // - Use the current Z for starting-Z if no MANUAL_PROBE_START_Z was provided
|
||||
#endif
|
||||
do_blocking_move_to_xy_z(pos, Z_CLEARANCE_BETWEEN_MANUAL_PROBES); // - Raise Z, then move to the new XY
|
||||
do_blocking_move_to_z(finalz); // - Lower down to the starting Z height, ready for adjustment!
|
||||
#elif defined(MANUAL_PROBE_START_Z) // A starting-Z was provided, but there's no raise:
|
||||
do_blocking_move_to_xy_z(pos, finalz); // - Move in XY then down to the starting Z height, ready for adjustment!
|
||||
#else // Zero raise and no starting Z height either:
|
||||
do_blocking_move_to_xy(pos); // - Move over with no raise, ready for adjustment!
|
||||
#endif
|
||||
|
||||
TERN_(LCD_BED_LEVELING, ui.wait_for_move = false);
|
||||
}
|
||||
|
||||
#endif // MESH_BED_LEVELING || PROBE_MANUALLY
|
||||
|
||||
#endif // HAS_LEVELING
|
||||
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* 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"
|
||||
|
||||
#if ANY(RESTORE_LEVELING_AFTER_G28, ENABLE_LEVELING_AFTER_G28)
|
||||
#define CAN_SET_LEVELING_AFTER_G28 1
|
||||
#endif
|
||||
|
||||
#if ENABLED(PROBE_MANUALLY)
|
||||
extern bool g29_in_progress;
|
||||
#else
|
||||
constexpr bool g29_in_progress = false;
|
||||
#endif
|
||||
|
||||
bool leveling_is_valid();
|
||||
void set_bed_leveling_enabled(const bool enable=true);
|
||||
void reset_bed_level();
|
||||
|
||||
#if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
|
||||
void set_z_fade_height(const_float_t zfh, const bool do_report=true);
|
||||
#endif
|
||||
|
||||
#if ANY(MESH_BED_LEVELING, PROBE_MANUALLY)
|
||||
void _manual_goto_xy(const xy_pos_t &pos);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A class to save and change the bed leveling state,
|
||||
* then restore it when it goes out of scope.
|
||||
*/
|
||||
class TemporaryBedLevelingState {
|
||||
bool saved;
|
||||
public:
|
||||
TemporaryBedLevelingState(const bool enable);
|
||||
~TemporaryBedLevelingState() { set_bed_leveling_enabled(saved); }
|
||||
};
|
||||
#define TEMPORARY_BED_LEVELING_STATE(enable) const TemporaryBedLevelingState tbls(enable)
|
||||
|
||||
#if HAS_MESH
|
||||
|
||||
typedef float bed_mesh_t[GRID_MAX_POINTS_X][GRID_MAX_POINTS_Y];
|
||||
|
||||
#if ENABLED(AUTO_BED_LEVELING_BILINEAR)
|
||||
#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
|
||||
|
||||
#if ANY(AUTO_BED_LEVELING_BILINEAR, MESH_BED_LEVELING)
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef float (*element_2d_fn)(const uint8_t, const uint8_t);
|
||||
|
||||
/**
|
||||
* 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, const float *values);
|
||||
|
||||
#endif
|
||||
|
||||
struct mesh_index_pair {
|
||||
xy_int8_t pos;
|
||||
float distance; // When populated, the distance from the search location
|
||||
void invalidate() { pos = -1; }
|
||||
bool valid() const { return pos.x >= 0 && pos.y >= 0; }
|
||||
#if ENABLED(AUTO_BED_LEVELING_UBL)
|
||||
xy_pos_t meshpos() {
|
||||
return { bedlevel.get_mesh_x(pos.x), bedlevel.get_mesh_y(pos.y) };
|
||||
}
|
||||
#endif
|
||||
operator xy_int8_t&() { return pos; }
|
||||
operator const xy_int8_t&() const { return pos; }
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,110 @@
|
||||
/*********************
|
||||
* hilbert_curve.cpp *
|
||||
*********************/
|
||||
|
||||
/****************************************************************************
|
||||
* Written By Marcio Teixeira 2021 - SynDaver Labs, Inc. *
|
||||
* *
|
||||
* 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. *
|
||||
* *
|
||||
* To view a copy of the GNU General Public License, go to the following *
|
||||
* location: <https://www.gnu.org/licenses/>. *
|
||||
****************************************************************************/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(UBL_HILBERT_CURVE)
|
||||
|
||||
#include "bedlevel.h"
|
||||
#include "hilbert_curve.h"
|
||||
|
||||
constexpr int8_t to_fix(int8_t v) { return v * 2; }
|
||||
constexpr int8_t to_int(int8_t v) { return v / 2; }
|
||||
constexpr uint8_t log2(uint8_t n) { return (n > 1) ? 1 + log2(uint8_t(n >> 1)) : 0; }
|
||||
constexpr uint8_t order(uint8_t n) { return uint8_t(log2(uint8_t(n - 1))) + 1; }
|
||||
constexpr uint8_t ord = order(_MAX(GRID_MAX_POINTS_X, GRID_MAX_POINTS_Y));
|
||||
constexpr uint8_t dim = _BV(ord);
|
||||
|
||||
static inline bool eval_candidate(int8_t x, int8_t y, hilbert_curve::callback_ptr func, void *data) {
|
||||
// The print bed likely has fewer points than the full Hilbert
|
||||
// curve, so cull unnecessary points
|
||||
return x < (GRID_MAX_POINTS_X) && y < (GRID_MAX_POINTS_Y) ? func(x, y, data) : false;
|
||||
}
|
||||
|
||||
bool hilbert_curve::hilbert(int8_t x, int8_t y, int8_t xi, int8_t xj, int8_t yi, int8_t yj, uint8_t n, hilbert_curve::callback_ptr func, void *data) {
|
||||
/**
|
||||
* Hilbert space-filling curve implementation
|
||||
*
|
||||
* x and y : coordinates of the bottom left corner
|
||||
* xi and xj : i and j components of the unit x vector of the frame
|
||||
* yi and yj : i and j components of the unit y vector of the frame
|
||||
*
|
||||
* From: http://www.fundza.com/algorithmic/space_filling/hilbert/basics/index.html
|
||||
*/
|
||||
if (n)
|
||||
return hilbert(x, y, yi/2, yj/2, xi/2, xj/2, n-1, func, data) ||
|
||||
hilbert(x+xi/2, y+xj/2, xi/2, xj/2, yi/2, yj/2, n-1, func, data) ||
|
||||
hilbert(x+xi/2+yi/2, y+xj/2+yj/2, xi/2, xj/2, yi/2, yj/2, n-1, func, data) ||
|
||||
hilbert(x+xi/2+yi, y+xj/2+yj, -yi/2, -yj/2, -xi/2, -xj/2, n-1, func, data);
|
||||
else
|
||||
return eval_candidate(to_int(x+(xi+yi)/2), to_int(y+(xj+yj)/2), func, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls func(x, y, data) for all points in the Hilbert curve.
|
||||
* If that function returns true, the search is terminated.
|
||||
*/
|
||||
bool hilbert_curve::search(hilbert_curve::callback_ptr func, void *data) {
|
||||
return hilbert(to_fix(0), to_fix(0),to_fix(dim), to_fix(0), to_fix(0), to_fix(dim), ord, func, data);
|
||||
}
|
||||
|
||||
/* Helper function for starting the search at a particular point */
|
||||
|
||||
typedef struct {
|
||||
uint8_t x, y;
|
||||
bool found_1st;
|
||||
hilbert_curve::callback_ptr func;
|
||||
void *data;
|
||||
} search_from_t;
|
||||
|
||||
static bool search_from_helper(uint8_t x, uint8_t y, void *data) {
|
||||
search_from_t *d = (search_from_t *) data;
|
||||
if (d->x == x && d->y == y)
|
||||
d->found_1st = true;
|
||||
return d->found_1st ? d->func(x, y, d->data) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as search, except start at a specific grid intersection point.
|
||||
*/
|
||||
bool hilbert_curve::search_from(uint8_t x, uint8_t y, hilbert_curve::callback_ptr func, void *data) {
|
||||
search_from_t d;
|
||||
d.x = x;
|
||||
d.y = y;
|
||||
d.found_1st = false;
|
||||
d.func = func;
|
||||
d.data = data;
|
||||
// Call twice to allow search to wrap back to the beginning and picked up points prior to the start.
|
||||
return search(search_from_helper, &d) || search(search_from_helper, &d);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like search_from, but takes a bed position and starts from the nearest
|
||||
* point on the Hilbert curve.
|
||||
*/
|
||||
bool hilbert_curve::search_from_closest(const xy_pos_t &pos, hilbert_curve::callback_ptr func, void *data) {
|
||||
// Find closest grid intersection
|
||||
const uint8_t grid_x = LROUND(constrain(float(pos.x - (MESH_MIN_X)) / (MESH_X_DIST), 0, (GRID_MAX_POINTS_X) - 1));
|
||||
const uint8_t grid_y = LROUND(constrain(float(pos.y - (MESH_MIN_Y)) / (MESH_Y_DIST), 0, (GRID_MAX_POINTS_Y) - 1));
|
||||
return search_from(grid_x, grid_y, func, data);
|
||||
}
|
||||
|
||||
#endif // UBL_HILBERT_CURVE
|
||||
@@ -0,0 +1,32 @@
|
||||
/*******************
|
||||
* hilbert_curve.h *
|
||||
*******************/
|
||||
|
||||
/****************************************************************************
|
||||
* Written By Marcio Teixeira 2021 - SynDaver Labs, Inc. *
|
||||
* *
|
||||
* 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. *
|
||||
* *
|
||||
* To view a copy of the GNU General Public License, go to the following *
|
||||
* location: <https://www.gnu.org/licenses/>. *
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
class hilbert_curve {
|
||||
public:
|
||||
typedef bool (*callback_ptr)(uint8_t x, uint8_t y, void *data);
|
||||
static bool search(callback_ptr func, void *data);
|
||||
static bool search_from(uint8_t x, uint8_t y, callback_ptr func, void *data);
|
||||
static bool search_from_closest(const xy_pos_t &pos, callback_ptr func, void *data);
|
||||
private:
|
||||
static bool hilbert(int8_t x, int8_t y, int8_t xi, int8_t xj, int8_t yi, int8_t yj, uint8_t n, callback_ptr func, void *data);
|
||||
};
|
||||
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(MESH_BED_LEVELING)
|
||||
|
||||
#include "../bedlevel.h"
|
||||
|
||||
#include "../../../module/motion.h"
|
||||
|
||||
#if ENABLED(EXTENSIBLE_UI)
|
||||
#include "../../../lcd/extui/ui_api.h"
|
||||
#endif
|
||||
|
||||
mesh_bed_leveling bedlevel;
|
||||
|
||||
float mesh_bed_leveling::z_offset,
|
||||
mesh_bed_leveling::z_values[GRID_MAX_POINTS_X][GRID_MAX_POINTS_Y],
|
||||
mesh_bed_leveling::index_to_xpos[GRID_MAX_POINTS_X],
|
||||
mesh_bed_leveling::index_to_ypos[GRID_MAX_POINTS_Y];
|
||||
|
||||
mesh_bed_leveling::mesh_bed_leveling() {
|
||||
for (uint8_t i = 0; i < GRID_MAX_POINTS_X; ++i)
|
||||
index_to_xpos[i] = MESH_MIN_X + i * (MESH_X_DIST);
|
||||
for (uint8_t i = 0; i < GRID_MAX_POINTS_Y; ++i)
|
||||
index_to_ypos[i] = MESH_MIN_Y + i * (MESH_Y_DIST);
|
||||
reset();
|
||||
}
|
||||
|
||||
void mesh_bed_leveling::reset() {
|
||||
z_offset = 0;
|
||||
ZERO(z_values);
|
||||
#if ENABLED(EXTENSIBLE_UI)
|
||||
GRID_LOOP(x, y) ExtUI::onMeshUpdate(x, y, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if IS_CARTESIAN && DISABLED(SEGMENT_LEVELED_MOVES)
|
||||
|
||||
/**
|
||||
* Prepare a mesh-leveled linear move in a Cartesian setup,
|
||||
* splitting the move where it crosses mesh borders.
|
||||
*/
|
||||
void mesh_bed_leveling::line_to_destination(const_feedRate_t scaled_fr_mm_s, uint8_t x_splits, uint8_t y_splits) {
|
||||
// Get current and destination cells for this line
|
||||
xy_int8_t scel = cell_indexes(current_position), ecel = cell_indexes(destination);
|
||||
NOMORE(scel.x, GRID_MAX_CELLS_X - 1);
|
||||
NOMORE(scel.y, GRID_MAX_CELLS_Y - 1);
|
||||
NOMORE(ecel.x, GRID_MAX_CELLS_X - 1);
|
||||
NOMORE(ecel.y, GRID_MAX_CELLS_Y - 1);
|
||||
|
||||
// Start and end in the same cell? No split needed.
|
||||
if (scel == ecel) {
|
||||
current_position = destination;
|
||||
line_to_current_position(scaled_fr_mm_s);
|
||||
return;
|
||||
}
|
||||
|
||||
#define MBL_SEGMENT_END(A) (current_position.A + (destination.A - current_position.A) * normalized_dist)
|
||||
|
||||
float normalized_dist;
|
||||
xyze_pos_t dest;
|
||||
const int8_t gcx = _MAX(scel.x, ecel.x), gcy = _MAX(scel.y, ecel.y);
|
||||
|
||||
// Crosses on the X and not already split on this X?
|
||||
// The x_splits flags are insurance against rounding errors.
|
||||
if (ecel.x != scel.x && TEST(x_splits, gcx)) {
|
||||
// Split on the X grid line
|
||||
CBI(x_splits, gcx);
|
||||
dest = destination;
|
||||
destination.x = index_to_xpos[gcx];
|
||||
normalized_dist = (destination.x - current_position.x) / (dest.x - current_position.x);
|
||||
destination.y = MBL_SEGMENT_END(y);
|
||||
}
|
||||
// Crosses on the Y and not already split on this Y?
|
||||
else if (ecel.y != scel.y && TEST(y_splits, gcy)) {
|
||||
// Split on the Y grid line
|
||||
CBI(y_splits, gcy);
|
||||
dest = destination;
|
||||
destination.y = index_to_ypos[gcy];
|
||||
normalized_dist = (destination.y - current_position.y) / (dest.y - current_position.y);
|
||||
destination.x = MBL_SEGMENT_END(x);
|
||||
}
|
||||
else {
|
||||
// Must already have been split on these border(s)
|
||||
// This should be a rare case.
|
||||
current_position = destination;
|
||||
line_to_current_position(scaled_fr_mm_s);
|
||||
return;
|
||||
}
|
||||
|
||||
destination.z = MBL_SEGMENT_END(z);
|
||||
destination.e = MBL_SEGMENT_END(e);
|
||||
|
||||
// Do the split and look for more borders
|
||||
line_to_destination(scaled_fr_mm_s, x_splits, y_splits);
|
||||
|
||||
// Restore destination from stack
|
||||
destination = dest;
|
||||
line_to_destination(scaled_fr_mm_s, x_splits, y_splits);
|
||||
}
|
||||
|
||||
#endif // IS_CARTESIAN && !SEGMENT_LEVELED_MOVES
|
||||
|
||||
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, z_values[0]);
|
||||
}
|
||||
|
||||
#endif // MESH_BED_LEVELING
|
||||
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../../../inc/MarlinConfig.h"
|
||||
|
||||
enum MeshLevelingState : char {
|
||||
MeshReport, // G29 S0
|
||||
MeshStart, // G29 S1
|
||||
MeshNext, // G29 S2
|
||||
MeshSet, // G29 S3
|
||||
MeshSetZOffset, // G29 S4
|
||||
MeshReset // G29 S5
|
||||
};
|
||||
|
||||
#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))
|
||||
|
||||
class mesh_bed_leveling {
|
||||
public:
|
||||
static float z_offset,
|
||||
z_values[GRID_MAX_POINTS_X][GRID_MAX_POINTS_Y],
|
||||
index_to_xpos[GRID_MAX_POINTS_X],
|
||||
index_to_ypos[GRID_MAX_POINTS_Y];
|
||||
|
||||
mesh_bed_leveling();
|
||||
|
||||
static void report_mesh();
|
||||
|
||||
static void reset();
|
||||
|
||||
FORCE_INLINE static bool has_mesh() {
|
||||
GRID_LOOP(x, y) if (z_values[x][y]) return true;
|
||||
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 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
|
||||
}
|
||||
|
||||
static void set_zigzag_z(const int8_t index, const_float_t z) {
|
||||
int8_t px, py;
|
||||
zigzag(index, px, py);
|
||||
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 uint8_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);
|
||||
}
|
||||
static uint8_t cell_index_y(const_float_t y) {
|
||||
int8_t cy = (y - (MESH_MIN_Y)) * RECIPROCAL(MESH_Y_DIST);
|
||||
return constrain(cy, 0, GRID_MAX_CELLS_Y - 1);
|
||||
}
|
||||
static xy_uint8_t cell_indexes(const_float_t x, const_float_t y) {
|
||||
return { cell_index_x(x), cell_index_y(y) };
|
||||
}
|
||||
static xy_uint8_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);
|
||||
return WITHIN(px, 0, (GRID_MAX_POINTS_X) - 1) ? px : -1;
|
||||
}
|
||||
static int8_t probe_index_y(const_float_t y) {
|
||||
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 xy_int8_t probe_indexes(const_float_t x, const_float_t y) {
|
||||
return { probe_index_x(x), probe_index_y(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),
|
||||
delta_a = a0 - a1;
|
||||
return z1 + delta_a * delta_z;
|
||||
}
|
||||
|
||||
static float get_z_offset() { return z_offset; }
|
||||
|
||||
static float get_z_correction(const xy_pos_t &pos) {
|
||||
const xy_uint8_t ind = cell_indexes(pos);
|
||||
const float x1 = index_to_xpos[ind.x], x2 = index_to_xpos[ind.x+1],
|
||||
y1 = index_to_ypos[ind.y], y2 = index_to_ypos[ind.y+1],
|
||||
z1 = calc_z0(pos.x, x1, z_values[ind.x][ind.y ], x2, z_values[ind.x+1][ind.y ]),
|
||||
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 zf;
|
||||
}
|
||||
|
||||
#if IS_CARTESIAN && DISABLED(SEGMENT_LEVELED_MOVES)
|
||||
static void line_to_destination(const_feedRate_t scaled_fr_mm_s, uint8_t x_splits=0xFF, uint8_t y_splits=0xFF);
|
||||
#endif
|
||||
};
|
||||
|
||||
extern mesh_bed_leveling bedlevel;
|
||||
@@ -0,0 +1,300 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(AUTO_BED_LEVELING_UBL)
|
||||
|
||||
#include "../bedlevel.h"
|
||||
|
||||
unified_bed_leveling bedlevel;
|
||||
|
||||
#include "../../../MarlinCore.h"
|
||||
#include "../../../gcode/gcode.h"
|
||||
|
||||
#include "../../../module/settings.h"
|
||||
#include "../../../module/planner.h"
|
||||
#include "../../../module/motion.h"
|
||||
#include "../../../module/probe.h"
|
||||
#include "../../../module/temperature.h"
|
||||
|
||||
#if ENABLED(EXTENSIBLE_UI)
|
||||
#include "../../../lcd/extui/ui_api.h"
|
||||
#endif
|
||||
|
||||
#include "math.h"
|
||||
|
||||
void unified_bed_leveling::echo_name() { SERIAL_ECHOPGM("Unified Bed Leveling"); }
|
||||
|
||||
void unified_bed_leveling::report_current_mesh() {
|
||||
if (!leveling_is_valid()) return;
|
||||
SERIAL_ECHO_MSG(" G29 I999");
|
||||
GRID_LOOP(x, y)
|
||||
if (!isnan(z_values[x][y])) {
|
||||
SERIAL_ECHO_START();
|
||||
SERIAL_ECHOPGM(" M421 I", x, " J", y);
|
||||
SERIAL_ECHOLNPAIR_F_P(SP_Z_STR, z_values[x][y], 4);
|
||||
serial_delay(75); // Prevent Printrun from exploding
|
||||
}
|
||||
}
|
||||
|
||||
void unified_bed_leveling::report_state() {
|
||||
echo_name();
|
||||
serial_ternary(planner.leveling_active, F(" System v" UBL_VERSION " "), nullptr, F("in"), F("active\n"));
|
||||
serial_delay(50);
|
||||
}
|
||||
|
||||
int8_t unified_bed_leveling::storage_slot;
|
||||
|
||||
float unified_bed_leveling::z_values[GRID_MAX_POINTS_X][GRID_MAX_POINTS_Y];
|
||||
|
||||
#define _GRIDPOS(A,N) (MESH_MIN_##A + N * (MESH_##A##_DIST))
|
||||
|
||||
const float
|
||||
unified_bed_leveling::_mesh_index_to_xpos[GRID_MAX_POINTS_X] PROGMEM = ARRAY_N(GRID_MAX_POINTS_X,
|
||||
_GRIDPOS(X, 0), _GRIDPOS(X, 1), _GRIDPOS(X, 2), _GRIDPOS(X, 3),
|
||||
_GRIDPOS(X, 4), _GRIDPOS(X, 5), _GRIDPOS(X, 6), _GRIDPOS(X, 7),
|
||||
_GRIDPOS(X, 8), _GRIDPOS(X, 9), _GRIDPOS(X, 10), _GRIDPOS(X, 11),
|
||||
_GRIDPOS(X, 12), _GRIDPOS(X, 13), _GRIDPOS(X, 14), _GRIDPOS(X, 15)
|
||||
),
|
||||
unified_bed_leveling::_mesh_index_to_ypos[GRID_MAX_POINTS_Y] PROGMEM = ARRAY_N(GRID_MAX_POINTS_Y,
|
||||
_GRIDPOS(Y, 0), _GRIDPOS(Y, 1), _GRIDPOS(Y, 2), _GRIDPOS(Y, 3),
|
||||
_GRIDPOS(Y, 4), _GRIDPOS(Y, 5), _GRIDPOS(Y, 6), _GRIDPOS(Y, 7),
|
||||
_GRIDPOS(Y, 8), _GRIDPOS(Y, 9), _GRIDPOS(Y, 10), _GRIDPOS(Y, 11),
|
||||
_GRIDPOS(Y, 12), _GRIDPOS(Y, 13), _GRIDPOS(Y, 14), _GRIDPOS(Y, 15)
|
||||
);
|
||||
|
||||
volatile int16_t unified_bed_leveling::encoder_diff;
|
||||
|
||||
unified_bed_leveling::unified_bed_leveling() { reset(); }
|
||||
|
||||
void unified_bed_leveling::reset() {
|
||||
const bool was_enabled = planner.leveling_active;
|
||||
set_bed_leveling_enabled(false);
|
||||
storage_slot = -1;
|
||||
ZERO(z_values);
|
||||
#if ENABLED(EXTENSIBLE_UI)
|
||||
GRID_LOOP(x, y) ExtUI::onMeshUpdate(x, y, 0);
|
||||
#endif
|
||||
if (was_enabled) report_current_position();
|
||||
}
|
||||
|
||||
void unified_bed_leveling::invalidate() {
|
||||
set_bed_leveling_enabled(false);
|
||||
set_all_mesh_points_to_value(NAN);
|
||||
}
|
||||
|
||||
void unified_bed_leveling::set_all_mesh_points_to_value(const_float_t value) {
|
||||
GRID_LOOP(x, y) {
|
||||
z_values[x][y] = value;
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(x, y, value));
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLED(OPTIMIZED_MESH_STORAGE)
|
||||
|
||||
constexpr float mesh_store_scaling = 1000;
|
||||
constexpr int16_t Z_STEPS_NAN = INT16_MAX;
|
||||
|
||||
void unified_bed_leveling::set_store_from_mesh(const bed_mesh_t &in_values, mesh_store_t &stored_values) {
|
||||
auto z_to_store = [](const_float_t z) {
|
||||
if (isnan(z)) return Z_STEPS_NAN;
|
||||
const int32_t z_scaled = TRUNC(z * mesh_store_scaling);
|
||||
if (z_scaled == Z_STEPS_NAN || !WITHIN(z_scaled, INT16_MIN, INT16_MAX))
|
||||
return Z_STEPS_NAN; // If Z is out of range, return our custom 'NaN'
|
||||
return int16_t(z_scaled);
|
||||
};
|
||||
GRID_LOOP(x, y) stored_values[x][y] = z_to_store(in_values[x][y]);
|
||||
}
|
||||
|
||||
void unified_bed_leveling::set_mesh_from_store(const mesh_store_t &stored_values, bed_mesh_t &out_values) {
|
||||
auto store_to_z = [](const int16_t z_scaled) {
|
||||
return z_scaled == Z_STEPS_NAN ? NAN : z_scaled / mesh_store_scaling;
|
||||
};
|
||||
GRID_LOOP(x, y) out_values[x][y] = store_to_z(stored_values[x][y]);
|
||||
}
|
||||
|
||||
#endif // OPTIMIZED_MESH_STORAGE
|
||||
|
||||
static void serial_echo_xy(const uint8_t sp, const int16_t x, const int16_t y) {
|
||||
SERIAL_ECHO_SP(sp);
|
||||
SERIAL_CHAR('(');
|
||||
if (x < 100) { SERIAL_CHAR(' '); if (x < 10) SERIAL_CHAR(' '); }
|
||||
SERIAL_ECHO(x);
|
||||
SERIAL_CHAR(',');
|
||||
if (y < 100) { SERIAL_CHAR(' '); if (y < 10) SERIAL_CHAR(' '); }
|
||||
SERIAL_ECHO(y);
|
||||
SERIAL_CHAR(')');
|
||||
serial_delay(5);
|
||||
}
|
||||
|
||||
static void serial_echo_column_labels(const uint8_t sp) {
|
||||
SERIAL_ECHO_SP(7);
|
||||
for (uint8_t i = 0; i < GRID_MAX_POINTS_X; ++i) {
|
||||
if (i < 10) SERIAL_CHAR(' ');
|
||||
SERIAL_ECHO(i);
|
||||
SERIAL_ECHO_SP(sp);
|
||||
}
|
||||
serial_delay(10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce one of these mesh maps:
|
||||
* 0: Human-readable
|
||||
* 1: CSV format for spreadsheet import
|
||||
* 2: TODO: Display on Graphical LCD
|
||||
* 4: Compact Human-Readable
|
||||
*/
|
||||
void unified_bed_leveling::display_map(const uint8_t map_type) {
|
||||
const bool was = gcode.set_autoreport_paused(true);
|
||||
|
||||
constexpr uint8_t eachsp = 1 + 6 + 1, // [-3.567]
|
||||
twixt = eachsp * (GRID_MAX_POINTS_X) - 9 * 2; // Leading 4sp, Coordinates 9sp each
|
||||
|
||||
const bool human = !(map_type & 0x3), csv = map_type == 1, lcd = map_type == 2, comp = map_type & 0x4;
|
||||
|
||||
SERIAL_ECHOPGM("\nBed Topography Report");
|
||||
if (human) {
|
||||
SERIAL_ECHOLNPGM(":\n");
|
||||
serial_echo_xy(4, MESH_MIN_X, MESH_MAX_Y);
|
||||
serial_echo_xy(twixt, MESH_MAX_X, MESH_MAX_Y);
|
||||
SERIAL_EOL();
|
||||
serial_echo_column_labels(eachsp - 2);
|
||||
}
|
||||
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
|
||||
// the current Z position after G28 and the mesh values.
|
||||
const xy_int8_t curr = closest_indexes(xy_pos_t(current_position) + probe.offset_xy);
|
||||
|
||||
if (!lcd) SERIAL_EOL();
|
||||
for (int8_t j = (GRID_MAX_POINTS_Y) - 1; j >= 0; j--) {
|
||||
|
||||
// Row Label (J index)
|
||||
if (human) {
|
||||
if (j < 10) SERIAL_CHAR(' ');
|
||||
SERIAL_ECHO(j);
|
||||
SERIAL_ECHOPGM(" |");
|
||||
}
|
||||
|
||||
// Row Values (I indexes)
|
||||
for (uint8_t i = 0; i < GRID_MAX_POINTS_X; ++i) {
|
||||
|
||||
// Opening Brace or Space
|
||||
const bool is_current = i == curr.x && j == curr.y;
|
||||
if (human) SERIAL_CHAR(is_current ? '[' : ' ');
|
||||
|
||||
// Z Value at current I, J
|
||||
const float f = z_values[i][j];
|
||||
if (lcd) {
|
||||
// TODO: Display on Graphical LCD
|
||||
}
|
||||
else if (isnan(f))
|
||||
SERIAL_ECHOF(human ? F(" . ") : F("NAN"));
|
||||
else if (human || csv) {
|
||||
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');
|
||||
|
||||
// Closing Brace or Space
|
||||
if (human) SERIAL_CHAR(is_current ? ']' : ' ');
|
||||
|
||||
SERIAL_FLUSHTX();
|
||||
idle_no_sleep();
|
||||
}
|
||||
if (!lcd) SERIAL_EOL();
|
||||
|
||||
// A blank line between rows (unless compact)
|
||||
if (j && human && !comp) SERIAL_ECHOLNPGM(" |");
|
||||
}
|
||||
|
||||
if (human) {
|
||||
serial_echo_column_labels(eachsp - 2);
|
||||
SERIAL_EOL();
|
||||
serial_echo_xy(4, MESH_MIN_X, MESH_MIN_Y);
|
||||
serial_echo_xy(twixt, MESH_MAX_X, MESH_MIN_Y);
|
||||
SERIAL_EOL();
|
||||
SERIAL_EOL();
|
||||
}
|
||||
|
||||
gcode.set_autoreport_paused(was);
|
||||
}
|
||||
|
||||
bool unified_bed_leveling::sanity_check() {
|
||||
uint8_t error_flag = 0;
|
||||
|
||||
if (settings.calc_num_meshes() < 1) {
|
||||
SERIAL_ECHOLNPGM("?Mesh too big for EEPROM.");
|
||||
error_flag++;
|
||||
}
|
||||
|
||||
return !!error_flag;
|
||||
}
|
||||
|
||||
#if ENABLED(UBL_MESH_WIZARD)
|
||||
|
||||
/**
|
||||
* M1004: UBL Mesh Wizard - One-click mesh creation with or without a probe
|
||||
*/
|
||||
void GcodeSuite::M1004() {
|
||||
|
||||
#define ALIGN_GCODE TERN(Z_STEPPER_AUTO_ALIGN, "G34\n", "")
|
||||
#define PROBE_GCODE TERN(HAS_BED_PROBE, "G29P1\nG29P3", "G29P4R")
|
||||
|
||||
#if HAS_HOTEND
|
||||
if (parser.seenval('H')) { // Handle H# parameter to set Hotend temp
|
||||
const celsius_t hotend_temp = parser.value_int(); // Marlin never sends itself F or K, always C
|
||||
thermalManager.setTargetHotend(hotend_temp, 0);
|
||||
thermalManager.wait_for_hotend(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAS_HEATED_BED
|
||||
if (parser.seenval('B')) { // Handle B# parameter to set Bed temp
|
||||
const celsius_t bed_temp = parser.value_int(); // Marlin never sends itself F or K, always C
|
||||
thermalManager.setTargetBed(bed_temp);
|
||||
thermalManager.wait_for_bed(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
process_subcommands_now(FPSTR(G28_STR)); // Home
|
||||
process_subcommands_now(F(ALIGN_GCODE // 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];
|
||||
sprintf_P(umw_gcode, PSTR("G29S%i"), parser.value_int());
|
||||
queue.inject(umw_gcode);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
#endif // AUTO_BED_LEVELING_UBL
|
||||
@@ -0,0 +1,315 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
//#define UBL_DEVEL_DEBUGGING
|
||||
|
||||
#include "../../../module/motion.h"
|
||||
|
||||
#define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
#include "../../../core/debug_out.h"
|
||||
|
||||
#define UBL_VERSION "1.01"
|
||||
#define UBL_OK false
|
||||
#define UBL_ERR true
|
||||
|
||||
enum MeshPointType : char { INVALID, REAL, SET_IN_BITMAP, CLOSEST };
|
||||
|
||||
// External references
|
||||
|
||||
struct mesh_index_pair;
|
||||
|
||||
#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))
|
||||
|
||||
#if ENABLED(OPTIMIZED_MESH_STORAGE)
|
||||
typedef int16_t mesh_store_t[GRID_MAX_POINTS_X][GRID_MAX_POINTS_Y];
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
bool C_seen;
|
||||
int8_t KLS_storage_slot;
|
||||
grid_count_t R_repetition;
|
||||
uint8_t V_verbosity,
|
||||
P_phase,
|
||||
T_map_type;
|
||||
float B_shim_thickness,
|
||||
C_constant;
|
||||
xy_pos_t XY_pos;
|
||||
xy_bool_t XY_seen;
|
||||
#if HAS_BED_PROBE
|
||||
uint8_t J_grid_size;
|
||||
#endif
|
||||
} G29_parameters_t;
|
||||
|
||||
class unified_bed_leveling {
|
||||
private:
|
||||
|
||||
static G29_parameters_t param;
|
||||
|
||||
#if IS_NEWPANEL
|
||||
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;
|
||||
#endif
|
||||
|
||||
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 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 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);
|
||||
}
|
||||
|
||||
#if ENABLED(UBL_DEVEL_DEBUGGING)
|
||||
static void g29_what_command();
|
||||
static void g29_eeprom_dump();
|
||||
static void g29_compare_current_mesh_to_stored_mesh();
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
static void echo_name();
|
||||
static void report_current_mesh();
|
||||
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 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 int8_t storage_slot;
|
||||
|
||||
static bed_mesh_t z_values;
|
||||
#if ENABLED(OPTIMIZED_MESH_STORAGE)
|
||||
static void set_store_from_mesh(const bed_mesh_t &in_values, mesh_store_t &stored_values);
|
||||
static void set_mesh_from_store(const mesh_store_t &stored_values, bed_mesh_t &out_values);
|
||||
#endif
|
||||
static const float _mesh_index_to_xpos[GRID_MAX_POINTS_X],
|
||||
_mesh_index_to_ypos[GRID_MAX_POINTS_Y];
|
||||
|
||||
#if HAS_MARLINUI_MENU
|
||||
static bool lcd_map_control;
|
||||
static void steppers_were_disabled();
|
||||
#else
|
||||
static void steppers_were_disabled() {}
|
||||
#endif
|
||||
|
||||
static volatile int16_t encoder_diff; // Volatile because buttons may change it at interrupt time
|
||||
|
||||
unified_bed_leveling();
|
||||
|
||||
FORCE_INLINE static void set_z(const int8_t px, const int8_t py, const_float_t z) { z_values[px][py] = z; }
|
||||
|
||||
static int8_t cell_index_x_raw(const_float_t x) {
|
||||
return FLOOR((x - (MESH_MIN_X)) * RECIPROCAL(MESH_X_DIST));
|
||||
}
|
||||
|
||||
static int8_t cell_index_y_raw(const_float_t y) {
|
||||
return FLOOR((y - (MESH_MIN_Y)) * RECIPROCAL(MESH_Y_DIST));
|
||||
}
|
||||
|
||||
static bool cell_index_x_valid(const_float_t x) {
|
||||
return WITHIN(cell_index_x_raw(x), 0, GRID_MAX_CELLS_X - 1);
|
||||
}
|
||||
|
||||
static bool cell_index_y_valid(const_float_t y) {
|
||||
return WITHIN(cell_index_y_raw(y), 0, GRID_MAX_CELLS_Y - 1);
|
||||
}
|
||||
|
||||
static uint8_t cell_index_x(const_float_t x) {
|
||||
return constrain(cell_index_x_raw(x), 0, GRID_MAX_CELLS_X - 1);
|
||||
}
|
||||
|
||||
static uint8_t cell_index_y(const_float_t y) {
|
||||
return constrain(cell_index_y_raw(y), 0, GRID_MAX_CELLS_Y - 1);
|
||||
}
|
||||
|
||||
static xy_uint8_t cell_indexes(const_float_t x, const_float_t y) {
|
||||
return { cell_index_x(x), cell_index_y(y) };
|
||||
}
|
||||
static xy_uint8_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);
|
||||
return WITHIN(px, 0, (GRID_MAX_POINTS_X) - 1) ? px : -1;
|
||||
}
|
||||
static int8_t closest_y_index(const_float_t y) {
|
||||
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 xy_int8_t closest_indexes(const xy_pos_t &xy) {
|
||||
return { closest_x_index(xy.x), closest_y_index(xy.y) };
|
||||
}
|
||||
|
||||
/**
|
||||
* z2 --|
|
||||
* z0 | |
|
||||
* | | + (z2-z1)
|
||||
* z1 | | |
|
||||
* ---+-------------+--------+-- --|
|
||||
* a1 a0 a2
|
||||
* |<---delta_a---------->|
|
||||
*
|
||||
* calc_z0 is the basis for all the Mesh Based correction. It is used to
|
||||
* find the expected Z Height at a position between two known Z-Height locations.
|
||||
*
|
||||
* It is fairly expensive with its 4 floating point additions and 2 floating point
|
||||
* multiplications.
|
||||
*/
|
||||
FORCE_INLINE static float calc_z0(const_float_t a0, const_float_t a1, const_float_t z1, const_float_t a2, const_float_t z2) {
|
||||
return z1 + (z2 - z1) * (a0 - a1) / (a2 - a1);
|
||||
}
|
||||
|
||||
#ifdef UBL_Z_RAISE_WHEN_OFF_MESH
|
||||
#define _UBL_OUTER_Z_RAISE UBL_Z_RAISE_WHEN_OFF_MESH
|
||||
#else
|
||||
#define _UBL_OUTER_Z_RAISE NAN
|
||||
#endif
|
||||
|
||||
/**
|
||||
* 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 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)) {
|
||||
if (WITHIN(x1_i, 0, (GRID_MAX_POINTS_X) - 1)) DEBUG_ECHOPGM("yi"); else DEBUG_ECHOPGM("x1_i");
|
||||
DEBUG_ECHOLNPGM(" out of bounds in z_correction_for_x_on_horizontal_mesh_line(rx0=", rx0, ",x1_i=", x1_i, ",yi=", yi, ")");
|
||||
}
|
||||
|
||||
// The requested location is off the mesh. Return UBL_Z_RAISE_WHEN_OFF_MESH or NAN.
|
||||
return _UBL_OUTER_Z_RAISE;
|
||||
}
|
||||
|
||||
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
|
||||
// If it is, it is clamped to the last element of the
|
||||
// z_values[][] array and no correction is applied.
|
||||
}
|
||||
|
||||
//
|
||||
// See comments above for z_correction_for_x_on_horizontal_mesh_line
|
||||
//
|
||||
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)) {
|
||||
if (WITHIN(xi, 0, (GRID_MAX_POINTS_X) - 1)) DEBUG_ECHOPGM("y1_i"); else DEBUG_ECHOPGM("xi");
|
||||
DEBUG_ECHOLNPGM(" out of bounds in z_correction_for_y_on_vertical_mesh_line(ry0=", ry0, ", xi=", xi, ", y1_i=", y1_i, ")");
|
||||
}
|
||||
|
||||
// The requested location is off the mesh. Return UBL_Z_RAISE_WHEN_OFF_MESH or NAN.
|
||||
return _UBL_OUTER_Z_RAISE;
|
||||
}
|
||||
|
||||
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
|
||||
// If it is, it is clamped to the last element of the
|
||||
// z_values[][] array and no correction is applied.
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the generic Z-Correction. It works anywhere within a Mesh Cell. It first
|
||||
* does a linear interpolation along both of the bounding X-Mesh-Lines to find the
|
||||
* Z-Height at both ends. Then it does a linear interpolation of these heights based
|
||||
* on the Y position within the cell.
|
||||
*/
|
||||
static float get_z_correction(const_float_t rx0, const_float_t ry0) {
|
||||
const int8_t cx = cell_index_x(rx0), cy = cell_index_y(ry0); // return values are clamped
|
||||
|
||||
/**
|
||||
* Check if the requested location is off the mesh. If so, and
|
||||
* UBL_Z_RAISE_WHEN_OFF_MESH is specified, that value is returned.
|
||||
*/
|
||||
#ifdef UBL_Z_RAISE_WHEN_OFF_MESH
|
||||
if (!WITHIN(rx0, MESH_MIN_X, MESH_MAX_X) || !WITHIN(ry0, MESH_MIN_Y, MESH_MAX_Y))
|
||||
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 x0 = get_mesh_x(cx), x1 = get_mesh_x(cx + 1),
|
||||
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 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 ");
|
||||
}
|
||||
|
||||
if (DEBUGGING(MESH_ADJUST)) {
|
||||
DEBUG_ECHOPGM("get_z_correction(", rx0, ", ", ry0);
|
||||
DEBUG_ECHOLNPAIR_F(") => ", z0, 6);
|
||||
}
|
||||
|
||||
return z0;
|
||||
}
|
||||
static float get_z_correction(const xy_pos_t &pos) { return get_z_correction(pos.x, pos.y); }
|
||||
|
||||
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 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);
|
||||
}
|
||||
|
||||
#if UBL_SEGMENTED
|
||||
static bool line_to_destination_segmented(const_feedRate_t scaled_fr_mm_s);
|
||||
#else
|
||||
static void line_to_destination_cartesian(const_feedRate_t scaled_fr_mm_s, const uint8_t e);
|
||||
#endif
|
||||
|
||||
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 bedlevel;
|
||||
|
||||
// Prevent debugging propagating to other files
|
||||
#include "../../../core/debug_out.h"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,492 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(AUTO_BED_LEVELING_UBL)
|
||||
|
||||
#include "../bedlevel.h"
|
||||
#include "../../../module/planner.h"
|
||||
#include "../../../module/motion.h"
|
||||
|
||||
#if ENABLED(DELTA)
|
||||
#include "../../../module/delta.h"
|
||||
#endif
|
||||
|
||||
#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
|
||||
* as possible to determine if this is the case. If this move is within the same cell, we will
|
||||
* just do the required Z-Height correction, call the Planner's buffer_line() routine, and leave
|
||||
*/
|
||||
#if HAS_POSITION_MODIFIERS
|
||||
xyze_pos_t start = current_position, end = destination;
|
||||
planner.apply_modifiers(start);
|
||||
planner.apply_modifiers(end);
|
||||
#else
|
||||
const xyze_pos_t &start = current_position, &end = destination;
|
||||
#endif
|
||||
|
||||
const xy_uint8_t istart = cell_indexes(start), iend = cell_indexes(end);
|
||||
|
||||
// A move within the same cell needs no splitting
|
||||
if (istart == iend) {
|
||||
|
||||
FINAL_MOVE:
|
||||
|
||||
// When UBL_Z_RAISE_WHEN_OFF_MESH is disabled Z correction is extrapolated from the edge of the mesh
|
||||
#ifdef UBL_Z_RAISE_WHEN_OFF_MESH
|
||||
// For a move off the UBL mesh, use a constant Z raise
|
||||
if (!cell_index_x_valid(end.x) || !cell_index_y_valid(end.y)) {
|
||||
|
||||
// Note: There is no Z Correction in this case. We are off the mesh and don't know what
|
||||
// a reasonable correction would be, UBL_Z_RAISE_WHEN_OFF_MESH will be used instead of
|
||||
// a calculated (Bi-Linear interpolation) correction.
|
||||
|
||||
end.z += UBL_Z_RAISE_WHEN_OFF_MESH;
|
||||
planner.buffer_segment(end, scaled_fr_mm_s, extruder);
|
||||
current_position = destination;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// The distance is always MESH_X_DIST so multiply by the constant reciprocal.
|
||||
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]);
|
||||
|
||||
// X cell-fraction done. Interpolate the two Z offsets with the Y fraction for the final Z offset.
|
||||
const float z0 = (z1 + (z2 - z1) * yratio) * planner.fade_scaling_factor_for_z(end.z);
|
||||
|
||||
// Undefined parts of the Mesh in z_values[][] are NAN.
|
||||
// Replace NAN corrections with 0.0 to prevent NAN propagation.
|
||||
if (!isnan(z0)) end.z += z0;
|
||||
planner.buffer_segment(end, scaled_fr_mm_s, extruder);
|
||||
current_position = destination;
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Past this point the move is known to cross one or more mesh lines. Check for the most common
|
||||
* case - crossing only one X or Y line - after details are worked out to reduce computation.
|
||||
*/
|
||||
|
||||
const xy_float_t dist = end - start;
|
||||
const xy_bool_t neg { dist.x < 0, dist.y < 0 };
|
||||
const xy_uint8_t ineg { uint8_t(neg.x), uint8_t(neg.y) };
|
||||
const xy_float_t sign { neg.x ? -1.0f : 1.0f, neg.y ? -1.0f : 1.0f };
|
||||
const xy_int8_t iadd { int8_t(iend.x == istart.x ? 0 : sign.x), int8_t(iend.y == istart.y ? 0 : sign.y) };
|
||||
|
||||
/**
|
||||
* Compute the extruder scaling factor for each partial move, checking for
|
||||
* zero-length moves that would result in an infinite scaling factor.
|
||||
* A float divide is required for this, but then it just multiplies.
|
||||
* Also select a scaling factor based on the larger of the X and Y
|
||||
* components. The larger of the two is used to preserve precision.
|
||||
*/
|
||||
|
||||
const xy_float_t ad = sign * dist;
|
||||
const bool use_x_dist = ad.x > ad.y;
|
||||
|
||||
float on_axis_distance = use_x_dist ? dist.x : dist.y;
|
||||
|
||||
const float z_normalized_dist = (end.z - start.z) / on_axis_distance; // Allow divide by zero
|
||||
#if HAS_EXTRUDERS
|
||||
const float e_normalized_dist = (end.e - start.e) / on_axis_distance;
|
||||
const bool inf_normalized_flag = isinf(e_normalized_dist);
|
||||
#endif
|
||||
|
||||
xy_uint8_t icell = istart;
|
||||
|
||||
const float ratio = dist.y / dist.x, // Allow divide by zero
|
||||
c = start.y - ratio * start.x;
|
||||
|
||||
const bool inf_ratio_flag = isinf(ratio);
|
||||
|
||||
xyze_pos_t dest; // Stores XYZE for segmented moves
|
||||
|
||||
/**
|
||||
* Handle vertical lines that stay within one column.
|
||||
* These need not be perfectly vertical.
|
||||
*/
|
||||
if (iadd.x == 0) { // Vertical line?
|
||||
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 = get_mesh_y(icell.y);
|
||||
|
||||
/**
|
||||
* Skip the calculations for an infinite slope.
|
||||
* For others the next X is the same so this can continue.
|
||||
* Calculate X at the next Y mesh line.
|
||||
*/
|
||||
dest.x = inf_ratio_flag ? start.x : (next_mesh_line_y - c) / ratio;
|
||||
|
||||
float z0 = z_correction_for_x_on_horizontal_mesh_line(dest.x, icell.x, icell.y)
|
||||
* planner.fade_scaling_factor_for_z(end.z);
|
||||
|
||||
// Undefined parts of the Mesh in z_values[][] are NAN.
|
||||
// Replace NAN corrections with 0.0 to prevent NAN propagation.
|
||||
if (isnan(z0)) z0 = 0.0;
|
||||
|
||||
dest.y = get_mesh_y(icell.y);
|
||||
|
||||
/**
|
||||
* Without this check, it's possible to generate a zero length move, as in the case where
|
||||
* the line is heading down, starting exactly on a mesh line boundary. Since this is rare
|
||||
* it might be fine to remove this check and let planner.buffer_segment() filter it out.
|
||||
*/
|
||||
if (dest.y != start.y) {
|
||||
if (!inf_normalized_flag) { // fall-through faster than branch
|
||||
on_axis_distance = use_x_dist ? dest.x - start.x : dest.y - start.y;
|
||||
TERN_(HAS_EXTRUDERS, dest.e = start.e + on_axis_distance * e_normalized_dist);
|
||||
dest.z = start.z + on_axis_distance * z_normalized_dist;
|
||||
}
|
||||
else {
|
||||
TERN_(HAS_EXTRUDERS, dest.e = end.e);
|
||||
dest.z = end.z;
|
||||
}
|
||||
|
||||
dest.z += z0;
|
||||
planner.buffer_segment(dest, scaled_fr_mm_s, extruder);
|
||||
|
||||
}
|
||||
else
|
||||
DEBUG_ECHOLNPGM("[ubl] skip Y segment");
|
||||
}
|
||||
|
||||
// At the final destination? Usually not, but when on a Y Mesh Line it's completed.
|
||||
if (xy_pos_t(current_position) != xy_pos_t(end))
|
||||
goto FINAL_MOVE;
|
||||
|
||||
current_position = destination;
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle horizontal lines that stay within one row.
|
||||
* These need not be perfectly horizontal.
|
||||
*/
|
||||
if (iadd.y == 0) { // Horizontal line?
|
||||
icell.x += ineg.x; // Heading left? Just go to the left edge of the cell for the first move.
|
||||
|
||||
while (icell.x != iend.x + ineg.x) {
|
||||
icell.x += iadd.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)
|
||||
* planner.fade_scaling_factor_for_z(end.z);
|
||||
|
||||
// Undefined parts of the Mesh in z_values[][] are NAN.
|
||||
// Replace NAN corrections with 0.0 to prevent NAN propagation.
|
||||
if (isnan(z0)) z0 = 0.0;
|
||||
|
||||
/**
|
||||
* Without this check, it's possible to generate a zero length move, as in the case where
|
||||
* the line is heading left, starting exactly on a mesh line boundary. Since this is rare
|
||||
* it might be fine to remove this check and let planner.buffer_segment() filter it out.
|
||||
*/
|
||||
if (dest.x != start.x) {
|
||||
if (!inf_normalized_flag) {
|
||||
on_axis_distance = use_x_dist ? dest.x - start.x : dest.y - start.y;
|
||||
TERN_(HAS_EXTRUDERS, dest.e = start.e + on_axis_distance * e_normalized_dist); // Based on X or Y because the move is horizontal
|
||||
dest.z = start.z + on_axis_distance * z_normalized_dist;
|
||||
}
|
||||
else {
|
||||
TERN_(HAS_EXTRUDERS, dest.e = end.e);
|
||||
dest.z = end.z;
|
||||
}
|
||||
|
||||
dest.z += z0;
|
||||
if (!planner.buffer_segment(dest, scaled_fr_mm_s, extruder)) break;
|
||||
|
||||
}
|
||||
else
|
||||
DEBUG_ECHOLNPGM("[ubl] skip Y segment");
|
||||
}
|
||||
|
||||
if (xy_pos_t(current_position) != xy_pos_t(end))
|
||||
goto FINAL_MOVE;
|
||||
|
||||
current_position = destination;
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic case of a line crossing both X and Y Mesh lines.
|
||||
*/
|
||||
|
||||
xy_uint8_t cnt = istart.diff(iend);
|
||||
|
||||
icell += ineg;
|
||||
|
||||
while (cnt) {
|
||||
|
||||
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
|
||||
// (No need to worry about ratio == 0.
|
||||
// In that case, it was already detected
|
||||
// as a vertical line move above.)
|
||||
|
||||
if (neg.x == (dest.x > next_mesh_line_x)) { // Check if we hit the Y line first
|
||||
// Yes! Crossing a Y Mesh Line next
|
||||
float z0 = z_correction_for_x_on_horizontal_mesh_line(dest.x, icell.x - ineg.x, icell.y + iadd.y)
|
||||
* planner.fade_scaling_factor_for_z(end.z);
|
||||
|
||||
// Undefined parts of the Mesh in z_values[][] are NAN.
|
||||
// Replace NAN corrections with 0.0 to prevent NAN propagation.
|
||||
if (isnan(z0)) z0 = 0.0;
|
||||
|
||||
dest.y = next_mesh_line_y;
|
||||
|
||||
if (!inf_normalized_flag) {
|
||||
on_axis_distance = use_x_dist ? dest.x - start.x : dest.y - start.y;
|
||||
TERN_(HAS_EXTRUDERS, dest.e = start.e + on_axis_distance * e_normalized_dist);
|
||||
dest.z = start.z + on_axis_distance * z_normalized_dist;
|
||||
}
|
||||
else {
|
||||
TERN_(HAS_EXTRUDERS, dest.e = end.e);
|
||||
dest.z = end.z;
|
||||
}
|
||||
|
||||
dest.z += z0;
|
||||
if (!planner.buffer_segment(dest, scaled_fr_mm_s, extruder)) break;
|
||||
|
||||
icell.y += iadd.y;
|
||||
cnt.y--;
|
||||
}
|
||||
else {
|
||||
// Yes! Crossing a X Mesh Line next
|
||||
float z0 = z_correction_for_y_on_vertical_mesh_line(dest.y, icell.x + iadd.x, icell.y - ineg.y)
|
||||
* planner.fade_scaling_factor_for_z(end.z);
|
||||
|
||||
// Undefined parts of the Mesh in z_values[][] are NAN.
|
||||
// Replace NAN corrections with 0.0 to prevent NAN propagation.
|
||||
if (isnan(z0)) z0 = 0.0;
|
||||
|
||||
dest.x = next_mesh_line_x;
|
||||
|
||||
if (!inf_normalized_flag) {
|
||||
on_axis_distance = use_x_dist ? dest.x - start.x : dest.y - start.y;
|
||||
TERN_(HAS_EXTRUDERS, dest.e = start.e + on_axis_distance * e_normalized_dist);
|
||||
dest.z = start.z + on_axis_distance * z_normalized_dist;
|
||||
}
|
||||
else {
|
||||
TERN_(HAS_EXTRUDERS, dest.e = end.e);
|
||||
dest.z = end.z;
|
||||
}
|
||||
|
||||
dest.z += z0;
|
||||
if (!planner.buffer_segment(dest, scaled_fr_mm_s, extruder)) break;
|
||||
|
||||
icell.x += iadd.x;
|
||||
cnt.x--;
|
||||
}
|
||||
|
||||
if (cnt.x < 0 || cnt.y < 0) break; // Too far! Exit the loop and go to FINAL_MOVE
|
||||
}
|
||||
|
||||
if (xy_pos_t(current_position) != xy_pos_t(end))
|
||||
goto FINAL_MOVE;
|
||||
|
||||
current_position = destination;
|
||||
}
|
||||
|
||||
#else // UBL_SEGMENTED
|
||||
|
||||
#if IS_SCARA
|
||||
#define SEGMENT_MIN_LENGTH 0.25 // SCARA minimum segment size is 0.25mm
|
||||
#elif IS_KINEMATIC
|
||||
#define SEGMENT_MIN_LENGTH 0.10 // (mm) Still subject to DEFAULT_SEGMENTS_PER_SECOND
|
||||
#else // CARTESIAN
|
||||
#ifdef LEVELED_SEGMENT_LENGTH
|
||||
#define SEGMENT_MIN_LENGTH LEVELED_SEGMENT_LENGTH
|
||||
#else
|
||||
#define SEGMENT_MIN_LENGTH 1.00 // (mm) Similar to G2/G3 arc segmentation
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Prepare a segmented linear move for DELTA/SCARA/CARTESIAN with UBL and FADE semantics.
|
||||
* This calls planner.buffer_segment multiple times for small incremental moves.
|
||||
* 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) {
|
||||
|
||||
if (!position_is_reachable(destination)) // fail if moving outside reachable boundary
|
||||
return true; // did not move, so current_position still accurate
|
||||
|
||||
const xyze_pos_t total = destination - current_position;
|
||||
|
||||
const float cart_xy_mm_2 = HYPOT2(total.x, total.y),
|
||||
cart_xy_mm = SQRT(cart_xy_mm_2); // Total XY distance
|
||||
|
||||
#if IS_KINEMATIC
|
||||
const float seconds = cart_xy_mm / scaled_fr_mm_s; // Duration of XY move at requested rate
|
||||
uint16_t segments = LROUND(segments_per_second * seconds), // Preferred number of segments for distance @ feedrate
|
||||
seglimit = LROUND(cart_xy_mm * RECIPROCAL(SEGMENT_MIN_LENGTH)); // Number of segments at minimum segment length
|
||||
NOMORE(segments, seglimit); // Limit to minimum segment length (fewer segments)
|
||||
#else
|
||||
uint16_t segments = LROUND(cart_xy_mm * RECIPROCAL(SEGMENT_MIN_LENGTH)); // Cartesian fixed segment length
|
||||
#endif
|
||||
|
||||
NOLESS(segments, 1U); // Must have at least one 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)
|
||||
hints.inv_duration = scaled_fr_mm_s / hints.millimeters;
|
||||
#endif
|
||||
|
||||
xyze_float_t diff = total * inv_segments;
|
||||
|
||||
// Note that E segment distance could vary slightly as z mesh height
|
||||
// changes for each segment, but small enough to ignore.
|
||||
|
||||
xyze_pos_t raw = current_position;
|
||||
|
||||
// Just do plain segmentation if UBL is inactive or the target is above the fade height
|
||||
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, hints);
|
||||
}
|
||||
planner.buffer_line(destination, scaled_fr_mm_s, active_extruder, hints);
|
||||
return false; // Did not set current from destination
|
||||
}
|
||||
|
||||
// Otherwise perform per-segment leveling
|
||||
|
||||
#if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
|
||||
const float fade_scaling_factor = planner.fade_scaling_factor_for_z(destination.z);
|
||||
#endif
|
||||
|
||||
// Move to first segment destination
|
||||
raw += diff;
|
||||
|
||||
for (;;) { // for each mesh cell encountered during the move
|
||||
|
||||
// Compute mesh cell invariants that remain constant for all segments within cell.
|
||||
// Note for cell index, if point is outside the mesh grid (in MESH_INSET perimeter)
|
||||
// the bilinear interpolation from the adjacent cell within the mesh will still work.
|
||||
// Inner loop will exit each time (because out of cell bounds) but will come back
|
||||
// in top of loop and again re-find same adjacent cell and use it, just less efficient
|
||||
// for mesh inset area.
|
||||
|
||||
xy_int8_t icell = {
|
||||
int8_t((raw.x - (MESH_MIN_X)) * RECIPROCAL(MESH_X_DIST)),
|
||||
int8_t((raw.y - (MESH_MIN_Y)) * RECIPROCAL(MESH_Y_DIST))
|
||||
};
|
||||
LIMIT(icell.x, 0, GRID_MAX_CELLS_X);
|
||||
LIMIT(icell.y, 0, GRID_MAX_CELLS_Y);
|
||||
|
||||
const int8_t ncellx = _MIN(icell.x+1, GRID_MAX_CELLS_X),
|
||||
ncelly = _MIN(icell.y+1, GRID_MAX_CELLS_Y);
|
||||
float z_x0y0 = z_values[icell.x][icell.y], // z at lower left corner
|
||||
z_x1y0 = z_values[ncellx ][icell.y], // z at upper left corner
|
||||
z_x0y1 = z_values[icell.x][ncelly ], // z at lower right corner
|
||||
z_x1y1 = z_values[ncellx ][ncelly ]; // z at upper right corner
|
||||
|
||||
if (isnan(z_x0y0)) z_x0y0 = 0; // ideally activating planner.leveling_active (G29 A)
|
||||
if (isnan(z_x1y0)) z_x1y0 = 0; // should refuse if any invalid mesh points
|
||||
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 = { 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)
|
||||
z_xmy1 = (z_x1y1 - z_x0y1) * RECIPROCAL(MESH_X_DIST); // z slope per x along y1 (upper left to upper right)
|
||||
|
||||
float z_cxy0 = z_x0y0 + z_xmy0 * cell.x; // z height along y0 at cell.x (changes for each cell.x in cell)
|
||||
|
||||
const float z_cxy1 = z_x0y1 + z_xmy1 * cell.x, // z height along y1 at cell.x
|
||||
z_cxyd = z_cxy1 - z_cxy0; // z height difference along cell.x from y0 to y1
|
||||
|
||||
float z_cxym = z_cxyd * RECIPROCAL(MESH_Y_DIST); // z slope per y along cell.x from pos.y to y1 (changes for each cell.x in cell)
|
||||
|
||||
// float z_cxcy = z_cxy0 + z_cxym * cell.y; // interpolated mesh z height along cell.x at cell.y (do inside the segment loop)
|
||||
|
||||
// As subsequent segments step through this cell, the z_cxy0 intercept will change
|
||||
// and the z_cxym slope will change, both as a function of cell.x within the cell, and
|
||||
// each change by a constant for fixed segment lengths.
|
||||
|
||||
const float z_sxy0 = z_xmy0 * diff.x, // per-segment adjustment to z_cxy0
|
||||
z_sxym = (z_xmy1 - z_xmy0) * RECIPROCAL(MESH_Y_DIST) * diff.x; // per-segment adjustment to z_cxym
|
||||
|
||||
for (;;) { // for all segments within this mesh cell
|
||||
|
||||
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
|
||||
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, hints);
|
||||
raw.z = oldz;
|
||||
|
||||
if (segments == 0) // done with last segment
|
||||
return false; // didn't set current from destination
|
||||
|
||||
raw += diff;
|
||||
cell += diff;
|
||||
|
||||
if (!WITHIN(cell.x, 0, MESH_X_DIST) || !WITHIN(cell.y, 0, MESH_Y_DIST)) // done within this cell, break to next
|
||||
break;
|
||||
|
||||
// Next segment still within same mesh cell, adjust the per-segment
|
||||
// slope and intercept to compute next z height.
|
||||
|
||||
z_cxy0 += z_sxy0; // adjust z_cxy0 by per-segment z_sxy0
|
||||
z_cxym += z_sxym; // adjust z_cxym by per-segment z_sxym
|
||||
|
||||
} // segment loop
|
||||
} // cell loop
|
||||
|
||||
return false; // caller will update current_position
|
||||
}
|
||||
|
||||
#endif // UBL_SEGMENTED
|
||||
|
||||
#endif // AUTO_BED_LEVELING_UBL
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
|
||||
#if ENABLED(BINARY_FILE_TRANSFER)
|
||||
|
||||
#include "../sd/cardreader.h"
|
||||
#include "binary_stream.h"
|
||||
|
||||
char* SDFileTransferProtocol::Packet::Open::data = nullptr;
|
||||
size_t SDFileTransferProtocol::data_waiting, SDFileTransferProtocol::transfer_timeout, SDFileTransferProtocol::idle_timeout;
|
||||
bool SDFileTransferProtocol::transfer_active, SDFileTransferProtocol::dummy_transfer, SDFileTransferProtocol::compression;
|
||||
|
||||
BinaryStream binaryStream[NUM_SERIAL];
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,456 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#define BINARY_STREAM_COMPRESSION
|
||||
#if ENABLED(BINARY_STREAM_COMPRESSION)
|
||||
#include "../libs/heatshrink/heatshrink_decoder.h"
|
||||
// STM32 (and others?) require a word-aligned buffer for SD card transfers via DMA
|
||||
static __attribute__((aligned(sizeof(size_t)))) uint8_t decode_buffer[512] = {};
|
||||
static heatshrink_decoder hsd;
|
||||
#endif
|
||||
|
||||
inline bool bs_serial_data_available(const serial_index_t index) {
|
||||
return SERIAL_IMPL.available(index);
|
||||
}
|
||||
|
||||
inline int bs_read_serial(const serial_index_t index) {
|
||||
return SERIAL_IMPL.read(index);
|
||||
}
|
||||
|
||||
class SDFileTransferProtocol {
|
||||
private:
|
||||
struct Packet {
|
||||
struct [[gnu::packed]] Open {
|
||||
static bool validate(char *buffer, size_t length) {
|
||||
return (length > sizeof(Open) && buffer[length - 1] == '\0');
|
||||
}
|
||||
static Open& decode(char *buffer) {
|
||||
data = &buffer[2];
|
||||
return *reinterpret_cast<Open*>(buffer);
|
||||
}
|
||||
bool compression_enabled() { return compression & 0x1; }
|
||||
bool dummy_transfer() { return dummy & 0x1; }
|
||||
static char* filename() { return data; }
|
||||
private:
|
||||
uint8_t dummy, compression;
|
||||
static char* data; // variable length strings complicate things
|
||||
};
|
||||
};
|
||||
|
||||
static bool file_open(char *filename) {
|
||||
if (!dummy_transfer) {
|
||||
card.mount();
|
||||
card.openFileWrite(filename);
|
||||
if (!card.isFileOpen()) return false;
|
||||
}
|
||||
transfer_active = true;
|
||||
data_waiting = 0;
|
||||
TERN_(BINARY_STREAM_COMPRESSION, heatshrink_decoder_reset(&hsd));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool file_write(char *buffer, const size_t length) {
|
||||
#if ENABLED(BINARY_STREAM_COMPRESSION)
|
||||
if (compression) {
|
||||
size_t total_processed = 0, processed_count = 0;
|
||||
HSD_poll_res presult;
|
||||
|
||||
while (total_processed < length) {
|
||||
heatshrink_decoder_sink(&hsd, reinterpret_cast<uint8_t*>(&buffer[total_processed]), length - total_processed, &processed_count);
|
||||
total_processed += processed_count;
|
||||
do {
|
||||
presult = heatshrink_decoder_poll(&hsd, &decode_buffer[data_waiting], sizeof(decode_buffer) - data_waiting, &processed_count);
|
||||
data_waiting += processed_count;
|
||||
if (data_waiting == sizeof(decode_buffer)) {
|
||||
if (!dummy_transfer)
|
||||
if (card.write(decode_buffer, data_waiting) < 0) {
|
||||
return false;
|
||||
}
|
||||
data_waiting = 0;
|
||||
}
|
||||
} while (presult == HSDR_POLL_MORE);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return (dummy_transfer || card.write(buffer, length) >= 0);
|
||||
}
|
||||
|
||||
static bool file_close() {
|
||||
if (!dummy_transfer) {
|
||||
#if ENABLED(BINARY_STREAM_COMPRESSION)
|
||||
// flush any buffered data
|
||||
if (data_waiting) {
|
||||
if (card.write(decode_buffer, data_waiting) < 0) return false;
|
||||
data_waiting = 0;
|
||||
}
|
||||
#endif
|
||||
card.closefile();
|
||||
card.release();
|
||||
}
|
||||
TERN_(BINARY_STREAM_COMPRESSION, heatshrink_decoder_finish(&hsd));
|
||||
transfer_active = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void transfer_abort() {
|
||||
if (!dummy_transfer) {
|
||||
card.closefile();
|
||||
card.removeFile(card.filename);
|
||||
card.release();
|
||||
TERN_(BINARY_STREAM_COMPRESSION, heatshrink_decoder_finish(&hsd));
|
||||
}
|
||||
transfer_active = false;
|
||||
return;
|
||||
}
|
||||
|
||||
enum class FileTransfer : uint8_t { QUERY, OPEN, CLOSE, WRITE, ABORT };
|
||||
|
||||
static size_t data_waiting, transfer_timeout, idle_timeout;
|
||||
static bool transfer_active, dummy_transfer, compression;
|
||||
|
||||
public:
|
||||
|
||||
static void idle() {
|
||||
// If a transfer is interrupted and a file is left open, abort it after TIMEOUT ms
|
||||
const millis_t ms = millis();
|
||||
if (transfer_active && ELAPSED(ms, idle_timeout)) {
|
||||
idle_timeout = ms + IDLE_PERIOD;
|
||||
if (ELAPSED(ms, transfer_timeout)) transfer_abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void process(uint8_t packet_type, char *buffer, const uint16_t length) {
|
||||
transfer_timeout = millis() + TIMEOUT;
|
||||
switch (static_cast<FileTransfer>(packet_type)) {
|
||||
case FileTransfer::QUERY:
|
||||
SERIAL_ECHOPGM("PFT:version:", VERSION_MAJOR, ".", VERSION_MINOR, ".", VERSION_PATCH);
|
||||
#if ENABLED(BINARY_STREAM_COMPRESSION)
|
||||
SERIAL_ECHOLNPGM(":compression:heatshrink,", HEATSHRINK_STATIC_WINDOW_BITS, ",", HEATSHRINK_STATIC_LOOKAHEAD_BITS);
|
||||
#else
|
||||
SERIAL_ECHOLNPGM(":compression:none");
|
||||
#endif
|
||||
break;
|
||||
case FileTransfer::OPEN:
|
||||
if (transfer_active)
|
||||
SERIAL_ECHOLNPGM("PFT:busy");
|
||||
else {
|
||||
if (Packet::Open::validate(buffer, length)) {
|
||||
auto packet = Packet::Open::decode(buffer);
|
||||
compression = packet.compression_enabled();
|
||||
dummy_transfer = packet.dummy_transfer();
|
||||
if (file_open(packet.filename())) {
|
||||
SERIAL_ECHOLNPGM("PFT:success");
|
||||
break;
|
||||
}
|
||||
}
|
||||
SERIAL_ECHOLNPGM("PFT:fail");
|
||||
}
|
||||
break;
|
||||
case FileTransfer::CLOSE:
|
||||
if (transfer_active) {
|
||||
if (file_close())
|
||||
SERIAL_ECHOLNPGM("PFT:success");
|
||||
else
|
||||
SERIAL_ECHOLNPGM("PFT:ioerror");
|
||||
}
|
||||
else SERIAL_ECHOLNPGM("PFT:invalid");
|
||||
break;
|
||||
case FileTransfer::WRITE:
|
||||
if (!transfer_active)
|
||||
SERIAL_ECHOLNPGM("PFT:invalid");
|
||||
else if (!file_write(buffer, length))
|
||||
SERIAL_ECHOLNPGM("PFT:ioerror");
|
||||
break;
|
||||
case FileTransfer::ABORT:
|
||||
transfer_abort();
|
||||
SERIAL_ECHOLNPGM("PFT:success");
|
||||
break;
|
||||
default:
|
||||
SERIAL_ECHOLNPGM("PTF:invalid");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const uint16_t VERSION_MAJOR = 0, VERSION_MINOR = 1, VERSION_PATCH = 0, TIMEOUT = 10000, IDLE_PERIOD = 1000;
|
||||
};
|
||||
|
||||
class BinaryStream {
|
||||
public:
|
||||
enum class Protocol : uint8_t { CONTROL, FILE_TRANSFER };
|
||||
|
||||
enum class ProtocolControl : uint8_t { SYNC = 1, CLOSE };
|
||||
|
||||
enum class StreamState : uint8_t { PACKET_RESET, PACKET_WAIT, PACKET_HEADER, PACKET_DATA, PACKET_FOOTER,
|
||||
PACKET_PROCESS, PACKET_RESEND, PACKET_TIMEOUT, PACKET_ERROR };
|
||||
|
||||
struct Packet { // 10 byte protocol overhead, ascii with checksum and line number has a minimum of 7 increasing with line
|
||||
|
||||
union Header {
|
||||
static constexpr uint16_t HEADER_TOKEN = 0xB5AD;
|
||||
struct [[gnu::packed]] {
|
||||
uint16_t token; // packet start token
|
||||
uint8_t sync; // stream sync, resend id and packet loss detection
|
||||
uint8_t meta; // 4 bit protocol,
|
||||
// 4 bit packet type
|
||||
uint16_t size; // data length
|
||||
uint16_t checksum; // header checksum
|
||||
};
|
||||
uint8_t protocol() { return (meta >> 4) & 0xF; }
|
||||
uint8_t type() { return meta & 0xF; }
|
||||
void reset() { token = 0; sync = 0; meta = 0; size = 0; checksum = 0; }
|
||||
uint8_t data[2];
|
||||
};
|
||||
|
||||
union Footer {
|
||||
struct [[gnu::packed]] {
|
||||
uint16_t checksum; // full packet checksum
|
||||
};
|
||||
void reset() { checksum = 0; }
|
||||
uint8_t data[1];
|
||||
};
|
||||
|
||||
Header header;
|
||||
Footer footer;
|
||||
uint32_t bytes_received;
|
||||
uint16_t checksum, header_checksum;
|
||||
millis_t timeout;
|
||||
char* buffer;
|
||||
|
||||
void reset() {
|
||||
header.reset();
|
||||
footer.reset();
|
||||
bytes_received = 0;
|
||||
checksum = 0;
|
||||
header_checksum = 0;
|
||||
timeout = millis() + PACKET_MAX_WAIT;
|
||||
buffer = nullptr;
|
||||
}
|
||||
} packet{};
|
||||
|
||||
void reset() {
|
||||
sync = 0;
|
||||
packet_retries = 0;
|
||||
buffer_next_index = 0;
|
||||
}
|
||||
|
||||
// fletchers 16 checksum
|
||||
uint32_t checksum(uint32_t cs, uint8_t value) {
|
||||
uint16_t cs_low = (((cs & 0xFF) + value) % 255);
|
||||
return ((((cs >> 8) + cs_low) % 255) << 8) | cs_low;
|
||||
}
|
||||
|
||||
// read the next byte from the data stream keeping track of
|
||||
// whether the stream times out from data starvation
|
||||
// takes the data variable by reference in order to return status
|
||||
bool stream_read(uint8_t& data) {
|
||||
if (stream_state != StreamState::PACKET_WAIT && ELAPSED(millis(), packet.timeout)) {
|
||||
stream_state = StreamState::PACKET_TIMEOUT;
|
||||
return false;
|
||||
}
|
||||
if (!bs_serial_data_available(card.transfer_port_index)) return false;
|
||||
data = bs_read_serial(card.transfer_port_index);
|
||||
packet.timeout = millis() + PACKET_MAX_WAIT;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<const size_t buffer_size>
|
||||
void receive(char (&buffer)[buffer_size]) {
|
||||
uint8_t data = 0;
|
||||
millis_t transfer_window = millis() + RX_TIMESLICE;
|
||||
|
||||
#if HAS_MEDIA
|
||||
PORT_REDIRECT(SERIAL_PORTMASK(card.transfer_port_index));
|
||||
#endif
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Warray-bounds"
|
||||
|
||||
while (PENDING(millis(), transfer_window)) {
|
||||
switch (stream_state) {
|
||||
/**
|
||||
* Data stream packet handling
|
||||
*/
|
||||
case StreamState::PACKET_RESET:
|
||||
packet.reset();
|
||||
stream_state = StreamState::PACKET_WAIT;
|
||||
case StreamState::PACKET_WAIT:
|
||||
if (!stream_read(data)) { idle(); return; } // no active packet so don't wait
|
||||
packet.header.data[1] = data;
|
||||
if (packet.header.token == packet.header.HEADER_TOKEN) {
|
||||
packet.bytes_received = 2;
|
||||
stream_state = StreamState::PACKET_HEADER;
|
||||
}
|
||||
else {
|
||||
// stream corruption drop data
|
||||
packet.header.data[0] = data;
|
||||
}
|
||||
break;
|
||||
case StreamState::PACKET_HEADER:
|
||||
if (!stream_read(data)) break;
|
||||
|
||||
packet.header.data[packet.bytes_received++] = data;
|
||||
packet.checksum = checksum(packet.checksum, data);
|
||||
|
||||
// header checksum calculation can't contain the checksum
|
||||
if (packet.bytes_received == sizeof(Packet::header) - 2)
|
||||
packet.header_checksum = packet.checksum;
|
||||
|
||||
if (packet.bytes_received == sizeof(Packet::header)) {
|
||||
if (packet.header.checksum == packet.header_checksum) {
|
||||
// The SYNC control packet is a special case in that it doesn't require the stream sync to be correct
|
||||
if (static_cast<Protocol>(packet.header.protocol()) == Protocol::CONTROL && static_cast<ProtocolControl>(packet.header.type()) == ProtocolControl::SYNC) {
|
||||
SERIAL_ECHOLNPGM("ss", sync, ",", buffer_size, ",", VERSION_MAJOR, ".", VERSION_MINOR, ".", VERSION_PATCH);
|
||||
stream_state = StreamState::PACKET_RESET;
|
||||
break;
|
||||
}
|
||||
if (packet.header.sync == sync) {
|
||||
buffer_next_index = 0;
|
||||
packet.bytes_received = 0;
|
||||
if (packet.header.size) {
|
||||
stream_state = StreamState::PACKET_DATA;
|
||||
packet.buffer = static_cast<char *>(&buffer[0]); // multipacket buffering not implemented, always allocate whole buffer to packet
|
||||
}
|
||||
else
|
||||
stream_state = StreamState::PACKET_PROCESS;
|
||||
}
|
||||
else if (packet.header.sync == sync - 1) { // ok response must have been lost
|
||||
SERIAL_ECHOLNPGM("ok", packet.header.sync); // transmit valid packet received and drop the payload
|
||||
stream_state = StreamState::PACKET_RESET;
|
||||
}
|
||||
else if (packet_retries) {
|
||||
stream_state = StreamState::PACKET_RESET; // could be packets already buffered on flow controlled connections, drop them without ack
|
||||
}
|
||||
else {
|
||||
SERIAL_ECHO_MSG("Datastream packet out of order");
|
||||
stream_state = StreamState::PACKET_RESEND;
|
||||
}
|
||||
}
|
||||
else {
|
||||
SERIAL_ECHO_MSG("Packet header(", packet.header.sync, "?) corrupt");
|
||||
stream_state = StreamState::PACKET_RESEND;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case StreamState::PACKET_DATA:
|
||||
if (!stream_read(data)) break;
|
||||
|
||||
if (buffer_next_index < buffer_size)
|
||||
packet.buffer[buffer_next_index] = data;
|
||||
else {
|
||||
SERIAL_ECHO_MSG("Datastream packet data buffer overrun");
|
||||
stream_state = StreamState::PACKET_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
packet.checksum = checksum(packet.checksum, data);
|
||||
packet.bytes_received++;
|
||||
buffer_next_index++;
|
||||
|
||||
if (packet.bytes_received == packet.header.size) {
|
||||
stream_state = StreamState::PACKET_FOOTER;
|
||||
packet.bytes_received = 0;
|
||||
}
|
||||
break;
|
||||
case StreamState::PACKET_FOOTER:
|
||||
if (!stream_read(data)) break;
|
||||
|
||||
packet.footer.data[packet.bytes_received++] = data;
|
||||
if (packet.bytes_received == sizeof(Packet::footer)) {
|
||||
if (packet.footer.checksum == packet.checksum) {
|
||||
stream_state = StreamState::PACKET_PROCESS;
|
||||
}
|
||||
else {
|
||||
SERIAL_ECHO_MSG("Packet(", packet.header.sync, ") payload corrupt");
|
||||
stream_state = StreamState::PACKET_RESEND;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case StreamState::PACKET_PROCESS:
|
||||
sync++;
|
||||
packet_retries = 0;
|
||||
bytes_received += packet.header.size;
|
||||
|
||||
SERIAL_ECHOLNPGM("ok", packet.header.sync); // transmit valid packet received
|
||||
dispatch();
|
||||
stream_state = StreamState::PACKET_RESET;
|
||||
break;
|
||||
case StreamState::PACKET_RESEND:
|
||||
if (packet_retries < MAX_RETRIES || MAX_RETRIES == 0) {
|
||||
packet_retries++;
|
||||
stream_state = StreamState::PACKET_RESET;
|
||||
SERIAL_ECHO_MSG("Resend request ", packet_retries);
|
||||
SERIAL_ECHOLNPGM("rs", sync);
|
||||
}
|
||||
else
|
||||
stream_state = StreamState::PACKET_ERROR;
|
||||
break;
|
||||
case StreamState::PACKET_TIMEOUT:
|
||||
SERIAL_ECHO_MSG("Datastream timeout");
|
||||
stream_state = StreamState::PACKET_RESEND;
|
||||
break;
|
||||
case StreamState::PACKET_ERROR:
|
||||
SERIAL_ECHOLNPGM("fe", packet.header.sync);
|
||||
reset(); // reset everything, resync required
|
||||
stream_state = StreamState::PACKET_RESET;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
|
||||
void dispatch() {
|
||||
switch (static_cast<Protocol>(packet.header.protocol())) {
|
||||
case Protocol::CONTROL:
|
||||
switch (static_cast<ProtocolControl>(packet.header.type())) {
|
||||
case ProtocolControl::CLOSE: // revert back to ASCII mode
|
||||
card.flag.binary_mode = false;
|
||||
break;
|
||||
default:
|
||||
SERIAL_ECHO_MSG("Unknown BinaryProtocolControl Packet");
|
||||
}
|
||||
break;
|
||||
case Protocol::FILE_TRANSFER:
|
||||
SDFileTransferProtocol::process(packet.header.type(), packet.buffer, packet.header.size); // send user data to be processed
|
||||
break;
|
||||
default:
|
||||
SERIAL_ECHO_MSG("Unsupported Binary Protocol");
|
||||
}
|
||||
}
|
||||
|
||||
void idle() {
|
||||
// Some Protocols may need periodic updates without new data
|
||||
SDFileTransferProtocol::idle();
|
||||
}
|
||||
|
||||
static const uint16_t PACKET_MAX_WAIT = 500, RX_TIMESLICE = 20, MAX_RETRIES = 0, VERSION_MAJOR = 0, VERSION_MINOR = 1, VERSION_PATCH = 0;
|
||||
uint8_t packet_retries, sync;
|
||||
uint16_t buffer_next_index;
|
||||
uint32_t bytes_received;
|
||||
StreamState stream_state = StreamState::PACKET_RESET;
|
||||
};
|
||||
|
||||
extern BinaryStream binaryStream[NUM_SERIAL];
|
||||
199
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/bltouch.cpp
Normal file
199
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/bltouch.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(BLTOUCH)
|
||||
|
||||
#include "bltouch.h"
|
||||
|
||||
BLTouch bltouch;
|
||||
|
||||
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"
|
||||
|
||||
#define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
#include "../core/debug_out.h"
|
||||
|
||||
bool BLTouch::command(const BLTCommand cmd, const millis_t &ms) {
|
||||
const BLTCommand current = servo[Z_PROBE_SERVO_NR].read();
|
||||
if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPGM("BLTouch from ", current, " to ", cmd);
|
||||
// If the new command is the same, skip it (and the delay).
|
||||
// The previous write should've already delayed to detect the alarm.
|
||||
if (cmd != current) {
|
||||
servo[Z_PROBE_SERVO_NR].move(cmd);
|
||||
safe_delay(_MAX(ms, (uint32_t)BLTOUCH_DELAY)); // BLTOUCH_DELAY is also the *minimum* delay
|
||||
}
|
||||
return triggered();
|
||||
}
|
||||
|
||||
// Init the class and device. Call from setup().
|
||||
void BLTouch::init(const bool set_voltage/*=false*/) {
|
||||
// Voltage Setting (if enabled). At every Marlin initialization:
|
||||
// BLTOUCH < V3.0 and clones: This will be ignored by the probe
|
||||
// BLTOUCH V3.0: SET_5V_MODE or SET_OD_MODE (if enabled).
|
||||
// OD_MODE is the default on power on, but setting it does not hurt
|
||||
// This mode will stay active until manual SET_OD_MODE or power cycle
|
||||
// BLTOUCH V3.1: SET_5V_MODE or SET_OD_MODE (if enabled).
|
||||
// At power on, the probe will default to the eeprom settings configured by the user
|
||||
_reset();
|
||||
_stow();
|
||||
|
||||
#if ENABLED(BLTOUCH_FORCE_MODE_SET)
|
||||
|
||||
constexpr bool should_set = true;
|
||||
|
||||
#else
|
||||
|
||||
#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 = od_5v_mode != ENABLED(BLTOUCH_SET_5V_MODE);
|
||||
|
||||
#endif
|
||||
|
||||
if (should_set && set_voltage)
|
||||
mode_conv_proc(ENABLED(BLTOUCH_SET_5V_MODE));
|
||||
}
|
||||
|
||||
void BLTouch::clear() {
|
||||
_reset(); // RESET or RESET_SW will clear an alarm condition but...
|
||||
// ...it will not clear a triggered condition in SW mode when the pin is currently up
|
||||
// ANTClabs <-- CODE ERROR
|
||||
_stow(); // STOW will pull up the pin and clear any triggered condition unless it fails, don't care
|
||||
_deploy(); // DEPLOY to test the probe. Could fail, don't care
|
||||
_stow(); // STOW to be ready for meaningful work. Could fail, don't care
|
||||
}
|
||||
|
||||
bool BLTouch::triggered() { return PROBE_TRIGGERED(); }
|
||||
|
||||
bool BLTouch::deploy_proc() {
|
||||
// Do a DEPLOY
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("BLTouch DEPLOY requested");
|
||||
|
||||
// Attempt to DEPLOY, wait for DEPLOY_DELAY or ALARM
|
||||
if (_deploy_query_alarm()) {
|
||||
// The deploy might have failed or the probe is already triggered (nozzle too low?)
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("BLTouch ALARM or TRIGGER after DEPLOY, recovering");
|
||||
|
||||
clear(); // Get the probe into start condition
|
||||
|
||||
// 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 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
|
||||
}
|
||||
}
|
||||
|
||||
// One of the recommended ANTClabs ways to probe, using SW MODE
|
||||
TERN_(BLTOUCH_FORCE_SW_MODE, _set_SW_mode());
|
||||
|
||||
// Now the probe is ready to issue a 10ms pulse when the pin goes up.
|
||||
// The trigger STOW (see motion.cpp for example) will pull up the probes pin as soon as the pulse
|
||||
// is registered.
|
||||
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("bltouch.deploy_proc() end");
|
||||
|
||||
return false; // report success to caller
|
||||
}
|
||||
|
||||
bool BLTouch::stow_proc() {
|
||||
// Do a STOW
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("BLTouch STOW requested");
|
||||
|
||||
// A STOW will clear a triggered condition in the probe (10ms pulse).
|
||||
// At the moment that we come in here, we might (pulse) or will (SW mode) see the trigger on the pin.
|
||||
// So even though we know a STOW will be ignored if an ALARM condition is active, we will STOW.
|
||||
// Note: If the probe is deployed AND in an ALARM condition, this STOW will not pull up the pin
|
||||
// and the ALARM condition will still be there. --> ANTClabs should change this behavior maybe
|
||||
|
||||
// Attempt to STOW, wait for STOW_DELAY or ALARM
|
||||
if (_stow_query_alarm()) {
|
||||
// The stow might have failed
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("BLTouch ALARM or TRIGGER after STOW, recovering");
|
||||
|
||||
_reset(); // This RESET will then also pull up the pin. If it doesn't
|
||||
// work and the pin is still down, there will no longer be
|
||||
// an ALARM condition though.
|
||||
// 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 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
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("bltouch.stow_proc() end");
|
||||
|
||||
return false; // report success to caller
|
||||
}
|
||||
|
||||
bool BLTouch::status_proc() {
|
||||
/**
|
||||
* Return a TRUE for "YES, it is DEPLOYED"
|
||||
* This function will ensure switch state is reset after execution
|
||||
*/
|
||||
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("BLTouch STATUS requested");
|
||||
|
||||
_set_SW_mode(); // Incidentally, _set_SW_mode() will also RESET any active alarm
|
||||
const bool tr = triggered(); // If triggered in SW mode, the pin is up, it is STOWED
|
||||
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("BLTouch is ", tr);
|
||||
|
||||
if (tr) _stow(); else _deploy(); // Turn off SW mode, reset any trigger, honor pin state
|
||||
return !tr;
|
||||
}
|
||||
|
||||
void BLTouch::mode_conv_proc(const bool M5V) {
|
||||
/**
|
||||
* BLTOUCH pre V3.0 and clones: No reaction at all to this sequence apart from a DEPLOY -> STOW
|
||||
* BLTOUCH V3.0: This will set the mode (twice) and sadly, a STOW is needed at the end, because of the deploy
|
||||
* BLTOUCH V3.1: This will set the mode and store it in the eeprom. The STOW is not needed but does not hurt
|
||||
*/
|
||||
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("BLTouch Set Mode - ", M5V);
|
||||
_deploy();
|
||||
if (M5V) _set_5V_mode(); else _set_OD_mode();
|
||||
_mode_store();
|
||||
if (M5V) _set_5V_mode(); else _set_OD_mode();
|
||||
_stow();
|
||||
od_5v_mode = M5V;
|
||||
}
|
||||
|
||||
#endif // BLTOUCH
|
||||
118
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/bltouch.h
Normal file
118
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/bltouch.h
Normal file
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* 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"
|
||||
|
||||
// BLTouch commands are sent as servo angles
|
||||
typedef unsigned char BLTCommand;
|
||||
|
||||
#define STOW_ALARM true
|
||||
#define BLTOUCH_DEPLOY 10
|
||||
#define BLTOUCH_STOW 90
|
||||
#define BLTOUCH_SW_MODE 60
|
||||
#define BLTOUCH_SELFTEST 120
|
||||
#define BLTOUCH_MODE_STORE 130
|
||||
#define BLTOUCH_5V_MODE 140
|
||||
#define BLTOUCH_OD_MODE 150
|
||||
#define BLTOUCH_RESET 160
|
||||
|
||||
/**
|
||||
* The following commands require different minimum delays.
|
||||
*
|
||||
* 500ms required for a reliable Reset.
|
||||
*
|
||||
* 750ms required for Deploy/Stow, otherwise the alarm state
|
||||
* will not be seen until the following move command.
|
||||
*/
|
||||
|
||||
#ifndef BLTOUCH_SET5V_DELAY
|
||||
#define BLTOUCH_SET5V_DELAY 150
|
||||
#endif
|
||||
#ifndef BLTOUCH_SETOD_DELAY
|
||||
#define BLTOUCH_SETOD_DELAY 150
|
||||
#endif
|
||||
#ifndef BLTOUCH_MODE_STORE_DELAY
|
||||
#define BLTOUCH_MODE_STORE_DELAY 150
|
||||
#endif
|
||||
#ifndef BLTOUCH_DEPLOY_DELAY
|
||||
#define BLTOUCH_DEPLOY_DELAY 750
|
||||
#endif
|
||||
#ifndef BLTOUCH_STOW_DELAY
|
||||
#define BLTOUCH_STOW_DELAY 750
|
||||
#endif
|
||||
#ifndef BLTOUCH_RESET_DELAY
|
||||
#define BLTOUCH_RESET_DELAY 500
|
||||
#endif
|
||||
|
||||
class BLTouch {
|
||||
public:
|
||||
|
||||
static void init(const bool set_voltage=false);
|
||||
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(); }
|
||||
static bool stow() { return stow_proc(); }
|
||||
static bool status() { return status_proc(); }
|
||||
|
||||
// Native BLTouch commands ("Underscore"...), used in lcd menus and internally
|
||||
static void _reset() { command(BLTOUCH_RESET, BLTOUCH_RESET_DELAY); }
|
||||
|
||||
static void _selftest() { command(BLTOUCH_SELFTEST, BLTOUCH_DELAY); }
|
||||
|
||||
static void _set_SW_mode() { command(BLTOUCH_SW_MODE, BLTOUCH_DELAY); }
|
||||
static void _reset_SW_mode() { if (triggered()) _stow(); else _deploy(); }
|
||||
|
||||
static void _set_5V_mode() { command(BLTOUCH_5V_MODE, BLTOUCH_SET5V_DELAY); }
|
||||
static void _set_OD_mode() { command(BLTOUCH_OD_MODE, BLTOUCH_SETOD_DELAY); }
|
||||
static void _mode_store() { command(BLTOUCH_MODE_STORE, BLTOUCH_MODE_STORE_DELAY); }
|
||||
|
||||
static void _deploy() { command(BLTOUCH_DEPLOY, BLTOUCH_DEPLOY_DELAY); }
|
||||
static void _stow() { command(BLTOUCH_STOW, BLTOUCH_STOW_DELAY); }
|
||||
|
||||
static void mode_conv_5V() { mode_conv_proc(true); }
|
||||
static void mode_conv_OD() { mode_conv_proc(false); }
|
||||
|
||||
static bool triggered();
|
||||
|
||||
private:
|
||||
static bool _deploy_query_alarm() { return command(BLTOUCH_DEPLOY, BLTOUCH_DEPLOY_DELAY); }
|
||||
static bool _stow_query_alarm() { return command(BLTOUCH_STOW, BLTOUCH_STOW_DELAY) == STOW_ALARM; }
|
||||
|
||||
static void clear();
|
||||
static bool command(const BLTCommand cmd, const millis_t &ms);
|
||||
static bool deploy_proc();
|
||||
static bool stow_proc();
|
||||
static bool status_proc();
|
||||
static void mode_conv_proc(const bool M5V);
|
||||
};
|
||||
|
||||
extern BLTouch bltouch;
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(CANCEL_OBJECTS)
|
||||
|
||||
#include "cancel_object.h"
|
||||
#include "../gcode/gcode.h"
|
||||
#include "../lcd/marlinui.h"
|
||||
|
||||
CancelObject cancelable;
|
||||
|
||||
int8_t CancelObject::object_count, // = 0
|
||||
CancelObject::active_object = -1;
|
||||
uint32_t CancelObject::canceled; // = 0x0000
|
||||
bool CancelObject::skipping; // = false
|
||||
|
||||
void CancelObject::set_active_object(const int8_t obj) {
|
||||
active_object = obj;
|
||||
if (WITHIN(obj, 0, 31)) {
|
||||
if (obj >= object_count) object_count = obj + 1;
|
||||
skipping = TEST(canceled, obj);
|
||||
}
|
||||
else
|
||||
skipping = false;
|
||||
|
||||
#if ALL(HAS_STATUS_MESSAGE, CANCEL_OBJECTS_REPORTING)
|
||||
if (active_object >= 0)
|
||||
ui.status_printf(0, F(S_FMT " %i"), GET_TEXT(MSG_PRINTING_OBJECT), int(active_object));
|
||||
else
|
||||
ui.reset_status();
|
||||
#endif
|
||||
}
|
||||
|
||||
void CancelObject::cancel_object(const int8_t obj) {
|
||||
if (WITHIN(obj, 0, 31)) {
|
||||
SBI(canceled, obj);
|
||||
if (obj == active_object) skipping = true;
|
||||
}
|
||||
}
|
||||
|
||||
void CancelObject::uncancel_object(const int8_t obj) {
|
||||
if (WITHIN(obj, 0, 31)) {
|
||||
CBI(canceled, obj);
|
||||
if (obj == active_object) skipping = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CancelObject::report() {
|
||||
if (active_object >= 0)
|
||||
SERIAL_ECHO_MSG("Active Object: ", active_object);
|
||||
|
||||
if (canceled) {
|
||||
SERIAL_ECHO_START();
|
||||
SERIAL_ECHOPGM("Canceled:");
|
||||
for (int i = 0; i < object_count; i++)
|
||||
if (TEST(canceled, i)) { SERIAL_CHAR(' '); SERIAL_ECHO(i); }
|
||||
SERIAL_EOL();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CANCEL_OBJECTS
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 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 <stdint.h>
|
||||
|
||||
class CancelObject {
|
||||
public:
|
||||
static bool skipping;
|
||||
static int8_t object_count, active_object;
|
||||
static uint32_t canceled;
|
||||
static void set_active_object(const int8_t obj);
|
||||
static void cancel_object(const int8_t obj);
|
||||
static void uncancel_object(const int8_t obj);
|
||||
static void report();
|
||||
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;
|
||||
103
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/caselight.cpp
Normal file
103
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/caselight.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(CASE_LIGHT_ENABLE)
|
||||
|
||||
#include "caselight.h"
|
||||
|
||||
CaseLight caselight;
|
||||
|
||||
#if CASELIGHT_USES_BRIGHTNESS && !defined(CASE_LIGHT_DEFAULT_BRIGHTNESS)
|
||||
#define CASE_LIGHT_DEFAULT_BRIGHTNESS 0 // For use on PWM pin as non-PWM just sets a default
|
||||
#endif
|
||||
|
||||
#if CASELIGHT_USES_BRIGHTNESS
|
||||
uint8_t CaseLight::brightness = CASE_LIGHT_DEFAULT_BRIGHTNESS;
|
||||
#endif
|
||||
|
||||
bool CaseLight::on = CASE_LIGHT_DEFAULT_ON;
|
||||
|
||||
#if CASE_LIGHT_IS_COLOR_LED
|
||||
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
|
||||
|
||||
void CaseLight::update(const bool sflag) {
|
||||
#if CASELIGHT_USES_BRIGHTNESS
|
||||
/**
|
||||
* The brightness_sav (and sflag) is needed because ARM chips ignore
|
||||
* a "WRITE(CASE_LIGHT_PIN,x)" command to the pins that are directly
|
||||
* controlled by the PWM module. In order to turn them off the brightness
|
||||
* level needs to be set to OFF. Since we can't use the PWM register to
|
||||
* save the last brightness level we need a variable to save it.
|
||||
*/
|
||||
static uint8_t brightness_sav; // Save brightness info for restore on "M355 S1"
|
||||
|
||||
if (on || !sflag)
|
||||
brightness_sav = brightness; // Save brightness except for M355 S0
|
||||
if (sflag && on)
|
||||
brightness = brightness_sav; // Restore last brightness for M355 S1
|
||||
|
||||
const uint8_t i = on ? brightness : 0, n10ct = ENABLED(INVERT_CASE_LIGHT) ? 255 - i : i;
|
||||
UNUSED(n10ct);
|
||||
#endif
|
||||
|
||||
#if CASE_LIGHT_IS_COLOR_LED
|
||||
#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())
|
||||
hal.set_pwm_duty(pin_t(CASE_LIGHT_PIN), (
|
||||
#if CASE_LIGHT_MAX_PWM == 255
|
||||
n10ct
|
||||
#else
|
||||
map(n10ct, 0, 255, 0, CASE_LIGHT_MAX_PWM)
|
||||
#endif
|
||||
));
|
||||
else
|
||||
#endif
|
||||
{
|
||||
const bool s = on ? TERN(INVERT_CASE_LIGHT, LOW, HIGH) : TERN(INVERT_CASE_LIGHT, HIGH, LOW);
|
||||
WRITE(CASE_LIGHT_PIN, s ? HIGH : LOW);
|
||||
}
|
||||
|
||||
#endif // !CASE_LIGHT_IS_COLOR_LED
|
||||
|
||||
#if ENABLED(CASE_LIGHT_USE_RGB_LED)
|
||||
if (leds.lights_on) leds.update(); else leds.set_off();
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // CASE_LIGHT_ENABLE
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#if CASE_LIGHT_IS_COLOR_LED
|
||||
#include "leds/leds.h" // for LEDColor
|
||||
#endif
|
||||
|
||||
class CaseLight {
|
||||
public:
|
||||
static bool on;
|
||||
#if CASELIGHT_USES_BRIGHTNESS
|
||||
static uint8_t brightness;
|
||||
#endif
|
||||
|
||||
static bool pin_is_pwm() { return TERN0(NEED_CASE_LIGHT_PIN, PWM_PIN(CASE_LIGHT_PIN)); }
|
||||
static bool has_brightness() { return TERN0(CASELIGHT_USES_BRIGHTNESS, TERN(CASE_LIGHT_USE_NEOPIXEL, true, pin_is_pwm())); }
|
||||
|
||||
static void init() {
|
||||
#if NEED_CASE_LIGHT_PIN
|
||||
if (pin_is_pwm()) SET_PWM(CASE_LIGHT_PIN); else SET_OUTPUT(CASE_LIGHT_PIN);
|
||||
#endif
|
||||
update_brightness();
|
||||
}
|
||||
|
||||
static void update(const bool sflag);
|
||||
static void update_brightness() { update(false); }
|
||||
static void update_enabled() { update(true); }
|
||||
|
||||
#if ENABLED(CASE_LIGHT_IS_COLOR_LED)
|
||||
private:
|
||||
static LEDColor color;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern CaseLight caselight;
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(EXTERNAL_CLOSED_LOOP_CONTROLLER)
|
||||
|
||||
#if !PIN_EXISTS(CLOSED_LOOP_ENABLE) || !PIN_EXISTS(CLOSED_LOOP_MOVE_COMPLETE)
|
||||
#error "CLOSED_LOOP_ENABLE_PIN and CLOSED_LOOP_MOVE_COMPLETE_PIN are required for EXTERNAL_CLOSED_LOOP_CONTROLLER."
|
||||
#endif
|
||||
|
||||
#include "closedloop.h"
|
||||
|
||||
ClosedLoop closedloop;
|
||||
|
||||
void ClosedLoop::init() {
|
||||
OUT_WRITE(CLOSED_LOOP_ENABLE_PIN, LOW);
|
||||
SET_INPUT_PULLUP(CLOSED_LOOP_MOVE_COMPLETE_PIN);
|
||||
}
|
||||
|
||||
void ClosedLoop::set(const byte val) {
|
||||
OUT_WRITE(CLOSED_LOOP_ENABLE_PIN, val);
|
||||
}
|
||||
|
||||
#endif // EXTERNAL_CLOSED_LOOP_CONTROLLER
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
class ClosedLoop {
|
||||
public:
|
||||
static void init();
|
||||
static void set(const byte val);
|
||||
};
|
||||
|
||||
extern ClosedLoop closedloop;
|
||||
|
||||
#define CLOSED_LOOP_WAITING() (READ(CLOSED_LOOP_ENABLE_PIN) && !READ(CLOSED_LOOP_MOVE_COMPLETE_PIN))
|
||||
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(USE_CONTROLLER_FAN)
|
||||
|
||||
#include "controllerfan.h"
|
||||
#include "../module/stepper.h"
|
||||
#include "../module/temperature.h"
|
||||
|
||||
ControllerFan controllerFan;
|
||||
|
||||
uint8_t ControllerFan::speed;
|
||||
|
||||
#if ENABLED(CONTROLLER_FAN_EDITABLE)
|
||||
controllerFan_settings_t ControllerFan::settings; // {0}
|
||||
#else
|
||||
const controllerFan_settings_t &ControllerFan::settings = controllerFan_defaults;
|
||||
#endif
|
||||
|
||||
#if ENABLED(FAN_SOFT_PWM)
|
||||
uint8_t ControllerFan::soft_pwm_speed;
|
||||
#endif
|
||||
|
||||
void ControllerFan::setup() {
|
||||
SET_OUTPUT(CONTROLLER_FAN_PIN);
|
||||
#ifdef CONTROLLER_FAN2_PIN
|
||||
SET_OUTPUT(CONTROLLER_FAN2_PIN);
|
||||
#endif
|
||||
init();
|
||||
}
|
||||
|
||||
void ControllerFan::set_fan_speed(const uint8_t s) {
|
||||
speed = s < (CONTROLLERFAN_SPEED_MIN) ? 0 : s; // Fan OFF below minimum
|
||||
}
|
||||
|
||||
void ControllerFan::update() {
|
||||
static millis_t lastMotorOn = 0, // Last time a motor was turned on
|
||||
nextMotorCheck = 0; // Last time the state was checked
|
||||
const millis_t ms = millis();
|
||||
if (ELAPSED(ms, nextMotorCheck)) {
|
||||
nextMotorCheck = ms + 2500UL; // Not a time critical function, so only check every 2.5s
|
||||
|
||||
// If any triggers for the controller fan are true...
|
||||
// - 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), (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)
|
||||
) lastMotorOn = ms; //... set time to NOW so the fan will turn on
|
||||
|
||||
// Fan Settings. Set fan > 0:
|
||||
// - If AutoMode is on and steppers have been enabled for CONTROLLERFAN_IDLE_TIME seconds.
|
||||
// - If System is on idle and idle fan speed settings is activated.
|
||||
set_fan_speed(
|
||||
settings.auto_mode && lastMotorOn && PENDING(ms, lastMotorOn + SEC_TO_MS(settings.duration))
|
||||
? settings.active_speed : settings.idle_speed
|
||||
);
|
||||
|
||||
speed = CALC_FAN_SPEED(speed);
|
||||
|
||||
#if FAN_KICKSTART_TIME
|
||||
static millis_t fan_kick_end = 0;
|
||||
if (speed > FAN_OFF_PWM) {
|
||||
if (!fan_kick_end) {
|
||||
fan_kick_end = ms + FAN_KICKSTART_TIME; // May be longer based on slow update interval for controller fn check. Sets minimum
|
||||
speed = FAN_KICKSTART_POWER;
|
||||
}
|
||||
else if (PENDING(ms, fan_kick_end))
|
||||
speed = FAN_KICKSTART_POWER;
|
||||
}
|
||||
else
|
||||
fan_kick_end = 0;
|
||||
#endif
|
||||
|
||||
#if ENABLED(FAN_SOFT_PWM)
|
||||
soft_pwm_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);
|
||||
|
||||
#ifdef CONTROLLER_FAN2_PIN
|
||||
if (PWM_PIN(CONTROLLER_FAN2_PIN))
|
||||
hal.set_pwm_duty(pin_t(CONTROLLER_FAN2_PIN), speed);
|
||||
else
|
||||
WRITE(CONTROLLER_FAN2_PIN, speed > 0);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif // USE_CONTROLLER_FAN
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* 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"
|
||||
|
||||
typedef struct {
|
||||
uint8_t active_speed, // 0-255 (fullspeed); Speed with enabled stepper motors
|
||||
idle_speed; // 0-255 (fullspeed); Speed after idle period with all motors are disabled
|
||||
uint16_t duration; // Duration in seconds for the fan to run after all motors are disabled
|
||||
bool auto_mode; // Default true
|
||||
} controllerFan_settings_t;
|
||||
|
||||
#ifndef CONTROLLERFAN_SPEED_ACTIVE
|
||||
#define CONTROLLERFAN_SPEED_ACTIVE 255
|
||||
#endif
|
||||
#ifndef CONTROLLERFAN_SPEED_IDLE
|
||||
#define CONTROLLERFAN_SPEED_IDLE 0
|
||||
#endif
|
||||
#ifndef CONTROLLERFAN_IDLE_TIME
|
||||
#define CONTROLLERFAN_IDLE_TIME 60
|
||||
#endif
|
||||
|
||||
static constexpr controllerFan_settings_t controllerFan_defaults = {
|
||||
CONTROLLERFAN_SPEED_ACTIVE,
|
||||
CONTROLLERFAN_SPEED_IDLE,
|
||||
CONTROLLERFAN_IDLE_TIME,
|
||||
true
|
||||
};
|
||||
|
||||
#if ENABLED(USE_CONTROLLER_FAN)
|
||||
|
||||
class ControllerFan {
|
||||
private:
|
||||
static uint8_t speed;
|
||||
static void set_fan_speed(const uint8_t s);
|
||||
|
||||
public:
|
||||
#if ENABLED(CONTROLLER_FAN_EDITABLE)
|
||||
static controllerFan_settings_t settings;
|
||||
#else
|
||||
static const controllerFan_settings_t &settings;
|
||||
#endif
|
||||
#if ENABLED(FAN_SOFT_PWM)
|
||||
static uint8_t soft_pwm_speed;
|
||||
#endif
|
||||
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();
|
||||
};
|
||||
|
||||
extern ControllerFan controllerFan;
|
||||
|
||||
#endif
|
||||
48
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/cooler.cpp
Normal file
48
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/cooler.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* 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 ANY(HAS_COOLER, LASER_COOLANT_FLOW_METER)
|
||||
|
||||
#include "cooler.h"
|
||||
Cooler cooler;
|
||||
|
||||
#if HAS_COOLER
|
||||
uint8_t Cooler::mode = 0;
|
||||
uint16_t Cooler::capacity;
|
||||
uint16_t Cooler::load;
|
||||
bool Cooler::enabled = false;
|
||||
#endif
|
||||
|
||||
#if ENABLED(LASER_COOLANT_FLOW_METER)
|
||||
bool Cooler::flowmeter = false;
|
||||
millis_t Cooler::flowmeter_next_ms; // = 0
|
||||
volatile uint16_t Cooler::flowpulses;
|
||||
float Cooler::flowrate;
|
||||
#if ENABLED(FLOWMETER_SAFETY)
|
||||
bool Cooler::flowsafety_enabled = true;
|
||||
bool Cooler::flowfault = false;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif // HAS_COOLER || LASER_COOLANT_FLOW_METER
|
||||
109
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/cooler.h
Normal file
109
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/cooler.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* 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"
|
||||
|
||||
#ifndef FLOWMETER_PPL
|
||||
#define FLOWMETER_PPL 5880 // Pulses per liter
|
||||
#endif
|
||||
#ifndef FLOWMETER_INTERVAL
|
||||
#define FLOWMETER_INTERVAL 1000 // milliseconds
|
||||
#endif
|
||||
|
||||
// Cooling device
|
||||
|
||||
class Cooler {
|
||||
public:
|
||||
static uint16_t capacity; // Cooling capacity in watts
|
||||
static uint16_t load; // Cooling load in watts
|
||||
|
||||
static bool enabled;
|
||||
static void enable() { enabled = true; }
|
||||
static void disable() { enabled = false; }
|
||||
static void toggle() { enabled = !enabled; }
|
||||
|
||||
static uint8_t mode; // 0 = CO2 Liquid cooling, 1 = Laser Diode TEC Heatsink Cooling
|
||||
static void set_mode(const uint8_t m) { mode = m; }
|
||||
|
||||
#if ENABLED(LASER_COOLANT_FLOW_METER)
|
||||
static float flowrate; // Flow meter reading in liters-per-minute.
|
||||
static bool flowmeter; // Flag to monitor the flow
|
||||
static volatile uint16_t flowpulses; // Flowmeter IRQ pulse count
|
||||
static millis_t flowmeter_next_ms; // Next time at which to calculate flow
|
||||
|
||||
static void set_flowmeter(const bool sflag) {
|
||||
if (flowmeter != sflag) {
|
||||
flowmeter = sflag;
|
||||
if (sflag) {
|
||||
flowpulses = 0;
|
||||
flowmeter_next_ms = millis() + FLOWMETER_INTERVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// To calculate flow we only need to count pulses
|
||||
static void flowmeter_ISR() { flowpulses++; }
|
||||
|
||||
// Enable / Disable the flow meter interrupt
|
||||
static void flowmeter_interrupt_enable() {
|
||||
attachInterrupt(digitalPinToInterrupt(FLOWMETER_PIN), flowmeter_ISR, RISING);
|
||||
}
|
||||
static void flowmeter_interrupt_disable() {
|
||||
detachInterrupt(digitalPinToInterrupt(FLOWMETER_PIN));
|
||||
}
|
||||
|
||||
// Enable / Disable the flow meter interrupt
|
||||
static void flowmeter_enable() { set_flowmeter(true); flowpulses = 0; flowmeter_interrupt_enable(); }
|
||||
static void flowmeter_disable() { set_flowmeter(false); flowmeter_interrupt_disable(); flowpulses = 0; }
|
||||
|
||||
// Get the total flow (in liters per minute) since the last reading
|
||||
static void calc_flowrate() {
|
||||
// flowrate = (litres) * (seconds) = litres per minute
|
||||
flowrate = (flowpulses / (float)FLOWMETER_PPL) * ((1000.0f / (float)FLOWMETER_INTERVAL) * 60.0f);
|
||||
flowpulses = 0;
|
||||
}
|
||||
|
||||
// Userland task to update the flow meter
|
||||
static void flowmeter_task(const millis_t ms=millis()) {
|
||||
if (!flowmeter) // !! The flow meter must always be on !!
|
||||
flowmeter_enable(); // Init and prime
|
||||
if (ELAPSED(ms, flowmeter_next_ms)) {
|
||||
calc_flowrate();
|
||||
flowmeter_next_ms = ms + FLOWMETER_INTERVAL;
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLED(FLOWMETER_SAFETY)
|
||||
static bool flowfault; // Flag that the cooler is in a fault state
|
||||
static bool flowsafety_enabled; // Flag to disable the cutter if flow rate is too low
|
||||
static void flowsafety_toggle() { flowsafety_enabled = !flowsafety_enabled; }
|
||||
static bool check_flow_too_low() {
|
||||
const bool too_low = flowsafety_enabled && flowrate < (FLOWMETER_MIN_LITERS_PER_MINUTE);
|
||||
flowfault = too_low;
|
||||
return too_low;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
extern Cooler cooler;
|
||||
@@ -0,0 +1,97 @@
|
||||
/***************************************************************
|
||||
*
|
||||
* External DAC for Alligator Board
|
||||
*
|
||||
****************************************************************/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if MB(ALLIGATOR)
|
||||
|
||||
#include "dac_dac084s085.h"
|
||||
|
||||
#include "../../MarlinCore.h"
|
||||
#include "../../HAL/shared/Delay.h"
|
||||
|
||||
dac084s085::dac084s085() { }
|
||||
|
||||
void dac084s085::begin() {
|
||||
uint8_t externalDac_buf[] = { 0x20, 0x00 }; // all off
|
||||
|
||||
// All SPI chip-select HIGH
|
||||
SET_OUTPUT(DAC0_SYNC_PIN);
|
||||
#if HAS_MULTI_EXTRUDER
|
||||
SET_OUTPUT(DAC1_SYNC_PIN);
|
||||
#endif
|
||||
cshigh();
|
||||
spiBegin();
|
||||
|
||||
//init onboard DAC
|
||||
DELAY_US(2);
|
||||
WRITE(DAC0_SYNC_PIN, LOW);
|
||||
DELAY_US(2);
|
||||
WRITE(DAC0_SYNC_PIN, HIGH);
|
||||
DELAY_US(2);
|
||||
WRITE(DAC0_SYNC_PIN, LOW);
|
||||
|
||||
spiSend(SPI_CHAN_DAC, externalDac_buf, COUNT(externalDac_buf));
|
||||
WRITE(DAC0_SYNC_PIN, HIGH);
|
||||
|
||||
#if HAS_MULTI_EXTRUDER
|
||||
//init Piggy DAC
|
||||
DELAY_US(2);
|
||||
WRITE(DAC1_SYNC_PIN, LOW);
|
||||
DELAY_US(2);
|
||||
WRITE(DAC1_SYNC_PIN, HIGH);
|
||||
DELAY_US(2);
|
||||
WRITE(DAC1_SYNC_PIN, LOW);
|
||||
|
||||
spiSend(SPI_CHAN_DAC, externalDac_buf, COUNT(externalDac_buf));
|
||||
WRITE(DAC1_SYNC_PIN, HIGH);
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void dac084s085::setValue(const uint8_t channel, const uint8_t value) {
|
||||
if (channel >= 7) return; // max channel (X,Y,Z,E0,E1,E2,E3)
|
||||
|
||||
const uint8_t externalDac_buf[] = {
|
||||
0x10 | ((channel > 3 ? 7 : 3) - channel << 6) | (value >> 4),
|
||||
0x00 | (value << 4)
|
||||
};
|
||||
|
||||
// All SPI chip-select HIGH
|
||||
cshigh();
|
||||
|
||||
if (channel > 3) { // DAC Piggy E1,E2,E3
|
||||
WRITE(DAC1_SYNC_PIN, LOW);
|
||||
DELAY_US(2);
|
||||
WRITE(DAC1_SYNC_PIN, HIGH);
|
||||
DELAY_US(2);
|
||||
WRITE(DAC1_SYNC_PIN, LOW);
|
||||
}
|
||||
else { // DAC onboard X,Y,Z,E0
|
||||
WRITE(DAC0_SYNC_PIN, LOW);
|
||||
DELAY_US(2);
|
||||
WRITE(DAC0_SYNC_PIN, HIGH);
|
||||
DELAY_US(2);
|
||||
WRITE(DAC0_SYNC_PIN, LOW);
|
||||
}
|
||||
|
||||
DELAY_US(2);
|
||||
spiSend(SPI_CHAN_DAC, externalDac_buf, COUNT(externalDac_buf));
|
||||
}
|
||||
|
||||
void dac084s085::cshigh() {
|
||||
WRITE(DAC0_SYNC_PIN, HIGH);
|
||||
#if HAS_MULTI_EXTRUDER
|
||||
WRITE(DAC1_SYNC_PIN, HIGH);
|
||||
#endif
|
||||
WRITE(SPI_EEPROM1_CS_PIN, HIGH);
|
||||
WRITE(SPI_EEPROM2_CS_PIN, HIGH);
|
||||
WRITE(SPI_FLASH_CS_PIN, HIGH);
|
||||
WRITE(SD_SS_PIN, HIGH);
|
||||
}
|
||||
|
||||
#endif // MB(ALLIGATOR)
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
class dac084s085 {
|
||||
public:
|
||||
dac084s085();
|
||||
static void begin();
|
||||
static void setValue(const uint8_t channel, const uint8_t value);
|
||||
private:
|
||||
static void cshigh();
|
||||
};
|
||||
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* mcp4728.cpp - Arduino library for MicroChip MCP4728 I2C D/A converter
|
||||
*
|
||||
* For implementation details, please take a look at the datasheet:
|
||||
* https://ww1.microchip.com/downloads/en/DeviceDoc/22187a.pdf
|
||||
*
|
||||
* For discussion and feedback, please go to:
|
||||
* https://forum.arduino.cc/index.php/topic,51842.0.html
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if HAS_MOTOR_CURRENT_DAC
|
||||
|
||||
#include "dac_mcp4728.h"
|
||||
|
||||
MCP4728 mcp4728;
|
||||
|
||||
xyze_uint_t dac_values;
|
||||
|
||||
/**
|
||||
* Begin I2C, get current values (input register and eeprom) of mcp4728
|
||||
*/
|
||||
void MCP4728::init() {
|
||||
Wire.begin();
|
||||
Wire.requestFrom(I2C_ADDRESS(DAC_DEV_ADDRESS), uint8_t(24));
|
||||
while (Wire.available()) {
|
||||
char deviceID = Wire.read(),
|
||||
hiByte = Wire.read(),
|
||||
loByte = Wire.read();
|
||||
|
||||
if (!(deviceID & 0x08))
|
||||
dac_values[(deviceID & 0x30) >> 4] = word((hiByte & 0x0F), loByte);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write input resister value to specified channel using fastwrite method.
|
||||
* Channel : 0-3, Values : 0-4095
|
||||
*/
|
||||
uint8_t MCP4728::analogWrite(const uint8_t channel, const uint16_t value) {
|
||||
dac_values[channel] = value;
|
||||
return fastWrite();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write all input resistor values to EEPROM using SequentialWrite method.
|
||||
* This will update both input register and EEPROM value
|
||||
* This will also write current Vref, PowerDown, Gain settings to EEPROM
|
||||
*/
|
||||
uint8_t MCP4728::eepromWrite() {
|
||||
Wire.beginTransmission(I2C_ADDRESS(DAC_DEV_ADDRESS));
|
||||
Wire.write(SEQWRITE);
|
||||
LOOP_LOGICAL_AXES(i) {
|
||||
Wire.write(DAC_STEPPER_VREF << 7 | DAC_STEPPER_GAIN << 4 | highByte(dac_values[i]));
|
||||
Wire.write(lowByte(dac_values[i]));
|
||||
}
|
||||
return Wire.endTransmission();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write Voltage reference setting to all input registers
|
||||
*/
|
||||
uint8_t MCP4728::setVref_all(const uint8_t value) {
|
||||
Wire.beginTransmission(I2C_ADDRESS(DAC_DEV_ADDRESS));
|
||||
Wire.write(VREFWRITE | (value ? 0x0F : 0x00));
|
||||
return Wire.endTransmission();
|
||||
}
|
||||
/**
|
||||
* Write Gain setting to all input registers
|
||||
*/
|
||||
uint8_t MCP4728::setGain_all(const uint8_t value) {
|
||||
Wire.beginTransmission(I2C_ADDRESS(DAC_DEV_ADDRESS));
|
||||
Wire.write(GAINWRITE | (value ? 0x0F : 0x00));
|
||||
return Wire.endTransmission();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return Input Register value
|
||||
*/
|
||||
uint16_t MCP4728::getValue(const uint8_t channel) { return dac_values[channel]; }
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* Steph: Might be useful in the future
|
||||
* Return Vout
|
||||
*/
|
||||
uint16_t MCP4728::getVout(const uint8_t channel) {
|
||||
const uint32_t vref = 2048,
|
||||
vOut = (vref * dac_values[channel] * (_DAC_STEPPER_GAIN + 1)) / 4096;
|
||||
return _MIN(vOut, defaultVDD);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns DAC values as a 0-100 percentage of drive strength
|
||||
*/
|
||||
uint8_t MCP4728::getDrvPct(const uint8_t channel) { return uint8_t(100.0 * dac_values[channel] / (DAC_STEPPER_MAX) + 0.5); }
|
||||
|
||||
/**
|
||||
* Receives all Drive strengths as 0-100 percent values, updates
|
||||
* DAC Values array and calls fastwrite to update the DAC.
|
||||
*/
|
||||
void MCP4728::setDrvPct(xyze_uint_t &pct) {
|
||||
dac_values = pct * (DAC_STEPPER_MAX) * 0.01f;
|
||||
fastWrite();
|
||||
}
|
||||
|
||||
/**
|
||||
* FastWrite input register values - All DAC output update. refer to DATASHEET 5.6.1
|
||||
* DAC Input and PowerDown bits update.
|
||||
* No EEPROM update
|
||||
*/
|
||||
uint8_t MCP4728::fastWrite() {
|
||||
Wire.beginTransmission(I2C_ADDRESS(DAC_DEV_ADDRESS));
|
||||
LOOP_LOGICAL_AXES(i) {
|
||||
Wire.write(highByte(dac_values[i]));
|
||||
Wire.write(lowByte(dac_values[i]));
|
||||
}
|
||||
return Wire.endTransmission();
|
||||
}
|
||||
|
||||
/**
|
||||
* Common function for simple general commands
|
||||
*/
|
||||
uint8_t MCP4728::simpleCommand(const byte simpleCommand) {
|
||||
Wire.beginTransmission(I2C_ADDRESS(GENERALCALL));
|
||||
Wire.write(simpleCommand);
|
||||
return Wire.endTransmission();
|
||||
}
|
||||
|
||||
#endif // HAS_MOTOR_CURRENT_DAC
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
/**
|
||||
* Arduino library for MicroChip MCP4728 I2C D/A converter.
|
||||
*/
|
||||
|
||||
#include "../../core/types.h"
|
||||
|
||||
#include <Wire.h>
|
||||
|
||||
/**
|
||||
* The following three macros are only used in this piece of code related to mcp4728.
|
||||
* They are defined in the standard Arduino framework but could be undefined in 32 bits Arduino frameworks.
|
||||
* (For instance not defined in Arduino lpc176x framework)
|
||||
* So we have to define them if needed.
|
||||
*/
|
||||
#ifndef word
|
||||
#define word(h, l) ((uint8_t) ((h << 8) | l))
|
||||
#endif
|
||||
|
||||
#ifndef lowByte
|
||||
#define lowByte(w) ((uint8_t) ((w) & 0xFF))
|
||||
#endif
|
||||
|
||||
#ifndef highByte
|
||||
#define highByte(w) ((uint8_t) ((w) >> 8))
|
||||
#endif
|
||||
|
||||
#define defaultVDD DAC_STEPPER_MAX //was 5000 but differs with internal Vref
|
||||
#define BASE_ADDR 0x60
|
||||
#define RESET 0b00000110
|
||||
#define WAKE 0b00001001
|
||||
#define UPDATE 0b00001000
|
||||
#define MULTIWRITE 0b01000000
|
||||
#define SINGLEWRITE 0b01011000
|
||||
#define SEQWRITE 0b01010000
|
||||
#define VREFWRITE 0b10000000
|
||||
#define GAINWRITE 0b11000000
|
||||
#define POWERDOWNWRITE 0b10100000
|
||||
#define GENERALCALL 0b00000000
|
||||
#define GAINWRITE 0b11000000
|
||||
|
||||
// This is taken from the original lib, makes it easy to edit if needed
|
||||
// DAC_OR_ADDRESS defined in pins_BOARD.h file
|
||||
#define DAC_DEV_ADDRESS (BASE_ADDR | DAC_OR_ADDRESS)
|
||||
|
||||
class MCP4728 {
|
||||
public:
|
||||
static void init();
|
||||
static uint8_t analogWrite(const uint8_t channel, const uint16_t value);
|
||||
static uint8_t eepromWrite();
|
||||
static uint8_t setVref_all(const uint8_t value);
|
||||
static uint8_t setGain_all(const uint8_t value);
|
||||
static uint16_t getValue(const uint8_t channel);
|
||||
static uint8_t fastWrite();
|
||||
static uint8_t simpleCommand(const byte simpleCommand);
|
||||
static uint8_t getDrvPct(const uint8_t channel);
|
||||
static void setDrvPct(xyze_uint_t &pct);
|
||||
};
|
||||
|
||||
extern MCP4728 mcp4728;
|
||||
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* stepper_dac.cpp - To set stepper current via DAC
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if HAS_MOTOR_CURRENT_DAC
|
||||
|
||||
#include "stepper_dac.h"
|
||||
|
||||
bool dac_present = false;
|
||||
constexpr xyze_uint8_t dac_order = DAC_STEPPER_ORDER;
|
||||
xyze_uint_t dac_channel_pct = DAC_MOTOR_CURRENT_DEFAULT;
|
||||
|
||||
StepperDAC stepper_dac;
|
||||
|
||||
int StepperDAC::init() {
|
||||
#if PIN_EXISTS(DAC_DISABLE)
|
||||
OUT_WRITE(DAC_DISABLE_PIN, LOW); // set pin low to enable DAC
|
||||
#endif
|
||||
|
||||
mcp4728.init();
|
||||
|
||||
if (mcp4728.simpleCommand(RESET)) return -1;
|
||||
|
||||
dac_present = true;
|
||||
|
||||
mcp4728.setVref_all(DAC_STEPPER_VREF);
|
||||
mcp4728.setGain_all(DAC_STEPPER_GAIN);
|
||||
|
||||
if (mcp4728.getDrvPct(0) < 1 || mcp4728.getDrvPct(1) < 1 || mcp4728.getDrvPct(2) < 1 || mcp4728.getDrvPct(3) < 1) {
|
||||
mcp4728.setDrvPct(dac_channel_pct);
|
||||
mcp4728.eepromWrite();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void StepperDAC::set_current_value(const uint8_t channel, uint16_t val) {
|
||||
if (!(dac_present && channel < LOGICAL_AXES)) return;
|
||||
|
||||
NOMORE(val, uint16_t(DAC_STEPPER_MAX));
|
||||
|
||||
mcp4728.analogWrite(dac_order[channel], val);
|
||||
mcp4728.simpleCommand(UPDATE);
|
||||
}
|
||||
|
||||
void StepperDAC::set_current_percent(const uint8_t channel, float val) {
|
||||
set_current_value(channel, _MIN(val, 100.0f) * (DAC_STEPPER_MAX) / 100.0f);
|
||||
}
|
||||
|
||||
static float dac_perc(int8_t n) { return mcp4728.getDrvPct(dac_order[n]); }
|
||||
static float dac_amps(int8_t n) { return mcp4728.getValue(dac_order[n]) * 0.125 * RECIPROCAL(DAC_STEPPER_SENSE * 1000); }
|
||||
|
||||
uint8_t StepperDAC::get_current_percent(const AxisEnum axis) { return mcp4728.getDrvPct(dac_order[axis]); }
|
||||
void StepperDAC::set_current_percents(xyze_uint8_t &pct) {
|
||||
LOOP_LOGICAL_AXES(i) dac_channel_pct[i] = pct[dac_order[i]];
|
||||
mcp4728.setDrvPct(dac_channel_pct);
|
||||
}
|
||||
|
||||
void StepperDAC::print_values() {
|
||||
if (!dac_present) return;
|
||||
SERIAL_ECHO_MSG("Stepper current values in % (Amps):");
|
||||
SERIAL_ECHO_START();
|
||||
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
|
||||
}
|
||||
|
||||
void StepperDAC::commit_eeprom() {
|
||||
if (!dac_present) return;
|
||||
mcp4728.eepromWrite();
|
||||
}
|
||||
|
||||
#endif // HAS_MOTOR_CURRENT_DAC
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
/**
|
||||
* stepper_dac.h - To set stepper current via DAC
|
||||
*/
|
||||
|
||||
#include "dac_mcp4728.h"
|
||||
|
||||
class StepperDAC {
|
||||
public:
|
||||
static int init();
|
||||
static void set_current_percent(const uint8_t channel, float val);
|
||||
static void set_current_value(const uint8_t channel, uint16_t val);
|
||||
static void print_values();
|
||||
static void commit_eeprom();
|
||||
static uint8_t get_current_percent(const AxisEnum axis);
|
||||
static void set_current_percents(xyze_uint8_t &pct);
|
||||
};
|
||||
|
||||
extern StepperDAC stepper_dac;
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
//
|
||||
// Header for MCP4018 and MCP4451 current control i2c devices
|
||||
//
|
||||
class DigipotI2C {
|
||||
public:
|
||||
static void init();
|
||||
static void set_current(const uint8_t channel, const float current);
|
||||
};
|
||||
|
||||
extern DigipotI2C digipot_i2c;
|
||||
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(DIGIPOT_MCP4018)
|
||||
|
||||
#include "digipot.h"
|
||||
|
||||
#include <Stream.h>
|
||||
#include <SlowSoftI2CMaster.h> // https://github.com/felias-fogg/SlowSoftI2CMaster
|
||||
|
||||
// Settings for the I2C based DIGIPOT (MCP4018) based on WT150
|
||||
|
||||
#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))
|
||||
|
||||
#define DIGIPOT_A4988_FACTOR ((DIGIPOT_MCP4018_MAX_VALUE) / DIGIPOT_A4988_Itripmax(DIGIPOT_A4988_Vrefmax))
|
||||
#define DIGIPOT_A4988_MAX_CURRENT 2.0
|
||||
|
||||
static byte current_to_wiper(const float current) {
|
||||
const int16_t value = TERN(DIGIPOT_USE_RAW_VALUES, current, CEIL(current * DIGIPOT_A4988_FACTOR));
|
||||
return byte(constrain(value, 0, DIGIPOT_MCP4018_MAX_VALUE));
|
||||
}
|
||||
|
||||
static SlowSoftI2CMaster pots[DIGIPOT_I2C_NUM_CHANNELS] = {
|
||||
SlowSoftI2CMaster(DIGIPOTS_I2C_SDA_X, DIGIPOTS_I2C_SCL, ENABLED(DIGIPOT_ENABLE_I2C_PULLUPS))
|
||||
#if DIGIPOT_I2C_NUM_CHANNELS > 1
|
||||
, SlowSoftI2CMaster(DIGIPOTS_I2C_SDA_Y, DIGIPOTS_I2C_SCL, ENABLED(DIGIPOT_ENABLE_I2C_PULLUPS))
|
||||
#if DIGIPOT_I2C_NUM_CHANNELS > 2
|
||||
, SlowSoftI2CMaster(DIGIPOTS_I2C_SDA_Z, DIGIPOTS_I2C_SCL, ENABLED(DIGIPOT_ENABLE_I2C_PULLUPS))
|
||||
#if DIGIPOT_I2C_NUM_CHANNELS > 3
|
||||
, SlowSoftI2CMaster(DIGIPOTS_I2C_SDA_E0, DIGIPOTS_I2C_SCL, ENABLED(DIGIPOT_ENABLE_I2C_PULLUPS))
|
||||
#if DIGIPOT_I2C_NUM_CHANNELS > 4
|
||||
, SlowSoftI2CMaster(DIGIPOTS_I2C_SDA_E1, DIGIPOTS_I2C_SCL, ENABLED(DIGIPOT_ENABLE_I2C_PULLUPS))
|
||||
#if DIGIPOT_I2C_NUM_CHANNELS > 5
|
||||
, SlowSoftI2CMaster(DIGIPOTS_I2C_SDA_E2, DIGIPOTS_I2C_SCL, ENABLED(DIGIPOT_ENABLE_I2C_PULLUPS))
|
||||
#if DIGIPOT_I2C_NUM_CHANNELS > 6
|
||||
, SlowSoftI2CMaster(DIGIPOTS_I2C_SDA_E3, DIGIPOTS_I2C_SCL, ENABLED(DIGIPOT_ENABLE_I2C_PULLUPS))
|
||||
#if DIGIPOT_I2C_NUM_CHANNELS > 7
|
||||
, SlowSoftI2CMaster(DIGIPOTS_I2C_SDA_E4, DIGIPOTS_I2C_SCL, ENABLED(DIGIPOT_ENABLE_I2C_PULLUPS))
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
static void digipot_i2c_send(const uint8_t channel, const byte v) {
|
||||
if (WITHIN(channel, 0, DIGIPOT_I2C_NUM_CHANNELS - 1)) {
|
||||
pots[channel].i2c_start(((DIGIPOT_I2C_ADDRESS_A) << 1) | I2C_WRITE);
|
||||
pots[channel].i2c_write(v);
|
||||
pots[channel].i2c_stop();
|
||||
}
|
||||
}
|
||||
|
||||
// This is for the MCP4018 I2C based digipot
|
||||
void DigipotI2C::set_current(const uint8_t channel, const float current) {
|
||||
const float ival = _MIN(_MAX(current, 0), float(DIGIPOT_MCP4018_MAX_VALUE));
|
||||
digipot_i2c_send(channel, current_to_wiper(ival));
|
||||
}
|
||||
|
||||
void DigipotI2C::init() {
|
||||
for (uint8_t i = 0; i < DIGIPOT_I2C_NUM_CHANNELS; ++i) pots[i].i2c_init();
|
||||
|
||||
// Init currents according to Configuration_adv.h
|
||||
static const float digipot_motor_current[] PROGMEM =
|
||||
#if ENABLED(DIGIPOT_USE_RAW_VALUES)
|
||||
DIGIPOT_MOTOR_CURRENT
|
||||
#else
|
||||
DIGIPOT_I2C_MOTOR_CURRENTS
|
||||
#endif
|
||||
;
|
||||
for (uint8_t i = 0; i < COUNT(digipot_motor_current); ++i)
|
||||
set_current(i, pgm_read_float(&digipot_motor_current[i]));
|
||||
}
|
||||
|
||||
DigipotI2C digipot_i2c;
|
||||
|
||||
#endif // DIGIPOT_MCP4018
|
||||
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(DIGIPOT_MCP4451)
|
||||
|
||||
#include "digipot.h"
|
||||
|
||||
#include <Stream.h>
|
||||
#include <Wire.h>
|
||||
|
||||
#if MB(MKS_SBASE)
|
||||
#include "digipot_mcp4451_I2C_routines.h"
|
||||
#endif
|
||||
|
||||
// Settings for the I2C based DIGIPOT (MCP4451) on Azteeg X3 Pro
|
||||
#if MB(5DPRINT)
|
||||
#define DIGIPOT_I2C_FACTOR 117.96f
|
||||
#define DIGIPOT_I2C_MAX_CURRENT 1.736f
|
||||
#elif MB(AZTEEG_X5_MINI, AZTEEG_X5_MINI_WIFI)
|
||||
#define DIGIPOT_I2C_FACTOR 113.5f
|
||||
#define DIGIPOT_I2C_MAX_CURRENT 2.0f
|
||||
#elif MB(AZTEEG_X5_GT)
|
||||
#define DIGIPOT_I2C_FACTOR 51.0f
|
||||
#define DIGIPOT_I2C_MAX_CURRENT 3.0f
|
||||
#else
|
||||
#define DIGIPOT_I2C_FACTOR 106.7f
|
||||
#define DIGIPOT_I2C_MAX_CURRENT 2.5f
|
||||
#endif
|
||||
|
||||
static byte current_to_wiper(const float current) {
|
||||
return byte(TERN(DIGIPOT_USE_RAW_VALUES, current, CEIL(DIGIPOT_I2C_FACTOR * current)));
|
||||
}
|
||||
|
||||
static void digipot_i2c_send(const byte addr, const byte a, const byte b) {
|
||||
#if MB(MKS_SBASE)
|
||||
digipot_mcp4451_start(addr);
|
||||
digipot_mcp4451_send_byte(a);
|
||||
digipot_mcp4451_send_byte(b);
|
||||
#else
|
||||
Wire.beginTransmission(I2C_ADDRESS(addr));
|
||||
Wire.write(a);
|
||||
Wire.write(b);
|
||||
Wire.endTransmission();
|
||||
#endif
|
||||
}
|
||||
|
||||
// This is for the MCP4451 I2C based digipot
|
||||
void DigipotI2C::set_current(const uint8_t channel, const float current) {
|
||||
// These addresses are specific to Azteeg X3 Pro, can be set to others.
|
||||
// In this case first digipot is at address A0=0, A1=0, second one is at A0=0, A1=1
|
||||
const byte addr = channel < 4 ? DIGIPOT_I2C_ADDRESS_A : DIGIPOT_I2C_ADDRESS_B; // channel 0-3 vs 4-7
|
||||
|
||||
// Initial setup
|
||||
digipot_i2c_send(addr, 0x40, 0xFF);
|
||||
digipot_i2c_send(addr, 0xA0, 0xFF);
|
||||
|
||||
// Set actual wiper value
|
||||
byte addresses[4] = { 0x00, 0x10, 0x60, 0x70 };
|
||||
digipot_i2c_send(addr, addresses[channel & 0x3], current_to_wiper(_MIN(float(_MAX(current, 0)), DIGIPOT_I2C_MAX_CURRENT)));
|
||||
}
|
||||
|
||||
void DigipotI2C::init() {
|
||||
#if MB(MKS_SBASE)
|
||||
configure_i2c(16); // Set clock_option to 16 ensure I2C is initialized at 400kHz
|
||||
#else
|
||||
Wire.begin();
|
||||
#endif
|
||||
// Set up initial currents as defined in Configuration_adv.h
|
||||
static const float digipot_motor_current[] PROGMEM =
|
||||
#if ENABLED(DIGIPOT_USE_RAW_VALUES)
|
||||
DIGIPOT_MOTOR_CURRENT
|
||||
#else
|
||||
DIGIPOT_I2C_MOTOR_CURRENTS
|
||||
#endif
|
||||
;
|
||||
for (uint8_t i = 0; i < COUNT(digipot_motor_current); ++i)
|
||||
set_current(i, pgm_read_float(&digipot_motor_current[i]));
|
||||
}
|
||||
|
||||
DigipotI2C digipot_i2c;
|
||||
|
||||
#endif // DIGIPOT_MCP4451
|
||||
@@ -0,0 +1,264 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
|
||||
#if ENABLED(DIRECT_STEPPING)
|
||||
|
||||
#include "direct_stepping.h"
|
||||
|
||||
#include "../MarlinCore.h"
|
||||
|
||||
#define CHECK_PAGE(I, R) do{ \
|
||||
if (I >= sizeof(page_states) / sizeof(page_states[0])) { \
|
||||
fatal_error = true; \
|
||||
return R; \
|
||||
} \
|
||||
}while(0)
|
||||
|
||||
#define CHECK_PAGE_STATE(I, R, S) do { \
|
||||
CHECK_PAGE(I, R); \
|
||||
if (page_states[I] != S) { \
|
||||
fatal_error = true; \
|
||||
return R; \
|
||||
} \
|
||||
}while(0)
|
||||
|
||||
namespace DirectStepping {
|
||||
|
||||
template<typename Cfg>
|
||||
State SerialPageManager<Cfg>::state;
|
||||
|
||||
template<typename Cfg>
|
||||
volatile bool SerialPageManager<Cfg>::fatal_error;
|
||||
|
||||
template<typename Cfg>
|
||||
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::PAGE_COUNT][Cfg::PAGE_SIZE];
|
||||
|
||||
template<typename Cfg>
|
||||
uint8_t SerialPageManager<Cfg>::checksum;
|
||||
|
||||
template<typename Cfg>
|
||||
typename Cfg::write_byte_idx_t SerialPageManager<Cfg>::write_byte_idx;
|
||||
|
||||
template<typename Cfg>
|
||||
typename Cfg::page_idx_t SerialPageManager<Cfg>::write_page_idx;
|
||||
|
||||
template<typename Cfg>
|
||||
typename Cfg::write_byte_idx_t SerialPageManager<Cfg>::write_page_size;
|
||||
|
||||
template <typename Cfg>
|
||||
void SerialPageManager<Cfg>::init() {
|
||||
for (int i = 0 ; i < Cfg::PAGE_COUNT ; i++)
|
||||
page_states[i] = PageState::FREE;
|
||||
|
||||
fatal_error = false;
|
||||
state = State::NEWLINE;
|
||||
|
||||
page_states_dirty = false;
|
||||
|
||||
SERIAL_ECHOLNPGM("pages_ready");
|
||||
}
|
||||
|
||||
template<typename Cfg>
|
||||
FORCE_INLINE bool SerialPageManager<Cfg>::maybe_store_rxd_char(uint8_t c) {
|
||||
switch (state) {
|
||||
default:
|
||||
case State::MONITOR:
|
||||
switch (c) {
|
||||
case '\n':
|
||||
case '\r':
|
||||
state = State::NEWLINE;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
case State::NEWLINE:
|
||||
switch (c) {
|
||||
case Cfg::CONTROL_CHAR:
|
||||
state = State::ADDRESS;
|
||||
return true;
|
||||
case '\n':
|
||||
case '\r':
|
||||
state = State::NEWLINE;
|
||||
return false;
|
||||
default:
|
||||
state = State::MONITOR;
|
||||
return false;
|
||||
}
|
||||
case State::ADDRESS:
|
||||
//TODO: 16 bit address, State::ADDRESS2
|
||||
write_page_idx = c;
|
||||
write_byte_idx = 0;
|
||||
checksum = 0;
|
||||
|
||||
CHECK_PAGE(write_page_idx, true);
|
||||
|
||||
if (page_states[write_page_idx] == PageState::FAIL) {
|
||||
// Special case for fail
|
||||
state = State::UNFAIL;
|
||||
return true;
|
||||
}
|
||||
|
||||
set_page_state(write_page_idx, PageState::WRITING);
|
||||
|
||||
state = Cfg::DIRECTIONAL ? State::COLLECT : State::SIZE;
|
||||
|
||||
return true;
|
||||
case State::SIZE:
|
||||
// Zero means full page size
|
||||
write_page_size = c;
|
||||
state = State::COLLECT;
|
||||
return true;
|
||||
case State::COLLECT:
|
||||
pages[write_page_idx][write_byte_idx++] = c;
|
||||
checksum ^= c;
|
||||
|
||||
// check if still collecting
|
||||
if (Cfg::PAGE_SIZE == 256) {
|
||||
// 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;
|
||||
|
||||
state = State::CHECKSUM;
|
||||
return true;
|
||||
case State::CHECKSUM: {
|
||||
const PageState page_state = (checksum == c) ? PageState::OK : PageState::FAIL;
|
||||
set_page_state(write_page_idx, page_state);
|
||||
state = State::MONITOR;
|
||||
return true;
|
||||
}
|
||||
case State::UNFAIL:
|
||||
if (c == 0)
|
||||
set_page_state(write_page_idx, PageState::FREE);
|
||||
else
|
||||
fatal_error = true;
|
||||
state = State::MONITOR;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Cfg>
|
||||
void SerialPageManager<Cfg>::write_responses() {
|
||||
if (fatal_error) {
|
||||
kill(GET_TEXT_F(MSG_BAD_PAGE));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!page_states_dirty) return;
|
||||
page_states_dirty = false;
|
||||
|
||||
SERIAL_CHAR(Cfg::CONTROL_CHAR);
|
||||
constexpr int state_bits = 2;
|
||||
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::PAGE_COUNT ; i++) {
|
||||
bits_b[i >> state_bits] |= page_states[i] << ((i * state_bits) & 0x7);
|
||||
}
|
||||
|
||||
uint8_t crc = 0;
|
||||
for (uint8_t i = 0 ; i < n_bytes ; i++) {
|
||||
crc ^= bits_b[i];
|
||||
SERIAL_CHAR(bits_b[i]);
|
||||
}
|
||||
|
||||
SERIAL_CHAR(crc);
|
||||
SERIAL_EOL();
|
||||
}
|
||||
|
||||
template <typename Cfg>
|
||||
FORCE_INLINE void SerialPageManager<Cfg>::set_page_state(const page_idx_t page_idx, const PageState page_state) {
|
||||
CHECK_PAGE(page_idx,);
|
||||
|
||||
page_states[page_idx] = page_state;
|
||||
page_states_dirty = true;
|
||||
}
|
||||
|
||||
template <>
|
||||
FORCE_INLINE uint8_t *PageManager::get_page(const page_idx_t page_idx) {
|
||||
CHECK_PAGE(page_idx, nullptr);
|
||||
|
||||
return pages[page_idx];
|
||||
}
|
||||
|
||||
template <>
|
||||
FORCE_INLINE void PageManager::free_page(const page_idx_t page_idx) {
|
||||
set_page_state(page_idx, PageState::FREE);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
DirectStepping::PageManager page_manager;
|
||||
|
||||
const uint8_t segment_table[DirectStepping::Config::NUM_SEGMENTS][DirectStepping::Config::SEGMENT_STEPS] PROGMEM = {
|
||||
|
||||
#if STEPPER_PAGE_FORMAT == SP_4x4D_128
|
||||
|
||||
{ 1, 1, 1, 1, 1, 1, 1 }, // 0 = -7
|
||||
{ 1, 1, 1, 0, 1, 1, 1 }, // 1 = -6
|
||||
{ 1, 1, 1, 0, 1, 0, 1 }, // 2 = -5
|
||||
{ 1, 1, 0, 1, 0, 1, 0 }, // 3 = -4
|
||||
{ 1, 1, 0, 0, 1, 0, 0 }, // 4 = -3
|
||||
{ 0, 0, 1, 0, 0, 0, 1 }, // 5 = -2
|
||||
{ 0, 0, 0, 1, 0, 0, 0 }, // 6 = -1
|
||||
{ 0, 0, 0, 0, 0, 0, 0 }, // 7 = 0
|
||||
{ 0, 0, 0, 1, 0, 0, 0 }, // 8 = 1
|
||||
{ 0, 0, 1, 0, 0, 0, 1 }, // 9 = 2
|
||||
{ 1, 1, 0, 0, 1, 0, 0 }, // 10 = 3
|
||||
{ 1, 1, 0, 1, 0, 1, 0 }, // 11 = 4
|
||||
{ 1, 1, 1, 0, 1, 0, 1 }, // 12 = 5
|
||||
{ 1, 1, 1, 0, 1, 1, 1 }, // 13 = 6
|
||||
{ 1, 1, 1, 1, 1, 1, 1 }, // 14 = 7
|
||||
{ 0 }
|
||||
|
||||
#elif STEPPER_PAGE_FORMAT == SP_4x2_256
|
||||
|
||||
{ 0, 0, 0 }, // 0
|
||||
{ 0, 1, 0 }, // 1
|
||||
{ 1, 0, 1 }, // 2
|
||||
{ 1, 1, 1 }, // 3
|
||||
|
||||
#elif STEPPER_PAGE_FORMAT == SP_4x1_512
|
||||
|
||||
{0} // Uncompressed format, table not used
|
||||
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#endif // DIRECT_STEPPING
|
||||
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
namespace DirectStepping {
|
||||
|
||||
enum State : char {
|
||||
MONITOR, NEWLINE, ADDRESS, SIZE, COLLECT, CHECKSUM, UNFAIL
|
||||
};
|
||||
|
||||
enum PageState : uint8_t {
|
||||
FREE, WRITING, OK, FAIL
|
||||
};
|
||||
|
||||
// Static state used for stepping through direct stepping pages
|
||||
struct page_step_state_t {
|
||||
// Current page
|
||||
uint8_t *page;
|
||||
// Current segment
|
||||
uint16_t segment_idx;
|
||||
// Current steps within segment
|
||||
uint8_t segment_steps;
|
||||
// Segment delta
|
||||
xyze_uint8_t sd;
|
||||
// Block delta
|
||||
xyze_int_t bd;
|
||||
};
|
||||
|
||||
template<typename Cfg>
|
||||
class SerialPageManager {
|
||||
public:
|
||||
|
||||
typedef typename Cfg::page_idx_t page_idx_t;
|
||||
|
||||
static bool maybe_store_rxd_char(uint8_t c);
|
||||
static void write_responses();
|
||||
|
||||
// common methods for page managers
|
||||
static void init();
|
||||
static uint8_t *get_page(const page_idx_t page_idx);
|
||||
static void free_page(const page_idx_t page_idx);
|
||||
|
||||
protected:
|
||||
|
||||
typedef typename Cfg::write_byte_idx_t write_byte_idx_t;
|
||||
|
||||
static State state;
|
||||
static volatile bool fatal_error;
|
||||
|
||||
static volatile PageState page_states[Cfg::PAGE_COUNT];
|
||||
static volatile bool page_states_dirty;
|
||||
|
||||
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;
|
||||
static write_byte_idx_t write_page_size;
|
||||
|
||||
static void set_page_state(const page_idx_t page_idx, const PageState page_state);
|
||||
};
|
||||
|
||||
template <int num_pages, int num_axes, int bits_segment, bool dir, int segments>
|
||||
struct config_t {
|
||||
static constexpr char CONTROL_CHAR = '!';
|
||||
|
||||
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;
|
||||
|
||||
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 = (AXIS_COUNT * BITS_SEGMENT * SEGMENTS) / 8;
|
||||
|
||||
typedef uvalue_t(PAGE_SIZE - 1) write_byte_idx_t;
|
||||
typedef uvalue_t(PAGE_COUNT - 1) page_idx_t;
|
||||
};
|
||||
|
||||
template <uint8_t num_pages>
|
||||
using SP_4x4D_128 = config_t<num_pages, 4, 4, true, 128>;
|
||||
|
||||
template <uint8_t num_pages>
|
||||
using SP_4x2_256 = config_t<num_pages, 4, 2, false, 256>;
|
||||
|
||||
template <uint8_t num_pages>
|
||||
using SP_4x1_512 = config_t<num_pages, 4, 1, false, 512>;
|
||||
|
||||
// configured types
|
||||
typedef STEPPER_PAGE_FORMAT<STEPPER_PAGES> Config;
|
||||
|
||||
template class PAGE_MANAGER<Config>;
|
||||
typedef PAGE_MANAGER<Config> PageManager;
|
||||
};
|
||||
|
||||
#define SP_4x4D_128 1
|
||||
//#define SP_4x4_128 2
|
||||
//#define SP_4x2D_256 3
|
||||
#define SP_4x2_256 4
|
||||
#define SP_4x1_512 5
|
||||
|
||||
typedef typename DirectStepping::Config::page_idx_t page_idx_t;
|
||||
|
||||
// TODO: use config
|
||||
typedef DirectStepping::page_step_state_t page_step_state_t;
|
||||
|
||||
extern const uint8_t segment_table[DirectStepping::Config::NUM_SEGMENTS][DirectStepping::Config::SEGMENT_STEPS];
|
||||
extern DirectStepping::PageManager page_manager;
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* e_parser.cpp - Intercept special commands directly in the serial stream
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
|
||||
#if ENABLED(EMERGENCY_PARSER)
|
||||
|
||||
#include "e_parser.h"
|
||||
|
||||
// Static data members
|
||||
bool EmergencyParser::killed_by_M112, // = false
|
||||
EmergencyParser::quickstop_by_M410,
|
||||
#if HAS_MEDIA
|
||||
EmergencyParser::sd_abort_by_M524,
|
||||
#endif
|
||||
EmergencyParser::enabled;
|
||||
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
uint8_t EmergencyParser::M876_reason; // = 0
|
||||
#endif
|
||||
|
||||
// Global instance
|
||||
EmergencyParser emergency_parser;
|
||||
|
||||
#endif // EMERGENCY_PARSER
|
||||
243
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/e_parser.h
Normal file
243
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/e_parser.h
Normal file
@@ -0,0 +1,243 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
/**
|
||||
* e_parser.h - Intercept special commands directly in the serial stream
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
#include "host_actions.h"
|
||||
#endif
|
||||
|
||||
// External references
|
||||
extern bool wait_for_user, wait_for_heatup;
|
||||
|
||||
#if ENABLED(REALTIME_REPORTING_COMMANDS)
|
||||
// From motion.h, which cannot be included here
|
||||
void report_current_position_moving();
|
||||
void quickpause_stepper();
|
||||
void quickresume_stepper();
|
||||
#endif
|
||||
|
||||
#if ENABLED(SOFT_RESET_VIA_SERIAL)
|
||||
void HAL_reboot();
|
||||
#endif
|
||||
|
||||
class EmergencyParser {
|
||||
|
||||
public:
|
||||
|
||||
// Currently looking for: M108, M112, M410, M524, M876 S[0-9], S000, P000, R000
|
||||
enum State : uint8_t {
|
||||
EP_RESET,
|
||||
EP_N,
|
||||
EP_M,
|
||||
EP_M1,
|
||||
EP_M10, EP_M108,
|
||||
EP_M11, EP_M112,
|
||||
EP_M4, EP_M41, EP_M410,
|
||||
#if HAS_MEDIA
|
||||
EP_M5, EP_M52, EP_M524,
|
||||
#endif
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
EP_M8, EP_M87, EP_M876, EP_M876S, EP_M876SN,
|
||||
#endif
|
||||
#if ENABLED(REALTIME_REPORTING_COMMANDS)
|
||||
EP_S, EP_S0, EP_S00, EP_GRBL_STATUS,
|
||||
EP_R, EP_R0, EP_R00, EP_GRBL_RESUME,
|
||||
EP_P, EP_P0, EP_P00, EP_GRBL_PAUSE,
|
||||
#endif
|
||||
#if ENABLED(SOFT_RESET_VIA_SERIAL)
|
||||
EP_ctrl,
|
||||
EP_K, EP_KI, EP_KIL, EP_KILL,
|
||||
#endif
|
||||
EP_IGNORE // to '\n'
|
||||
};
|
||||
|
||||
static bool killed_by_M112;
|
||||
static bool quickstop_by_M410;
|
||||
|
||||
#if HAS_MEDIA
|
||||
static bool sd_abort_by_M524;
|
||||
#endif
|
||||
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
static uint8_t M876_reason;
|
||||
#endif
|
||||
|
||||
EmergencyParser() { enable(); }
|
||||
|
||||
FORCE_INLINE static void enable() { enabled = true; }
|
||||
FORCE_INLINE static void disable() { enabled = false; }
|
||||
|
||||
FORCE_INLINE static void update(State &state, const uint8_t c) {
|
||||
switch (state) {
|
||||
case EP_RESET:
|
||||
switch (c) {
|
||||
case ' ': case '\n': case '\r': break;
|
||||
case 'N': state = EP_N; break;
|
||||
case 'M': state = EP_M; break;
|
||||
#if ENABLED(REALTIME_REPORTING_COMMANDS)
|
||||
case 'S': state = EP_S; break;
|
||||
case 'P': state = EP_P; break;
|
||||
case 'R': state = EP_R; break;
|
||||
#endif
|
||||
#if ENABLED(SOFT_RESET_VIA_SERIAL)
|
||||
case '^': state = EP_ctrl; break;
|
||||
case 'K': state = EP_K; break;
|
||||
#endif
|
||||
default: state = EP_IGNORE;
|
||||
}
|
||||
break;
|
||||
|
||||
case EP_N:
|
||||
switch (c) {
|
||||
case '0' ... '9':
|
||||
case '-': case ' ': break;
|
||||
case 'M': state = EP_M; break;
|
||||
#if ENABLED(REALTIME_REPORTING_COMMANDS)
|
||||
case 'S': state = EP_S; break;
|
||||
case 'P': state = EP_P; break;
|
||||
case 'R': state = EP_R; break;
|
||||
#endif
|
||||
default: state = EP_IGNORE;
|
||||
}
|
||||
break;
|
||||
|
||||
#if ENABLED(REALTIME_REPORTING_COMMANDS)
|
||||
case EP_S: state = (c == '0') ? EP_S0 : EP_IGNORE; break;
|
||||
case EP_S0: state = (c == '0') ? EP_S00 : EP_IGNORE; break;
|
||||
case EP_S00: state = (c == '0') ? EP_GRBL_STATUS : EP_IGNORE; break;
|
||||
|
||||
case EP_R: state = (c == '0') ? EP_R0 : EP_IGNORE; break;
|
||||
case EP_R0: state = (c == '0') ? EP_R00 : EP_IGNORE; break;
|
||||
case EP_R00: state = (c == '0') ? EP_GRBL_RESUME : EP_IGNORE; break;
|
||||
|
||||
case EP_P: state = (c == '0') ? EP_P0 : EP_IGNORE; break;
|
||||
case EP_P0: state = (c == '0') ? EP_P00 : EP_IGNORE; break;
|
||||
case EP_P00: state = (c == '0') ? EP_GRBL_PAUSE : EP_IGNORE; break;
|
||||
#endif
|
||||
|
||||
#if ENABLED(SOFT_RESET_VIA_SERIAL)
|
||||
case EP_ctrl: state = (c == 'X') ? EP_KILL : EP_IGNORE; break;
|
||||
case EP_K: state = (c == 'I') ? EP_KI : EP_IGNORE; break;
|
||||
case EP_KI: state = (c == 'L') ? EP_KIL : EP_IGNORE; break;
|
||||
case EP_KIL: state = (c == 'L') ? EP_KILL : EP_IGNORE; break;
|
||||
#endif
|
||||
|
||||
case EP_M:
|
||||
switch (c) {
|
||||
case ' ': break;
|
||||
case '1': state = EP_M1; break;
|
||||
case '4': state = EP_M4; break;
|
||||
#if HAS_MEDIA
|
||||
case '5': state = EP_M5; break;
|
||||
#endif
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
case '8': state = EP_M8; break;
|
||||
#endif
|
||||
default: state = EP_IGNORE;
|
||||
}
|
||||
break;
|
||||
|
||||
case EP_M1:
|
||||
switch (c) {
|
||||
case '0': state = EP_M10; break;
|
||||
case '1': state = EP_M11; break;
|
||||
default: state = EP_IGNORE;
|
||||
}
|
||||
break;
|
||||
|
||||
case EP_M10: state = (c == '8') ? EP_M108 : EP_IGNORE; break;
|
||||
case EP_M11: state = (c == '2') ? EP_M112 : EP_IGNORE; break;
|
||||
case EP_M4: state = (c == '1') ? EP_M41 : EP_IGNORE; break;
|
||||
case EP_M41: state = (c == '0') ? EP_M410 : EP_IGNORE; break;
|
||||
|
||||
#if HAS_MEDIA
|
||||
case EP_M5: state = (c == '2') ? EP_M52 : EP_IGNORE; break;
|
||||
case EP_M52: state = (c == '4') ? EP_M524 : EP_IGNORE; break;
|
||||
#endif
|
||||
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
|
||||
case EP_M8: state = (c == '7') ? EP_M87 : EP_IGNORE; break;
|
||||
case EP_M87: state = (c == '6') ? EP_M876 : EP_IGNORE; break;
|
||||
|
||||
case EP_M876:
|
||||
switch (c) {
|
||||
case ' ': break;
|
||||
case 'S': state = EP_M876S; break;
|
||||
default: state = EP_IGNORE; break;
|
||||
}
|
||||
break;
|
||||
|
||||
case EP_M876S:
|
||||
switch (c) {
|
||||
case ' ': break;
|
||||
case '0' ... '9':
|
||||
state = EP_M876SN;
|
||||
M876_reason = uint8_t(c - '0');
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
#endif
|
||||
|
||||
case EP_IGNORE:
|
||||
if (ISEOL(c)) state = EP_RESET;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (ISEOL(c)) {
|
||||
if (enabled) switch (state) {
|
||||
case EP_M108: wait_for_user = wait_for_heatup = false; break;
|
||||
case EP_M112: killed_by_M112 = true; break;
|
||||
case EP_M410: quickstop_by_M410 = true; break;
|
||||
#if HAS_MEDIA
|
||||
case EP_M524: sd_abort_by_M524 = true; break;
|
||||
#endif
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
case EP_M876SN: hostui.handle_response(M876_reason); break;
|
||||
#endif
|
||||
#if ENABLED(REALTIME_REPORTING_COMMANDS)
|
||||
case EP_GRBL_STATUS: report_current_position_moving(); break;
|
||||
case EP_GRBL_PAUSE: quickpause_stepper(); break;
|
||||
case EP_GRBL_RESUME: quickresume_stepper(); break;
|
||||
#endif
|
||||
#if ENABLED(SOFT_RESET_VIA_SERIAL)
|
||||
case EP_KILL: HAL_reboot(); break;
|
||||
#endif
|
||||
default: break;
|
||||
}
|
||||
state = EP_RESET;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static bool enabled;
|
||||
};
|
||||
|
||||
extern EmergencyParser emergency_parser;
|
||||
@@ -0,0 +1,233 @@
|
||||
/**
|
||||
* 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 int16_t filecnt = card.get_num_items(); // Count printable files in cwd
|
||||
if (filecnt == 0) return; // None are printable?
|
||||
card.selectFileByIndex(filecnt); // Select the last file (without sort)
|
||||
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
|
||||
@@ -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;
|
||||
1126
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/encoder_i2c.cpp
Normal file
1126
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/encoder_i2c.cpp
Normal file
File diff suppressed because it is too large
Load Diff
320
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/encoder_i2c.h
Normal file
320
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/encoder_i2c.h
Normal file
@@ -0,0 +1,320 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#include "../module/planner.h"
|
||||
|
||||
#include <Wire.h>
|
||||
|
||||
//=========== Advanced / Less-Common Encoder Configuration Settings ==========
|
||||
|
||||
#define I2CPE_EC_THRESH_PROPORTIONAL // if enabled adjusts the error correction threshold
|
||||
// proportional to the current speed of the axis allows
|
||||
// for very small error margin at low speeds without
|
||||
// stuttering due to reading latency at high speeds
|
||||
|
||||
#define I2CPE_DEBUG // enable encoder-related debug serial echos
|
||||
|
||||
#define I2CPE_REBOOT_TIME 5000 // time we wait for an encoder module to reboot
|
||||
// after changing address.
|
||||
|
||||
#define I2CPE_MAG_SIG_GOOD 0
|
||||
#define I2CPE_MAG_SIG_MID 1
|
||||
#define I2CPE_MAG_SIG_BAD 2
|
||||
#define I2CPE_MAG_SIG_NF 255
|
||||
|
||||
#define I2CPE_REQ_REPORT 0
|
||||
#define I2CPE_RESET_COUNT 1
|
||||
#define I2CPE_SET_ADDR 2
|
||||
#define I2CPE_SET_REPORT_MODE 3
|
||||
#define I2CPE_CLEAR_EEPROM 4
|
||||
|
||||
#define I2CPE_LED_PAR_MODE 10
|
||||
#define I2CPE_LED_PAR_BRT 11
|
||||
#define I2CPE_LED_PAR_RATE 14
|
||||
|
||||
#define I2CPE_REPORT_DISTANCE 0
|
||||
#define I2CPE_REPORT_STRENGTH 1
|
||||
#define I2CPE_REPORT_VERSION 2
|
||||
|
||||
// Default I2C addresses
|
||||
#define I2CPE_PRESET_ADDR_X 30
|
||||
#define I2CPE_PRESET_ADDR_Y 31
|
||||
#define I2CPE_PRESET_ADDR_Z 32
|
||||
#define I2CPE_PRESET_ADDR_E 33
|
||||
|
||||
#define I2CPE_DEF_AXIS X_AXIS
|
||||
#define I2CPE_DEF_ADDR I2CPE_PRESET_ADDR_X
|
||||
|
||||
// Error event counter; tracks how many times there is an error exceeding a certain threshold
|
||||
#define I2CPE_ERR_CNT_THRESH 3.00
|
||||
#define I2CPE_ERR_CNT_DEBOUNCE_MS 2000
|
||||
|
||||
#if ENABLED(I2CPE_ERR_ROLLING_AVERAGE)
|
||||
#define I2CPE_ERR_ARRAY_SIZE 32
|
||||
#define I2CPE_ERR_PRST_ARRAY_SIZE 10
|
||||
#endif
|
||||
|
||||
// Error Correction Methods
|
||||
#define I2CPE_ECM_NONE 0
|
||||
#define I2CPE_ECM_MICROSTEP 1
|
||||
#define I2CPE_ECM_PLANNER 2
|
||||
#define I2CPE_ECM_STALLDETECT 3
|
||||
|
||||
// Encoder types
|
||||
#define I2CPE_ENC_TYPE_ROTARY 0
|
||||
#define I2CPE_ENC_TYPE_LINEAR 1
|
||||
|
||||
// Parser
|
||||
#define I2CPE_PARSE_ERR 1
|
||||
#define I2CPE_PARSE_OK 0
|
||||
|
||||
#define LOOP_PE(VAR) for (uint8_t VAR = 0; VAR < I2CPE_ENCODER_CNT; ++VAR)
|
||||
#define CHECK_IDX() do{ if (!WITHIN(idx, 0, I2CPE_ENCODER_CNT - 1)) return; }while(0)
|
||||
|
||||
typedef union {
|
||||
volatile int32_t val = 0;
|
||||
uint8_t bval[4];
|
||||
} i2cLong;
|
||||
|
||||
class I2CPositionEncoder {
|
||||
private:
|
||||
AxisEnum encoderAxis = I2CPE_DEF_AXIS;
|
||||
|
||||
uint8_t i2cAddress = I2CPE_DEF_ADDR,
|
||||
ecMethod = I2CPE_DEF_EC_METHOD,
|
||||
type = I2CPE_DEF_TYPE,
|
||||
H = I2CPE_MAG_SIG_NF; // Magnetic field strength
|
||||
|
||||
int encoderTicksPerUnit = I2CPE_DEF_ENC_TICKS_UNIT,
|
||||
stepperTicks = I2CPE_DEF_TICKS_REV,
|
||||
errorCount = 0,
|
||||
errorPrev = 0;
|
||||
|
||||
float ecThreshold = I2CPE_DEF_EC_THRESH;
|
||||
|
||||
bool homed = false,
|
||||
trusted = false,
|
||||
initialized = false,
|
||||
active = false,
|
||||
invert = false,
|
||||
ec = true;
|
||||
|
||||
int32_t zeroOffset = 0,
|
||||
lastPosition = 0,
|
||||
position;
|
||||
|
||||
millis_t lastPositionTime = 0,
|
||||
nextErrorCountTime = 0,
|
||||
lastErrorTime;
|
||||
|
||||
#if ENABLED(I2CPE_ERR_ROLLING_AVERAGE)
|
||||
uint8_t errIdx = 0, errPrstIdx = 0;
|
||||
int err[I2CPE_ERR_ARRAY_SIZE] = { 0 },
|
||||
errPrst[I2CPE_ERR_PRST_ARRAY_SIZE] = { 0 };
|
||||
#endif
|
||||
|
||||
public:
|
||||
void init(const uint8_t address, const AxisEnum axis);
|
||||
void reset();
|
||||
|
||||
void update();
|
||||
|
||||
void set_homed();
|
||||
void set_unhomed();
|
||||
|
||||
int32_t get_raw_count();
|
||||
|
||||
FORCE_INLINE float mm_from_count(const int32_t count) {
|
||||
switch (type) {
|
||||
default: return -1;
|
||||
case I2CPE_ENC_TYPE_LINEAR:
|
||||
return count / encoderTicksPerUnit;
|
||||
case I2CPE_ENC_TYPE_ROTARY:
|
||||
return (count * stepperTicks) / (encoderTicksPerUnit * planner.settings.axis_steps_per_mm[encoderAxis]);
|
||||
}
|
||||
}
|
||||
|
||||
FORCE_INLINE float get_position_mm() { return mm_from_count(get_position()); }
|
||||
FORCE_INLINE int32_t get_position() { return get_raw_count() - zeroOffset; }
|
||||
|
||||
int32_t get_axis_error_steps(const bool report);
|
||||
float get_axis_error_mm(const bool report);
|
||||
|
||||
void calibrate_steps_mm(const uint8_t iter);
|
||||
|
||||
bool passes_test(const bool report);
|
||||
|
||||
bool test_axis();
|
||||
|
||||
FORCE_INLINE int get_error_count() { return errorCount; }
|
||||
FORCE_INLINE void set_error_count(const int newCount) { errorCount = newCount; }
|
||||
|
||||
FORCE_INLINE uint8_t get_address() { return i2cAddress; }
|
||||
FORCE_INLINE void set_address(const uint8_t addr) { i2cAddress = addr; }
|
||||
|
||||
FORCE_INLINE bool get_active() { return active; }
|
||||
FORCE_INLINE void set_active(const bool a) { active = a; }
|
||||
|
||||
FORCE_INLINE void set_inverted(const bool i) { invert = i; }
|
||||
|
||||
FORCE_INLINE AxisEnum get_axis() { return encoderAxis; }
|
||||
|
||||
FORCE_INLINE bool get_ec_enabled() { return ec; }
|
||||
FORCE_INLINE void set_ec_enabled(const bool enabled) { ec = enabled; }
|
||||
|
||||
FORCE_INLINE uint8_t get_ec_method() { return ecMethod; }
|
||||
FORCE_INLINE void set_ec_method(const byte method) { ecMethod = method; }
|
||||
|
||||
FORCE_INLINE float get_ec_threshold() { return ecThreshold; }
|
||||
FORCE_INLINE void set_ec_threshold(const_float_t newThreshold) { ecThreshold = newThreshold; }
|
||||
|
||||
FORCE_INLINE int get_encoder_ticks_mm() {
|
||||
switch (type) {
|
||||
default: return 0;
|
||||
case I2CPE_ENC_TYPE_LINEAR:
|
||||
return encoderTicksPerUnit;
|
||||
case I2CPE_ENC_TYPE_ROTARY:
|
||||
return (int)((encoderTicksPerUnit / stepperTicks) * planner.settings.axis_steps_per_mm[encoderAxis]);
|
||||
}
|
||||
}
|
||||
|
||||
FORCE_INLINE int get_ticks_unit() { return encoderTicksPerUnit; }
|
||||
FORCE_INLINE void set_ticks_unit(const int ticks) { encoderTicksPerUnit = ticks; }
|
||||
|
||||
FORCE_INLINE uint8_t get_type() { return type; }
|
||||
FORCE_INLINE void set_type(const byte newType) { type = newType; }
|
||||
|
||||
FORCE_INLINE int get_stepper_ticks() { return stepperTicks; }
|
||||
FORCE_INLINE void set_stepper_ticks(const int ticks) { stepperTicks = ticks; }
|
||||
};
|
||||
|
||||
class I2CPositionEncodersMgr {
|
||||
private:
|
||||
static bool I2CPE_anyaxis;
|
||||
static uint8_t I2CPE_addr, I2CPE_idx;
|
||||
|
||||
public:
|
||||
|
||||
static void init();
|
||||
|
||||
// consider only updating one endoder per call / tick if encoders become too time intensive
|
||||
static void update() { LOOP_PE(i) encoders[i].update(); }
|
||||
|
||||
static void homed(const AxisEnum axis) {
|
||||
LOOP_PE(i)
|
||||
if (encoders[i].get_axis() == axis) encoders[i].set_homed();
|
||||
}
|
||||
|
||||
static void unhomed(const AxisEnum axis) {
|
||||
LOOP_PE(i)
|
||||
if (encoders[i].get_axis() == axis) encoders[i].set_unhomed();
|
||||
}
|
||||
|
||||
static void report_position(const int8_t idx, const bool units, const bool noOffset);
|
||||
|
||||
static void report_status(const int8_t idx) {
|
||||
CHECK_IDX();
|
||||
SERIAL_ECHOLNPGM("Encoder ", idx, ": ");
|
||||
encoders[idx].get_raw_count();
|
||||
encoders[idx].passes_test(true);
|
||||
}
|
||||
|
||||
static void report_error(const int8_t idx) {
|
||||
CHECK_IDX();
|
||||
encoders[idx].get_axis_error_steps(true);
|
||||
}
|
||||
|
||||
static void test_axis(const int8_t idx) {
|
||||
CHECK_IDX();
|
||||
encoders[idx].test_axis();
|
||||
}
|
||||
|
||||
static void calibrate_steps_mm(const int8_t idx, const int iterations) {
|
||||
CHECK_IDX();
|
||||
encoders[idx].calibrate_steps_mm(iterations);
|
||||
}
|
||||
|
||||
static void change_module_address(const uint8_t oldaddr, const uint8_t newaddr);
|
||||
static void report_module_firmware(const uint8_t address);
|
||||
|
||||
static void report_error_count(const int8_t idx, const AxisEnum axis) {
|
||||
CHECK_IDX();
|
||||
SERIAL_ECHOLNPGM("Error count on ", C(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 ", C(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 ", C(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 ", C(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 ", C(AXIS_CHAR(axis)), " axis is ", threshold, "mm.");
|
||||
}
|
||||
|
||||
static int8_t idx_from_axis(const AxisEnum axis) {
|
||||
LOOP_PE(i)
|
||||
if (encoders[i].get_axis() == axis) return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int8_t idx_from_addr(const uint8_t addr) {
|
||||
LOOP_PE(i)
|
||||
if (encoders[i].get_address() == addr) return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int8_t parse();
|
||||
|
||||
static void M860();
|
||||
static void M861();
|
||||
static void M862();
|
||||
static void M863();
|
||||
static void M864();
|
||||
static void M865();
|
||||
static void M866();
|
||||
static void M867();
|
||||
static void M868();
|
||||
static void M869();
|
||||
|
||||
static I2CPositionEncoder encoders[I2CPE_ENCODER_CNT];
|
||||
};
|
||||
|
||||
extern I2CPositionEncodersMgr I2CPEM;
|
||||
175
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/ethernet.cpp
Normal file
175
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/ethernet.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
|
||||
#if HAS_ETHERNET
|
||||
|
||||
#include "ethernet.h"
|
||||
#include "../core/serial.h"
|
||||
|
||||
#define DEBUG_OUT ENABLED(DEBUG_ETHERNET)
|
||||
#include "../core/debug_out.h"
|
||||
|
||||
bool MarlinEthernet::hardware_enabled, // = false
|
||||
MarlinEthernet::have_telnet_client; // = false
|
||||
|
||||
IPAddress MarlinEthernet::ip,
|
||||
MarlinEthernet::myDns,
|
||||
MarlinEthernet::gateway,
|
||||
MarlinEthernet::subnet;
|
||||
|
||||
EthernetClient MarlinEthernet::telnetClient; // connected client
|
||||
|
||||
MarlinEthernet ethernet;
|
||||
|
||||
EthernetServer server(23); // telnet server
|
||||
|
||||
enum linkStates { UNLINKED, LINKING, LINKED, CONNECTING, CONNECTED, NO_HARDWARE } linkState;
|
||||
|
||||
#ifdef __IMXRT1062__
|
||||
|
||||
static void teensyMAC(uint8_t * const mac) {
|
||||
const uint32_t m1 = HW_OCOTP_MAC1, m2 = HW_OCOTP_MAC0;
|
||||
mac[0] = m1 >> 8;
|
||||
mac[1] = m1 >> 0;
|
||||
mac[2] = m2 >> 24;
|
||||
mac[3] = m2 >> 16;
|
||||
mac[4] = m2 >> 8;
|
||||
mac[5] = m2 >> 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
byte mac[] = MAC_ADDRESS;
|
||||
|
||||
#endif
|
||||
|
||||
void ethernet_cable_error() { SERIAL_ERROR_MSG("Ethernet cable is not connected."); }
|
||||
|
||||
void MarlinEthernet::init() {
|
||||
if (!hardware_enabled) return;
|
||||
|
||||
SERIAL_ECHO_MSG("Starting network...");
|
||||
|
||||
// Init the Ethernet device
|
||||
#ifdef __IMXRT1062__
|
||||
uint8_t mac[6];
|
||||
teensyMAC(mac);
|
||||
#endif
|
||||
|
||||
if (!ip) {
|
||||
Ethernet.begin(mac); // use DHCP
|
||||
}
|
||||
else {
|
||||
if (!gateway) {
|
||||
gateway = ip;
|
||||
gateway[3] = 1;
|
||||
myDns = gateway;
|
||||
subnet = IPAddress(255,255,255,0);
|
||||
}
|
||||
if (!myDns) myDns = gateway;
|
||||
if (!subnet) subnet = IPAddress(255,255,255,0);
|
||||
Ethernet.begin(mac, ip, myDns, gateway, subnet);
|
||||
}
|
||||
|
||||
// Check for Ethernet hardware present
|
||||
if (Ethernet.hardwareStatus() == EthernetNoHardware) {
|
||||
SERIAL_ERROR_MSG("No Ethernet hardware found.");
|
||||
linkState = NO_HARDWARE;
|
||||
return;
|
||||
}
|
||||
|
||||
linkState = UNLINKED;
|
||||
|
||||
if (Ethernet.linkStatus() == LinkOFF)
|
||||
ethernet_cable_error();
|
||||
}
|
||||
|
||||
void MarlinEthernet::check() {
|
||||
if (!hardware_enabled) return;
|
||||
|
||||
switch (linkState) {
|
||||
case NO_HARDWARE:
|
||||
break;
|
||||
|
||||
case UNLINKED:
|
||||
if (Ethernet.linkStatus() == LinkOFF) break;
|
||||
|
||||
SERIAL_ECHOLNPGM("Ethernet cable connected");
|
||||
server.begin();
|
||||
linkState = LINKING;
|
||||
break;
|
||||
|
||||
case LINKING:
|
||||
if (!Ethernet.localIP()) break;
|
||||
|
||||
SERIAL_ECHOPGM("Successfully started telnet server with IP ");
|
||||
MYSERIAL1.println(Ethernet.localIP());
|
||||
|
||||
linkState = LINKED;
|
||||
break;
|
||||
|
||||
case LINKED:
|
||||
if (Ethernet.linkStatus() == LinkOFF) {
|
||||
ethernet_cable_error();
|
||||
linkState = UNLINKED;
|
||||
break;
|
||||
}
|
||||
telnetClient = server.accept();
|
||||
if (telnetClient) linkState = CONNECTING;
|
||||
break;
|
||||
|
||||
case CONNECTING:
|
||||
telnetClient.println("Marlin " SHORT_BUILD_VERSION);
|
||||
#if defined(STRING_DISTRIBUTION_DATE) && defined(STRING_CONFIG_H_AUTHOR)
|
||||
telnetClient.println(
|
||||
" Last Updated: " STRING_DISTRIBUTION_DATE
|
||||
" | Author: " STRING_CONFIG_H_AUTHOR
|
||||
);
|
||||
#endif
|
||||
telnetClient.println(" Compiled: " __DATE__);
|
||||
|
||||
SERIAL_ECHOLNPGM("Client connected");
|
||||
have_telnet_client = true;
|
||||
linkState = CONNECTED;
|
||||
break;
|
||||
|
||||
case CONNECTED:
|
||||
if (telnetClient && !telnetClient.connected()) {
|
||||
SERIAL_ECHOLNPGM("Client disconnected");
|
||||
telnetClient.stop();
|
||||
have_telnet_client = false;
|
||||
linkState = LINKED;
|
||||
}
|
||||
if (Ethernet.linkStatus() == LinkOFF) {
|
||||
ethernet_cable_error();
|
||||
if (telnetClient) telnetClient.stop();
|
||||
linkState = UNLINKED;
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HAS_ETHERNET
|
||||
39
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/ethernet.h
Normal file
39
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/ethernet.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
#ifdef __IMXRT1062__
|
||||
#include <NativeEthernet.h>
|
||||
#endif
|
||||
|
||||
// Teensy 4.1 uses internal MAC Address
|
||||
|
||||
class MarlinEthernet {
|
||||
public:
|
||||
static bool hardware_enabled, have_telnet_client;
|
||||
static IPAddress ip, myDns, gateway, subnet;
|
||||
static EthernetClient telnetClient;
|
||||
static void init();
|
||||
static void check();
|
||||
};
|
||||
|
||||
extern MarlinEthernet ethernet;
|
||||
207
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/fancheck.cpp
Normal file
207
creality-ender3/Marlin-2.1.2.4/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;
|
||||
for (uint8_t f = 0; f < TACHO_COUNT; ++f) {
|
||||
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;
|
||||
for (uint8_t f = 0; f < TACHO_COUNT; ++f) {
|
||||
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
|
||||
for (uint8_t f = 0; f < TACHO_COUNT; ++f) 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() {
|
||||
for (uint8_t s = 0; s < 2; ++s) {
|
||||
for (uint8_t f = 0; f < TACHO_COUNT; ++f) {
|
||||
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
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/fancheck.h
Normal file
89
creality-ender3/Marlin-2.1.2.4/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
|
||||
55
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/fanmux.cpp
Normal file
55
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/fanmux.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* feature/pause.cpp - Pause feature support functions
|
||||
* This may be combined with related G-codes if features are consolidated.
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#if HAS_FANMUX
|
||||
|
||||
#include "fanmux.h"
|
||||
|
||||
void fanmux_switch(const uint8_t e) {
|
||||
WRITE(FANMUX0_PIN, TEST(e, 0) ? HIGH : LOW);
|
||||
#if PIN_EXISTS(FANMUX1)
|
||||
WRITE(FANMUX1_PIN, TEST(e, 1) ? HIGH : LOW);
|
||||
#if PIN_EXISTS(FANMUX2)
|
||||
WRITE(FANMUX2_PIN, TEST(e, 2) ? HIGH : LOW);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void fanmux_init() {
|
||||
SET_OUTPUT(FANMUX0_PIN);
|
||||
#if PIN_EXISTS(FANMUX1)
|
||||
SET_OUTPUT(FANMUX1_PIN);
|
||||
#if PIN_EXISTS(FANMUX2)
|
||||
SET_OUTPUT(FANMUX2_PIN);
|
||||
#endif
|
||||
#endif
|
||||
fanmux_switch(0);
|
||||
}
|
||||
|
||||
#endif // HAS_FANMUX
|
||||
29
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/fanmux.h
Normal file
29
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/fanmux.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
/**
|
||||
* feature/fanmux.h - Cooling Fan Multiplexer support functions
|
||||
*/
|
||||
|
||||
void fanmux_switch(const uint8_t e);
|
||||
void fanmux_init();
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(FILAMENT_WIDTH_SENSOR)
|
||||
|
||||
#include "filwidth.h"
|
||||
|
||||
FilamentWidthSensor filwidth;
|
||||
|
||||
bool FilamentWidthSensor::enabled; // = false; // (M405-M406) Filament Width Sensor ON/OFF.
|
||||
uint32_t FilamentWidthSensor::accum; // = 0 // ADC accumulator
|
||||
uint16_t FilamentWidthSensor::raw; // = 0 // Measured filament diameter - one extruder only
|
||||
float FilamentWidthSensor::nominal_mm = DEFAULT_NOMINAL_FILAMENT_DIA, // (M104) Nominal filament width
|
||||
FilamentWidthSensor::measured_mm = DEFAULT_MEASURED_FILAMENT_DIA, // Measured filament diameter
|
||||
FilamentWidthSensor::e_count = 0,
|
||||
FilamentWidthSensor::delay_dist = 0;
|
||||
uint8_t FilamentWidthSensor::meas_delay_cm = MEASUREMENT_DELAY_CM; // Distance delay setting
|
||||
int8_t FilamentWidthSensor::ratios[MAX_MEASUREMENT_DELAY + 1], // Ring buffer to delay measurement. (Extruder factor minus 100)
|
||||
FilamentWidthSensor::index_r, // Indexes into ring buffer
|
||||
FilamentWidthSensor::index_w;
|
||||
|
||||
void FilamentWidthSensor::init() {
|
||||
const int8_t ratio = sample_to_size_ratio();
|
||||
for (uint8_t i = 0; i < COUNT(ratios); ++i) ratios[i] = ratio;
|
||||
index_r = index_w = 0;
|
||||
}
|
||||
|
||||
#endif // FILAMENT_WIDTH_SENSOR
|
||||
120
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/filwidth.h
Normal file
120
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/filwidth.h
Normal file
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
#include "../module/planner.h"
|
||||
#include "../module/thermistor/thermistors.h"
|
||||
|
||||
class FilamentWidthSensor {
|
||||
public:
|
||||
static constexpr int MMD_CM = MAX_MEASUREMENT_DELAY + 1, MMD_MM = MMD_CM * 10;
|
||||
static bool enabled; // (M405-M406) Filament Width Sensor ON/OFF.
|
||||
static uint32_t accum; // ADC accumulator
|
||||
static uint16_t raw; // Measured filament diameter - one extruder only
|
||||
static float nominal_mm, // (M104) Nominal filament width
|
||||
measured_mm, // Measured filament diameter
|
||||
e_count, delay_dist;
|
||||
static uint8_t meas_delay_cm; // Distance delay setting
|
||||
static int8_t ratios[MMD_CM], // Ring buffer to delay measurement. (Extruder factor minus 100)
|
||||
index_r, index_w; // Indexes into ring buffer
|
||||
|
||||
FilamentWidthSensor() { init(); }
|
||||
static void init();
|
||||
|
||||
static void enable(const bool ena) { enabled = ena; }
|
||||
|
||||
static void set_delay_cm(const uint8_t cm) {
|
||||
meas_delay_cm = _MIN(cm, MAX_MEASUREMENT_DELAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Filament Width (mm) to an extrusion ratio
|
||||
* and reduce to an 8 bit value.
|
||||
*
|
||||
* A nominal width of 1.75 and measured width of 1.73
|
||||
* gives (100 * 1.75 / 1.73) for a ratio of 101 and
|
||||
* a return value of 1.
|
||||
*/
|
||||
static int8_t sample_to_size_ratio() {
|
||||
return ABS(nominal_mm - measured_mm) <= FILWIDTH_ERROR_MARGIN
|
||||
? int(100.0f * nominal_mm / measured_mm) - 100 : 0;
|
||||
}
|
||||
|
||||
// Apply a single ADC reading to the raw value
|
||||
static void accumulate(const uint16_t adc) {
|
||||
if (adc > 102) // Ignore ADC under 0.5 volts
|
||||
accum += (uint32_t(adc) << 7) - (accum >> 7);
|
||||
}
|
||||
|
||||
// Convert raw measurement to mm
|
||||
static float raw_to_mm(const uint16_t v) { return v * (float(ADC_VREF_MV) / 1000.0f) * 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 void reading_ready() { raw = accum >> 10; }
|
||||
|
||||
// Update mm from the raw measurement
|
||||
static void update_measured_mm() { measured_mm = raw_to_mm(); }
|
||||
|
||||
// Update ring buffer used to delay filament measurements
|
||||
static void advance_e(const_float_t e_move) {
|
||||
|
||||
// Increment counters with the E distance
|
||||
e_count += e_move;
|
||||
delay_dist += e_move;
|
||||
|
||||
// Only get new measurements on forward E movement
|
||||
if (!UNEAR_ZERO(e_count)) {
|
||||
|
||||
// Loop the delay distance counter (modulus by the mm length)
|
||||
while (delay_dist >= MMD_MM) delay_dist -= MMD_MM;
|
||||
|
||||
// Convert into an index (cm) into the measurement array
|
||||
index_r = int8_t(delay_dist * 0.1f);
|
||||
|
||||
// If the ring buffer is not full...
|
||||
if (index_r != index_w) {
|
||||
e_count = 0; // Reset the E movement counter
|
||||
const int8_t meas_sample = sample_to_size_ratio();
|
||||
do {
|
||||
if (++index_w >= MMD_CM) index_w = 0; // The next unused slot
|
||||
ratios[index_w] = meas_sample; // Store the measurement
|
||||
} while (index_r != index_w); // More slots to fill?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dynamically set the volumetric multiplier based on the delayed width measurement.
|
||||
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
|
||||
LIMIT(read_index, 0, MAX_MEASUREMENT_DELAY);
|
||||
planner.apply_filament_width_sensor(ratios[read_index]);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
extern FilamentWidthSensor filwidth;
|
||||
266
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/fwretract.cpp
Normal file
266
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/fwretract.cpp
Normal file
@@ -0,0 +1,266 @@
|
||||
/**
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* fwretract.cpp - Implement firmware-based retraction
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(FWRETRACT)
|
||||
|
||||
#include "fwretract.h"
|
||||
|
||||
FWRetract fwretract; // Single instance - this calls the constructor
|
||||
|
||||
#include "../module/motion.h"
|
||||
#include "../module/planner.h"
|
||||
|
||||
#include "../gcode/gcode.h"
|
||||
|
||||
#if ENABLED(RETRACT_SYNC_MIXING)
|
||||
#include "mixing.h"
|
||||
#endif
|
||||
|
||||
// private:
|
||||
|
||||
#if HAS_MULTI_EXTRUDER
|
||||
Flags<EXTRUDERS> FWRetract::retracted_swap; // Which extruders are swap-retracted
|
||||
#endif
|
||||
|
||||
// public:
|
||||
|
||||
fwretract_settings_t FWRetract::settings; // M207 S F Z W, M208 S F W R
|
||||
|
||||
#if ENABLED(FWRETRACT_AUTORETRACT)
|
||||
bool FWRetract::autoretract_enabled; // M209 S - Autoretract switch
|
||||
#endif
|
||||
|
||||
Flags<EXTRUDERS> FWRetract::retracted; // Which extruders are currently retracted
|
||||
|
||||
float FWRetract::current_retract[EXTRUDERS], // Retract value used by planner
|
||||
FWRetract::current_hop;
|
||||
|
||||
void FWRetract::reset() {
|
||||
TERN_(FWRETRACT_AUTORETRACT, autoretract_enabled = false);
|
||||
settings.retract_length = RETRACT_LENGTH;
|
||||
settings.retract_feedrate_mm_s = RETRACT_FEEDRATE;
|
||||
settings.retract_zraise = RETRACT_ZRAISE;
|
||||
settings.retract_recover_extra = RETRACT_RECOVER_LENGTH;
|
||||
settings.retract_recover_feedrate_mm_s = RETRACT_RECOVER_FEEDRATE;
|
||||
settings.swap_retract_length = RETRACT_LENGTH_SWAP;
|
||||
settings.swap_retract_recover_extra = RETRACT_RECOVER_LENGTH_SWAP;
|
||||
settings.swap_retract_recover_feedrate_mm_s = RETRACT_RECOVER_FEEDRATE_SWAP;
|
||||
current_hop = 0.0;
|
||||
|
||||
retracted.reset();
|
||||
EXTRUDER_LOOP() {
|
||||
E_TERN_(retracted_swap.clear(e));
|
||||
current_retract[e] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retract or recover according to firmware settings
|
||||
*
|
||||
* This function handles retract/recover moves for G10 and G11,
|
||||
* plus auto-retract moves sent from G0/G1 when E-only moves are done.
|
||||
*
|
||||
* To simplify the logic, doubled retract/recover moves are ignored.
|
||||
*
|
||||
* Note: Auto-retract will apply the set Z hop in addition to any Z hop
|
||||
* included in the G-code. Use M207 Z0 to to prevent double hop.
|
||||
*/
|
||||
void FWRetract::retract(const bool retracting E_OPTARG(bool swapping/*=false*/)) {
|
||||
// Prevent two retracts or recovers in a row
|
||||
if (retracted[active_extruder] == retracting) return;
|
||||
|
||||
// Prevent two swap-retract or recovers in a row
|
||||
#if HAS_MULTI_EXTRUDER
|
||||
// Allow G10 S1 only after G11
|
||||
if (swapping && retracted_swap[active_extruder] == retracting) return;
|
||||
// G11 priority to recover the long retract if activated
|
||||
if (!retracting) swapping = retracted_swap[active_extruder];
|
||||
#else
|
||||
constexpr bool swapping = false;
|
||||
#endif
|
||||
|
||||
/* // debugging
|
||||
SERIAL_ECHOLNPGM(
|
||||
"retracting ", AS_DIGIT(retracting),
|
||||
" swapping ", swapping,
|
||||
" active extruder ", active_extruder
|
||||
);
|
||||
EXTRUDER_LOOP() {
|
||||
SERIAL_ECHOLNPGM("retracted[", e, "] ", AS_DIGIT(retracted[e]));
|
||||
#if HAS_MULTI_EXTRUDER
|
||||
SERIAL_ECHOLNPGM("retracted_swap[", e, "] ", AS_DIGIT(retracted_swap[e]));
|
||||
#endif
|
||||
}
|
||||
SERIAL_ECHOLNPGM("current_position.z ", current_position.z);
|
||||
SERIAL_ECHOLNPGM("current_position.e ", current_position.e);
|
||||
SERIAL_ECHOLNPGM("current_hop ", current_hop);
|
||||
//*/
|
||||
|
||||
const float base_retract = TERN1(RETRACT_SYNC_MIXING, (MIXING_STEPPERS))
|
||||
* (swapping ? settings.swap_retract_length : settings.retract_length);
|
||||
|
||||
// The current position will be the destination for E and Z moves
|
||||
destination = current_position;
|
||||
|
||||
#if ENABLED(RETRACT_SYNC_MIXING)
|
||||
const uint8_t old_mixing_tool = mixer.get_current_vtool();
|
||||
mixer.T(MIXER_AUTORETRACT_TOOL);
|
||||
#endif
|
||||
|
||||
const feedRate_t fr_max_z = planner.settings.max_feedrate_mm_s[Z_AXIS];
|
||||
if (retracting) {
|
||||
// Retract by moving from a faux E position back to the current E position
|
||||
current_retract[active_extruder] = base_retract;
|
||||
prepare_internal_move_to_destination( // set current from destination
|
||||
settings.retract_feedrate_mm_s * TERN1(RETRACT_SYNC_MIXING, (MIXING_STEPPERS))
|
||||
);
|
||||
|
||||
// Is a Z hop set, and has the hop not yet been done?
|
||||
if (!current_hop && settings.retract_zraise > 0.01f) { // Apply hop only once
|
||||
current_hop += settings.retract_zraise; // Add to the hop total (again, only once)
|
||||
// Raise up, set_current_to_destination. Maximum Z feedrate
|
||||
prepare_internal_move_to_destination(fr_max_z);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// If a hop was done and Z hasn't changed, undo the Z hop
|
||||
if (current_hop) {
|
||||
current_hop = 0;
|
||||
// Lower Z, set_current_to_destination. Maximum Z feedrate
|
||||
prepare_internal_move_to_destination(fr_max_z);
|
||||
}
|
||||
|
||||
const float extra_recover = swapping ? settings.swap_retract_recover_extra : settings.retract_recover_extra;
|
||||
if (extra_recover) {
|
||||
current_position.e -= extra_recover; // Adjust the current E position by the extra amount to recover
|
||||
sync_plan_position_e(); // Sync the planner position so the extra amount is recovered
|
||||
}
|
||||
|
||||
current_retract[active_extruder] = 0;
|
||||
|
||||
// Recover E, set_current_to_destination
|
||||
prepare_internal_move_to_destination(
|
||||
(swapping ? settings.swap_retract_recover_feedrate_mm_s : settings.retract_recover_feedrate_mm_s)
|
||||
* TERN1(RETRACT_SYNC_MIXING, (MIXING_STEPPERS))
|
||||
);
|
||||
}
|
||||
|
||||
TERN_(RETRACT_SYNC_MIXING, mixer.T(old_mixing_tool)); // Restore original mixing tool
|
||||
|
||||
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.set(active_extruder, retracting);
|
||||
#endif
|
||||
|
||||
/* // debugging
|
||||
SERIAL_ECHOLNPGM("retracting ", AS_DIGIT(retracting));
|
||||
SERIAL_ECHOLNPGM("swapping ", AS_DIGIT(swapping));
|
||||
SERIAL_ECHOLNPGM("active_extruder ", active_extruder);
|
||||
EXTRUDER_LOOP() {
|
||||
SERIAL_ECHOLNPGM("retracted[", e, "] ", AS_DIGIT(retracted[e]));
|
||||
#if HAS_MULTI_EXTRUDER
|
||||
SERIAL_ECHOLNPGM("retracted_swap[", e, "] ", AS_DIGIT(retracted_swap[e]));
|
||||
#endif
|
||||
}
|
||||
SERIAL_ECHOLNPGM("current_position.z ", current_position.z);
|
||||
SERIAL_ECHOLNPGM("current_position.e ", current_position.e);
|
||||
SERIAL_ECHOLNPGM("current_hop ", current_hop);
|
||||
//*/
|
||||
}
|
||||
|
||||
/**
|
||||
* M207: Set firmware retraction values
|
||||
*
|
||||
* S[+units] retract_length
|
||||
* W[+units] swap_retract_length (multi-extruder)
|
||||
* F[units/min] retract_feedrate_mm_s
|
||||
* Z[units] retract_zraise
|
||||
*/
|
||||
void FWRetract::M207() {
|
||||
if (!parser.seen("FSWZ")) return M207_report();
|
||||
if (parser.seenval('S')) settings.retract_length = parser.value_axis_units(E_AXIS);
|
||||
if (parser.seenval('F')) settings.retract_feedrate_mm_s = MMM_TO_MMS(parser.value_axis_units(E_AXIS));
|
||||
if (parser.seenval('Z')) settings.retract_zraise = parser.value_linear_units();
|
||||
if (parser.seenval('W')) settings.swap_retract_length = parser.value_axis_units(E_AXIS);
|
||||
}
|
||||
|
||||
void FWRetract::M207_report() {
|
||||
SERIAL_ECHOLNPGM_P(
|
||||
PSTR(" M207 S"), LINEAR_UNIT(settings.retract_length)
|
||||
, PSTR(" W"), LINEAR_UNIT(settings.swap_retract_length)
|
||||
, PSTR(" F"), LINEAR_UNIT(MMS_TO_MMM(settings.retract_feedrate_mm_s))
|
||||
, SP_Z_STR, LINEAR_UNIT(settings.retract_zraise)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* M208: Set firmware un-retraction values
|
||||
*
|
||||
* S[+units] retract_recover_extra (in addition to M207 S*)
|
||||
* W[+units] swap_retract_recover_extra (multi-extruder)
|
||||
* F[units/min] retract_recover_feedrate_mm_s
|
||||
* R[units/min] swap_retract_recover_feedrate_mm_s
|
||||
*/
|
||||
void FWRetract::M208() {
|
||||
if (!parser.seen("FSRW")) return M208_report();
|
||||
if (parser.seen('S')) settings.retract_recover_extra = parser.value_axis_units(E_AXIS);
|
||||
if (parser.seen('F')) settings.retract_recover_feedrate_mm_s = MMM_TO_MMS(parser.value_axis_units(E_AXIS));
|
||||
if (parser.seen('R')) settings.swap_retract_recover_feedrate_mm_s = MMM_TO_MMS(parser.value_axis_units(E_AXIS));
|
||||
if (parser.seen('W')) settings.swap_retract_recover_extra = parser.value_axis_units(E_AXIS);
|
||||
}
|
||||
|
||||
void FWRetract::M208_report() {
|
||||
SERIAL_ECHOLNPGM(
|
||||
" M208 S", LINEAR_UNIT(settings.retract_recover_extra)
|
||||
, " W", LINEAR_UNIT(settings.swap_retract_recover_extra)
|
||||
, " F", LINEAR_UNIT(MMS_TO_MMM(settings.retract_recover_feedrate_mm_s))
|
||||
);
|
||||
}
|
||||
|
||||
#if ENABLED(FWRETRACT_AUTORETRACT)
|
||||
|
||||
/**
|
||||
* M209: Enable automatic retract (M209 S1)
|
||||
* For slicers that don't support G10/11, reversed extrude-only
|
||||
* moves will be classified as retraction.
|
||||
*/
|
||||
void FWRetract::M209() {
|
||||
if (!parser.seen('S')) return M209_report();
|
||||
if (MIN_AUTORETRACT <= MAX_AUTORETRACT)
|
||||
enable_autoretract(parser.value_bool());
|
||||
}
|
||||
|
||||
void FWRetract::M209_report() {
|
||||
SERIAL_ECHOLNPGM(" M209 S", AS_DIGIT(autoretract_enabled));
|
||||
}
|
||||
|
||||
#endif // FWRETRACT_AUTORETRACT
|
||||
|
||||
#endif // FWRETRACT
|
||||
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
/**
|
||||
* fwretract.h - Define firmware-based retraction interface
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
|
||||
typedef struct {
|
||||
float retract_length; // M207 S - G10 Retract length
|
||||
feedRate_t retract_feedrate_mm_s; // M207 F - G10 Retract feedrate
|
||||
float retract_zraise, // M207 Z - G10 Retract hop size
|
||||
retract_recover_extra; // M208 S - G11 Recover length
|
||||
feedRate_t retract_recover_feedrate_mm_s; // M208 F - G11 Recover feedrate
|
||||
float swap_retract_length, // M207 W - G10 Swap Retract length
|
||||
swap_retract_recover_extra; // M208 W - G11 Swap Recover length
|
||||
feedRate_t swap_retract_recover_feedrate_mm_s; // M208 R - G11 Swap Recover feedrate
|
||||
} fwretract_settings_t;
|
||||
|
||||
#if ENABLED(FWRETRACT)
|
||||
|
||||
class FWRetract {
|
||||
private:
|
||||
#if HAS_MULTI_EXTRUDER
|
||||
static Flags<EXTRUDERS> retracted_swap; // Which extruders are swap-retracted
|
||||
#endif
|
||||
|
||||
public:
|
||||
static fwretract_settings_t settings;
|
||||
|
||||
#if ENABLED(FWRETRACT_AUTORETRACT)
|
||||
static bool autoretract_enabled; // M209 S - Autoretract switch
|
||||
#else
|
||||
static constexpr bool autoretract_enabled = false;
|
||||
#endif
|
||||
|
||||
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
|
||||
|
||||
FWRetract() { reset(); }
|
||||
|
||||
static void reset();
|
||||
|
||||
static void refresh_autoretract() { retracted.reset(); }
|
||||
|
||||
static void enable_autoretract(const bool enable) {
|
||||
#if ENABLED(FWRETRACT_AUTORETRACT)
|
||||
autoretract_enabled = enable;
|
||||
refresh_autoretract();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void retract(const bool retracting E_OPTARG(bool swapping=false));
|
||||
|
||||
static void M207_report();
|
||||
static void M207();
|
||||
static void M208_report();
|
||||
static void M208();
|
||||
#if ENABLED(FWRETRACT_AUTORETRACT)
|
||||
static void M209_report();
|
||||
static void M209();
|
||||
#endif
|
||||
};
|
||||
|
||||
extern FWRetract fwretract;
|
||||
|
||||
#endif // FWRETRACT
|
||||
@@ -0,0 +1,225 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(HOST_ACTION_COMMANDS)
|
||||
|
||||
//#define DEBUG_HOST_ACTIONS
|
||||
|
||||
#include "host_actions.h"
|
||||
|
||||
#if ENABLED(ADVANCED_PAUSE_FEATURE)
|
||||
#include "pause.h"
|
||||
#include "../gcode/queue.h"
|
||||
#endif
|
||||
|
||||
#if HAS_FILAMENT_SENSOR
|
||||
#include "runout.h"
|
||||
#endif
|
||||
|
||||
HostUI hostui;
|
||||
|
||||
void HostUI::action(FSTR_P const fstr, const bool eol) {
|
||||
PORT_REDIRECT(SerialMask::All);
|
||||
SERIAL_ECHOPGM("//action:");
|
||||
SERIAL_ECHOF(fstr);
|
||||
if (eol) SERIAL_EOL();
|
||||
}
|
||||
|
||||
#ifdef ACTION_ON_KILL
|
||||
void HostUI::kill() { action(F(ACTION_ON_KILL)); }
|
||||
#endif
|
||||
#ifdef ACTION_ON_PAUSE
|
||||
void HostUI::pause(const bool eol/*=true*/) { action(F(ACTION_ON_PAUSE), eol); }
|
||||
#endif
|
||||
#ifdef ACTION_ON_PAUSED
|
||||
void HostUI::paused(const bool eol/*=true*/) { action(F(ACTION_ON_PAUSED), eol); }
|
||||
#endif
|
||||
#ifdef ACTION_ON_RESUME
|
||||
void HostUI::resume() { action(F(ACTION_ON_RESUME)); }
|
||||
#endif
|
||||
#ifdef ACTION_ON_RESUMED
|
||||
void HostUI::resumed() { action(F(ACTION_ON_RESUMED)); }
|
||||
#endif
|
||||
#ifdef ACTION_ON_CANCEL
|
||||
void HostUI::cancel() { action(F(ACTION_ON_CANCEL)); }
|
||||
#endif
|
||||
#ifdef 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");
|
||||
|
||||
#if HAS_RESUME_CONTINUE
|
||||
extern bool wait_for_user;
|
||||
#endif
|
||||
|
||||
void HostUI::notify(const char * const cstr) {
|
||||
PORT_REDIRECT(SerialMask::All);
|
||||
action(F("notification "), false);
|
||||
SERIAL_ECHOLN(cstr);
|
||||
}
|
||||
|
||||
void HostUI::notify_P(PGM_P const pstr) {
|
||||
PORT_REDIRECT(SerialMask::All);
|
||||
action(F("notification "), false);
|
||||
SERIAL_ECHOLNPGM_P(pstr);
|
||||
}
|
||||
|
||||
void HostUI::prompt(FSTR_P const ptype, const bool eol/*=true*/) {
|
||||
PORT_REDIRECT(SerialMask::All);
|
||||
action(F("prompt_"), false);
|
||||
SERIAL_ECHOF(ptype);
|
||||
if (eol) SERIAL_EOL();
|
||||
}
|
||||
|
||||
void HostUI::prompt_plus(const bool pgm, FSTR_P const ptype, const char * const str, const char extra_char/*='\0'*/) {
|
||||
prompt(ptype, false);
|
||||
PORT_REDIRECT(SerialMask::All);
|
||||
SERIAL_CHAR(' ');
|
||||
if (pgm)
|
||||
SERIAL_ECHOPGM_P(str);
|
||||
else
|
||||
SERIAL_ECHO(str);
|
||||
if (extra_char != '\0') SERIAL_CHAR(extra_char);
|
||||
SERIAL_EOL();
|
||||
}
|
||||
|
||||
void HostUI::prompt_begin(const PromptReason reason, FSTR_P const fstr, const char extra_char/*='\0'*/) {
|
||||
prompt_end();
|
||||
host_prompt_reason = reason;
|
||||
prompt_plus(F("begin"), fstr, extra_char);
|
||||
}
|
||||
void HostUI::prompt_begin(const PromptReason reason, const char * const cstr, const char extra_char/*='\0'*/) {
|
||||
prompt_end();
|
||||
host_prompt_reason = reason;
|
||||
prompt_plus(F("begin"), cstr, extra_char);
|
||||
}
|
||||
|
||||
void HostUI::prompt_end() { prompt(F("end")); }
|
||||
void HostUI::prompt_show() { prompt(F("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 HostUI::prompt_button(FSTR_P const fstr) { prompt_plus(F("button"), fstr); }
|
||||
void HostUI::prompt_button(const char * const cstr) { prompt_plus(F("button"), cstr); }
|
||||
|
||||
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 HostUI::prompt_do(const PromptReason reason, const char * const cstr, FSTR_P const btn1/*=nullptr*/, FSTR_P const btn2/*=nullptr*/) {
|
||||
prompt_begin(reason, cstr);
|
||||
_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 HostUI::prompt_do(const PromptReason reason, const char * const cstr, const char extra_char, FSTR_P const btn1/*=nullptr*/, FSTR_P const btn2/*=nullptr*/) {
|
||||
prompt_begin(reason, cstr, extra_char);
|
||||
_prompt_show(btn1, btn2);
|
||||
}
|
||||
|
||||
#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:
|
||||
// - Filament runout responses: Purge More, Continue
|
||||
// - General "Continue" response
|
||||
// - Resume Print response
|
||||
// - Dismissal of info
|
||||
//
|
||||
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) {
|
||||
case PROMPT_FILAMENT_RUNOUT:
|
||||
switch (response) {
|
||||
|
||||
case 0: // "Purge More" button
|
||||
#if ALL(M600_PURGE_MORE_RESUMABLE, ADVANCED_PAUSE_FEATURE)
|
||||
pause_menu_response = PAUSE_RESPONSE_EXTRUDE_MORE; // Simulate menu selection (menu exits, doesn't extrude more)
|
||||
#endif
|
||||
break;
|
||||
|
||||
case 1: // "Continue" / "Disable Runout" button
|
||||
#if ALL(M600_PURGE_MORE_RESUMABLE, ADVANCED_PAUSE_FEATURE)
|
||||
pause_menu_response = PAUSE_RESPONSE_RESUME_PRINT; // Simulate menu selection
|
||||
#endif
|
||||
#if HAS_FILAMENT_SENSOR
|
||||
if (runout.filament_ran_out) { // Disable a triggered sensor
|
||||
runout.enabled = false;
|
||||
runout.reset();
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case PROMPT_USER_CONTINUE:
|
||||
TERN_(HAS_RESUME_CONTINUE, wait_for_user = false);
|
||||
break;
|
||||
case PROMPT_PAUSE_RESUME:
|
||||
#if ALL(ADVANCED_PAUSE_FEATURE, HAS_MEDIA)
|
||||
extern const char M24_STR[];
|
||||
queue.inject_P(M24_STR);
|
||||
#endif
|
||||
break;
|
||||
case PROMPT_INFO:
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HOST_PROMPT_SUPPORT
|
||||
|
||||
#endif // HOST_ACTION_COMMANDS
|
||||
131
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/host_actions.h
Normal file
131
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/host_actions.h
Normal file
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* 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"
|
||||
#include "../HAL/shared/Marduino.h"
|
||||
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
|
||||
enum PromptReason : uint8_t {
|
||||
PROMPT_NOT_DEFINED,
|
||||
PROMPT_FILAMENT_RUNOUT,
|
||||
PROMPT_USER_CONTINUE,
|
||||
PROMPT_FILAMENT_RUNOUT_REHEAT,
|
||||
PROMPT_PAUSE_RESUME,
|
||||
PROMPT_INFO
|
||||
};
|
||||
|
||||
extern const char CONTINUE_STR[], DISMISS_STR[];
|
||||
|
||||
#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(const bool pgm, FSTR_P const ptype, const char * const str, const char extra_char='\0');
|
||||
static void prompt_plus(FSTR_P const ptype, FSTR_P const fstr, const char extra_char='\0') {
|
||||
prompt_plus(true, ptype, FTOP(fstr), extra_char);
|
||||
}
|
||||
static void prompt_plus(FSTR_P const ptype, const char * const cstr, const char extra_char='\0') {
|
||||
prompt_plus(false, ptype, cstr, extra_char);
|
||||
}
|
||||
|
||||
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_begin(const PromptReason reason, const char * const cstr, const char extra_char='\0');
|
||||
static void prompt_end();
|
||||
|
||||
static void prompt_button(FSTR_P const fstr);
|
||||
static void prompt_button(const char * const cstr);
|
||||
|
||||
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, const char * const cstr, 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_do(const PromptReason reason, const char * const cstr, const char extra_char, FSTR_P const btn1=nullptr, FSTR_P const btn2=nullptr);
|
||||
|
||||
static void continue_prompt(FSTR_P const fstr) { prompt_do(PROMPT_USER_CONTINUE, fstr, FPSTR(CONTINUE_STR)); }
|
||||
static void continue_prompt(const char * const cstr) { prompt_do(PROMPT_USER_CONTINUE, cstr, FPSTR(CONTINUE_STR)); }
|
||||
|
||||
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;
|
||||
@@ -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"
|
||||
|
||||
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
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* 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 "../core/millis_t.h"
|
||||
|
||||
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();
|
||||
};
|
||||
|
||||
extern HotendIdleProtection hotend_idle;
|
||||
184
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/joystick.cpp
Normal file
184
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/joystick.cpp
Normal file
@@ -0,0 +1,184 @@
|
||||
/**
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* joystick.cpp - joystick input / jogging
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
|
||||
#if ENABLED(JOYSTICK)
|
||||
|
||||
#include "joystick.h"
|
||||
|
||||
#include "../inc/MarlinConfig.h" // for pins
|
||||
#include "../module/planner.h"
|
||||
|
||||
Joystick joystick;
|
||||
|
||||
#if ENABLED(EXTENSIBLE_UI)
|
||||
#include "../lcd/extui/ui_api.h"
|
||||
#endif
|
||||
|
||||
#if HAS_JOY_ADC_X
|
||||
temp_info_t Joystick::x; // = { 0 }
|
||||
#if ENABLED(INVERT_JOY_X)
|
||||
#define JOY_X(N) (16383 - (N))
|
||||
#else
|
||||
#define JOY_X(N) (N)
|
||||
#endif
|
||||
#endif
|
||||
#if HAS_JOY_ADC_Y
|
||||
temp_info_t Joystick::y; // = { 0 }
|
||||
#if ENABLED(INVERT_JOY_Y)
|
||||
#define JOY_Y(N) (16383 - (N))
|
||||
#else
|
||||
#define JOY_Y(N) (N)
|
||||
#endif
|
||||
#endif
|
||||
#if HAS_JOY_ADC_Z
|
||||
temp_info_t Joystick::z; // = { 0 }
|
||||
#if ENABLED(INVERT_JOY_Z)
|
||||
#define JOY_Z(N) (16383 - (N))
|
||||
#else
|
||||
#define JOY_Z(N) (N)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if ENABLED(JOYSTICK_DEBUG)
|
||||
void Joystick::report() {
|
||||
SERIAL_ECHOPGM("Joystick");
|
||||
#if HAS_JOY_ADC_X
|
||||
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.getraw()));
|
||||
#endif
|
||||
#if HAS_JOY_ADC_Z
|
||||
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)");
|
||||
#endif
|
||||
SERIAL_EOL();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAS_JOY_ADC_X || HAS_JOY_ADC_Y || HAS_JOY_ADC_Z
|
||||
|
||||
void Joystick::calculate(xyz_float_t &norm_jog) {
|
||||
// Do nothing if enable pin (active-low) is not LOW
|
||||
#if HAS_JOY_ADC_EN
|
||||
if (READ(JOY_EN_PIN)) return;
|
||||
#endif
|
||||
|
||||
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 = 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 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 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 raw_adc_t joy_z_limits[4] = JOY_Z_LIMITS;
|
||||
_normalize_joy(norm_jog.z, JOY_Z(z.getraw()), joy_z_limits);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if ENABLED(POLL_JOG)
|
||||
|
||||
void Joystick::inject_jog_moves() {
|
||||
// Recursion barrier
|
||||
static bool injecting_now; // = false;
|
||||
if (injecting_now) return;
|
||||
|
||||
#if ENABLED(NO_MOTION_BEFORE_HOMING)
|
||||
if (TERN0(HAS_JOY_ADC_X, axis_should_home(X_AXIS)) || TERN0(HAS_JOY_ADC_Y, axis_should_home(Y_AXIS)) || TERN0(HAS_JOY_ADC_Z, axis_should_home(Z_AXIS)))
|
||||
return;
|
||||
#endif
|
||||
|
||||
static constexpr int QUEUE_DEPTH = 5; // Insert up to this many movements
|
||||
static constexpr float target_lag = 0.25f, // Aim for 1/4 second lag
|
||||
seg_time = target_lag / QUEUE_DEPTH; // 0.05 seconds, short segments inserted every 1/20th of a second
|
||||
static constexpr millis_t timer_limit_ms = millis_t(seg_time * 500); // 25 ms minimum delay between insertions
|
||||
|
||||
// The planner can merge/collapse small moves, so the movement queue is unreliable to control the lag
|
||||
static millis_t next_run = 0;
|
||||
if (PENDING(millis(), next_run)) return;
|
||||
next_run = millis() + timer_limit_ms;
|
||||
|
||||
// Only inject a command if the planner has fewer than 5 moves and there are no unparsed commands
|
||||
if (planner.movesplanned() >= QUEUE_DEPTH || queue.has_commands_queued())
|
||||
return;
|
||||
|
||||
// Normalized jog values are 0 for no movement and -1 or +1 for as max feedrate (nonlinear relationship)
|
||||
// Jog are initialized to zero and handling input can update values but doesn't have to
|
||||
// You could use a two-axis joystick and a one-axis keypad and they might work together
|
||||
xyz_float_t norm_jog{0};
|
||||
|
||||
// Use ADC values and defined limits. The active zone is normalized: -1..0 (dead) 0..1
|
||||
#if HAS_JOY_ADC_X || HAS_JOY_ADC_Y || HAS_JOY_ADC_Z
|
||||
joystick.calculate(norm_jog);
|
||||
#endif
|
||||
|
||||
// Other non-joystick poll-based jogging could be implemented here
|
||||
// with "jogging" encapsulated as a more general class.
|
||||
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::_joystick_update(norm_jog));
|
||||
|
||||
// norm_jog values of [-1 .. 1] maps linearly to [-feedrate .. feedrate]
|
||||
xyz_float_t move_dist{0};
|
||||
float hypot2 = 0;
|
||||
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]);
|
||||
}
|
||||
|
||||
if (!UNEAR_ZERO(hypot2)) {
|
||||
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, hints);
|
||||
injecting_now = false;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // POLL_JOG
|
||||
|
||||
#endif // JOYSTICK
|
||||
52
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/joystick.h
Normal file
52
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/joystick.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
/**
|
||||
* joystick.h - joystick input / jogging
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
#include "../core/types.h"
|
||||
#include "../module/temperature.h"
|
||||
|
||||
class Joystick {
|
||||
friend class Temperature;
|
||||
private:
|
||||
#if HAS_JOY_ADC_X
|
||||
static temp_info_t x;
|
||||
#endif
|
||||
#if HAS_JOY_ADC_Y
|
||||
static temp_info_t y;
|
||||
#endif
|
||||
#if HAS_JOY_ADC_Z
|
||||
static temp_info_t z;
|
||||
#endif
|
||||
public:
|
||||
#if ENABLED(JOYSTICK_DEBUG)
|
||||
static void report();
|
||||
#endif
|
||||
static void calculate(xyz_float_t &norm_jog);
|
||||
static void inject_jog_moves();
|
||||
};
|
||||
|
||||
extern Joystick joystick;
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* blinkm.cpp - Control a BlinkM over i2c
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(BLINKM)
|
||||
|
||||
#include "blinkm.h"
|
||||
#include "leds.h"
|
||||
#include <Wire.h>
|
||||
|
||||
void blinkm_set_led_color(const LEDColor &color) {
|
||||
Wire.begin();
|
||||
Wire.beginTransmission(I2C_ADDRESS(0x09));
|
||||
Wire.write('o'); //to disable ongoing script, only needs to be used once
|
||||
Wire.write('n');
|
||||
Wire.write(color.r);
|
||||
Wire.write(color.g);
|
||||
Wire.write(color.b);
|
||||
Wire.endTransmission();
|
||||
}
|
||||
|
||||
#endif // BLINKM
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
/**
|
||||
* blinkm.h - Control a BlinkM over i2c
|
||||
*/
|
||||
|
||||
struct LEDColor;
|
||||
typedef LEDColor LEDColor;
|
||||
|
||||
void blinkm_set_led_color(const LEDColor &color);
|
||||
300
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/leds/leds.cpp
Normal file
300
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/leds/leds.cpp
Normal file
@@ -0,0 +1,300 @@
|
||||
/**
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* leds.cpp - Marlin RGB LED general support
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if HAS_COLOR_LEDS
|
||||
|
||||
#include "leds.h"
|
||||
|
||||
#if ANY(CASE_LIGHT_USE_RGB_LED, CASE_LIGHT_USE_NEOPIXEL)
|
||||
#include "../../feature/caselight.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(LED_COLOR_PRESETS)
|
||||
const LEDColor LEDLights::defaultLEDColor = LEDColor(
|
||||
LED_USER_PRESET_RED, LED_USER_PRESET_GREEN, LED_USER_PRESET_BLUE
|
||||
OPTARG(HAS_WHITE_LED, LED_USER_PRESET_WHITE)
|
||||
OPTARG(NEOPIXEL_LED, LED_USER_PRESET_BRIGHTNESS)
|
||||
);
|
||||
#endif
|
||||
|
||||
#if ANY(LED_CONTROL_MENU, PRINTER_EVENT_LEDS, CASE_LIGHT_IS_COLOR_LED)
|
||||
LEDColor LEDLights::color;
|
||||
bool LEDLights::lights_on;
|
||||
#endif
|
||||
|
||||
LEDLights leds;
|
||||
|
||||
void LEDLights::setup() {
|
||||
#if ANY(RGB_LED, RGBW_LED)
|
||||
if (PWM_PIN(RGB_LED_R_PIN)) SET_PWM(RGB_LED_R_PIN); else SET_OUTPUT(RGB_LED_R_PIN);
|
||||
if (PWM_PIN(RGB_LED_G_PIN)) SET_PWM(RGB_LED_G_PIN); else SET_OUTPUT(RGB_LED_G_PIN);
|
||||
if (PWM_PIN(RGB_LED_B_PIN)) SET_PWM(RGB_LED_B_PIN); else SET_OUTPUT(RGB_LED_B_PIN);
|
||||
#if ENABLED(RGBW_LED)
|
||||
if (PWM_PIN(RGB_LED_W_PIN)) SET_PWM(RGB_LED_W_PIN); else SET_OUTPUT(RGB_LED_W_PIN);
|
||||
#endif
|
||||
|
||||
#if ENABLED(RGB_STARTUP_TEST)
|
||||
int8_t led_pin_count = 0;
|
||||
if (PWM_PIN(RGB_LED_R_PIN) && PWM_PIN(RGB_LED_G_PIN) && PWM_PIN(RGB_LED_B_PIN)) led_pin_count = 3;
|
||||
#if ENABLED(RGBW_LED)
|
||||
if (PWM_PIN(RGB_LED_W_PIN) && led_pin_count) led_pin_count++;
|
||||
#endif
|
||||
// Startup animation
|
||||
if (led_pin_count) {
|
||||
// blackout
|
||||
if (PWM_PIN(RGB_LED_R_PIN)) hal.set_pwm_duty(pin_t(RGB_LED_R_PIN), 0); else WRITE(RGB_LED_R_PIN, LOW);
|
||||
if (PWM_PIN(RGB_LED_G_PIN)) hal.set_pwm_duty(pin_t(RGB_LED_G_PIN), 0); else WRITE(RGB_LED_G_PIN, LOW);
|
||||
if (PWM_PIN(RGB_LED_B_PIN)) hal.set_pwm_duty(pin_t(RGB_LED_B_PIN), 0); else WRITE(RGB_LED_B_PIN, LOW);
|
||||
#if ENABLED(RGBW_LED)
|
||||
if (PWM_PIN(RGB_LED_W_PIN)) hal.set_pwm_duty(pin_t(RGB_LED_W_PIN), 0);
|
||||
else WRITE(RGB_LED_W_PIN, LOW);
|
||||
#endif
|
||||
delay(200);
|
||||
|
||||
for (uint8_t i = 0; i < led_pin_count; ++i) {
|
||||
for (uint8_t b = 0; b <= 200; ++b) {
|
||||
const uint16_t led_pwm = b <= 100 ? b : 200 - b;
|
||||
if (i == 0 && PWM_PIN(RGB_LED_R_PIN)) hal.set_pwm_duty(pin_t(RGB_LED_R_PIN), led_pwm); else WRITE(RGB_LED_R_PIN, b < 100 ? HIGH : LOW);
|
||||
if (i == 1 && PWM_PIN(RGB_LED_G_PIN)) hal.set_pwm_duty(pin_t(RGB_LED_G_PIN), led_pwm); else WRITE(RGB_LED_G_PIN, b < 100 ? HIGH : LOW);
|
||||
if (i == 2 && PWM_PIN(RGB_LED_B_PIN)) hal.set_pwm_duty(pin_t(RGB_LED_B_PIN), led_pwm); else WRITE(RGB_LED_B_PIN, b < 100 ? HIGH : LOW);
|
||||
#if ENABLED(RGBW_LED)
|
||||
if (i == 3){
|
||||
if (PWM_PIN(RGB_LED_W_PIN)) hal.set_pwm_duty(pin_t(RGB_LED_W_PIN), led_pwm);
|
||||
else WRITE(RGB_LED_W_PIN, b < 100 ? HIGH : LOW);
|
||||
delay(RGB_STARTUP_TEST_INNER_MS);//More slowing for ending
|
||||
}
|
||||
#endif
|
||||
delay(RGB_STARTUP_TEST_INNER_MS);
|
||||
}
|
||||
}
|
||||
delay(500);
|
||||
}
|
||||
#endif // RGB_STARTUP_TEST
|
||||
|
||||
#elif ALL(PCA9632, RGB_STARTUP_TEST) // PCA9632 RGB_STARTUP_TEST
|
||||
|
||||
constexpr int8_t led_pin_count = TERN(HAS_WHITE_LED, 4, 3);
|
||||
|
||||
// Startup animation
|
||||
LEDColor curColor = LEDColorOff();
|
||||
PCA9632_set_led_color(curColor); // blackout
|
||||
delay(200);
|
||||
|
||||
/**
|
||||
* LED Pin Counter steps -> events
|
||||
* | 0-100 | 100-200 | 200-300 | 300-400 |
|
||||
* fade in steady | fade out
|
||||
* start next pin fade in
|
||||
*/
|
||||
|
||||
uint16_t led_pin_counters[led_pin_count] = { 1, 0, 0 };
|
||||
|
||||
bool canEnd = false;
|
||||
while (led_pin_counters[0] != 99 || !canEnd) {
|
||||
if (led_pin_counters[0] == 99) // End loop next time pin0 counter is 99
|
||||
canEnd = true;
|
||||
for (uint8_t i = 0; i < led_pin_count; ++i) {
|
||||
if (led_pin_counters[i] > 0) {
|
||||
if (++led_pin_counters[i] == 400) // turn off current pin counter in led_pin_counters
|
||||
led_pin_counters[i] = 0;
|
||||
else if (led_pin_counters[i] == 201) { // start next pin pwm
|
||||
led_pin_counters[i + 1 == led_pin_count ? 0 : i + 1] = 1;
|
||||
i++; // skip next pin in this loop so it doesn't increment twice
|
||||
}
|
||||
}
|
||||
}
|
||||
uint16_t r, g, b;
|
||||
r = led_pin_counters[0]; curColor.r = r <= 100 ? r : r <= 300 ? 100 : 400 - r;
|
||||
g = led_pin_counters[1]; curColor.g = g <= 100 ? g : g <= 300 ? 100 : 400 - g;
|
||||
b = led_pin_counters[2]; curColor.b = b <= 100 ? b : b <= 300 ? 100 : 400 - b;
|
||||
#if HAS_WHITE_LED
|
||||
const uint16_t w = led_pin_counters[3]; curColor.w = w <= 100 ? w : w <= 300 ? 100 : 400 - w;
|
||||
#endif
|
||||
PCA9632_set_led_color(curColor);
|
||||
delay(RGB_STARTUP_TEST_INNER_MS);
|
||||
}
|
||||
|
||||
// Fade to white
|
||||
for (uint8_t led_pwm = 0; led_pwm <= 100; ++led_pwm) {
|
||||
NOLESS(curColor.r, led_pwm);
|
||||
NOLESS(curColor.g, led_pwm);
|
||||
NOLESS(curColor.b, led_pwm);
|
||||
TERN_(HAS_WHITE_LED, NOLESS(curColor.w, led_pwm));
|
||||
PCA9632_set_led_color(curColor);
|
||||
delay(RGB_STARTUP_TEST_INNER_MS);
|
||||
}
|
||||
|
||||
#endif // PCA9632 && RGB_STARTUP_TEST
|
||||
|
||||
TERN_(NEOPIXEL_LED, neo.init());
|
||||
TERN_(PCA9533, PCA9533_init());
|
||||
TERN_(LED_USER_PRESET_STARTUP, set_default());
|
||||
}
|
||||
|
||||
void LEDLights::set_color(const LEDColor &incol
|
||||
OPTARG(NEOPIXEL_IS_SEQUENTIAL, bool isSequence/*=false*/)
|
||||
) {
|
||||
|
||||
#if ENABLED(NEOPIXEL_LED)
|
||||
|
||||
const uint32_t neocolor = LEDColorWhite() == incol
|
||||
? neo.Color(NEO_WHITE)
|
||||
: neo.Color(incol.r, incol.g, incol.b OPTARG(HAS_WHITE_LED, incol.w));
|
||||
|
||||
#if ENABLED(NEOPIXEL_IS_SEQUENTIAL)
|
||||
static uint16_t nextLed = 0;
|
||||
#ifdef NEOPIXEL_BKGD_INDEX_FIRST
|
||||
while (WITHIN(nextLed, NEOPIXEL_BKGD_INDEX_FIRST, NEOPIXEL_BKGD_INDEX_LAST)) {
|
||||
neo.reset_background_color();
|
||||
if (++nextLed >= neo.pixels()) { nextLed = 0; return; }
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if ALL(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)
|
||||
if (isSequence) {
|
||||
neo.set_pixel_color(nextLed, neocolor);
|
||||
neo.show();
|
||||
if (++nextLed >= neo.pixels()) nextLed = 0;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ALL(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
|
||||
|
||||
#if ENABLED(BLINKM)
|
||||
|
||||
// This variant uses i2c to send the RGB components to the device.
|
||||
blinkm_set_led_color(incol);
|
||||
|
||||
#endif
|
||||
|
||||
#if ANY(RGB_LED, RGBW_LED)
|
||||
|
||||
// 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)) \
|
||||
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);
|
||||
#if ENABLED(RGBW_LED)
|
||||
UPDATE_RGBW(W,w);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
// Update I2C LED driver
|
||||
TERN_(PCA9632, PCA9632_set_led_color(incol));
|
||||
TERN_(PCA9533, PCA9533_set_rgb(incol.r, incol.g, incol.b));
|
||||
|
||||
#if ANY(LED_CONTROL_MENU, PRINTER_EVENT_LEDS)
|
||||
// Don't update the color when OFF
|
||||
lights_on = !incol.is_off();
|
||||
if (lights_on) color = incol;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ENABLED(LED_CONTROL_MENU)
|
||||
void LEDLights::toggle() { if (lights_on) set_off(); else update(); }
|
||||
#endif
|
||||
|
||||
#if LED_POWEROFF_TIMEOUT > 0
|
||||
|
||||
millis_t LEDLights::led_off_time; // = 0
|
||||
|
||||
void LEDLights::update_timeout(const bool power_on) {
|
||||
if (lights_on) {
|
||||
const millis_t ms = millis();
|
||||
if (power_on)
|
||||
reset_timeout(ms);
|
||||
else if (ELAPSED(ms, led_off_time))
|
||||
set_off();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if ENABLED(NEOPIXEL2_SEPARATE)
|
||||
|
||||
#if ENABLED(NEO2_COLOR_PRESETS)
|
||||
const LEDColor LEDLights2::defaultLEDColor = LEDColor(
|
||||
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
|
||||
|
||||
#if ENABLED(LED_CONTROL_MENU)
|
||||
LEDColor LEDLights2::color;
|
||||
bool LEDLights2::lights_on;
|
||||
#endif
|
||||
|
||||
LEDLights2 leds2;
|
||||
|
||||
void LEDLights2::setup() {
|
||||
neo2.init();
|
||||
TERN_(NEO2_USER_PRESET_STARTUP, set_default());
|
||||
}
|
||||
|
||||
void LEDLights2::set_color(const LEDColor &incol) {
|
||||
const uint32_t neocolor = LEDColorWhite() == incol
|
||||
? neo2.Color(NEO2_WHITE)
|
||||
: neo2.Color(incol.r, incol.g, incol.b OPTARG(HAS_WHITE_LED2, incol.w));
|
||||
neo2.set_brightness(incol.i);
|
||||
neo2.set_color(neocolor);
|
||||
|
||||
#if ENABLED(LED_CONTROL_MENU)
|
||||
// Don't update the color when OFF
|
||||
lights_on = !incol.is_off();
|
||||
if (lights_on) color = incol;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ENABLED(LED_CONTROL_MENU)
|
||||
void LEDLights2::toggle() { if (lights_on) set_off(); else update(); }
|
||||
#endif
|
||||
|
||||
#endif // NEOPIXEL2_SEPARATE
|
||||
|
||||
#endif // HAS_COLOR_LEDS
|
||||
230
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/leds/leds.h
Normal file
230
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/leds/leds.h
Normal file
@@ -0,0 +1,230 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
/**
|
||||
* leds.h - Marlin general RGB LED support
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfigPre.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
// A white component can be passed
|
||||
#if ANY(RGBW_LED, PCA9632_RGBW)
|
||||
#define HAS_WHITE_LED 1
|
||||
#endif
|
||||
|
||||
#if ENABLED(NEOPIXEL_LED)
|
||||
#define _NEOPIXEL_INCLUDE_
|
||||
#include "neopixel.h"
|
||||
#undef _NEOPIXEL_INCLUDE_
|
||||
#endif
|
||||
|
||||
#if ENABLED(BLINKM)
|
||||
#include "blinkm.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(PCA9533)
|
||||
#include "pca9533.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(PCA9632)
|
||||
#include "pca9632.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* LEDcolor type for use with leds.set_color
|
||||
*/
|
||||
typedef struct LEDColor {
|
||||
uint8_t r, g, b
|
||||
OPTARG(HAS_WHITE_LED, w)
|
||||
OPTARG(NEOPIXEL_LED, i)
|
||||
;
|
||||
|
||||
LEDColor() : r(255), g(255), b(255)
|
||||
OPTARG(HAS_WHITE_LED, w(255))
|
||||
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)) {}
|
||||
|
||||
LEDColor(const uint8_t (&rgbw)[4]) : r(rgbw[0]), g(rgbw[1]), b(rgbw[2])
|
||||
OPTARG(HAS_WHITE_LED, w(rgbw[3]))
|
||||
OPTARG(NEOPIXEL_LED, i(NEOPIXEL_BRIGHTNESS))
|
||||
{}
|
||||
|
||||
LEDColor& operator=(const uint8_t (&rgbw)[4]) {
|
||||
r = rgbw[0]; g = rgbw[1]; b = rgbw[2];
|
||||
TERN_(HAS_WHITE_LED, w = rgbw[3]);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const LEDColor &right) {
|
||||
if (this == &right) return true;
|
||||
return 0 == memcmp(this, &right, sizeof(LEDColor));
|
||||
}
|
||||
|
||||
bool operator!=(const LEDColor &right) { return !operator==(right); }
|
||||
|
||||
bool is_off() const {
|
||||
return 3 > r + g + b + TERN0(HAS_WHITE_LED, w);
|
||||
}
|
||||
} LEDColor;
|
||||
|
||||
/**
|
||||
* Color presets
|
||||
*/
|
||||
|
||||
#define LEDColorOff() LEDColor( 0, 0, 0)
|
||||
#define LEDColorRed() LEDColor(255, 0, 0)
|
||||
#if ENABLED(LED_COLORS_REDUCE_GREEN)
|
||||
#define LEDColorOrange() LEDColor(255, 25, 0)
|
||||
#define LEDColorYellow() LEDColor(255, 75, 0)
|
||||
#else
|
||||
#define LEDColorOrange() LEDColor(255, 80, 0)
|
||||
#define LEDColorYellow() LEDColor(255, 255, 0)
|
||||
#endif
|
||||
#define LEDColorGreen() LEDColor( 0, 255, 0)
|
||||
#define LEDColorBlue() LEDColor( 0, 0, 255)
|
||||
#define LEDColorIndigo() LEDColor( 0, 255, 255)
|
||||
#define LEDColorViolet() LEDColor(255, 0, 255)
|
||||
#if HAS_WHITE_LED && DISABLED(RGB_LED)
|
||||
#define LEDColorWhite() LEDColor( 0, 0, 0, 255)
|
||||
#else
|
||||
#define LEDColorWhite() LEDColor(255, 255, 255)
|
||||
#endif
|
||||
|
||||
class LEDLights {
|
||||
public:
|
||||
#if ANY(LED_CONTROL_MENU, PRINTER_EVENT_LEDS, CASE_LIGHT_IS_COLOR_LED)
|
||||
static LEDColor color; // last non-off color
|
||||
static bool lights_on; // the last set color was "on"
|
||||
#else
|
||||
static constexpr bool lights_on = true;
|
||||
#endif
|
||||
|
||||
LEDLights() {} // ctor
|
||||
|
||||
static void setup(); // init()
|
||||
|
||||
static void set_color(const LEDColor &color
|
||||
OPTARG(NEOPIXEL_IS_SEQUENTIAL, bool isSequence=false)
|
||||
);
|
||||
|
||||
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)
|
||||
) {
|
||||
set_color(LEDColor(r, g, b OPTARG(HAS_WHITE_LED, w) OPTARG(NEOPIXEL_LED, i)) OPTARG(NEOPIXEL_IS_SEQUENTIAL, isSequence));
|
||||
}
|
||||
|
||||
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 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 LEDColor get_color() { return lights_on ? color : LEDColorOff(); }
|
||||
#endif
|
||||
|
||||
#if ENABLED(LED_CONTROL_MENU)
|
||||
static void toggle(); // swap "off" with color
|
||||
#endif
|
||||
#if ANY(LED_CONTROL_MENU, CASE_LIGHT_USE_RGB_LED) || LED_POWEROFF_TIMEOUT > 0
|
||||
static void update() { set_color(color); }
|
||||
#endif
|
||||
|
||||
#if LED_POWEROFF_TIMEOUT > 0
|
||||
private:
|
||||
static millis_t led_off_time;
|
||||
public:
|
||||
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);
|
||||
#endif
|
||||
};
|
||||
|
||||
extern LEDLights leds;
|
||||
|
||||
#if ENABLED(NEOPIXEL2_SEPARATE)
|
||||
|
||||
class LEDLights2 {
|
||||
public:
|
||||
LEDLights2() {}
|
||||
|
||||
static void setup(); // init()
|
||||
|
||||
static void set_color(const LEDColor &color);
|
||||
|
||||
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)
|
||||
) {
|
||||
set_color(LEDColor(r, g, b
|
||||
OPTARG(HAS_WHITE_LED, w)
|
||||
OPTARG(NEOPIXEL_LED, i)
|
||||
));
|
||||
}
|
||||
|
||||
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 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 void update() { set_color(color); }
|
||||
#endif
|
||||
};
|
||||
|
||||
extern LEDLights2 leds2;
|
||||
|
||||
#endif // NEOPIXEL2_SEPARATE
|
||||
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Marlin RGB LED general support
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(NEOPIXEL_LED)
|
||||
|
||||
#include "leds.h"
|
||||
|
||||
#if ANY(NEOPIXEL_STARTUP_TEST, NEOPIXEL2_STARTUP_TEST)
|
||||
#include "../../core/utility.h"
|
||||
#endif
|
||||
|
||||
Marlin_NeoPixel neo;
|
||||
pixel_index_t Marlin_NeoPixel::neoindex;
|
||||
|
||||
Adafruit_NeoPixel Marlin_NeoPixel::adaneo1(NEOPIXEL_PIXELS, NEOPIXEL_PIN, NEOPIXEL_TYPE + NEO_KHZ800);
|
||||
#if CONJOINED_NEOPIXEL
|
||||
Adafruit_NeoPixel Marlin_NeoPixel::adaneo2(NEOPIXEL_PIXELS, NEOPIXEL2_PIN, NEOPIXEL2_TYPE + NEO_KHZ800);
|
||||
#endif
|
||||
|
||||
#ifdef NEOPIXEL_BKGD_INDEX_FIRST
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#endif // NEOPIXEL_BKGD_INDEX_FIRST
|
||||
|
||||
void Marlin_NeoPixel::set_color(const uint32_t color) {
|
||||
if (neoindex >= 0) {
|
||||
set_pixel_color(neoindex, color);
|
||||
neoindex = -1;
|
||||
}
|
||||
else {
|
||||
for (uint16_t i = 0; i < pixels(); ++i) {
|
||||
#ifdef NEOPIXEL_BKGD_INDEX_FIRST
|
||||
if (i == NEOPIXEL_BKGD_INDEX_FIRST && TERN(NEOPIXEL_BKGD_ALWAYS_ON, true, color != 0x000000)) {
|
||||
reset_background_color();
|
||||
i += NEOPIXEL_BKGD_INDEX_LAST - (NEOPIXEL_BKGD_INDEX_FIRST);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
set_pixel_color(i, color);
|
||||
}
|
||||
}
|
||||
show();
|
||||
}
|
||||
|
||||
void Marlin_NeoPixel::set_color_startup(const uint32_t color) {
|
||||
for (uint16_t i = 0; i < pixels(); ++i)
|
||||
set_pixel_color(i, color);
|
||||
show();
|
||||
}
|
||||
|
||||
void Marlin_NeoPixel::init() {
|
||||
neoindex = -1; // -1 .. NEOPIXEL_PIXELS-1 range
|
||||
set_brightness(NEOPIXEL_BRIGHTNESS); // 0 .. 255 range
|
||||
begin();
|
||||
show(); // initialize to all off
|
||||
|
||||
#if ENABLED(NEOPIXEL_STARTUP_TEST)
|
||||
set_color_startup(adaneo1.Color(255, 0, 0, 0)); // red
|
||||
safe_delay(500);
|
||||
set_color_startup(adaneo1.Color(0, 255, 0, 0)); // green
|
||||
safe_delay(500);
|
||||
set_color_startup(adaneo1.Color(0, 0, 255, 0)); // blue
|
||||
safe_delay(500);
|
||||
#if HAS_WHITE_LED
|
||||
set_color_startup(adaneo1.Color(0, 0, 0, 255)); // white
|
||||
safe_delay(500);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef NEOPIXEL_BKGD_INDEX_FIRST
|
||||
reset_background_color();
|
||||
#endif
|
||||
|
||||
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))
|
||||
);
|
||||
}
|
||||
|
||||
#if ENABLED(NEOPIXEL2_SEPARATE)
|
||||
|
||||
Marlin_NeoPixel2 neo2;
|
||||
|
||||
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) {
|
||||
if (neoindex >= 0) {
|
||||
set_pixel_color(neoindex, color);
|
||||
neoindex = -1;
|
||||
}
|
||||
else {
|
||||
for (uint16_t i = 0; i < pixels(); ++i)
|
||||
set_pixel_color(i, color);
|
||||
}
|
||||
show();
|
||||
}
|
||||
|
||||
void Marlin_NeoPixel2::set_color_startup(const uint32_t color) {
|
||||
for (uint16_t i = 0; i < pixels(); ++i)
|
||||
set_pixel_color(i, color);
|
||||
show();
|
||||
}
|
||||
|
||||
void Marlin_NeoPixel2::init() {
|
||||
neoindex = -1; // -1 .. NEOPIXEL2_PIXELS-1 range
|
||||
set_brightness(NEOPIXEL2_BRIGHTNESS); // 0 .. 255 range
|
||||
begin();
|
||||
show(); // initialize to all off
|
||||
|
||||
#if ENABLED(NEOPIXEL2_STARTUP_TEST)
|
||||
set_color_startup(adaneo.Color(255, 0, 0, 0)); // red
|
||||
safe_delay(500);
|
||||
set_color_startup(adaneo.Color(0, 255, 0, 0)); // green
|
||||
safe_delay(500);
|
||||
set_color_startup(adaneo.Color(0, 0, 255, 0)); // blue
|
||||
safe_delay(500);
|
||||
#if HAS_WHITE_LED2
|
||||
set_color_startup(adaneo.Color(0, 0, 0, 255)); // white
|
||||
safe_delay(500);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
set_color(adaneo.Color
|
||||
TERN(NEO2_USER_PRESET_STARTUP,
|
||||
(NEO2_USER_PRESET_RED, NEO2_USER_PRESET_GREEN, NEO2_USER_PRESET_BLUE, NEO2_USER_PRESET_WHITE),
|
||||
(0, 0, 0, 0))
|
||||
);
|
||||
}
|
||||
|
||||
#endif // NEOPIXEL2_SEPARATE
|
||||
|
||||
#endif // NEOPIXEL_LED
|
||||
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
/**
|
||||
* NeoPixel support
|
||||
*/
|
||||
|
||||
#ifndef _NEOPIXEL_INCLUDE_
|
||||
#error "Always include 'leds.h' and not 'neopixel.h' directly."
|
||||
#endif
|
||||
|
||||
// ------------------------
|
||||
// Includes
|
||||
// ------------------------
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#include <Adafruit_NeoPixel.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// ------------------------
|
||||
// Defines
|
||||
// ------------------------
|
||||
|
||||
#define _NEO_IS_RGB(N) (N == NEO_RGB || N == NEO_RBG || N == NEO_GRB || N == NEO_GBR || N == NEO_BRG || N == NEO_BGR)
|
||||
|
||||
#if !_NEO_IS_RGB(NEOPIXEL_TYPE)
|
||||
#define HAS_WHITE_LED 1
|
||||
#endif
|
||||
|
||||
#if HAS_WHITE_LED
|
||||
#define NEO_WHITE 0, 0, 0, 255
|
||||
#else
|
||||
#define NEO_WHITE 255, 255, 255
|
||||
#endif
|
||||
|
||||
#if defined(NEOPIXEL2_TYPE) && NEOPIXEL2_TYPE != NEOPIXEL_TYPE && DISABLED(NEOPIXEL2_SEPARATE)
|
||||
#define MULTIPLE_NEOPIXEL_TYPES 1
|
||||
#endif
|
||||
|
||||
#if ANY(MULTIPLE_NEOPIXEL_TYPES, NEOPIXEL2_INSERIES)
|
||||
#define CONJOINED_NEOPIXEL 1
|
||||
#endif
|
||||
|
||||
// ------------------------
|
||||
// Types
|
||||
// ------------------------
|
||||
|
||||
typedef value_t(TERN0(NEOPIXEL_LED, NEOPIXEL_PIXELS)) pixel_index_t;
|
||||
|
||||
// ------------------------
|
||||
// Classes
|
||||
// ------------------------
|
||||
|
||||
class Marlin_NeoPixel {
|
||||
private:
|
||||
static Adafruit_NeoPixel adaneo1;
|
||||
#if CONJOINED_NEOPIXEL
|
||||
static Adafruit_NeoPixel adaneo2;
|
||||
#endif
|
||||
|
||||
public:
|
||||
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);
|
||||
|
||||
#ifdef NEOPIXEL_BKGD_INDEX_FIRST
|
||||
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 void begin() {
|
||||
adaneo1.begin();
|
||||
TERN_(CONJOINED_NEOPIXEL, adaneo2.begin());
|
||||
}
|
||||
|
||||
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);
|
||||
#else
|
||||
adaneo1.setPixelColor(n, c);
|
||||
TERN_(MULTIPLE_NEOPIXEL_TYPES, adaneo2.setPixelColor(n, c));
|
||||
#endif
|
||||
}
|
||||
|
||||
static void set_brightness(const uint8_t b) {
|
||||
adaneo1.setBrightness(b);
|
||||
TERN_(CONJOINED_NEOPIXEL, adaneo2.setBrightness(b));
|
||||
}
|
||||
|
||||
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();
|
||||
#if PIN_EXISTS(NEOPIXEL2)
|
||||
#if CONJOINED_NEOPIXEL
|
||||
adaneo2.show();
|
||||
#else
|
||||
adaneo1.show();
|
||||
adaneo1.setPin(NEOPIXEL_PIN);
|
||||
#endif
|
||||
#endif
|
||||
TERN_(HAS_PAUSE_SERVO_OUTPUT, RESUME_SERVO_OUTPUT());
|
||||
}
|
||||
|
||||
// Accessors
|
||||
static uint16_t pixels() { return adaneo1.numPixels() * TERN1(NEOPIXEL2_INSERIES, 2); }
|
||||
|
||||
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 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));
|
||||
}
|
||||
};
|
||||
|
||||
extern Marlin_NeoPixel neo;
|
||||
|
||||
// Neo pixel channel 2
|
||||
#if ENABLED(NEOPIXEL2_SEPARATE)
|
||||
|
||||
#if _NEO_IS_RGB(NEOPIXEL2_TYPE)
|
||||
#define NEOPIXEL2_IS_RGB 1
|
||||
#define NEO2_WHITE 255, 255, 255
|
||||
#else
|
||||
#define NEOPIXEL2_IS_RGBW 1
|
||||
#define HAS_WHITE_LED2 1 // A white component can be passed for NEOPIXEL2
|
||||
#define NEO2_WHITE 0, 0, 0, 255
|
||||
#endif
|
||||
|
||||
class Marlin_NeoPixel2 {
|
||||
private:
|
||||
static Adafruit_NeoPixel adaneo;
|
||||
|
||||
public:
|
||||
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 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 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));
|
||||
}
|
||||
};
|
||||
|
||||
extern Marlin_NeoPixel2 neo2;
|
||||
|
||||
#endif // NEOPIXEL2_SEPARATE
|
||||
|
||||
#undef _NEO_IS_RGB
|
||||
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* PCA9533 LED controller driver (MightyBoard, FlashForge Creator Pro, etc.)
|
||||
* by @grauerfuchs - 1 Apr 2020
|
||||
*/
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(PCA9533)
|
||||
|
||||
#include "pca9533.h"
|
||||
#include <Wire.h>
|
||||
|
||||
void PCA9533_init() {
|
||||
Wire.begin();
|
||||
PCA9533_reset();
|
||||
}
|
||||
|
||||
static void PCA9533_writeAllRegisters(uint8_t psc0, uint8_t pwm0, uint8_t psc1, uint8_t pwm1, uint8_t ls0) {
|
||||
uint8_t data[6] = { PCA9533_REG_PSC0 | PCA9533_REGM_AI, psc0, pwm0, psc1, pwm1, ls0 };
|
||||
Wire.beginTransmission(PCA9533_Addr >> 1);
|
||||
Wire.write(data, 6);
|
||||
Wire.endTransmission();
|
||||
delayMicroseconds(1);
|
||||
}
|
||||
|
||||
static void PCA9533_writeRegister(uint8_t reg, uint8_t val) {
|
||||
uint8_t data[2] = { reg, val };
|
||||
Wire.beginTransmission(PCA9533_Addr >> 1);
|
||||
Wire.write(data, 2);
|
||||
Wire.endTransmission();
|
||||
delayMicroseconds(1);
|
||||
}
|
||||
|
||||
// Reset (clear) all registers
|
||||
void PCA9533_reset() {
|
||||
PCA9533_writeAllRegisters(0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
// Turn all LEDs off
|
||||
void PCA9533_setOff() {
|
||||
PCA9533_writeRegister(PCA9533_REG_SEL, 0);
|
||||
}
|
||||
|
||||
void PCA9533_set_rgb(uint8_t red, uint8_t green, uint8_t blue) {
|
||||
uint8_t r_pwm0 = 0; // Register data - PWM value
|
||||
uint8_t r_pwm1 = 0; // Register data - PWM value
|
||||
|
||||
uint8_t op_g = 0, op_r = 0, op_b = 0; // Opcodes - Green, Red, Blue
|
||||
|
||||
// Light theory! GREEN takes priority because
|
||||
// it's the most visible to the human eye.
|
||||
if (green == 0) op_g = PCA9533_LED_OP_OFF;
|
||||
else if (green == 255) op_g = PCA9533_LED_OP_ON;
|
||||
else { r_pwm0 = green; op_g = PCA9533_LED_OP_PWM0; }
|
||||
|
||||
// RED
|
||||
if (red == 0) op_r = PCA9533_LED_OP_OFF;
|
||||
else if (red == 255) op_r = PCA9533_LED_OP_ON;
|
||||
else if (r_pwm0 == 0 || r_pwm0 == red) {
|
||||
r_pwm0 = red; op_r = PCA9533_LED_OP_PWM0;
|
||||
}
|
||||
else {
|
||||
r_pwm1 = red; op_r = PCA9533_LED_OP_PWM1;
|
||||
}
|
||||
|
||||
// BLUE
|
||||
if (blue == 0) op_b = PCA9533_LED_OP_OFF;
|
||||
else if (blue == 255) op_b = PCA9533_LED_OP_ON;
|
||||
else if (r_pwm0 == 0 || r_pwm0 == blue) {
|
||||
r_pwm0 = blue; op_b = PCA9533_LED_OP_PWM0;
|
||||
}
|
||||
else if (r_pwm1 == 0 || r_pwm1 == blue) {
|
||||
r_pwm1 = blue; op_b = PCA9533_LED_OP_PWM1;
|
||||
}
|
||||
else {
|
||||
/**
|
||||
* Conflict. 3 values are requested but only 2 channels exist.
|
||||
* G is on channel 0 and R is on channel 1, so work from there.
|
||||
* Find the closest match, average the values, then use the free channel.
|
||||
*/
|
||||
uint8_t dgb = ABS(green - blue),
|
||||
dgr = ABS(green - red),
|
||||
dbr = ABS(blue - red);
|
||||
if (dgb < dgr && dgb < dbr) { // Mix with G on channel 0.
|
||||
op_b = PCA9533_LED_OP_PWM0;
|
||||
r_pwm0 = uint8_t(((uint16_t)green + (uint16_t)blue) / 2);
|
||||
}
|
||||
else if (dbr <= dgr && dbr <= dgb) { // Mix with R on channel 1.
|
||||
op_b = PCA9533_LED_OP_PWM1;
|
||||
r_pwm1 = uint8_t(((uint16_t)red + (uint16_t)blue) / 2);
|
||||
}
|
||||
else { // Mix R+G on 0 and put B on 1.
|
||||
op_r = PCA9533_LED_OP_PWM0;
|
||||
r_pwm0 = uint8_t(((uint16_t)green + (uint16_t)red) / 2);
|
||||
op_b = PCA9533_LED_OP_PWM1;
|
||||
r_pwm1 = blue;
|
||||
}
|
||||
}
|
||||
|
||||
// Write the changes to the hardware
|
||||
PCA9533_writeAllRegisters(0, r_pwm0, 0, r_pwm1,
|
||||
(op_g << PCA9533_LED_OFS_GRN) | (op_r << PCA9533_LED_OFS_RED) | (op_b << PCA9533_LED_OFS_BLU)
|
||||
);
|
||||
}
|
||||
|
||||
#endif // PCA9533
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
/**
|
||||
* Driver for the PCA9533 LED controller found on the MightyBoard
|
||||
* used by FlashForge Creator Pro, MakerBot, etc.
|
||||
* Written 2020 APR 01 by grauerfuchs
|
||||
*/
|
||||
#include <Arduino.h>
|
||||
|
||||
#define ENABLE_I2C_PULLUPS
|
||||
|
||||
// Chip address (for Wire)
|
||||
#define PCA9533_Addr 0xC4
|
||||
|
||||
// Control registers
|
||||
#define PCA9533_REG_READ 0x00
|
||||
#define PCA9533_REG_PSC0 0x01
|
||||
#define PCA9533_REG_PWM0 0x02
|
||||
#define PCA9533_REG_PSC1 0x03
|
||||
#define PCA9533_REG_PWM1 0x04
|
||||
#define PCA9533_REG_SEL 0x05
|
||||
#define PCA9533_REGM_AI 0x10
|
||||
|
||||
// LED selector operation
|
||||
#define PCA9533_LED_OP_OFF 0B00
|
||||
#define PCA9533_LED_OP_ON 0B01
|
||||
#define PCA9533_LED_OP_PWM0 0B10
|
||||
#define PCA9533_LED_OP_PWM1 0B11
|
||||
|
||||
// Select register bit offsets for LED colors
|
||||
#define PCA9533_LED_OFS_RED 0
|
||||
#define PCA9533_LED_OFS_GRN 2
|
||||
#define PCA9533_LED_OFS_BLU 4
|
||||
|
||||
void PCA9533_init();
|
||||
void PCA9533_reset();
|
||||
void PCA9533_set_rgb(uint8_t red, uint8_t green, uint8_t blue);
|
||||
void PCA9533_setOff();
|
||||
@@ -0,0 +1,160 @@
|
||||
/**
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Driver for the Philips PCA9632 LED driver.
|
||||
* Written by Robert Mendon Feb 2017.
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(PCA9632)
|
||||
|
||||
#include "pca9632.h"
|
||||
#include "leds.h"
|
||||
#include <Wire.h>
|
||||
|
||||
#define PCA9632_MODE1_VALUE 0b00000001 //(ALLCALL)
|
||||
#define PCA9632_MODE2_VALUE 0b00010101 //(DIMMING, INVERT, CHANGE ON STOP,TOTEM)
|
||||
#define PCA9632_LEDOUT_VALUE 0b00101010
|
||||
|
||||
/* Register addresses */
|
||||
#define PCA9632_MODE1 0x00
|
||||
#define PCA9632_MODE2 0x01
|
||||
#define PCA9632_PWM0 0x02
|
||||
#define PCA9632_PWM1 0x03
|
||||
#define PCA9632_PWM2 0x04
|
||||
#define PCA9632_PWM3 0x05
|
||||
#define PCA9632_GRPPWM 0x06
|
||||
#define PCA9632_GRPFREQ 0x07
|
||||
#define PCA9632_LEDOUT 0x08
|
||||
#define PCA9632_SUBADR1 0x09
|
||||
#define PCA9632_SUBADR2 0x0A
|
||||
#define PCA9632_SUBADR3 0x0B
|
||||
#define PCA9632_ALLCALLADDR 0x0C
|
||||
|
||||
#define PCA9632_NO_AUTOINC 0x00
|
||||
#define PCA9632_AUTO_ALL 0x80
|
||||
#define PCA9632_AUTO_IND 0xA0
|
||||
#define PCA9632_AUTOGLO 0xC0
|
||||
#define PCA9632_AUTOGI 0xE0
|
||||
|
||||
// Red=LED0 Green=LED1 Blue=LED2 White=LED3
|
||||
#ifndef PCA9632_RED
|
||||
#define PCA9632_RED 0x00
|
||||
#endif
|
||||
#ifndef PCA9632_GRN
|
||||
#define PCA9632_GRN 0x02
|
||||
#endif
|
||||
#ifndef PCA9632_BLU
|
||||
#define PCA9632_BLU 0x04
|
||||
#endif
|
||||
#if HAS_WHITE_LED && !defined(PCA9632_WHT)
|
||||
#define PCA9632_WHT 0x06
|
||||
#endif
|
||||
|
||||
// If any of the color indexes are greater than 0x04 they can't use auto increment
|
||||
#if !defined(PCA9632_NO_AUTO_INC) && (PCA9632_RED > 0x04 || PCA9632_GRN > 0x04 || PCA9632_BLU > 0x04 || PCA9632_WHT > 0x04)
|
||||
#define PCA9632_NO_AUTO_INC
|
||||
#endif
|
||||
|
||||
#define LED_OFF 0x00
|
||||
#define LED_ON 0x01
|
||||
#define LED_PWM 0x02
|
||||
|
||||
#define PCA9632_ADDRESS 0b01100000
|
||||
|
||||
byte PCA_init = 0;
|
||||
|
||||
static void PCA9632_WriteRegister(const byte addr, const byte regadd, const byte value) {
|
||||
Wire.beginTransmission(I2C_ADDRESS(addr));
|
||||
Wire.write(regadd);
|
||||
Wire.write(value);
|
||||
Wire.endTransmission();
|
||||
}
|
||||
|
||||
static void PCA9632_WriteAllRegisters(const byte addr, const byte regadd, const byte vr, const byte vg, const byte vb
|
||||
OPTARG(PCA9632_RGBW, const byte vw)
|
||||
) {
|
||||
#if DISABLED(PCA9632_NO_AUTO_INC)
|
||||
uint8_t data[4];
|
||||
data[0] = PCA9632_AUTO_IND | regadd;
|
||||
data[1 + (PCA9632_RED >> 1)] = vr;
|
||||
data[1 + (PCA9632_GRN >> 1)] = vg;
|
||||
data[1 + (PCA9632_BLU >> 1)] = vb;
|
||||
Wire.beginTransmission(I2C_ADDRESS(addr));
|
||||
Wire.write(data, sizeof(data));
|
||||
Wire.endTransmission();
|
||||
#else
|
||||
PCA9632_WriteRegister(addr, regadd + (PCA9632_RED >> 1), vr);
|
||||
PCA9632_WriteRegister(addr, regadd + (PCA9632_GRN >> 1), vg);
|
||||
PCA9632_WriteRegister(addr, regadd + (PCA9632_BLU >> 1), vb);
|
||||
#if ENABLED(PCA9632_RGBW)
|
||||
PCA9632_WriteRegister(addr, regadd + (PCA9632_WHT >> 1), vw);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
#if 0
|
||||
static byte PCA9632_ReadRegister(const byte addr, const byte regadd) {
|
||||
Wire.beginTransmission(I2C_ADDRESS(addr));
|
||||
Wire.write(regadd);
|
||||
const byte value = Wire.read();
|
||||
Wire.endTransmission();
|
||||
return value;
|
||||
}
|
||||
#endif
|
||||
|
||||
void PCA9632_set_led_color(const LEDColor &color) {
|
||||
Wire.begin();
|
||||
if (!PCA_init) {
|
||||
PCA_init = 1;
|
||||
PCA9632_WriteRegister(PCA9632_ADDRESS,PCA9632_MODE1, PCA9632_MODE1_VALUE);
|
||||
PCA9632_WriteRegister(PCA9632_ADDRESS,PCA9632_MODE2, PCA9632_MODE2_VALUE);
|
||||
}
|
||||
|
||||
const byte LEDOUT = (color.r ? LED_PWM << PCA9632_RED : 0)
|
||||
| (color.g ? LED_PWM << PCA9632_GRN : 0)
|
||||
| (color.b ? LED_PWM << PCA9632_BLU : 0)
|
||||
#if ENABLED(PCA9632_RGBW)
|
||||
| (color.w ? LED_PWM << PCA9632_WHT : 0)
|
||||
#endif
|
||||
;
|
||||
|
||||
PCA9632_WriteAllRegisters(PCA9632_ADDRESS,PCA9632_PWM0, color.r, color.g, color.b
|
||||
OPTARG(PCA9632_RGBW, color.w)
|
||||
);
|
||||
PCA9632_WriteRegister(PCA9632_ADDRESS,PCA9632_LEDOUT, LEDOUT);
|
||||
}
|
||||
|
||||
#if ENABLED(PCA9632_BUZZER)
|
||||
|
||||
void PCA9632_buzz(const long, const uint16_t=0) {
|
||||
uint8_t data[] = PCA9632_BUZZER_DATA;
|
||||
Wire.beginTransmission(I2C_ADDRESS(PCA9632_ADDRESS));
|
||||
Wire.write(data, sizeof(data));
|
||||
Wire.endTransmission();
|
||||
}
|
||||
|
||||
#endif // PCA9632_BUZZER
|
||||
|
||||
#endif // PCA9632
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
/**
|
||||
* Driver for the Philips PCA9632 LED driver.
|
||||
* Written by Robert Mendon Feb 2017.
|
||||
*/
|
||||
|
||||
struct LEDColor;
|
||||
typedef LEDColor LEDColor;
|
||||
|
||||
void PCA9632_set_led_color(const LEDColor &color);
|
||||
|
||||
#if ENABLED(PCA9632_BUZZER)
|
||||
#include <stdint.h>
|
||||
void PCA9632_buzz(const long, const uint16_t=0);
|
||||
#endif
|
||||
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* feature/leds/printer_event_leds.cpp - LED color changing based on printer status
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfigPre.h"
|
||||
|
||||
#if ENABLED(PRINTER_EVENT_LEDS)
|
||||
|
||||
#include "printer_event_leds.h"
|
||||
|
||||
PrinterEventLEDs printerEventLEDs;
|
||||
|
||||
#if HAS_LEDS_OFF_FLAG
|
||||
bool PrinterEventLEDs::leds_off_after_print; // = false
|
||||
#endif
|
||||
|
||||
#if HAS_TEMP_HOTEND || HAS_HEATED_BED
|
||||
|
||||
uint8_t PrinterEventLEDs::old_intensity = 0;
|
||||
|
||||
inline uint8_t pel_intensity(const celsius_t start, const celsius_t current, const celsius_t target) {
|
||||
if (start == target) return 255;
|
||||
return (uint8_t)map(constrain(current, start, target), start, target, 0, 255);
|
||||
}
|
||||
|
||||
inline void pel_set_rgb(const uint8_t r, const uint8_t g, const uint8_t b OPTARG(HAS_WHITE_LED, const uint8_t w=0)) {
|
||||
leds.set_color(
|
||||
LEDColor(r, g, b OPTARG(HAS_WHITE_LED, w) OPTARG(NEOPIXEL_LED, neo.brightness()))
|
||||
OPTARG(NEOPIXEL_IS_SEQUENTIAL, true)
|
||||
);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if HAS_TEMP_HOTEND
|
||||
|
||||
void PrinterEventLEDs::onHotendHeating(const celsius_t start, const celsius_t current, const celsius_t target) {
|
||||
const uint8_t blue = pel_intensity(start, current, target);
|
||||
if (blue != old_intensity) {
|
||||
old_intensity = blue;
|
||||
pel_set_rgb(255, 0, 255 - blue);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if HAS_HEATED_BED
|
||||
|
||||
void PrinterEventLEDs::onBedHeating(const celsius_t start, const celsius_t current, const celsius_t target) {
|
||||
const uint8_t red = pel_intensity(start, current, target);
|
||||
if (red != old_intensity) {
|
||||
old_intensity = red;
|
||||
pel_set_rgb(red, 0, 255);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if HAS_HEATED_CHAMBER
|
||||
|
||||
void PrinterEventLEDs::onChamberHeating(const celsius_t start, const celsius_t current, const celsius_t target) {
|
||||
const uint8_t green = pel_intensity(start, current, target);
|
||||
if (green != old_intensity) {
|
||||
old_intensity = green;
|
||||
pel_set_rgb(255, green, 255);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif // PRINTER_EVENT_LEDS
|
||||
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
/**
|
||||
* feature/leds/printer_event_leds.h - LED color changing based on printer status
|
||||
*/
|
||||
|
||||
#include "leds.h"
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
class PrinterEventLEDs {
|
||||
private:
|
||||
static uint8_t old_intensity;
|
||||
|
||||
#if HAS_LEDS_OFF_FLAG
|
||||
static bool leds_off_after_print;
|
||||
#endif
|
||||
|
||||
static void set_done() { TERN(LED_COLOR_PRESETS, leds.set_default(), leds.set_off()); }
|
||||
|
||||
public:
|
||||
#if HAS_TEMP_HOTEND
|
||||
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 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 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 void onHeatingDone() { leds.set_white(); }
|
||||
static void onPidTuningDone(LEDColor c) { leds.set_color(c); }
|
||||
#endif
|
||||
|
||||
#if HAS_MEDIA
|
||||
|
||||
static void onPrintCompleted() {
|
||||
leds.set_green();
|
||||
#if HAS_LEDS_OFF_FLAG
|
||||
leds_off_after_print = true;
|
||||
#else
|
||||
safe_delay(2000);
|
||||
set_done();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void onResumeAfterWait() {
|
||||
#if HAS_LEDS_OFF_FLAG
|
||||
if (leds_off_after_print) {
|
||||
set_done();
|
||||
leds_off_after_print = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // HAS_MEDIA
|
||||
};
|
||||
|
||||
extern PrinterEventLEDs printerEventLEDs;
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Marlin RGB LED general support
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(TEMP_STAT_LEDS)
|
||||
|
||||
#include "tempstat.h"
|
||||
#include "../../module/temperature.h"
|
||||
|
||||
void handle_status_leds() {
|
||||
static int8_t old_red = -1; // Invalid value to force LED initialization
|
||||
static millis_t next_status_led_update_ms = 0;
|
||||
if (ELAPSED(millis(), next_status_led_update_ms)) {
|
||||
next_status_led_update_ms += 500; // Update every 0.5s
|
||||
celsius_t max_temp = TERN0(HAS_HEATED_BED, _MAX(thermalManager.degTargetBed(), thermalManager.wholeDegBed()));
|
||||
HOTEND_LOOP()
|
||||
max_temp = _MAX(max_temp, thermalManager.wholeDegHotend(e), thermalManager.degTargetHotend(e));
|
||||
const int8_t new_red = (max_temp > 55) ? HIGH : (max_temp < 54 || old_red < 0) ? LOW : old_red;
|
||||
if (new_red != old_red) {
|
||||
old_red = new_red;
|
||||
#if PIN_EXISTS(STAT_LED_RED)
|
||||
WRITE(STAT_LED_RED_PIN, new_red);
|
||||
#endif
|
||||
#if PIN_EXISTS(STAT_LED_BLUE)
|
||||
WRITE(STAT_LED_BLUE_PIN, !new_red);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // TEMP_STAT_LEDS
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
/**
|
||||
* Marlin general RGB LED support
|
||||
*/
|
||||
|
||||
void handle_status_leds();
|
||||
739
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/max7219.cpp
Normal file
739
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/max7219.cpp
Normal file
@@ -0,0 +1,739 @@
|
||||
/**
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* This module is off by default, but can be enabled to facilitate the display of
|
||||
* extra debug information during code development.
|
||||
*
|
||||
* Just connect up 5V and GND to give it power, then connect up the pins assigned
|
||||
* in Configuration_adv.h. For example, on the Re-ARM you could use:
|
||||
*
|
||||
* #define MAX7219_CLK_PIN 77
|
||||
* #define MAX7219_DIN_PIN 78
|
||||
* #define MAX7219_LOAD_PIN 79
|
||||
*
|
||||
* send() is called automatically at startup, and then there are a number of
|
||||
* support functions available to control the LEDs in the 8x8 grid.
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
|
||||
#if ENABLED(MAX7219_DEBUG)
|
||||
|
||||
#define MAX7219_ERRORS // Requires ~400 bytes of flash
|
||||
|
||||
#include "max7219.h"
|
||||
|
||||
#include "../module/planner.h"
|
||||
#include "../MarlinCore.h"
|
||||
#include "../HAL/shared/Delay.h"
|
||||
|
||||
#if ENABLED(MAX7219_SIDE_BY_SIDE) && MAX7219_NUMBER_UNITS > 1
|
||||
#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)
|
||||
#elif _ROT == 90 || _ROT == 270
|
||||
#define MAX7219_X_LEDS TERN(HAS_SIDE_BY_SIDE, MAX7219_LINES, 8)
|
||||
#define MAX7219_Y_LEDS TERN(HAS_SIDE_BY_SIDE, 8, MAX7219_LINES)
|
||||
#else
|
||||
#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 };
|
||||
uint8_t Max7219::suspended; // = 0;
|
||||
|
||||
#define LINE_REG(Q) (max7219_reg_digit0 + ((Q) & 0x7))
|
||||
|
||||
#if (_ROT == 0 || _ROT == 270) == DISABLED(MAX7219_REVERSE_EACH)
|
||||
#define _LED_BIT(Q) (7 - ((Q) & 0x7))
|
||||
#else
|
||||
#define _LED_BIT(Q) ((Q) & 0x7)
|
||||
#endif
|
||||
#if _ROT == 0 || _ROT == 180
|
||||
#define LED_BIT(X,Y) _LED_BIT(X)
|
||||
#else
|
||||
#define LED_BIT(X,Y) _LED_BIT(Y)
|
||||
#endif
|
||||
#if _ROT == 0 || _ROT == 90
|
||||
#define _LED_IND(P,Q) (_LED_TOP(P) + ((Q) & 0x7))
|
||||
#else
|
||||
#define _LED_IND(P,Q) (_LED_TOP(P) + (7 - ((Q) & 0x7)))
|
||||
#endif
|
||||
|
||||
#if HAS_SIDE_BY_SIDE
|
||||
#if (_ROT == 0 || _ROT == 90) == DISABLED(MAX7219_REVERSE_ORDER)
|
||||
#define _LED_TOP(Q) ((MAX7219_NUMBER_UNITS - 1 - ((Q) >> 3)) << 3)
|
||||
#else
|
||||
#define _LED_TOP(Q) ((Q) & ~0x7)
|
||||
#endif
|
||||
#if _ROT == 0 || _ROT == 180
|
||||
#define LED_IND(X,Y) _LED_IND(Y,Y)
|
||||
#elif _ROT == 90 || _ROT == 270
|
||||
#define LED_IND(X,Y) _LED_IND(X,X)
|
||||
#endif
|
||||
#else
|
||||
#if (_ROT == 0 || _ROT == 270) == DISABLED(MAX7219_REVERSE_ORDER)
|
||||
#define _LED_TOP(Q) ((Q) & ~0x7)
|
||||
#else
|
||||
#define _LED_TOP(Q) ((MAX7219_NUMBER_UNITS - 1 - ((Q) >> 3)) << 3)
|
||||
#endif
|
||||
#if _ROT == 0 || _ROT == 180
|
||||
#define LED_IND(X,Y) _LED_IND(X,Y)
|
||||
#elif _ROT == 90 || _ROT == 270
|
||||
#define LED_IND(X,Y) _LED_IND(Y,X)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define XOR_7219(X,Y) do{ led_line[LED_IND(X,Y)] ^= _BV(LED_BIT(X,Y)); }while(0)
|
||||
#define SET_7219(X,Y) do{ led_line[LED_IND(X,Y)] |= _BV(LED_BIT(X,Y)); }while(0)
|
||||
#define CLR_7219(X,Y) do{ led_line[LED_IND(X,Y)] &= ~_BV(LED_BIT(X,Y)); }while(0)
|
||||
#define BIT_7219(X,Y) TEST(led_line[LED_IND(X,Y)], LED_BIT(X,Y))
|
||||
|
||||
#ifdef CPU_32_BIT
|
||||
#define SIG_DELAY() DELAY_US(1) // Approximate a 1µs delay on 32-bit ARM
|
||||
#undef CRITICAL_SECTION_START
|
||||
#undef CRITICAL_SECTION_END
|
||||
#define CRITICAL_SECTION_START() NOOP
|
||||
#define CRITICAL_SECTION_END() NOOP
|
||||
#else
|
||||
#define SIG_DELAY() DELAY_NS(250)
|
||||
#endif
|
||||
|
||||
void Max7219::error(FSTR_P const func, const int32_t v1, const int32_t v2/*=-1*/) {
|
||||
#if ENABLED(MAX7219_ERRORS)
|
||||
SERIAL_ECHOPGM("??? Max7219::");
|
||||
SERIAL_ECHOF(func, C('('));
|
||||
SERIAL_ECHO(v1);
|
||||
if (v2 > 0) SERIAL_ECHOPGM(", ", v2);
|
||||
SERIAL_CHAR(')');
|
||||
SERIAL_EOL();
|
||||
#else
|
||||
UNUSED(func); UNUSED(v1); UNUSED(v2);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Flip the lowest n_bytes of the supplied bits:
|
||||
* flipped(x, 1) flips the low 8 bits of x.
|
||||
* flipped(x, 2) flips the low 16 bits of x.
|
||||
* flipped(x, 3) flips the low 24 bits of x.
|
||||
* flipped(x, 4) flips the low 32 bits of x.
|
||||
*/
|
||||
inline uint32_t flipped(const uint32_t bits, const uint8_t n_bytes) {
|
||||
uint32_t mask = 1, outbits = 0;
|
||||
for (uint8_t b = 0; b < n_bytes * 8; ++b) {
|
||||
outbits <<= 1;
|
||||
if (bits & mask) outbits |= 1;
|
||||
mask <<= 1;
|
||||
}
|
||||
return outbits;
|
||||
}
|
||||
|
||||
void Max7219::noop() {
|
||||
CRITICAL_SECTION_START();
|
||||
SIG_DELAY();
|
||||
WRITE(MAX7219_DIN_PIN, LOW);
|
||||
for (uint8_t i = 16; i--;) {
|
||||
SIG_DELAY();
|
||||
WRITE(MAX7219_CLK_PIN, LOW);
|
||||
SIG_DELAY();
|
||||
SIG_DELAY();
|
||||
WRITE(MAX7219_CLK_PIN, HIGH);
|
||||
SIG_DELAY();
|
||||
}
|
||||
CRITICAL_SECTION_END();
|
||||
}
|
||||
|
||||
void Max7219::putbyte(uint8_t data) {
|
||||
CRITICAL_SECTION_START();
|
||||
for (uint8_t i = 8; i--;) {
|
||||
SIG_DELAY();
|
||||
WRITE(MAX7219_CLK_PIN, LOW); // tick
|
||||
SIG_DELAY();
|
||||
WRITE(MAX7219_DIN_PIN, (data & 0x80) ? HIGH : LOW); // send 1 or 0 based on data bit
|
||||
SIG_DELAY();
|
||||
WRITE(MAX7219_CLK_PIN, HIGH); // tock
|
||||
SIG_DELAY();
|
||||
data <<= 1;
|
||||
}
|
||||
CRITICAL_SECTION_END();
|
||||
}
|
||||
|
||||
void Max7219::pulse_load() {
|
||||
SIG_DELAY();
|
||||
WRITE(MAX7219_LOAD_PIN, LOW); // tell the chip to load the data
|
||||
SIG_DELAY();
|
||||
WRITE(MAX7219_LOAD_PIN, HIGH);
|
||||
SIG_DELAY();
|
||||
}
|
||||
|
||||
void Max7219::send(const uint8_t reg, const uint8_t data) {
|
||||
SIG_DELAY();
|
||||
CRITICAL_SECTION_START();
|
||||
SIG_DELAY();
|
||||
putbyte(reg); // specify register
|
||||
SIG_DELAY();
|
||||
putbyte(data); // put data
|
||||
CRITICAL_SECTION_END();
|
||||
}
|
||||
|
||||
// Send out a single native row of bits to just one unit
|
||||
void Max7219::refresh_unit_line(const uint8_t line) {
|
||||
if (suspended) return;
|
||||
#if MAX7219_NUMBER_UNITS == 1
|
||||
send(LINE_REG(line), led_line[line]);
|
||||
#else
|
||||
for (uint8_t u = MAX7219_NUMBER_UNITS; u--;)
|
||||
if (u == (line >> 3)) send(LINE_REG(line), led_line[line]); else noop();
|
||||
#endif
|
||||
pulse_load();
|
||||
}
|
||||
|
||||
// Send out a single native row of bits to all units
|
||||
void Max7219::refresh_line(const uint8_t line) {
|
||||
if (suspended) return;
|
||||
#if MAX7219_NUMBER_UNITS == 1
|
||||
refresh_unit_line(line);
|
||||
#else
|
||||
for (uint8_t u = MAX7219_NUMBER_UNITS; u--;)
|
||||
send(LINE_REG(line), led_line[(u << 3) | (line & 0x7)]);
|
||||
#endif
|
||||
pulse_load();
|
||||
}
|
||||
|
||||
void Max7219::set(const uint8_t line, const uint8_t bits) {
|
||||
led_line[line] = bits;
|
||||
refresh_unit_line(line);
|
||||
}
|
||||
|
||||
#if ENABLED(MAX7219_NUMERIC)
|
||||
|
||||
// Draw an integer with optional leading zeros and optional decimal point
|
||||
void Max7219::print(const uint8_t start, int16_t value, uint8_t size, const bool leadzero=false, bool dec=false) {
|
||||
if (suspended) return;
|
||||
constexpr uint8_t led_numeral[10] = { 0x7E, 0x60, 0x6D, 0x79, 0x63, 0x5B, 0x5F, 0x70, 0x7F, 0x7A },
|
||||
led_decimal = 0x80, led_minus = 0x01;
|
||||
bool blank = false, neg = value < 0;
|
||||
if (neg) value *= -1;
|
||||
while (size--) {
|
||||
const bool minus = neg && blank;
|
||||
if (minus) neg = false;
|
||||
send(
|
||||
max7219_reg_digit0 + start + size,
|
||||
minus ? led_minus : blank ? 0x00 : led_numeral[value % 10] | (dec ? led_decimal : 0x00)
|
||||
);
|
||||
pulse_load(); // tell the chips to load the clocked out data
|
||||
value /= 10;
|
||||
if (!value && !leadzero) blank = true;
|
||||
dec = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw a float with a decimal point and optional digits
|
||||
void Max7219::print(const uint8_t start, const_float_t value, const uint8_t pre_size, const uint8_t post_size, const bool leadzero=false) {
|
||||
if (pre_size) print(start, value, pre_size, leadzero, !!post_size);
|
||||
if (post_size) {
|
||||
const int16_t after = ABS(value) * (10 ^ post_size);
|
||||
print(start + pre_size, after, post_size, true);
|
||||
}
|
||||
}
|
||||
|
||||
#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, 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, 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, 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, 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) {
|
||||
if (suspended) return;
|
||||
#if _ROT == 0 || _ROT == 180 // Native Lines are horizontal too
|
||||
#if MAX7219_X_LEDS <= 8
|
||||
refresh_unit_line(LED_IND(0, row)); // A single unit line
|
||||
#else
|
||||
refresh_line(LED_IND(0, row)); // Same line, all units
|
||||
#endif
|
||||
#else // Native lines are vertical
|
||||
UNUSED(row);
|
||||
refresh(); // Actually a column
|
||||
#endif
|
||||
}
|
||||
|
||||
void Max7219::send_column(const uint8_t col) {
|
||||
if (suspended) return;
|
||||
#if _ROT == 90 || _ROT == 270 // Native Lines are vertical too
|
||||
#if MAX7219_Y_LEDS <= 8
|
||||
refresh_unit_line(LED_IND(col, 0)); // A single unit line
|
||||
#else
|
||||
refresh_line(LED_IND(col, 0)); // Same line, all units
|
||||
#endif
|
||||
#else // Native lines are horizontal
|
||||
UNUSED(col);
|
||||
refresh(); // Actually a row
|
||||
#endif
|
||||
}
|
||||
|
||||
void Max7219::clear() {
|
||||
ZERO(led_line);
|
||||
refresh();
|
||||
}
|
||||
|
||||
void Max7219::fill() {
|
||||
memset(led_line, 0xFF, sizeof(led_line));
|
||||
refresh();
|
||||
}
|
||||
|
||||
void Max7219::clear_row(const uint8_t row) {
|
||||
if (row >= MAX7219_Y_LEDS) return error(F("clear_row"), row);
|
||||
for (uint8_t x = 0; x < MAX7219_X_LEDS; ++x) CLR_7219(x, row);
|
||||
send_row(row);
|
||||
}
|
||||
|
||||
void Max7219::clear_column(const uint8_t col) {
|
||||
if (col >= MAX7219_X_LEDS) return error(F("set_column"), col);
|
||||
for (uint8_t y = 0; y < MAX7219_Y_LEDS; ++y) CLR_7219(col, y);
|
||||
send_column(col);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plot the low order bits of val to the specified row of the matrix.
|
||||
* With 4 Max7219 units in the chain, it's possible to set 32 bits at
|
||||
* 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(F("set_row"), row);
|
||||
uint32_t mask = _BV32(MAX7219_X_LEDS - 1);
|
||||
for (uint8_t x = 0; x < MAX7219_X_LEDS; ++x) {
|
||||
if (val & mask) SET_7219(x, row); else CLR_7219(x, row);
|
||||
mask >>= 1;
|
||||
}
|
||||
send_row(row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plot the low order bits of val to the specified column of the matrix.
|
||||
* With 4 Max7219 units in the chain, it's possible to set 32 bits at
|
||||
* 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(F("set_column"), col);
|
||||
uint32_t mask = _BV32(MAX7219_Y_LEDS - 1);
|
||||
for (uint8_t y = 0; y < MAX7219_Y_LEDS; ++y) {
|
||||
if (val & mask) SET_7219(col, y); else CLR_7219(col, y);
|
||||
mask >>= 1;
|
||||
}
|
||||
send_column(col);
|
||||
}
|
||||
|
||||
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(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(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(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(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(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(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(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(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(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(F("set_rows_32bits"), x, val);
|
||||
set_column(x, val);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Initialize the Max7219
|
||||
void Max7219::register_setup() {
|
||||
for (uint8_t i = 0; i < MAX7219_NUMBER_UNITS; ++i)
|
||||
send(max7219_reg_scanLimit, 0x07);
|
||||
pulse_load(); // Tell the chips to load the clocked out data
|
||||
|
||||
for (uint8_t i = 0; i < MAX7219_NUMBER_UNITS; ++i)
|
||||
send(max7219_reg_decodeMode, 0x00); // Using an led matrix (not digits)
|
||||
pulse_load(); // Tell the chips to load the clocked out data
|
||||
|
||||
for (uint8_t i = 0; i < MAX7219_NUMBER_UNITS; ++i)
|
||||
send(max7219_reg_shutdown, 0x01); // Not in shutdown mode
|
||||
pulse_load(); // Tell the chips to load the clocked out data
|
||||
|
||||
for (uint8_t i = 0; i < MAX7219_NUMBER_UNITS; ++i)
|
||||
send(max7219_reg_displayTest, 0x00); // No display test
|
||||
pulse_load(); // Tell the chips to load the clocked out data
|
||||
|
||||
for (uint8_t i = 0; i < MAX7219_NUMBER_UNITS; ++i)
|
||||
send(max7219_reg_intensity, 0x01 & 0x0F); // The first 0x0F is the value you can set
|
||||
// Range: 0x00 to 0x0F
|
||||
pulse_load(); // Tell the chips to load the clocked out data
|
||||
}
|
||||
|
||||
#if MAX7219_INIT_TEST
|
||||
|
||||
uint8_t test_mode = 0;
|
||||
millis_t next_patt_ms;
|
||||
bool patt_on;
|
||||
|
||||
#if MAX7219_INIT_TEST == 2
|
||||
|
||||
#define MAX7219_LEDS (MAX7219_X_LEDS * MAX7219_Y_LEDS)
|
||||
|
||||
constexpr millis_t pattern_delay = 4;
|
||||
|
||||
int8_t spiralx, spiraly, spiral_dir;
|
||||
uvalue_t(MAX7219_LEDS) spiral_count;
|
||||
|
||||
void Max7219::test_pattern() {
|
||||
constexpr int8_t way[][2] = { { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 } };
|
||||
led_set(spiralx, spiraly, patt_on);
|
||||
const int8_t x = spiralx + way[spiral_dir][0], y = spiraly + way[spiral_dir][1];
|
||||
if (!WITHIN(x, 0, MAX7219_X_LEDS - 1) || !WITHIN(y, 0, MAX7219_Y_LEDS - 1) || BIT_7219(x, y) == patt_on)
|
||||
spiral_dir = (spiral_dir + 1) & 0x3;
|
||||
spiralx += way[spiral_dir][0];
|
||||
spiraly += way[spiral_dir][1];
|
||||
if (!spiral_count--) {
|
||||
if (!patt_on)
|
||||
test_mode = 0;
|
||||
else {
|
||||
spiral_count = MAX7219_LEDS;
|
||||
spiralx = spiraly = spiral_dir = 0;
|
||||
patt_on = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
constexpr millis_t pattern_delay = 20;
|
||||
int8_t sweep_count, sweepx, sweep_dir;
|
||||
|
||||
void Max7219::test_pattern() {
|
||||
set_column(sweepx, patt_on ? 0xFFFFFFFF : 0x00000000);
|
||||
sweepx += sweep_dir;
|
||||
if (!WITHIN(sweepx, 0, MAX7219_X_LEDS - 1)) {
|
||||
if (!patt_on) {
|
||||
sweep_dir *= -1;
|
||||
sweepx += sweep_dir;
|
||||
}
|
||||
else
|
||||
sweepx -= MAX7219_X_LEDS * sweep_dir;
|
||||
patt_on ^= true;
|
||||
next_patt_ms += 100;
|
||||
if (++test_mode > 4) test_mode = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void Max7219::run_test_pattern() {
|
||||
const millis_t ms = millis();
|
||||
if (PENDING(ms, next_patt_ms)) return;
|
||||
next_patt_ms = ms + pattern_delay;
|
||||
test_pattern();
|
||||
}
|
||||
|
||||
void Max7219::start_test_pattern() {
|
||||
clear();
|
||||
test_mode = 1;
|
||||
patt_on = true;
|
||||
#if MAX7219_INIT_TEST == 2
|
||||
spiralx = spiraly = spiral_dir = 0;
|
||||
spiral_count = MAX7219_LEDS;
|
||||
#else
|
||||
sweep_dir = 1;
|
||||
sweepx = 0;
|
||||
sweep_count = MAX7219_X_LEDS;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // MAX7219_INIT_TEST
|
||||
|
||||
void Max7219::init() {
|
||||
SET_OUTPUT(MAX7219_DIN_PIN);
|
||||
SET_OUTPUT(MAX7219_CLK_PIN);
|
||||
OUT_WRITE(MAX7219_LOAD_PIN, HIGH);
|
||||
delay(1);
|
||||
|
||||
register_setup();
|
||||
|
||||
clear();
|
||||
|
||||
#if MAX7219_INIT_TEST
|
||||
start_test_pattern();
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* This code demonstrates some simple debugging using a single 8x8 LED Matrix. If your feature could
|
||||
* benefit from matrix display, add its code here. Very little processing is required, so the 7219 is
|
||||
* ideal for debugging when realtime feedback is important but serial output can't be used.
|
||||
*/
|
||||
|
||||
// Apply changes to update a marker
|
||||
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, 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, 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), 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, 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, rcm);
|
||||
if (oh != nh) for (uint8_t n = (oh + 1) & 0xF; n != ((nh + 1) & 0xF); n = (n + 1) & 0xF)
|
||||
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, rcm);
|
||||
if (oh != nh) for (uint8_t n = (oh + 1) & 0xF; n != ((nh + 1) & 0xF); n = (n + 1) & 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), 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), rcm);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Apply changes to update a quantity
|
||||
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.
|
||||
i, pos
|
||||
#elif MAX7219_Y_LEDS > 8 // At least 16 LEDs on the Y-Axis. Use a single column.
|
||||
pos, i
|
||||
#else // Single 8x8 LED matrix. Use two lines to get 16 LEDs.
|
||||
i >> 1, pos + (i & 1)
|
||||
#endif
|
||||
, nv >= ov
|
||||
, rcm
|
||||
);
|
||||
}
|
||||
|
||||
void Max7219::idle_tasks() {
|
||||
#define MAX7219_USE_HEAD (defined(MAX7219_DEBUG_PLANNER_HEAD) || defined(MAX7219_DEBUG_PLANNER_QUEUE))
|
||||
#define MAX7219_USE_TAIL (defined(MAX7219_DEBUG_PLANNER_TAIL) || defined(MAX7219_DEBUG_PLANNER_QUEUE))
|
||||
#if MAX7219_USE_HEAD || MAX7219_USE_TAIL
|
||||
CRITICAL_SECTION_START();
|
||||
#if MAX7219_USE_HEAD
|
||||
const uint8_t head = planner.block_buffer_head;
|
||||
#endif
|
||||
#if MAX7219_USE_TAIL
|
||||
const uint8_t tail = planner.block_buffer_tail;
|
||||
#endif
|
||||
CRITICAL_SECTION_END();
|
||||
#endif
|
||||
|
||||
#if ENABLED(MAX7219_DEBUG_PRINTER_ALIVE)
|
||||
static uint8_t refresh_cnt; // = 0
|
||||
constexpr uint16_t refresh_limit = 5;
|
||||
static millis_t next_blink = 0;
|
||||
const millis_t ms = millis();
|
||||
const bool do_blink = ELAPSED(ms, next_blink);
|
||||
#else
|
||||
static uint16_t refresh_cnt; // = 0
|
||||
constexpr bool do_blink = true;
|
||||
constexpr uint16_t refresh_limit = 50000;
|
||||
#endif
|
||||
|
||||
// Some Max7219 units are vulnerable to electrical noise, especially
|
||||
// with long wires next to high current wires. If the display becomes
|
||||
// corrupted, this will fix it within a couple seconds.
|
||||
if (do_blink && ++refresh_cnt >= refresh_limit) {
|
||||
refresh_cnt = 0;
|
||||
register_setup();
|
||||
}
|
||||
|
||||
#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, &row_change_mask);
|
||||
next_blink = ms + 1000;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(MAX7219_DEBUG_PLANNER_HEAD) && defined(MAX7219_DEBUG_PLANNER_TAIL) && MAX7219_DEBUG_PLANNER_HEAD == MAX7219_DEBUG_PLANNER_TAIL
|
||||
|
||||
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, &row_change_mask);
|
||||
last_head_cnt = head;
|
||||
last_tail_cnt = tail;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#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, &row_change_mask);
|
||||
last_head_cnt = head;
|
||||
}
|
||||
#endif
|
||||
|
||||
#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, &row_change_mask);
|
||||
last_tail_cnt = tail;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MAX7219_DEBUG_PLANNER_QUEUE
|
||||
static int16_t last_depth = 0;
|
||||
const int16_t current_depth = BLOCK_MOD(head - tail + (BLOCK_BUFFER_SIZE)) & 0xF;
|
||||
if (current_depth != last_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)
|
||||
for (uint8_t i = 0; i < 8; ++i) if (row_change_mask & _BV(i))
|
||||
refresh_line(i);
|
||||
|
||||
// After resume() automatically do a refresh()
|
||||
if (suspended == 0x80) {
|
||||
suspended = 0;
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // MAX7219_DEBUG
|
||||
222
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/max7219.h
Normal file
222
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/max7219.h
Normal file
@@ -0,0 +1,222 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
/**
|
||||
* This module is off by default, but can be enabled to facilitate the display of
|
||||
* extra debug information during code development.
|
||||
*
|
||||
* Just connect up 5V and GND to give it power, then connect up the pins assigned
|
||||
* in Configuration_adv.h. For example, on the Re-ARM you could use:
|
||||
*
|
||||
* #define MAX7219_CLK_PIN 77
|
||||
* #define MAX7219_DIN_PIN 78
|
||||
* #define MAX7219_LOAD_PIN 79
|
||||
*
|
||||
* max7219.init() is called automatically at startup, and then there are a number of
|
||||
* support functions available to control the LEDs in the 8x8 grid.
|
||||
*
|
||||
* If you are using the Max7219 matrix for firmware debug purposes in time sensitive
|
||||
* areas of the code, please be aware that the orientation (rotation) of the display can
|
||||
* affect the speed. The Max7219 can update a single column fairly fast. It is much
|
||||
* faster to do a Max7219_Set_Column() with a rotation of 90 or 270 degrees than to do
|
||||
* 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
|
||||
|
||||
#ifndef MAX7219_NUMBER_UNITS
|
||||
#define MAX7219_NUMBER_UNITS 1
|
||||
#endif
|
||||
#define MAX7219_LINES (8 * (MAX7219_NUMBER_UNITS))
|
||||
|
||||
//
|
||||
// MAX7219 registers
|
||||
//
|
||||
#define max7219_reg_noop 0x00
|
||||
#define max7219_reg_digit0 0x01
|
||||
#define max7219_reg_digit1 0x02
|
||||
#define max7219_reg_digit2 0x03
|
||||
#define max7219_reg_digit3 0x04
|
||||
#define max7219_reg_digit4 0x05
|
||||
#define max7219_reg_digit5 0x06
|
||||
#define max7219_reg_digit6 0x07
|
||||
#define max7219_reg_digit7 0x08
|
||||
|
||||
#define max7219_reg_decodeMode 0x09
|
||||
#define max7219_reg_intensity 0x0A
|
||||
#define max7219_reg_scanLimit 0x0B
|
||||
#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];
|
||||
|
||||
Max7219() {}
|
||||
|
||||
static void init();
|
||||
static void register_setup();
|
||||
static void putbyte(uint8_t data);
|
||||
static void pulse_load();
|
||||
|
||||
// Set a single register (e.g., a whole native row)
|
||||
static void send(const uint8_t reg, const uint8_t data);
|
||||
|
||||
// Refresh all units
|
||||
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 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);
|
||||
|
||||
// Update a single native line on just one unit
|
||||
static void refresh_unit_line(const uint8_t line);
|
||||
|
||||
#if ENABLED(MAX7219_NUMERIC)
|
||||
// Draw an integer with optional leading zeros and optional decimal point
|
||||
void print(const uint8_t start, int16_t value, uint8_t size, const bool leadzero=false, bool dec=false);
|
||||
// Draw a float with a decimal point and optional digits
|
||||
void print(const uint8_t start, const_float_t value, const uint8_t pre_size, const uint8_t post_size, const bool leadzero=false);
|
||||
#endif
|
||||
|
||||
// Set a single LED by XY coordinate
|
||||
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);
|
||||
static void clear_column(const uint8_t col);
|
||||
|
||||
// Set all LEDs in a single row
|
||||
static void set_row(const uint8_t row, const uint32_t val);
|
||||
static void clear_row(const uint8_t row);
|
||||
|
||||
// 16 and 32 bit versions of Row and Column functions
|
||||
// Multiple rows and columns will be used to display the value if
|
||||
// the array of matrix LED's is too narrow to accomplish the goal
|
||||
static void set_rows_16bits(const uint8_t y, uint32_t val);
|
||||
static void set_rows_32bits(const uint8_t y, uint32_t val);
|
||||
static void set_columns_16bits(const uint8_t x, uint32_t val);
|
||||
static void set_columns_32bits(const uint8_t x, uint32_t val);
|
||||
|
||||
// Quickly clear the whole matrix
|
||||
static void clear();
|
||||
|
||||
// Quickly fill the whole matrix
|
||||
static void fill();
|
||||
|
||||
// Apply custom code to update the matrix
|
||||
static void idle_tasks();
|
||||
|
||||
private:
|
||||
static uint8_t suspended;
|
||||
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, 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);
|
||||
|
||||
#if MAX7219_INIT_TEST
|
||||
static void test_pattern();
|
||||
static void run_test_pattern();
|
||||
static void start_test_pattern();
|
||||
#endif
|
||||
};
|
||||
|
||||
extern Max7219 max7219;
|
||||
218
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/meatpack.cpp
Normal file
218
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/meatpack.cpp
Normal file
@@ -0,0 +1,218 @@
|
||||
/**
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* MeatPack G-code Compression
|
||||
*
|
||||
* Algorithm & Implementation: Scott Mudge - mail@scottmudge.com
|
||||
* Date: Dec. 2020
|
||||
*
|
||||
* 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
|
||||
* '.' -> 3035310 '5' -> 1189871 'E' -> 965275 'S' -> 9910
|
||||
* '2' -> 1523296 '6' -> 1127900 'Y' -> 965274
|
||||
* '8' -> 1366812 '7' -> 1112908 'F' -> 99416
|
||||
*
|
||||
* When space is omitted the letter 'E' is used in its place
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#if HAS_MEATPACK
|
||||
|
||||
#include "meatpack.h"
|
||||
|
||||
#define MeatPack_ProtocolVersion "PV01"
|
||||
//#define MP_DEBUG
|
||||
|
||||
#define DEBUG_OUT ENABLED(MP_DEBUG)
|
||||
#include "../core/debug_out.h"
|
||||
|
||||
// The 15 most-common characters used in G-code, ~90-95% of all G-code uses these characters
|
||||
// Stored in SRAM for performance.
|
||||
uint8_t meatPackLookupTable[16] = {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'.', ' ', '\n', 'G', 'X',
|
||||
'\0' // Unused. 0b1111 indicates a literal character
|
||||
};
|
||||
|
||||
#if ENABLED(MP_DEBUG)
|
||||
uint8_t chars_decoded = 0; // Log the first 64 bytes after each reset
|
||||
#endif
|
||||
|
||||
void MeatPack::reset_state() {
|
||||
state = 0;
|
||||
cmd_is_next = false;
|
||||
second_char = 0;
|
||||
cmd_count = full_char_count = char_out_count = 0;
|
||||
TERN_(MP_DEBUG, chars_decoded = 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpack one or two characters from a packed byte into a buffer.
|
||||
* Return flags indicating whether any literal bytes follow.
|
||||
*/
|
||||
uint8_t MeatPack::unpack_chars(const uint8_t pk, uint8_t* __restrict const chars_out) {
|
||||
uint8_t out = 0;
|
||||
|
||||
// If lower nybble is 1111, the higher nybble is unused, and next char is full.
|
||||
if ((pk & kFirstNotPacked) == kFirstNotPacked)
|
||||
out = kFirstCharIsLiteral;
|
||||
else {
|
||||
const uint8_t chr = pk & 0x0F;
|
||||
chars_out[0] = meatPackLookupTable[chr]; // Set the first char
|
||||
}
|
||||
|
||||
// Check if upper nybble is 1111... if so, we don't need the second char.
|
||||
if ((pk & kSecondNotPacked) == kSecondNotPacked)
|
||||
out |= kSecondCharIsLiteral;
|
||||
else {
|
||||
const uint8_t chr = (pk >> 4) & 0x0F;
|
||||
chars_out[1] = meatPackLookupTable[chr]; // Set the second char
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpret a single (non-command) character
|
||||
* according to the current MeatPack state.
|
||||
*/
|
||||
void MeatPack::handle_rx_char_inner(const uint8_t c) {
|
||||
if (TEST(state, MPConfig_Bit_Active)) { // Is MeatPack active?
|
||||
if (!full_char_count) { // No literal characters to fetch?
|
||||
uint8_t buf[2] = { 0, 0 };
|
||||
const uint8_t res = unpack_chars(c, buf); // Decode the byte into one or two characters.
|
||||
if (res & kFirstCharIsLiteral) { // The 1st character couldn't be packed.
|
||||
++full_char_count; // So the next stream byte is a full character.
|
||||
if (res & kSecondCharIsLiteral) ++full_char_count; // The 2nd character couldn't be packed. Another stream byte is a full character.
|
||||
else second_char = buf[1]; // Retain the unpacked second character.
|
||||
}
|
||||
else {
|
||||
handle_output_char(buf[0]); // Send the unpacked first character out.
|
||||
if (buf[0] != '\n') { // After a newline the next char won't be set
|
||||
if (res & kSecondCharIsLiteral) ++full_char_count; // The 2nd character couldn't be packed. The next stream byte is a full character.
|
||||
else handle_output_char(buf[1]); // Send the unpacked second character out.
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
handle_output_char(c); // Pass through the character that couldn't be packed...
|
||||
if (second_char) {
|
||||
handle_output_char(second_char); // ...and send an unpacked 2nd character, if set.
|
||||
second_char = 0;
|
||||
}
|
||||
--full_char_count; // One literal character was consumed
|
||||
}
|
||||
}
|
||||
else // Packing not enabled, just copy character to output
|
||||
handle_output_char(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Buffer a single output character which will be picked up in
|
||||
* GCodeQueue::get_serial_commands via calls to get_result_char
|
||||
*/
|
||||
void MeatPack::handle_output_char(const uint8_t c) {
|
||||
char_out_buf[char_out_count++] = c;
|
||||
|
||||
#if ENABLED(MP_DEBUG)
|
||||
if (chars_decoded < 1024) {
|
||||
++chars_decoded;
|
||||
DEBUG_ECHOLNPGM("RB: ", C(c));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a MeatPack command byte to update the state.
|
||||
* Report the new state to serial.
|
||||
*/
|
||||
void MeatPack::handle_command(const MeatPack_Command c) {
|
||||
switch (c) {
|
||||
case MPCommand_QueryConfig: break;
|
||||
case MPCommand_EnablePacking: SBI(state, MPConfig_Bit_Active); DEBUG_ECHOLNPGM("[MPDBG] ENA REC"); break;
|
||||
case MPCommand_DisablePacking: CBI(state, MPConfig_Bit_Active); DEBUG_ECHOLNPGM("[MPDBG] DIS REC"); break;
|
||||
case MPCommand_ResetAll: reset_state(); DEBUG_ECHOLNPGM("[MPDBG] RESET REC"); break;
|
||||
case MPCommand_EnableNoSpaces:
|
||||
SBI(state, MPConfig_Bit_NoSpaces);
|
||||
meatPackLookupTable[kSpaceCharIdx] = kSpaceCharReplace; DEBUG_ECHOLNPGM("[MPDBG] ENA NSP"); break;
|
||||
case MPCommand_DisableNoSpaces:
|
||||
CBI(state, MPConfig_Bit_NoSpaces);
|
||||
meatPackLookupTable[kSpaceCharIdx] = ' '; DEBUG_ECHOLNPGM("[MPDBG] DIS NSP"); break;
|
||||
default: DEBUG_ECHOLNPGM("[MPDBG] UNK CMD REC");
|
||||
}
|
||||
report_state();
|
||||
}
|
||||
|
||||
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] " MeatPack_ProtocolVersion " ");
|
||||
serialprint_onoff(TEST(state, MPConfig_Bit_Active));
|
||||
SERIAL_ECHOF(TEST(state, MPConfig_Bit_NoSpaces) ? F(" NSP\n") : F(" ESP\n"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpret a single character received from serial
|
||||
* according to the current meatpack state.
|
||||
*/
|
||||
void MeatPack::handle_rx_char(const uint8_t c, const serial_index_t serial_ind) {
|
||||
if (c == kCommandByte) { // A command (0xFF) byte?
|
||||
if (cmd_count) { // In fact, two in a row?
|
||||
cmd_is_next = true; // Then a MeatPack command follows
|
||||
cmd_count = 0;
|
||||
}
|
||||
else
|
||||
++cmd_count; // cmd_count = 1 // One command byte received so far...
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmd_is_next) { // Were two command bytes received?
|
||||
PORT_REDIRECT(SERIAL_PORTMASK(serial_ind));
|
||||
handle_command((MeatPack_Command)c); // Then the byte is a MeatPack command
|
||||
cmd_is_next = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmd_count) { // Only a single 0xFF was received
|
||||
handle_rx_char_inner(kCommandByte); // A single 0xFF is passed on literally so it can be interpreted as kFirstNotPacked|kSecondNotPacked
|
||||
cmd_count = 0;
|
||||
}
|
||||
|
||||
handle_rx_char_inner(c); // Other characters are passed on for MeatPack decoding
|
||||
}
|
||||
|
||||
uint8_t MeatPack::get_result_char(char * const __restrict out) {
|
||||
uint8_t res = 0;
|
||||
if (char_out_count) {
|
||||
res = char_out_count;
|
||||
char_out_count = 0;
|
||||
for (uint8_t i = 0; i < res; ++i)
|
||||
out[i] = (char)char_out_buf[i];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif // HAS_MEATPACK
|
||||
174
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/meatpack.h
Normal file
174
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/meatpack.h
Normal file
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* MeatPack G-code Compression
|
||||
*
|
||||
* Algorithm & Implementation: Scott Mudge - mail@scottmudge.com
|
||||
* Date: Dec. 2020
|
||||
*
|
||||
* 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 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
|
||||
* they fall within this limited 15-character alphabet. Using a 4-bit lookup table, these 8-bit
|
||||
* characters can be represented by a 4-bit index.
|
||||
*
|
||||
* 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 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
|
||||
* baud rate provided by Prusa's Atmega2560-based boards, over the USB serial connection. So soft-
|
||||
* ware like OctoPrint would also suffer this same micro-stuttering and poor print quality issue.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "../core/serial_hook.h"
|
||||
|
||||
/**
|
||||
* Commands sent to MeatPack to control its behavior.
|
||||
* They are sent by first sending 2x MeatPack_CommandByte (0xFF) in sequence,
|
||||
* followed by one of the command bytes below.
|
||||
* Provided that 0xFF is an exceedingly rare character that is virtually never
|
||||
* present in G-code naturally, it is safe to assume 2 in sequence should never
|
||||
* happen naturally, and so it is used as a signal here.
|
||||
*
|
||||
* 0xFF *IS* used in "packed" G-code (used to denote that the next 2 characters are
|
||||
* full-width), however 2 in a row will never occur, as the next 2 bytes will always
|
||||
* some non-0xFF character.
|
||||
*/
|
||||
enum MeatPack_Command : uint8_t {
|
||||
MPCommand_None = 0,
|
||||
MPCommand_EnablePacking = 0xFB,
|
||||
MPCommand_DisablePacking = 0xFA,
|
||||
MPCommand_ResetAll = 0xF9,
|
||||
MPCommand_QueryConfig = 0xF8,
|
||||
MPCommand_EnableNoSpaces = 0xF7,
|
||||
MPCommand_DisableNoSpaces = 0xF6
|
||||
};
|
||||
|
||||
enum MeatPack_ConfigStateBits : uint8_t {
|
||||
MPConfig_Bit_Active = 0,
|
||||
MPConfig_Bit_NoSpaces = 1
|
||||
};
|
||||
|
||||
class MeatPack {
|
||||
|
||||
// Utility definitions
|
||||
static const uint8_t kCommandByte = 0b11111111,
|
||||
kFirstNotPacked = 0b00001111,
|
||||
kSecondNotPacked = 0b11110000,
|
||||
kFirstCharIsLiteral = 0b00000001,
|
||||
kSecondCharIsLiteral = 0b00000010;
|
||||
|
||||
static const uint8_t kSpaceCharIdx = 11;
|
||||
static const char kSpaceCharReplace = 'E';
|
||||
|
||||
bool cmd_is_next; // A command is pending
|
||||
uint8_t state; // Configuration state
|
||||
uint8_t second_char; // Buffers a character if dealing with out-of-sequence pairs
|
||||
uint8_t cmd_count, // Counter of command bytes received (need 2)
|
||||
full_char_count, // Counter for full-width characters to be received
|
||||
char_out_count; // Stores number of characters to be read out.
|
||||
uint8_t char_out_buf[2]; // Output buffer for caching up to 2 characters
|
||||
|
||||
public:
|
||||
// Pass in a character rx'd by SD card or serial. Automatically parses command/ctrl sequences,
|
||||
// and will control state internally.
|
||||
void handle_rx_char(const uint8_t c, const serial_index_t serial_ind);
|
||||
|
||||
/**
|
||||
* After passing in rx'd char using above method, call this to get characters out.
|
||||
* Can return from 0 to 2 characters at once.
|
||||
* @param out [in] Output pointer for unpacked/processed data.
|
||||
* @return Number of characters returned. Range from 0 to 2.
|
||||
*/
|
||||
uint8_t get_result_char(char * const __restrict out);
|
||||
|
||||
void reset_state();
|
||||
void report_state();
|
||||
uint8_t unpack_chars(const uint8_t pk, uint8_t* __restrict const chars_out);
|
||||
void handle_command(const MeatPack_Command c);
|
||||
void handle_output_char(const uint8_t c);
|
||||
void handle_rx_char_inner(const uint8_t c);
|
||||
|
||||
MeatPack() : cmd_is_next(false), state(0), second_char(0), cmd_count(0), full_char_count(0), char_out_count(0) {}
|
||||
};
|
||||
|
||||
// Implement the MeatPack serial class so it's transparent to rest of the code
|
||||
template <typename SerialT>
|
||||
struct MeatpackSerial : public SerialBase <MeatpackSerial < SerialT >> {
|
||||
typedef SerialBase< MeatpackSerial<SerialT> > BaseClassT;
|
||||
|
||||
SerialT & out;
|
||||
MeatPack meatpack;
|
||||
|
||||
char serialBuffer[2];
|
||||
uint8_t charCount;
|
||||
uint8_t readIndex;
|
||||
|
||||
NO_INLINE void write(uint8_t c) { out.write(c); }
|
||||
void flush() { out.flush(); }
|
||||
void begin(long br) { out.begin(br); readIndex = 0; }
|
||||
void end() { out.end(); }
|
||||
|
||||
void msgDone() { out.msgDone(); }
|
||||
// Existing instances implement Arduino's operator bool, so use that if it's available
|
||||
bool connected() { return Private::HasMember_connected<SerialT>::value ? CALL_IF_EXISTS(bool, &out, connected) : (bool)out; }
|
||||
void flushTX() { CALL_IF_EXISTS(void, &out, flushTX); }
|
||||
SerialFeature features(serial_index_t index) const { return SerialFeature::MeatPack | CALL_IF_EXISTS(SerialFeature, &out, features, index); }
|
||||
|
||||
int available(serial_index_t index) {
|
||||
if (charCount) return charCount; // The buffer still has data
|
||||
if (out.available(index) <= 0) return 0; // No data to read
|
||||
|
||||
// Don't read in read method, instead do it here, so we can make progress in the read method
|
||||
const int r = out.read(index);
|
||||
if (r == -1) return 0; // This is an error from the underlying serial code
|
||||
meatpack.handle_rx_char((uint8_t)r, index);
|
||||
charCount = meatpack.get_result_char(serialBuffer);
|
||||
readIndex = 0;
|
||||
|
||||
return charCount;
|
||||
}
|
||||
|
||||
int readImpl(const serial_index_t index) {
|
||||
// Not enough char to make progress?
|
||||
if (charCount == 0 && available(index) == 0) return -1;
|
||||
|
||||
charCount--;
|
||||
return serialBuffer[readIndex++];
|
||||
}
|
||||
|
||||
int read(serial_index_t index) { return readImpl(index); }
|
||||
int available() { return available(0); }
|
||||
int read() { return readImpl(0); }
|
||||
|
||||
MeatpackSerial(const bool e, SerialT & out) : BaseClassT(e), out(out) {}
|
||||
};
|
||||
208
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/mixing.cpp
Normal file
208
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/mixing.cpp
Normal file
@@ -0,0 +1,208 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
#if ENABLED(MIXING_EXTRUDER)
|
||||
|
||||
#include "mixing.h"
|
||||
|
||||
Mixer mixer;
|
||||
|
||||
#ifdef MIXER_NORMALIZER_DEBUG
|
||||
#include "../core/serial.h"
|
||||
#endif
|
||||
|
||||
// Used up to Planner level
|
||||
uint_fast8_t Mixer::selected_vtool = 0;
|
||||
float Mixer::collector[MIXING_STEPPERS]; // mix proportion. 0.0 = off, otherwise <= COLOR_A_MASK.
|
||||
mixer_comp_t Mixer::color[NR_MIXING_VIRTUAL_TOOLS][MIXING_STEPPERS];
|
||||
|
||||
// Used in Stepper
|
||||
int_fast8_t Mixer::runner = 0;
|
||||
mixer_comp_t Mixer::s_color[MIXING_STEPPERS];
|
||||
mixer_accu_t Mixer::accu[MIXING_STEPPERS] = { 0 };
|
||||
|
||||
#if ANY(HAS_DUAL_MIXING, GRADIENT_MIX)
|
||||
mixer_perc_t Mixer::mix[MIXING_STEPPERS];
|
||||
#endif
|
||||
|
||||
void Mixer::normalize(const uint8_t tool_index) {
|
||||
float cmax = 0;
|
||||
#ifdef MIXER_NORMALIZER_DEBUG
|
||||
float csum = 0;
|
||||
#endif
|
||||
MIXER_STEPPER_LOOP(i) {
|
||||
const float v = collector[i];
|
||||
NOLESS(cmax, v);
|
||||
#ifdef MIXER_NORMALIZER_DEBUG
|
||||
csum += v;
|
||||
#endif
|
||||
}
|
||||
#ifdef MIXER_NORMALIZER_DEBUG
|
||||
SERIAL_ECHOPGM("Mixer: Old relation : [ ");
|
||||
MIXER_STEPPER_LOOP(i) {
|
||||
SERIAL_DECIMAL(collector[i] / csum);
|
||||
SERIAL_CHAR(' ');
|
||||
}
|
||||
SERIAL_ECHOLNPGM("]");
|
||||
#endif
|
||||
|
||||
// Scale all values so their maximum is COLOR_A_MASK
|
||||
const float scale = float(COLOR_A_MASK) / cmax;
|
||||
MIXER_STEPPER_LOOP(i) color[tool_index][i] = collector[i] * scale;
|
||||
|
||||
#ifdef MIXER_NORMALIZER_DEBUG
|
||||
csum = 0;
|
||||
SERIAL_ECHOPGM("Mixer: Normalize to : [ ");
|
||||
MIXER_STEPPER_LOOP(i) {
|
||||
SERIAL_ECHO(uint16_t(color[tool_index][i]));
|
||||
SERIAL_CHAR(' ');
|
||||
csum += color[tool_index][i];
|
||||
}
|
||||
SERIAL_ECHOLNPGM("]");
|
||||
SERIAL_ECHOPGM("Mixer: New relation : [ ");
|
||||
MIXER_STEPPER_LOOP(i) {
|
||||
SERIAL_ECHO_F(uint16_t(color[tool_index][i]) / csum, 3);
|
||||
SERIAL_CHAR(' ');
|
||||
}
|
||||
SERIAL_ECHOLNPGM("]");
|
||||
#endif
|
||||
|
||||
TERN_(GRADIENT_MIX, refresh_gradient());
|
||||
}
|
||||
|
||||
void Mixer::reset_vtools() {
|
||||
// Virtual Tools 0, 1, 2, 3 = Filament 1, 2, 3, 4, etc.
|
||||
// Every virtual tool gets a pure filament
|
||||
for (uint8_t t = 0; t < _MIN(MIXING_VIRTUAL_TOOLS, MIXING_STEPPERS); ++t)
|
||||
MIXER_STEPPER_LOOP(i)
|
||||
color[t][i] = (t == i) ? COLOR_A_MASK : 0;
|
||||
|
||||
// Remaining virtual tools are 100% filament 1
|
||||
#if MIXING_VIRTUAL_TOOLS > MIXING_STEPPERS
|
||||
for (uint8_t t = MIXING_STEPPERS; t < MIXING_VIRTUAL_TOOLS; ++t)
|
||||
MIXER_STEPPER_LOOP(i)
|
||||
color[t][i] = (i == 0) ? COLOR_A_MASK : 0;
|
||||
#endif
|
||||
|
||||
// MIXING_PRESETS: Set a variety of obvious mixes as presets
|
||||
#if ENABLED(MIXING_PRESETS) && WITHIN(MIXING_STEPPERS, 2, 3)
|
||||
#if MIXING_STEPPERS == 2
|
||||
if (MIXING_VIRTUAL_TOOLS > 2) { collector[0] = 1; collector[1] = 1; mixer.normalize(2); } // 1:1
|
||||
if (MIXING_VIRTUAL_TOOLS > 3) { collector[0] = 3; mixer.normalize(3); } // 3:1
|
||||
if (MIXING_VIRTUAL_TOOLS > 4) { collector[0] = 1; collector[1] = 3; mixer.normalize(4); } // 1:3
|
||||
if (MIXING_VIRTUAL_TOOLS > 5) { collector[1] = 2; mixer.normalize(5); } // 1:2
|
||||
if (MIXING_VIRTUAL_TOOLS > 6) { collector[0] = 2; collector[1] = 1; mixer.normalize(6); } // 2:1
|
||||
if (MIXING_VIRTUAL_TOOLS > 7) { collector[0] = 3; collector[1] = 2; mixer.normalize(7); } // 3:2
|
||||
#else
|
||||
if (MIXING_VIRTUAL_TOOLS > 3) { collector[0] = 1; collector[1] = 1; collector[2] = 1; mixer.normalize(3); } // 1:1:1
|
||||
if (MIXING_VIRTUAL_TOOLS > 4) { collector[1] = 3; collector[2] = 0; mixer.normalize(4); } // 1:3:0
|
||||
if (MIXING_VIRTUAL_TOOLS > 5) { collector[0] = 0; collector[2] = 1; mixer.normalize(5); } // 0:3:1
|
||||
if (MIXING_VIRTUAL_TOOLS > 6) { collector[1] = 1; mixer.normalize(6); } // 0:1:1
|
||||
if (MIXING_VIRTUAL_TOOLS > 7) { collector[0] = 1; collector[2] = 0; mixer.normalize(7); } // 1:1:0
|
||||
#endif
|
||||
ZERO(collector);
|
||||
#endif
|
||||
}
|
||||
|
||||
// called at boot
|
||||
void Mixer::init() {
|
||||
|
||||
ZERO(collector);
|
||||
|
||||
reset_vtools();
|
||||
|
||||
#if HAS_MIXER_SYNC_CHANNEL
|
||||
// AUTORETRACT_TOOL gets the same amount of all filaments
|
||||
MIXER_STEPPER_LOOP(i)
|
||||
color[MIXER_AUTORETRACT_TOOL][i] = COLOR_A_MASK;
|
||||
#endif
|
||||
|
||||
#if ANY(HAS_DUAL_MIXING, GRADIENT_MIX)
|
||||
update_mix_from_vtool();
|
||||
#endif
|
||||
|
||||
TERN_(GRADIENT_MIX, update_gradient_for_planner_z());
|
||||
}
|
||||
|
||||
void Mixer::refresh_collector(const float proportion/*=1.0*/, const uint8_t t/*=selected_vtool*/, float (&c)[MIXING_STEPPERS]/*=collector*/) {
|
||||
float csum = 0, cmax = 0;
|
||||
MIXER_STEPPER_LOOP(i) {
|
||||
const float v = color[t][i];
|
||||
cmax = _MAX(cmax, v);
|
||||
csum += v;
|
||||
}
|
||||
//SERIAL_ECHOPGM("Mixer::refresh_collector(", proportion, ", ", t, ") cmax=", cmax, " csum=", csum, " color");
|
||||
const float inv_prop = proportion / csum;
|
||||
MIXER_STEPPER_LOOP(i) {
|
||||
c[i] = color[t][i] * inv_prop;
|
||||
//SERIAL_ECHOPGM(" [", t, "][", i, "] = ", color[t][i], " (", c[i], ") ");
|
||||
}
|
||||
//SERIAL_EOL();
|
||||
}
|
||||
|
||||
#if ENABLED(GRADIENT_MIX)
|
||||
|
||||
#include "../module/motion.h"
|
||||
#include "../module/planner.h"
|
||||
|
||||
gradient_t Mixer::gradient = {
|
||||
false, // enabled
|
||||
{0}, // color (array)
|
||||
0, 0, // start_z, end_z
|
||||
0, 1, // start_vtool, end_vtool
|
||||
{0}, {0} // start_mix[], end_mix[]
|
||||
OPTARG(GRADIENT_VTOOL, -1) // vtool_index
|
||||
};
|
||||
|
||||
float Mixer::prev_z; // = 0
|
||||
|
||||
void Mixer::update_gradient_for_z(const_float_t z) {
|
||||
if (z == prev_z) return;
|
||||
prev_z = z;
|
||||
|
||||
const float slice = gradient.end_z - gradient.start_z;
|
||||
|
||||
float pct = (z - gradient.start_z) / slice;
|
||||
NOLESS(pct, 0.0f); NOMORE(pct, 1.0f);
|
||||
|
||||
MIXER_STEPPER_LOOP(i) {
|
||||
const mixer_perc_t sm = gradient.start_mix[i];
|
||||
mix[i] = sm + (gradient.end_mix[i] - sm) * pct;
|
||||
}
|
||||
|
||||
copy_mix_to_color(gradient.color);
|
||||
}
|
||||
|
||||
void Mixer::update_gradient_for_planner_z() {
|
||||
#if ENABLED(DELTA)
|
||||
get_cartesian_from_steppers();
|
||||
update_gradient_for_z(cartes.z);
|
||||
#else
|
||||
update_gradient_for_z(planner.get_axis_position_mm(Z_AXIS));
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // GRADIENT_MIX
|
||||
|
||||
#endif // MIXING_EXTRUDER
|
||||
255
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/mixing.h
Normal file
255
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/mixing.h
Normal file
@@ -0,0 +1,255 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../inc/MarlinConfig.h"
|
||||
|
||||
//#define MIXER_NORMALIZER_DEBUG
|
||||
|
||||
#ifndef __AVR__ // || HAS_DUAL_MIXING
|
||||
// Use 16-bit (or fastest) data for the integer mix factors
|
||||
typedef uint_fast16_t mixer_comp_t;
|
||||
typedef uint_fast16_t mixer_accu_t;
|
||||
#define COLOR_A_MASK 0x8000
|
||||
#define COLOR_MASK 0x7FFF
|
||||
#else
|
||||
// Use 8-bit data for the integer mix factors
|
||||
// Exactness is sacrificed for speed
|
||||
#define MIXER_ACCU_SIGNED
|
||||
typedef uint8_t mixer_comp_t;
|
||||
typedef int8_t mixer_accu_t;
|
||||
#define COLOR_A_MASK 0x80
|
||||
#define COLOR_MASK 0x7F
|
||||
#endif
|
||||
|
||||
typedef int8_t mixer_perc_t;
|
||||
|
||||
#ifndef MIXING_VIRTUAL_TOOLS
|
||||
#define MIXING_VIRTUAL_TOOLS 1
|
||||
#endif
|
||||
|
||||
enum MixTool {
|
||||
FIRST_USER_VIRTUAL_TOOL = 0
|
||||
, LAST_USER_VIRTUAL_TOOL = MIXING_VIRTUAL_TOOLS - 1
|
||||
, NR_USER_VIRTUAL_TOOLS
|
||||
, MIXER_DIRECT_SET_TOOL = NR_USER_VIRTUAL_TOOLS
|
||||
#if HAS_MIXER_SYNC_CHANNEL
|
||||
, MIXER_AUTORETRACT_TOOL
|
||||
#endif
|
||||
, NR_MIXING_VIRTUAL_TOOLS
|
||||
};
|
||||
|
||||
#define MAX_VTOOLS TERN(HAS_MIXER_SYNC_CHANNEL, 254, 255)
|
||||
static_assert(NR_MIXING_VIRTUAL_TOOLS <= MAX_VTOOLS, "MIXING_VIRTUAL_TOOLS must be <= " STRINGIFY(MAX_VTOOLS) "!");
|
||||
|
||||
#define MIXER_STEPPER_LOOP(VAR) for (uint_fast8_t VAR = 0; VAR < MIXING_STEPPERS; VAR++)
|
||||
|
||||
#if ENABLED(GRADIENT_MIX)
|
||||
|
||||
typedef struct {
|
||||
bool enabled; // This gradient is enabled
|
||||
mixer_comp_t color[MIXING_STEPPERS]; // The current gradient color
|
||||
float start_z, end_z; // Region for gradient
|
||||
int8_t start_vtool, end_vtool; // Start and end virtual tools
|
||||
mixer_perc_t start_mix[MIXING_STEPPERS], // Start and end mixes from those tools
|
||||
end_mix[MIXING_STEPPERS];
|
||||
#if ENABLED(GRADIENT_VTOOL)
|
||||
int8_t vtool_index; // Use this virtual tool number as index
|
||||
#endif
|
||||
} gradient_t;
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Mixer class
|
||||
* @details Contains data and behaviors for a Mixing Extruder
|
||||
*/
|
||||
class Mixer {
|
||||
public:
|
||||
|
||||
static float collector[MIXING_STEPPERS]; // M163 components, also editable from LCD
|
||||
|
||||
static void init(); // Populate colors at boot time
|
||||
|
||||
static void reset_vtools();
|
||||
static void refresh_collector(const float proportion=1.0, const uint8_t t=selected_vtool, float (&c)[MIXING_STEPPERS]=collector);
|
||||
|
||||
// Used up to Planner level
|
||||
FORCE_INLINE static void set_collector(const uint8_t c, const float f) { collector[c] = _MAX(f, 0.0f); }
|
||||
|
||||
static void normalize(const uint8_t tool_index);
|
||||
FORCE_INLINE static void normalize() { normalize(selected_vtool); }
|
||||
|
||||
FORCE_INLINE static uint8_t get_current_vtool() { return selected_vtool; }
|
||||
|
||||
FORCE_INLINE static void T(const uint_fast8_t c) {
|
||||
selected_vtool = c;
|
||||
TERN_(GRADIENT_VTOOL, refresh_gradient());
|
||||
TERN_(HAS_DUAL_MIXING, update_mix_from_vtool());
|
||||
}
|
||||
|
||||
// Used when dealing with blocks
|
||||
FORCE_INLINE static void populate_block(mixer_comp_t (&b_color)[MIXING_STEPPERS]) {
|
||||
#if ENABLED(GRADIENT_MIX)
|
||||
if (gradient.enabled) {
|
||||
MIXER_STEPPER_LOOP(i) b_color[i] = gradient.color[i];
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
MIXER_STEPPER_LOOP(i) b_color[i] = color[selected_vtool][i];
|
||||
}
|
||||
|
||||
FORCE_INLINE static void stepper_setup(mixer_comp_t (&b_color)[MIXING_STEPPERS]) {
|
||||
MIXER_STEPPER_LOOP(i) s_color[i] = b_color[i];
|
||||
}
|
||||
|
||||
#if ANY(HAS_DUAL_MIXING, GRADIENT_MIX)
|
||||
|
||||
static mixer_perc_t mix[MIXING_STEPPERS]; // Scratch array for the Mix in proportion to 100
|
||||
|
||||
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(
|
||||
LIST_N(MIXING_STEPPERS, mix[0], mix[1], mix[2], mix[3], mix[4], mix[5])
|
||||
));
|
||||
|
||||
// Scale all values so their maximum is COLOR_A_MASK
|
||||
MIXER_STEPPER_LOOP(i) tcolor[i] = mix[i] * scale;
|
||||
|
||||
#ifdef MIXER_NORMALIZER_DEBUG
|
||||
SERIAL_ECHOPGM("Mix [ ");
|
||||
SERIAL_ECHOLIST_N(MIXING_STEPPERS, mix[0], mix[1], mix[2], mix[3], mix[4], mix[5]);
|
||||
SERIAL_ECHOPGM(" ] to Color [ ");
|
||||
SERIAL_ECHOLIST_N(MIXING_STEPPERS, tcolor[0], tcolor[1], tcolor[2], tcolor[3], tcolor[4], tcolor[5]);
|
||||
SERIAL_ECHOLNPGM(" ]");
|
||||
#endif
|
||||
}
|
||||
|
||||
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] = mixer_perc_t(100.0f * color[j][i] / ctot + 0.5f);
|
||||
|
||||
#ifdef MIXER_NORMALIZER_DEBUG
|
||||
SERIAL_ECHOPGM("V-tool ", j, " [ ");
|
||||
SERIAL_ECHOLIST_N(MIXING_STEPPERS, color[j][0], color[j][1], color[j][2], color[j][3], color[j][4], color[j][5]);
|
||||
SERIAL_ECHOPGM(" ] to Mix [ ");
|
||||
SERIAL_ECHOLIST_N(MIXING_STEPPERS, mix[0], mix[1], mix[2], mix[3], mix[4], mix[5]);
|
||||
SERIAL_ECHOLNPGM(" ]");
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // HAS_DUAL_MIXING || GRADIENT_MIX
|
||||
|
||||
#if HAS_DUAL_MIXING
|
||||
|
||||
// Update the virtual tool from an edited 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];
|
||||
// normalize();
|
||||
}
|
||||
|
||||
#endif // HAS_DUAL_MIXING
|
||||
|
||||
#if ENABLED(GRADIENT_MIX)
|
||||
|
||||
static gradient_t gradient;
|
||||
static float prev_z;
|
||||
|
||||
// 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 void gradient_control(const_float_t z) {
|
||||
if (gradient.enabled) {
|
||||
if (z >= gradient.end_z)
|
||||
T(gradient.end_vtool);
|
||||
else
|
||||
update_gradient_for_z(z);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
#ifdef MIXER_NORMALIZER_DEBUG
|
||||
SERIAL_ECHOPGM("Gradient [ ");
|
||||
SERIAL_ECHOLIST_N(MIXING_STEPPERS, gradient.color[0], gradient.color[1], gradient.color[2], gradient.color[3], gradient.color[4], gradient.color[5]);
|
||||
SERIAL_ECHOPGM(" ] to Mix [ ");
|
||||
SERIAL_ECHOLIST_N(MIXING_STEPPERS, mix[0], mix[1], mix[2], mix[3], mix[4], mix[5]);
|
||||
SERIAL_ECHOLNPGM(" ]");
|
||||
#endif
|
||||
}
|
||||
|
||||
// Refresh the gradient after a change
|
||||
static void refresh_gradient() {
|
||||
#if ENABLED(GRADIENT_VTOOL)
|
||||
const bool is_grd = (gradient.vtool_index == -1 || selected_vtool == (uint8_t)gradient.vtool_index);
|
||||
#else
|
||||
constexpr bool is_grd = true;
|
||||
#endif
|
||||
gradient.enabled = is_grd && gradient.start_vtool != gradient.end_vtool && gradient.start_z < gradient.end_z;
|
||||
if (gradient.enabled) {
|
||||
mixer_perc_t mix_bak[MIXING_STEPPERS];
|
||||
COPY(mix_bak, mix);
|
||||
update_mix_from_vtool(gradient.start_vtool);
|
||||
COPY(gradient.start_mix, mix);
|
||||
update_mix_from_vtool(gradient.end_vtool);
|
||||
COPY(gradient.end_mix, mix);
|
||||
update_gradient_for_planner_z();
|
||||
COPY(mix, mix_bak);
|
||||
prev_z = -1;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // GRADIENT_MIX
|
||||
|
||||
// Used in Stepper
|
||||
FORCE_INLINE static uint8_t get_stepper() { return runner; }
|
||||
FORCE_INLINE static uint8_t get_next_stepper() {
|
||||
for (;;) {
|
||||
if (--runner < 0) runner = MIXING_STEPPERS - 1;
|
||||
accu[runner] += s_color[runner];
|
||||
if (TERN(MIXER_ACCU_SIGNED, accu[runner] < 0, accu[runner] & COLOR_A_MASK)) {
|
||||
accu[runner] &= COLOR_MASK;
|
||||
return runner;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// Used up to Planner level
|
||||
static uint_fast8_t selected_vtool;
|
||||
static mixer_comp_t color[NR_MIXING_VIRTUAL_TOOLS][MIXING_STEPPERS];
|
||||
|
||||
// Used in Stepper
|
||||
static int_fast8_t runner;
|
||||
static mixer_comp_t s_color[MIXING_STEPPERS];
|
||||
static mixer_accu_t accu[MIXING_STEPPERS];
|
||||
};
|
||||
|
||||
extern Mixer mixer;
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if HAS_PRUSA_MMU1
|
||||
|
||||
#include "../../MarlinCore.h"
|
||||
#include "../../module/planner.h"
|
||||
#include "../../module/stepper.h"
|
||||
|
||||
void mmu_init() {
|
||||
SET_OUTPUT(E_MUX0_PIN);
|
||||
SET_OUTPUT(E_MUX1_PIN);
|
||||
SET_OUTPUT(E_MUX2_PIN);
|
||||
}
|
||||
|
||||
void select_multiplexed_stepper(const uint8_t e) {
|
||||
planner.synchronize();
|
||||
stepper.disable_e_steppers();
|
||||
WRITE(E_MUX0_PIN, TEST(e, 0) ? HIGH : LOW);
|
||||
WRITE(E_MUX1_PIN, TEST(e, 1) ? HIGH : LOW);
|
||||
WRITE(E_MUX2_PIN, TEST(e, 2) ? HIGH : LOW);
|
||||
safe_delay(100);
|
||||
}
|
||||
|
||||
#endif // HAS_PRUSA_MMU1
|
||||
25
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/mmu/mmu.h
Normal file
25
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/mmu/mmu.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
void mmu_init();
|
||||
void select_multiplexed_stepper(const uint8_t e);
|
||||
@@ -0,0 +1,84 @@
|
||||
Startup sequence
|
||||
================
|
||||
|
||||
When initialized, MMU sends
|
||||
|
||||
- MMU => 'start\n'
|
||||
|
||||
We follow with
|
||||
|
||||
- MMU <= 'S1\n'
|
||||
- MMU => 'ok*Firmware version*\n'
|
||||
- MMU <= 'S2\n'
|
||||
- MMU => 'ok*Build number*\n'
|
||||
|
||||
#if (12V_mode)
|
||||
|
||||
- MMU <= 'M1\n'
|
||||
- MMU => 'ok\n'
|
||||
|
||||
#endif
|
||||
|
||||
- MMU <= 'P0\n'
|
||||
- MMU => '*FINDA status*\n'
|
||||
|
||||
Now we are sure MMU is available and ready. If there was a timeout or other communication problem somewhere, printer will be killed.
|
||||
|
||||
- *Firmware version* is an integer value, but we don't care about it
|
||||
- *Build number* is an integer value and has to be >=126, or =>132 if 12V mode is enabled
|
||||
- *FINDA status* is 1 if the filament is loaded to the extruder, 0 otherwise
|
||||
|
||||
*Build number* is checked against the required value, if it does not match, printer is halted.
|
||||
|
||||
Toolchange
|
||||
==========
|
||||
|
||||
- MMU <= 'T*Filament index*\n'
|
||||
|
||||
MMU sends
|
||||
|
||||
- MMU => 'ok\n'
|
||||
|
||||
as soon as the filament is fed down to the extruder. We follow with
|
||||
|
||||
- MMU <= 'C0\n'
|
||||
|
||||
MMU will feed a few more millimeters of filament for the extruder gears to grab.
|
||||
When done, the MMU sends
|
||||
|
||||
- MMU => 'ok\n'
|
||||
|
||||
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.
|
||||
|
||||
FINDA status
|
||||
============
|
||||
|
||||
- MMU <= 'P0\n'
|
||||
- MMU => '*FINDA status*\n'
|
||||
|
||||
*FINDA status* is 1 if the is filament loaded to the extruder, 0 otherwise. This could be used as filament runout sensor if probed regularly.
|
||||
|
||||
Load filament
|
||||
=============
|
||||
|
||||
- MMU <= 'L*Filament index*\n'
|
||||
|
||||
MMU will feed filament down to the extruder, when done
|
||||
|
||||
- MMU => 'ok\n'
|
||||
|
||||
Unload filament
|
||||
=============
|
||||
|
||||
- MMU <= 'U0\n'
|
||||
|
||||
MMU will retract current filament from the extruder, when done
|
||||
|
||||
- MMU => 'ok\n'
|
||||
|
||||
Eject filament
|
||||
==============
|
||||
|
||||
- MMU <= 'E*Filament index*\n'
|
||||
- MMU => 'ok\n'
|
||||
1062
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/mmu/mmu2.cpp
Normal file
1062
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/mmu/mmu2.cpp
Normal file
File diff suppressed because it is too large
Load Diff
112
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/mmu/mmu2.h
Normal file
112
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/mmu/mmu2.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if HAS_FILAMENT_SENSOR
|
||||
#include "../runout.h"
|
||||
#endif
|
||||
|
||||
#if SERIAL_USB
|
||||
#define MMU_RX_SIZE 256
|
||||
#define MMU_TX_SIZE 256
|
||||
#else
|
||||
#define MMU_RX_SIZE 16
|
||||
#define MMU_TX_SIZE 16
|
||||
#endif
|
||||
|
||||
struct E_Step;
|
||||
|
||||
class MMU2 {
|
||||
public:
|
||||
MMU2();
|
||||
|
||||
static void init();
|
||||
static void reset();
|
||||
static bool enabled() { return _enabled; }
|
||||
static void mmu_loop();
|
||||
static void tool_change(const uint8_t index);
|
||||
static void tool_change(const char *special);
|
||||
static int8_t get_current_tool();
|
||||
static void set_filament_type(const uint8_t index, const uint8_t type);
|
||||
|
||||
static bool unload();
|
||||
static void load_filament(uint8_t);
|
||||
static void load_all();
|
||||
static bool load_filament_to_nozzle(const uint8_t index);
|
||||
static bool eject_filament(const uint8_t index, const bool recover);
|
||||
|
||||
private:
|
||||
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();
|
||||
static bool rx_start();
|
||||
static void check_version();
|
||||
|
||||
static void command(const uint8_t cmd);
|
||||
static bool get_response();
|
||||
static void manage_response(const bool move_axes, const bool turn_off_nozzle);
|
||||
|
||||
static void load_to_nozzle();
|
||||
static void execute_extruder_sequence(const E_Step * sequence, int steps);
|
||||
|
||||
static void filament_runout();
|
||||
|
||||
#if HAS_PRUSA_MMU2S
|
||||
static bool mmu2s_triggered;
|
||||
static void check_filament();
|
||||
static bool can_load();
|
||||
static bool load_to_gears();
|
||||
#else
|
||||
FORCE_INLINE static bool load_to_gears() { return true; }
|
||||
#endif
|
||||
|
||||
#if ENABLED(MMU_EXTRUDER_SENSOR)
|
||||
#define MMU_LOAD_FEEDRATE 19.02f // (mm/s)
|
||||
static void mmu_continue_loading();
|
||||
#endif
|
||||
|
||||
static bool _enabled, ready, mmu_print_saved;
|
||||
|
||||
static uint8_t cmd, cmd_arg, last_cmd, extruder;
|
||||
static int8_t state;
|
||||
static volatile int8_t finda;
|
||||
static volatile bool finda_runout_valid;
|
||||
static int16_t version, buildnr;
|
||||
static millis_t prev_request, prev_P0_request;
|
||||
static char rx_buffer[MMU_RX_SIZE], tx_buffer[MMU_TX_SIZE];
|
||||
|
||||
static void set_runout_valid(const bool valid) {
|
||||
finda_runout_valid = valid;
|
||||
#if HAS_FILAMENT_SENSOR
|
||||
if (valid) runout.reset();
|
||||
#endif
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
extern MMU2 mmu2;
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfigPre.h"
|
||||
|
||||
#if ENABLED(PASSWORD_FEATURE)
|
||||
|
||||
#include "password.h"
|
||||
#include "../../gcode/gcode.h"
|
||||
#include "../../core/serial.h"
|
||||
|
||||
Password password;
|
||||
|
||||
// public:
|
||||
bool Password::is_set, Password::is_locked, Password::did_first_run; // = false
|
||||
uint32_t Password::value, Password::value_entry;
|
||||
|
||||
//
|
||||
// Authenticate user with password.
|
||||
// Called from Setup, after SD Prinitng Stops/Aborts, and M510
|
||||
//
|
||||
void Password::lock_machine() {
|
||||
is_locked = true;
|
||||
TERN_(HAS_MARLINUI_MENU, authenticate_user(ui.status_screen, screen_password_entry));
|
||||
}
|
||||
|
||||
//
|
||||
// Authentication check
|
||||
//
|
||||
void Password::authentication_check() {
|
||||
if (value_entry == value) {
|
||||
is_locked = false;
|
||||
did_first_run = true;
|
||||
}
|
||||
else {
|
||||
is_locked = true;
|
||||
SERIAL_ECHOLNPGM(STR_WRONG_PASSWORD);
|
||||
}
|
||||
TERN_(HAS_MARLINUI_MENU, authentication_done());
|
||||
}
|
||||
|
||||
#endif // PASSWORD_FEATURE
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* 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 "../../lcd/marlinui.h"
|
||||
|
||||
class Password {
|
||||
public:
|
||||
static bool is_set, is_locked, did_first_run;
|
||||
static uint32_t value, value_entry;
|
||||
|
||||
Password() {}
|
||||
|
||||
static void lock_machine();
|
||||
static void authentication_check();
|
||||
|
||||
#if HAS_MARLINUI_MENU
|
||||
static void access_menu_password();
|
||||
static void authentication_done();
|
||||
static void media_gatekeeper();
|
||||
|
||||
private:
|
||||
static void authenticate_user(const screenFunc_t, const screenFunc_t);
|
||||
static void menu_password();
|
||||
static void menu_password_entry();
|
||||
static void screen_password_entry();
|
||||
static void screen_set_password();
|
||||
static void start_over();
|
||||
|
||||
static void digit_entered();
|
||||
static void set_password_done(const bool with_set=true);
|
||||
static void menu_password_report();
|
||||
|
||||
static void remove_password();
|
||||
#endif
|
||||
};
|
||||
|
||||
extern Password password;
|
||||
718
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/pause.cpp
Normal file
718
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/pause.cpp
Normal file
@@ -0,0 +1,718 @@
|
||||
/**
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* feature/pause.cpp - Pause feature support functions
|
||||
* This may be combined with related G-codes if features are consolidated.
|
||||
*/
|
||||
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
|
||||
#if ENABLED(ADVANCED_PAUSE_FEATURE)
|
||||
|
||||
//#define DEBUG_PAUSE_RESUME
|
||||
|
||||
#include "../MarlinCore.h"
|
||||
#include "../gcode/gcode.h"
|
||||
#include "../module/motion.h"
|
||||
#include "../module/planner.h"
|
||||
#include "../module/printcounter.h"
|
||||
#include "../module/temperature.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
|
||||
|
||||
#if HAS_FILAMENT_SENSOR
|
||||
#include "runout.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(HOST_ACTION_COMMANDS)
|
||||
#include "host_actions.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(EXTENSIBLE_UI)
|
||||
#include "../lcd/extui/ui_api.h"
|
||||
#elif ENABLED(DWIN_LCD_PROUI)
|
||||
#include "../lcd/e3v2/proui/dwin.h"
|
||||
#endif
|
||||
|
||||
#include "../lcd/marlinui.h"
|
||||
|
||||
#if HAS_SOUND
|
||||
#include "../libs/buzzer.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(POWER_LOSS_RECOVERY)
|
||||
#include "powerloss.h"
|
||||
#endif
|
||||
|
||||
#include "../libs/nozzle.h"
|
||||
#include "pause.h"
|
||||
|
||||
#define DEBUG_OUT ENABLED(DEBUG_PAUSE_RESUME)
|
||||
#include "../core/debug_out.h"
|
||||
|
||||
// private:
|
||||
|
||||
static xyze_pos_t resume_position;
|
||||
|
||||
#if M600_PURGE_MORE_RESUMABLE
|
||||
PauseMenuResponse pause_menu_response;
|
||||
PauseMode pause_mode = PAUSE_MODE_PAUSE_PRINT;
|
||||
#endif
|
||||
|
||||
fil_change_settings_t fc_settings[EXTRUDERS];
|
||||
|
||||
#if HAS_MEDIA
|
||||
#include "../sd/cardreader.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(EMERGENCY_PARSER)
|
||||
#define _PMSG(L) L##_M108
|
||||
#else
|
||||
#define _PMSG(L) L##_LCD
|
||||
#endif
|
||||
|
||||
#if HAS_SOUND
|
||||
static void impatient_beep(const int8_t max_beep_count, const bool restart=false) {
|
||||
|
||||
if (TERN0(HAS_MARLINUI_MENU, pause_mode == PAUSE_MODE_PAUSE_PRINT)) return;
|
||||
|
||||
static millis_t next_buzz = 0;
|
||||
static int8_t runout_beep = 0;
|
||||
|
||||
if (restart) next_buzz = runout_beep = 0;
|
||||
|
||||
const bool always = max_beep_count < 0;
|
||||
|
||||
const millis_t ms = millis();
|
||||
if (ELAPSED(ms, next_buzz)) {
|
||||
if (always || runout_beep < max_beep_count + 5) { // Only beep as long as we're supposed to
|
||||
next_buzz = ms + ((always || runout_beep < max_beep_count) ? 1000 : 500);
|
||||
BUZZ(50, 880 - (runout_beep & 1) * 220);
|
||||
runout_beep++;
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void first_impatient_beep(const int8_t max_beep_count) { impatient_beep(max_beep_count, true); }
|
||||
#else
|
||||
inline void impatient_beep(const int8_t, const bool=false) {}
|
||||
inline void first_impatient_beep(const int8_t) {}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Ensure a safe temperature for extrusion
|
||||
*
|
||||
* - Fail if the TARGET temperature is too low
|
||||
* - Display LCD placard with temperature status
|
||||
* - Return when heating is done or aborted
|
||||
*
|
||||
* Returns 'true' if heating was completed, 'false' for abort
|
||||
*/
|
||||
static bool ensure_safe_temperature(const bool wait=true, const PauseMode mode=PAUSE_MODE_SAME) {
|
||||
DEBUG_SECTION(est, "ensure_safe_temperature", true);
|
||||
DEBUG_ECHOLNPGM("... wait:", wait, " mode:", mode);
|
||||
|
||||
#if ENABLED(PREVENT_COLD_EXTRUSION)
|
||||
if (!DEBUGGING(DRYRUN) && thermalManager.targetTooColdToExtrude(active_extruder))
|
||||
thermalManager.setTargetHotend(thermalManager.extrude_min_temp, active_extruder);
|
||||
#endif
|
||||
|
||||
ui.pause_show_message(PAUSE_MESSAGE_HEATING, mode); UNUSED(mode);
|
||||
|
||||
if (wait) return thermalManager.wait_for_hotend(active_extruder);
|
||||
|
||||
// Allow interruption by Emergency Parser M108
|
||||
wait_for_heatup = TERN1(PREVENT_COLD_EXTRUSION, !thermalManager.allow_cold_extrude);
|
||||
while (wait_for_heatup && ABS(thermalManager.wholeDegHotend(active_extruder) - thermalManager.degTargetHotend(active_extruder)) > (TEMP_WINDOW))
|
||||
idle();
|
||||
wait_for_heatup = false;
|
||||
|
||||
#if ENABLED(PREVENT_COLD_EXTRUSION)
|
||||
// A user can cancel wait-for-heating with M108
|
||||
if (!DEBUGGING(DRYRUN) && thermalManager.targetTooColdToExtrude(active_extruder)) {
|
||||
SERIAL_ECHO_MSG(STR_ERR_HOTEND_TOO_COLD);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load filament into the hotend
|
||||
*
|
||||
* - Fail if the a safe temperature was not reached
|
||||
* - If pausing for confirmation, wait for a click or M108
|
||||
* - Show "wait for load" placard
|
||||
* - Load and purge filament
|
||||
* - Show "Purge more" / "Continue" menu
|
||||
* - Return when "Continue" is selected
|
||||
*
|
||||
* Returns 'true' if load was completed, 'false' for abort
|
||||
*/
|
||||
bool load_filament(const_float_t slow_load_length/*=0*/, const_float_t fast_load_length/*=0*/, const_float_t purge_length/*=0*/, const int8_t max_beep_count/*=0*/,
|
||||
const bool show_lcd/*=false*/, const bool pause_for_user/*=false*/,
|
||||
const PauseMode mode/*=PAUSE_MODE_PAUSE_PRINT*/
|
||||
DXC_ARGS
|
||||
) {
|
||||
DEBUG_SECTION(lf, "load_filament", true);
|
||||
DEBUG_ECHOLNPGM("... slowlen:", slow_load_length, " fastlen:", fast_load_length, " purgelen:", purge_length, " maxbeep:", max_beep_count, " showlcd:", show_lcd, " pauseforuser:", pause_for_user, " pausemode:", mode DXC_SAY);
|
||||
|
||||
if (!ensure_safe_temperature(false, mode)) {
|
||||
if (show_lcd) ui.pause_show_message(PAUSE_MESSAGE_STATUS, mode);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pause_for_user) {
|
||||
if (show_lcd) ui.pause_show_message(PAUSE_MESSAGE_INSERT, mode);
|
||||
SERIAL_ECHO_MSG(_PMSG(STR_FILAMENT_CHANGE_INSERT));
|
||||
|
||||
first_impatient_beep(max_beep_count);
|
||||
|
||||
KEEPALIVE_STATE(PAUSED_FOR_USER);
|
||||
wait_for_user = true; // LCD click or M108 will clear this
|
||||
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired(GET_TEXT_F(MSG_FILAMENTLOAD)));
|
||||
|
||||
#if ENABLED(HOST_PROMPT_SUPPORT)
|
||||
const char tool = '0' + TERN0(MULTI_FILAMENT_SENSOR, active_extruder);
|
||||
hostui.prompt_do(PROMPT_USER_CONTINUE, F("Load Filament T"), tool, FPSTR(CONTINUE_STR));
|
||||
#endif
|
||||
|
||||
while (wait_for_user) {
|
||||
impatient_beep(max_beep_count);
|
||||
#if ALL(FILAMENT_CHANGE_RESUME_ON_INSERT, FILAMENT_RUNOUT_SENSOR)
|
||||
#if MULTI_FILAMENT_SENSOR
|
||||
#define _CASE_INSERTED(N) case N-1: if (READ(FIL_RUNOUT##N##_PIN) != FIL_RUNOUT##N##_STATE) wait_for_user = false; break;
|
||||
switch (active_extruder) {
|
||||
REPEAT_1(NUM_RUNOUT_SENSORS, _CASE_INSERTED)
|
||||
}
|
||||
#else
|
||||
if (READ(FIL_RUNOUT_PIN) != FIL_RUNOUT_STATE) wait_for_user = false;
|
||||
#endif
|
||||
#endif
|
||||
idle_no_sleep();
|
||||
}
|
||||
}
|
||||
|
||||
if (show_lcd) ui.pause_show_message(PAUSE_MESSAGE_LOAD, mode);
|
||||
|
||||
#if ENABLED(DUAL_X_CARRIAGE)
|
||||
const int8_t saved_ext = active_extruder;
|
||||
const bool saved_ext_dup_mode = extruder_duplication_enabled;
|
||||
set_duplication_enabled(false, DXC_ext);
|
||||
#endif
|
||||
|
||||
TERN_(BELTPRINTER, do_blocking_move_to_xy(0.00, 50.00));
|
||||
|
||||
// Slow Load filament
|
||||
if (slow_load_length) unscaled_e_move(slow_load_length, FILAMENT_CHANGE_SLOW_LOAD_FEEDRATE);
|
||||
|
||||
// Fast Load Filament
|
||||
if (fast_load_length) {
|
||||
#if FILAMENT_CHANGE_FAST_LOAD_ACCEL > 0
|
||||
const float saved_acceleration = planner.settings.retract_acceleration;
|
||||
planner.settings.retract_acceleration = FILAMENT_CHANGE_FAST_LOAD_ACCEL;
|
||||
#endif
|
||||
|
||||
unscaled_e_move(fast_load_length, FILAMENT_CHANGE_FAST_LOAD_FEEDRATE);
|
||||
|
||||
#if FILAMENT_CHANGE_FAST_LOAD_ACCEL > 0
|
||||
planner.settings.retract_acceleration = saved_acceleration;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ENABLED(DUAL_X_CARRIAGE) // Tie the two extruders movement back together.
|
||||
set_duplication_enabled(saved_ext_dup_mode, saved_ext);
|
||||
#endif
|
||||
|
||||
#if ENABLED(ADVANCED_PAUSE_CONTINUOUS_PURGE)
|
||||
|
||||
if (show_lcd) ui.pause_show_message(PAUSE_MESSAGE_PURGE);
|
||||
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired(GET_TEXT_F(MSG_FILAMENT_CHANGE_PURGE)));
|
||||
TERN_(HOST_PROMPT_SUPPORT, hostui.continue_prompt(GET_TEXT_F(MSG_FILAMENT_CHANGE_PURGE)));
|
||||
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);
|
||||
wait_for_user = false;
|
||||
|
||||
#else
|
||||
|
||||
do {
|
||||
if (purge_length > 0) {
|
||||
// "Wait for filament purge"
|
||||
if (show_lcd) ui.pause_show_message(PAUSE_MESSAGE_PURGE);
|
||||
|
||||
// Extrude filament to get into hotend
|
||||
unscaled_e_move(purge_length, ADVANCED_PAUSE_PURGE_FEEDRATE);
|
||||
}
|
||||
|
||||
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 ANY(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;
|
||||
#endif
|
||||
while (pause_menu_response == PAUSE_RESPONSE_WAIT_FOR) idle_no_sleep();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Keep looping if "Purge More" was selected
|
||||
} while (TERN0(M600_PURGE_MORE_RESUMABLE, pause_menu_response == PAUSE_RESPONSE_EXTRUDE_MORE));
|
||||
|
||||
#endif
|
||||
TERN_(HOST_PROMPT_SUPPORT, hostui.prompt_end());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disabling E steppers for manual filament change should be fine
|
||||
* as long as users don't spin the E motor ridiculously fast and
|
||||
* send current back to their board, potentially frying it.
|
||||
*/
|
||||
inline void disable_active_extruder() {
|
||||
#if HAS_EXTRUDERS
|
||||
stepper.DISABLE_EXTRUDER(active_extruder);
|
||||
safe_delay(100);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Unload filament from the hotend
|
||||
*
|
||||
* - Fail if the a safe temperature was not reached
|
||||
* - Show "wait for unload" placard
|
||||
* - Retract, pause, then unload filament
|
||||
* - Disable E stepper (on most machines)
|
||||
*
|
||||
* Returns 'true' if unload was completed, 'false' for abort
|
||||
*/
|
||||
bool unload_filament(const_float_t unload_length, const bool show_lcd/*=false*/,
|
||||
const PauseMode mode/*=PAUSE_MODE_PAUSE_PRINT*/
|
||||
#if ALL(FILAMENT_UNLOAD_ALL_EXTRUDERS, MIXING_EXTRUDER)
|
||||
, const_float_t mix_multiplier/*=1.0*/
|
||||
#endif
|
||||
) {
|
||||
DEBUG_SECTION(uf, "unload_filament", true);
|
||||
DEBUG_ECHOLNPGM("... unloadlen:", unload_length, " showlcd:", show_lcd, " mode:", mode
|
||||
#if ALL(FILAMENT_UNLOAD_ALL_EXTRUDERS, MIXING_EXTRUDER)
|
||||
, " mixmult:", mix_multiplier
|
||||
#endif
|
||||
);
|
||||
|
||||
#if !ALL(FILAMENT_UNLOAD_ALL_EXTRUDERS, MIXING_EXTRUDER)
|
||||
constexpr float mix_multiplier = 1.0f;
|
||||
#endif
|
||||
|
||||
if (!ensure_safe_temperature(false, mode)) {
|
||||
if (show_lcd) ui.pause_show_message(PAUSE_MESSAGE_STATUS);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (show_lcd) ui.pause_show_message(PAUSE_MESSAGE_UNLOAD, mode);
|
||||
|
||||
// Retract filament
|
||||
unscaled_e_move(-(FILAMENT_UNLOAD_PURGE_RETRACT) * mix_multiplier, (PAUSE_PARK_RETRACT_FEEDRATE) * mix_multiplier);
|
||||
|
||||
// Wait for filament to cool
|
||||
safe_delay(FILAMENT_UNLOAD_PURGE_DELAY);
|
||||
|
||||
// Quickly purge
|
||||
unscaled_e_move((FILAMENT_UNLOAD_PURGE_RETRACT + FILAMENT_UNLOAD_PURGE_LENGTH) * mix_multiplier,
|
||||
(FILAMENT_UNLOAD_PURGE_FEEDRATE) * mix_multiplier);
|
||||
|
||||
// Unload filament
|
||||
#if FILAMENT_CHANGE_UNLOAD_ACCEL > 0
|
||||
const float saved_acceleration = planner.settings.retract_acceleration;
|
||||
planner.settings.retract_acceleration = FILAMENT_CHANGE_UNLOAD_ACCEL;
|
||||
#endif
|
||||
|
||||
unscaled_e_move(unload_length * mix_multiplier, (FILAMENT_CHANGE_UNLOAD_FEEDRATE) * mix_multiplier);
|
||||
|
||||
#if FILAMENT_CHANGE_FAST_LOAD_ACCEL > 0
|
||||
planner.settings.retract_acceleration = saved_acceleration;
|
||||
#endif
|
||||
|
||||
// Disable the Extruder for manual change
|
||||
disable_active_extruder();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// public:
|
||||
|
||||
/**
|
||||
* Pause procedure
|
||||
*
|
||||
* - Abort if already paused
|
||||
* - Send host action for pause, if configured
|
||||
* - Abort if TARGET temperature is too low
|
||||
* - Display "wait for start of filament change" (if a length was specified)
|
||||
* - Initial retract, if current temperature is hot enough
|
||||
* - Park the nozzle at the given position
|
||||
* - Call unload_filament (if a length was specified)
|
||||
*
|
||||
* Return 'true' if pause was completed, 'false' for abort
|
||||
*/
|
||||
uint8_t did_pause_print = 0;
|
||||
|
||||
bool pause_print(const_float_t retract, const xyz_pos_t &park_point, const bool show_lcd/*=false*/, const_float_t unload_length/*=0*/ DXC_ARGS) {
|
||||
DEBUG_SECTION(pp, "pause_print", true);
|
||||
DEBUG_ECHOLNPGM("... park.x:", park_point.x, " y:", park_point.y, " z:", park_point.z, " unloadlen:", unload_length, " showlcd:", show_lcd DXC_SAY);
|
||||
|
||||
UNUSED(show_lcd);
|
||||
|
||||
if (did_pause_print) return false; // already paused
|
||||
|
||||
#if ENABLED(HOST_ACTION_COMMANDS)
|
||||
#ifdef ACTION_ON_PAUSED
|
||||
hostui.paused();
|
||||
#elif defined(ACTION_ON_PAUSE)
|
||||
hostui.pause();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
TERN_(HOST_PROMPT_SUPPORT, hostui.prompt_open(PROMPT_INFO, F("Pause"), FPSTR(DISMISS_STR)));
|
||||
|
||||
// Indicate that the printer is paused
|
||||
++did_pause_print;
|
||||
|
||||
// Pause the print job and timer
|
||||
#if HAS_MEDIA
|
||||
const bool was_sd_printing = IS_SD_PRINTING();
|
||||
if (was_sd_printing) {
|
||||
card.pauseSDPrint();
|
||||
++did_pause_print; // Indicate SD pause also
|
||||
}
|
||||
#endif
|
||||
|
||||
print_job_timer.pause();
|
||||
|
||||
// Save current position
|
||||
resume_position = current_position;
|
||||
|
||||
// Will the nozzle be parking?
|
||||
const bool do_park = !axes_should_home();
|
||||
|
||||
#if ENABLED(POWER_LOSS_RECOVERY)
|
||||
// Save PLR info in case the power goes out while parked
|
||||
const float park_raise = do_park ? nozzle.park_mode_0_height(park_point.z) - current_position.z : POWER_LOSS_ZRAISE;
|
||||
if (was_sd_printing && recovery.enabled) recovery.save(true, park_raise, do_park);
|
||||
#endif
|
||||
|
||||
// Wait for buffered blocks to complete
|
||||
planner.synchronize();
|
||||
|
||||
#if ALL(ADVANCED_PAUSE_FANS_PAUSE, HAS_FAN)
|
||||
thermalManager.set_fans_paused(true);
|
||||
#endif
|
||||
|
||||
// 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
|
||||
if (do_park) nozzle.park(0, park_point); // Park the nozzle by doing a Minimum Z Raise followed by an XY Move
|
||||
if (!do_park) LCD_MESSAGE(MSG_PARK_FAILED);
|
||||
|
||||
#if ENABLED(DUAL_X_CARRIAGE)
|
||||
const int8_t saved_ext = active_extruder;
|
||||
const bool saved_ext_dup_mode = extruder_duplication_enabled;
|
||||
set_duplication_enabled(false, DXC_ext);
|
||||
#endif
|
||||
|
||||
// Unload the filament, if specified
|
||||
if (unload_length)
|
||||
unload_filament(unload_length, show_lcd, PAUSE_MODE_CHANGE_FILAMENT);
|
||||
|
||||
TERN_(DUAL_X_CARRIAGE, set_duplication_enabled(saved_ext_dup_mode, saved_ext));
|
||||
|
||||
// Disable the Extruder for manual change
|
||||
disable_active_extruder();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* For Paused Print:
|
||||
* - Show "Press button (or M108) to resume"
|
||||
*
|
||||
* For Filament Change:
|
||||
* - Show "Insert filament and press button to continue"
|
||||
*
|
||||
* - Wait for a click before returning
|
||||
* - Heaters can time out and must reheat before continuing
|
||||
*
|
||||
* Used by M125 and M600
|
||||
*/
|
||||
|
||||
void show_continue_prompt(const bool is_reload) {
|
||||
DEBUG_SECTION(scp, "pause_print", true);
|
||||
DEBUG_ECHOLNPGM("... is_reload:", is_reload);
|
||||
|
||||
ui.pause_show_message(is_reload ? PAUSE_MESSAGE_INSERT : PAUSE_MESSAGE_WAITING);
|
||||
SERIAL_ECHO_START();
|
||||
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) {
|
||||
DEBUG_SECTION(wfc, "wait_for_confirmation", true);
|
||||
DEBUG_ECHOLNPGM("... is_reload:", is_reload, " maxbeep:", max_beep_count DXC_SAY);
|
||||
|
||||
bool nozzle_timed_out = false;
|
||||
|
||||
show_continue_prompt(is_reload);
|
||||
|
||||
first_impatient_beep(max_beep_count);
|
||||
|
||||
// Start the heater idle timers
|
||||
const millis_t nozzle_timeout = SEC_TO_MS(PAUSE_PARK_NOZZLE_TIMEOUT);
|
||||
|
||||
HOTEND_LOOP() thermalManager.heater_idle[e].start(nozzle_timeout);
|
||||
|
||||
#if ENABLED(DUAL_X_CARRIAGE)
|
||||
const int8_t saved_ext = active_extruder;
|
||||
const bool saved_ext_dup_mode = extruder_duplication_enabled;
|
||||
set_duplication_enabled(false, DXC_ext);
|
||||
#endif
|
||||
|
||||
// Wait for filament insert by user and press button
|
||||
KEEPALIVE_STATE(PAUSED_FOR_USER);
|
||||
TERN_(HOST_PROMPT_SUPPORT, hostui.continue_prompt(GET_TEXT_F(MSG_NOZZLE_PARKED)));
|
||||
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);
|
||||
|
||||
// If the nozzle has timed out...
|
||||
if (!nozzle_timed_out)
|
||||
HOTEND_LOOP() nozzle_timed_out |= thermalManager.heater_idle[e].timed_out;
|
||||
|
||||
// Wait for the user to press the button to re-heat the nozzle, then
|
||||
// re-heat the nozzle, re-show the continue prompt, restart idle timers, start over
|
||||
if (nozzle_timed_out) {
|
||||
ui.pause_show_message(PAUSE_MESSAGE_HEAT);
|
||||
SERIAL_ECHO_MSG(_PMSG(STR_FILAMENT_CHANGE_HEAT));
|
||||
|
||||
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(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, hostui.prompt_do(PROMPT_INFO, GET_TEXT_F(MSG_REHEATING)));
|
||||
|
||||
LCD_MESSAGE(MSG_REHEATING);
|
||||
|
||||
// Re-enable the heaters if they timed out
|
||||
HOTEND_LOOP() thermalManager.reset_hotend_idle_timer(e);
|
||||
|
||||
// Wait for the heaters to reach the target temperatures
|
||||
ensure_safe_temperature(false);
|
||||
|
||||
// Show the prompt to continue
|
||||
show_continue_prompt(is_reload);
|
||||
|
||||
// Start the heater idle timers
|
||||
const millis_t nozzle_timeout = SEC_TO_MS(PAUSE_PARK_NOZZLE_TIMEOUT);
|
||||
|
||||
HOTEND_LOOP() thermalManager.heater_idle[e].start(nozzle_timeout);
|
||||
|
||||
TERN_(HOST_PROMPT_SUPPORT, hostui.continue_prompt(GET_TEXT_F(MSG_REHEATDONE)));
|
||||
#if ENABLED(EXTENSIBLE_UI)
|
||||
ExtUI::onUserConfirmRequired(GET_TEXT_F(MSG_REHEATDONE));
|
||||
#else
|
||||
LCD_MESSAGE(MSG_REHEATDONE);
|
||||
#endif
|
||||
|
||||
IF_DISABLED(PAUSE_REHEAT_FAST_RESUME, wait_for_user = true);
|
||||
|
||||
nozzle_timed_out = false;
|
||||
first_impatient_beep(max_beep_count);
|
||||
}
|
||||
idle_no_sleep();
|
||||
}
|
||||
TERN_(DUAL_X_CARRIAGE, set_duplication_enabled(saved_ext_dup_mode, saved_ext));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resume or Start print procedure
|
||||
*
|
||||
* - If not paused, do nothing and return
|
||||
* - Reset heater idle timers
|
||||
* - Load filament if specified, but only if:
|
||||
* - a nozzle timed out, or
|
||||
* - the nozzle is already heated.
|
||||
* - Display "wait for print to resume"
|
||||
* - Retract to prevent oozing
|
||||
* - Move the nozzle back to resume_position
|
||||
* - Unretract
|
||||
* - Re-prime the nozzle...
|
||||
* - FWRETRACT: Recover/prime from the prior G10.
|
||||
* - !FWRETRACT: Retract by resume_position.e, if negative.
|
||||
* Not sure how this logic comes into use.
|
||||
* - Sync the planner E to resume_position.e
|
||||
* - Send host action for resume, if configured
|
||||
* - Resume the current SD print job, if any
|
||||
*/
|
||||
void resume_print(const_float_t slow_load_length/*=0*/, const_float_t fast_load_length/*=0*/, const_float_t purge_length/*=ADVANCED_PAUSE_PURGE_LENGTH*/, const int8_t max_beep_count/*=0*/, const celsius_t targetTemp/*=0*/ DXC_ARGS) {
|
||||
DEBUG_SECTION(rp, "resume_print", true);
|
||||
DEBUG_ECHOLNPGM("... slowlen:", slow_load_length, " fastlen:", fast_load_length, " purgelen:", purge_length, " maxbeep:", max_beep_count, " targetTemp:", targetTemp DXC_SAY);
|
||||
|
||||
/*
|
||||
SERIAL_ECHOLNPGM(
|
||||
"start of resume_print()\ndual_x_carriage_mode:", dual_x_carriage_mode,
|
||||
"\nextruder_duplication_enabled:", extruder_duplication_enabled,
|
||||
"\nactive_extruder:", active_extruder,
|
||||
"\n"
|
||||
);
|
||||
//*/
|
||||
|
||||
if (!did_pause_print) return;
|
||||
|
||||
// Re-enable the heaters if they timed out
|
||||
bool nozzle_timed_out = false;
|
||||
HOTEND_LOOP() {
|
||||
nozzle_timed_out |= thermalManager.heater_idle[e].timed_out;
|
||||
thermalManager.reset_hotend_idle_timer(e);
|
||||
}
|
||||
|
||||
if (targetTemp > thermalManager.degTargetHotend(active_extruder))
|
||||
thermalManager.setTargetHotend(targetTemp, active_extruder);
|
||||
|
||||
// Load the new filament
|
||||
load_filament(slow_load_length, fast_load_length, purge_length, max_beep_count, true, nozzle_timed_out, PAUSE_MODE_SAME DXC_PASS);
|
||||
|
||||
if (targetTemp > 0) {
|
||||
thermalManager.setTargetHotend(targetTemp, active_extruder);
|
||||
thermalManager.wait_for_hotend(active_extruder, false);
|
||||
}
|
||||
|
||||
ui.pause_show_message(PAUSE_MESSAGE_RESUME);
|
||||
|
||||
// Check Temperature before moving hotend
|
||||
ensure_safe_temperature(DISABLED(BELTPRINTER));
|
||||
|
||||
// Retract to prevent oozing
|
||||
unscaled_e_move(-(PAUSE_PARK_RETRACT_LENGTH), feedRate_t(PAUSE_PARK_RETRACT_FEEDRATE));
|
||||
|
||||
if (!axes_should_home()) {
|
||||
// Move XY back to saved position
|
||||
destination.set(resume_position.x, resume_position.y, current_position.z, current_position.e);
|
||||
prepare_internal_move_to_destination(NOZZLE_PARK_XY_FEEDRATE);
|
||||
|
||||
// Move Z back to saved position
|
||||
destination.z = resume_position.z;
|
||||
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
|
||||
if (fwretract.retracted[active_extruder])
|
||||
unscaled_e_move(-fwretract.settings.retract_length, fwretract.settings.retract_feedrate_mm_s);
|
||||
#endif
|
||||
|
||||
// If resume_position is negative
|
||||
if (resume_position.e < 0) unscaled_e_move(resume_position.e, feedRate_t(PAUSE_PARK_RETRACT_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
|
||||
// Set extruder to saved position
|
||||
planner.set_e_position_mm((destination.e = current_position.e = resume_position.e));
|
||||
|
||||
ui.pause_show_message(PAUSE_MESSAGE_STATUS);
|
||||
|
||||
#ifdef ACTION_ON_RESUMED
|
||||
hostui.resumed();
|
||||
#elif defined(ACTION_ON_RESUME)
|
||||
hostui.resume();
|
||||
#endif
|
||||
|
||||
--did_pause_print;
|
||||
|
||||
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();
|
||||
|
||||
#if HAS_MEDIA
|
||||
if (did_pause_print) {
|
||||
--did_pause_print;
|
||||
card.startOrResumeFilePrinting();
|
||||
// Write PLR now to update the z axis value
|
||||
TERN_(POWER_LOSS_RECOVERY, if (recovery.enabled) recovery.save(true));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLED(ADVANCED_PAUSE_FANS_PAUSE) && HAS_FAN
|
||||
thermalManager.set_fans_paused(false);
|
||||
#endif
|
||||
|
||||
TERN_(HAS_FILAMENT_SENSOR, runout.reset());
|
||||
|
||||
ui.reset_status();
|
||||
ui.return_to_status();
|
||||
}
|
||||
|
||||
#endif // ADVANCED_PAUSE_FEATURE
|
||||
129
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/pause.h
Normal file
129
creality-ender3/Marlin-2.1.2.4/Marlin/src/feature/pause.h
Normal file
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
/**
|
||||
* feature/pause.h - Pause feature support functions
|
||||
* This may be combined with related G-codes if features are consolidated.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
float unload_length, load_length;
|
||||
} fil_change_settings_t;
|
||||
|
||||
#include "../inc/MarlinConfigPre.h"
|
||||
|
||||
#if ENABLED(ADVANCED_PAUSE_FEATURE)
|
||||
|
||||
#include "../libs/nozzle.h"
|
||||
|
||||
enum PauseMode : char {
|
||||
PAUSE_MODE_SAME,
|
||||
PAUSE_MODE_PAUSE_PRINT,
|
||||
PAUSE_MODE_CHANGE_FILAMENT,
|
||||
PAUSE_MODE_LOAD_FILAMENT,
|
||||
PAUSE_MODE_UNLOAD_FILAMENT
|
||||
};
|
||||
|
||||
enum PauseMessage : char {
|
||||
PAUSE_MESSAGE_PARKING,
|
||||
PAUSE_MESSAGE_CHANGING,
|
||||
PAUSE_MESSAGE_WAITING,
|
||||
PAUSE_MESSAGE_INSERT,
|
||||
PAUSE_MESSAGE_LOAD,
|
||||
PAUSE_MESSAGE_UNLOAD,
|
||||
PAUSE_MESSAGE_PURGE,
|
||||
PAUSE_MESSAGE_OPTION,
|
||||
PAUSE_MESSAGE_RESUME,
|
||||
PAUSE_MESSAGE_HEAT,
|
||||
PAUSE_MESSAGE_HEATING,
|
||||
PAUSE_MESSAGE_STATUS
|
||||
};
|
||||
|
||||
#if M600_PURGE_MORE_RESUMABLE
|
||||
enum PauseMenuResponse : char {
|
||||
PAUSE_RESPONSE_WAIT_FOR,
|
||||
PAUSE_RESPONSE_EXTRUDE_MORE,
|
||||
PAUSE_RESPONSE_RESUME_PRINT
|
||||
};
|
||||
extern PauseMenuResponse pause_menu_response;
|
||||
extern PauseMode pause_mode;
|
||||
#endif
|
||||
|
||||
extern fil_change_settings_t fc_settings[EXTRUDERS];
|
||||
|
||||
extern uint8_t did_pause_print;
|
||||
|
||||
#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(
|
||||
const_float_t retract, // (mm) Retraction length
|
||||
const xyz_pos_t &park_point, // Parking XY Position and Z Raise
|
||||
const bool show_lcd=false, // Set LCD status messages?
|
||||
const_float_t unload_length=0 // (mm) Filament Change Unload Length - 0 to skip
|
||||
DXC_PARAMS // Dual-X-Carriage extruder index
|
||||
);
|
||||
|
||||
void wait_for_confirmation(
|
||||
const bool is_reload=false, // Reload Filament? (otherwise Resume Print)
|
||||
const int8_t max_beep_count=0 // Beep alert for attention
|
||||
DXC_PARAMS // Dual-X-Carriage extruder index
|
||||
);
|
||||
|
||||
void resume_print(
|
||||
const_float_t slow_load_length=0, // (mm) Slow Load Length for finishing move
|
||||
const_float_t fast_load_length=0, // (mm) Fast Load Length for initial move
|
||||
const_float_t extrude_length=ADVANCED_PAUSE_PURGE_LENGTH, // (mm) Purge length
|
||||
const int8_t max_beep_count=0, // Beep alert for attention
|
||||
const celsius_t targetTemp=0 // (°C) A target temperature for the hotend
|
||||
DXC_PARAMS // Dual-X-Carriage extruder index
|
||||
);
|
||||
|
||||
bool load_filament(
|
||||
const_float_t slow_load_length=0, // (mm) Slow Load Length for finishing move
|
||||
const_float_t fast_load_length=0, // (mm) Fast Load Length for initial move
|
||||
const_float_t extrude_length=0, // (mm) Purge length
|
||||
const int8_t max_beep_count=0, // Beep alert for attention
|
||||
const bool show_lcd=false, // Set LCD status messages?
|
||||
const bool pause_for_user=false, // Pause for user before returning?
|
||||
const PauseMode mode=PAUSE_MODE_PAUSE_PRINT // Pause Mode to apply
|
||||
DXC_PARAMS // Dual-X-Carriage extruder index
|
||||
);
|
||||
|
||||
bool unload_filament(
|
||||
const_float_t unload_length, // (mm) Filament Unload Length - 0 to skip
|
||||
const bool show_lcd=false, // Set LCD status messages?
|
||||
const PauseMode mode=PAUSE_MODE_PAUSE_PRINT // Pause Mode to apply
|
||||
#if ALL(FILAMENT_UNLOAD_ALL_EXTRUDERS, MIXING_EXTRUDER)
|
||||
, const_float_t mix_multiplier=1.0f // Extrusion multiplier (for a Mixing Extruder)
|
||||
#endif
|
||||
);
|
||||
|
||||
#else // !ADVANCED_PAUSE_FEATURE
|
||||
|
||||
constexpr uint8_t did_pause_print = 0;
|
||||
|
||||
#endif // !ADVANCED_PAUSE_FEATURE
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user