|
|
|
@ -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)
|
|
|
|
|
bool Temperature::pid_debug_flag; // = 0
|
|
|
|
|
#define HAS_PID_DEBUG 1
|
|
|
|
|
bool Temperature::pid_debug_flag; // = false
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if HAS_PID_HEATING
|
|
|
|
|
|
|
|
|
|
template<typename TT, int MIN_POW, int MAX_POW>
|
|
|
|
|
class PIDRunner {
|
|
|
|
|
public:
|
|
|
|
|
TT &tempinfo;
|
|
|
|
|
__typeof__(TT::pid) work_pid{0};
|
|
|
|
|
float temp_iState = 0, temp_dState = 0;
|
|
|
|
|
bool pid_reset = true;
|
|
|
|
|
|
|
|
|
|
PIDRunner(TT &t) : tempinfo(t) { }
|
|
|
|
|
|
|
|
|
|
float get_pid_output() {
|
|
|
|
|
|
|
|
|
|
#if ENABLED(PID_OPENLOOP)
|
|
|
|
|
|
|
|
|
|
return constrain(tempinfo.target, 0, MAX_POW);
|
|
|
|
|
|
|
|
|
|
#else // !PID_OPENLOOP
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
pid_reset = true;
|
|
|
|
|
return MAX_POW;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pid_reset) {
|
|
|
|
|
pid_reset = false;
|
|
|
|
|
temp_iState = 0.0;
|
|
|
|
|
work_pid.Kd = 0.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
work_pid.Kp = tempinfo.pid.Kp * pid_error;
|
|
|
|
|
work_pid.Ki = tempinfo.pid.Ki * temp_iState;
|
|
|
|
|
work_pid.Kd = work_pid.Kd + PID_K2 * (tempinfo.pid.Kd * (temp_dState - tempinfo.celsius) - work_pid.Kd);
|
|
|
|
|
|
|
|
|
|
temp_dState = tempinfo.celsius;
|
|
|
|
|
|
|
|
|
|
return constrain(work_pid.Kp + work_pid.Ki + work_pid.Kd + float(MIN_POW), 0, MAX_POW);
|
|
|
|
|
|
|
|
|
|
#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)
|
|
|
|
|
#if DISABLED(PID_OPENLOOP)
|
|
|
|
|
static hotend_pid_t work_pid[HOTENDS];
|
|
|
|
|
static float temp_iState[HOTENDS] = { 0 },
|
|
|
|
|
temp_dState[HOTENDS] = { 0 };
|
|
|
|
|
static Flags<HOTENDS> pid_reset;
|
|
|
|
|
const float pid_error = temp_hotend[ee].target - temp_hotend[ee].celsius;
|
|
|
|
|
|
|
|
|
|
float pid_output;
|
|
|
|
|
typedef PIDRunner<hotend_info_t, 0, PID_MAX> PIDRunnerHotend;
|
|
|
|
|
|
|
|
|
|
if (temp_hotend[ee].target == 0
|
|
|
|
|
|| pid_error < -(PID_FUNCTIONAL_RANGE)
|
|
|
|
|
|| TERN0(HEATER_IDLE_HANDLER, heater_idle[ee].timed_out)
|
|
|
|
|
) {
|
|
|
|
|
pid_output = 0;
|
|
|
|
|
pid_reset.set(ee);
|
|
|
|
|
}
|
|
|
|
|
else if (pid_error > PID_FUNCTIONAL_RANGE) {
|
|
|
|
|
pid_output = PID_MAX;
|
|
|
|
|
pid_reset.set(ee);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (pid_reset[ee]) {
|
|
|
|
|
temp_iState[ee] = 0.0;
|
|
|
|
|
work_pid[ee].Kd = 0.0;
|
|
|
|
|
pid_reset.clear(ee);
|
|
|
|
|
}
|
|
|
|
|
static PIDRunnerHotend hotend_pid[HOTENDS] = {
|
|
|
|
|
#define _HOTENDPID(E) temp_hotend[E],
|
|
|
|
|
REPEAT(HOTENDS, _HOTENDPID)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
const float max_power_over_i_gain = float(PID_MAX) / PID_PARAM(Ki, ee) - float(MIN_POWER);
|
|
|
|
|
temp_iState[ee] = constrain(temp_iState[ee] + pid_error, 0, max_power_over_i_gain);
|
|
|
|
|
work_pid[ee].Kp = PID_PARAM(Kp, ee) * pid_error;
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
#if ENABLED(PID_EXTRUSION_SCALING)
|
|
|
|
|
#if HOTENDS == 1
|
|
|
|
|
constexpr bool this_hotend = true;
|
|
|
|
|
#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;
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
const float pid_output = constrain(temp_hotend[ee].target, 0, PID_MAX);
|
|
|
|
|
|
|
|
|
|
#endif // PID_OPENLOOP
|
|
|
|
|
const float pid_output = hotend_pid[ee].get_pid_output();
|
|
|
|
|
|
|
|
|
|
#if ENABLED(PID_DEBUG)
|
|
|
|
|
if (ee == active_extruder && pid_debug_flag) {
|
|
|
|
|
SERIAL_ECHO_MSG(STR_PID_DEBUG, ee, STR_PID_DEBUG_INPUT, temp_hotend[ee].celsius, STR_PID_DEBUG_OUTPUT, pid_output
|
|
|
|
|
#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
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (ee == active_extruder)
|
|
|
|
|
hotend_pid[ee].debug(temp_hotend[ee].celsius, pid_output, F("E"), ee);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#elif ENABLED(MPCTEMP)
|
|
|
|
|
|
|
|
|
|
MPCHeaterInfo &hotend = temp_hotend[ee];
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
@ -1509,61 +1506,9 @@ void Temperature::min_temp_error(const heater_id_t heater_id) {
|
|
|
|
|
#if ENABLED(PIDTEMPBED)
|
|
|
|
|
|
|
|
|
|
float Temperature::get_pid_output_bed() {
|
|
|
|
|
|
|
|
|
|
#if DISABLED(PID_OPENLOOP)
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
static PIDRunner<bed_info_t, MIN_BED_POWER, MAX_BED_POWER> bed_pid(temp_bed);
|
|
|
|
|
const float pid_output = bed_pid.get_pid_output();
|
|
|
|
|
TERN_(PID_BED_DEBUG, bed_pid.debug(temp_bed.celsius, pid_output, F("(Bed)")));
|
|
|
|
|
return pid_output;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1572,114 +1517,17 @@ void Temperature::min_temp_error(const heater_id_t heater_id) {
|
|
|
|
|
#if ENABLED(PIDTEMPCHAMBER)
|
|
|
|
|
|
|
|
|
|
float Temperature::get_pid_output_chamber() {
|
|
|
|
|
|
|
|
|
|
#if DISABLED(PID_OPENLOOP)
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
static PIDRunner<chamber_info_t, MIN_CHAMBER_POWER, MAX_CHAMBER_POWER> chamber_pid(temp_chamber);
|
|
|
|
|
const float pid_output = chamber_pid.get_pid_output();
|
|
|
|
|
TERN_(PID_CHAMBER_DEBUG, chamber_pid.debug(temp_chamber.celsius, pid_output, F("(Chamber)")));
|
|
|
|
|
return pid_output;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif // PIDTEMPCHAMBER
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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
|
|
|
|
|
#if HAS_HOTEND
|
|
|
|
|
|
|
|
|
|
void Temperature::manage_hotends(const millis_t &ms) {
|
|
|
|
|
HOTEND_LOOP() {
|
|
|
|
|
#if ENABLED(THERMAL_PROTECTION_HOTENDS)
|
|
|
|
|
if (degHotend(e) > temp_range[e].maxtemp) max_temp_error((heater_id_t)e);
|
|
|
|
@ -1707,25 +1555,13 @@ void Temperature::manage_heater() {
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
} // 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
|
|
|
|
|
#if HAS_HEATED_BED
|
|
|
|
|
|
|
|
|
|
// 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());
|
|
|
|
|
|
|
|
|
|
#if HAS_HEATED_BED
|
|
|
|
|
void Temperature::manage_heated_bed(const millis_t &ms) {
|
|
|
|
|
|
|
|
|
|
#if ENABLED(THERMAL_PROTECTION_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[IDLE_INDEX_BED].timed_out) {
|
|
|
|
|
temp_bed.soft_pwm_amount = 0;
|
|
|
|
|
#if DISABLED(PIDTEMPBED)
|
|
|
|
|
WRITE_HEATER_BED(LOW);
|
|
|
|
|
#endif
|
|
|
|
|
if (DISABLED(PIDTEMPBED)) WRITE_HEATER_BED(LOW);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
#endif
|
|
|
|
@ -1785,10 +1619,10 @@ void Temperature::manage_heater() {
|
|
|
|
|
#if ENABLED(BED_LIMIT_SWITCHING)
|
|
|
|
|
if (temp_bed.celsius >= temp_bed.target + BED_HYSTERESIS)
|
|
|
|
|
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;
|
|
|
|
|
#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
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
@ -1799,10 +1633,13 @@ void Temperature::manage_heater() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} 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
|
|
|
|
|
#define CHAMBER_CHECK_INTERVAL 1000UL
|
|
|
|
@ -1897,17 +1734,17 @@ void Temperature::manage_heater() {
|
|
|
|
|
if (flag_chamber_excess_heat) {
|
|
|
|
|
temp_chamber.soft_pwm_amount = 0;
|
|
|
|
|
#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
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
#if ENABLED(CHAMBER_LIMIT_SWITCHING)
|
|
|
|
|
if (temp_chamber.celsius >= temp_chamber.target + TEMP_CHAMBER_HYSTERESIS)
|
|
|
|
|
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;
|
|
|
|
|
#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
|
|
|
|
|
#if ENABLED(CHAMBER_VENT)
|
|
|
|
|
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);
|
|
|
|
|
#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
|
|
|
|
|
#define COOLER_CHECK_INTERVAL 2000UL
|
|
|
|
@ -1984,8 +1824,82 @@ void Temperature::manage_heater() {
|
|
|
|
|
#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);
|
|
|
|
|
#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)
|
|
|
|
|
cooler.flowmeter_task(ms);
|
|
|
|
@ -2479,7 +2393,7 @@ void Temperature::updateTemperaturesFromRawValues() {
|
|
|
|
|
/**
|
|
|
|
|
* 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
|
|
|
|
|
* - 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 };
|
|
|
|
|
|
|
|
|
|
// Return last-read value between readings
|
|
|
|
|
millis_t ms = millis();
|
|
|
|
|
const millis_t ms = millis();
|
|
|
|
|
if (PENDING(ms, next_max_tc_ms[hindex]))
|
|
|
|
|
return THERMO_TEMP(hindex);
|
|
|
|
|
|
|
|
|
@ -3419,16 +3333,18 @@ void Temperature::isr() {
|
|
|
|
|
_PWM_MOD(COOLER, soft_pwm_cooler, temp_cooler);
|
|
|
|
|
#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(USE_CONTROLLER_FAN)
|
|
|
|
|
WRITE(CONTROLLER_FAN_PIN, soft_pwm_controller.add(pwm_mask, soft_pwm_controller_speed));
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#define _FAN_PWM(N) do{ \
|
|
|
|
|
uint8_t &spcf = soft_pwm_count_fan[N]; \
|
|
|
|
|
spcf = (spcf & pwm_mask) + (soft_pwm_amount_fan[N] >> 1); \
|
|
|
|
|
WRITE_FAN(N, spcf > pwm_mask ? HIGH : LOW); \
|
|
|
|
|
}while(0)
|
|
|
|
|
|
|
|
|
|
#if HAS_FAN0
|
|
|
|
|
_FAN_PWM(0);
|
|
|
|
|
#endif
|
|
|
|
|