Correct handling of min/maxtemp

- Flag the error condition from the temp_mgr_isr
- Handle the error state from the user code

Currently only handles min/maxtemp and relays the error to the original
handler (which is a poor fit for the current design).
This commit is contained in:
Yuri D'Elia 2022-05-25 00:40:36 +02:00
parent 4a0203d691
commit bd9a6acd59
1 changed files with 193 additions and 87 deletions

View File

@ -431,6 +431,53 @@ int getHeaterPower(int heater) {
// reset PID state after changing target_temperature // reset PID state after changing target_temperature
void resetPID(uint8_t extruder _UNUSED) {} void resetPID(uint8_t extruder _UNUSED) {}
enum class TempErrorSource : uint8_t
{
hotend,
bed,
ambient,
};
enum class TempErrorType : uint8_t
{
min,
max,
runaway,
};
// error state (updated via set_temp_error from isr context)
volatile static union
{
uint8_t v;
struct
{
uint8_t error: 1; // error condition
uint8_t assert: 1; // error is still asserted
uint8_t source: 2; // source
uint8_t index: 2; // source index
uint8_t type: 2; // error type
};
} temp_error_state;
// set the error type from within the temp_mgr isr to be handled in manager_heater
// - immediately disable all heaters and turn on all fans at full speed
// - prevent the user to set temperatures until all errors are cleared
void set_temp_error(TempErrorSource source, uint8_t index, TempErrorType type)
{
// keep disabling heaters and keep fans on as long as the condition is asserted
disable_heater();
hotendFanSetFullSpeed();
// set the error state
temp_error_state.error = true;
temp_error_state.assert = true;
temp_error_state.source = (uint8_t)source;
temp_error_state.index = index;
temp_error_state.type = (uint8_t)type;
}
void handle_temp_error();
void manage_heater() void manage_heater()
{ {
#ifdef WATCHDOG #ifdef WATCHDOG
@ -441,6 +488,10 @@ void manage_heater()
if(temp_meas_ready) if(temp_meas_ready)
updateTemperatures(); updateTemperatures();
// handle temperature errors
if(temp_error_state.v)
handle_temp_error();
// periodically check fans // periodically check fans
checkFans(); checkFans();
} }
@ -1493,37 +1544,38 @@ ISR(TIMER0_COMPB_vect)
ENABLE_SOFT_PWM_INTERRUPT(); ENABLE_SOFT_PWM_INTERRUPT();
} }
void check_max_temp() void check_max_temp_raw()
{ {
//heater //heater
#if HEATER_0_RAW_LO_TEMP > HEATER_0_RAW_HI_TEMP #if HEATER_0_RAW_LO_TEMP > HEATER_0_RAW_HI_TEMP
if (current_temperature_raw[0] <= maxttemp_raw[0]) { if (current_temperature_raw[0] <= maxttemp_raw[0]) {
#else #else
if (current_temperature_raw[0] >= maxttemp_raw[0]) { if (current_temperature_raw[0] >= maxttemp_raw[0]) {
#endif #endif
max_temp_error(0); set_temp_error(TempErrorSource::hotend, 0, TempErrorType::max);
} }
//bed //bed
#if defined(BED_MAXTEMP) && (TEMP_SENSOR_BED != 0) #if defined(BED_MAXTEMP) && (TEMP_SENSOR_BED != 0)
#if HEATER_BED_RAW_LO_TEMP > HEATER_BED_RAW_HI_TEMP #if HEATER_BED_RAW_LO_TEMP > HEATER_BED_RAW_HI_TEMP
if (current_temperature_bed_raw <= bed_maxttemp_raw) { if (current_temperature_bed_raw <= bed_maxttemp_raw) {
#else #else
if (current_temperature_bed_raw >= bed_maxttemp_raw) { if (current_temperature_bed_raw >= bed_maxttemp_raw) {
#endif #endif
bed_max_temp_error(); set_temp_error(TempErrorSource::bed, 0, TempErrorType::max);
} }
#endif #endif
//ambient //ambient
#if defined(AMBIENT_MAXTEMP) && (TEMP_SENSOR_AMBIENT != 0) #if defined(AMBIENT_MAXTEMP) && (TEMP_SENSOR_AMBIENT != 0)
#if AMBIENT_RAW_LO_TEMP > AMBIENT_RAW_HI_TEMP #if AMBIENT_RAW_LO_TEMP > AMBIENT_RAW_HI_TEMP
if (current_temperature_raw_ambient <= ambient_maxttemp_raw) { if (current_temperature_raw_ambient <= ambient_maxttemp_raw) {
#else #else
if (current_temperature_raw_ambient >= ambient_maxttemp_raw) { if (current_temperature_raw_ambient >= ambient_maxttemp_raw) {
#endif #endif
ambient_max_temp_error(); set_temp_error(TempErrorSource::ambient, 0, TempErrorType::max);
} }
#endif #endif
} }
//! number of repeating the same state with consecutive step() calls //! number of repeating the same state with consecutive step() calls
//! used to slow down text switching //! used to slow down text switching
struct alert_automaton_mintemp { struct alert_automaton_mintemp {
@ -1581,23 +1633,12 @@ static alert_automaton_mintemp alert_automaton_hotend(m2hotend), alert_automaton
void check_min_temp_heater0() void check_min_temp_heater0()
{ {
//heater
#if HEATER_0_RAW_LO_TEMP > HEATER_0_RAW_HI_TEMP #if HEATER_0_RAW_LO_TEMP > HEATER_0_RAW_HI_TEMP
if (current_temperature_raw[0] >= minttemp_raw[0]) { if (current_temperature_raw[0] >= minttemp_raw[0]) {
#else #else
if (current_temperature_raw[0] <= minttemp_raw[0]) { if (current_temperature_raw[0] <= minttemp_raw[0]) {
#endif #endif
menu_set_serious_error(SERIOUS_ERR_MINTEMP_HEATER); set_temp_error(TempErrorSource::hotend, 0, TempErrorType::min);
min_temp_error(0);
} else if( menu_is_serious_error(SERIOUS_ERR_MINTEMP_HEATER) ) {
// no recovery, just force the user to restart the printer
// which is a safer variant than just continuing printing
// The automaton also checks for hysteresis - the temperature must have reached a few degrees above the MINTEMP, before
// we shall signalize, that MINTEMP has been fixed
// Code notice: normally the alert_automaton instance would have been placed here
// as static alert_automaton_mintemp alert_automaton_hotend, but
// due to stupid compiler that takes 16 more bytes.
alert_automaton_hotend.step(current_temperature[0], minttemp[0] + TEMP_HYSTERESIS);
} }
} }
@ -1608,12 +1649,7 @@ void check_min_temp_bed()
#else #else
if (current_temperature_bed_raw <= bed_minttemp_raw) { if (current_temperature_bed_raw <= bed_minttemp_raw) {
#endif #endif
menu_set_serious_error(SERIOUS_ERR_MINTEMP_BED); set_temp_error(TempErrorSource::bed, 0, TempErrorType::min);
bed_min_temp_error();
} else if( menu_is_serious_error(SERIOUS_ERR_MINTEMP_BED) ){
// no recovery, just force the user to restart the printer
// which is a safer variant than just continuing printing
alert_automaton_bed.step(current_temperature_bed, BED_MINTEMP + TEMP_HYSTERESIS);
} }
} }
@ -1625,72 +1661,70 @@ void check_min_temp_ambient()
#else #else
if (current_temperature_raw_ambient <= ambient_minttemp_raw) { if (current_temperature_raw_ambient <= ambient_minttemp_raw) {
#endif #endif
ambient_min_temp_error(); set_temp_error(TempErrorSource::ambient, 0, TempErrorType::min);
} }
} }
#endif #endif
void check_min_temp() void handle_temp_error()
{ {
static bool bCheckingOnHeater=false; // state variable, which allows to short no-checking delay (is set, when temperature is (first time) over heaterMintemp) // TODO: Stop and the various *_error() functions need an overhaul!
static bool bCheckingOnBed=false; // state variable, which allows to short no-checking delay (is set, when temperature is (first time) over bedMintemp) // The heaters are kept disabled already at a higher level, making almost
static ShortTimer oTimer4minTempHeater; // all the code inside the invidual handlers useless!
static ShortTimer oTimer4minTempBed;
#ifdef AMBIENT_THERMISTOR // relay to the original handler
#ifdef AMBIENT_MINTEMP switch((TempErrorSource)temp_error_state.source) {
check_min_temp_ambient(); case TempErrorSource::hotend:
#endif switch((TempErrorType)temp_error_state.type) {
#if AMBIENT_RAW_LO_TEMP > AMBIENT_RAW_HI_TEMP case TempErrorType::min:
if(current_temperature_raw_ambient>(OVERSAMPLENR*MINTEMP_MINAMBIENT_RAW)) // thermistor is NTC type if(temp_error_state.assert) {
#else menu_set_serious_error(SERIOUS_ERR_MINTEMP_HEATER);
if(current_temperature_raw_ambient=<(OVERSAMPLENR*MINTEMP_MINAMBIENT_RAW)) min_temp_error(temp_error_state.index);
#endif } else if( menu_is_serious_error(SERIOUS_ERR_MINTEMP_HEATER) ) {
{ // ambient temperature is low // no recovery, just force the user to restart the printer
#endif //AMBIENT_THERMISTOR // which is a safer variant than just continuing printing
// *** 'common' part of code for MK2.5 & MK3 // The automaton also checks for hysteresis - the temperature must have reached a few degrees above the MINTEMP, before
// * nozzle checking // we shall signalize, that MINTEMP has been fixed
if(target_temperature[active_extruder]>minttemp[active_extruder]) // Code notice: normally the alert_automaton instance would have been placed here
{ // ~ nozzle heating is on // as static alert_automaton_mintemp alert_automaton_hotend, but
bCheckingOnHeater=bCheckingOnHeater||(current_temperature[active_extruder]>(minttemp[active_extruder]+TEMP_HYSTERESIS)); // for eventually delay cutting alert_automaton_hotend.step(current_temperature[0], minttemp[0] + TEMP_HYSTERESIS);
if(oTimer4minTempHeater.expired(HEATER_MINTEMP_DELAY)||(!oTimer4minTempHeater.running())||bCheckingOnHeater) }
{ break;
bCheckingOnHeater=true; // not necessary case TempErrorType::max:
check_min_temp_heater0(); // delay is elapsed or temperature is/was over minTemp => periodical checking is active max_temp_error(temp_error_state.index);
} break;
} }
else { // ~ nozzle heating is off break;
oTimer4minTempHeater.start(); case TempErrorSource::bed:
bCheckingOnHeater=false; switch((TempErrorType)temp_error_state.type) {
} case TempErrorType::min:
// * bed checking if(temp_error_state.assert) {
if(target_temperature_bed>BED_MINTEMP) menu_set_serious_error(SERIOUS_ERR_MINTEMP_BED);
{ // ~ bed heating is on bed_min_temp_error();
bCheckingOnBed=bCheckingOnBed||(current_temperature_bed>(BED_MINTEMP+TEMP_HYSTERESIS)); // for eventually delay cutting } else if( menu_is_serious_error(SERIOUS_ERR_MINTEMP_BED) ){
if(oTimer4minTempBed.expired(BED_MINTEMP_DELAY)||(!oTimer4minTempBed.running())||bCheckingOnBed) // no recovery, just force the user to restart the printer
{ // which is a safer variant than just continuing printing
bCheckingOnBed=true; // not necessary alert_automaton_bed.step(current_temperature_bed, BED_MINTEMP + TEMP_HYSTERESIS);
check_min_temp_bed(); // delay is elapsed or temperature is/was over minTemp => periodical checking is active }
} break;
} case TempErrorType::max:
else { // ~ bed heating is off bed_max_temp_error();
oTimer4minTempBed.start(); break;
bCheckingOnBed=false; }
} break;
// *** end of 'common' part case TempErrorSource::ambient:
#ifdef AMBIENT_THERMISTOR switch((TempErrorType)temp_error_state.type) {
} case TempErrorType::min: ambient_min_temp_error(); break;
else { // ambient temperature is standard case TempErrorType::max: ambient_max_temp_error(); break;
check_min_temp_heater0(); case TempErrorType::runaway: break; // not needed
check_min_temp_bed(); }
} break;
#endif //AMBIENT_THERMISTOR }
} }
#ifdef PIDTEMP #ifdef PIDTEMP
// Apply the scale factors to the PID values // Apply the scale factors to the PID values
float scalePID_i(float i) float scalePID_i(float i)
{ {
return i*PID_dT; return i*PID_dT;
@ -2051,13 +2085,14 @@ static void setIsrTargetTemperatures()
/* Synchronize temperatures: /* Synchronize temperatures:
- fetch updated values from temp_mgr_isr to current values - fetch updated values from temp_mgr_isr to current values
- update target temperatures for temp_mgr_isr regulation - update target temperatures for temp_mgr_isr regulation *if* no temperature error is set
This function is blocking: check temp_meas_ready before calling! */ This function is blocking: check temp_meas_ready before calling! */
static void updateTemperatures() static void updateTemperatures()
{ {
TempMgrGuard temp_mgr_guard; TempMgrGuard temp_mgr_guard;
setCurrentTemperaturesFromIsr(); setCurrentTemperaturesFromIsr();
setIsrTargetTemperatures(); if(!temp_error_state.v)
setIsrTargetTemperatures();
temp_meas_ready = false; temp_meas_ready = false;
} }
@ -2085,15 +2120,18 @@ static void temp_mgr_pid()
pid_bed(current_temperature_bed_isr, target_temperature_bed_isr); pid_bed(current_temperature_bed_isr, target_temperature_bed_isr);
} }
void check_temp_raw();
static void temp_mgr_isr() static void temp_mgr_isr()
{ {
// update *_isr temperatures from raw values for PID regulation // update *_isr temperatures from raw values for PID regulation
setIsrTemperaturesFromRawValues(); setIsrTemperaturesFromRawValues();
// TODO: this is now running inside an isr and cannot directly manipulate the lcd, // clear the error assertion flag before checking again
// this needs to disable temperatures and flag the error to be shown in manage_heater! temp_error_state.assert = false;
check_max_temp();
check_min_temp(); // check min/max temp using raw values
check_temp_raw();
// PID regulation // PID regulation
temp_mgr_pid(); temp_mgr_pid();
@ -2144,3 +2182,71 @@ void disable_heater()
CRITICAL_SECTION_END; CRITICAL_SECTION_END;
} }
void check_min_temp_raw()
{
static bool bCheckingOnHeater = false; // state variable, which allows to short no-checking delay (is set, when temperature is (first time) over heaterMintemp)
static bool bCheckingOnBed = false; // state variable, which allows to short no-checking delay (is set, when temperature is (first time) over bedMintemp)
static ShortTimer oTimer4minTempHeater;
static ShortTimer oTimer4minTempBed;
#ifdef AMBIENT_THERMISTOR
#ifdef AMBIENT_MINTEMP
// we need to check ambient temperature
check_min_temp_ambient();
#endif
#if AMBIENT_RAW_LO_TEMP > AMBIENT_RAW_HI_TEMP
if(current_temperature_raw_ambient>(OVERSAMPLENR*MINTEMP_MINAMBIENT_RAW)) // thermistor is NTC type
#else
if(current_temperature_raw_ambient=<(OVERSAMPLENR*MINTEMP_MINAMBIENT_RAW))
#endif
{
// ambient temperature is low
#endif //AMBIENT_THERMISTOR
// *** 'common' part of code for MK2.5 & MK3
// * nozzle checking
if(target_temperature_isr[active_extruder]>minttemp[active_extruder]) {
// ~ nozzle heating is on
bCheckingOnHeater=bCheckingOnHeater||(current_temperature_isr[active_extruder]>(minttemp[active_extruder]+TEMP_HYSTERESIS)); // for eventually delay cutting
if(oTimer4minTempHeater.expired(HEATER_MINTEMP_DELAY)||(!oTimer4minTempHeater.running())||bCheckingOnHeater) {
bCheckingOnHeater=true; // not necessary
check_min_temp_heater0(); // delay is elapsed or temperature is/was over minTemp => periodical checking is active
}
}
else {
// ~ nozzle heating is off
oTimer4minTempHeater.start();
bCheckingOnHeater=false;
}
// * bed checking
if(target_temperature_bed_isr>BED_MINTEMP) {
// ~ bed heating is on
bCheckingOnBed=bCheckingOnBed||(current_temperature_bed_isr>(BED_MINTEMP+TEMP_HYSTERESIS)); // for eventually delay cutting
if(oTimer4minTempBed.expired(BED_MINTEMP_DELAY)||(!oTimer4minTempBed.running())||bCheckingOnBed) {
bCheckingOnBed=true; // not necessary
check_min_temp_bed(); // delay is elapsed or temperature is/was over minTemp => periodical checking is active
}
}
else {
// ~ bed heating is off
oTimer4minTempBed.start();
bCheckingOnBed=false;
}
// *** end of 'common' part
#ifdef AMBIENT_THERMISTOR
}
else {
// ambient temperature is standard
check_min_temp_heater0();
check_min_temp_bed();
}
#endif //AMBIENT_THERMISTOR
}
void check_temp_raw()
{
// order is relevant: check_min_temp_raw requires max to be reliable due to
// ambient temperature being used for low handling temperatures
check_max_temp_raw();
check_min_temp_raw();
}