1
0
mirror of https://github.com/MarlinFirmware/Marlin.git synced 2024-11-30 15:26:18 +00:00

♻️ Encapsulate PID in class (#24389)

This commit is contained in:
Scott Lahteine 2022-07-03 10:32:47 -05:00
parent 678955949f
commit e4f85e8fbc
13 changed files with 219 additions and 301 deletions

View File

@ -789,7 +789,7 @@ void idle(bool no_stepper_sleep/*=false*/) {
manage_inactivity(no_stepper_sleep); manage_inactivity(no_stepper_sleep);
// Manage Heaters (and Watchdog) // Manage Heaters (and Watchdog)
thermalManager.manage_heater(); thermalManager.task();
// Max7219 heartbeat, animation, etc // Max7219 heartbeat, animation, etc
TERN_(MAX7219_DEBUG, max7219.idle_tasks()); TERN_(MAX7219_DEBUG, max7219.idle_tasks());

View File

@ -227,10 +227,6 @@
#define STR_PID_DEBUG " PID_DEBUG " #define STR_PID_DEBUG " PID_DEBUG "
#define STR_PID_DEBUG_INPUT ": Input " #define STR_PID_DEBUG_INPUT ": Input "
#define STR_PID_DEBUG_OUTPUT " Output " #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_INVALID_EXTRUDER_NUM " - Invalid extruder number !"
#define STR_MPC_AUTOTUNE "MPC Autotune" #define STR_MPC_AUTOTUNE "MPC Autotune"
#define STR_MPC_AUTOTUNE_START " start for " STR_E #define STR_MPC_AUTOTUNE_START " start for " STR_E

View File

@ -29,10 +29,10 @@ void safe_delay(millis_t ms) {
while (ms > 50) { while (ms > 50) {
ms -= 50; ms -= 50;
delay(50); delay(50);
thermalManager.manage_heater(); thermalManager.task();
} }
delay(ms); 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 // A delay to provide brittle hosts time to receive bytes

View File

@ -290,7 +290,7 @@ void plan_arc(
for (uint16_t i = 1; i < segments; i++) { // Iterate (segments-1) times for (uint16_t i = 1; i < segments; i++) { // Iterate (segments-1) times
thermalManager.manage_heater(); thermalManager.task();
const millis_t ms = millis(); const millis_t ms = millis();
if (ELAPSED(ms, next_idle_ms)) { if (ELAPSED(ms, next_idle_ms)) {
next_idle_ms = ms + 200UL; next_idle_ms = ms + 200UL;

View File

@ -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. buff[ind] = '\0'; // Of course, I'm a Terminator.
const bool is_empty = (ind == 0); // An empty line? const bool is_empty = (ind == 0); // An empty line?
if (is_empty) if (is_empty)
thermalManager.manage_heater(); // Keep sensors satisfied thermalManager.task(); // Keep sensors satisfied
else else
ind = 0; // Start a new line ind = 0; // Start a new line
return is_empty; // Inform the caller return is_empty; // Inform the caller

View File

@ -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) if (thermalManager.temp_hotend[0].target < thermalManager.extrude_min_temp)
Popup_Handler(ETemp); Popup_Handler(ETemp);
else { 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); Popup_Handler(Heating);
thermalManager.wait_for_hotend(0); 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); Popup_Handler(ETemp);
} }
else { 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); Popup_Handler(Heating);
thermalManager.wait_for_hotend(0); thermalManager.wait_for_hotend(0);
Redraw_Menu(); 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) if (thermalManager.temp_hotend[0].target < thermalManager.extrude_min_temp)
Popup_Handler(ETemp); Popup_Handler(ETemp);
else { 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); Popup_Handler(Heating);
thermalManager.wait_for_hotend(0); 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); Popup_Handler(ETemp);
} }
else { 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); Popup_Handler(Heating);
thermalManager.wait_for_hotend(0); 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) if (thermalManager.temp_hotend[0].target < thermalManager.extrude_min_temp)
Popup_Handler(ETemp); Popup_Handler(ETemp);
else { 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); Popup_Handler(Heating);
thermalManager.wait_for_hotend(0); thermalManager.wait_for_hotend(0);
} }
@ -4404,7 +4404,7 @@ void CrealityDWINClass::Popup_Control() {
if (thermalManager.temp_hotend[0].target < thermalManager.extrude_min_temp) if (thermalManager.temp_hotend[0].target < thermalManager.extrude_min_temp)
Popup_Handler(ETemp); Popup_Handler(ETemp);
else { 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); Popup_Handler(Heating);
thermalManager.wait_for_hotend(0); thermalManager.wait_for_hotend(0);
} }

View File

@ -169,7 +169,7 @@ namespace ExtUI {
} }
void yield() { void yield() {
if (!flags.printer_killed) thermalManager.manage_heater(); if (!flags.printer_killed) thermalManager.task();
} }
void enableHeater(const extruder_t extruder) { void enableHeater(const extruder_t extruder) {

View File

@ -48,7 +48,7 @@ void Buzzer::tone(const uint16_t duration, const uint16_t frequency/*=0*/) {
if (!ui.sound_on) return; if (!ui.sound_on) return;
while (buffer.isFull()) { while (buffer.isFull()) {
tick(); tick();
thermalManager.manage_heater(); thermalManager.task();
} }
tone_t tone = { duration, frequency }; tone_t tone = { duration, frequency };
buffer.enqueue(tone); buffer.enqueue(tone);

View File

@ -966,7 +966,7 @@ FORCE_INLINE void segment_idle(millis_t &next_idle_ms) {
next_idle_ms = ms + 200UL; next_idle_ms = ms + 200UL;
return idle(); return idle();
} }
thermalManager.manage_heater(); // Returns immediately on most calls thermalManager.task(); // Returns immediately on most calls
} }
#if IS_KINEMATIC #if IS_KINEMATIC

View File

@ -123,7 +123,7 @@ void cubic_b_spline(
for (float t = 0; t < 1;) { for (float t = 0; t < 1;) {
thermalManager.manage_heater(); thermalManager.task();
millis_t now = millis(); millis_t now = millis();
if (ELAPSED(now, next_idle_ms)) { if (ELAPSED(now, next_idle_ms)) {
next_idle_ms = now + 200UL; next_idle_ms = now + 200UL;

View File

@ -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_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 #if HAS_HOTEND
float Temperature::get_pid_output_hotend(const uint8_t E_NAME) { float Temperature::get_pid_output_hotend(const uint8_t E_NAME) {
const uint8_t ee = HOTEND_INDEX; const uint8_t ee = HOTEND_INDEX;
#if ENABLED(PIDTEMP) #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 static PIDRunnerHotend hotend_pid[HOTENDS] = {
|| pid_error < -(PID_FUNCTIONAL_RANGE) #define _HOTENDPID(E) temp_hotend[E],
|| TERN0(HEATER_IDLE_HANDLER, heater_idle[ee].timed_out) REPEAT(HOTENDS, _HOTENDPID)
) { };
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);
}
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 pid_output = hotend_pid[ee].get_pid_output();
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
#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 #if HAS_HEATED_BED
// 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 void Temperature::manage_heated_bed(const millis_t &ms) {
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
#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

View File

@ -232,6 +232,7 @@ public:
typedef struct HeaterInfo : public TempInfo { typedef struct HeaterInfo : public TempInfo {
celsius_t target; celsius_t target;
uint8_t soft_pwm_amount; uint8_t soft_pwm_amount;
bool is_below_target(const celsius_t offs=0) const { return (celsius < (target + offs)); }
} heater_info_t; } heater_info_t;
// A heater with PID stabilization // A heater with PID stabilization
@ -715,9 +716,9 @@ class Temperature {
static void readings_ready(); 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 * Preheating hotends
@ -807,6 +808,8 @@ class Temperature {
#endif #endif
} }
static void manage_hotends(const millis_t &ms);
#endif // HAS_HOTEND #endif // HAS_HOTEND
#if HAS_HEATED_BED #if HAS_HEATED_BED
@ -819,6 +822,9 @@ class Temperature {
static celsius_t degTargetBed() { return temp_bed.target; } static celsius_t degTargetBed() { return temp_bed.target; }
static bool isHeatingBed() { return temp_bed.target > temp_bed.celsius; } static bool isHeatingBed() { return temp_bed.target > temp_bed.celsius; }
static bool isCoolingBed() { 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 // 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())); } 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 void wait_for_bed_heating();
static bool degBedNear(const celsius_t temp) { static void manage_heated_bed(const millis_t &ms);
return ABS(wholeDegBed() - temp) < (TEMP_BED_HYSTERESIS);
}
#endif // HAS_HEATED_BED #endif // HAS_HEATED_BED
@ -863,6 +867,7 @@ class Temperature {
static bool isHeatingChamber() { return temp_chamber.target > temp_chamber.celsius; } static bool isHeatingChamber() { return temp_chamber.target > temp_chamber.celsius; }
static bool isCoolingChamber() { 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 bool wait_for_chamber(const bool no_wait_for_cooling=true);
static void manage_heated_chamber(const millis_t &ms);
#endif #endif
#endif #endif
@ -886,6 +891,7 @@ class Temperature {
static bool isLaserHeating() { return temp_cooler.target > temp_cooler.celsius; } static bool isLaserHeating() { return temp_cooler.target > temp_cooler.celsius; }
static bool isLaserCooling() { 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 bool wait_for_cooler(const bool no_wait_for_cooling=true);
static void manage_cooler(const millis_t &ms);
#endif #endif
#endif #endif

View File

@ -62,7 +62,7 @@
#define USB_HOST_MANUAL_POLL // Optimization to shut off IRQ automatically #define USB_HOST_MANUAL_POLL // Optimization to shut off IRQ automatically
// Workarounds to keep Marlin's watchdog timer from barking... // 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 SYSTEM_OR_SPECIAL_YIELD(...) marlin_yield();
#define delay(x) safe_delay(x) #define delay(x) safe_delay(x)