diff --git a/Marlin/src/MarlinCore.cpp b/Marlin/src/MarlinCore.cpp index 2724006dd1..c1bd973b4a 100644 --- a/Marlin/src/MarlinCore.cpp +++ b/Marlin/src/MarlinCore.cpp @@ -789,7 +789,7 @@ void idle(bool no_stepper_sleep/*=false*/) { manage_inactivity(no_stepper_sleep); // Manage Heaters (and Watchdog) - thermalManager.manage_heater(); + thermalManager.task(); // Max7219 heartbeat, animation, etc TERN_(MAX7219_DEBUG, max7219.idle_tasks()); diff --git a/Marlin/src/core/language.h b/Marlin/src/core/language.h index 16d7b1bf66..157bd69185 100644 --- a/Marlin/src/core/language.h +++ b/Marlin/src/core/language.h @@ -227,10 +227,6 @@ #define STR_PID_DEBUG " PID_DEBUG " #define STR_PID_DEBUG_INPUT ": Input " #define STR_PID_DEBUG_OUTPUT " Output " -#define STR_PID_DEBUG_PTERM " pTerm " -#define STR_PID_DEBUG_ITERM " iTerm " -#define STR_PID_DEBUG_DTERM " dTerm " -#define STR_PID_DEBUG_CTERM " cTerm " #define STR_INVALID_EXTRUDER_NUM " - Invalid extruder number !" #define STR_MPC_AUTOTUNE "MPC Autotune" #define STR_MPC_AUTOTUNE_START " start for " STR_E diff --git a/Marlin/src/core/utility.cpp b/Marlin/src/core/utility.cpp index 84e4c1f696..9cdf8dec7b 100644 --- a/Marlin/src/core/utility.cpp +++ b/Marlin/src/core/utility.cpp @@ -29,10 +29,10 @@ void safe_delay(millis_t ms) { while (ms > 50) { ms -= 50; delay(50); - thermalManager.manage_heater(); + thermalManager.task(); } delay(ms); - thermalManager.manage_heater(); // This keeps us safe if too many small safe_delay() calls are made + thermalManager.task(); // This keeps us safe if too many small safe_delay() calls are made } // A delay to provide brittle hosts time to receive bytes diff --git a/Marlin/src/gcode/motion/G2_G3.cpp b/Marlin/src/gcode/motion/G2_G3.cpp index d9458f61eb..14ef9ac2a6 100644 --- a/Marlin/src/gcode/motion/G2_G3.cpp +++ b/Marlin/src/gcode/motion/G2_G3.cpp @@ -290,7 +290,7 @@ void plan_arc( for (uint16_t i = 1; i < segments; i++) { // Iterate (segments-1) times - thermalManager.manage_heater(); + thermalManager.task(); const millis_t ms = millis(); if (ELAPSED(ms, next_idle_ms)) { next_idle_ms = ms + 200UL; diff --git a/Marlin/src/gcode/queue.cpp b/Marlin/src/gcode/queue.cpp index 454a009b85..a390a46d8e 100644 --- a/Marlin/src/gcode/queue.cpp +++ b/Marlin/src/gcode/queue.cpp @@ -384,7 +384,7 @@ inline bool process_line_done(uint8_t &sis, char (&buff)[MAX_CMD_SIZE], int &ind buff[ind] = '\0'; // Of course, I'm a Terminator. const bool is_empty = (ind == 0); // An empty line? if (is_empty) - thermalManager.manage_heater(); // Keep sensors satisfied + thermalManager.task(); // Keep sensors satisfied else ind = 0; // Start a new line return is_empty; // Inform the caller diff --git a/Marlin/src/lcd/e3v2/jyersui/dwin.cpp b/Marlin/src/lcd/e3v2/jyersui/dwin.cpp index 5f1507feb3..285013d750 100644 --- a/Marlin/src/lcd/e3v2/jyersui/dwin.cpp +++ b/Marlin/src/lcd/e3v2/jyersui/dwin.cpp @@ -1202,7 +1202,7 @@ void CrealityDWINClass::Menu_Item_Handler(uint8_t menu, uint8_t item, bool draw/ if (thermalManager.temp_hotend[0].target < thermalManager.extrude_min_temp) Popup_Handler(ETemp); else { - if (thermalManager.temp_hotend[0].celsius < thermalManager.temp_hotend[0].target - 2) { + if (thermalManager.temp_hotend[0].is_below_target(-2)) { Popup_Handler(Heating); thermalManager.wait_for_hotend(0); } @@ -1345,7 +1345,7 @@ void CrealityDWINClass::Menu_Item_Handler(uint8_t menu, uint8_t item, bool draw/ Popup_Handler(ETemp); } else { - if (thermalManager.temp_hotend[0].celsius < thermalManager.temp_hotend[0].target - 2) { + if (thermalManager.temp_hotend[0].is_below_target(-2)) { Popup_Handler(Heating); thermalManager.wait_for_hotend(0); Redraw_Menu(); @@ -1732,7 +1732,7 @@ void CrealityDWINClass::Menu_Item_Handler(uint8_t menu, uint8_t item, bool draw/ if (thermalManager.temp_hotend[0].target < thermalManager.extrude_min_temp) Popup_Handler(ETemp); else { - if (thermalManager.temp_hotend[0].celsius < thermalManager.temp_hotend[0].target - 2) { + if (thermalManager.temp_hotend[0].is_below_target(-2)) { Popup_Handler(Heating); thermalManager.wait_for_hotend(0); } @@ -1751,7 +1751,7 @@ void CrealityDWINClass::Menu_Item_Handler(uint8_t menu, uint8_t item, bool draw/ Popup_Handler(ETemp); } else { - if (thermalManager.temp_hotend[0].celsius < thermalManager.temp_hotend[0].target - 2) { + if (thermalManager.temp_hotend[0].is_below_target(-2)) { Popup_Handler(Heating); thermalManager.wait_for_hotend(0); } @@ -1769,7 +1769,7 @@ void CrealityDWINClass::Menu_Item_Handler(uint8_t menu, uint8_t item, bool draw/ if (thermalManager.temp_hotend[0].target < thermalManager.extrude_min_temp) Popup_Handler(ETemp); else { - if (thermalManager.temp_hotend[0].celsius < thermalManager.temp_hotend[0].target - 2) { + if (thermalManager.temp_hotend[0].is_below_target(-2)) { Popup_Handler(Heating); thermalManager.wait_for_hotend(0); } @@ -4404,7 +4404,7 @@ void CrealityDWINClass::Popup_Control() { if (thermalManager.temp_hotend[0].target < thermalManager.extrude_min_temp) Popup_Handler(ETemp); else { - if (thermalManager.temp_hotend[0].celsius < thermalManager.temp_hotend[0].target - 2) { + if (thermalManager.temp_hotend[0].is_below_target(-2)) { Popup_Handler(Heating); thermalManager.wait_for_hotend(0); } diff --git a/Marlin/src/lcd/extui/ui_api.cpp b/Marlin/src/lcd/extui/ui_api.cpp index 68b0a81fe5..57822279c5 100644 --- a/Marlin/src/lcd/extui/ui_api.cpp +++ b/Marlin/src/lcd/extui/ui_api.cpp @@ -169,7 +169,7 @@ namespace ExtUI { } void yield() { - if (!flags.printer_killed) thermalManager.manage_heater(); + if (!flags.printer_killed) thermalManager.task(); } void enableHeater(const extruder_t extruder) { diff --git a/Marlin/src/libs/buzzer.cpp b/Marlin/src/libs/buzzer.cpp index a3c838ebc0..1e2f23c5fd 100644 --- a/Marlin/src/libs/buzzer.cpp +++ b/Marlin/src/libs/buzzer.cpp @@ -48,7 +48,7 @@ void Buzzer::tone(const uint16_t duration, const uint16_t frequency/*=0*/) { if (!ui.sound_on) return; while (buffer.isFull()) { tick(); - thermalManager.manage_heater(); + thermalManager.task(); } tone_t tone = { duration, frequency }; buffer.enqueue(tone); diff --git a/Marlin/src/module/motion.cpp b/Marlin/src/module/motion.cpp index 36afdb2e48..b3b607e677 100644 --- a/Marlin/src/module/motion.cpp +++ b/Marlin/src/module/motion.cpp @@ -966,7 +966,7 @@ FORCE_INLINE void segment_idle(millis_t &next_idle_ms) { next_idle_ms = ms + 200UL; return idle(); } - thermalManager.manage_heater(); // Returns immediately on most calls + thermalManager.task(); // Returns immediately on most calls } #if IS_KINEMATIC diff --git a/Marlin/src/module/planner_bezier.cpp b/Marlin/src/module/planner_bezier.cpp index fa7e16a387..93b118f330 100644 --- a/Marlin/src/module/planner_bezier.cpp +++ b/Marlin/src/module/planner_bezier.cpp @@ -123,7 +123,7 @@ void cubic_b_spline( for (float t = 0; t < 1;) { - thermalManager.manage_heater(); + thermalManager.task(); millis_t now = millis(); if (ELAPSED(now, next_idle_ms)) { next_idle_ms = now + 200UL; diff --git a/Marlin/src/module/temperature.cpp b/Marlin/src/module/temperature.cpp index 8e12b2c400..9bdc6eced7 100644 --- a/Marlin/src/module/temperature.cpp +++ b/Marlin/src/module/temperature.cpp @@ -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 + 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 pid_reset; - const float pid_error = temp_hotend[ee].target - temp_hotend[ee].celsius; - float pid_output; + typedef PIDRunner 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_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_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 diff --git a/Marlin/src/module/temperature.h b/Marlin/src/module/temperature.h index c11c9d76de..80d15ce39e 100644 --- a/Marlin/src/module/temperature.h +++ b/Marlin/src/module/temperature.h @@ -232,6 +232,7 @@ public: typedef struct HeaterInfo : public TempInfo { celsius_t target; uint8_t soft_pwm_amount; + bool is_below_target(const celsius_t offs=0) const { return (celsius < (target + offs)); } } heater_info_t; // A heater with PID stabilization @@ -715,9 +716,9 @@ class Temperature { static void readings_ready(); /** - * Call periodically to manage heaters + * Call periodically to manage heaters and keep the watchdog fed */ - static void manage_heater() __O2; // __O2 added to work around a compiler error + static void task(); /** * Preheating hotends @@ -807,6 +808,8 @@ class Temperature { #endif } + static void manage_hotends(const millis_t &ms); + #endif // HAS_HOTEND #if HAS_HEATED_BED @@ -819,6 +822,9 @@ class Temperature { static celsius_t degTargetBed() { return temp_bed.target; } static bool isHeatingBed() { return temp_bed.target > temp_bed.celsius; } static bool isCoolingBed() { return temp_bed.target < temp_bed.celsius; } + static bool degBedNear(const celsius_t temp) { + return ABS(wholeDegBed() - temp) < (TEMP_BED_HYSTERESIS); + } // Start watching the Bed to make sure it's really heating up static void start_watching_bed() { TERN_(WATCH_BED, watch_bed.restart(degBed(), degTargetBed())); } @@ -835,9 +841,7 @@ class Temperature { static void wait_for_bed_heating(); - static bool degBedNear(const celsius_t temp) { - return ABS(wholeDegBed() - temp) < (TEMP_BED_HYSTERESIS); - } + static void manage_heated_bed(const millis_t &ms); #endif // HAS_HEATED_BED @@ -863,6 +867,7 @@ class Temperature { static bool isHeatingChamber() { return temp_chamber.target > temp_chamber.celsius; } static bool isCoolingChamber() { return temp_chamber.target < temp_chamber.celsius; } static bool wait_for_chamber(const bool no_wait_for_cooling=true); + static void manage_heated_chamber(const millis_t &ms); #endif #endif @@ -886,6 +891,7 @@ class Temperature { static bool isLaserHeating() { return temp_cooler.target > temp_cooler.celsius; } static bool isLaserCooling() { return temp_cooler.target < temp_cooler.celsius; } static bool wait_for_cooler(const bool no_wait_for_cooling=true); + static void manage_cooler(const millis_t &ms); #endif #endif diff --git a/Marlin/src/sd/usb_flashdrive/Sd2Card_FlashDrive.cpp b/Marlin/src/sd/usb_flashdrive/Sd2Card_FlashDrive.cpp index a681af4efa..7d698247e5 100644 --- a/Marlin/src/sd/usb_flashdrive/Sd2Card_FlashDrive.cpp +++ b/Marlin/src/sd/usb_flashdrive/Sd2Card_FlashDrive.cpp @@ -62,7 +62,7 @@ #define USB_HOST_MANUAL_POLL // Optimization to shut off IRQ automatically // Workarounds to keep Marlin's watchdog timer from barking... - void marlin_yield() { thermalManager.manage_heater(); } + void marlin_yield() { thermalManager.task(); } #define SYSTEM_OR_SPECIAL_YIELD(...) marlin_yield(); #define delay(x) safe_delay(x)