|
@ -1318,104 +1318,101 @@ void Temperature::min_temp_error(const heater_id_t heater_id) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
#if ANY(PID_DEBUG, PID_BED_DEBUG, PID_CHAMBER_DEBUG) |
|
|
#if ANY(PID_DEBUG, PID_BED_DEBUG, PID_CHAMBER_DEBUG) |
|
|
bool Temperature::pid_debug_flag; // = 0
|
|
|
#define HAS_PID_DEBUG 1 |
|
|
|
|
|
bool Temperature::pid_debug_flag; // = false
|
|
|
#endif |
|
|
#endif |
|
|
|
|
|
|
|
|
#if HAS_HOTEND |
|
|
#if HAS_PID_HEATING |
|
|
|
|
|
|
|
|
float Temperature::get_pid_output_hotend(const uint8_t E_NAME) { |
|
|
template<typename TT, int MIN_POW, int MAX_POW> |
|
|
const uint8_t ee = HOTEND_INDEX; |
|
|
class PIDRunner { |
|
|
#if ENABLED(PIDTEMP) |
|
|
public: |
|
|
#if DISABLED(PID_OPENLOOP) |
|
|
TT &tempinfo; |
|
|
static hotend_pid_t work_pid[HOTENDS]; |
|
|
__typeof__(TT::pid) work_pid{0}; |
|
|
static float temp_iState[HOTENDS] = { 0 }, |
|
|
float temp_iState = 0, temp_dState = 0; |
|
|
temp_dState[HOTENDS] = { 0 }; |
|
|
bool pid_reset = true; |
|
|
static Flags<HOTENDS> pid_reset; |
|
|
|
|
|
const float pid_error = temp_hotend[ee].target - temp_hotend[ee].celsius; |
|
|
PIDRunner(TT &t) : tempinfo(t) { } |
|
|
|
|
|
|
|
|
float pid_output; |
|
|
float get_pid_output() { |
|
|
|
|
|
|
|
|
if (temp_hotend[ee].target == 0 |
|
|
#if ENABLED(PID_OPENLOOP) |
|
|
|| pid_error < -(PID_FUNCTIONAL_RANGE) |
|
|
|
|
|
|| TERN0(HEATER_IDLE_HANDLER, heater_idle[ee].timed_out) |
|
|
return constrain(tempinfo.target, 0, MAX_POW); |
|
|
) { |
|
|
|
|
|
pid_output = 0; |
|
|
#else // !PID_OPENLOOP
|
|
|
pid_reset.set(ee); |
|
|
|
|
|
|
|
|
const float pid_error = tempinfo.target - tempinfo.celsius; |
|
|
|
|
|
if (!tempinfo.target || pid_error < -(PID_FUNCTIONAL_RANGE)) { |
|
|
|
|
|
pid_reset = true; |
|
|
|
|
|
return 0; |
|
|
} |
|
|
} |
|
|
else if (pid_error > PID_FUNCTIONAL_RANGE) { |
|
|
else if (pid_error > PID_FUNCTIONAL_RANGE) { |
|
|
pid_output = PID_MAX; |
|
|
pid_reset = true; |
|
|
pid_reset.set(ee); |
|
|
return MAX_POW; |
|
|
} |
|
|
} |
|
|
else { |
|
|
|
|
|
if (pid_reset[ee]) { |
|
|
|
|
|
temp_iState[ee] = 0.0; |
|
|
|
|
|
work_pid[ee].Kd = 0.0; |
|
|
|
|
|
pid_reset.clear(ee); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
work_pid[ee].Kd = work_pid[ee].Kd + PID_K2 * (PID_PARAM(Kd, ee) * (temp_dState[ee] - temp_hotend[ee].celsius) - work_pid[ee].Kd); |
|
|
if (pid_reset) { |
|
|
const float max_power_over_i_gain = float(PID_MAX) / PID_PARAM(Ki, ee) - float(MIN_POWER); |
|
|
pid_reset = false; |
|
|
temp_iState[ee] = constrain(temp_iState[ee] + pid_error, 0, max_power_over_i_gain); |
|
|
temp_iState = 0.0; |
|
|
work_pid[ee].Kp = PID_PARAM(Kp, ee) * pid_error; |
|
|
work_pid.Kd = 0.0; |
|
|
work_pid[ee].Ki = PID_PARAM(Ki, ee) * temp_iState[ee]; |
|
|
} |
|
|
|
|
|
|
|
|
pid_output = work_pid[ee].Kp + work_pid[ee].Ki + work_pid[ee].Kd + float(MIN_POWER); |
|
|
const float max_power_over_i_gain = float(MAX_POW) / tempinfo.pid.Ki - float(MIN_POW); |
|
|
|
|
|
temp_iState = constrain(temp_iState + pid_error, 0, max_power_over_i_gain); |
|
|
|
|
|
|
|
|
#if ENABLED(PID_EXTRUSION_SCALING) |
|
|
work_pid.Kp = tempinfo.pid.Kp * pid_error; |
|
|
#if HOTENDS == 1 |
|
|
work_pid.Ki = tempinfo.pid.Ki * temp_iState; |
|
|
constexpr bool this_hotend = true; |
|
|
work_pid.Kd = work_pid.Kd + PID_K2 * (tempinfo.pid.Kd * (temp_dState - tempinfo.celsius) - work_pid.Kd); |
|
|
#else |
|
|
|
|
|
const bool this_hotend = (ee == active_extruder); |
|
|
|
|
|
#endif |
|
|
|
|
|
work_pid[ee].Kc = 0; |
|
|
|
|
|
if (this_hotend) { |
|
|
|
|
|
const long e_position = stepper.position(E_AXIS); |
|
|
|
|
|
if (e_position > pes_e_position) { |
|
|
|
|
|
lpq[lpq_ptr] = e_position - pes_e_position; |
|
|
|
|
|
pes_e_position = e_position; |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
lpq[lpq_ptr] = 0; |
|
|
|
|
|
|
|
|
|
|
|
if (++lpq_ptr >= lpq_len) lpq_ptr = 0; |
|
|
temp_dState = tempinfo.celsius; |
|
|
work_pid[ee].Kc = (lpq[lpq_ptr] * planner.mm_per_step[E_AXIS]) * PID_PARAM(Kc, ee); |
|
|
|
|
|
pid_output += work_pid[ee].Kc; |
|
|
|
|
|
} |
|
|
|
|
|
#endif // PID_EXTRUSION_SCALING
|
|
|
|
|
|
#if ENABLED(PID_FAN_SCALING) |
|
|
|
|
|
if (fan_speed[active_extruder] > PID_FAN_SCALING_MIN_SPEED) { |
|
|
|
|
|
work_pid[ee].Kf = PID_PARAM(Kf, ee) + (PID_FAN_SCALING_LIN_FACTOR) * fan_speed[active_extruder]; |
|
|
|
|
|
pid_output += work_pid[ee].Kf; |
|
|
|
|
|
} |
|
|
|
|
|
//pid_output -= work_pid[ee].Ki;
|
|
|
|
|
|
//pid_output += work_pid[ee].Ki * work_pid[ee].Kf
|
|
|
|
|
|
#endif // PID_FAN_SCALING
|
|
|
|
|
|
LIMIT(pid_output, 0, PID_MAX); |
|
|
|
|
|
} |
|
|
|
|
|
temp_dState[ee] = temp_hotend[ee].celsius; |
|
|
|
|
|
|
|
|
|
|
|
#else // PID_OPENLOOP
|
|
|
return constrain(work_pid.Kp + work_pid.Ki + work_pid.Kd + float(MIN_POW), 0, MAX_POW); |
|
|
|
|
|
|
|
|
const float pid_output = constrain(temp_hotend[ee].target, 0, PID_MAX); |
|
|
#endif // !PID_OPENLOOP
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
#endif // PID_OPENLOOP
|
|
|
FORCE_INLINE void debug(const_celsius_float_t c, const_float_t pid_out, FSTR_P const name=nullptr, const int8_t index=-1) { |
|
|
|
|
|
if (TERN0(HAS_PID_DEBUG, thermalManager.pid_debug_flag)) { |
|
|
|
|
|
SERIAL_ECHO_START(); |
|
|
|
|
|
if (name) SERIAL_ECHOLNF(name); |
|
|
|
|
|
if (index >= 0) SERIAL_ECHO(index); |
|
|
|
|
|
SERIAL_ECHOLNPGM( |
|
|
|
|
|
STR_PID_DEBUG_INPUT, c, |
|
|
|
|
|
STR_PID_DEBUG_OUTPUT, pid_out |
|
|
|
|
|
#if DISABLED(PID_OPENLOOP) |
|
|
|
|
|
, "pTerm", work_pid.Kp, "iTerm", work_pid.Ki, "dTerm", work_pid.Kd |
|
|
|
|
|
#endif |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
#endif // HAS_PID_HEATING
|
|
|
|
|
|
|
|
|
|
|
|
#if HAS_HOTEND |
|
|
|
|
|
|
|
|
|
|
|
float Temperature::get_pid_output_hotend(const uint8_t E_NAME) { |
|
|
|
|
|
const uint8_t ee = HOTEND_INDEX; |
|
|
|
|
|
|
|
|
|
|
|
#if ENABLED(PIDTEMP) |
|
|
|
|
|
|
|
|
|
|
|
typedef PIDRunner<hotend_info_t, 0, PID_MAX> PIDRunnerHotend; |
|
|
|
|
|
|
|
|
|
|
|
static PIDRunnerHotend hotend_pid[HOTENDS] = { |
|
|
|
|
|
#define _HOTENDPID(E) temp_hotend[E], |
|
|
|
|
|
REPEAT(HOTENDS, _HOTENDPID) |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const float pid_output = hotend_pid[ee].get_pid_output(); |
|
|
|
|
|
|
|
|
#if ENABLED(PID_DEBUG) |
|
|
#if ENABLED(PID_DEBUG) |
|
|
if (ee == active_extruder && pid_debug_flag) { |
|
|
if (ee == active_extruder) |
|
|
SERIAL_ECHO_MSG(STR_PID_DEBUG, ee, STR_PID_DEBUG_INPUT, temp_hotend[ee].celsius, STR_PID_DEBUG_OUTPUT, pid_output |
|
|
hotend_pid[ee].debug(temp_hotend[ee].celsius, pid_output, F("E"), ee); |
|
|
#if DISABLED(PID_OPENLOOP) |
|
|
|
|
|
, STR_PID_DEBUG_PTERM, work_pid[ee].Kp |
|
|
|
|
|
, STR_PID_DEBUG_ITERM, work_pid[ee].Ki |
|
|
|
|
|
, STR_PID_DEBUG_DTERM, work_pid[ee].Kd |
|
|
|
|
|
#if ENABLED(PID_EXTRUSION_SCALING) |
|
|
|
|
|
, STR_PID_DEBUG_CTERM, work_pid[ee].Kc |
|
|
|
|
|
#endif |
|
|
|
|
|
#endif |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
#endif |
|
|
#endif |
|
|
|
|
|
|
|
|
#elif ENABLED(MPCTEMP) |
|
|
#elif ENABLED(MPCTEMP) |
|
|
|
|
|
|
|
|
MPCHeaterInfo &hotend = temp_hotend[ee]; |
|
|
MPCHeaterInfo &hotend = temp_hotend[ee]; |
|
|
MPC_t &constants = hotend.constants; |
|
|
MPC_t &constants = hotend.constants; |
|
|
|
|
|
|
|
@ -1497,7 +1494,7 @@ void Temperature::min_temp_error(const heater_id_t heater_id) { |
|
|
#else // No PID or MPC enabled
|
|
|
#else // No PID or MPC enabled
|
|
|
|
|
|
|
|
|
const bool is_idling = TERN0(HEATER_IDLE_HANDLER, heater_idle[ee].timed_out); |
|
|
const bool is_idling = TERN0(HEATER_IDLE_HANDLER, heater_idle[ee].timed_out); |
|
|
const float pid_output = (!is_idling && temp_hotend[ee].celsius < temp_hotend[ee].target) ? BANG_MAX : 0; |
|
|
const float pid_output = (!is_idling && temp_hotend[ee].is_below_target()) ? BANG_MAX : 0; |
|
|
|
|
|
|
|
|
#endif |
|
|
#endif |
|
|
|
|
|
|
|
@ -1509,61 +1506,9 @@ void Temperature::min_temp_error(const heater_id_t heater_id) { |
|
|
#if ENABLED(PIDTEMPBED) |
|
|
#if ENABLED(PIDTEMPBED) |
|
|
|
|
|
|
|
|
float Temperature::get_pid_output_bed() { |
|
|
float Temperature::get_pid_output_bed() { |
|
|
|
|
|
static PIDRunner<bed_info_t, MIN_BED_POWER, MAX_BED_POWER> bed_pid(temp_bed); |
|
|
#if DISABLED(PID_OPENLOOP) |
|
|
const float pid_output = bed_pid.get_pid_output(); |
|
|
|
|
|
TERN_(PID_BED_DEBUG, bed_pid.debug(temp_bed.celsius, pid_output, F("(Bed)"))); |
|
|
static PID_t work_pid{0}; |
|
|
|
|
|
static float temp_iState = 0, temp_dState = 0; |
|
|
|
|
|
static bool pid_reset = true; |
|
|
|
|
|
float pid_output = 0; |
|
|
|
|
|
const float max_power_over_i_gain = float(MAX_BED_POWER) / temp_bed.pid.Ki - float(MIN_BED_POWER), |
|
|
|
|
|
pid_error = temp_bed.target - temp_bed.celsius; |
|
|
|
|
|
|
|
|
|
|
|
if (!temp_bed.target || pid_error < -(PID_FUNCTIONAL_RANGE)) { |
|
|
|
|
|
pid_output = 0; |
|
|
|
|
|
pid_reset = true; |
|
|
|
|
|
} |
|
|
|
|
|
else if (pid_error > PID_FUNCTIONAL_RANGE) { |
|
|
|
|
|
pid_output = MAX_BED_POWER; |
|
|
|
|
|
pid_reset = true; |
|
|
|
|
|
} |
|
|
|
|
|
else { |
|
|
|
|
|
if (pid_reset) { |
|
|
|
|
|
temp_iState = 0.0; |
|
|
|
|
|
work_pid.Kd = 0.0; |
|
|
|
|
|
pid_reset = false; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
temp_iState = constrain(temp_iState + pid_error, 0, max_power_over_i_gain); |
|
|
|
|
|
|
|
|
|
|
|
work_pid.Kp = temp_bed.pid.Kp * pid_error; |
|
|
|
|
|
work_pid.Ki = temp_bed.pid.Ki * temp_iState; |
|
|
|
|
|
work_pid.Kd = work_pid.Kd + PID_K2 * (temp_bed.pid.Kd * (temp_dState - temp_bed.celsius) - work_pid.Kd); |
|
|
|
|
|
|
|
|
|
|
|
temp_dState = temp_bed.celsius; |
|
|
|
|
|
|
|
|
|
|
|
pid_output = constrain(work_pid.Kp + work_pid.Ki + work_pid.Kd + float(MIN_BED_POWER), 0, MAX_BED_POWER); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#else // PID_OPENLOOP
|
|
|
|
|
|
|
|
|
|
|
|
const float pid_output = constrain(temp_bed.target, 0, MAX_BED_POWER); |
|
|
|
|
|
|
|
|
|
|
|
#endif // PID_OPENLOOP
|
|
|
|
|
|
|
|
|
|
|
|
#if ENABLED(PID_BED_DEBUG) |
|
|
|
|
|
if (pid_debug_flag) { |
|
|
|
|
|
SERIAL_ECHO_MSG( |
|
|
|
|
|
" PID_BED_DEBUG : Input ", temp_bed.celsius, " Output ", pid_output |
|
|
|
|
|
#if DISABLED(PID_OPENLOOP) |
|
|
|
|
|
, STR_PID_DEBUG_PTERM, work_pid.Kp |
|
|
|
|
|
, STR_PID_DEBUG_ITERM, work_pid.Ki |
|
|
|
|
|
, STR_PID_DEBUG_DTERM, work_pid.Kd |
|
|
|
|
|
#endif |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
return pid_output; |
|
|
return pid_output; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -1572,114 +1517,17 @@ void Temperature::min_temp_error(const heater_id_t heater_id) { |
|
|
#if ENABLED(PIDTEMPCHAMBER) |
|
|
#if ENABLED(PIDTEMPCHAMBER) |
|
|
|
|
|
|
|
|
float Temperature::get_pid_output_chamber() { |
|
|
float Temperature::get_pid_output_chamber() { |
|
|
|
|
|
static PIDRunner<chamber_info_t, MIN_CHAMBER_POWER, MAX_CHAMBER_POWER> chamber_pid(temp_chamber); |
|
|
#if DISABLED(PID_OPENLOOP) |
|
|
const float pid_output = chamber_pid.get_pid_output(); |
|
|
|
|
|
TERN_(PID_CHAMBER_DEBUG, chamber_pid.debug(temp_chamber.celsius, pid_output, F("(Chamber)"))); |
|
|
static PID_t work_pid{0}; |
|
|
|
|
|
static float temp_iState = 0, temp_dState = 0; |
|
|
|
|
|
static bool pid_reset = true; |
|
|
|
|
|
float pid_output = 0; |
|
|
|
|
|
const float max_power_over_i_gain = float(MAX_CHAMBER_POWER) / temp_chamber.pid.Ki - float(MIN_CHAMBER_POWER), |
|
|
|
|
|
pid_error = temp_chamber.target - temp_chamber.celsius; |
|
|
|
|
|
|
|
|
|
|
|
if (!temp_chamber.target || pid_error < -(PID_FUNCTIONAL_RANGE)) { |
|
|
|
|
|
pid_output = 0; |
|
|
|
|
|
pid_reset = true; |
|
|
|
|
|
} |
|
|
|
|
|
else if (pid_error > PID_FUNCTIONAL_RANGE) { |
|
|
|
|
|
pid_output = MAX_CHAMBER_POWER; |
|
|
|
|
|
pid_reset = true; |
|
|
|
|
|
} |
|
|
|
|
|
else { |
|
|
|
|
|
if (pid_reset) { |
|
|
|
|
|
temp_iState = 0.0; |
|
|
|
|
|
work_pid.Kd = 0.0; |
|
|
|
|
|
pid_reset = false; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
temp_iState = constrain(temp_iState + pid_error, 0, max_power_over_i_gain); |
|
|
|
|
|
|
|
|
|
|
|
work_pid.Kp = temp_chamber.pid.Kp * pid_error; |
|
|
|
|
|
work_pid.Ki = temp_chamber.pid.Ki * temp_iState; |
|
|
|
|
|
work_pid.Kd = work_pid.Kd + PID_K2 * (temp_chamber.pid.Kd * (temp_dState - temp_chamber.celsius) - work_pid.Kd); |
|
|
|
|
|
|
|
|
|
|
|
temp_dState = temp_chamber.celsius; |
|
|
|
|
|
|
|
|
|
|
|
pid_output = constrain(work_pid.Kp + work_pid.Ki + work_pid.Kd + float(MIN_CHAMBER_POWER), 0, MAX_CHAMBER_POWER); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#else // PID_OPENLOOP
|
|
|
|
|
|
|
|
|
|
|
|
const float pid_output = constrain(temp_chamber.target, 0, MAX_CHAMBER_POWER); |
|
|
|
|
|
|
|
|
|
|
|
#endif // PID_OPENLOOP
|
|
|
|
|
|
|
|
|
|
|
|
#if ENABLED(PID_CHAMBER_DEBUG) |
|
|
|
|
|
{ |
|
|
|
|
|
SERIAL_ECHO_MSG( |
|
|
|
|
|
" PID_CHAMBER_DEBUG : Input ", temp_chamber.celsius, " Output ", pid_output |
|
|
|
|
|
#if DISABLED(PID_OPENLOOP) |
|
|
|
|
|
, STR_PID_DEBUG_PTERM, work_pid.Kp |
|
|
|
|
|
, STR_PID_DEBUG_ITERM, work_pid.Ki |
|
|
|
|
|
, STR_PID_DEBUG_DTERM, work_pid.Kd |
|
|
|
|
|
#endif |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
return pid_output; |
|
|
return pid_output; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
#endif // PIDTEMPCHAMBER
|
|
|
#endif // PIDTEMPCHAMBER
|
|
|
|
|
|
|
|
|
/**
|
|
|
#if HAS_HOTEND |
|
|
* Manage heating activities for extruder hot-ends and a heated bed |
|
|
|
|
|
* - Acquire updated temperature readings |
|
|
|
|
|
* - Also resets the watchdog timer |
|
|
|
|
|
* - Invoke thermal runaway protection |
|
|
|
|
|
* - Manage extruder auto-fan |
|
|
|
|
|
* - Apply filament width to the extrusion rate (may move) |
|
|
|
|
|
* - Update the heated bed PID output value |
|
|
|
|
|
*/ |
|
|
|
|
|
void Temperature::manage_heater() { |
|
|
|
|
|
if (marlin_state == MF_INITIALIZING) return hal.watchdog_refresh(); // If Marlin isn't started, at least reset the watchdog!
|
|
|
|
|
|
|
|
|
|
|
|
static bool no_reentry = false; // Prevent recursion
|
|
|
|
|
|
if (no_reentry) return; |
|
|
|
|
|
REMEMBER(mh, no_reentry, true); |
|
|
|
|
|
|
|
|
|
|
|
#if ENABLED(EMERGENCY_PARSER) |
|
|
|
|
|
if (emergency_parser.killed_by_M112) kill(FPSTR(M112_KILL_STR), nullptr, true); |
|
|
|
|
|
|
|
|
|
|
|
if (emergency_parser.quickstop_by_M410) { |
|
|
|
|
|
emergency_parser.quickstop_by_M410 = false; // quickstop_stepper may call idle so clear this now!
|
|
|
|
|
|
quickstop_stepper(); |
|
|
|
|
|
} |
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
if (!updateTemperaturesIfReady()) return; // Will also reset the watchdog if temperatures are ready
|
|
|
|
|
|
|
|
|
|
|
|
#if DISABLED(IGNORE_THERMOCOUPLE_ERRORS) |
|
|
|
|
|
#if TEMP_SENSOR_0_IS_MAX_TC |
|
|
|
|
|
if (degHotend(0) > _MIN(HEATER_0_MAXTEMP, TEMP_SENSOR_0_MAX_TC_TMAX - 1.0)) max_temp_error(H_E0); |
|
|
|
|
|
if (degHotend(0) < _MAX(HEATER_0_MINTEMP, TEMP_SENSOR_0_MAX_TC_TMIN + .01)) min_temp_error(H_E0); |
|
|
|
|
|
#endif |
|
|
|
|
|
#if TEMP_SENSOR_1_IS_MAX_TC |
|
|
|
|
|
if (degHotend(1) > _MIN(HEATER_1_MAXTEMP, TEMP_SENSOR_1_MAX_TC_TMAX - 1.0)) max_temp_error(H_E1); |
|
|
|
|
|
if (degHotend(1) < _MAX(HEATER_1_MINTEMP, TEMP_SENSOR_1_MAX_TC_TMIN + .01)) min_temp_error(H_E1); |
|
|
|
|
|
#endif |
|
|
|
|
|
#if TEMP_SENSOR_REDUNDANT_IS_MAX_TC |
|
|
|
|
|
if (degRedundant() > TEMP_SENSOR_REDUNDANT_MAX_TC_TMAX - 1.0) max_temp_error(H_REDUNDANT); |
|
|
|
|
|
if (degRedundant() < TEMP_SENSOR_REDUNDANT_MAX_TC_TMIN + .01) min_temp_error(H_REDUNDANT); |
|
|
|
|
|
#endif |
|
|
|
|
|
#else |
|
|
|
|
|
#warning "Safety Alert! Disable IGNORE_THERMOCOUPLE_ERRORS for the final build!" |
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
millis_t ms = millis(); |
|
|
|
|
|
|
|
|
|
|
|
#if HAS_HOTEND |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Temperature::manage_hotends(const millis_t &ms) { |
|
|
HOTEND_LOOP() { |
|
|
HOTEND_LOOP() { |
|
|
#if ENABLED(THERMAL_PROTECTION_HOTENDS) |
|
|
#if ENABLED(THERMAL_PROTECTION_HOTENDS) |
|
|
if (degHotend(e) > temp_range[e].maxtemp) max_temp_error((heater_id_t)e); |
|
|
if (degHotend(e) > temp_range[e].maxtemp) max_temp_error((heater_id_t)e); |
|
@ -1707,25 +1555,13 @@ void Temperature::manage_heater() { |
|
|
#endif |
|
|
#endif |
|
|
|
|
|
|
|
|
} // HOTEND_LOOP
|
|
|
} // HOTEND_LOOP
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
#endif // HAS_HOTEND
|
|
|
#endif // HAS_HOTEND
|
|
|
|
|
|
|
|
|
#if HAS_TEMP_REDUNDANT |
|
|
|
|
|
// Make sure measured temperatures are close together
|
|
|
|
|
|
if (ABS(degRedundantTarget() - degRedundant()) > TEMP_SENSOR_REDUNDANT_MAX_DIFF) |
|
|
|
|
|
_temp_error((heater_id_t)HEATER_ID(TEMP_SENSOR_REDUNDANT_TARGET), F(STR_REDUNDANCY), GET_TEXT_F(MSG_ERR_REDUNDANT_TEMP)); |
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
// Manage extruder auto fans and/or read fan tachometers
|
|
|
|
|
|
TERN_(HAS_FAN_LOGIC, manage_extruder_fans(ms)); |
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
#if HAS_HEATED_BED |
|
|
* Dynamically set the volumetric multiplier based |
|
|
|
|
|
* on the delayed Filament Width measurement. |
|
|
|
|
|
*/ |
|
|
|
|
|
TERN_(FILAMENT_WIDTH_SENSOR, filwidth.update_volumetric()); |
|
|
|
|
|
|
|
|
|
|
|
#if HAS_HEATED_BED |
|
|
void Temperature::manage_heated_bed(const millis_t &ms) { |
|
|
|
|
|
|
|
|
#if ENABLED(THERMAL_PROTECTION_BED) |
|
|
#if ENABLED(THERMAL_PROTECTION_BED) |
|
|
if (degBed() > BED_MAXTEMP) max_temp_error(H_BED); |
|
|
if (degBed() > BED_MAXTEMP) max_temp_error(H_BED); |
|
@ -1770,9 +1606,7 @@ void Temperature::manage_heater() { |
|
|
#if HEATER_IDLE_HANDLER |
|
|
#if HEATER_IDLE_HANDLER |
|
|
if (heater_idle[IDLE_INDEX_BED].timed_out) { |
|
|
if (heater_idle[IDLE_INDEX_BED].timed_out) { |
|
|
temp_bed.soft_pwm_amount = 0; |
|
|
temp_bed.soft_pwm_amount = 0; |
|
|
#if DISABLED(PIDTEMPBED) |
|
|
if (DISABLED(PIDTEMPBED)) WRITE_HEATER_BED(LOW); |
|
|
WRITE_HEATER_BED(LOW); |
|
|
|
|
|
#endif |
|
|
|
|
|
} |
|
|
} |
|
|
else |
|
|
else |
|
|
#endif |
|
|
#endif |
|
@ -1785,10 +1619,10 @@ void Temperature::manage_heater() { |
|
|
#if ENABLED(BED_LIMIT_SWITCHING) |
|
|
#if ENABLED(BED_LIMIT_SWITCHING) |
|
|
if (temp_bed.celsius >= temp_bed.target + BED_HYSTERESIS) |
|
|
if (temp_bed.celsius >= temp_bed.target + BED_HYSTERESIS) |
|
|
temp_bed.soft_pwm_amount = 0; |
|
|
temp_bed.soft_pwm_amount = 0; |
|
|
else if (temp_bed.celsius <= temp_bed.target - (BED_HYSTERESIS)) |
|
|
else if (temp_bed.is_below_target(-(BED_HYSTERESIS) + 1)) |
|
|
temp_bed.soft_pwm_amount = MAX_BED_POWER >> 1; |
|
|
temp_bed.soft_pwm_amount = MAX_BED_POWER >> 1; |
|
|
#else // !PIDTEMPBED && !BED_LIMIT_SWITCHING
|
|
|
#else // !PIDTEMPBED && !BED_LIMIT_SWITCHING
|
|
|
temp_bed.soft_pwm_amount = temp_bed.celsius < temp_bed.target ? MAX_BED_POWER >> 1 : 0; |
|
|
temp_bed.soft_pwm_amount = temp_bed.is_below_target() ? MAX_BED_POWER >> 1 : 0; |
|
|
#endif |
|
|
#endif |
|
|
} |
|
|
} |
|
|
else { |
|
|
else { |
|
@ -1799,10 +1633,13 @@ void Temperature::manage_heater() { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
} while (false); |
|
|
} while (false); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
#endif // HAS_HEATED_BED
|
|
|
#endif // HAS_HEATED_BED
|
|
|
|
|
|
|
|
|
#if HAS_HEATED_CHAMBER |
|
|
#if HAS_HEATED_CHAMBER |
|
|
|
|
|
|
|
|
|
|
|
void Temperature::manage_heated_chamber(const millis_t &ms) { |
|
|
|
|
|
|
|
|
#ifndef CHAMBER_CHECK_INTERVAL |
|
|
#ifndef CHAMBER_CHECK_INTERVAL |
|
|
#define CHAMBER_CHECK_INTERVAL 1000UL |
|
|
#define CHAMBER_CHECK_INTERVAL 1000UL |
|
@ -1897,17 +1734,17 @@ void Temperature::manage_heater() { |
|
|
if (flag_chamber_excess_heat) { |
|
|
if (flag_chamber_excess_heat) { |
|
|
temp_chamber.soft_pwm_amount = 0; |
|
|
temp_chamber.soft_pwm_amount = 0; |
|
|
#if ENABLED(CHAMBER_VENT) |
|
|
#if ENABLED(CHAMBER_VENT) |
|
|
if (!flag_chamber_off) servo[CHAMBER_VENT_SERVO_NR].move(temp_chamber.celsius <= temp_chamber.target ? 0 : 90); |
|
|
if (!flag_chamber_off) servo[CHAMBER_VENT_SERVO_NR].move(temp_chamber.is_below_target() ? 0 : 90); |
|
|
#endif |
|
|
#endif |
|
|
} |
|
|
} |
|
|
else { |
|
|
else { |
|
|
#if ENABLED(CHAMBER_LIMIT_SWITCHING) |
|
|
#if ENABLED(CHAMBER_LIMIT_SWITCHING) |
|
|
if (temp_chamber.celsius >= temp_chamber.target + TEMP_CHAMBER_HYSTERESIS) |
|
|
if (temp_chamber.celsius >= temp_chamber.target + TEMP_CHAMBER_HYSTERESIS) |
|
|
temp_chamber.soft_pwm_amount = 0; |
|
|
temp_chamber.soft_pwm_amount = 0; |
|
|
else if (temp_chamber.celsius <= temp_chamber.target - (TEMP_CHAMBER_HYSTERESIS)) |
|
|
else if (temp_chamber.is_below_target(-(TEMP_CHAMBER_HYSTERESIS) + 1)) |
|
|
temp_chamber.soft_pwm_amount = (MAX_CHAMBER_POWER) >> 1; |
|
|
temp_chamber.soft_pwm_amount = (MAX_CHAMBER_POWER) >> 1; |
|
|
#else |
|
|
#else |
|
|
temp_chamber.soft_pwm_amount = temp_chamber.celsius < temp_chamber.target ? (MAX_CHAMBER_POWER) >> 1 : 0; |
|
|
temp_chamber.soft_pwm_amount = temp_chamber.is_below_target() ? (MAX_CHAMBER_POWER) >> 1 : 0; |
|
|
#endif |
|
|
#endif |
|
|
#if ENABLED(CHAMBER_VENT) |
|
|
#if ENABLED(CHAMBER_VENT) |
|
|
if (!flag_chamber_off) servo[CHAMBER_VENT_SERVO_NR].move(0); |
|
|
if (!flag_chamber_off) servo[CHAMBER_VENT_SERVO_NR].move(0); |
|
@ -1923,10 +1760,13 @@ void Temperature::manage_heater() { |
|
|
tr_state_machine[RUNAWAY_IND_CHAMBER].run(temp_chamber.celsius, temp_chamber.target, H_CHAMBER, THERMAL_PROTECTION_CHAMBER_PERIOD, THERMAL_PROTECTION_CHAMBER_HYSTERESIS); |
|
|
tr_state_machine[RUNAWAY_IND_CHAMBER].run(temp_chamber.celsius, temp_chamber.target, H_CHAMBER, THERMAL_PROTECTION_CHAMBER_PERIOD, THERMAL_PROTECTION_CHAMBER_HYSTERESIS); |
|
|
#endif |
|
|
#endif |
|
|
#endif |
|
|
#endif |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
#endif // HAS_HEATED_CHAMBER
|
|
|
#endif // HAS_HEATED_CHAMBER
|
|
|
|
|
|
|
|
|
#if HAS_COOLER |
|
|
#if HAS_COOLER |
|
|
|
|
|
|
|
|
|
|
|
void Temperature::manage_cooler(const millis_t &ms) { |
|
|
|
|
|
|
|
|
#ifndef COOLER_CHECK_INTERVAL |
|
|
#ifndef COOLER_CHECK_INTERVAL |
|
|
#define COOLER_CHECK_INTERVAL 2000UL |
|
|
#define COOLER_CHECK_INTERVAL 2000UL |
|
@ -1984,8 +1824,82 @@ void Temperature::manage_heater() { |
|
|
#if ENABLED(THERMAL_PROTECTION_COOLER) |
|
|
#if ENABLED(THERMAL_PROTECTION_COOLER) |
|
|
tr_state_machine[RUNAWAY_IND_COOLER].run(temp_cooler.celsius, temp_cooler.target, H_COOLER, THERMAL_PROTECTION_COOLER_PERIOD, THERMAL_PROTECTION_COOLER_HYSTERESIS); |
|
|
tr_state_machine[RUNAWAY_IND_COOLER].run(temp_cooler.celsius, temp_cooler.target, H_COOLER, THERMAL_PROTECTION_COOLER_PERIOD, THERMAL_PROTECTION_COOLER_HYSTERESIS); |
|
|
#endif |
|
|
#endif |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
#endif // HAS_COOLER
|
|
|
#endif // HAS_COOLER
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Manage heating activities for extruder hot-ends and a heated bed |
|
|
|
|
|
* - Acquire updated temperature readings |
|
|
|
|
|
* - Also resets the watchdog timer |
|
|
|
|
|
* - Invoke thermal runaway protection |
|
|
|
|
|
* - Manage extruder auto-fan |
|
|
|
|
|
* - Apply filament width to the extrusion rate (may move) |
|
|
|
|
|
* - Update the heated bed PID output value |
|
|
|
|
|
*/ |
|
|
|
|
|
void Temperature::task() { |
|
|
|
|
|
if (marlin_state == MF_INITIALIZING) return hal.watchdog_refresh(); // If Marlin isn't started, at least reset the watchdog!
|
|
|
|
|
|
|
|
|
|
|
|
static bool no_reentry = false; // Prevent recursion
|
|
|
|
|
|
if (no_reentry) return; |
|
|
|
|
|
REMEMBER(mh, no_reentry, true); |
|
|
|
|
|
|
|
|
|
|
|
#if ENABLED(EMERGENCY_PARSER) |
|
|
|
|
|
if (emergency_parser.killed_by_M112) kill(FPSTR(M112_KILL_STR), nullptr, true); |
|
|
|
|
|
|
|
|
|
|
|
if (emergency_parser.quickstop_by_M410) { |
|
|
|
|
|
emergency_parser.quickstop_by_M410 = false; // quickstop_stepper may call idle so clear this now!
|
|
|
|
|
|
quickstop_stepper(); |
|
|
|
|
|
} |
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
if (!updateTemperaturesIfReady()) return; // Will also reset the watchdog if temperatures are ready
|
|
|
|
|
|
|
|
|
|
|
|
#if DISABLED(IGNORE_THERMOCOUPLE_ERRORS) |
|
|
|
|
|
#if TEMP_SENSOR_0_IS_MAX_TC |
|
|
|
|
|
if (degHotend(0) > _MIN(HEATER_0_MAXTEMP, TEMP_SENSOR_0_MAX_TC_TMAX - 1.0)) max_temp_error(H_E0); |
|
|
|
|
|
if (degHotend(0) < _MAX(HEATER_0_MINTEMP, TEMP_SENSOR_0_MAX_TC_TMIN + .01)) min_temp_error(H_E0); |
|
|
|
|
|
#endif |
|
|
|
|
|
#if TEMP_SENSOR_1_IS_MAX_TC |
|
|
|
|
|
if (degHotend(1) > _MIN(HEATER_1_MAXTEMP, TEMP_SENSOR_1_MAX_TC_TMAX - 1.0)) max_temp_error(H_E1); |
|
|
|
|
|
if (degHotend(1) < _MAX(HEATER_1_MINTEMP, TEMP_SENSOR_1_MAX_TC_TMIN + .01)) min_temp_error(H_E1); |
|
|
|
|
|
#endif |
|
|
|
|
|
#if TEMP_SENSOR_REDUNDANT_IS_MAX_TC |
|
|
|
|
|
if (degRedundant() > TEMP_SENSOR_REDUNDANT_MAX_TC_TMAX - 1.0) max_temp_error(H_REDUNDANT); |
|
|
|
|
|
if (degRedundant() < TEMP_SENSOR_REDUNDANT_MAX_TC_TMIN + .01) min_temp_error(H_REDUNDANT); |
|
|
|
|
|
#endif |
|
|
|
|
|
#else |
|
|
|
|
|
#warning "Safety Alert! Disable IGNORE_THERMOCOUPLE_ERRORS for the final build!" |
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
const millis_t ms = millis(); |
|
|
|
|
|
|
|
|
|
|
|
// Handle Hotend Temp Errors, Heating Watch, etc.
|
|
|
|
|
|
TERN_(HAS_HOTEND, manage_hotends(ms)); |
|
|
|
|
|
|
|
|
|
|
|
#if HAS_TEMP_REDUNDANT |
|
|
|
|
|
// Make sure measured temperatures are close together
|
|
|
|
|
|
if (ABS(degRedundantTarget() - degRedundant()) > TEMP_SENSOR_REDUNDANT_MAX_DIFF) |
|
|
|
|
|
_temp_error((heater_id_t)HEATER_ID(TEMP_SENSOR_REDUNDANT_TARGET), F(STR_REDUNDANCY), GET_TEXT_F(MSG_ERR_REDUNDANT_TEMP)); |
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
// Manage extruder auto fans and/or read fan tachometers
|
|
|
|
|
|
TERN_(HAS_FAN_LOGIC, manage_extruder_fans(ms)); |
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Dynamically set the volumetric multiplier based |
|
|
|
|
|
* on the delayed Filament Width measurement. |
|
|
|
|
|
*/ |
|
|
|
|
|
TERN_(FILAMENT_WIDTH_SENSOR, filwidth.update_volumetric()); |
|
|
|
|
|
|
|
|
|
|
|
// Handle Bed Temp Errors, Heating Watch, etc.
|
|
|
|
|
|
TERN_(HAS_HEATED_BED, manage_heated_bed(ms)); |
|
|
|
|
|
|
|
|
|
|
|
// Handle Heated Chamber Temp Errors, Heating Watch, etc.
|
|
|
|
|
|
TERN_(HAS_HEATED_CHAMBER, manage_heated_chamber(ms)); |
|
|
|
|
|
|
|
|
|
|
|
// Handle Cooler Temp Errors, Cooling Watch, etc.
|
|
|
|
|
|
TERN_(HAS_COOLER, manage_cooler(ms)); |
|
|
|
|
|
|
|
|
#if ENABLED(LASER_COOLANT_FLOW_METER) |
|
|
#if ENABLED(LASER_COOLANT_FLOW_METER) |
|
|
cooler.flowmeter_task(ms); |
|
|
cooler.flowmeter_task(ms); |
|
@ -2479,7 +2393,7 @@ void Temperature::updateTemperaturesFromRawValues() { |
|
|
/**
|
|
|
/**
|
|
|
* Initialize the temperature manager |
|
|
* Initialize the temperature manager |
|
|
* |
|
|
* |
|
|
* The manager is implemented by periodic calls to manage_heater() |
|
|
* The manager is implemented by periodic calls to task() |
|
|
* |
|
|
* |
|
|
* - Init (and disable) SPI thermocouples like MAX6675 and MAX31865 |
|
|
* - Init (and disable) SPI thermocouples like MAX6675 and MAX31865 |
|
|
* - Disable RUMBA JTAG to accommodate a thermocouple extension |
|
|
* - Disable RUMBA JTAG to accommodate a thermocouple extension |
|
@ -3111,7 +3025,7 @@ void Temperature::disable_all_heaters() { |
|
|
static millis_t next_max_tc_ms[MAX_TC_COUNT] = { 0 }; |
|
|
static millis_t next_max_tc_ms[MAX_TC_COUNT] = { 0 }; |
|
|
|
|
|
|
|
|
// Return last-read value between readings
|
|
|
// Return last-read value between readings
|
|
|
millis_t ms = millis(); |
|
|
const millis_t ms = millis(); |
|
|
if (PENDING(ms, next_max_tc_ms[hindex])) |
|
|
if (PENDING(ms, next_max_tc_ms[hindex])) |
|
|
return THERMO_TEMP(hindex); |
|
|
return THERMO_TEMP(hindex); |
|
|
|
|
|
|
|
@ -3419,16 +3333,18 @@ void Temperature::isr() { |
|
|
_PWM_MOD(COOLER, soft_pwm_cooler, temp_cooler); |
|
|
_PWM_MOD(COOLER, soft_pwm_cooler, temp_cooler); |
|
|
#endif |
|
|
#endif |
|
|
|
|
|
|
|
|
#if BOTH(USE_CONTROLLER_FAN, FAN_SOFT_PWM) |
|
|
|
|
|
WRITE(CONTROLLER_FAN_PIN, soft_pwm_controller.add(pwm_mask, soft_pwm_controller_speed)); |
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
#if ENABLED(FAN_SOFT_PWM) |
|
|
#if ENABLED(FAN_SOFT_PWM) |
|
|
|
|
|
|
|
|
|
|
|
#if ENABLED(USE_CONTROLLER_FAN) |
|
|
|
|
|
WRITE(CONTROLLER_FAN_PIN, soft_pwm_controller.add(pwm_mask, soft_pwm_controller_speed)); |
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
#define _FAN_PWM(N) do{ \ |
|
|
#define _FAN_PWM(N) do{ \ |
|
|
uint8_t &spcf = soft_pwm_count_fan[N]; \ |
|
|
uint8_t &spcf = soft_pwm_count_fan[N]; \ |
|
|
spcf = (spcf & pwm_mask) + (soft_pwm_amount_fan[N] >> 1); \ |
|
|
spcf = (spcf & pwm_mask) + (soft_pwm_amount_fan[N] >> 1); \ |
|
|
WRITE_FAN(N, spcf > pwm_mask ? HIGH : LOW); \ |
|
|
WRITE_FAN(N, spcf > pwm_mask ? HIGH : LOW); \ |
|
|
}while(0) |
|
|
}while(0) |
|
|
|
|
|
|
|
|
#if HAS_FAN0 |
|
|
#if HAS_FAN0 |
|
|
_FAN_PWM(0); |
|
|
_FAN_PWM(0); |
|
|
#endif |
|
|
#endif |
|
|