2017-06-29 16:35:43 +00:00
/*
temperature . c - temperature control
Part of Marlin
Copyright ( C ) 2011 Camiel Gubbels / Erik van der Zalm
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
/*
This firmware is a mashup between Sprinter and grbl .
( https : //github.com/kliment/Sprinter)
( https : //github.com/simen/grbl/tree)
It has preliminary support for Matthew Roberts advance algorithm
http : //reprap.org/pipermail/reprap-dev/2011-May/003323.html
*/
2022-05-14 13:07:58 +00:00
# include "temperature.h"
# include "stepper.h"
2017-06-29 16:35:43 +00:00
# include "ultralcd.h"
2021-06-12 13:09:03 +00:00
# include "menu.h"
2018-07-29 20:59:14 +00:00
# include "sound.h"
2022-05-14 13:07:58 +00:00
# include "fancheck.h"
2022-06-28 19:06:39 +00:00
# include "messages.h"
2017-06-29 16:35:43 +00:00
2021-06-12 13:21:16 +00:00
# include "SdFatUtil.h"
2017-06-29 16:35:43 +00:00
2017-11-24 20:53:35 +00:00
# include <avr/wdt.h>
2022-05-24 16:37:07 +00:00
# include <util/atomic.h>
2017-12-20 12:42:20 +00:00
# include "adc.h"
2018-09-24 14:54:50 +00:00
# include "ConfigurationStore.h"
2018-11-28 22:43:16 +00:00
# include "Timer.h"
# include "Configuration_prusa.h"
2022-05-14 22:35:59 +00:00
# if (ADC_OVRSAMPL != OVERSAMPLENR)
# error "ADC_OVRSAMPL oversampling must match OVERSAMPLENR"
# endif
2022-06-24 14:04:00 +00:00
// temperature manager timer configuration
# define TEMP_MGR_INTV 0.27 // seconds, ~3.7Hz
# define TIMER5_PRESCALE 256
# define TIMER5_OCRA_OVF (uint16_t)(TEMP_MGR_INTV / ((long double)TIMER5_PRESCALE / F_CPU))
2022-06-26 22:17:46 +00:00
# define TEMP_MGR_INT_FLAG_STATE() (TIFR5 & (1<<OCF5A))
# define TEMP_MGR_INT_FLAG_CLEAR() TIFR5 |= (1<<OCF5A)
2022-06-24 14:04:00 +00:00
# define TEMP_MGR_INTERRUPT_STATE() (TIMSK5 & (1<<OCIE5A))
# define ENABLE_TEMP_MGR_INTERRUPT() TIMSK5 |= (1<<OCIE5A)
# define DISABLE_TEMP_MGR_INTERRUPT() TIMSK5 &= ~(1<<OCIE5A)
# ifdef TEMP_MODEL
// temperature model interface
# include "temp_model.h"
# endif
2017-06-29 16:35:43 +00:00
//===========================================================================
//=============================public variables============================
//===========================================================================
int target_temperature [ EXTRUDERS ] = { 0 } ;
int target_temperature_bed = 0 ;
int current_temperature_raw [ EXTRUDERS ] = { 0 } ;
float current_temperature [ EXTRUDERS ] = { 0.0 } ;
2017-09-05 12:02:35 +00:00
2017-08-30 19:56:48 +00:00
# ifdef PINDA_THERMISTOR
2022-05-24 16:37:07 +00:00
uint16_t current_temperature_raw_pinda = 0 ;
2017-08-30 19:56:48 +00:00
float current_temperature_pinda = 0.0 ;
# endif //PINDA_THERMISTOR
2017-09-05 12:02:35 +00:00
# ifdef AMBIENT_THERMISTOR
2022-05-24 16:37:07 +00:00
int current_temperature_raw_ambient = 0 ;
2017-09-05 12:02:35 +00:00
float current_temperature_ambient = 0.0 ;
# endif //AMBIENT_THERMISTOR
2017-12-15 17:33:35 +00:00
# ifdef VOLT_PWR_PIN
int current_voltage_raw_pwr = 0 ;
# endif
# ifdef VOLT_BED_PIN
int current_voltage_raw_bed = 0 ;
# endif
2020-03-26 13:40:47 +00:00
# ifdef IR_SENSOR_ANALOG
2020-04-28 08:20:21 +00:00
uint16_t current_voltage_raw_IR = 0 ;
2019-09-15 22:43:37 +00:00
# endif //IR_SENSOR_ANALOG
2017-06-29 16:35:43 +00:00
int current_temperature_bed_raw = 0 ;
float current_temperature_bed = 0.0 ;
# ifdef PIDTEMP
float _Kp , _Ki , _Kd ;
int pid_cycle , pid_number_of_cycles ;
2022-06-03 10:55:30 +00:00
static bool pid_tuning_finished = true ;
bool pidTuningRunning ( ) {
return ! pid_tuning_finished ;
}
void preparePidTuning ( ) {
// ensure heaters are disabled before we switch off PID management!
disable_heater ( ) ;
pid_tuning_finished = false ;
}
2017-06-29 16:35:43 +00:00
# endif //PIDTEMP
unsigned char soft_pwm_bed ;
2017-08-21 15:23:30 +00:00
2017-06-29 16:35:43 +00:00
# ifdef BABYSTEPPING
volatile int babystepsTodo [ 3 ] = { 0 , 0 , 0 } ;
# endif
//===========================================================================
//=============================private variables============================
//===========================================================================
static volatile bool temp_meas_ready = false ;
# ifdef PIDTEMP
//static cannot be external:
2019-01-17 01:57:08 +00:00
static float iState_sum [ EXTRUDERS ] = { 0 } ;
static float dState_last [ EXTRUDERS ] = { 0 } ;
2017-06-29 16:35:43 +00:00
static float pTerm [ EXTRUDERS ] ;
static float iTerm [ EXTRUDERS ] ;
static float dTerm [ EXTRUDERS ] ;
static float pid_error [ EXTRUDERS ] ;
2019-01-17 01:57:08 +00:00
static float iState_sum_min [ EXTRUDERS ] ;
static float iState_sum_max [ EXTRUDERS ] ;
2017-06-29 16:35:43 +00:00
static bool pid_reset [ EXTRUDERS ] ;
# endif //PIDTEMP
# ifdef PIDTEMPBED
//static cannot be external:
static float temp_iState_bed = { 0 } ;
static float temp_dState_bed = { 0 } ;
static float pTerm_bed ;
static float iTerm_bed ;
static float dTerm_bed ;
static float pid_error_bed ;
static float temp_iState_min_bed ;
static float temp_iState_max_bed ;
# else //PIDTEMPBED
static unsigned long previous_millis_bed_heater ;
# endif //PIDTEMPBED
static unsigned char soft_pwm [ EXTRUDERS ] ;
# ifdef FAN_SOFT_PWM
2022-05-14 13:07:58 +00:00
unsigned char fanSpeedSoftPwm ;
2017-06-29 16:35:43 +00:00
static unsigned char soft_pwm_fan ;
# endif
2020-06-01 15:58:15 +00:00
uint8_t fanSpeedBckp = 255 ;
2017-06-29 16:35:43 +00:00
# if EXTRUDERS > 3
# error Unsupported number of extruders
# elif EXTRUDERS > 2
# define ARRAY_BY_EXTRUDERS(v1, v2, v3) { v1, v2, v3 }
# elif EXTRUDERS > 1
# define ARRAY_BY_EXTRUDERS(v1, v2, v3) { v1, v2 }
# else
# define ARRAY_BY_EXTRUDERS(v1, v2, v3) { v1 }
# endif
// Init min and max temp with extreme values to prevent false errors during startup
static int minttemp_raw [ EXTRUDERS ] = ARRAY_BY_EXTRUDERS ( HEATER_0_RAW_LO_TEMP , HEATER_1_RAW_LO_TEMP , HEATER_2_RAW_LO_TEMP ) ;
static int maxttemp_raw [ EXTRUDERS ] = ARRAY_BY_EXTRUDERS ( HEATER_0_RAW_HI_TEMP , HEATER_1_RAW_HI_TEMP , HEATER_2_RAW_HI_TEMP ) ;
static int minttemp [ EXTRUDERS ] = ARRAY_BY_EXTRUDERS ( 0 , 0 , 0 ) ;
static int maxttemp [ EXTRUDERS ] = ARRAY_BY_EXTRUDERS ( 16383 , 16383 , 16383 ) ;
# ifdef BED_MINTEMP
static int bed_minttemp_raw = HEATER_BED_RAW_LO_TEMP ;
# endif
# ifdef BED_MAXTEMP
static int bed_maxttemp_raw = HEATER_BED_RAW_HI_TEMP ;
# endif
2020-06-08 01:14:49 +00:00
# ifdef AMBIENT_MINTEMP
static int ambient_minttemp_raw = AMBIENT_RAW_LO_TEMP ;
# endif
# ifdef AMBIENT_MAXTEMP
static int ambient_maxttemp_raw = AMBIENT_RAW_HI_TEMP ;
# endif
2017-06-29 16:35:43 +00:00
2019-08-27 18:05:10 +00:00
static void * heater_ttbl_map [ EXTRUDERS ] = ARRAY_BY_EXTRUDERS ( ( void * ) HEATER_0_TEMPTABLE , ( void * ) HEATER_1_TEMPTABLE , ( void * ) HEATER_2_TEMPTABLE ) ;
static uint8_t heater_ttbllen_map [ EXTRUDERS ] = ARRAY_BY_EXTRUDERS ( HEATER_0_TEMPTABLE_LEN , HEATER_1_TEMPTABLE_LEN , HEATER_2_TEMPTABLE_LEN ) ;
2017-06-29 16:35:43 +00:00
static float analog2temp ( int raw , uint8_t e ) ;
static float analog2tempBed ( int raw ) ;
2021-02-03 18:16:41 +00:00
# ifdef AMBIENT_MAXTEMP
2017-09-06 14:04:50 +00:00
static float analog2tempAmbient ( int raw ) ;
2021-02-03 18:16:41 +00:00
# endif
2022-05-24 16:37:07 +00:00
static void updateTemperatures ( ) ;
2017-06-29 16:35:43 +00:00
2021-08-06 15:03:35 +00:00
enum TempRunawayStates : uint8_t
2017-06-29 16:35:43 +00:00
{
TempRunaway_INACTIVE = 0 ,
TempRunaway_PREHEAT = 1 ,
TempRunaway_ACTIVE = 2 ,
} ;
# ifndef SOFT_PWM_SCALE
# define SOFT_PWM_SCALE 0
# endif
//===========================================================================
//============================= functions ============================
//===========================================================================
2018-07-23 11:35:38 +00:00
# if (defined (TEMP_RUNAWAY_BED_HYSTERESIS) && TEMP_RUNAWAY_BED_TIMEOUT > 0) || (defined (TEMP_RUNAWAY_EXTRUDER_HYSTERESIS) && TEMP_RUNAWAY_EXTRUDER_TIMEOUT > 0)
2022-01-30 12:16:05 +00:00
static uint8_t temp_runaway_status [ 1 + EXTRUDERS ] ;
2021-05-30 10:26:07 +00:00
static float temp_runaway_target [ 1 + EXTRUDERS ] ;
2022-01-30 12:16:05 +00:00
static uint32_t temp_runaway_timer [ 1 + EXTRUDERS ] ;
static uint16_t temp_runaway_error_counter [ 1 + EXTRUDERS ] ;
2018-07-23 11:35:38 +00:00
2021-08-08 19:53:37 +00:00
static void temp_runaway_check ( uint8_t _heater_id , float _target_temperature , float _current_temperature , float _output , bool _isbed ) ;
2018-07-23 11:35:38 +00:00
static void temp_runaway_stop ( bool isPreheat , bool isBed ) ;
# endif
2020-03-03 13:57:45 +00:00
// return "false", if all extruder-heaters are 'off' (ie. "true", if any heater is 'on')
bool checkAllHotends ( void )
{
bool result = false ;
for ( int i = 0 ; i < EXTRUDERS ; i + + ) result = ( result | | ( target_temperature [ i ] ! = 0 ) ) ;
return ( result ) ;
}
2021-06-23 19:52:25 +00:00
// WARNING: the following function has been marked noinline to avoid a GCC 4.9.2 LTO
// codegen bug causing a stack overwrite issue in process_commands()
void __attribute__ ( ( noinline ) ) PID_autotune ( float temp , int extruder , int ncycles )
{
2022-06-03 10:55:30 +00:00
preparePidTuning ( ) ;
2017-06-29 16:35:43 +00:00
pid_number_of_cycles = ncycles ;
float input = 0.0 ;
pid_cycle = 0 ;
bool heating = true ;
2019-01-27 21:48:51 +00:00
unsigned long temp_millis = _millis ( ) ;
2017-06-29 16:35:43 +00:00
unsigned long t1 = temp_millis ;
unsigned long t2 = temp_millis ;
long t_high = 0 ;
long t_low = 0 ;
long bias , d ;
float Ku , Tu ;
float max = 0 , min = 10000 ;
2017-11-21 14:11:15 +00:00
uint8_t safety_check_cycles = 0 ;
const uint8_t safety_check_cycles_count = ( extruder < 0 ) ? 45 : 10 ; //10 cycles / 20s delay for extruder and 45 cycles / 90s for heatbed
float temp_ambient ;
2017-06-29 16:35:43 +00:00
2020-06-01 15:58:15 +00:00
# if (defined(EXTRUDER_0_AUTO_FAN_PIN) && EXTRUDER_0_AUTO_FAN_PIN > -1)
2019-01-27 21:48:51 +00:00
unsigned long extruder_autofan_last_check = _millis ( ) ;
2017-06-29 16:35:43 +00:00
# endif
if ( ( extruder > = EXTRUDERS )
# if (TEMP_BED_PIN <= -1)
| | ( extruder < 0 )
# endif
) {
2022-06-02 19:06:32 +00:00
SERIAL_ECHOLNPGM ( " PID Autotune failed. Bad extruder number. " ) ;
2017-06-29 16:35:43 +00:00
pid_tuning_finished = true ;
pid_cycle = 0 ;
return ;
}
2022-06-02 19:06:32 +00:00
SERIAL_ECHOLNPGM ( " PID Autotune start " ) ;
2017-06-29 16:35:43 +00:00
if ( extruder < 0 )
{
soft_pwm_bed = ( MAX_BED_POWER ) / 2 ;
2019-01-28 13:20:31 +00:00
timer02_set_pwm0 ( soft_pwm_bed < < 1 ) ;
2017-06-29 16:35:43 +00:00
bias = d = ( MAX_BED_POWER ) / 2 ;
2019-05-07 14:15:42 +00:00
target_temperature_bed = ( int ) temp ; // to display the requested target bed temperature properly on the main screen
2017-06-29 16:35:43 +00:00
}
else
{
soft_pwm [ extruder ] = ( PID_MAX ) / 2 ;
bias = d = ( PID_MAX ) / 2 ;
2019-05-07 14:15:42 +00:00
target_temperature [ extruder ] = ( int ) temp ; // to display the requested target extruder temperature properly on the main screen
2017-06-29 16:35:43 +00:00
}
2022-05-14 22:45:18 +00:00
for ( ; ; ) {
2018-03-07 13:13:34 +00:00
# ifdef WATCHDOG
wdt_reset ( ) ;
# endif //WATCHDOG
2017-06-29 16:35:43 +00:00
if ( temp_meas_ready = = true ) { // temp sample ready
2022-05-24 16:37:07 +00:00
updateTemperatures ( ) ;
2017-06-29 16:35:43 +00:00
input = ( extruder < 0 ) ? current_temperature_bed : current_temperature [ extruder ] ;
max = max ( max , input ) ;
min = min ( min , input ) ;
2020-06-01 15:58:15 +00:00
# if (defined(EXTRUDER_0_AUTO_FAN_PIN) && EXTRUDER_0_AUTO_FAN_PIN > -1)
2019-01-27 21:48:51 +00:00
if ( _millis ( ) - extruder_autofan_last_check > 2500 ) {
2017-06-29 16:35:43 +00:00
checkExtruderAutoFans ( ) ;
2019-01-27 21:48:51 +00:00
extruder_autofan_last_check = _millis ( ) ;
2017-06-29 16:35:43 +00:00
}
# endif
if ( heating = = true & & input > temp ) {
2019-01-27 21:48:51 +00:00
if ( _millis ( ) - t2 > 5000 ) {
2017-06-29 16:35:43 +00:00
heating = false ;
if ( extruder < 0 )
2019-01-28 13:20:31 +00:00
{
2017-06-29 16:35:43 +00:00
soft_pwm_bed = ( bias - d ) > > 1 ;
2019-01-28 13:20:31 +00:00
timer02_set_pwm0 ( soft_pwm_bed < < 1 ) ;
}
2017-06-29 16:35:43 +00:00
else
soft_pwm [ extruder ] = ( bias - d ) > > 1 ;
2019-01-27 21:48:51 +00:00
t1 = _millis ( ) ;
2017-06-29 16:35:43 +00:00
t_high = t1 - t2 ;
max = temp ;
}
}
if ( heating = = false & & input < temp ) {
2019-01-27 21:48:51 +00:00
if ( _millis ( ) - t1 > 5000 ) {
2017-06-29 16:35:43 +00:00
heating = true ;
2019-01-27 21:48:51 +00:00
t2 = _millis ( ) ;
2017-06-29 16:35:43 +00:00
t_low = t2 - t1 ;
if ( pid_cycle > 0 ) {
bias + = ( d * ( t_high - t_low ) ) / ( t_low + t_high ) ;
bias = constrain ( bias , 20 , ( extruder < 0 ? ( MAX_BED_POWER ) : ( PID_MAX ) ) - 20 ) ;
if ( bias > ( extruder < 0 ? ( MAX_BED_POWER ) : ( PID_MAX ) ) / 2 ) d = ( extruder < 0 ? ( MAX_BED_POWER ) : ( PID_MAX ) ) - 1 - bias ;
else d = bias ;
SERIAL_PROTOCOLPGM ( " bias: " ) ; SERIAL_PROTOCOL ( bias ) ;
SERIAL_PROTOCOLPGM ( " d: " ) ; SERIAL_PROTOCOL ( d ) ;
SERIAL_PROTOCOLPGM ( " min: " ) ; SERIAL_PROTOCOL ( min ) ;
SERIAL_PROTOCOLPGM ( " max: " ) ; SERIAL_PROTOCOLLN ( max ) ;
if ( pid_cycle > 2 ) {
Ku = ( 4.0 * d ) / ( 3.14159 * ( max - min ) / 2.0 ) ;
Tu = ( ( float ) ( t_low + t_high ) / 1000.0 ) ;
SERIAL_PROTOCOLPGM ( " Ku: " ) ; SERIAL_PROTOCOL ( Ku ) ;
SERIAL_PROTOCOLPGM ( " Tu: " ) ; SERIAL_PROTOCOLLN ( Tu ) ;
_Kp = 0.6 * Ku ;
_Ki = 2 * _Kp / Tu ;
_Kd = _Kp * Tu / 8 ;
SERIAL_PROTOCOLLNPGM ( " Classic PID " ) ;
SERIAL_PROTOCOLPGM ( " Kp: " ) ; SERIAL_PROTOCOLLN ( _Kp ) ;
SERIAL_PROTOCOLPGM ( " Ki: " ) ; SERIAL_PROTOCOLLN ( _Ki ) ;
SERIAL_PROTOCOLPGM ( " Kd: " ) ; SERIAL_PROTOCOLLN ( _Kd ) ;
/*
_Kp = 0.33 * Ku ;
_Ki = _Kp / Tu ;
_Kd = _Kp * Tu / 3 ;
SERIAL_PROTOCOLLNPGM ( " Some overshoot " ) ;
SERIAL_PROTOCOLPGM ( " Kp: " ) ; SERIAL_PROTOCOLLN ( _Kp ) ;
SERIAL_PROTOCOLPGM ( " Ki: " ) ; SERIAL_PROTOCOLLN ( _Ki ) ;
SERIAL_PROTOCOLPGM ( " Kd: " ) ; SERIAL_PROTOCOLLN ( _Kd ) ;
_Kp = 0.2 * Ku ;
_Ki = 2 * _Kp / Tu ;
_Kd = _Kp * Tu / 3 ;
SERIAL_PROTOCOLLNPGM ( " No overshoot " ) ;
SERIAL_PROTOCOLPGM ( " Kp: " ) ; SERIAL_PROTOCOLLN ( _Kp ) ;
SERIAL_PROTOCOLPGM ( " Ki: " ) ; SERIAL_PROTOCOLLN ( _Ki ) ;
SERIAL_PROTOCOLPGM ( " Kd: " ) ; SERIAL_PROTOCOLLN ( _Kd ) ;
*/
}
}
if ( extruder < 0 )
2019-01-28 13:20:31 +00:00
{
2017-06-29 16:35:43 +00:00
soft_pwm_bed = ( bias + d ) > > 1 ;
2019-01-28 13:20:31 +00:00
timer02_set_pwm0 ( soft_pwm_bed < < 1 ) ;
}
2017-06-29 16:35:43 +00:00
else
soft_pwm [ extruder ] = ( bias + d ) > > 1 ;
pid_cycle + + ;
min = temp ;
}
}
}
if ( input > ( temp + 20 ) ) {
SERIAL_PROTOCOLLNPGM ( " PID Autotune failed! Temperature too high " ) ;
pid_tuning_finished = true ;
pid_cycle = 0 ;
return ;
}
2019-01-27 21:48:51 +00:00
if ( _millis ( ) - temp_millis > 2000 ) {
2017-06-29 16:35:43 +00:00
int p ;
if ( extruder < 0 ) {
p = soft_pwm_bed ;
2018-01-25 14:23:11 +00:00
SERIAL_PROTOCOLPGM ( " B: " ) ;
2017-06-29 16:35:43 +00:00
} else {
p = soft_pwm [ extruder ] ;
2018-01-25 14:23:11 +00:00
SERIAL_PROTOCOLPGM ( " T: " ) ;
2017-06-29 16:35:43 +00:00
}
SERIAL_PROTOCOL ( input ) ;
SERIAL_PROTOCOLPGM ( " @: " ) ;
SERIAL_PROTOCOLLN ( p ) ;
2017-11-21 14:11:15 +00:00
if ( safety_check_cycles = = 0 ) { //save ambient temp
temp_ambient = input ;
//SERIAL_ECHOPGM("Ambient T: ");
//MYSERIAL.println(temp_ambient);
safety_check_cycles + + ;
}
else if ( safety_check_cycles < safety_check_cycles_count ) { //delay
safety_check_cycles + + ;
}
else if ( safety_check_cycles = = safety_check_cycles_count ) { //check that temperature is rising
safety_check_cycles + + ;
//SERIAL_ECHOPGM("Time from beginning: ");
//MYSERIAL.print(safety_check_cycles_count * 2);
//SERIAL_ECHOPGM("s. Difference between current and ambient T: ");
//MYSERIAL.println(input - temp_ambient);
2021-07-02 17:07:01 +00:00
if ( fabs ( input - temp_ambient ) < 5.0 ) {
2017-11-21 14:11:15 +00:00
temp_runaway_stop ( false , ( extruder < 0 ) ) ;
pid_tuning_finished = true ;
return ;
}
}
2019-01-27 21:48:51 +00:00
temp_millis = _millis ( ) ;
2017-06-29 16:35:43 +00:00
}
2019-01-27 21:48:51 +00:00
if ( ( ( _millis ( ) - t1 ) + ( _millis ( ) - t2 ) ) > ( 10L * 60L * 1000L * 2L ) ) {
2017-06-29 16:35:43 +00:00
SERIAL_PROTOCOLLNPGM ( " PID Autotune failed! timeout " ) ;
pid_tuning_finished = true ;
pid_cycle = 0 ;
return ;
}
if ( pid_cycle > ncycles ) {
SERIAL_PROTOCOLLNPGM ( " PID Autotune finished! Put the last Kp, Ki and Kd constants from above into Configuration.h " ) ;
pid_tuning_finished = true ;
pid_cycle = 0 ;
return ;
}
2018-07-16 00:13:52 +00:00
lcd_update ( 0 ) ;
2017-06-29 16:35:43 +00:00
}
}
void updatePID ( )
{
2022-05-24 10:57:35 +00:00
// TODO: iState_sum_max and PID values should be synchronized for temp_mgr_isr
2017-06-29 16:35:43 +00:00
# ifdef PIDTEMP
2019-06-12 13:01:57 +00:00
for ( uint_least8_t e = 0 ; e < EXTRUDERS ; e + + ) {
2019-01-17 01:57:08 +00:00
iState_sum_max [ e ] = PID_INTEGRAL_DRIVE_MAX / cs . Ki ;
2017-06-29 16:35:43 +00:00
}
# endif
# ifdef PIDTEMPBED
2018-09-24 14:57:48 +00:00
temp_iState_max_bed = PID_INTEGRAL_DRIVE_MAX / cs . bedKi ;
2017-06-29 16:35:43 +00:00
# endif
}
int getHeaterPower ( int heater ) {
if ( heater < 0 )
return soft_pwm_bed ;
return soft_pwm [ heater ] ;
}
2022-05-14 23:13:44 +00:00
// reset PID state after changing target_temperature
void resetPID ( uint8_t extruder _UNUSED ) { }
2019-01-17 01:57:08 +00:00
2022-05-24 22:40:36 +00:00
enum class TempErrorSource : uint8_t
{
hotend ,
bed ,
2022-07-04 21:34:23 +00:00
# ifdef AMBIENT_THERMISTOR
2022-05-24 22:40:36 +00:00
ambient ,
2022-07-04 21:34:23 +00:00
# endif
2022-05-24 22:40:36 +00:00
} ;
2022-06-09 09:43:46 +00:00
// thermal error type (in order of decreasing priority!)
2022-05-24 22:40:36 +00:00
enum class TempErrorType : uint8_t
{
max ,
2022-06-09 09:43:46 +00:00
min ,
2022-05-24 23:09:51 +00:00
preheat ,
2022-05-24 22:40:36 +00:00
runaway ,
2022-06-24 14:04:00 +00:00
# ifdef TEMP_MODEL
2022-05-26 17:22:17 +00:00
model ,
2022-06-24 14:04:00 +00:00
# endif
2022-05-24 22:40:36 +00:00
} ;
// 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
2022-05-24 23:09:51 +00:00
uint8_t index : 1 ; // source index
uint8_t type : 3 ; // error type
2022-05-24 22:40:36 +00:00
} ;
} 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 ( ) ;
2022-06-09 09:43:46 +00:00
// set the initial error source to the highest priority error
if ( ! temp_error_state . error | | ( uint8_t ) type < temp_error_state . type ) {
2022-06-09 09:08:54 +00:00
temp_error_state . source = ( uint8_t ) source ;
temp_error_state . index = index ;
temp_error_state . type = ( uint8_t ) type ;
}
// always set the error state
2022-05-24 22:40:36 +00:00
temp_error_state . error = true ;
temp_error_state . assert = true ;
}
void handle_temp_error ( ) ;
2017-06-29 16:35:43 +00:00
void manage_heater ( )
{
2018-03-07 13:13:34 +00:00
# ifdef WATCHDOG
wdt_reset ( ) ;
# endif //WATCHDOG
2022-05-24 16:37:07 +00:00
2022-05-25 17:44:41 +00:00
// limit execution to the same rate as temp_mgr (low-level fault handling is already handled -
// any remaining error handling is just user-facing and can wait one extra cycle)
if ( ! temp_meas_ready )
return ;
2022-05-24 16:37:07 +00:00
// syncronize temperatures with isr
2022-05-25 17:44:41 +00:00
updateTemperatures ( ) ;
2022-05-24 16:37:07 +00:00
2022-06-24 14:04:00 +00:00
# ifdef TEMP_MODEL
// handle model warnings first, so not to override the error handler
if ( temp_model : : warning_state . warning )
temp_model : : handle_warning ( ) ;
# endif
2022-05-24 22:40:36 +00:00
// handle temperature errors
if ( temp_error_state . v )
handle_temp_error ( ) ;
2022-05-24 16:37:07 +00:00
// periodically check fans
2022-05-15 21:35:46 +00:00
checkFans ( ) ;
2022-06-14 10:11:58 +00:00
2022-06-24 14:04:00 +00:00
# ifdef TEMP_MODEL_DEBUG
temp_model : : log_usr ( ) ;
2022-06-14 10:11:58 +00:00
# endif
2017-06-29 16:35:43 +00:00
}
# define PGM_RD_W(x) (short)pgm_read_word(&x)
// Derived from RepRap FiveD extruder::getTemperature()
// For hot end temperature measurement.
static float analog2temp ( int raw , uint8_t e ) {
if ( e > = EXTRUDERS )
{
SERIAL_ERROR_START ;
SERIAL_ERROR ( ( int ) e ) ;
SERIAL_ERRORLNPGM ( " - Invalid extruder number ! " ) ;
2019-11-29 20:49:22 +00:00
kill ( NULL , 6 ) ;
2017-06-29 16:35:43 +00:00
return 0.0 ;
}
# ifdef HEATER_0_USES_MAX6675
if ( e = = 0 )
{
return 0.25 * raw ;
}
# endif
if ( heater_ttbl_map [ e ] ! = NULL )
{
float celsius = 0 ;
uint8_t i ;
short ( * tt ) [ ] [ 2 ] = ( short ( * ) [ ] [ 2 ] ) ( heater_ttbl_map [ e ] ) ;
for ( i = 1 ; i < heater_ttbllen_map [ e ] ; i + + )
{
if ( PGM_RD_W ( ( * tt ) [ i ] [ 0 ] ) > raw )
{
celsius = PGM_RD_W ( ( * tt ) [ i - 1 ] [ 1 ] ) +
( raw - PGM_RD_W ( ( * tt ) [ i - 1 ] [ 0 ] ) ) *
( float ) ( PGM_RD_W ( ( * tt ) [ i ] [ 1 ] ) - PGM_RD_W ( ( * tt ) [ i - 1 ] [ 1 ] ) ) /
( float ) ( PGM_RD_W ( ( * tt ) [ i ] [ 0 ] ) - PGM_RD_W ( ( * tt ) [ i - 1 ] [ 0 ] ) ) ;
break ;
}
}
// Overflow: Set to last value in the table
if ( i = = heater_ttbllen_map [ e ] ) celsius = PGM_RD_W ( ( * tt ) [ i - 1 ] [ 1 ] ) ;
return celsius ;
}
return ( ( raw * ( ( 5.0 * 100.0 ) / 1024.0 ) / OVERSAMPLENR ) * TEMP_SENSOR_AD595_GAIN ) + TEMP_SENSOR_AD595_OFFSET ;
}
// Derived from RepRap FiveD extruder::getTemperature()
// For bed temperature measurement.
static float analog2tempBed ( int raw ) {
# ifdef BED_USES_THERMISTOR
float celsius = 0 ;
byte i ;
for ( i = 1 ; i < BEDTEMPTABLE_LEN ; i + + )
{
if ( PGM_RD_W ( BEDTEMPTABLE [ i ] [ 0 ] ) > raw )
{
celsius = PGM_RD_W ( BEDTEMPTABLE [ i - 1 ] [ 1 ] ) +
( raw - PGM_RD_W ( BEDTEMPTABLE [ i - 1 ] [ 0 ] ) ) *
( float ) ( PGM_RD_W ( BEDTEMPTABLE [ i ] [ 1 ] ) - PGM_RD_W ( BEDTEMPTABLE [ i - 1 ] [ 1 ] ) ) /
( float ) ( PGM_RD_W ( BEDTEMPTABLE [ i ] [ 0 ] ) - PGM_RD_W ( BEDTEMPTABLE [ i - 1 ] [ 0 ] ) ) ;
break ;
}
}
// Overflow: Set to last value in the table
if ( i = = BEDTEMPTABLE_LEN ) celsius = PGM_RD_W ( BEDTEMPTABLE [ i - 1 ] [ 1 ] ) ;
// temperature offset adjustment
# ifdef BED_OFFSET
float _offset = BED_OFFSET ;
float _offset_center = BED_OFFSET_CENTER ;
float _offset_start = BED_OFFSET_START ;
float _first_koef = ( _offset / 2 ) / ( _offset_center - _offset_start ) ;
float _second_koef = ( _offset / 2 ) / ( 100 - _offset_center ) ;
if ( celsius > = _offset_start & & celsius < = _offset_center )
{
celsius = celsius + ( _first_koef * ( celsius - _offset_start ) ) ;
}
else if ( celsius > _offset_center & & celsius < = 100 )
{
celsius = celsius + ( _first_koef * ( _offset_center - _offset_start ) ) + ( _second_koef * ( celsius - ( 100 - _offset_center ) ) ) ;
}
else if ( celsius > 100 )
{
celsius = celsius + _offset ;
}
# endif
return celsius ;
# elif defined BED_USES_AD595
return ( ( raw * ( ( 5.0 * 100.0 ) / 1024.0 ) / OVERSAMPLENR ) * TEMP_SENSOR_AD595_GAIN ) + TEMP_SENSOR_AD595_OFFSET ;
# else
return 0 ;
# endif
}
2018-02-01 19:08:11 +00:00
# ifdef AMBIENT_THERMISTOR
2017-09-06 14:04:50 +00:00
static float analog2tempAmbient ( int raw )
{
float celsius = 0 ;
byte i ;
for ( i = 1 ; i < AMBIENTTEMPTABLE_LEN ; i + + )
{
if ( PGM_RD_W ( AMBIENTTEMPTABLE [ i ] [ 0 ] ) > raw )
{
celsius = PGM_RD_W ( AMBIENTTEMPTABLE [ i - 1 ] [ 1 ] ) +
( raw - PGM_RD_W ( AMBIENTTEMPTABLE [ i - 1 ] [ 0 ] ) ) *
( float ) ( PGM_RD_W ( AMBIENTTEMPTABLE [ i ] [ 1 ] ) - PGM_RD_W ( AMBIENTTEMPTABLE [ i - 1 ] [ 1 ] ) ) /
( float ) ( PGM_RD_W ( AMBIENTTEMPTABLE [ i ] [ 0 ] ) - PGM_RD_W ( AMBIENTTEMPTABLE [ i - 1 ] [ 0 ] ) ) ;
break ;
}
}
// Overflow: Set to last value in the table
if ( i = = AMBIENTTEMPTABLE_LEN ) celsius = PGM_RD_W ( AMBIENTTEMPTABLE [ i - 1 ] [ 1 ] ) ;
return celsius ;
}
2018-02-01 19:08:11 +00:00
# endif //AMBIENT_THERMISTOR
2017-09-06 14:04:50 +00:00
2022-05-15 17:01:50 +00:00
void soft_pwm_init ( )
2017-06-29 16:35:43 +00:00
{
# if MB(RUMBA) && ((TEMP_SENSOR_0==-1)||(TEMP_SENSOR_1==-1)||(TEMP_SENSOR_2==-1)||(TEMP_SENSOR_BED==-1))
//disable RUMBA JTAG in case the thermocouple extension is plugged on top of JTAG connector
MCUCR = ( 1 < < JTD ) ;
MCUCR = ( 1 < < JTD ) ;
# endif
// Finish init of mult extruder arrays
for ( int e = 0 ; e < EXTRUDERS ; e + + ) {
// populate with the first value
maxttemp [ e ] = maxttemp [ 0 ] ;
# ifdef PIDTEMP
2019-01-17 01:57:08 +00:00
iState_sum_min [ e ] = 0.0 ;
iState_sum_max [ e ] = PID_INTEGRAL_DRIVE_MAX / cs . Ki ;
2017-06-29 16:35:43 +00:00
# endif //PIDTEMP
# ifdef PIDTEMPBED
temp_iState_min_bed = 0.0 ;
2018-09-24 14:57:48 +00:00
temp_iState_max_bed = PID_INTEGRAL_DRIVE_MAX / cs . bedKi ;
2017-06-29 16:35:43 +00:00
# endif //PIDTEMPBED
}
# if defined(HEATER_0_PIN) && (HEATER_0_PIN > -1)
SET_OUTPUT ( HEATER_0_PIN ) ;
# endif
# if defined(HEATER_1_PIN) && (HEATER_1_PIN > -1)
SET_OUTPUT ( HEATER_1_PIN ) ;
# endif
# if defined(HEATER_2_PIN) && (HEATER_2_PIN > -1)
SET_OUTPUT ( HEATER_2_PIN ) ;
# endif
# if defined(HEATER_BED_PIN) && (HEATER_BED_PIN > -1)
SET_OUTPUT ( HEATER_BED_PIN ) ;
# endif
# if defined(FAN_PIN) && (FAN_PIN > -1)
SET_OUTPUT ( FAN_PIN ) ;
# ifdef FAST_PWM_FAN
setPwmFrequency ( FAN_PIN , 1 ) ; // No prescaling. Pwm frequency = F_CPU/256/8
# endif
# ifdef FAN_SOFT_PWM
2018-12-06 14:18:07 +00:00
soft_pwm_fan = fanSpeedSoftPwm / ( 1 < < ( 8 - FAN_SOFT_PWM_BITS ) ) ;
2017-08-21 15:23:30 +00:00
# endif
# endif
2017-06-29 16:35:43 +00:00
# ifdef HEATER_0_USES_MAX6675
# ifndef SDSUPPORT
SET_OUTPUT ( SCK_PIN ) ;
WRITE ( SCK_PIN , 0 ) ;
SET_OUTPUT ( MOSI_PIN ) ;
WRITE ( MOSI_PIN , 1 ) ;
SET_INPUT ( MISO_PIN ) ;
WRITE ( MISO_PIN , 1 ) ;
# endif
/* Using pinMode and digitalWrite, as that was the only way I could get it to compile */
//Have to toggle SD card CS pin to low first, to enable firmware to talk with SD card
pinMode ( SS_PIN , OUTPUT ) ;
digitalWrite ( SS_PIN , 0 ) ;
pinMode ( MAX6675_SS , OUTPUT ) ;
digitalWrite ( MAX6675_SS , 1 ) ;
# endif
# ifdef HEATER_0_MINTEMP
minttemp [ 0 ] = HEATER_0_MINTEMP ;
while ( analog2temp ( minttemp_raw [ 0 ] , 0 ) < HEATER_0_MINTEMP ) {
# if HEATER_0_RAW_LO_TEMP < HEATER_0_RAW_HI_TEMP
minttemp_raw [ 0 ] + = OVERSAMPLENR ;
# else
minttemp_raw [ 0 ] - = OVERSAMPLENR ;
# endif
}
# endif //MINTEMP
# ifdef HEATER_0_MAXTEMP
maxttemp [ 0 ] = HEATER_0_MAXTEMP ;
while ( analog2temp ( maxttemp_raw [ 0 ] , 0 ) > HEATER_0_MAXTEMP ) {
# if HEATER_0_RAW_LO_TEMP < HEATER_0_RAW_HI_TEMP
maxttemp_raw [ 0 ] - = OVERSAMPLENR ;
# else
maxttemp_raw [ 0 ] + = OVERSAMPLENR ;
# endif
}
# endif //MAXTEMP
# if (EXTRUDERS > 1) && defined(HEATER_1_MINTEMP)
minttemp [ 1 ] = HEATER_1_MINTEMP ;
while ( analog2temp ( minttemp_raw [ 1 ] , 1 ) < HEATER_1_MINTEMP ) {
# if HEATER_1_RAW_LO_TEMP < HEATER_1_RAW_HI_TEMP
minttemp_raw [ 1 ] + = OVERSAMPLENR ;
# else
minttemp_raw [ 1 ] - = OVERSAMPLENR ;
# endif
}
# endif // MINTEMP 1
# if (EXTRUDERS > 1) && defined(HEATER_1_MAXTEMP)
maxttemp [ 1 ] = HEATER_1_MAXTEMP ;
while ( analog2temp ( maxttemp_raw [ 1 ] , 1 ) > HEATER_1_MAXTEMP ) {
# if HEATER_1_RAW_LO_TEMP < HEATER_1_RAW_HI_TEMP
maxttemp_raw [ 1 ] - = OVERSAMPLENR ;
# else
maxttemp_raw [ 1 ] + = OVERSAMPLENR ;
# endif
}
# endif //MAXTEMP 1
# if (EXTRUDERS > 2) && defined(HEATER_2_MINTEMP)
minttemp [ 2 ] = HEATER_2_MINTEMP ;
while ( analog2temp ( minttemp_raw [ 2 ] , 2 ) < HEATER_2_MINTEMP ) {
# if HEATER_2_RAW_LO_TEMP < HEATER_2_RAW_HI_TEMP
minttemp_raw [ 2 ] + = OVERSAMPLENR ;
# else
minttemp_raw [ 2 ] - = OVERSAMPLENR ;
# endif
}
# endif //MINTEMP 2
# if (EXTRUDERS > 2) && defined(HEATER_2_MAXTEMP)
maxttemp [ 2 ] = HEATER_2_MAXTEMP ;
while ( analog2temp ( maxttemp_raw [ 2 ] , 2 ) > HEATER_2_MAXTEMP ) {
# if HEATER_2_RAW_LO_TEMP < HEATER_2_RAW_HI_TEMP
maxttemp_raw [ 2 ] - = OVERSAMPLENR ;
# else
maxttemp_raw [ 2 ] + = OVERSAMPLENR ;
# endif
}
# endif //MAXTEMP 2
# ifdef BED_MINTEMP
while ( analog2tempBed ( bed_minttemp_raw ) < BED_MINTEMP ) {
# if HEATER_BED_RAW_LO_TEMP < HEATER_BED_RAW_HI_TEMP
bed_minttemp_raw + = OVERSAMPLENR ;
# else
bed_minttemp_raw - = OVERSAMPLENR ;
# endif
}
# endif //BED_MINTEMP
# ifdef BED_MAXTEMP
while ( analog2tempBed ( bed_maxttemp_raw ) > BED_MAXTEMP ) {
# if HEATER_BED_RAW_LO_TEMP < HEATER_BED_RAW_HI_TEMP
bed_maxttemp_raw - = OVERSAMPLENR ;
# else
bed_maxttemp_raw + = OVERSAMPLENR ;
# endif
}
# endif //BED_MAXTEMP
2020-06-08 01:14:49 +00:00
# ifdef AMBIENT_MINTEMP
while ( analog2tempAmbient ( ambient_minttemp_raw ) < AMBIENT_MINTEMP ) {
2022-04-29 14:50:27 +00:00
# if AMBIENT_RAW_LO_TEMP < AMBIENT_RAW_HI_TEMP
2020-06-08 01:14:49 +00:00
ambient_minttemp_raw + = OVERSAMPLENR ;
# else
ambient_minttemp_raw - = OVERSAMPLENR ;
# endif
}
# endif //AMBIENT_MINTEMP
# ifdef AMBIENT_MAXTEMP
while ( analog2tempAmbient ( ambient_maxttemp_raw ) > AMBIENT_MAXTEMP ) {
2022-04-29 14:50:27 +00:00
# if AMBIENT_RAW_LO_TEMP < AMBIENT_RAW_HI_TEMP
2020-06-08 01:14:49 +00:00
ambient_maxttemp_raw - = OVERSAMPLENR ;
# else
ambient_maxttemp_raw + = OVERSAMPLENR ;
# endif
}
# endif //AMBIENT_MAXTEMP
2022-06-08 22:10:30 +00:00
timer0_init ( ) ; //enables the heatbed timer.
// timer2 already enabled earlier in the code
// now enable the COMPB temperature interrupt
OCR2B = 128 ;
ENABLE_SOFT_PWM_INTERRUPT ( ) ;
timer4_init ( ) ; //for tone and Extruder fan PWM
2017-06-29 16:35:43 +00:00
}
# if (defined (TEMP_RUNAWAY_BED_HYSTERESIS) && TEMP_RUNAWAY_BED_TIMEOUT > 0) || (defined (TEMP_RUNAWAY_EXTRUDER_HYSTERESIS) && TEMP_RUNAWAY_EXTRUDER_TIMEOUT > 0)
2022-06-28 15:07:12 +00:00
static void temp_runaway_check ( uint8_t _heater_id , float _target_temperature , float _current_temperature , float _output , bool _isbed )
2017-06-29 16:35:43 +00:00
{
2022-05-24 23:09:51 +00:00
float __delta ;
2017-06-29 16:35:43 +00:00
float __hysteresis = 0 ;
2021-08-06 15:08:46 +00:00
uint16_t __timeout = 0 ;
2017-06-29 16:35:43 +00:00
bool temp_runaway_check_active = false ;
static float __preheat_start [ 2 ] = { 0 , 0 } ; //currently just bed and one extruder
2021-08-08 19:45:29 +00:00
static uint8_t __preheat_counter [ 2 ] = { 0 , 0 } ;
static uint8_t __preheat_errors [ 2 ] = { 0 , 0 } ;
2017-06-29 16:35:43 +00:00
2019-01-27 21:48:51 +00:00
if ( _millis ( ) - temp_runaway_timer [ _heater_id ] > 2000 )
2017-06-29 16:35:43 +00:00
{
2018-09-27 02:23:00 +00:00
# ifdef TEMP_RUNAWAY_BED_TIMEOUT
if ( _isbed )
{
__hysteresis = TEMP_RUNAWAY_BED_HYSTERESIS ;
__timeout = TEMP_RUNAWAY_BED_TIMEOUT ;
}
2017-06-29 16:35:43 +00:00
# endif
# ifdef TEMP_RUNAWAY_EXTRUDER_TIMEOUT
2018-09-27 02:23:00 +00:00
if ( ! _isbed )
{
__hysteresis = TEMP_RUNAWAY_EXTRUDER_HYSTERESIS ;
__timeout = TEMP_RUNAWAY_EXTRUDER_TIMEOUT ;
}
2017-06-29 16:35:43 +00:00
# endif
2019-01-27 21:48:51 +00:00
temp_runaway_timer [ _heater_id ] = _millis ( ) ;
2017-06-29 16:35:43 +00:00
if ( _output = = 0 )
{
temp_runaway_check_active = false ;
temp_runaway_error_counter [ _heater_id ] = 0 ;
}
if ( temp_runaway_target [ _heater_id ] ! = _target_temperature )
{
if ( _target_temperature > 0 )
{
temp_runaway_status [ _heater_id ] = TempRunaway_PREHEAT ;
temp_runaway_target [ _heater_id ] = _target_temperature ;
__preheat_start [ _heater_id ] = _current_temperature ;
__preheat_counter [ _heater_id ] = 0 ;
}
else
{
temp_runaway_status [ _heater_id ] = TempRunaway_INACTIVE ;
temp_runaway_target [ _heater_id ] = _target_temperature ;
}
}
2018-09-27 02:23:00 +00:00
if ( ( _current_temperature < _target_temperature ) & & ( temp_runaway_status [ _heater_id ] = = TempRunaway_PREHEAT ) )
2017-06-29 16:35:43 +00:00
{
2018-09-27 02:23:00 +00:00
__preheat_counter [ _heater_id ] + + ;
if ( __preheat_counter [ _heater_id ] > ( ( _isbed ) ? 16 : 8 ) ) // periodicaly check if current temperature changes
2017-06-29 16:35:43 +00:00
{
2018-09-27 02:23:00 +00:00
/*SERIAL_ECHOPGM("Heater:");
MYSERIAL . print ( _heater_id ) ;
SERIAL_ECHOPGM ( " T: " ) ;
MYSERIAL . print ( _current_temperature ) ;
SERIAL_ECHOPGM ( " Tstart: " ) ;
2019-04-09 22:29:58 +00:00
MYSERIAL . print ( __preheat_start [ _heater_id ] ) ;
SERIAL_ECHOPGM ( " delta: " ) ;
MYSERIAL . print ( _current_temperature - __preheat_start [ _heater_id ] ) ; */
2018-09-27 02:23:00 +00:00
2019-04-09 22:29:58 +00:00
//-// if (_current_temperature - __preheat_start[_heater_id] < 2) {
//-// if (_current_temperature - __preheat_start[_heater_id] < ((_isbed && (_current_temperature>105.0))?0.6:2.0)) {
__delta = 2.0 ;
if ( _isbed )
{
__delta = 3.0 ;
if ( _current_temperature > 90.0 ) __delta = 2.0 ;
if ( _current_temperature > 105.0 ) __delta = 0.6 ;
}
if ( _current_temperature - __preheat_start [ _heater_id ] < __delta ) {
2018-09-27 02:23:00 +00:00
__preheat_errors [ _heater_id ] + + ;
/*SERIAL_ECHOPGM(" Preheat errors:");
MYSERIAL . println ( __preheat_errors [ _heater_id ] ) ; */
}
else {
//SERIAL_ECHOLNPGM("");
__preheat_errors [ _heater_id ] = 0 ;
}
2017-06-29 16:35:43 +00:00
2019-04-03 20:48:58 +00:00
if ( __preheat_errors [ _heater_id ] > ( ( _isbed ) ? 3 : 5 ) )
2022-05-24 23:09:51 +00:00
set_temp_error ( ( _isbed ? TempErrorSource : : bed : TempErrorSource : : hotend ) , _heater_id , TempErrorType : : preheat ) ;
2018-09-27 02:23:00 +00:00
__preheat_start [ _heater_id ] = _current_temperature ;
__preheat_counter [ _heater_id ] = 0 ;
2017-06-29 16:35:43 +00:00
}
}
2019-04-09 22:29:58 +00:00
//-// if (_current_temperature >= _target_temperature && temp_runaway_status[_heater_id] == TempRunaway_PREHEAT)
if ( ( _current_temperature > ( _target_temperature - __hysteresis ) ) & & temp_runaway_status [ _heater_id ] = = TempRunaway_PREHEAT )
2017-06-29 16:35:43 +00:00
{
2019-04-09 22:29:58 +00:00
/*SERIAL_ECHOPGM("Heater:");
MYSERIAL . print ( _heater_id ) ;
MYSERIAL . println ( " ->tempRunaway " ) ; */
2017-06-29 16:35:43 +00:00
temp_runaway_status [ _heater_id ] = TempRunaway_ACTIVE ;
temp_runaway_check_active = false ;
2019-04-09 22:29:58 +00:00
temp_runaway_error_counter [ _heater_id ] = 0 ;
2017-06-29 16:35:43 +00:00
}
2018-09-27 02:23:00 +00:00
if ( _output > 0 )
2017-06-29 16:35:43 +00:00
{
temp_runaway_check_active = true ;
}
if ( temp_runaway_check_active )
{
// we are in range
2018-09-27 02:23:00 +00:00
if ( ( _current_temperature > ( _target_temperature - __hysteresis ) ) & & ( _current_temperature < ( _target_temperature + __hysteresis ) ) )
2017-06-29 16:35:43 +00:00
{
temp_runaway_check_active = false ;
temp_runaway_error_counter [ _heater_id ] = 0 ;
}
else
{
if ( temp_runaway_status [ _heater_id ] > TempRunaway_PREHEAT )
{
temp_runaway_error_counter [ _heater_id ] + + ;
if ( temp_runaway_error_counter [ _heater_id ] * 2 > __timeout )
2022-05-24 23:09:51 +00:00
set_temp_error ( ( _isbed ? TempErrorSource : : bed : TempErrorSource : : hotend ) , _heater_id , TempErrorType : : runaway ) ;
2017-06-29 16:35:43 +00:00
}
}
}
}
}
2022-06-28 15:07:12 +00:00
static void temp_runaway_stop ( bool isPreheat , bool isBed )
2017-06-29 16:35:43 +00:00
{
2022-06-28 15:07:12 +00:00
if ( IsStopped ( ) = = false ) {
if ( isPreheat ) {
lcd_setalertstatuspgm ( isBed ? PSTR ( " BED PREHEAT ERROR " ) : PSTR ( " PREHEAT ERROR " ) , LCD_STATUS_CRITICAL ) ;
SERIAL_ERROR_START ;
if ( isBed ) {
SERIAL_ERRORLNPGM ( " THERMAL RUNAWAY (PREHEAT HEATBED) " ) ;
} else {
SERIAL_ERRORLNPGM ( " THERMAL RUNAWAY (PREHEAT HOTEND) " ) ;
}
} else {
lcd_setalertstatuspgm ( isBed ? PSTR ( " BED THERMAL RUNAWAY " ) : PSTR ( " THERMAL RUNAWAY " ) , LCD_STATUS_CRITICAL ) ;
SERIAL_ERROR_START ;
if ( isBed ) {
SERIAL_ERRORLNPGM ( " HEATBED THERMAL RUNAWAY " ) ;
} else {
SERIAL_ERRORLNPGM ( " HOTEND THERMAL RUNAWAY " ) ;
}
}
if ( farm_mode ) {
prusa_statistics ( 0 ) ;
prusa_statistics ( isPreheat ? 91 : 90 ) ;
}
2022-05-24 23:09:51 +00:00
}
2022-06-28 15:07:12 +00:00
ThermalStop ( ) ;
2017-06-29 16:35:43 +00:00
}
# endif
2019-05-24 06:46:44 +00:00
//! codes of alert messages for the LCD - it is shorter to compare an uin8_t
//! than raw const char * of the messages themselves.
//! Could be used for MAXTEMP situations too - after reaching MAXTEMP and turning off the heater automagically
//! the heater/bed may cool down and a similar alert message like "MAXTERM fixed..." may be displayed.
enum { LCDALERT_NONE = 0 , LCDALERT_HEATERMINTEMP , LCDALERT_BEDMINTEMP , LCDALERT_MINTEMPFIXED , LCDALERT_PLEASERESTART } ;
//! remember the last alert message sent to the LCD
//! to prevent flicker and improve speed
2022-06-28 15:07:12 +00:00
static uint8_t last_alert_sent_to_lcd = LCDALERT_NONE ;
2017-06-29 16:35:43 +00:00
2020-07-31 18:19:51 +00:00
//! update the current temperature error message
//! @param type short error abbreviation (PROGMEM)
2022-06-28 15:07:12 +00:00
static void temp_update_messagepgm ( const char * PROGMEM type )
2020-07-31 18:19:51 +00:00
{
char msg [ LCD_WIDTH ] ;
strcpy_P ( msg , PSTR ( " Err: " ) ) ;
strcat_P ( msg , type ) ;
2021-12-07 10:09:58 +00:00
lcd_setalertstatus ( msg , LCD_STATUS_CRITICAL ) ;
2020-07-31 18:19:51 +00:00
}
//! signal a temperature error on both the lcd and serial
//! @param type short error abbreviation (PROGMEM)
//! @param e optional extruder index for hotend errors
2022-06-28 15:07:12 +00:00
static void temp_error_messagepgm ( const char * PROGMEM type , uint8_t e = EXTRUDERS )
2020-07-31 18:19:51 +00:00
{
2021-12-07 10:09:58 +00:00
temp_update_messagepgm ( type ) ;
2020-07-31 18:19:51 +00:00
SERIAL_ERROR_START ;
if ( e ! = EXTRUDERS ) {
SERIAL_ERROR ( ( int ) e ) ;
SERIAL_ERRORPGM ( " : " ) ;
}
SERIAL_ERRORPGM ( " Heaters switched off. " ) ;
SERIAL_ERRORRPGM ( type ) ;
SERIAL_ERRORLNPGM ( " triggered! " ) ;
}
2022-06-28 15:07:12 +00:00
static void max_temp_error ( uint8_t e ) {
if ( IsStopped ( ) = = false ) {
temp_error_messagepgm ( PSTR ( " MAXTEMP " ) , e ) ;
if ( farm_mode ) prusa_statistics ( 93 ) ;
}
# ifndef BOGUS_TEMPERATURE_FAILSAFE_OVERRIDE
ThermalStop ( ) ;
# endif
2017-06-29 16:35:43 +00:00
}
2022-06-28 15:07:12 +00:00
static void min_temp_error ( uint8_t e ) {
static const char err [ ] PROGMEM = " MINTEMP " ;
if ( IsStopped ( ) = = false ) {
temp_error_messagepgm ( err , e ) ;
last_alert_sent_to_lcd = LCDALERT_HEATERMINTEMP ;
if ( farm_mode ) prusa_statistics ( 92 ) ;
} else if ( last_alert_sent_to_lcd ! = LCDALERT_HEATERMINTEMP ) { // only update, if the lcd message is to be changed (i.e. not the same as last time)
// we are already stopped due to some error, only update the status message without flickering
temp_update_messagepgm ( err ) ;
last_alert_sent_to_lcd = LCDALERT_HEATERMINTEMP ;
}
ThermalStop ( ) ;
2017-06-29 16:35:43 +00:00
}
2022-06-28 15:07:12 +00:00
static void bed_max_temp_error ( void ) {
if ( IsStopped ( ) = = false ) {
temp_error_messagepgm ( PSTR ( " MAXTEMP BED " ) ) ;
}
ThermalStop ( ) ;
2017-06-29 16:35:43 +00:00
}
2022-06-28 15:07:12 +00:00
static void bed_min_temp_error ( void ) {
2020-08-04 11:14:35 +00:00
static const char err [ ] PROGMEM = " MINTEMP BED " ;
2017-06-29 16:35:43 +00:00
if ( IsStopped ( ) = = false ) {
2020-07-31 18:19:51 +00:00
temp_error_messagepgm ( err ) ;
2019-05-24 06:46:44 +00:00
last_alert_sent_to_lcd = LCDALERT_BEDMINTEMP ;
} else if ( last_alert_sent_to_lcd ! = LCDALERT_BEDMINTEMP ) { // only update, if the lcd message is to be changed (i.e. not the same as last time)
// we are already stopped due to some error, only update the status message without flickering
2020-07-31 18:19:51 +00:00
temp_update_messagepgm ( err ) ;
2019-05-24 06:46:44 +00:00
last_alert_sent_to_lcd = LCDALERT_BEDMINTEMP ;
2017-06-29 16:35:43 +00:00
}
2022-06-28 15:07:12 +00:00
ThermalStop ( ) ;
2017-06-29 16:35:43 +00:00
}
2020-06-08 01:14:49 +00:00
# ifdef AMBIENT_THERMISTOR
2022-06-28 15:07:12 +00:00
static void ambient_max_temp_error ( void ) {
2020-06-08 01:14:49 +00:00
if ( IsStopped ( ) = = false ) {
2020-07-31 18:19:51 +00:00
temp_error_messagepgm ( PSTR ( " MAXTEMP AMB " ) ) ;
2020-06-08 01:14:49 +00:00
}
2022-06-28 15:07:12 +00:00
ThermalStop ( ) ;
2020-06-08 01:14:49 +00:00
}
2022-06-28 15:07:12 +00:00
static void ambient_min_temp_error ( void ) {
2020-06-08 01:14:49 +00:00
if ( IsStopped ( ) = = false ) {
2020-07-31 18:19:51 +00:00
temp_error_messagepgm ( PSTR ( " MINTEMP AMB " ) ) ;
2020-06-08 01:14:49 +00:00
}
2022-06-28 15:07:12 +00:00
ThermalStop ( ) ;
2020-06-08 01:14:49 +00:00
}
# endif
2017-06-29 16:35:43 +00:00
# ifdef HEATER_0_USES_MAX6675
# define MAX6675_HEAT_INTERVAL 250
long max6675_previous_millis = MAX6675_HEAT_INTERVAL ;
int max6675_temp = 2000 ;
int read_max6675 ( )
{
2019-01-27 21:48:51 +00:00
if ( _millis ( ) - max6675_previous_millis < MAX6675_HEAT_INTERVAL )
2017-06-29 16:35:43 +00:00
return max6675_temp ;
2019-01-27 21:48:51 +00:00
max6675_previous_millis = _millis ( ) ;
2017-06-29 16:35:43 +00:00
max6675_temp = 0 ;
# ifdef PRR
PRR & = ~ ( 1 < < PRSPI ) ;
# elif defined PRR0
PRR0 & = ~ ( 1 < < PRSPI ) ;
# endif
SPCR = ( 1 < < MSTR ) | ( 1 < < SPE ) | ( 1 < < SPR0 ) ;
// enable TT_MAX6675
WRITE ( MAX6675_SS , 0 ) ;
// ensure 100ns delay - a bit extra is fine
asm ( " nop " ) ; //50ns on 20Mhz, 62.5ns on 16Mhz
asm ( " nop " ) ; //50ns on 20Mhz, 62.5ns on 16Mhz
// read MSB
SPDR = 0 ;
for ( ; ( SPSR & ( 1 < < SPIF ) ) = = 0 ; ) ;
max6675_temp = SPDR ;
max6675_temp < < = 8 ;
// read LSB
SPDR = 0 ;
for ( ; ( SPSR & ( 1 < < SPIF ) ) = = 0 ; ) ;
max6675_temp | = SPDR ;
// disable TT_MAX6675
WRITE ( MAX6675_SS , 1 ) ;
if ( max6675_temp & 4 )
{
// thermocouple open
max6675_temp = 2000 ;
}
else
{
max6675_temp = max6675_temp > > 3 ;
}
return max6675_temp ;
}
# endif
2022-05-14 23:00:34 +00:00
# ifdef BABYSTEPPING
FORCE_INLINE static void applyBabysteps ( ) {
for ( uint8_t axis = 0 ; axis < 3 ; axis + + )
{
int curTodo = babystepsTodo [ axis ] ; //get rid of volatile for performance
if ( curTodo > 0 )
{
CRITICAL_SECTION_START ;
babystep ( axis , /*fwd*/ true ) ;
babystepsTodo [ axis ] - - ; //less to do next time
CRITICAL_SECTION_END ;
}
else
if ( curTodo < 0 )
{
CRITICAL_SECTION_START ;
babystep ( axis , /*fwd*/ false ) ;
babystepsTodo [ axis ] + + ; //less to do next time
CRITICAL_SECTION_END ;
}
}
}
# endif //BABYSTEPPING
2022-05-14 23:07:06 +00:00
FORCE_INLINE static void soft_pwm_core ( )
2017-06-29 16:35:43 +00:00
{
2019-07-08 14:42:21 +00:00
static uint8_t pwm_count = ( 1 < < SOFT_PWM_SCALE ) ;
static uint8_t soft_pwm_0 ;
2017-06-29 16:35:43 +00:00
# ifdef SLOW_PWM_HEATERS
static unsigned char slow_pwm_count = 0 ;
static unsigned char state_heater_0 = 0 ;
static unsigned char state_timer_heater_0 = 0 ;
# endif
# if (EXTRUDERS > 1) || defined(HEATERS_PARALLEL)
static unsigned char soft_pwm_1 ;
# ifdef SLOW_PWM_HEATERS
static unsigned char state_heater_1 = 0 ;
static unsigned char state_timer_heater_1 = 0 ;
# endif
# endif
# if EXTRUDERS > 2
static unsigned char soft_pwm_2 ;
# ifdef SLOW_PWM_HEATERS
static unsigned char state_heater_2 = 0 ;
static unsigned char state_timer_heater_2 = 0 ;
# endif
# endif
# if HEATER_BED_PIN > -1
2019-07-08 14:42:21 +00:00
// @@DR static unsigned char soft_pwm_b;
2017-06-29 16:35:43 +00:00
# ifdef SLOW_PWM_HEATERS
static unsigned char state_heater_b = 0 ;
static unsigned char state_timer_heater_b = 0 ;
# endif
# endif
# if defined(FILWIDTH_PIN) &&(FILWIDTH_PIN > -1)
static unsigned long raw_filwidth_value = 0 ; //added for filament width sensor
# endif
# ifndef SLOW_PWM_HEATERS
/*
* standard PWM modulation
*/
2017-08-21 15:23:30 +00:00
if ( pwm_count = = 0 )
{
2017-06-29 16:35:43 +00:00
soft_pwm_0 = soft_pwm [ 0 ] ;
2017-08-21 15:23:30 +00:00
if ( soft_pwm_0 > 0 )
{
2017-06-29 16:35:43 +00:00
WRITE ( HEATER_0_PIN , 1 ) ;
# ifdef HEATERS_PARALLEL
WRITE ( HEATER_1_PIN , 1 ) ;
# endif
} else WRITE ( HEATER_0_PIN , 0 ) ;
# if EXTRUDERS > 1
soft_pwm_1 = soft_pwm [ 1 ] ;
if ( soft_pwm_1 > 0 ) WRITE ( HEATER_1_PIN , 1 ) ; else WRITE ( HEATER_1_PIN , 0 ) ;
# endif
# if EXTRUDERS > 2
soft_pwm_2 = soft_pwm [ 2 ] ;
if ( soft_pwm_2 > 0 ) WRITE ( HEATER_2_PIN , 1 ) ; else WRITE ( HEATER_2_PIN , 0 ) ;
# endif
2019-05-06 16:07:42 +00:00
}
2017-06-29 16:35:43 +00:00
# if defined(HEATER_BED_PIN) && HEATER_BED_PIN > -1
2019-07-08 14:42:21 +00:00
#if 0 // @@DR vypnuto pro hw pwm bedu
// tuhle prasarnu bude potreba poustet ve stanovenych intervalech, jinak nemam moc sanci zareagovat
// teoreticky by se tato cast uz vubec nemusela poustet
2019-05-06 16:07:42 +00:00
if ( ( pwm_count & ( ( 1 < < HEATER_BED_SOFT_PWM_BITS ) - 1 ) ) = = 0 )
{
2019-05-07 13:35:34 +00:00
soft_pwm_b = soft_pwm_bed > > ( 7 - HEATER_BED_SOFT_PWM_BITS ) ;
2019-07-08 14:42:21 +00:00
# ifndef SYSTEM_TIMER_2
// tady budu krokovat pomalou frekvenci na automatu - tohle je rizeni spinani a rozepinani
// jako ridici frekvenci mam 2khz, jako vystupni frekvenci mam 30hz
// 2kHz jsou ovsem ve slysitelnem pasmu, mozna bude potreba jit s frekvenci nahoru (a tomu taky prizpusobit ostatni veci)
// Teoreticky bych mohl stahnout OCR0B citac na 6, cimz bych se dostal nekam ke 40khz a tady potom honit PWM rychleji nebo i pomaleji
// to nicemu nevadi. Soft PWM scale by se 20x zvetsilo (no dobre, 16x), cimz by se to posunulo k puvodnimu 30Hz PWM
//if(soft_pwm_b > 0) WRITE(HEATER_BED_PIN,1); else WRITE(HEATER_BED_PIN,0);
# endif //SYSTEM_TIMER_2
2018-12-06 14:18:07 +00:00
}
2019-05-06 16:07:42 +00:00
# endif
2019-07-08 14:42:21 +00:00
# endif
2017-06-29 16:35:43 +00:00
# ifdef FAN_SOFT_PWM
2018-12-06 14:18:07 +00:00
if ( ( pwm_count & ( ( 1 < < FAN_SOFT_PWM_BITS ) - 1 ) ) = = 0 )
{
soft_pwm_fan = fanSpeedSoftPwm / ( 1 < < ( 8 - FAN_SOFT_PWM_BITS ) ) ;
2017-06-29 16:35:43 +00:00
if ( soft_pwm_fan > 0 ) WRITE ( FAN_PIN , 1 ) ; else WRITE ( FAN_PIN , 0 ) ;
}
2018-12-06 14:18:07 +00:00
# endif
2017-08-21 15:23:30 +00:00
if ( soft_pwm_0 < pwm_count )
{
2017-06-29 16:35:43 +00:00
WRITE ( HEATER_0_PIN , 0 ) ;
# ifdef HEATERS_PARALLEL
WRITE ( HEATER_1_PIN , 0 ) ;
# endif
}
2017-08-21 15:23:30 +00:00
2017-06-29 16:35:43 +00:00
# if EXTRUDERS > 1
if ( soft_pwm_1 < pwm_count ) WRITE ( HEATER_1_PIN , 0 ) ;
# endif
# if EXTRUDERS > 2
if ( soft_pwm_2 < pwm_count ) WRITE ( HEATER_2_PIN , 0 ) ;
# endif
2019-07-08 14:42:21 +00:00
#if 0 // @@DR
2017-06-29 16:35:43 +00:00
# if defined(HEATER_BED_PIN) && HEATER_BED_PIN > -1
2019-07-08 14:42:21 +00:00
if ( soft_pwm_b < ( pwm_count & ( ( 1 < < HEATER_BED_SOFT_PWM_BITS ) - 1 ) ) ) {
//WRITE(HEATER_BED_PIN,0);
}
//WRITE(HEATER_BED_PIN, pwm_count & 1 );
# endif
2017-06-29 16:35:43 +00:00
# endif
# ifdef FAN_SOFT_PWM
2018-12-06 14:18:07 +00:00
if ( soft_pwm_fan < ( pwm_count & ( ( 1 < < FAN_SOFT_PWM_BITS ) - 1 ) ) ) WRITE ( FAN_PIN , 0 ) ;
2017-06-29 16:35:43 +00:00
# endif
pwm_count + = ( 1 < < SOFT_PWM_SCALE ) ;
pwm_count & = 0x7f ;
2017-08-21 15:23:30 +00:00
2017-06-29 16:35:43 +00:00
# else //ifndef SLOW_PWM_HEATERS
/*
* SLOW PWM HEATERS
*
* for heaters drived by relay
*/
# ifndef MIN_STATE_TIME
# define MIN_STATE_TIME 16 // MIN_STATE_TIME * 65.5 = time in milliseconds
# endif
if ( slow_pwm_count = = 0 ) {
// EXTRUDER 0
soft_pwm_0 = soft_pwm [ 0 ] ;
if ( soft_pwm_0 > 0 ) {
// turn ON heather only if the minimum time is up
if ( state_timer_heater_0 = = 0 ) {
// if change state set timer
if ( state_heater_0 = = 0 ) {
state_timer_heater_0 = MIN_STATE_TIME ;
}
state_heater_0 = 1 ;
WRITE ( HEATER_0_PIN , 1 ) ;
# ifdef HEATERS_PARALLEL
WRITE ( HEATER_1_PIN , 1 ) ;
# endif
}
} else {
// turn OFF heather only if the minimum time is up
if ( state_timer_heater_0 = = 0 ) {
// if change state set timer
if ( state_heater_0 = = 1 ) {
state_timer_heater_0 = MIN_STATE_TIME ;
}
state_heater_0 = 0 ;
WRITE ( HEATER_0_PIN , 0 ) ;
# ifdef HEATERS_PARALLEL
WRITE ( HEATER_1_PIN , 0 ) ;
# endif
}
}
# if EXTRUDERS > 1
// EXTRUDER 1
soft_pwm_1 = soft_pwm [ 1 ] ;
if ( soft_pwm_1 > 0 ) {
// turn ON heather only if the minimum time is up
if ( state_timer_heater_1 = = 0 ) {
// if change state set timer
if ( state_heater_1 = = 0 ) {
state_timer_heater_1 = MIN_STATE_TIME ;
}
state_heater_1 = 1 ;
WRITE ( HEATER_1_PIN , 1 ) ;
}
} else {
// turn OFF heather only if the minimum time is up
if ( state_timer_heater_1 = = 0 ) {
// if change state set timer
if ( state_heater_1 = = 1 ) {
state_timer_heater_1 = MIN_STATE_TIME ;
}
state_heater_1 = 0 ;
WRITE ( HEATER_1_PIN , 0 ) ;
}
}
# endif
# if EXTRUDERS > 2
// EXTRUDER 2
soft_pwm_2 = soft_pwm [ 2 ] ;
if ( soft_pwm_2 > 0 ) {
// turn ON heather only if the minimum time is up
if ( state_timer_heater_2 = = 0 ) {
// if change state set timer
if ( state_heater_2 = = 0 ) {
state_timer_heater_2 = MIN_STATE_TIME ;
}
state_heater_2 = 1 ;
WRITE ( HEATER_2_PIN , 1 ) ;
}
} else {
// turn OFF heather only if the minimum time is up
if ( state_timer_heater_2 = = 0 ) {
// if change state set timer
if ( state_heater_2 = = 1 ) {
state_timer_heater_2 = MIN_STATE_TIME ;
}
state_heater_2 = 0 ;
WRITE ( HEATER_2_PIN , 0 ) ;
}
}
# endif
# if defined(HEATER_BED_PIN) && HEATER_BED_PIN > -1
// BED
soft_pwm_b = soft_pwm_bed ;
if ( soft_pwm_b > 0 ) {
// turn ON heather only if the minimum time is up
if ( state_timer_heater_b = = 0 ) {
// if change state set timer
if ( state_heater_b = = 0 ) {
state_timer_heater_b = MIN_STATE_TIME ;
}
state_heater_b = 1 ;
2019-01-28 13:20:31 +00:00
//WRITE(HEATER_BED_PIN, 1);
2017-06-29 16:35:43 +00:00
}
} else {
// turn OFF heather only if the minimum time is up
if ( state_timer_heater_b = = 0 ) {
// if change state set timer
if ( state_heater_b = = 1 ) {
state_timer_heater_b = MIN_STATE_TIME ;
}
state_heater_b = 0 ;
WRITE ( HEATER_BED_PIN , 0 ) ;
}
}
# endif
} // if (slow_pwm_count == 0)
// EXTRUDER 0
if ( soft_pwm_0 < slow_pwm_count ) {
// turn OFF heather only if the minimum time is up
if ( state_timer_heater_0 = = 0 ) {
// if change state set timer
if ( state_heater_0 = = 1 ) {
state_timer_heater_0 = MIN_STATE_TIME ;
}
state_heater_0 = 0 ;
WRITE ( HEATER_0_PIN , 0 ) ;
# ifdef HEATERS_PARALLEL
WRITE ( HEATER_1_PIN , 0 ) ;
# endif
}
}
# if EXTRUDERS > 1
// EXTRUDER 1
if ( soft_pwm_1 < slow_pwm_count ) {
// turn OFF heather only if the minimum time is up
if ( state_timer_heater_1 = = 0 ) {
// if change state set timer
if ( state_heater_1 = = 1 ) {
state_timer_heater_1 = MIN_STATE_TIME ;
}
state_heater_1 = 0 ;
WRITE ( HEATER_1_PIN , 0 ) ;
}
}
# endif
# if EXTRUDERS > 2
// EXTRUDER 2
if ( soft_pwm_2 < slow_pwm_count ) {
// turn OFF heather only if the minimum time is up
if ( state_timer_heater_2 = = 0 ) {
// if change state set timer
if ( state_heater_2 = = 1 ) {
state_timer_heater_2 = MIN_STATE_TIME ;
}
state_heater_2 = 0 ;
WRITE ( HEATER_2_PIN , 0 ) ;
}
}
# endif
# if defined(HEATER_BED_PIN) && HEATER_BED_PIN > -1
// BED
if ( soft_pwm_b < slow_pwm_count ) {
// turn OFF heather only if the minimum time is up
if ( state_timer_heater_b = = 0 ) {
// if change state set timer
if ( state_heater_b = = 1 ) {
state_timer_heater_b = MIN_STATE_TIME ;
}
state_heater_b = 0 ;
WRITE ( HEATER_BED_PIN , 0 ) ;
}
}
# endif
# ifdef FAN_SOFT_PWM
2018-12-06 14:18:07 +00:00
if ( ( pwm_count & ( ( 1 < < FAN_SOFT_PWM_BITS ) - 1 ) ) = = 0 )
soft_pwm_fan = fanSpeedSoftPwm / ( 1 < < ( 8 - FAN_SOFT_PWM_BITS ) ) ;
2017-06-29 16:35:43 +00:00
if ( soft_pwm_fan > 0 ) WRITE ( FAN_PIN , 1 ) ; else WRITE ( FAN_PIN , 0 ) ;
}
if ( soft_pwm_fan < pwm_count ) WRITE ( FAN_PIN , 0 ) ;
# endif
2017-08-21 15:23:30 +00:00
2017-06-29 16:35:43 +00:00
pwm_count + = ( 1 < < SOFT_PWM_SCALE ) ;
pwm_count & = 0x7f ;
// increment slow_pwm_count only every 64 pwm_count circa 65.5ms
if ( ( pwm_count % 64 ) = = 0 ) {
slow_pwm_count + + ;
slow_pwm_count & = 0x7f ;
// Extruder 0
if ( state_timer_heater_0 > 0 ) {
state_timer_heater_0 - - ;
}
# if EXTRUDERS > 1
// Extruder 1
if ( state_timer_heater_1 > 0 )
state_timer_heater_1 - - ;
# endif
# if EXTRUDERS > 2
// Extruder 2
if ( state_timer_heater_2 > 0 )
state_timer_heater_2 - - ;
# endif
# if defined(HEATER_BED_PIN) && HEATER_BED_PIN > -1
// Bed
if ( state_timer_heater_b > 0 )
state_timer_heater_b - - ;
# endif
} //if ((pwm_count % 64) == 0) {
# endif //ifndef SLOW_PWM_HEATERS
2022-05-14 23:07:06 +00:00
}
FORCE_INLINE static void soft_pwm_isr ( )
{
lcd_buttons_update ( ) ;
soft_pwm_core ( ) ;
2017-12-15 17:33:35 +00:00
2017-12-20 12:42:20 +00:00
# ifdef BABYSTEPPING
2022-05-14 23:00:34 +00:00
applyBabysteps ( ) ;
2017-12-20 12:42:20 +00:00
# endif //BABYSTEPPING
2017-06-29 16:35:43 +00:00
2021-06-12 13:21:16 +00:00
// Check if a stack overflow happened
if ( ! SdFatUtil : : test_stack_integrity ( ) ) stack_error ( ) ;
2018-02-21 14:19:34 +00:00
# if (defined(FANCHECK) && defined(TACH_0) && (TACH_0 > -1))
2022-05-14 22:54:34 +00:00
readFanTach ( ) ;
2018-02-01 19:08:11 +00:00
# endif //(defined(TACH_0))
2020-08-05 15:04:11 +00:00
}
2018-01-09 18:54:07 +00:00
2020-08-05 15:04:11 +00:00
// Timer2 (originaly timer0) is shared with millies
# ifdef SYSTEM_TIMER_2
ISR ( TIMER2_COMPB_vect )
# else //SYSTEM_TIMER_2
ISR ( TIMER0_COMPB_vect )
# endif //SYSTEM_TIMER_2
{
2022-05-14 22:50:03 +00:00
DISABLE_SOFT_PWM_INTERRUPT ( ) ;
2022-05-14 16:03:43 +00:00
sei ( ) ;
2022-05-14 22:50:03 +00:00
soft_pwm_isr ( ) ;
2022-05-14 16:03:43 +00:00
cli ( ) ;
2022-05-14 22:50:03 +00:00
ENABLE_SOFT_PWM_INTERRUPT ( ) ;
2017-12-20 12:42:20 +00:00
}
2017-06-29 16:35:43 +00:00
2022-05-24 22:40:36 +00:00
void check_max_temp_raw ( )
2017-12-20 12:42:20 +00:00
{
2022-05-24 22:40:36 +00:00
//heater
2017-06-29 16:35:43 +00:00
# if HEATER_0_RAW_LO_TEMP > HEATER_0_RAW_HI_TEMP
2017-12-23 02:36:08 +00:00
if ( current_temperature_raw [ 0 ] < = maxttemp_raw [ 0 ] ) {
2017-06-29 16:35:43 +00:00
# else
2017-12-23 02:36:08 +00:00
if ( current_temperature_raw [ 0 ] > = maxttemp_raw [ 0 ] ) {
2017-06-29 16:35:43 +00:00
# endif
2022-05-24 22:40:36 +00:00
set_temp_error ( TempErrorSource : : hotend , 0 , TempErrorType : : max ) ;
2017-06-29 16:35:43 +00:00
}
2022-05-24 22:40:36 +00:00
//bed
2017-12-23 02:36:08 +00:00
# if defined(BED_MAXTEMP) && (TEMP_SENSOR_BED != 0)
# if HEATER_BED_RAW_LO_TEMP > HEATER_BED_RAW_HI_TEMP
if ( current_temperature_bed_raw < = bed_maxttemp_raw ) {
2017-06-29 16:35:43 +00:00
# else
2017-12-23 02:36:08 +00:00
if ( current_temperature_bed_raw > = bed_maxttemp_raw ) {
2017-06-29 16:35:43 +00:00
# endif
2022-05-24 22:40:36 +00:00
set_temp_error ( TempErrorSource : : bed , 0 , TempErrorType : : max ) ;
2017-06-29 16:35:43 +00:00
}
# endif
2022-05-24 22:40:36 +00:00
//ambient
2020-06-08 01:14:49 +00:00
# if defined(AMBIENT_MAXTEMP) && (TEMP_SENSOR_AMBIENT != 0)
# if AMBIENT_RAW_LO_TEMP > AMBIENT_RAW_HI_TEMP
if ( current_temperature_raw_ambient < = ambient_maxttemp_raw ) {
# else
if ( current_temperature_raw_ambient > = ambient_maxttemp_raw ) {
# endif
2022-05-24 22:40:36 +00:00
set_temp_error ( TempErrorSource : : ambient , 0 , TempErrorType : : max ) ;
2020-06-08 01:14:49 +00:00
}
# endif
2017-12-23 02:36:08 +00:00
}
2022-05-24 22:40:36 +00:00
2019-05-24 06:46:44 +00:00
//! number of repeating the same state with consecutive step() calls
//! used to slow down text switching
struct alert_automaton_mintemp {
2020-01-30 15:24:32 +00:00
const char * m2 ;
alert_automaton_mintemp ( const char * m2 ) : m2 ( m2 ) { }
2019-05-27 12:57:02 +00:00
private :
2019-05-24 06:46:44 +00:00
enum { ALERT_AUTOMATON_SPEED_DIV = 5 } ;
2019-06-12 14:20:21 +00:00
enum class States : uint8_t { Init = 0 , TempAboveMintemp , ShowPleaseRestart , ShowMintemp } ;
States state = States : : Init ;
2019-05-27 12:57:02 +00:00
uint8_t repeat = ALERT_AUTOMATON_SPEED_DIV ;
2019-05-24 06:46:44 +00:00
2019-05-27 14:18:21 +00:00
void substep ( States next_state ) {
2019-05-24 06:46:44 +00:00
if ( repeat = = 0 ) {
state = next_state ; // advance to the next state
repeat = ALERT_AUTOMATON_SPEED_DIV ; // and prepare repeating for it too
} else {
- - repeat ;
}
}
2019-05-27 12:57:02 +00:00
public :
//! brief state automaton step routine
//! @param current_temp current hotend/bed temperature (for computing simple hysteresis)
//! @param mintemp minimal temperature including hysteresis to check current_temp against
2019-05-24 06:46:44 +00:00
void step ( float current_temp , float mintemp ) {
static const char m1 [ ] PROGMEM = " Please restart " ;
switch ( state ) {
2019-06-12 14:20:21 +00:00
case States : : Init : // initial state - check hysteresis
2019-05-24 06:46:44 +00:00
if ( current_temp > mintemp ) {
2019-06-12 14:20:21 +00:00
state = States : : TempAboveMintemp ;
2019-05-24 06:46:44 +00:00
}
// otherwise keep the Err MINTEMP alert message on the display,
// i.e. do not transfer to state 1
break ;
2019-06-12 14:20:21 +00:00
case States : : TempAboveMintemp : // the temperature has risen above the hysteresis check
2019-05-24 06:46:44 +00:00
lcd_setalertstatuspgm ( m2 ) ;
2019-06-12 14:20:21 +00:00
substep ( States : : ShowMintemp ) ;
2019-05-24 06:46:44 +00:00
last_alert_sent_to_lcd = LCDALERT_MINTEMPFIXED ;
break ;
2019-06-12 14:20:21 +00:00
case States : : ShowPleaseRestart : // displaying "Please restart"
2022-06-28 17:33:32 +00:00
lcd_setalertstatuspgm ( m1 ) ;
2019-06-12 14:20:21 +00:00
substep ( States : : ShowMintemp ) ;
2019-05-24 06:46:44 +00:00
last_alert_sent_to_lcd = LCDALERT_PLEASERESTART ;
break ;
2019-06-12 14:20:21 +00:00
case States : : ShowMintemp : // displaying "MINTEMP fixed"
2022-06-28 17:33:32 +00:00
lcd_setalertstatuspgm ( m2 ) ;
2019-06-12 14:20:21 +00:00
substep ( States : : ShowPleaseRestart ) ;
2019-05-24 06:46:44 +00:00
last_alert_sent_to_lcd = LCDALERT_MINTEMPFIXED ;
break ;
}
}
} ;
2020-02-04 15:30:44 +00:00
static const char m2hotend [ ] PROGMEM = " MINTEMP HOTEND fixed " ;
2020-01-30 15:24:32 +00:00
static const char m2bed [ ] PROGMEM = " MINTEMP BED fixed " ;
static alert_automaton_mintemp alert_automaton_hotend ( m2hotend ) , alert_automaton_bed ( m2bed ) ;
2017-12-20 12:42:20 +00:00
2017-12-27 17:20:04 +00:00
void check_min_temp_heater0 ( )
2017-12-23 02:36:08 +00:00
{
# if HEATER_0_RAW_LO_TEMP > HEATER_0_RAW_HI_TEMP
if ( current_temperature_raw [ 0 ] > = minttemp_raw [ 0 ] ) {
2017-06-29 16:35:43 +00:00
# else
2017-12-23 02:36:08 +00:00
if ( current_temperature_raw [ 0 ] < = minttemp_raw [ 0 ] ) {
2017-06-29 16:35:43 +00:00
# endif
2022-05-24 22:40:36 +00:00
set_temp_error ( TempErrorSource : : hotend , 0 , TempErrorType : : min ) ;
2017-12-23 02:36:08 +00:00
}
2017-12-27 17:20:04 +00:00
}
void check_min_temp_bed ( )
{
2017-12-23 02:36:08 +00:00
# if HEATER_BED_RAW_LO_TEMP > HEATER_BED_RAW_HI_TEMP
if ( current_temperature_bed_raw > = bed_minttemp_raw ) {
2017-06-29 16:35:43 +00:00
# else
2017-12-23 02:36:08 +00:00
if ( current_temperature_bed_raw < = bed_minttemp_raw ) {
2017-06-29 16:35:43 +00:00
# endif
2022-05-24 22:40:36 +00:00
set_temp_error ( TempErrorSource : : bed , 0 , TempErrorType : : min ) ;
2017-12-23 02:36:08 +00:00
}
2017-09-21 13:20:02 +00:00
}
2020-06-08 01:14:49 +00:00
# ifdef AMBIENT_MINTEMP
void check_min_temp_ambient ( )
{
# if AMBIENT_RAW_LO_TEMP > AMBIENT_RAW_HI_TEMP
if ( current_temperature_raw_ambient > = ambient_minttemp_raw ) {
# else
if ( current_temperature_raw_ambient < = ambient_minttemp_raw ) {
# endif
2022-05-24 22:40:36 +00:00
set_temp_error ( TempErrorSource : : ambient , 0 , TempErrorType : : min ) ;
2020-06-08 01:14:49 +00:00
}
}
# endif
2022-05-24 22:40:36 +00:00
void handle_temp_error ( )
2017-12-27 17:20:04 +00:00
{
2022-05-24 22:40:36 +00:00
// relay to the original handler
2022-05-24 23:09:51 +00:00
switch ( ( TempErrorType ) temp_error_state . type ) {
case TempErrorType : : min :
switch ( ( TempErrorSource ) temp_error_state . source ) {
case TempErrorSource : : hotend :
2022-05-24 22:40:36 +00:00
if ( temp_error_state . assert ) {
menu_set_serious_error ( SERIOUS_ERR_MINTEMP_HEATER ) ;
min_temp_error ( temp_error_state . index ) ;
} 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
alert_automaton_hotend . step ( current_temperature [ 0 ] , minttemp [ 0 ] + TEMP_HYSTERESIS ) ;
}
break ;
2022-05-24 23:09:51 +00:00
case TempErrorSource : : bed :
2022-05-24 22:40:36 +00:00
if ( temp_error_state . assert ) {
menu_set_serious_error ( SERIOUS_ERR_MINTEMP_BED ) ;
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 ) ;
}
break ;
2022-07-04 21:34:23 +00:00
# ifdef AMBIENT_THERMISTOR
2022-05-24 23:09:51 +00:00
case TempErrorSource : : ambient :
ambient_min_temp_error ( ) ;
break ;
2022-07-04 21:34:23 +00:00
# endif
2022-05-24 23:09:51 +00:00
}
break ;
case TempErrorType : : max :
switch ( ( TempErrorSource ) temp_error_state . source ) {
case TempErrorSource : : hotend :
max_temp_error ( temp_error_state . index ) ;
break ;
case TempErrorSource : : bed :
2022-05-24 22:40:36 +00:00
bed_max_temp_error ( ) ;
break ;
2022-07-04 21:34:23 +00:00
# ifdef AMBIENT_THERMISTOR
2022-05-24 23:09:51 +00:00
case TempErrorSource : : ambient :
ambient_max_temp_error ( ) ;
break ;
2022-07-04 21:34:23 +00:00
# endif
2022-05-24 22:40:36 +00:00
}
break ;
2022-05-24 23:09:51 +00:00
case TempErrorType : : preheat :
case TempErrorType : : runaway :
switch ( ( TempErrorSource ) temp_error_state . source ) {
case TempErrorSource : : hotend :
case TempErrorSource : : bed :
temp_runaway_stop (
( ( TempErrorType ) temp_error_state . type = = TempErrorType : : preheat ) ,
( ( TempErrorSource ) temp_error_state . source = = TempErrorSource : : bed ) ) ;
break ;
2022-07-04 21:34:23 +00:00
# ifdef AMBIENT_THERMISTOR
2022-05-24 23:09:51 +00:00
case TempErrorSource : : ambient :
// not needed
break ;
2022-07-04 21:34:23 +00:00
# endif
2022-05-24 22:40:36 +00:00
}
break ;
2022-06-24 14:04:00 +00:00
# ifdef TEMP_MODEL
2022-05-26 17:22:17 +00:00
case TempErrorType : : model :
2022-06-24 14:04:00 +00:00
if ( temp_error_state . assert ) {
// TODO: do something meaningful
SERIAL_ECHOLNPGM ( " TM: error triggered! " ) ;
WRITE ( BEEPER , HIGH ) ;
} else {
2022-05-26 17:22:17 +00:00
temp_error_state . v = 0 ;
WRITE ( BEEPER , LOW ) ;
2022-06-24 14:04:00 +00:00
SERIAL_ECHOLNPGM ( " TM: error cleared " ) ;
2022-05-26 17:22:17 +00:00
}
break ;
2022-06-24 14:04:00 +00:00
# endif
2022-05-24 22:40:36 +00:00
}
2017-12-27 17:20:04 +00:00
}
2022-05-24 22:40:36 +00:00
2017-06-29 16:35:43 +00:00
# ifdef PIDTEMP
// Apply the scale factors to the PID values
float scalePID_i ( float i )
{
return i * PID_dT ;
}
float unscalePID_i ( float i )
{
return i / PID_dT ;
}
float scalePID_d ( float d )
{
return d / PID_dT ;
}
float unscalePID_d ( float d )
{
return d * PID_dT ;
}
# endif //PIDTEMP
2020-06-15 22:41:21 +00:00
# ifdef PINDA_THERMISTOR
2020-06-16 00:02:12 +00:00
//! @brief PINDA thermistor detected
//!
//! @retval true firmware should do temperature compensation and allow calibration
//! @retval false PINDA thermistor is not detected, disable temperature compensation and calibration
2020-12-07 14:44:49 +00:00
//! @retval true/false when forced via LCD menu Settings->HW Setup->SuperPINDA
2020-06-16 00:02:12 +00:00
//!
2020-06-15 22:41:21 +00:00
bool has_temperature_compensation ( )
{
2020-12-07 14:32:19 +00:00
# ifdef SUPERPINDA_SUPPORT
2020-12-08 16:50:04 +00:00
# ifdef PINDA_TEMP_COMP
2020-12-07 14:32:19 +00:00
uint8_t pinda_temp_compensation = eeprom_read_byte ( ( uint8_t * ) EEPROM_PINDA_TEMP_COMPENSATION ) ;
if ( pinda_temp_compensation = = EEPROM_EMPTY_VALUE ) //Unkown PINDA temp compenstation, so check it.
{
2020-12-08 16:50:04 +00:00
# endif //PINDA_TEMP_COMP
2020-12-07 14:32:19 +00:00
return ( current_temperature_pinda > = PINDA_MINTEMP ) ? true : false ;
2020-12-08 16:50:04 +00:00
# ifdef PINDA_TEMP_COMP
2020-12-07 14:32:19 +00:00
}
else if ( pinda_temp_compensation = = 0 ) return true ; //Overwritten via LCD menu SuperPINDA [No]
else return false ; //Overwritten via LCD menu SuperPINDA [YES]
2020-12-08 16:50:04 +00:00
# endif //PINDA_TEMP_COMP
2020-06-15 22:41:21 +00:00
# else
return true ;
# endif
}
# endif //PINDA_THERMISTOR
2022-05-15 21:35:46 +00:00
2022-05-24 19:16:03 +00:00
// RAII helper class to run a code block with temp_mgr_isr disabled
class TempMgrGuard
{
bool temp_mgr_state ;
public :
TempMgrGuard ( ) {
ATOMIC_BLOCK ( ATOMIC_RESTORESTATE ) {
temp_mgr_state = TEMP_MGR_INTERRUPT_STATE ( ) ;
2022-06-26 15:06:40 +00:00
DISABLE_TEMP_MGR_INTERRUPT ( ) ;
2022-05-24 19:16:03 +00:00
}
}
~ TempMgrGuard ( ) throw ( ) {
ATOMIC_BLOCK ( ATOMIC_RESTORESTATE ) {
if ( temp_mgr_state ) ENABLE_TEMP_MGR_INTERRUPT ( ) ;
}
}
} ;
2022-05-15 21:35:46 +00:00
void temp_mgr_init ( )
{
// initialize the ADC and start a conversion
adc_init ( ) ;
adc_start_cycle ( ) ;
// initialize timer5
CRITICAL_SECTION_START ;
// CTC
TCCR5B & = ~ ( 1 < < WGM53 ) ;
TCCR5B | = ( 1 < < WGM52 ) ;
TCCR5A & = ~ ( 1 < < WGM51 ) ;
TCCR5A & = ~ ( 1 < < WGM50 ) ;
// output mode = 00 (disconnected)
TCCR5A & = ~ ( 3 < < COM5A0 ) ;
TCCR5A & = ~ ( 3 < < COM5B0 ) ;
// x/256 prescaler
TCCR5B | = ( 1 < < CS52 ) ;
TCCR5B & = ~ ( 1 < < CS51 ) ;
TCCR5B & = ~ ( 1 < < CS50 ) ;
// reset counter
TCNT5 = 0 ;
OCR5A = TIMER5_OCRA_OVF ;
// clear pending interrupts, enable COMPA
2022-06-26 22:17:46 +00:00
TEMP_MGR_INT_FLAG_CLEAR ( ) ;
2022-05-15 21:35:46 +00:00
ENABLE_TEMP_MGR_INTERRUPT ( ) ;
2022-05-24 16:37:07 +00:00
CRITICAL_SECTION_END ;
2022-05-15 21:35:46 +00:00
}
2022-05-24 10:57:35 +00:00
static void pid_heater ( uint8_t e , const float current , const int target )
2022-05-15 21:35:46 +00:00
{
float pid_input ;
float pid_output ;
# ifdef PIDTEMP
2022-05-24 10:57:35 +00:00
pid_input = current ;
2022-05-15 21:35:46 +00:00
# ifndef PID_OPENLOOP
2022-05-24 10:57:35 +00:00
if ( target = = 0 ) {
2022-05-15 21:35:46 +00:00
pid_output = 0 ;
pid_reset [ e ] = true ;
} else {
2022-05-24 10:57:35 +00:00
pid_error [ e ] = target - pid_input ;
2022-05-15 21:35:46 +00:00
if ( pid_reset [ e ] ) {
iState_sum [ e ] = 0.0 ;
dTerm [ e ] = 0.0 ; // 'dState_last[e]' initial setting is not necessary (see end of if-statement)
pid_reset [ e ] = false ;
}
# ifndef PonM
pTerm [ e ] = cs . Kp * pid_error [ e ] ;
iState_sum [ e ] + = pid_error [ e ] ;
iState_sum [ e ] = constrain ( iState_sum [ e ] , iState_sum_min [ e ] , iState_sum_max [ e ] ) ;
iTerm [ e ] = cs . Ki * iState_sum [ e ] ;
// PID_K1 defined in Configuration.h in the PID settings
# define K2 (1.0-PID_K1)
dTerm [ e ] = ( cs . Kd * ( pid_input - dState_last [ e ] ) ) * K2 + ( PID_K1 * dTerm [ e ] ) ; // e.g. digital filtration of derivative term changes
pid_output = pTerm [ e ] + iTerm [ e ] - dTerm [ e ] ; // subtraction due to "Derivative on Measurement" method (i.e. derivative of input instead derivative of error is used)
if ( pid_output > PID_MAX ) {
if ( pid_error [ e ] > 0 ) iState_sum [ e ] - = pid_error [ e ] ; // conditional un-integration
pid_output = PID_MAX ;
} else if ( pid_output < 0 ) {
if ( pid_error [ e ] < 0 ) iState_sum [ e ] - = pid_error [ e ] ; // conditional un-integration
pid_output = 0 ;
}
# else // PonM ("Proportional on Measurement" method)
iState_sum [ e ] + = cs . Ki * pid_error [ e ] ;
iState_sum [ e ] - = cs . Kp * ( pid_input - dState_last [ e ] ) ;
iState_sum [ e ] = constrain ( iState_sum [ e ] , 0 , PID_INTEGRAL_DRIVE_MAX ) ;
dTerm [ e ] = cs . Kd * ( pid_input - dState_last [ e ] ) ;
pid_output = iState_sum [ e ] - dTerm [ e ] ; // subtraction due to "Derivative on Measurement" method (i.e. derivative of input instead derivative of error is used)
pid_output = constrain ( pid_output , 0 , PID_MAX ) ;
# endif // PonM
}
dState_last [ e ] = pid_input ;
# else //PID_OPENLOOP
2022-05-24 10:57:35 +00:00
pid_output = constrain ( target [ e ] , 0 , PID_MAX ) ;
2022-05-15 21:35:46 +00:00
# endif //PID_OPENLOOP
# ifdef PID_DEBUG
SERIAL_ECHO_START ;
SERIAL_ECHO ( " PID_DEBUG " ) ;
SERIAL_ECHO ( e ) ;
SERIAL_ECHO ( " : Input " ) ;
SERIAL_ECHO ( pid_input ) ;
SERIAL_ECHO ( " Output " ) ;
SERIAL_ECHO ( pid_output ) ;
SERIAL_ECHO ( " pTerm " ) ;
SERIAL_ECHO ( pTerm [ e ] ) ;
SERIAL_ECHO ( " iTerm " ) ;
SERIAL_ECHO ( iTerm [ e ] ) ;
SERIAL_ECHO ( " dTerm " ) ;
SERIAL_ECHOLN ( - dTerm [ e ] ) ;
# endif //PID_DEBUG
# else /* PID off */
pid_output = 0 ;
2022-05-24 10:57:35 +00:00
if ( current [ e ] < target [ e ] ) {
2022-05-15 21:35:46 +00:00
pid_output = PID_MAX ;
}
# endif
// Check if temperature is within the correct range
2022-05-24 10:57:35 +00:00
if ( ( current < maxttemp [ e ] ) & & ( target ! = 0 ) )
2022-05-15 21:35:46 +00:00
soft_pwm [ e ] = ( int ) pid_output > > 1 ;
else
soft_pwm [ e ] = 0 ;
}
2022-05-24 10:57:35 +00:00
static void pid_bed ( const float current , const int target )
2022-05-15 21:35:46 +00:00
{
float pid_input ;
float pid_output ;
# ifndef PIDTEMPBED
if ( _millis ( ) - previous_millis_bed_heater < BED_CHECK_INTERVAL )
return ;
previous_millis_bed_heater = _millis ( ) ;
# endif
# if TEMP_SENSOR_BED != 0
# ifdef PIDTEMPBED
2022-05-24 10:57:35 +00:00
pid_input = current ;
2022-05-15 21:35:46 +00:00
# ifndef PID_OPENLOOP
2022-05-24 10:57:35 +00:00
pid_error_bed = target - pid_input ;
2022-05-15 21:35:46 +00:00
pTerm_bed = cs . bedKp * pid_error_bed ;
temp_iState_bed + = pid_error_bed ;
temp_iState_bed = constrain ( temp_iState_bed , temp_iState_min_bed , temp_iState_max_bed ) ;
iTerm_bed = cs . bedKi * temp_iState_bed ;
//PID_K1 defined in Configuration.h in the PID settings
# define K2 (1.0-PID_K1)
dTerm_bed = ( cs . bedKd * ( pid_input - temp_dState_bed ) ) * K2 + ( PID_K1 * dTerm_bed ) ;
temp_dState_bed = pid_input ;
pid_output = pTerm_bed + iTerm_bed - dTerm_bed ;
if ( pid_output > MAX_BED_POWER ) {
if ( pid_error_bed > 0 ) temp_iState_bed - = pid_error_bed ; // conditional un-integration
pid_output = MAX_BED_POWER ;
} else if ( pid_output < 0 ) {
if ( pid_error_bed < 0 ) temp_iState_bed - = pid_error_bed ; // conditional un-integration
pid_output = 0 ;
}
# else
2022-05-24 10:57:35 +00:00
pid_output = constrain ( target , 0 , MAX_BED_POWER ) ;
2022-05-15 21:35:46 +00:00
# endif //PID_OPENLOOP
2022-05-24 10:57:35 +00:00
if ( current < BED_MAXTEMP )
2022-05-15 21:35:46 +00:00
{
soft_pwm_bed = ( int ) pid_output > > 1 ;
timer02_set_pwm0 ( soft_pwm_bed < < 1 ) ;
}
else
{
soft_pwm_bed = 0 ;
timer02_set_pwm0 ( soft_pwm_bed < < 1 ) ;
}
# elif !defined(BED_LIMIT_SWITCHING)
// Check if temperature is within the correct range
2022-05-24 10:57:35 +00:00
if ( current < BED_MAXTEMP )
2022-05-15 21:35:46 +00:00
{
2022-05-24 10:57:35 +00:00
if ( current > = target )
2022-05-15 21:35:46 +00:00
{
soft_pwm_bed = 0 ;
timer02_set_pwm0 ( soft_pwm_bed < < 1 ) ;
}
else
{
soft_pwm_bed = MAX_BED_POWER > > 1 ;
timer02_set_pwm0 ( soft_pwm_bed < < 1 ) ;
}
}
else
{
soft_pwm_bed = 0 ;
timer02_set_pwm0 ( soft_pwm_bed < < 1 ) ;
WRITE ( HEATER_BED_PIN , LOW ) ;
}
# else //#ifdef BED_LIMIT_SWITCHING
// Check if temperature is within the correct band
2022-05-24 10:57:35 +00:00
if ( current < BED_MAXTEMP )
2022-05-15 21:35:46 +00:00
{
2022-05-24 10:57:35 +00:00
if ( current > target + BED_HYSTERESIS )
2022-05-15 21:35:46 +00:00
{
soft_pwm_bed = 0 ;
timer02_set_pwm0 ( soft_pwm_bed < < 1 ) ;
}
2022-05-24 10:57:35 +00:00
else if ( current < = target - BED_HYSTERESIS )
2022-05-15 21:35:46 +00:00
{
soft_pwm_bed = MAX_BED_POWER > > 1 ;
timer02_set_pwm0 ( soft_pwm_bed < < 1 ) ;
}
}
else
{
soft_pwm_bed = 0 ;
timer02_set_pwm0 ( soft_pwm_bed < < 1 ) ;
WRITE ( HEATER_BED_PIN , LOW ) ;
}
# endif //BED_LIMIT_SWITCHING
2022-05-24 10:57:35 +00:00
if ( target = = 0 )
2022-05-15 21:35:46 +00:00
{
soft_pwm_bed = 0 ;
timer02_set_pwm0 ( soft_pwm_bed < < 1 ) ;
}
# endif //TEMP_SENSOR_BED
}
2022-05-24 16:37:07 +00:00
// ISR-safe temperatures
static volatile bool adc_values_ready = false ;
float current_temperature_isr [ EXTRUDERS ] ;
int target_temperature_isr [ EXTRUDERS ] ;
float current_temperature_bed_isr ;
int target_temperature_bed_isr ;
# ifdef PINDA_THERMISTOR
float current_temperature_pinda_isr ;
# endif
# ifdef AMBIENT_THERMISTOR
float current_temperature_ambient_isr ;
# endif
// ISR callback from adc when sampling finished
2022-05-24 16:43:33 +00:00
void adc_callback ( )
2022-05-24 16:37:07 +00:00
{
current_temperature_raw [ 0 ] = adc_values [ ADC_PIN_IDX ( TEMP_0_PIN ) ] ; //heater
current_temperature_bed_raw = adc_values [ ADC_PIN_IDX ( TEMP_BED_PIN ) ] ;
# ifdef PINDA_THERMISTOR
current_temperature_raw_pinda = adc_values [ ADC_PIN_IDX ( TEMP_PINDA_PIN ) ] ;
# endif //PINDA_THERMISTOR
# ifdef AMBIENT_THERMISTOR
current_temperature_raw_ambient = adc_values [ ADC_PIN_IDX ( TEMP_AMBIENT_PIN ) ] ; // 5->6
# endif //AMBIENT_THERMISTOR
# ifdef VOLT_PWR_PIN
current_voltage_raw_pwr = adc_values [ ADC_PIN_IDX ( VOLT_PWR_PIN ) ] ;
# endif
# ifdef VOLT_BED_PIN
current_voltage_raw_bed = adc_values [ ADC_PIN_IDX ( VOLT_BED_PIN ) ] ; // 6->9
# endif
# ifdef IR_SENSOR_ANALOG
current_voltage_raw_IR = adc_values [ ADC_PIN_IDX ( VOLT_IR_PIN ) ] ;
# endif //IR_SENSOR_ANALOG
adc_values_ready = true ;
}
2022-05-24 19:16:03 +00:00
static void setCurrentTemperaturesFromIsr ( )
2022-05-24 16:37:07 +00:00
{
2022-05-24 19:16:03 +00:00
for ( uint8_t e = 0 ; e < EXTRUDERS ; e + + )
2022-05-24 16:37:07 +00:00
current_temperature [ e ] = current_temperature_isr [ e ] ;
current_temperature_bed = current_temperature_bed_isr ;
# ifdef PINDA_THERMISTOR
current_temperature_pinda = current_temperature_pinda_isr ;
# endif
# ifdef AMBIENT_THERMISTOR
current_temperature_ambient = current_temperature_ambient_isr ;
# endif
2022-05-24 19:16:03 +00:00
}
2022-05-24 16:37:07 +00:00
2022-05-24 19:16:03 +00:00
static void setIsrTargetTemperatures ( )
{
for ( uint8_t e = 0 ; e < EXTRUDERS ; e + + )
target_temperature_isr [ e ] = target_temperature [ e ] ;
target_temperature_bed_isr = target_temperature_bed ;
}
/* Synchronize temperatures:
- fetch updated values from temp_mgr_isr to current values
2022-05-24 22:40:36 +00:00
- update target temperatures for temp_mgr_isr regulation * if * no temperature error is set
2022-05-24 19:16:03 +00:00
This function is blocking : check temp_meas_ready before calling ! */
static void updateTemperatures ( )
{
TempMgrGuard temp_mgr_guard ;
setCurrentTemperaturesFromIsr ( ) ;
2022-05-25 17:45:47 +00:00
if ( ! temp_error_state . v ) {
// refuse to update target temperatures in any error condition!
2022-05-24 22:40:36 +00:00
setIsrTargetTemperatures ( ) ;
2022-05-25 17:45:47 +00:00
}
2022-05-24 19:16:03 +00:00
temp_meas_ready = false ;
2022-05-24 16:37:07 +00:00
}
/* Convert raw values into actual temperatures for temp_mgr. The raw values are created in the ADC
2022-05-25 17:45:47 +00:00
interrupt context , while this function runs from temp_mgr_isr which * is * preemptible as
2022-05-24 16:37:07 +00:00
analog2temp is relatively slow */
static void setIsrTemperaturesFromRawValues ( )
{
for ( uint8_t e = 0 ; e < EXTRUDERS ; e + + )
current_temperature_isr [ e ] = analog2temp ( current_temperature_raw [ e ] , e ) ;
current_temperature_bed_isr = analog2tempBed ( current_temperature_bed_raw ) ;
# ifdef PINDA_THERMISTOR
current_temperature_pinda_isr = analog2tempBed ( current_temperature_raw_pinda ) ;
# endif
# ifdef AMBIENT_THERMISTOR
current_temperature_ambient_isr = analog2tempAmbient ( current_temperature_raw_ambient ) ; //thermistor for ambient is NTCG104LH104JT1 (2000)
# endif
temp_meas_ready = true ;
}
2022-05-24 19:16:03 +00:00
static void temp_mgr_pid ( )
{
for ( uint8_t e = 0 ; e < EXTRUDERS ; e + + )
pid_heater ( e , current_temperature_isr [ e ] , target_temperature_isr [ e ] ) ;
pid_bed ( current_temperature_bed_isr , target_temperature_bed_isr ) ;
}
2022-05-25 17:46:00 +00:00
static void check_temp_runaway ( )
{
# ifdef TEMP_RUNAWAY_EXTRUDER_HYSTERESIS
for ( uint8_t e = 0 ; e < EXTRUDERS ; e + + )
temp_runaway_check ( e + 1 , target_temperature_isr [ e ] , current_temperature_isr [ e ] , soft_pwm [ e ] , false ) ;
# endif
# ifdef TEMP_RUNAWAY_BED_HYSTERESIS
temp_runaway_check ( 0 , target_temperature_bed_isr , current_temperature_bed_isr , soft_pwm_bed , true ) ;
# endif
}
2022-05-26 17:22:17 +00:00
static void check_temp_raw ( ) ;
2022-05-24 22:40:36 +00:00
2022-05-15 21:35:46 +00:00
static void temp_mgr_isr ( )
{
2022-05-24 16:37:07 +00:00
// update *_isr temperatures from raw values for PID regulation
setIsrTemperaturesFromRawValues ( ) ;
2022-05-15 21:35:46 +00:00
2022-05-24 22:40:36 +00:00
// clear the error assertion flag before checking again
temp_error_state . assert = false ;
2022-05-25 17:46:00 +00:00
check_temp_raw ( ) ; // check min/max temp using raw values
check_temp_runaway ( ) ; // classic temperature hysteresis check
2022-06-24 14:04:00 +00:00
# ifdef TEMP_MODEL
temp_model : : check ( ) ; // model-based heater check
# ifdef TEMP_MODEL_DEBUG
temp_model : : log_isr ( ) ;
2022-05-26 17:22:17 +00:00
# endif
2022-06-14 10:11:58 +00:00
# endif
2022-05-24 16:37:07 +00:00
// PID regulation
2022-06-02 21:33:21 +00:00
if ( pid_tuning_finished )
temp_mgr_pid ( ) ;
2022-05-15 21:35:46 +00:00
}
ISR ( TIMER5_COMPA_vect )
{
// immediately schedule a new conversion
2022-05-24 16:37:07 +00:00
if ( adc_values_ready ! = true ) return ;
adc_values_ready = false ;
2022-06-26 22:17:46 +00:00
adc_start_cycle ( ) ;
2022-05-15 21:35:46 +00:00
// run temperature management with interrupts enabled to reduce latency
DISABLE_TEMP_MGR_INTERRUPT ( ) ;
sei ( ) ;
temp_mgr_isr ( ) ;
cli ( ) ;
ENABLE_TEMP_MGR_INTERRUPT ( ) ;
}
2022-05-24 19:16:03 +00:00
void disable_heater ( )
{
setAllTargetHotends ( 0 ) ;
setTargetBed ( 0 ) ;
CRITICAL_SECTION_START ;
// propagate all values down the chain
setIsrTargetTemperatures ( ) ;
temp_mgr_pid ( ) ;
// we can't call soft_pwm_core directly to toggle the pins as it would require removing the inline
// attribute, so disable each pin individually
# if defined(HEATER_0_PIN) && HEATER_0_PIN > -1 && EXTRUDERS > 0
WRITE ( HEATER_0_PIN , LOW ) ;
# endif
# if defined(HEATER_1_PIN) && HEATER_1_PIN > -1 && EXTRUDERS > 1
WRITE ( HEATER_1_PIN , LOW ) ;
# endif
# if defined(HEATER_2_PIN) && HEATER_2_PIN > -1 && EXTRUDERS > 2
WRITE ( HEATER_2_PIN , LOW ) ;
# endif
# if defined(HEATER_BED_PIN) && HEATER_BED_PIN > -1
// TODO: this doesn't take immediate effect!
timer02_set_pwm0 ( 0 ) ;
bedPWMDisabled = 0 ;
# endif
CRITICAL_SECTION_END ;
}
2022-05-24 22:40:36 +00:00
2022-05-26 17:22:17 +00:00
static void check_min_temp_raw ( )
2022-05-24 22:40:36 +00:00
{
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
}
2022-05-26 17:22:17 +00:00
static void check_temp_raw ( )
2022-05-24 22:40:36 +00:00
{
// 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 ( ) ;
}
2022-05-26 17:22:17 +00:00
2022-06-24 14:04:00 +00:00
# ifdef TEMP_MODEL
namespace temp_model {
2022-05-26 17:22:17 +00:00
2022-06-24 14:04:00 +00:00
void model_data : : reset ( uint8_t heater_pwm , uint8_t fan_pwm , float heater_temp , float ambient_temp )
{
// pre-compute invariant values
C_i = ( TEMP_MGR_INTV / C ) ;
warn_s = warn * TEMP_MGR_INTV ;
err_s = err * TEMP_MGR_INTV ;
// initial values
memset ( dT_lag_buf , 0 , sizeof ( dT_lag_buf ) ) ;
dT_lag_idx = 0 ;
dT_err_prev = 0 ;
T_prev = heater_temp ;
// perform one step to initialize the first delta
step ( heater_pwm , fan_pwm , heater_temp , ambient_temp ) ;
// clear the initialization flag
flag_bits . uninitialized = false ;
}
2022-05-26 17:22:17 +00:00
2022-06-24 14:04:00 +00:00
void model_data : : step ( uint8_t heater_pwm , uint8_t fan_pwm , float heater_temp , float ambient_temp )
2022-05-26 17:22:17 +00:00
{
2022-06-24 14:04:00 +00:00
constexpr float soft_pwm_inv = 1. / ( ( 1 < < 7 ) - 1 ) ;
2022-05-26 18:02:41 +00:00
2022-05-26 17:22:17 +00:00
// input values
2022-06-24 14:04:00 +00:00
const float heater_scale = soft_pwm_inv * heater_pwm ;
const float cur_heater_temp = heater_temp ;
const float cur_ambient_temp = ambient_temp + Ta_corr ;
const float cur_R = R [ fan_pwm ] ; // resistance at current fan power (K/W)
2022-05-26 17:22:17 +00:00
2022-06-24 14:04:00 +00:00
float dP = P * heater_scale ; // current power [W]
float dPl = ( cur_heater_temp - cur_ambient_temp ) / cur_R ; // [W] leakage power
2022-05-26 17:22:17 +00:00
float dT = ( dP - dPl ) * C_i ; // expected temperature difference (K)
// filter and lag dT
2022-06-24 14:04:00 +00:00
uint8_t dT_next_idx = ( dT_lag_idx = = ( TEMP_MODEL_LAG_SIZE - 1 ) ? 0 : dT_lag_idx + 1 ) ;
float dT_lag = dT_lag_buf [ dT_next_idx ] ;
float dT_lag_prev = dT_lag_buf [ dT_lag_idx ] ;
float dT_f = ( dT_lag_prev * ( 1.f - TEMP_MODEL_fS ) ) + ( dT * TEMP_MODEL_fS ) ;
dT_lag_buf [ dT_next_idx ] = dT_f ;
dT_lag_idx = dT_next_idx ;
2022-05-26 17:22:17 +00:00
2022-05-26 17:34:33 +00:00
// calculate and filter dT_err
2022-06-24 14:04:00 +00:00
float dT_err = ( cur_heater_temp - T_prev ) - dT_lag ;
float dT_err_f = ( dT_err_prev * ( 1.f - TEMP_MODEL_fE ) ) + ( dT_err * TEMP_MODEL_fE ) ;
T_prev = cur_heater_temp ;
dT_err_prev = dT_err_f ;
2022-05-26 17:22:17 +00:00
// check and trigger errors
2022-06-24 14:04:00 +00:00
flag_bits . error = ( fabsf ( dT_err_f ) > err_s ) ;
flag_bits . warning = ( fabsf ( dT_err_f ) > warn_s ) ;
}
// verify calibration status and trigger a model reset if valid
void setup ( )
{
if ( ! calibrated ( ) ) enabled = false ;
data . flag_bits . uninitialized = true ;
}
bool calibrated ( )
{
if ( ! ( data . P > = 0 ) ) return false ;
if ( ! ( data . C > = 0 ) ) return false ;
if ( ! ( data . Ta_corr ! = NAN ) ) return false ;
for ( uint8_t i = 0 ; i ! = TEMP_MODEL_R_SIZE ; + + i ) {
if ( ! ( temp_model : : data . R [ i ] > = 0 ) )
return false ;
}
if ( ! ( data . warn ! = NAN ) ) return false ;
if ( ! ( data . err ! = NAN ) ) return false ;
return true ;
}
void check ( )
{
if ( ! enabled ) return ;
uint8_t heater_pwm = soft_pwm [ 0 ] ;
uint8_t fan_pwm = soft_pwm_fan ;
float heater_temp = current_temperature_isr [ 0 ] ;
float ambient_temp = current_temperature_ambient_isr ;
// check if a reset is required to seed the model: this needs to be done with valid
// ADC values, so we can't do that directly in init()
if ( data . flag_bits . uninitialized )
data . reset ( heater_pwm , fan_pwm , heater_temp , ambient_temp ) ;
// step the model
data . step ( heater_pwm , fan_pwm , heater_temp , ambient_temp ) ;
// handle errors
if ( data . flag_bits . error )
set_temp_error ( TempErrorSource : : hotend , 0 , TempErrorType : : model ) ;
// handle warning conditions as lower-priority but with greater feedback
warning_state . assert = data . flag_bits . warning ;
if ( warning_state . assert ) {
warning_state . warning = true ;
warning_state . dT_err = temp_model : : data . dT_err_prev ;
2022-05-26 17:22:17 +00:00
}
}
2022-06-14 10:11:58 +00:00
2022-06-24 14:04:00 +00:00
void handle_warning ( )
2022-06-14 10:11:58 +00:00
{
2022-06-24 14:04:00 +00:00
// update values
float warn = data . warn ;
float dT_err ;
2022-06-14 10:11:58 +00:00
{
2022-06-24 14:04:00 +00:00
TempMgrGuard temp_mgr_guard ;
dT_err = warning_state . dT_err ;
}
dT_err / = TEMP_MGR_INTV ; // per-sample => K/s
printf_P ( PSTR ( " TM: error |%f|>%f \n " ) , ( double ) dT_err , ( double ) warn ) ;
2022-06-28 19:06:39 +00:00
static bool first = true ;
2022-06-24 14:04:00 +00:00
if ( warning_state . assert ) {
2022-06-28 19:06:39 +00:00
if ( first ) {
lcd_setalertstatuspgm ( MSG_THERMAL_ANOMALY , LCD_STATUS_INFO ) ;
if ( warn_beep ) WRITE ( BEEPER , HIGH ) ;
first = false ;
} else {
if ( warn_beep ) TOGGLE ( BEEPER ) ;
2022-06-26 13:57:43 +00:00
}
2022-06-24 14:04:00 +00:00
} else {
// warning cleared, reset state
warning_state . warning = false ;
2022-06-28 19:06:39 +00:00
if ( warn_beep ) WRITE ( BEEPER , LOW ) ;
first = true ;
2022-06-24 14:04:00 +00:00
}
}
# ifdef TEMP_MODEL_DEBUG
void log_usr ( )
2022-06-14 10:11:58 +00:00
{
2022-06-24 14:04:00 +00:00
if ( ! log_buf . enabled ) return ;
2022-06-14 10:11:58 +00:00
2022-06-24 14:04:00 +00:00
uint8_t counter = log_buf . entry . counter ;
if ( counter = = log_buf . serial ) return ;
2022-06-14 10:11:58 +00:00
int8_t delta_ms ;
uint8_t cur_pwm ;
2022-06-24 14:04:00 +00:00
// avoid strict-aliasing warnings
union { float cur_temp ; uint32_t cur_temp_b ; } ;
union { float cur_amb ; uint32_t cur_amb_b ; } ;
2022-06-14 10:11:58 +00:00
{
TempMgrGuard temp_mgr_guard ;
2022-06-24 14:04:00 +00:00
delta_ms = log_buf . entry . delta_ms ;
counter = log_buf . entry . counter ;
cur_pwm = log_buf . entry . cur_pwm ;
cur_temp = log_buf . entry . cur_temp ;
cur_amb = log_buf . entry . cur_amb ;
2022-06-14 10:11:58 +00:00
}
2022-06-24 14:04:00 +00:00
uint8_t d = counter - log_buf . serial ;
log_buf . serial = counter ;
2022-06-14 10:11:58 +00:00
2022-06-24 14:04:00 +00:00
printf_P ( PSTR ( " TML %d %d %x %lx %lx \n " ) , ( unsigned ) d - 1 , ( int ) delta_ms + 1 ,
( int ) cur_pwm , ( unsigned long ) cur_temp_b , ( unsigned long ) cur_amb_b ) ;
2022-06-14 10:11:58 +00:00
}
2022-06-24 14:04:00 +00:00
void log_isr ( )
2022-06-14 10:11:58 +00:00
{
2022-06-24 14:04:00 +00:00
if ( ! log_buf . enabled ) return ;
2022-06-14 10:11:58 +00:00
uint32_t stamp = _millis ( ) ;
2022-06-24 14:04:00 +00:00
uint8_t delta_ms = stamp - log_buf . entry . stamp - ( TEMP_MGR_INTV * 1000 ) ;
log_buf . entry . stamp = stamp ;
+ + log_buf . entry . counter ;
log_buf . entry . delta_ms = delta_ms ;
log_buf . entry . cur_pwm = soft_pwm [ 0 ] ;
log_buf . entry . cur_temp = current_temperature_isr [ 0 ] ;
log_buf . entry . cur_amb = current_temperature_ambient_isr ;
}
# endif
} // namespace temp_model
2022-06-14 10:11:58 +00:00
2022-06-24 14:04:00 +00:00
void temp_model_set_enabled ( bool enabled )
{
// set the enabled flag
{
TempMgrGuard temp_mgr_guard ;
temp_model : : enabled = enabled ;
temp_model : : setup ( ) ;
}
// verify that the model has been enabled
if ( enabled & & ! temp_model : : enabled )
SERIAL_ECHOLNPGM ( " TM: invalid parameters, cannot enable " ) ;
}
2022-06-26 13:57:43 +00:00
void temp_model_set_warn_beep ( bool enabled )
{
temp_model : : warn_beep = enabled ;
}
2022-06-24 14:04:00 +00:00
void temp_model_set_params ( float C , float P , float Ta_corr , float warn , float err )
{
TempMgrGuard temp_mgr_guard ;
2022-06-26 13:57:43 +00:00
2022-06-24 14:04:00 +00:00
if ( ! isnan ( C ) & & C > 0 ) temp_model : : data . C = C ;
if ( ! isnan ( P ) & & P > 0 ) temp_model : : data . P = P ;
if ( ! isnan ( Ta_corr ) ) temp_model : : data . Ta_corr = Ta_corr ;
if ( ! isnan ( err ) & & err > 0 ) temp_model : : data . err = err ;
if ( ! isnan ( warn ) & & warn > 0 ) temp_model : : data . warn = warn ;
// ensure warn <= err
if ( temp_model : : data . warn > temp_model : : data . err )
temp_model : : data . warn = temp_model : : data . err ;
temp_model : : setup ( ) ;
}
void temp_model_set_resistance ( uint8_t index , float R )
{
if ( index > = TEMP_MODEL_R_SIZE | | R < = 0 )
return ;
TempMgrGuard temp_mgr_guard ;
temp_model : : data . R [ index ] = R ;
temp_model : : setup ( ) ;
}
void temp_model_report_settings ( )
{
2022-06-26 11:13:59 +00:00
SERIAL_ECHO_START ;
SERIAL_ECHOLNPGM ( " Temperature Model settings: " ) ;
2022-06-24 14:04:00 +00:00
for ( uint8_t i = 0 ; i ! = TEMP_MODEL_R_SIZE ; + + i )
printf_P ( PSTR ( " %S M310 I%u R%.2f \n " ) , echomagic , ( unsigned ) i , ( double ) temp_model : : data . R [ i ] ) ;
2022-06-26 11:13:59 +00:00
printf_P ( PSTR ( " %S M310 P%.2f C%.2f S%u B%u E%.2f W%.2f T%.2f \n " ) ,
echomagic , ( double ) temp_model : : data . P , ( double ) temp_model : : data . C ,
( unsigned ) temp_model : : enabled , ( unsigned ) temp_model : : warn_beep ,
( double ) temp_model : : data . err , ( double ) temp_model : : data . warn ,
( double ) temp_model : : data . Ta_corr ) ;
}
void temp_model_reset_settings ( )
{
TempMgrGuard temp_mgr_guard ;
temp_model : : data . P = TEMP_MODEL_P ;
temp_model : : data . C = NAN ;
for ( uint8_t i = 0 ; i ! = TEMP_MODEL_R_SIZE ; + + i )
temp_model : : data . R [ i ] = NAN ;
temp_model : : data . Ta_corr = TEMP_MODEL_Ta_corr ;
temp_model : : data . warn = TEMP_MODEL_W ;
temp_model : : data . err = TEMP_MODEL_E ;
2022-06-26 13:57:43 +00:00
temp_model : : warn_beep = true ;
2022-06-26 11:13:59 +00:00
temp_model : : enabled = false ;
}
void temp_model_load_settings ( )
{
static_assert ( TEMP_MODEL_R_SIZE = = 16 ) ; // ensure we don't desync with the eeprom table
TempMgrGuard temp_mgr_guard ;
temp_model : : enabled = eeprom_read_byte ( ( uint8_t * ) EEPROM_TEMP_MODEL_ENABLE ) ;
temp_model : : data . P = eeprom_read_float ( ( float * ) EEPROM_TEMP_MODEL_P ) ;
temp_model : : data . C = eeprom_read_float ( ( float * ) EEPROM_TEMP_MODEL_C ) ;
for ( uint8_t i = 0 ; i ! = TEMP_MODEL_R_SIZE ; + + i )
temp_model : : data . R [ i ] = eeprom_read_float ( ( float * ) EEPROM_TEMP_MODEL_R + i ) ;
temp_model : : data . Ta_corr = eeprom_read_float ( ( float * ) EEPROM_TEMP_MODEL_Ta_corr ) ;
temp_model : : data . warn = eeprom_read_float ( ( float * ) EEPROM_TEMP_MODEL_W ) ;
temp_model : : data . err = eeprom_read_float ( ( float * ) EEPROM_TEMP_MODEL_E ) ;
if ( ! temp_model : : calibrated ( ) ) {
SERIAL_ECHOLNPGM ( " TM: stored calibration invalid, resetting " ) ;
temp_model_reset_settings ( ) ;
}
temp_model : : setup ( ) ;
}
void temp_model_save_settings ( )
{
eeprom_update_byte ( ( uint8_t * ) EEPROM_TEMP_MODEL_ENABLE , temp_model : : enabled ) ;
eeprom_update_float ( ( float * ) EEPROM_TEMP_MODEL_P , temp_model : : data . P ) ;
eeprom_update_float ( ( float * ) EEPROM_TEMP_MODEL_C , temp_model : : data . C ) ;
for ( uint8_t i = 0 ; i ! = TEMP_MODEL_R_SIZE ; + + i )
eeprom_update_float ( ( float * ) EEPROM_TEMP_MODEL_R + i , temp_model : : data . R [ i ] ) ;
eeprom_update_float ( ( float * ) EEPROM_TEMP_MODEL_Ta_corr , temp_model : : data . Ta_corr ) ;
eeprom_update_float ( ( float * ) EEPROM_TEMP_MODEL_W , temp_model : : data . warn ) ;
eeprom_update_float ( ( float * ) EEPROM_TEMP_MODEL_E , temp_model : : data . err ) ;
2022-06-24 14:04:00 +00:00
}
2022-06-26 22:17:46 +00:00
namespace temp_model_cal {
void waiting_handler ( )
{
manage_heater ( ) ;
host_keepalive ( ) ;
host_autoreport ( ) ;
2022-06-28 09:28:56 +00:00
checkFans ( ) ;
2022-06-27 09:54:10 +00:00
lcd_update ( 0 ) ;
2022-06-26 22:17:46 +00:00
}
void wait ( unsigned ms )
{
unsigned long mark = _millis ( ) + ms ;
2022-06-27 08:35:49 +00:00
while ( _millis ( ) < mark ) {
if ( temp_error_state . v ) break ;
2022-06-26 22:17:46 +00:00
waiting_handler ( ) ;
2022-06-27 08:35:49 +00:00
}
2022-06-26 22:17:46 +00:00
}
void wait_temp ( )
{
2022-06-27 08:35:49 +00:00
while ( current_temperature [ 0 ] < ( target_temperature [ 0 ] - TEMP_HYSTERESIS ) ) {
if ( temp_error_state . v ) break ;
2022-06-26 22:17:46 +00:00
waiting_handler ( ) ;
2022-06-27 08:35:49 +00:00
}
2022-06-26 22:17:46 +00:00
}
void cooldown ( float temp )
{
float old_speed = fanSpeedSoftPwm ;
fanSpeedSoftPwm = 255 ;
2022-06-27 08:35:49 +00:00
while ( current_temperature [ 0 ] > = temp ) {
if ( temp_error_state . v ) break ;
float ambient = current_temperature_ambient + temp_model : : data . Ta_corr ;
if ( current_temperature [ 0 ] < ( ambient + TEMP_HYSTERESIS ) ) {
// do not get stuck waiting very close to ambient temperature
break ;
}
2022-06-26 22:17:46 +00:00
waiting_handler ( ) ;
2022-06-27 08:35:49 +00:00
}
2022-06-26 22:17:46 +00:00
fanSpeedSoftPwm = old_speed ;
}
uint16_t record ( uint16_t samples = REC_BUFFER_SIZE ) {
TempMgrGuard temp_mgr_guard ;
uint16_t pos = 0 ;
while ( pos < samples ) {
if ( ! TEMP_MGR_INT_FLAG_STATE ( ) ) {
2022-06-27 09:54:10 +00:00
// temperatures not ready yet, just manage heaters while waiting to reduce jitter
manage_heater ( ) ;
2022-06-26 22:17:46 +00:00
continue ;
}
TEMP_MGR_INT_FLAG_CLEAR ( ) ;
// manually repeat what the regular isr would do
if ( adc_values_ready ! = true ) continue ;
adc_values_ready = false ;
adc_start_cycle ( ) ;
temp_mgr_isr ( ) ;
2022-06-27 08:35:49 +00:00
// stop recording for an hard error condition
if ( temp_error_state . v )
return 0 ;
2022-06-26 22:17:46 +00:00
// record a new entry
rec_entry & entry = rec_buffer [ pos ] ;
entry . temp = current_temperature_isr [ 0 ] ;
entry . pwm = soft_pwm [ 0 ] ;
+ + pos ;
2022-06-27 09:54:10 +00:00
// it's now safer to give regular serial/lcd updates a shot
waiting_handler ( ) ;
2022-06-26 22:17:46 +00:00
}
return pos ;
}
2022-06-28 09:28:56 +00:00
float cost_fn ( uint16_t samples , float * const var , float v , uint8_t fan_pwm , float ambient )
2022-06-26 22:17:46 +00:00
{
* var = v ;
temp_model : : data . reset ( rec_buffer [ 0 ] . pwm , fan_pwm , rec_buffer [ 0 ] . temp , ambient ) ;
float err = 0 ;
for ( uint16_t i = 1 ; i < samples ; + + i ) {
temp_model : : data . step ( rec_buffer [ i ] . pwm , fan_pwm , rec_buffer [ i ] . temp , ambient ) ;
err + = fabsf ( temp_model : : data . dT_err_prev ) ;
}
return ( err / ( samples - 1 ) ) ;
}
constexpr float GOLDEN_RATIO = 0.6180339887498949 ;
void update_section ( float points [ 2 ] , const float bounds [ 2 ] )
{
float d = GOLDEN_RATIO * ( bounds [ 1 ] - bounds [ 0 ] ) ;
points [ 0 ] = bounds [ 0 ] + d ;
points [ 1 ] = bounds [ 1 ] - d ;
}
2022-06-28 09:28:56 +00:00
float estimate ( uint16_t samples ,
float * const var , float min , float max ,
float thr , uint16_t max_itr ,
uint8_t fan_pwm , float ambient )
2022-06-26 22:17:46 +00:00
{
float e = NAN ;
float points [ 2 ] ;
float bounds [ 2 ] = { min , max } ;
update_section ( points , bounds ) ;
for ( uint8_t it = 0 ; it ! = max_itr ; + + it ) {
2022-06-28 09:28:56 +00:00
float c1 = cost_fn ( samples , var , points [ 0 ] , fan_pwm , ambient ) ;
float c2 = cost_fn ( samples , var , points [ 1 ] , fan_pwm , ambient ) ;
2022-06-26 22:17:46 +00:00
bool dir = ( c2 < c1 ) ;
bounds [ dir ] = points [ ! dir ] ;
update_section ( points , bounds ) ;
float x = points [ ! dir ] ;
e = ( 1 - GOLDEN_RATIO ) * fabsf ( ( bounds [ 0 ] - bounds [ 1 ] ) / x ) ;
printf_P ( PSTR ( " TM iter:%u v:%.2f e:%.3f \n " ) , it , x , e ) ;
if ( e < thr ) return e ;
}
SERIAL_ECHOLNPGM ( " TM estimation did not converge " ) ;
2022-06-27 08:35:49 +00:00
return NAN ;
2022-06-26 22:17:46 +00:00
}
2022-06-27 08:35:49 +00:00
bool autotune ( int16_t cal_temp )
2022-06-26 22:17:46 +00:00
{
uint16_t samples ;
2022-06-27 08:35:49 +00:00
float e ;
2022-06-26 22:17:46 +00:00
// bootstrap C/R values without fan
fanSpeedSoftPwm = 0 ;
for ( uint8_t i = 0 ; i ! = 2 ; + + i ) {
const char * PROGMEM verb = ( i = = 0 ? PSTR ( " initial " ) : PSTR ( " refining " ) ) ;
target_temperature [ 0 ] = 0 ;
if ( current_temperature [ 0 ] > = TEMP_MODEL_CAL_Tl ) {
printf_P ( PSTR ( " TM: cooling down to %dC \n " ) , TEMP_MODEL_CAL_Tl ) ;
cooldown ( TEMP_MODEL_CAL_Tl ) ;
wait ( 10000 ) ;
}
// we need a valid R value for the initial C guess
if ( isnan ( temp_model : : data . R [ 0 ] ) )
temp_model : : data . R [ 0 ] = TEMP_MODEL_Rh ;
printf_P ( PSTR ( " TM: %S C estimation \n " ) , verb ) ;
target_temperature [ 0 ] = cal_temp ;
samples = record ( ) ;
2022-06-27 08:35:49 +00:00
if ( temp_error_state . v | | ! samples )
return true ;
e = estimate ( samples , & temp_model : : data . C ,
2022-06-26 22:17:46 +00:00
TEMP_MODEL_Cl , TEMP_MODEL_Ch , TEMP_MODEL_C_thr , TEMP_MODEL_C_itr ,
2022-06-28 09:28:56 +00:00
0 , current_temperature_ambient ) ;
2022-06-27 08:35:49 +00:00
if ( isnan ( e ) )
return true ;
2022-06-26 22:17:46 +00:00
wait_temp ( ) ;
if ( i ) break ; // we don't need to refine R
wait ( 30000 ) ; // settle PID regulation
printf_P ( PSTR ( " TM: %S R estimation @ %dC \n " ) , verb , cal_temp ) ;
samples = record ( ) ;
2022-06-27 08:35:49 +00:00
if ( temp_error_state . v | | ! samples )
return true ;
e = estimate ( samples , & temp_model : : data . R [ 0 ] ,
2022-06-26 22:17:46 +00:00
TEMP_MODEL_Rl , TEMP_MODEL_Rh , TEMP_MODEL_R_thr , TEMP_MODEL_R_itr ,
2022-06-28 09:28:56 +00:00
0 , current_temperature_ambient ) ;
2022-06-27 08:35:49 +00:00
if ( isnan ( e ) )
return true ;
2022-06-26 22:17:46 +00:00
}
// Estimate fan losses at regular intervals, starting from full speed to avoid low-speed
// kickstart issues, although this requires us to wait more for the PID stabilization.
// Normally exhibits logarithmic behavior with the stock fan+shroud, so the shorter interval
// at lower speeds is helpful to increase the resolution of the interpolation.
fanSpeedSoftPwm = 255 ;
wait ( 30000 ) ;
for ( int8_t i = TEMP_MODEL_R_SIZE ; i > 0 ; i - = TEMP_MODEL_CAL_R_STEP ) {
fanSpeedSoftPwm = 256 / TEMP_MODEL_R_SIZE * i - 1 ;
wait ( 10000 ) ;
2022-06-28 09:28:56 +00:00
printf_P ( PSTR ( " TM: R[%u] estimation \n " ) , ( unsigned ) i ) ;
2022-06-26 22:17:46 +00:00
samples = record ( ) ;
2022-06-27 08:35:49 +00:00
if ( temp_error_state . v | | ! samples )
return true ;
2022-06-28 09:28:56 +00:00
// a fixed fan pwm (the norminal value) is used here, as soft_pwm_fan will be modified
// during fan measurements and we'd like to include that skew during normal operation.
e = estimate ( samples , & temp_model : : data . R [ i ] ,
2022-06-26 22:17:46 +00:00
TEMP_MODEL_Rl , temp_model : : data . R [ 0 ] , TEMP_MODEL_R_thr , TEMP_MODEL_R_itr ,
2022-06-28 09:28:56 +00:00
i , current_temperature_ambient ) ;
2022-06-27 08:35:49 +00:00
if ( isnan ( e ) )
return true ;
2022-06-26 22:17:46 +00:00
}
// interpolate remaining steps to speed-up calibration
int8_t next = TEMP_MODEL_R_SIZE - 1 ;
for ( uint8_t i = TEMP_MODEL_R_SIZE - 2 ; i ! = 0 ; - - i ) {
if ( ! ( ( TEMP_MODEL_R_SIZE - i - 1 ) % TEMP_MODEL_CAL_R_STEP ) ) {
next = i ;
continue ;
}
int8_t prev = next - TEMP_MODEL_CAL_R_STEP ;
if ( prev < 0 ) prev = 0 ;
float f = ( float ) ( i - prev ) / TEMP_MODEL_CAL_R_STEP ;
float d = ( temp_model : : data . R [ next ] - temp_model : : data . R [ prev ] ) ;
temp_model : : data . R [ i ] = temp_model : : data . R [ prev ] + d * f ;
}
2022-06-27 08:35:49 +00:00
return false ;
2022-06-26 22:17:46 +00:00
}
} // namespace temp_model_cal
void temp_model_autotune ( int16_t temp )
2022-06-24 14:04:00 +00:00
{
2022-06-27 10:25:38 +00:00
if ( moves_planned ( ) | | printer_active ( ) ) {
SERIAL_ECHOLNPGM ( " TM: printer needs to be idle for calibration " ) ;
return ;
}
2022-06-26 22:17:46 +00:00
KEEPALIVE_STATE ( IN_PROCESS ) ;
2022-06-27 08:35:49 +00:00
// disable the model checking during self-calibration
bool was_enabled = temp_model : : enabled ;
temp_model_set_enabled ( false ) ;
SERIAL_ECHOLNPGM ( " TM: autotune start " ) ;
bool err = temp_model_cal : : autotune ( temp > 0 ? temp : TEMP_MODEL_CAL_Th ) ;
// always reset temperature
target_temperature [ 0 ] = 0 ;
if ( err ) {
SERIAL_ECHOLNPGM ( " TM: autotune failed " ) ;
if ( temp_error_state . v )
fanSpeedSoftPwm = 255 ;
} else {
fanSpeedSoftPwm = 0 ;
temp_model_set_enabled ( was_enabled ) ;
temp_model_report_settings ( ) ;
}
2022-06-14 10:11:58 +00:00
}
2022-06-24 14:04:00 +00:00
# ifdef TEMP_MODEL_DEBUG
2022-06-14 10:11:58 +00:00
void temp_model_log_enable ( bool enable )
{
if ( enable ) {
TempMgrGuard temp_mgr_guard ;
2022-06-24 14:04:00 +00:00
temp_model : : log_buf . entry . stamp = _millis ( ) ;
2022-06-14 10:11:58 +00:00
}
2022-06-24 14:04:00 +00:00
temp_model : : log_buf . enabled = enable ;
2022-06-14 10:11:58 +00:00
}
# endif
2022-05-26 17:22:17 +00:00
# endif