mirror of
https://github.com/MarlinFirmware/Marlin.git
synced 2024-11-22 18:25:18 +00:00
♻️ Encapsulate PID in class (#24389)
This commit is contained in:
parent
12ae80bcec
commit
08e6e8b9be
@ -782,7 +782,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());
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -261,7 +261,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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -871,7 +871,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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user