Blame | Last modification | View Log | RSS feed
/*** Marlin 3D Printer Firmware* Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]** Based on Sprinter and grbl.* 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/>.**//*** temperature.cpp - temperature control*/#include "Marlin.h"#include "temperature.h"#include "thermistortables.h"#include "ultralcd.h"#include "planner.h"#include "language.h"#include "printcounter.h"#include "delay.h"#include "endstops.h"#if ENABLED(HEATER_0_USES_MAX6675)#include "MarlinSPI.h"#endif#if ENABLED(BABYSTEPPING)#include "stepper.h"#endif#if ENABLED(USE_WATCHDOG)#include "watchdog.h"#endif#if ENABLED(EMERGENCY_PARSER)#include "emergency_parser.h"#endif#if HOTEND_USES_THERMISTOR#if ENABLED(TEMP_SENSOR_1_AS_REDUNDANT)static void* heater_ttbl_map[2] = { (void*)HEATER_0_TEMPTABLE, (void*)HEATER_1_TEMPTABLE };static constexpr uint8_t heater_ttbllen_map[2] = { HEATER_0_TEMPTABLE_LEN, HEATER_1_TEMPTABLE_LEN };#elsestatic void* heater_ttbl_map[HOTENDS] = ARRAY_BY_HOTENDS((void*)HEATER_0_TEMPTABLE, (void*)HEATER_1_TEMPTABLE, (void*)HEATER_2_TEMPTABLE, (void*)HEATER_3_TEMPTABLE, (void*)HEATER_4_TEMPTABLE);static constexpr uint8_t heater_ttbllen_map[HOTENDS] = ARRAY_BY_HOTENDS(HEATER_0_TEMPTABLE_LEN, HEATER_1_TEMPTABLE_LEN, HEATER_2_TEMPTABLE_LEN, HEATER_3_TEMPTABLE_LEN, HEATER_4_TEMPTABLE_LEN);#endif#endifTemperature thermalManager;/*** Macros to include the heater id in temp errors. The compiler's dead-code* elimination should (hopefully) optimize out the unused strings.*/#if HAS_HEATED_BED#define TEMP_ERR_PSTR(MSG, E) \(E) == -1 ? PSTR(MSG ## _BED) : \(HOTENDS > 1 && (E) == 1) ? PSTR(MSG_E2 " " MSG) : \(HOTENDS > 2 && (E) == 2) ? PSTR(MSG_E3 " " MSG) : \(HOTENDS > 3 && (E) == 3) ? PSTR(MSG_E4 " " MSG) : \(HOTENDS > 4 && (E) == 4) ? PSTR(MSG_E5 " " MSG) : \PSTR(MSG_E1 " " MSG)#else#define TEMP_ERR_PSTR(MSG, E) \(HOTENDS > 1 && (E) == 1) ? PSTR(MSG_E2 " " MSG) : \(HOTENDS > 2 && (E) == 2) ? PSTR(MSG_E3 " " MSG) : \(HOTENDS > 3 && (E) == 3) ? PSTR(MSG_E4 " " MSG) : \(HOTENDS > 4 && (E) == 4) ? PSTR(MSG_E5 " " MSG) : \PSTR(MSG_E1 " " MSG)#endif// public:float Temperature::current_temperature[HOTENDS] = { 0.0 };int16_t Temperature::current_temperature_raw[HOTENDS] = { 0 },Temperature::target_temperature[HOTENDS] = { 0 };#if ENABLED(AUTO_POWER_E_FANS)int16_t Temperature::autofan_speed[HOTENDS] = { 0 };#endif#if HAS_HEATED_BEDfloat Temperature::current_temperature_bed = 0.0;int16_t Temperature::current_temperature_bed_raw = 0,Temperature::target_temperature_bed = 0;uint8_t Temperature::soft_pwm_amount_bed;#ifdef BED_MINTEMPint16_t Temperature::bed_minttemp_raw = HEATER_BED_RAW_LO_TEMP;#endif#ifdef BED_MAXTEMPint16_t Temperature::bed_maxttemp_raw = HEATER_BED_RAW_HI_TEMP;#endif#if WATCH_THE_BEDuint16_t Temperature::watch_target_bed_temp = 0;millis_t Temperature::watch_bed_next_ms = 0;#endif#if ENABLED(PIDTEMPBED)float Temperature::bedKp, Temperature::bedKi, Temperature::bedKd, // Initialized by settings.load()Temperature::temp_iState_bed = { 0 },Temperature::temp_dState_bed = { 0 },Temperature::pTerm_bed,Temperature::iTerm_bed,Temperature::dTerm_bed,Temperature::pid_error_bed;#elsemillis_t Temperature::next_bed_check_ms;#endifuint16_t Temperature::raw_temp_bed_value = 0;#if HEATER_IDLE_HANDLERmillis_t Temperature::bed_idle_timeout_ms = 0;bool Temperature::bed_idle_timeout_exceeded = false;#endif#endif // HAS_HEATED_BED#if HAS_TEMP_CHAMBERfloat Temperature::current_temperature_chamber = 0.0;int16_t Temperature::current_temperature_chamber_raw = 0;uint16_t Temperature::raw_temp_chamber_value = 0;#endif// Initialized by settings.load()#if ENABLED(PIDTEMP)#if ENABLED(PID_PARAMS_PER_HOTEND) && HOTENDS > 1float Temperature::Kp[HOTENDS], Temperature::Ki[HOTENDS], Temperature::Kd[HOTENDS];#if ENABLED(PID_EXTRUSION_SCALING)float Temperature::Kc[HOTENDS];#endif#elsefloat Temperature::Kp, Temperature::Ki, Temperature::Kd;#if ENABLED(PID_EXTRUSION_SCALING)float Temperature::Kc;#endif#endif#endif#if ENABLED(BABYSTEPPING)volatile int Temperature::babystepsTodo[XYZ] = { 0 };#endif#if WATCH_HOTENDSuint16_t Temperature::watch_target_temp[HOTENDS] = { 0 };millis_t Temperature::watch_heater_next_ms[HOTENDS] = { 0 };#endif#if ENABLED(PREVENT_COLD_EXTRUSION)bool Temperature::allow_cold_extrude = false;int16_t Temperature::extrude_min_temp = EXTRUDE_MINTEMP;#endif#if ENABLED(TEMP_SENSOR_1_AS_REDUNDANT)uint16_t Temperature::redundant_temperature_raw = 0;float Temperature::redundant_temperature = 0.0;#endifvolatile bool Temperature::temp_meas_ready = false;#if ENABLED(PIDTEMP)float Temperature::temp_iState[HOTENDS] = { 0 },Temperature::temp_dState[HOTENDS] = { 0 },Temperature::pTerm[HOTENDS],Temperature::iTerm[HOTENDS],Temperature::dTerm[HOTENDS];#if ENABLED(PID_EXTRUSION_SCALING)float Temperature::cTerm[HOTENDS];long Temperature::last_e_position;long Temperature::lpq[LPQ_MAX_LEN];int Temperature::lpq_ptr = 0;#endiffloat Temperature::pid_error[HOTENDS];bool Temperature::pid_reset[HOTENDS];#endifuint16_t Temperature::raw_temp_value[MAX_EXTRUDERS] = { 0 };// Init min and max temp with extreme values to prevent false errors during startupint16_t Temperature::minttemp_raw[HOTENDS] = ARRAY_BY_HOTENDS(HEATER_0_RAW_LO_TEMP , HEATER_1_RAW_LO_TEMP , HEATER_2_RAW_LO_TEMP, HEATER_3_RAW_LO_TEMP, HEATER_4_RAW_LO_TEMP),Temperature::maxttemp_raw[HOTENDS] = ARRAY_BY_HOTENDS(HEATER_0_RAW_HI_TEMP , HEATER_1_RAW_HI_TEMP , HEATER_2_RAW_HI_TEMP, HEATER_3_RAW_HI_TEMP, HEATER_4_RAW_HI_TEMP),Temperature::minttemp[HOTENDS] = { 0 },Temperature::maxttemp[HOTENDS] = ARRAY_BY_HOTENDS1(16383);#ifdef MAX_CONSECUTIVE_LOW_TEMPERATURE_ERROR_ALLOWEDuint8_t Temperature::consecutive_low_temperature_error[HOTENDS] = { 0 };#endif#ifdef MILLISECONDS_PREHEAT_TIMEmillis_t Temperature::preheat_end_time[HOTENDS] = { 0 };#endif#if ENABLED(FILAMENT_WIDTH_SENSOR)int8_t Temperature::meas_shift_index; // Index of a delayed sample in buffer#endif#if HAS_AUTO_FANmillis_t Temperature::next_auto_fan_check_ms = 0;#endifuint8_t Temperature::soft_pwm_amount[HOTENDS];#if ENABLED(FAN_SOFT_PWM)uint8_t Temperature::soft_pwm_amount_fan[FAN_COUNT],Temperature::soft_pwm_count_fan[FAN_COUNT];#endif#if ENABLED(FILAMENT_WIDTH_SENSOR)uint16_t Temperature::current_raw_filwidth = 0; // Measured filament diameter - one extruder only#endif#if ENABLED(PROBING_HEATERS_OFF)bool Temperature::paused;#endif#if HEATER_IDLE_HANDLERmillis_t Temperature::heater_idle_timeout_ms[HOTENDS] = { 0 };bool Temperature::heater_idle_timeout_exceeded[HOTENDS] = { false };#endif#if ENABLED(ADC_KEYPAD)uint32_t Temperature::current_ADCKey_raw = 0;uint8_t Temperature::ADCKey_count = 0;#endif#if ENABLED(PID_EXTRUSION_SCALING)int16_t Temperature::lpq_len; // Initialized in configuration_store#endif#if HAS_PID_HEATING/*** PID Autotuning (M303)** Alternately heat and cool the nozzle, observing its behavior to* determine the best PID values to achieve a stable temperature.*/void Temperature::pid_autotune(const float &target, const int8_t hotend, const int8_t ncycles, const bool set_result/*=false*/) {float current = 0.0;int cycles = 0;bool heating = true;millis_t next_temp_ms = millis(), t1 = next_temp_ms, t2 = next_temp_ms;long t_high = 0, t_low = 0;long bias, d;float Ku, Tu,workKp = 0, workKi = 0, workKd = 0,max = 0, min = 10000;#if HAS_PID_FOR_BOTH#define GHV(B,H) (hotend < 0 ? (B) : (H))#define SHV(S,B,H) if (hotend < 0) S##_bed = B; else S [hotend] = H;#elif ENABLED(PIDTEMPBED)#define GHV(B,H) B#define SHV(S,B,H) (S##_bed = B)#else#define GHV(B,H) H#define SHV(S,B,H) (S [hotend] = H)#endif#if WATCH_THE_BED || WATCH_HOTENDS#define HAS_TP_BED (ENABLED(THERMAL_PROTECTION_BED) && ENABLED(PIDTEMPBED))#if HAS_TP_BED && ENABLED(THERMAL_PROTECTION_HOTENDS) && ENABLED(PIDTEMP)#define GTV(B,H) (hotend < 0 ? (B) : (H))#elif HAS_TP_BED#define GTV(B,H) (B)#else#define GTV(B,H) (H)#endifconst uint16_t watch_temp_period = GTV(WATCH_BED_TEMP_PERIOD, WATCH_TEMP_PERIOD);const uint8_t watch_temp_increase = GTV(WATCH_BED_TEMP_INCREASE, WATCH_TEMP_INCREASE);const float watch_temp_target = target - float(watch_temp_increase + GTV(TEMP_BED_HYSTERESIS, TEMP_HYSTERESIS) + 1);millis_t temp_change_ms = next_temp_ms + watch_temp_period * 1000UL;float next_watch_temp = 0.0;bool heated = false;#endif#if HAS_AUTO_FANnext_auto_fan_check_ms = next_temp_ms + 2500UL;#endif#if ENABLED(PIDTEMP)#define _TOP_HOTEND HOTENDS - 1#else#define _TOP_HOTEND -1#endif#if ENABLED(PIDTEMPBED)#define _BOT_HOTEND -1#else#define _BOT_HOTEND 0#endifif (!WITHIN(hotend, _BOT_HOTEND, _TOP_HOTEND)) {SERIAL_ECHOLNPGM(MSG_PID_BAD_EXTRUDER_NUM);return;}if (target > GHV(BED_MAXTEMP, maxttemp[hotend]) - 15) {SERIAL_ECHOLNPGM(MSG_PID_TEMP_TOO_HIGH);return;}SERIAL_ECHOLNPGM(MSG_PID_AUTOTUNE_START);disable_all_heaters(); // switch off all heaters.SHV(soft_pwm_amount, bias = d = (MAX_BED_POWER) >> 1, bias = d = (PID_MAX) >> 1);wait_for_heatup = true; // Can be interrupted with M108// PID Tuning loopwhile (wait_for_heatup) {const millis_t ms = millis();if (temp_meas_ready) { // temp sample readycalculate_celsius_temperatures();// Get the current temperature and constrain itcurrent = GHV(current_temperature_bed, current_temperature[hotend]);NOLESS(max, current);NOMORE(min, current);#if HAS_AUTO_FANif (ELAPSED(ms, next_auto_fan_check_ms)) {check_extruder_auto_fans();next_auto_fan_check_ms = ms + 2500UL;}#endifif (heating && current > target) {if (ELAPSED(ms, t2 + 5000UL)) {heating = false;SHV(soft_pwm_amount, (bias - d) >> 1, (bias - d) >> 1);t1 = ms;t_high = t1 - t2;max = target;}}if (!heating && current < target) {if (ELAPSED(ms, t1 + 5000UL)) {heating = true;t2 = ms;t_low = t2 - t1;if (cycles > 0) {const long max_pow = GHV(MAX_BED_POWER, PID_MAX);bias += (d * (t_high - t_low)) / (t_low + t_high);bias = constrain(bias, 20, max_pow - 20);d = (bias > max_pow >> 1) ? max_pow - 1 - bias : bias;SERIAL_PROTOCOLPAIR(MSG_BIAS, bias);SERIAL_PROTOCOLPAIR(MSG_D, d);SERIAL_PROTOCOLPAIR(MSG_T_MIN, min);SERIAL_PROTOCOLPAIR(MSG_T_MAX, max);if (cycles > 2) {Ku = (4.0f * d) / (M_PI * (max - min) * 0.5f);Tu = ((float)(t_low + t_high) * 0.001f);SERIAL_PROTOCOLPAIR(MSG_KU, Ku);SERIAL_PROTOCOLPAIR(MSG_TU, Tu);workKp = 0.6f * Ku;workKi = 2 * workKp / Tu;workKd = workKp * Tu * 0.125f;SERIAL_PROTOCOLLNPGM("\n" MSG_CLASSIC_PID);SERIAL_PROTOCOLPAIR(MSG_KP, workKp);SERIAL_PROTOCOLPAIR(MSG_KI, workKi);SERIAL_PROTOCOLLNPAIR(MSG_KD, workKd);/**workKp = 0.33*Ku;workKi = workKp/Tu;workKd = workKp*Tu/3;SERIAL_PROTOCOLLNPGM(" Some overshoot");SERIAL_PROTOCOLPAIR(" Kp: ", workKp);SERIAL_PROTOCOLPAIR(" Ki: ", workKi);SERIAL_PROTOCOLPAIR(" Kd: ", workKd);workKp = 0.2*Ku;workKi = 2*workKp/Tu;workKd = workKp*Tu/3;SERIAL_PROTOCOLLNPGM(" No overshoot");SERIAL_PROTOCOLPAIR(" Kp: ", workKp);SERIAL_PROTOCOLPAIR(" Ki: ", workKi);SERIAL_PROTOCOLPAIR(" Kd: ", workKd);*/}}SHV(soft_pwm_amount, (bias + d) >> 1, (bias + d) >> 1);cycles++;min = target;}}}// Did the temperature overshoot very far?#ifndef MAX_OVERSHOOT_PID_AUTOTUNE#define MAX_OVERSHOOT_PID_AUTOTUNE 20#endifif (current > target + MAX_OVERSHOOT_PID_AUTOTUNE) {SERIAL_PROTOCOLLNPGM(MSG_PID_TEMP_TOO_HIGH);break;}// Report heater states every 2 secondsif (ELAPSED(ms, next_temp_ms)) {#if HAS_TEMP_SENSORprint_heaterstates();SERIAL_EOL();#endifnext_temp_ms = ms + 2000UL;// Make sure heating is actually working#if WATCH_THE_BED || WATCH_HOTENDSif (#if WATCH_THE_BED && WATCH_HOTENDStrue#elif WATCH_HOTENDShotend >= 0#elsehotend < 0#endif) {if (!heated) { // If not yet reached target...if (current > next_watch_temp) { // Over the watch temp?next_watch_temp = current + watch_temp_increase; // - set the next temp to watch fortemp_change_ms = ms + watch_temp_period * 1000UL; // - move the expiration timer upif (current > watch_temp_target) heated = true; // - Flag if target temperature reached}else if (ELAPSED(ms, temp_change_ms)) // Watch timer expired_temp_error(hotend, PSTR(MSG_T_HEATING_FAILED), TEMP_ERR_PSTR(MSG_HEATING_FAILED_LCD, hotend));}else if (current < target - (MAX_OVERSHOOT_PID_AUTOTUNE)) // Heated, then temperature fell too far?_temp_error(hotend, PSTR(MSG_T_THERMAL_RUNAWAY), TEMP_ERR_PSTR(MSG_THERMAL_RUNAWAY, hotend));}#endif} // every 2 seconds// Timeout after MAX_CYCLE_TIME_PID_AUTOTUNE minutes since the last undershoot/overshoot cycle#ifndef MAX_CYCLE_TIME_PID_AUTOTUNE#define MAX_CYCLE_TIME_PID_AUTOTUNE 20L#endifif (((ms - t1) + (ms - t2)) > (MAX_CYCLE_TIME_PID_AUTOTUNE * 60L * 1000L)) {SERIAL_PROTOCOLLNPGM(MSG_PID_TIMEOUT);break;}if (cycles > ncycles) {SERIAL_PROTOCOLLNPGM(MSG_PID_AUTOTUNE_FINISHED);#if HAS_PID_FOR_BOTHconst char* estring = GHV("bed", "");SERIAL_PROTOCOLPAIR("#define DEFAULT_", estring); SERIAL_PROTOCOLPAIR("Kp ", workKp); SERIAL_EOL();SERIAL_PROTOCOLPAIR("#define DEFAULT_", estring); SERIAL_PROTOCOLPAIR("Ki ", workKi); SERIAL_EOL();SERIAL_PROTOCOLPAIR("#define DEFAULT_", estring); SERIAL_PROTOCOLPAIR("Kd ", workKd); SERIAL_EOL();#elif ENABLED(PIDTEMP)SERIAL_PROTOCOLPAIR("#define DEFAULT_Kp ", workKp); SERIAL_EOL();SERIAL_PROTOCOLPAIR("#define DEFAULT_Ki ", workKi); SERIAL_EOL();SERIAL_PROTOCOLPAIR("#define DEFAULT_Kd ", workKd); SERIAL_EOL();#elseSERIAL_PROTOCOLPAIR("#define DEFAULT_bedKp ", workKp); SERIAL_EOL();SERIAL_PROTOCOLPAIR("#define DEFAULT_bedKi ", workKi); SERIAL_EOL();SERIAL_PROTOCOLPAIR("#define DEFAULT_bedKd ", workKd); SERIAL_EOL();#endif#define _SET_BED_PID() do { \bedKp = workKp; \bedKi = scalePID_i(workKi); \bedKd = scalePID_d(workKd); \}while(0)#define _SET_EXTRUDER_PID() do { \PID_PARAM(Kp, hotend) = workKp; \PID_PARAM(Ki, hotend) = scalePID_i(workKi); \PID_PARAM(Kd, hotend) = scalePID_d(workKd); \update_pid(); }while(0)// Use the result? (As with "M303 U1")if (set_result) {#if HAS_PID_FOR_BOTHif (hotend < 0)_SET_BED_PID();else_SET_EXTRUDER_PID();#elif ENABLED(PIDTEMP)_SET_EXTRUDER_PID();#else_SET_BED_PID();#endif}return;}lcd_update();}disable_all_heaters();}#endif // HAS_PID_HEATING/*** Class and Instance Methods*/Temperature::Temperature() { }int Temperature::getHeaterPower(const int heater) {return (#if HAS_HEATED_BEDheater < 0 ? soft_pwm_amount_bed :#endifsoft_pwm_amount[heater]);}#if HAS_AUTO_FANvoid Temperature::check_extruder_auto_fans() {static const pin_t fanPin[] PROGMEM = { E0_AUTO_FAN_PIN, E1_AUTO_FAN_PIN, E2_AUTO_FAN_PIN, E3_AUTO_FAN_PIN, E4_AUTO_FAN_PIN, CHAMBER_AUTO_FAN_PIN };static const uint8_t fanBit[] PROGMEM = {0,AUTO_1_IS_0 ? 0 : 1,AUTO_2_IS_0 ? 0 : AUTO_2_IS_1 ? 1 : 2,AUTO_3_IS_0 ? 0 : AUTO_3_IS_1 ? 1 : AUTO_3_IS_2 ? 2 : 3,AUTO_4_IS_0 ? 0 : AUTO_4_IS_1 ? 1 : AUTO_4_IS_2 ? 2 : AUTO_4_IS_3 ? 3 : 4,AUTO_CHAMBER_IS_0 ? 0 : AUTO_CHAMBER_IS_1 ? 1 : AUTO_CHAMBER_IS_2 ? 2 : AUTO_CHAMBER_IS_3 ? 3 : AUTO_CHAMBER_IS_4 ? 4 : 5};uint8_t fanState = 0;HOTEND_LOOP()if (current_temperature[e] > EXTRUDER_AUTO_FAN_TEMPERATURE)SBI(fanState, pgm_read_byte(&fanBit[e]));#if HAS_TEMP_CHAMBERif (current_temperature_chamber > EXTRUDER_AUTO_FAN_TEMPERATURE)SBI(fanState, pgm_read_byte(&fanBit[5]));#endifuint8_t fanDone = 0;for (uint8_t f = 0; f < COUNT(fanPin); f++) {const pin_t pin =#ifdef ARDUINOpgm_read_byte(&fanPin[f])#elsefanPin[f]#endif;const uint8_t bit = pgm_read_byte(&fanBit[f]);if (pin >= 0 && !TEST(fanDone, bit)) {uint8_t newFanSpeed = TEST(fanState, bit) ? EXTRUDER_AUTO_FAN_SPEED : 0;#if ENABLED(AUTO_POWER_E_FANS)autofan_speed[f] = newFanSpeed;#endif// this idiom allows both digital and PWM fan outputs (see M42 handling).digitalWrite(pin, newFanSpeed);analogWrite(pin, newFanSpeed);SBI(fanDone, bit);}}}#endif // HAS_AUTO_FAN//// Temperature Error Handlers//void Temperature::_temp_error(const int8_t e, const char * const serial_msg, const char * const lcd_msg) {if (IsRunning()) {SERIAL_ERROR_START();serialprintPGM(serial_msg);SERIAL_ERRORPGM(MSG_STOPPED_HEATER);if (e >= 0) SERIAL_ERRORLN((int)e); else SERIAL_ERRORLNPGM(MSG_HEATER_BED);}#if DISABLED(BOGUS_TEMPERATURE_FAILSAFE_OVERRIDE)static bool killed = false;if (!killed) {Running = false;killed = true;kill(lcd_msg);}elsedisable_all_heaters(); // paranoia#endif}void Temperature::max_temp_error(const int8_t e) {_temp_error(e, PSTR(MSG_T_MAXTEMP), TEMP_ERR_PSTR(MSG_ERR_MAXTEMP, e));}void Temperature::min_temp_error(const int8_t e) {_temp_error(e, PSTR(MSG_T_MINTEMP), TEMP_ERR_PSTR(MSG_ERR_MINTEMP, e));}float Temperature::get_pid_output(const int8_t e) {#if HOTENDS == 1UNUSED(e);#define _HOTEND_TEST true#else#define _HOTEND_TEST e == active_extruder#endiffloat pid_output;#if ENABLED(PIDTEMP)#if DISABLED(PID_OPENLOOP)pid_error[HOTEND_INDEX] = target_temperature[HOTEND_INDEX] - current_temperature[HOTEND_INDEX];dTerm[HOTEND_INDEX] = PID_K2 * PID_PARAM(Kd, HOTEND_INDEX) * (current_temperature[HOTEND_INDEX] - temp_dState[HOTEND_INDEX]) + float(PID_K1) * dTerm[HOTEND_INDEX];temp_dState[HOTEND_INDEX] = current_temperature[HOTEND_INDEX];if (target_temperature[HOTEND_INDEX] == 0|| pid_error[HOTEND_INDEX] < -(PID_FUNCTIONAL_RANGE)#if HEATER_IDLE_HANDLER|| heater_idle_timeout_exceeded[HOTEND_INDEX]#endif) {pid_output = 0;pid_reset[HOTEND_INDEX] = true;}else if (pid_error[HOTEND_INDEX] > PID_FUNCTIONAL_RANGE) {pid_output = BANG_MAX;pid_reset[HOTEND_INDEX] = true;}else {if (pid_reset[HOTEND_INDEX]) {temp_iState[HOTEND_INDEX] = 0.0;pid_reset[HOTEND_INDEX] = false;}pTerm[HOTEND_INDEX] = PID_PARAM(Kp, HOTEND_INDEX) * pid_error[HOTEND_INDEX];temp_iState[HOTEND_INDEX] += pid_error[HOTEND_INDEX];iTerm[HOTEND_INDEX] = PID_PARAM(Ki, HOTEND_INDEX) * temp_iState[HOTEND_INDEX];pid_output = pTerm[HOTEND_INDEX] + iTerm[HOTEND_INDEX] - dTerm[HOTEND_INDEX];#if ENABLED(PID_EXTRUSION_SCALING)cTerm[HOTEND_INDEX] = 0;if (_HOTEND_TEST) {const long e_position = stepper.position(E_AXIS);if (e_position > last_e_position) {lpq[lpq_ptr] = e_position - last_e_position;last_e_position = e_position;}elselpq[lpq_ptr] = 0;if (++lpq_ptr >= lpq_len) lpq_ptr = 0;cTerm[HOTEND_INDEX] = (lpq[lpq_ptr] * planner.steps_to_mm[E_AXIS]) * PID_PARAM(Kc, HOTEND_INDEX);pid_output += cTerm[HOTEND_INDEX];}#endif // PID_EXTRUSION_SCALINGif (pid_output > PID_MAX) {if (pid_error[HOTEND_INDEX] > 0) temp_iState[HOTEND_INDEX] -= pid_error[HOTEND_INDEX]; // conditional un-integrationpid_output = PID_MAX;}else if (pid_output < 0) {if (pid_error[HOTEND_INDEX] < 0) temp_iState[HOTEND_INDEX] -= pid_error[HOTEND_INDEX]; // conditional un-integrationpid_output = 0;}}#elsepid_output = constrain(target_temperature[HOTEND_INDEX], 0, PID_MAX);#endif // PID_OPENLOOP#if ENABLED(PID_DEBUG)SERIAL_ECHO_START();SERIAL_ECHOPAIR(MSG_PID_DEBUG, HOTEND_INDEX);SERIAL_ECHOPAIR(MSG_PID_DEBUG_INPUT, current_temperature[HOTEND_INDEX]);SERIAL_ECHOPAIR(MSG_PID_DEBUG_OUTPUT, pid_output);SERIAL_ECHOPAIR(MSG_PID_DEBUG_PTERM, pTerm[HOTEND_INDEX]);SERIAL_ECHOPAIR(MSG_PID_DEBUG_ITERM, iTerm[HOTEND_INDEX]);SERIAL_ECHOPAIR(MSG_PID_DEBUG_DTERM, dTerm[HOTEND_INDEX]);#if ENABLED(PID_EXTRUSION_SCALING)SERIAL_ECHOPAIR(MSG_PID_DEBUG_CTERM, cTerm[HOTEND_INDEX]);#endifSERIAL_EOL();#endif // PID_DEBUG#else /* PID off */#if HEATER_IDLE_HANDLERif (heater_idle_timeout_exceeded[HOTEND_INDEX])pid_output = 0;else#endifpid_output = (current_temperature[HOTEND_INDEX] < target_temperature[HOTEND_INDEX]) ? PID_MAX : 0;#endifreturn pid_output;}#if ENABLED(PIDTEMPBED)float Temperature::get_pid_output_bed() {float pid_output;#if DISABLED(PID_OPENLOOP)pid_error_bed = target_temperature_bed - current_temperature_bed;pTerm_bed = bedKp * pid_error_bed;temp_iState_bed += pid_error_bed;iTerm_bed = bedKi * temp_iState_bed;dTerm_bed = PID_K2 * bedKd * (current_temperature_bed - temp_dState_bed) + PID_K1 * dTerm_bed;temp_dState_bed = current_temperature_bed;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-integrationpid_output = MAX_BED_POWER;}else if (pid_output < 0) {if (pid_error_bed < 0) temp_iState_bed -= pid_error_bed; // conditional un-integrationpid_output = 0;}#elsepid_output = constrain(target_temperature_bed, 0, MAX_BED_POWER);#endif // PID_OPENLOOP#if ENABLED(PID_BED_DEBUG)SERIAL_ECHO_START();SERIAL_ECHOPGM(" PID_BED_DEBUG ");SERIAL_ECHOPGM(": Input ");SERIAL_ECHO(current_temperature_bed);SERIAL_ECHOPGM(" Output ");SERIAL_ECHO(pid_output);SERIAL_ECHOPGM(" pTerm ");SERIAL_ECHO(pTerm_bed);SERIAL_ECHOPGM(" iTerm ");SERIAL_ECHO(iTerm_bed);SERIAL_ECHOPGM(" dTerm ");SERIAL_ECHOLN(dTerm_bed);#endif // PID_BED_DEBUGreturn pid_output;}#endif // PIDTEMPBED/*** Manage heating activities for extruder hot-ends and a heated bed* - Acquire updated temperature readings* - Also resets the watchdog timer* - Invoke thermal runaway protection* - Manage extruder auto-fan* - Apply filament width to the extrusion rate (may move)* - Update the heated bed PID output value*/void Temperature::manage_heater() {#if ENABLED(PROBING_HEATERS_OFF) && ENABLED(BED_LIMIT_SWITCHING)static bool last_pause_state;#endif#if ENABLED(EMERGENCY_PARSER)if (emergency_parser.killed_by_M112) kill(PSTR(MSG_KILLED));#endifif (!temp_meas_ready) return;calculate_celsius_temperatures(); // also resets the watchdog#if ENABLED(HEATER_0_USES_MAX6675)if (current_temperature[0] > MIN(HEATER_0_MAXTEMP, MAX6675_TMAX - 1.0)) max_temp_error(0);if (current_temperature[0] < MAX(HEATER_0_MINTEMP, MAX6675_TMIN + .01)) min_temp_error(0);#endif#if WATCH_HOTENDS || WATCH_THE_BED || DISABLED(PIDTEMPBED) || HAS_AUTO_FAN || HEATER_IDLE_HANDLERmillis_t ms = millis();#endifHOTEND_LOOP() {#if HEATER_IDLE_HANDLERif (!heater_idle_timeout_exceeded[e] && heater_idle_timeout_ms[e] && ELAPSED(ms, heater_idle_timeout_ms[e]))heater_idle_timeout_exceeded[e] = true;#endif#if ENABLED(THERMAL_PROTECTION_HOTENDS)// Check for thermal runawaythermal_runaway_protection(&thermal_runaway_state_machine[e], &thermal_runaway_timer[e], current_temperature[e], target_temperature[e], e, THERMAL_PROTECTION_PERIOD, THERMAL_PROTECTION_HYSTERESIS);#endifsoft_pwm_amount[e] = (current_temperature[e] > minttemp[e] || is_preheating(e)) && current_temperature[e] < maxttemp[e] ? (int)get_pid_output(e) >> 1 : 0;#if WATCH_HOTENDS// Make sure temperature is increasingif (watch_heater_next_ms[e] && ELAPSED(ms, watch_heater_next_ms[e])) { // Time to check this extruder?if (degHotend(e) < watch_target_temp[e]) // Failed to increase enough?_temp_error(e, PSTR(MSG_T_HEATING_FAILED), TEMP_ERR_PSTR(MSG_HEATING_FAILED_LCD, e));else // Start again if the target is still far offstart_watching_heater(e);}#endif#if ENABLED(TEMP_SENSOR_1_AS_REDUNDANT)// Make sure measured temperatures are close togetherif (ABS(current_temperature[0] - redundant_temperature) > MAX_REDUNDANT_TEMP_SENSOR_DIFF)_temp_error(0, PSTR(MSG_REDUNDANCY), PSTR(MSG_ERR_REDUNDANT_TEMP));#endif} // HOTEND_LOOP#if HAS_AUTO_FANif (ELAPSED(ms, next_auto_fan_check_ms)) { // only need to check fan state very infrequentlycheck_extruder_auto_fans();next_auto_fan_check_ms = ms + 2500UL;}#endif#if ENABLED(FILAMENT_WIDTH_SENSOR)/*** Filament Width Sensor dynamically sets the volumetric multiplier* based on a delayed measurement of the filament diameter.*/if (filament_sensor) {meas_shift_index = filwidth_delay_index[0] - meas_delay_cm;if (meas_shift_index < 0) meas_shift_index += MAX_MEASUREMENT_DELAY + 1; //loop around buffer if neededmeas_shift_index = constrain(meas_shift_index, 0, MAX_MEASUREMENT_DELAY);planner.calculate_volumetric_for_width_sensor(measurement_delay[meas_shift_index]);}#endif // FILAMENT_WIDTH_SENSOR#if HAS_HEATED_BED#if WATCH_THE_BED// Make sure temperature is increasingif (watch_bed_next_ms && ELAPSED(ms, watch_bed_next_ms)) { // Time to check the bed?if (degBed() < watch_target_bed_temp) // Failed to increase enough?_temp_error(-1, PSTR(MSG_T_HEATING_FAILED), TEMP_ERR_PSTR(MSG_HEATING_FAILED_LCD, -1));else // Start again if the target is still far offstart_watching_bed();}#endif // WATCH_THE_BED#if DISABLED(PIDTEMPBED)if (PENDING(ms, next_bed_check_ms)#if ENABLED(PROBING_HEATERS_OFF) && ENABLED(BED_LIMIT_SWITCHING)&& paused == last_pause_state#endif) return;next_bed_check_ms = ms + BED_CHECK_INTERVAL;#if ENABLED(PROBING_HEATERS_OFF) && ENABLED(BED_LIMIT_SWITCHING)last_pause_state = paused;#endif#endif#if HEATER_IDLE_HANDLERif (!bed_idle_timeout_exceeded && bed_idle_timeout_ms && ELAPSED(ms, bed_idle_timeout_ms))bed_idle_timeout_exceeded = true;#endif#if HAS_THERMALLY_PROTECTED_BEDthermal_runaway_protection(&thermal_runaway_bed_state_machine, &thermal_runaway_bed_timer, current_temperature_bed, target_temperature_bed, -1, THERMAL_PROTECTION_BED_PERIOD, THERMAL_PROTECTION_BED_HYSTERESIS);#endif#if HEATER_IDLE_HANDLERif (bed_idle_timeout_exceeded) {soft_pwm_amount_bed = 0;#if DISABLED(PIDTEMPBED)WRITE_HEATER_BED(LOW);#endif}else#endif{#if ENABLED(PIDTEMPBED)soft_pwm_amount_bed = WITHIN(current_temperature_bed, BED_MINTEMP, BED_MAXTEMP) ? (int)get_pid_output_bed() >> 1 : 0;#else// Check if temperature is within the correct bandif (WITHIN(current_temperature_bed, BED_MINTEMP, BED_MAXTEMP)) {#if ENABLED(BED_LIMIT_SWITCHING)if (current_temperature_bed >= target_temperature_bed + BED_HYSTERESIS)soft_pwm_amount_bed = 0;else if (current_temperature_bed <= target_temperature_bed - (BED_HYSTERESIS))soft_pwm_amount_bed = MAX_BED_POWER >> 1;#else // !PIDTEMPBED && !BED_LIMIT_SWITCHINGsoft_pwm_amount_bed = current_temperature_bed < target_temperature_bed ? MAX_BED_POWER >> 1 : 0;#endif}else {soft_pwm_amount_bed = 0;WRITE_HEATER_BED(LOW);}#endif}#endif // HAS_HEATED_BED}#define TEMP_AD595(RAW) ((RAW) * 5.0 * 100.0 / 1024.0 / (OVERSAMPLENR) * (TEMP_SENSOR_AD595_GAIN) + TEMP_SENSOR_AD595_OFFSET)#define TEMP_AD8495(RAW) ((RAW) * 6.6 * 100.0 / 1024.0 / (OVERSAMPLENR) * (TEMP_SENSOR_AD8495_GAIN) + TEMP_SENSOR_AD8495_OFFSET)/*** Bisect search for the range of the 'raw' value, then interpolate* proportionally between the under and over values.*/#define SCAN_THERMISTOR_TABLE(TBL,LEN) do{ \uint8_t l = 0, r = LEN, m; \for (;;) { \m = (l + r) >> 1; \if (m == l || m == r) return (short)pgm_read_word(&TBL[LEN-1][1]); \short v00 = pgm_read_word(&TBL[m-1][0]), \v10 = pgm_read_word(&TBL[m-0][0]); \if (raw < v00) r = m; \else if (raw > v10) l = m; \else { \const short v01 = (short)pgm_read_word(&TBL[m-1][1]), \v11 = (short)pgm_read_word(&TBL[m-0][1]); \return v01 + (raw - v00) * float(v11 - v01) / float(v10 - v00); \} \} \}while(0)// Derived from RepRap FiveD extruder::getTemperature()// For hot end temperature measurement.float Temperature::analog_to_celsius_hotend(const int raw, const uint8_t e) {#if ENABLED(TEMP_SENSOR_1_AS_REDUNDANT)if (e > HOTENDS)#elseif (e >= HOTENDS)#endif{SERIAL_ERROR_START();SERIAL_ERROR((int)e);SERIAL_ERRORLNPGM(MSG_INVALID_EXTRUDER_NUM);kill(PSTR(MSG_KILLED));return 0.0;}switch (e) {case 0:#if ENABLED(HEATER_0_USES_MAX6675)return raw * 0.25;#elif ENABLED(HEATER_0_USES_AD595)return TEMP_AD595(raw);#elif ENABLED(HEATER_0_USES_AD8495)return TEMP_AD8495(raw);#elsebreak;#endifcase 1:#if ENABLED(HEATER_1_USES_AD595)return TEMP_AD595(raw);#elif ENABLED(HEATER_1_USES_AD8495)return TEMP_AD8495(raw);#elsebreak;#endifcase 2:#if ENABLED(HEATER_2_USES_AD595)return TEMP_AD595(raw);#elif ENABLED(HEATER_2_USES_AD8495)return TEMP_AD8495(raw);#elsebreak;#endifcase 3:#if ENABLED(HEATER_3_USES_AD595)return TEMP_AD595(raw);#elif ENABLED(HEATER_3_USES_AD8495)return TEMP_AD8495(raw);#elsebreak;#endifcase 4:#if ENABLED(HEATER_4_USES_AD595)return TEMP_AD595(raw);#elif ENABLED(HEATER_4_USES_AD8495)return TEMP_AD8495(raw);#elsebreak;#endifdefault: break;}#if HOTEND_USES_THERMISTOR// Thermistor with conversion table?const short(*tt)[][2] = (short(*)[][2])(heater_ttbl_map[e]);SCAN_THERMISTOR_TABLE((*tt), heater_ttbllen_map[e]);#endifreturn 0;}#if HAS_HEATED_BED// Derived from RepRap FiveD extruder::getTemperature()// For bed temperature measurement.float Temperature::analog_to_celsius_bed(const int raw) {#if ENABLED(HEATER_BED_USES_THERMISTOR)SCAN_THERMISTOR_TABLE(BEDTEMPTABLE, BEDTEMPTABLE_LEN);#elif ENABLED(HEATER_BED_USES_AD595)return TEMP_AD595(raw);#elif ENABLED(HEATER_BED_USES_AD8495)return TEMP_AD8495(raw);#elsereturn 0;#endif}#endif // HAS_HEATED_BED#if HAS_TEMP_CHAMBER// Derived from RepRap FiveD extruder::getTemperature()// For chamber temperature measurement.float Temperature::analog_to_celsius_chamber(const int raw) {#if ENABLED(HEATER_CHAMBER_USES_THERMISTOR)SCAN_THERMISTOR_TABLE(CHAMBERTEMPTABLE, CHAMBERTEMPTABLE_LEN);#elif ENABLED(HEATER_CHAMBER_USES_AD595)return TEMP_AD595(raw);#elif ENABLED(HEATER_CHAMBER_USES_AD8495)return TEMP_AD8495(raw);#elsereturn 0;#endif}#endif // HAS_TEMP_CHAMBER/*** Get the raw values into the actual temperatures.* The raw values are created in interrupt context,* and this function is called from normal context* as it would block the stepper routine.*/void Temperature::calculate_celsius_temperatures() {#if ENABLED(HEATER_0_USES_MAX6675)current_temperature_raw[0] = read_max6675();#endifHOTEND_LOOP() current_temperature[e] = analog_to_celsius_hotend(current_temperature_raw[e], e);#if HAS_HEATED_BEDcurrent_temperature_bed = analog_to_celsius_bed(current_temperature_bed_raw);#endif#if HAS_TEMP_CHAMBERcurrent_temperature_chamber = analog_to_celsius_chamber(current_temperature_chamber_raw);#endif#if ENABLED(TEMP_SENSOR_1_AS_REDUNDANT)redundant_temperature = analog_to_celsius_hotend(redundant_temperature_raw, 1);#endif#if ENABLED(FILAMENT_WIDTH_SENSOR)filament_width_meas = analog_to_mm_fil_width();#endif#if ENABLED(USE_WATCHDOG)// Reset the watchdog after we know we have a temperature measurement.watchdog_reset();#endiftemp_meas_ready = false;}#if ENABLED(FILAMENT_WIDTH_SENSOR)// Convert raw Filament Width to millimetersfloat Temperature::analog_to_mm_fil_width() {return current_raw_filwidth * 5.0f * (1.0f / 16383.0);}/*** Convert Filament Width (mm) to a simple ratio* and reduce to an 8 bit value.** A nominal width of 1.75 and measured width of 1.73* gives (100 * 1.75 / 1.73) for a ratio of 101 and* a return value of 1.*/int8_t Temperature::widthFil_to_size_ratio() {if (ABS(filament_width_nominal - filament_width_meas) <= FILWIDTH_ERROR_MARGIN)return int(100.0f * filament_width_nominal / filament_width_meas) - 100;return 0;}#endif#if ENABLED(HEATER_0_USES_MAX6675)#ifndef MAX6675_SCK_PIN#define MAX6675_SCK_PIN SCK_PIN#endif#ifndef MAX6675_DO_PIN#define MAX6675_DO_PIN MISO_PIN#endifSPI<MAX6675_DO_PIN, MOSI_PIN, MAX6675_SCK_PIN> max6675_spi;#endif/*** Initialize the temperature manager* The manager is implemented by periodic calls to manage_heater()*/void Temperature::init() {#if MB(RUMBA) && ( \ENABLED(HEATER_0_USES_AD595) || ENABLED(HEATER_1_USES_AD595) || ENABLED(HEATER_2_USES_AD595) || ENABLED(HEATER_3_USES_AD595) || ENABLED(HEATER_4_USES_AD595) || ENABLED(HEATER_BED_USES_AD595) || ENABLED(HEATER_CHAMBER_USES_AD595) \|| ENABLED(HEATER_0_USES_AD8495) || ENABLED(HEATER_1_USES_AD8495) || ENABLED(HEATER_2_USES_AD8495) || ENABLED(HEATER_3_USES_AD8495) || ENABLED(HEATER_4_USES_AD8495) || ENABLED(HEATER_BED_USES_AD8495) || ENABLED(HEATER_CHAMBER_USES_AD8495))// Disable RUMBA JTAG in case the thermocouple extension is plugged on top of JTAG connectorMCUCR = _BV(JTD);MCUCR = _BV(JTD);#endif// Finish init of mult hotend arraysHOTEND_LOOP() maxttemp[e] = maxttemp[0];#if ENABLED(PIDTEMP) && ENABLED(PID_EXTRUSION_SCALING)last_e_position = 0;#endif#if HAS_HEATER_0SET_OUTPUT(HEATER_0_PIN);#endif#if HAS_HEATER_1SET_OUTPUT(HEATER_1_PIN);#endif#if HAS_HEATER_2SET_OUTPUT(HEATER_2_PIN);#endif#if HAS_HEATER_3SET_OUTPUT(HEATER_3_PIN);#endif#if HAS_HEATER_4SET_OUTPUT(HEATER_3_PIN);#endif#if HAS_HEATED_BEDSET_OUTPUT(HEATER_BED_PIN);#endif#if HAS_FAN0SET_OUTPUT(FAN_PIN);#if ENABLED(FAST_PWM_FAN)setPwmFrequency(FAN_PIN, 1); // No prescaling. Pwm frequency = F_CPU/256/8#endif#endif#if HAS_FAN1SET_OUTPUT(FAN1_PIN);#if ENABLED(FAST_PWM_FAN)setPwmFrequency(FAN1_PIN, 1); // No prescaling. Pwm frequency = F_CPU/256/8#endif#endif#if HAS_FAN2SET_OUTPUT(FAN2_PIN);#if ENABLED(FAST_PWM_FAN)setPwmFrequency(FAN2_PIN, 1); // No prescaling. Pwm frequency = F_CPU/256/8#endif#endif#if ENABLED(HEATER_0_USES_MAX6675)OUT_WRITE(SCK_PIN, LOW);OUT_WRITE(MOSI_PIN, HIGH);SET_INPUT_PULLUP(MISO_PIN);max6675_spi.init();OUT_WRITE(SS_PIN, HIGH);OUT_WRITE(MAX6675_SS, HIGH);#endif // HEATER_0_USES_MAX6675HAL_adc_init();#if HAS_TEMP_ADC_0HAL_ANALOG_SELECT(TEMP_0_PIN);#endif#if HAS_TEMP_ADC_1HAL_ANALOG_SELECT(TEMP_1_PIN);#endif#if HAS_TEMP_ADC_2HAL_ANALOG_SELECT(TEMP_2_PIN);#endif#if HAS_TEMP_ADC_3HAL_ANALOG_SELECT(TEMP_3_PIN);#endif#if HAS_TEMP_ADC_4HAL_ANALOG_SELECT(TEMP_4_PIN);#endif#if HAS_HEATED_BEDHAL_ANALOG_SELECT(TEMP_BED_PIN);#endif#if HAS_TEMP_CHAMBERHAL_ANALOG_SELECT(TEMP_CHAMBER_PIN);#endif#if ENABLED(FILAMENT_WIDTH_SENSOR)HAL_ANALOG_SELECT(FILWIDTH_PIN);#endifHAL_timer_start(TEMP_TIMER_NUM, TEMP_TIMER_FREQUENCY);ENABLE_TEMPERATURE_INTERRUPT();#if HAS_AUTO_FAN_0#if E0_AUTO_FAN_PIN == FAN1_PINSET_OUTPUT(E0_AUTO_FAN_PIN);#if ENABLED(FAST_PWM_FAN)setPwmFrequency(E0_AUTO_FAN_PIN, 1); // No prescaling. Pwm frequency = F_CPU/256/8#endif#elseSET_OUTPUT(E0_AUTO_FAN_PIN);#endif#endif#if HAS_AUTO_FAN_1 && !AUTO_1_IS_0#if E1_AUTO_FAN_PIN == FAN1_PINSET_OUTPUT(E1_AUTO_FAN_PIN);#if ENABLED(FAST_PWM_FAN)setPwmFrequency(E1_AUTO_FAN_PIN, 1); // No prescaling. Pwm frequency = F_CPU/256/8#endif#elseSET_OUTPUT(E1_AUTO_FAN_PIN);#endif#endif#if HAS_AUTO_FAN_2 && !AUTO_2_IS_0 && !AUTO_2_IS_1#if E2_AUTO_FAN_PIN == FAN1_PINSET_OUTPUT(E2_AUTO_FAN_PIN);#if ENABLED(FAST_PWM_FAN)setPwmFrequency(E2_AUTO_FAN_PIN, 1); // No prescaling. Pwm frequency = F_CPU/256/8#endif#elseSET_OUTPUT(E2_AUTO_FAN_PIN);#endif#endif#if HAS_AUTO_FAN_3 && !AUTO_3_IS_0 && !AUTO_3_IS_1 && !AUTO_3_IS_2#if E3_AUTO_FAN_PIN == FAN1_PINSET_OUTPUT(E3_AUTO_FAN_PIN);#if ENABLED(FAST_PWM_FAN)setPwmFrequency(E3_AUTO_FAN_PIN, 1); // No prescaling. Pwm frequency = F_CPU/256/8#endif#elseSET_OUTPUT(E3_AUTO_FAN_PIN);#endif#endif#if HAS_AUTO_FAN_4 && !AUTO_4_IS_0 && !AUTO_4_IS_1 && !AUTO_4_IS_2 && !AUTO_4_IS_3#if E4_AUTO_FAN_PIN == FAN1_PINSET_OUTPUT(E4_AUTO_FAN_PIN);#if ENABLED(FAST_PWM_FAN)setPwmFrequency(E4_AUTO_FAN_PIN, 1); // No prescaling. Pwm frequency = F_CPU/256/8#endif#elseSET_OUTPUT(E4_AUTO_FAN_PIN);#endif#endif#if HAS_AUTO_CHAMBER_FAN && !AUTO_CHAMBER_IS_0 && !AUTO_CHAMBER_IS_1 && !AUTO_CHAMBER_IS_2 && !AUTO_CHAMBER_IS_3 && ! AUTO_CHAMBER_IS_4#if CHAMBER_AUTO_FAN_PIN == FAN1_PINSET_OUTPUT(CHAMBER_AUTO_FAN_PIN);#if ENABLED(FAST_PWM_FAN)setPwmFrequency(CHAMBER_AUTO_FAN_PIN, 1); // No prescaling. Pwm frequency = F_CPU/256/8#endif#elseSET_OUTPUT(CHAMBER_AUTO_FAN_PIN);#endif#endif// Wait for temperature measurement to settledelay(250);#define TEMP_MIN_ROUTINE(NR) \minttemp[NR] = HEATER_ ##NR## _MINTEMP; \while (analog_to_celsius_hotend(minttemp_raw[NR], NR) < HEATER_ ##NR## _MINTEMP) { \if (HEATER_ ##NR## _RAW_LO_TEMP < HEATER_ ##NR## _RAW_HI_TEMP) \minttemp_raw[NR] += OVERSAMPLENR; \else \minttemp_raw[NR] -= OVERSAMPLENR; \}#define TEMP_MAX_ROUTINE(NR) \maxttemp[NR] = HEATER_ ##NR## _MAXTEMP; \while (analog_to_celsius_hotend(maxttemp_raw[NR], NR) > HEATER_ ##NR## _MAXTEMP) { \if (HEATER_ ##NR## _RAW_LO_TEMP < HEATER_ ##NR## _RAW_HI_TEMP) \maxttemp_raw[NR] -= OVERSAMPLENR; \else \maxttemp_raw[NR] += OVERSAMPLENR; \}#ifdef HEATER_0_MINTEMPTEMP_MIN_ROUTINE(0);#endif#ifdef HEATER_0_MAXTEMPTEMP_MAX_ROUTINE(0);#endif#if HOTENDS > 1#ifdef HEATER_1_MINTEMPTEMP_MIN_ROUTINE(1);#endif#ifdef HEATER_1_MAXTEMPTEMP_MAX_ROUTINE(1);#endif#if HOTENDS > 2#ifdef HEATER_2_MINTEMPTEMP_MIN_ROUTINE(2);#endif#ifdef HEATER_2_MAXTEMPTEMP_MAX_ROUTINE(2);#endif#if HOTENDS > 3#ifdef HEATER_3_MINTEMPTEMP_MIN_ROUTINE(3);#endif#ifdef HEATER_3_MAXTEMPTEMP_MAX_ROUTINE(3);#endif#if HOTENDS > 4#ifdef HEATER_4_MINTEMPTEMP_MIN_ROUTINE(4);#endif#ifdef HEATER_4_MAXTEMPTEMP_MAX_ROUTINE(4);#endif#endif // HOTENDS > 4#endif // HOTENDS > 3#endif // HOTENDS > 2#endif // HOTENDS > 1#if HAS_HEATED_BED#ifdef BED_MINTEMPwhile (analog_to_celsius_bed(bed_minttemp_raw) < BED_MINTEMP) {#if HEATER_BED_RAW_LO_TEMP < HEATER_BED_RAW_HI_TEMPbed_minttemp_raw += OVERSAMPLENR;#elsebed_minttemp_raw -= OVERSAMPLENR;#endif}#endif // BED_MINTEMP#ifdef BED_MAXTEMPwhile (analog_to_celsius_bed(bed_maxttemp_raw) > BED_MAXTEMP) {#if HEATER_BED_RAW_LO_TEMP < HEATER_BED_RAW_HI_TEMPbed_maxttemp_raw -= OVERSAMPLENR;#elsebed_maxttemp_raw += OVERSAMPLENR;#endif}#endif // BED_MAXTEMP#endif // HAS_HEATED_BED#if ENABLED(PROBING_HEATERS_OFF)paused = false;#endif}#if ENABLED(FAST_PWM_FAN)void Temperature::setPwmFrequency(const pin_t pin, int val) {val &= 0x07;switch (digitalPinToTimer(pin)) {#ifdef TCCR0A#if !AVR_AT90USB1286_FAMILYcase TIMER0A:#endifcase TIMER0B: //_SET_CS(0, val);break;#endif#ifdef TCCR1Acase TIMER1A: case TIMER1B: //_SET_CS(1, val);break;#endif#if defined(TCCR2) || defined(TCCR2A)#ifdef TCCR2case TIMER2:#endif#ifdef TCCR2Acase TIMER2A: case TIMER2B:#endif_SET_CS(2, val); break;#endif#ifdef TCCR3Acase TIMER3A: case TIMER3B: case TIMER3C: _SET_CS(3, val); break;#endif#ifdef TCCR4Acase TIMER4A: case TIMER4B: case TIMER4C: _SET_CS(4, val); break;#endif#ifdef TCCR5Acase TIMER5A: case TIMER5B: case TIMER5C: _SET_CS(5, val); break;#endif}}#endif // FAST_PWM_FAN#if WATCH_HOTENDS/*** Start Heating Sanity Check for hotends that are below* their target temperature by a configurable margin.* This is called when the temperature is set. (M104, M109)*/void Temperature::start_watching_heater(const uint8_t e) {#if HOTENDS == 1UNUSED(e);#endifif (degHotend(HOTEND_INDEX) < degTargetHotend(HOTEND_INDEX) - (WATCH_TEMP_INCREASE + TEMP_HYSTERESIS + 1)) {watch_target_temp[HOTEND_INDEX] = degHotend(HOTEND_INDEX) + WATCH_TEMP_INCREASE;watch_heater_next_ms[HOTEND_INDEX] = millis() + (WATCH_TEMP_PERIOD) * 1000UL;}elsewatch_heater_next_ms[HOTEND_INDEX] = 0;}#endif#if WATCH_THE_BED/*** Start Heating Sanity Check for hotends that are below* their target temperature by a configurable margin.* This is called when the temperature is set. (M140, M190)*/void Temperature::start_watching_bed() {if (degBed() < degTargetBed() - (WATCH_BED_TEMP_INCREASE + TEMP_BED_HYSTERESIS + 1)) {watch_target_bed_temp = degBed() + WATCH_BED_TEMP_INCREASE;watch_bed_next_ms = millis() + (WATCH_BED_TEMP_PERIOD) * 1000UL;}elsewatch_bed_next_ms = 0;}#endif#if ENABLED(THERMAL_PROTECTION_HOTENDS) || HAS_THERMALLY_PROTECTED_BED#if ENABLED(THERMAL_PROTECTION_HOTENDS)Temperature::TRState Temperature::thermal_runaway_state_machine[HOTENDS] = { TRInactive };millis_t Temperature::thermal_runaway_timer[HOTENDS] = { 0 };#endif#if HAS_THERMALLY_PROTECTED_BEDTemperature::TRState Temperature::thermal_runaway_bed_state_machine = TRInactive;millis_t Temperature::thermal_runaway_bed_timer;#endifvoid Temperature::thermal_runaway_protection(Temperature::TRState * const state, millis_t * const timer, const float ¤t, const float &target, const int8_t heater_id, const uint16_t period_seconds, const uint16_t hysteresis_degc) {static float tr_target_temperature[HOTENDS + 1] = { 0.0 };/**SERIAL_ECHO_START();SERIAL_ECHOPGM("Thermal Thermal Runaway Running. Heater ID: ");if (heater_id < 0) SERIAL_ECHOPGM("bed"); else SERIAL_ECHO(heater_id);SERIAL_ECHOPAIR(" ; State:", *state);SERIAL_ECHOPAIR(" ; Timer:", *timer);SERIAL_ECHOPAIR(" ; Temperature:", current);SERIAL_ECHOPAIR(" ; Target Temp:", target);if (heater_id >= 0)SERIAL_ECHOPAIR(" ; Idle Timeout:", heater_idle_timeout_exceeded[heater_id]);elseSERIAL_ECHOPAIR(" ; Idle Timeout:", bed_idle_timeout_exceeded);SERIAL_EOL();*/const int heater_index = heater_id >= 0 ? heater_id : HOTENDS;#if HEATER_IDLE_HANDLER// If the heater idle timeout expires, restartif ((heater_id >= 0 && heater_idle_timeout_exceeded[heater_id])#if HAS_HEATED_BED|| (heater_id < 0 && bed_idle_timeout_exceeded)#endif) {*state = TRInactive;tr_target_temperature[heater_index] = 0;}else#endif{// If the target temperature changes, restartif (tr_target_temperature[heater_index] != target) {tr_target_temperature[heater_index] = target;*state = target > 0 ? TRFirstHeating : TRInactive;}}switch (*state) {// Inactive state waits for a target temperature to be setcase TRInactive: break;// When first heating, wait for the temperature to be reached then go to Stable statecase TRFirstHeating:if (current < tr_target_temperature[heater_index]) break;*state = TRStable;// While the temperature is stable watch for a bad temperaturecase TRStable:if (current >= tr_target_temperature[heater_index] - hysteresis_degc) {*timer = millis() + period_seconds * 1000UL;break;}else if (PENDING(millis(), *timer)) break;*state = TRRunaway;case TRRunaway:_temp_error(heater_id, PSTR(MSG_T_THERMAL_RUNAWAY), TEMP_ERR_PSTR(MSG_THERMAL_RUNAWAY, heater_id));}}#endif // THERMAL_PROTECTION_HOTENDS || THERMAL_PROTECTION_BEDvoid Temperature::disable_all_heaters() {#if ENABLED(AUTOTEMP)planner.autotemp_enabled = false;#endifHOTEND_LOOP() setTargetHotend(0, e);#if HAS_HEATED_BEDsetTargetBed(0);#endif// Unpause and reset everything#if ENABLED(PROBING_HEATERS_OFF)pause(false);#endif// If all heaters go down then for sure our print job has stoppedprint_job_timer.stop();#define DISABLE_HEATER(NR) { \setTargetHotend(0, NR); \soft_pwm_amount[NR] = 0; \WRITE_HEATER_ ##NR (LOW); \}#if HAS_TEMP_HOTENDDISABLE_HEATER(0);#if HOTENDS > 1DISABLE_HEATER(1);#if HOTENDS > 2DISABLE_HEATER(2);#if HOTENDS > 3DISABLE_HEATER(3);#if HOTENDS > 4DISABLE_HEATER(4);#endif // HOTENDS > 4#endif // HOTENDS > 3#endif // HOTENDS > 2#endif // HOTENDS > 1#endif#if HAS_HEATED_BEDtarget_temperature_bed = 0;soft_pwm_amount_bed = 0;#if HAS_HEATED_BEDWRITE_HEATER_BED(LOW);#endif#endif}#if ENABLED(PROBING_HEATERS_OFF)void Temperature::pause(const bool p) {if (p != paused) {paused = p;if (p) {HOTEND_LOOP() start_heater_idle_timer(e, 0); // timeout immediately#if HAS_HEATED_BEDstart_bed_idle_timer(0); // timeout immediately#endif}else {HOTEND_LOOP() reset_heater_idle_timer(e);#if HAS_HEATED_BEDreset_bed_idle_timer();#endif}}}#endif // PROBING_HEATERS_OFF#if ENABLED(HEATER_0_USES_MAX6675)#define MAX6675_HEAT_INTERVAL 250u#if ENABLED(MAX6675_IS_MAX31855)uint32_t max6675_temp = 2000;#define MAX6675_ERROR_MASK 7#define MAX6675_DISCARD_BITS 18#define MAX6675_SPEED_BITS (_BV(SPR1)) // clock ÷ 64#elseuint16_t max6675_temp = 2000;#define MAX6675_ERROR_MASK 4#define MAX6675_DISCARD_BITS 3#define MAX6675_SPEED_BITS (_BV(SPR0)) // clock ÷ 16#endifint Temperature::read_max6675() {static millis_t next_max6675_ms = 0;millis_t ms = millis();if (PENDING(ms, next_max6675_ms)) return (int)max6675_temp;next_max6675_ms = ms + MAX6675_HEAT_INTERVAL;CBI(#ifdef PRRPRR#elif defined(PRR0)PRR0#endif, PRSPI);SPCR = _BV(MSTR) | _BV(SPE) | MAX6675_SPEED_BITS;WRITE(MAX6675_SS, 0); // enable TT_MAX6675DELAY_NS(100); // Ensure 100ns delay// Read a big-endian temperature valuemax6675_temp = 0;for (uint8_t i = sizeof(max6675_temp); i--;) {max6675_temp |= max6675_spi.receive();if (i > 0) max6675_temp <<= 8; // shift left if not the last byte}WRITE(MAX6675_SS, 1); // disable TT_MAX6675if (max6675_temp & MAX6675_ERROR_MASK) {SERIAL_ERROR_START();SERIAL_ERRORPGM("Temp measurement error! ");#if MAX6675_ERROR_MASK == 7SERIAL_ERRORPGM("MAX31855 ");if (max6675_temp & 1)SERIAL_ERRORLNPGM("Open Circuit");else if (max6675_temp & 2)SERIAL_ERRORLNPGM("Short to GND");else if (max6675_temp & 4)SERIAL_ERRORLNPGM("Short to VCC");#elseSERIAL_ERRORLNPGM("MAX6675");#endifmax6675_temp = MAX6675_TMAX * 4; // thermocouple open}elsemax6675_temp >>= MAX6675_DISCARD_BITS;#if ENABLED(MAX6675_IS_MAX31855)// Support negative temperatureif (max6675_temp & 0x00002000) max6675_temp |= 0xFFFFC000;#endifreturn (int)max6675_temp;}#endif // HEATER_0_USES_MAX6675/*** Get raw temperatures*/void Temperature::set_current_temp_raw() {#if HAS_TEMP_ADC_0 && DISABLED(HEATER_0_USES_MAX6675)current_temperature_raw[0] = raw_temp_value[0];#endif#if HAS_TEMP_ADC_1#if ENABLED(TEMP_SENSOR_1_AS_REDUNDANT)redundant_temperature_raw = raw_temp_value[1];#elsecurrent_temperature_raw[1] = raw_temp_value[1];#endif#if HAS_TEMP_ADC_2current_temperature_raw[2] = raw_temp_value[2];#if HAS_TEMP_ADC_3current_temperature_raw[3] = raw_temp_value[3];#if HAS_TEMP_ADC_4current_temperature_raw[4] = raw_temp_value[4];#endif#endif#endif#endif#if HAS_HEATED_BEDcurrent_temperature_bed_raw = raw_temp_bed_value;#endif#if HAS_TEMP_CHAMBERcurrent_temperature_chamber_raw = raw_temp_chamber_value;#endiftemp_meas_ready = true;}#if ENABLED(PINS_DEBUGGING)/*** monitors endstops & Z probe for changes** If a change is detected then the LED is toggled and* a message is sent out the serial port** Yes, we could miss a rapid back & forth change but* that won't matter because this is all manual.**/void endstop_monitor() {static uint16_t old_live_state_local = 0;static uint8_t local_LED_status = 0;uint16_t live_state_local = 0;#if HAS_X_MINif (READ(X_MIN_PIN)) SBI(live_state_local, X_MIN);#endif#if HAS_X_MAXif (READ(X_MAX_PIN)) SBI(live_state_local, X_MAX);#endif#if HAS_Y_MINif (READ(Y_MIN_PIN)) SBI(live_state_local, Y_MIN);#endif#if HAS_Y_MAXif (READ(Y_MAX_PIN)) SBI(live_state_local, Y_MAX);#endif#if HAS_Z_MINif (READ(Z_MIN_PIN)) SBI(live_state_local, Z_MIN);#endif#if HAS_Z_MAXif (READ(Z_MAX_PIN)) SBI(live_state_local, Z_MAX);#endif#if HAS_Z_MIN_PROBE_PINif (READ(Z_MIN_PROBE_PIN)) SBI(live_state_local, Z_MIN_PROBE);#endif#if HAS_Z2_MINif (READ(Z2_MIN_PIN)) SBI(live_state_local, Z2_MIN);#endif#if HAS_Z2_MAXif (READ(Z2_MAX_PIN)) SBI(live_state_local, Z2_MAX);#endifuint16_t endstop_change = live_state_local ^ old_live_state_local;if (endstop_change) {#if HAS_X_MINif (TEST(endstop_change, X_MIN)) SERIAL_PROTOCOLPAIR(" X_MIN:", !!TEST(live_state_local, X_MIN));#endif#if HAS_X_MAXif (TEST(endstop_change, X_MAX)) SERIAL_PROTOCOLPAIR(" X_MAX:", !!TEST(live_state_local, X_MAX));#endif#if HAS_Y_MINif (TEST(endstop_change, Y_MIN)) SERIAL_PROTOCOLPAIR(" Y_MIN:", !!TEST(live_state_local, Y_MIN));#endif#if HAS_Y_MAXif (TEST(endstop_change, Y_MAX)) SERIAL_PROTOCOLPAIR(" Y_MAX:", !!TEST(live_state_local, Y_MAX));#endif#if HAS_Z_MINif (TEST(endstop_change, Z_MIN)) SERIAL_PROTOCOLPAIR(" Z_MIN:", !!TEST(live_state_local, Z_MIN));#endif#if HAS_Z_MAXif (TEST(endstop_change, Z_MAX)) SERIAL_PROTOCOLPAIR(" Z_MAX:", !!TEST(live_state_local, Z_MAX));#endif#if HAS_Z_MIN_PROBE_PINif (TEST(endstop_change, Z_MIN_PROBE)) SERIAL_PROTOCOLPAIR(" PROBE:", !!TEST(live_state_local, Z_MIN_PROBE));#endif#if HAS_Z2_MINif (TEST(endstop_change, Z2_MIN)) SERIAL_PROTOCOLPAIR(" Z2_MIN:", !!TEST(live_state_local, Z2_MIN));#endif#if HAS_Z2_MAXif (TEST(endstop_change, Z2_MAX)) SERIAL_PROTOCOLPAIR(" Z2_MAX:", !!TEST(live_state_local, Z2_MAX));#endifSERIAL_PROTOCOLPGM("\n\n");analogWrite(LED_PIN, local_LED_status);local_LED_status ^= 255;old_live_state_local = live_state_local;}}#endif // PINS_DEBUGGING#if ENABLED(FILAMENT_WIDTH_SENSOR)uint32_t raw_filwidth_value; // = 0#endifvoid Temperature::readings_ready() {// Update the raw values if they've been read. Else we could be updating them during reading.if (!temp_meas_ready) set_current_temp_raw();// Filament Sensor - can be read any time since IIR filtering is used#if ENABLED(FILAMENT_WIDTH_SENSOR)current_raw_filwidth = raw_filwidth_value >> 10; // Divide to get to 0-16384 range since we used 1/128 IIR filter approach#endifZERO(raw_temp_value);#if HAS_HEATED_BEDraw_temp_bed_value = 0;#endif#if HAS_TEMP_CHAMBERraw_temp_chamber_value = 0;#endif#define TEMPDIR(N) ((HEATER_##N##_RAW_LO_TEMP) > (HEATER_##N##_RAW_HI_TEMP) ? -1 : 1)int constexpr temp_dir[] = {#if ENABLED(HEATER_0_USES_MAX6675)0#elseTEMPDIR(0)#endif#if HOTENDS > 1, TEMPDIR(1)#if HOTENDS > 2, TEMPDIR(2)#if HOTENDS > 3, TEMPDIR(3)#if HOTENDS > 4, TEMPDIR(4)#endif // HOTENDS > 4#endif // HOTENDS > 3#endif // HOTENDS > 2#endif // HOTENDS > 1};for (uint8_t e = 0; e < COUNT(temp_dir); e++) {const int16_t tdir = temp_dir[e], rawtemp = current_temperature_raw[e] * tdir;const bool heater_on = (target_temperature[e] > 0)#if ENABLED(PIDTEMP)|| (soft_pwm_amount[e] > 0)#endif;if (rawtemp > maxttemp_raw[e] * tdir) max_temp_error(e);if (rawtemp < minttemp_raw[e] * tdir && !is_preheating(e) && heater_on) {#ifdef MAX_CONSECUTIVE_LOW_TEMPERATURE_ERROR_ALLOWEDif (++consecutive_low_temperature_error[e] >= MAX_CONSECUTIVE_LOW_TEMPERATURE_ERROR_ALLOWED)#endifmin_temp_error(e);}#ifdef MAX_CONSECUTIVE_LOW_TEMPERATURE_ERROR_ALLOWEDelseconsecutive_low_temperature_error[e] = 0;#endif}#if HAS_HEATED_BED#if HEATER_BED_RAW_LO_TEMP > HEATER_BED_RAW_HI_TEMP#define GEBED <=#else#define GEBED >=#endifconst bool bed_on = (target_temperature_bed > 0)#if ENABLED(PIDTEMPBED)|| (soft_pwm_amount_bed > 0)#endif;if (current_temperature_bed_raw GEBED bed_maxttemp_raw) max_temp_error(-1);if (bed_minttemp_raw GEBED current_temperature_bed_raw && bed_on) min_temp_error(-1);#endif}/*** Timer 0 is shared with millis so don't change the prescaler.** This ISR uses the compare method so it runs at the base* frequency (16 MHz / 64 / 256 = 976.5625 Hz), but at the TCNT0 set* in OCR0B above (128 or halfway between OVFs).** - Manage PWM to all the heaters and fan* - Prepare or Measure one of the raw ADC sensor values* - Check new temperature values for MIN/MAX errors (kill on error)* - Step the babysteps value for each axis towards 0* - For PINS_DEBUGGING, monitor and report endstop pins* - For ENDSTOP_INTERRUPTS_FEATURE check endstops if flagged* - Call planner.tick to count down its "ignore" time*/HAL_TEMP_TIMER_ISR {HAL_timer_isr_prologue(TEMP_TIMER_NUM);Temperature::isr();HAL_timer_isr_epilogue(TEMP_TIMER_NUM);}void Temperature::isr() {static int8_t temp_count = -1;static ADCSensorState adc_sensor_state = StartupDelay;static uint8_t pwm_count = _BV(SOFT_PWM_SCALE);// avoid multiple loads of pwm_countuint8_t pwm_count_tmp = pwm_count;#if ENABLED(ADC_KEYPAD)static unsigned int raw_ADCKey_value = 0;#endif// Static members for each heater#if ENABLED(SLOW_PWM_HEATERS)static uint8_t slow_pwm_count = 0;#define ISR_STATICS(n) \static uint8_t soft_pwm_count_ ## n, \state_heater_ ## n = 0, \state_timer_heater_ ## n = 0#else#define ISR_STATICS(n) static uint8_t soft_pwm_count_ ## n = 0#endif// Statics per heaterISR_STATICS(0);#if HOTENDS > 1ISR_STATICS(1);#if HOTENDS > 2ISR_STATICS(2);#if HOTENDS > 3ISR_STATICS(3);#if HOTENDS > 4ISR_STATICS(4);#endif // HOTENDS > 4#endif // HOTENDS > 3#endif // HOTENDS > 2#endif // HOTENDS > 1#if HAS_HEATED_BEDISR_STATICS(BED);#endif#if DISABLED(SLOW_PWM_HEATERS)constexpr uint8_t pwm_mask =#if ENABLED(SOFT_PWM_DITHER)_BV(SOFT_PWM_SCALE) - 1#else0#endif;/*** Standard PWM modulation*/if (pwm_count_tmp >= 127) {pwm_count_tmp -= 127;soft_pwm_count_0 = (soft_pwm_count_0 & pwm_mask) + soft_pwm_amount[0];WRITE_HEATER_0(soft_pwm_count_0 > pwm_mask ? HIGH : LOW);#if HOTENDS > 1soft_pwm_count_1 = (soft_pwm_count_1 & pwm_mask) + soft_pwm_amount[1];WRITE_HEATER_1(soft_pwm_count_1 > pwm_mask ? HIGH : LOW);#if HOTENDS > 2soft_pwm_count_2 = (soft_pwm_count_2 & pwm_mask) + soft_pwm_amount[2];WRITE_HEATER_2(soft_pwm_count_2 > pwm_mask ? HIGH : LOW);#if HOTENDS > 3soft_pwm_count_3 = (soft_pwm_count_3 & pwm_mask) + soft_pwm_amount[3];WRITE_HEATER_3(soft_pwm_count_3 > pwm_mask ? HIGH : LOW);#if HOTENDS > 4soft_pwm_count_4 = (soft_pwm_count_4 & pwm_mask) + soft_pwm_amount[4];WRITE_HEATER_4(soft_pwm_count_4 > pwm_mask ? HIGH : LOW);#endif // HOTENDS > 4#endif // HOTENDS > 3#endif // HOTENDS > 2#endif // HOTENDS > 1#if HAS_HEATED_BEDsoft_pwm_count_BED = (soft_pwm_count_BED & pwm_mask) + soft_pwm_amount_bed;WRITE_HEATER_BED(soft_pwm_count_BED > pwm_mask ? HIGH : LOW);#endif#if ENABLED(FAN_SOFT_PWM)#if HAS_FAN0soft_pwm_count_fan[0] = (soft_pwm_count_fan[0] & pwm_mask) + (soft_pwm_amount_fan[0] >> 1);WRITE_FAN(soft_pwm_count_fan[0] > pwm_mask ? HIGH : LOW);#endif#if HAS_FAN1soft_pwm_count_fan[1] = (soft_pwm_count_fan[1] & pwm_mask) + (soft_pwm_amount_fan[1] >> 1);WRITE_FAN1(soft_pwm_count_fan[1] > pwm_mask ? HIGH : LOW);#endif#if HAS_FAN2soft_pwm_count_fan[2] = (soft_pwm_count_fan[2] & pwm_mask) + (soft_pwm_amount_fan[2] >> 1);WRITE_FAN2(soft_pwm_count_fan[2] > pwm_mask ? HIGH : LOW);#endif#endif}else {if (soft_pwm_count_0 <= pwm_count_tmp) WRITE_HEATER_0(LOW);#if HOTENDS > 1if (soft_pwm_count_1 <= pwm_count_tmp) WRITE_HEATER_1(LOW);#if HOTENDS > 2if (soft_pwm_count_2 <= pwm_count_tmp) WRITE_HEATER_2(LOW);#if HOTENDS > 3if (soft_pwm_count_3 <= pwm_count_tmp) WRITE_HEATER_3(LOW);#if HOTENDS > 4if (soft_pwm_count_4 <= pwm_count_tmp) WRITE_HEATER_4(LOW);#endif // HOTENDS > 4#endif // HOTENDS > 3#endif // HOTENDS > 2#endif // HOTENDS > 1#if HAS_HEATED_BEDif (soft_pwm_count_BED <= pwm_count_tmp) WRITE_HEATER_BED(LOW);#endif#if ENABLED(FAN_SOFT_PWM)#if HAS_FAN0if (soft_pwm_count_fan[0] <= pwm_count_tmp) WRITE_FAN(LOW);#endif#if HAS_FAN1if (soft_pwm_count_fan[1] <= pwm_count_tmp) WRITE_FAN1(LOW);#endif#if HAS_FAN2if (soft_pwm_count_fan[2] <= pwm_count_tmp) WRITE_FAN2(LOW);#endif#endif}// SOFT_PWM_SCALE to frequency://// 0: 16000000/64/256/128 = 7.6294 Hz// 1: / 64 = 15.2588 Hz// 2: / 32 = 30.5176 Hz// 3: / 16 = 61.0352 Hz// 4: / 8 = 122.0703 Hz// 5: / 4 = 244.1406 Hzpwm_count = pwm_count_tmp + _BV(SOFT_PWM_SCALE);#else // SLOW_PWM_HEATERS/*** SLOW PWM HEATERS** For relay-driven heaters*/#ifndef MIN_STATE_TIME#define MIN_STATE_TIME 16 // MIN_STATE_TIME * 65.5 = time in milliseconds#endif// Macros for Slow PWM timer logic#define _SLOW_PWM_ROUTINE(NR, src) \soft_pwm_count_ ##NR = src; \if (soft_pwm_count_ ##NR > 0) { \if (state_timer_heater_ ##NR == 0) { \if (state_heater_ ##NR == 0) state_timer_heater_ ##NR = MIN_STATE_TIME; \state_heater_ ##NR = 1; \WRITE_HEATER_ ##NR(1); \} \} \else { \if (state_timer_heater_ ##NR == 0) { \if (state_heater_ ##NR == 1) state_timer_heater_ ##NR = MIN_STATE_TIME; \state_heater_ ##NR = 0; \WRITE_HEATER_ ##NR(0); \} \}#define SLOW_PWM_ROUTINE(n) _SLOW_PWM_ROUTINE(n, soft_pwm_amount[n])#define PWM_OFF_ROUTINE(NR) \if (soft_pwm_count_ ##NR < slow_pwm_count) { \if (state_timer_heater_ ##NR == 0) { \if (state_heater_ ##NR == 1) state_timer_heater_ ##NR = MIN_STATE_TIME; \state_heater_ ##NR = 0; \WRITE_HEATER_ ##NR (0); \} \}if (slow_pwm_count == 0) {SLOW_PWM_ROUTINE(0);#if HOTENDS > 1SLOW_PWM_ROUTINE(1);#if HOTENDS > 2SLOW_PWM_ROUTINE(2);#if HOTENDS > 3SLOW_PWM_ROUTINE(3);#if HOTENDS > 4SLOW_PWM_ROUTINE(4);#endif // HOTENDS > 4#endif // HOTENDS > 3#endif // HOTENDS > 2#endif // HOTENDS > 1#if HAS_HEATED_BED_SLOW_PWM_ROUTINE(BED, soft_pwm_amount_bed); // BED#endif} // slow_pwm_count == 0PWM_OFF_ROUTINE(0);#if HOTENDS > 1PWM_OFF_ROUTINE(1);#if HOTENDS > 2PWM_OFF_ROUTINE(2);#if HOTENDS > 3PWM_OFF_ROUTINE(3);#if HOTENDS > 4PWM_OFF_ROUTINE(4);#endif // HOTENDS > 4#endif // HOTENDS > 3#endif // HOTENDS > 2#endif // HOTENDS > 1#if HAS_HEATED_BEDPWM_OFF_ROUTINE(BED); // BED#endif#if ENABLED(FAN_SOFT_PWM)if (pwm_count_tmp >= 127) {pwm_count_tmp = 0;#if HAS_FAN0soft_pwm_count_fan[0] = soft_pwm_amount_fan[0] >> 1;WRITE_FAN(soft_pwm_count_fan[0] > 0 ? HIGH : LOW);#endif#if HAS_FAN1soft_pwm_count_fan[1] = soft_pwm_amount_fan[1] >> 1;WRITE_FAN1(soft_pwm_count_fan[1] > 0 ? HIGH : LOW);#endif#if HAS_FAN2soft_pwm_count_fan[2] = soft_pwm_amount_fan[2] >> 1;WRITE_FAN2(soft_pwm_count_fan[2] > 0 ? HIGH : LOW);#endif}#if HAS_FAN0if (soft_pwm_count_fan[0] <= pwm_count_tmp) WRITE_FAN(LOW);#endif#if HAS_FAN1if (soft_pwm_count_fan[1] <= pwm_count_tmp) WRITE_FAN1(LOW);#endif#if HAS_FAN2if (soft_pwm_count_fan[2] <= pwm_count_tmp) WRITE_FAN2(LOW);#endif#endif // FAN_SOFT_PWM// SOFT_PWM_SCALE to frequency://// 0: 16000000/64/256/128 = 7.6294 Hz// 1: / 64 = 15.2588 Hz// 2: / 32 = 30.5176 Hz// 3: / 16 = 61.0352 Hz// 4: / 8 = 122.0703 Hz// 5: / 4 = 244.1406 Hzpwm_count = pwm_count_tmp + _BV(SOFT_PWM_SCALE);// increment slow_pwm_count only every 64th pwm_count,// i.e. yielding a PWM frequency of 16/128 Hz (8s).if (((pwm_count >> SOFT_PWM_SCALE) & 0x3F) == 0) {slow_pwm_count++;slow_pwm_count &= 0x7F;if (state_timer_heater_0 > 0) state_timer_heater_0--;#if HOTENDS > 1if (state_timer_heater_1 > 0) state_timer_heater_1--;#if HOTENDS > 2if (state_timer_heater_2 > 0) state_timer_heater_2--;#if HOTENDS > 3if (state_timer_heater_3 > 0) state_timer_heater_3--;#if HOTENDS > 4if (state_timer_heater_4 > 0) state_timer_heater_4--;#endif // HOTENDS > 4#endif // HOTENDS > 3#endif // HOTENDS > 2#endif // HOTENDS > 1#if HAS_HEATED_BEDif (state_timer_heater_BED > 0) state_timer_heater_BED--;#endif} // ((pwm_count >> SOFT_PWM_SCALE) & 0x3F) == 0#endif // SLOW_PWM_HEATERS//// Update lcd buttons 488 times per second//static bool do_buttons;if ((do_buttons ^= true)) lcd_buttons_update();/*** One sensor is sampled on every other call of the ISR.* Each sensor is read 16 (OVERSAMPLENR) times, taking the average.** On each Prepare pass, ADC is started for a sensor pin.* On the next pass, the ADC value is read and accumulated.** This gives each ADC 0.9765ms to charge up.*/#define ACCUMULATE_ADC(var) do{ \if (!HAL_ADC_READY()) next_sensor_state = adc_sensor_state; \else var += HAL_READ_ADC(); \}while(0)ADCSensorState next_sensor_state = adc_sensor_state < SensorsReady ? (ADCSensorState)(int(adc_sensor_state) + 1) : StartSampling;switch (adc_sensor_state) {case SensorsReady: {// All sensors have been read. Stay in this state for a few// ISRs to save on calls to temp update/checking code below.constexpr int8_t extra_loops = MIN_ADC_ISR_LOOPS - (int8_t)SensorsReady;static uint8_t delay_count = 0;if (extra_loops > 0) {if (delay_count == 0) delay_count = extra_loops; // Init this delayif (--delay_count) // While delaying...next_sensor_state = SensorsReady; // retain this state (else, next state will be 0)break;}else {adc_sensor_state = StartSampling; // Fall-through to start samplingnext_sensor_state = (ADCSensorState)(int(StartSampling) + 1);}}case StartSampling: // Start of sampling loops. Do updates/checks.if (++temp_count >= OVERSAMPLENR) { // 10 * 16 * 1/(16000000/64/256) = 164ms.temp_count = 0;readings_ready();}break;#if HAS_TEMP_ADC_0case PrepareTemp_0:HAL_START_ADC(TEMP_0_PIN);break;case MeasureTemp_0:ACCUMULATE_ADC(raw_temp_value[0]);break;#endif#if HAS_HEATED_BEDcase PrepareTemp_BED:HAL_START_ADC(TEMP_BED_PIN);break;case MeasureTemp_BED:ACCUMULATE_ADC(raw_temp_bed_value);break;#endif#if HAS_TEMP_CHAMBERcase PrepareTemp_CHAMBER:HAL_START_ADC(TEMP_CHAMBER_PIN);break;case MeasureTemp_CHAMBER:ACCUMULATE_ADC(raw_temp_chamber_value);break;#endif#if HAS_TEMP_ADC_1case PrepareTemp_1:HAL_START_ADC(TEMP_1_PIN);break;case MeasureTemp_1:ACCUMULATE_ADC(raw_temp_value[1]);break;#endif#if HAS_TEMP_ADC_2case PrepareTemp_2:HAL_START_ADC(TEMP_2_PIN);break;case MeasureTemp_2:ACCUMULATE_ADC(raw_temp_value[2]);break;#endif#if HAS_TEMP_ADC_3case PrepareTemp_3:HAL_START_ADC(TEMP_3_PIN);break;case MeasureTemp_3:ACCUMULATE_ADC(raw_temp_value[3]);break;#endif#if HAS_TEMP_ADC_4case PrepareTemp_4:HAL_START_ADC(TEMP_4_PIN);break;case MeasureTemp_4:ACCUMULATE_ADC(raw_temp_value[4]);break;#endif#if ENABLED(FILAMENT_WIDTH_SENSOR)case Prepare_FILWIDTH:HAL_START_ADC(FILWIDTH_PIN);break;case Measure_FILWIDTH:if (!HAL_ADC_READY())next_sensor_state = adc_sensor_state; // redo this stateelse if (HAL_READ_ADC() > 102) { // Make sure ADC is reading > 0.5 volts, otherwise don't read.raw_filwidth_value -= raw_filwidth_value >> 7; // Subtract 1/128th of the raw_filwidth_valueraw_filwidth_value += uint32_t(HAL_READ_ADC()) << 7; // Add new ADC reading, scaled by 128}break;#endif#if ENABLED(ADC_KEYPAD)case Prepare_ADC_KEY:HAL_START_ADC(ADC_KEYPAD_PIN);break;case Measure_ADC_KEY:if (!HAL_ADC_READY())next_sensor_state = adc_sensor_state; // redo this stateelse if (ADCKey_count < 16) {raw_ADCKey_value = HAL_READ_ADC();if (raw_ADCKey_value > 900) {//ADC Key releaseADCKey_count = 0;current_ADCKey_raw = 0;}else {current_ADCKey_raw += raw_ADCKey_value;ADCKey_count++;}}break;#endif // ADC_KEYPADcase StartupDelay: break;} // switch(adc_sensor_state)// Go to the next stateadc_sensor_state = next_sensor_state;//// Additional ~1KHz Tasks//#if ENABLED(BABYSTEPPING)LOOP_XYZ(axis) {const int curTodo = babystepsTodo[axis]; // get rid of volatile for performanceif (curTodo) {stepper.babystep((AxisEnum)axis, curTodo > 0);if (curTodo > 0) babystepsTodo[axis]--;else babystepsTodo[axis]++;}}#endif // BABYSTEPPING// Poll endstops state, if requiredendstops.poll();// Periodically call the planner timerplanner.tick();}#if HAS_TEMP_SENSORvoid print_heater_state(const float &c, const float &t,#if ENABLED(SHOW_TEMP_ADC_VALUES)const float r,#endifconst int8_t e=-3) {#if !(HAS_HEATED_BED && HAS_TEMP_HOTEND && HAS_TEMP_CHAMBER) && HOTENDS <= 1UNUSED(e);#endifSERIAL_PROTOCOLCHAR(' ');SERIAL_PROTOCOLCHAR(#if HAS_TEMP_CHAMBER && HAS_HEATED_BED && HAS_TEMP_HOTENDe == -2 ? 'C' : e == -1 ? 'B' : 'T'#elif HAS_HEATED_BED && HAS_TEMP_HOTENDe == -1 ? 'B' : 'T'#elif HAS_TEMP_HOTEND'T'#else'B'#endif);#if HOTENDS > 1if (e >= 0) SERIAL_PROTOCOLCHAR('0' + e);#endifSERIAL_PROTOCOLCHAR(':');SERIAL_PROTOCOL(c);SERIAL_PROTOCOLPAIR(" /" , t);#if ENABLED(SHOW_TEMP_ADC_VALUES)SERIAL_PROTOCOLPAIR(" (", r / OVERSAMPLENR);SERIAL_PROTOCOLCHAR(')');#endifdelay(2);}extern uint8_t target_extruder;void Temperature::print_heaterstates() {#if HAS_TEMP_HOTENDprint_heater_state(degHotend(target_extruder), degTargetHotend(target_extruder)#if ENABLED(SHOW_TEMP_ADC_VALUES), rawHotendTemp(target_extruder)#endif);#endif#if HAS_HEATED_BEDprint_heater_state(degBed(), degTargetBed()#if ENABLED(SHOW_TEMP_ADC_VALUES), rawBedTemp()#endif, -1 // BED);#endif#if HAS_TEMP_CHAMBERprint_heater_state(degChamber(), 0#if ENABLED(SHOW_TEMP_ADC_VALUES), rawChamberTemp()#endif, -2 // CHAMBER);#endif#if HOTENDS > 1HOTEND_LOOP() print_heater_state(degHotend(e), degTargetHotend(e)#if ENABLED(SHOW_TEMP_ADC_VALUES), rawHotendTemp(e)#endif, e);#endifSERIAL_PROTOCOLPGM(" @:");SERIAL_PROTOCOL(getHeaterPower(target_extruder));#if HAS_HEATED_BEDSERIAL_PROTOCOLPGM(" B@:");SERIAL_PROTOCOL(getHeaterPower(-1));#endif#if HOTENDS > 1HOTEND_LOOP() {SERIAL_PROTOCOLPAIR(" @", e);SERIAL_PROTOCOLCHAR(':');SERIAL_PROTOCOL(getHeaterPower(e));}#endif}#if ENABLED(AUTO_REPORT_TEMPERATURES)uint8_t Temperature::auto_report_temp_interval;millis_t Temperature::next_temp_report_ms;void Temperature::auto_report_temperatures() {if (auto_report_temp_interval && ELAPSED(millis(), next_temp_report_ms)) {next_temp_report_ms = millis() + 1000UL * auto_report_temp_interval;print_heaterstates();SERIAL_EOL();}}#endif // AUTO_REPORT_TEMPERATURES#endif // HAS_TEMP_SENSOR