diff --git a/Marlin/temperature.cpp b/Marlin/temperature.cpp index 10a503472..f5a9e39ff 100644 --- a/Marlin/temperature.cpp +++ b/Marlin/temperature.cpp @@ -48,6 +48,145 @@ Temperature thermalManager; +// public: + +int Temperature::current_temperature_raw[EXTRUDERS] = { 0 }; +float Temperature::current_temperature[EXTRUDERS] = { 0.0 }; +int Temperature::target_temperature[EXTRUDERS] = { 0 }; + +int Temperature::current_temperature_bed_raw = 0; +float Temperature::current_temperature_bed = 0.0; +int Temperature::target_temperature_bed = 0; + +#if ENABLED(TEMP_SENSOR_1_AS_REDUNDANT) + float Temperature::redundant_temperature = 0.0; +#endif + +unsigned char Temperature::soft_pwm_bed; + +#if ENABLED(FAN_SOFT_PWM) + unsigned char Temperature::fanSpeedSoftPwm[FAN_COUNT]; +#endif + +#if ENABLED(PIDTEMP) + #if ENABLED(PID_PARAMS_PER_EXTRUDER) + float Temperature::Kp[EXTRUDERS] = ARRAY_BY_EXTRUDERS1(DEFAULT_Kp), + Temperature::Ki[EXTRUDERS] = ARRAY_BY_EXTRUDERS1((DEFAULT_Ki) * (PID_dT)), + Temperature::Kd[EXTRUDERS] = ARRAY_BY_EXTRUDERS1((DEFAULT_Kd) / (PID_dT)); + #if ENABLED(PID_ADD_EXTRUSION_RATE) + float Temperature::Kc[EXTRUDERS] = ARRAY_BY_EXTRUDERS1(DEFAULT_Kc); + #endif + #else + float Temperature::Kp = DEFAULT_Kp, + Temperature::Ki = (DEFAULT_Ki) * (PID_dT), + Temperature::Kd = (DEFAULT_Kd) / (PID_dT); + #if ENABLED(PID_ADD_EXTRUSION_RATE) + float Temperature::Kc = DEFAULT_Kc; + #endif + #endif +#endif + +#if ENABLED(PIDTEMPBED) + float Temperature::bedKp = DEFAULT_bedKp, + Temperature::bedKi = ((DEFAULT_bedKi) * PID_dT), + Temperature::bedKd = ((DEFAULT_bedKd) / PID_dT); +#endif + +#if ENABLED(BABYSTEPPING) + volatile int Temperature::babystepsTodo[3] = { 0 }; +#endif + +#if ENABLED(THERMAL_PROTECTION_HOTENDS) && WATCH_TEMP_PERIOD > 0 + int Temperature::watch_target_temp[EXTRUDERS] = { 0 }; + millis_t Temperature::watch_heater_next_ms[EXTRUDERS] = { 0 }; +#endif + +#if ENABLED(THERMAL_PROTECTION_HOTENDS) && WATCH_BED_TEMP_PERIOD > 0 + int Temperature::watch_target_bed_temp = 0; + millis_t Temperature::watch_bed_next_ms = 0; +#endif + +#if ENABLED(PREVENT_DANGEROUS_EXTRUDE) + float Temperature::extrude_min_temp = EXTRUDE_MINTEMP; +#endif + +// private: + +#if ENABLED(TEMP_SENSOR_1_AS_REDUNDANT) + int Temperature::redundant_temperature_raw = 0; + float Temperature::redundant_temperature = 0.0; +#endif + +volatile bool Temperature::temp_meas_ready = false; + +#if ENABLED(PIDTEMP) + float Temperature::temp_iState[EXTRUDERS] = { 0 }; + float Temperature::temp_dState[EXTRUDERS] = { 0 }; + float Temperature::pTerm[EXTRUDERS]; + float Temperature::iTerm[EXTRUDERS]; + float Temperature::dTerm[EXTRUDERS]; + + #if ENABLED(PID_ADD_EXTRUSION_RATE) + float Temperature::cTerm[EXTRUDERS]; + long Temperature::last_position[EXTRUDERS]; + long Temperature::lpq[LPQ_MAX_LEN]; + int Temperature::lpq_ptr = 0; + #endif + + float Temperature::pid_error[EXTRUDERS]; + float Temperature::temp_iState_min[EXTRUDERS]; + float Temperature::temp_iState_max[EXTRUDERS]; + bool Temperature::pid_reset[EXTRUDERS]; +#endif + +#if ENABLED(PIDTEMPBED) + float Temperature::temp_iState_bed = { 0 }; + float Temperature::temp_dState_bed = { 0 }; + float Temperature::pTerm_bed; + float Temperature::iTerm_bed; + float Temperature::dTerm_bed; + float Temperature::pid_error_bed; + float Temperature::temp_iState_min_bed; + float Temperature::temp_iState_max_bed; +#else + millis_t Temperature::next_bed_check_ms; +#endif + +unsigned long Temperature::raw_temp_value[4] = { 0 }; +unsigned long Temperature::raw_temp_bed_value = 0; + +// Init min and max temp with extreme values to prevent false errors during startup +int Temperature::minttemp_raw[EXTRUDERS] = ARRAY_BY_EXTRUDERS(HEATER_0_RAW_LO_TEMP , HEATER_1_RAW_LO_TEMP , HEATER_2_RAW_LO_TEMP, HEATER_3_RAW_LO_TEMP); +int Temperature::maxttemp_raw[EXTRUDERS] = ARRAY_BY_EXTRUDERS(HEATER_0_RAW_HI_TEMP , HEATER_1_RAW_HI_TEMP , HEATER_2_RAW_HI_TEMP, HEATER_3_RAW_HI_TEMP); +int Temperature::minttemp[EXTRUDERS] = { 0 }; +int Temperature::maxttemp[EXTRUDERS] = ARRAY_BY_EXTRUDERS1(16383); + +#ifdef BED_MINTEMP + int Temperature::bed_minttemp_raw = HEATER_BED_RAW_LO_TEMP; +#endif + +#ifdef BED_MAXTEMP + int Temperature::bed_maxttemp_raw = HEATER_BED_RAW_HI_TEMP; +#endif + +#if ENABLED(FILAMENT_WIDTH_SENSOR) + int Temperature::meas_shift_index; // Index of a delayed sample in buffer +#endif + +#if HAS_AUTO_FAN + millis_t Temperature::next_auto_fan_check_ms; +#endif + +unsigned char Temperature::soft_pwm[EXTRUDERS]; + +#if ENABLED(FAN_SOFT_PWM) + unsigned char Temperature::soft_pwm_fan[FAN_COUNT]; +#endif + +#if ENABLED(FILAMENT_WIDTH_SENSOR) + int Temperature::current_raw_filwidth = 0; //Holds measured filament diameter - one extruder only +#endif + #if HAS_PID_HEATING void Temperature::PID_autotune(float temp, int extruder, int ncycles, bool set_result/*=false*/) { @@ -283,31 +422,9 @@ Temperature thermalManager; #endif // HAS_PID_HEATING -#if ENABLED(PIDTEMP) - - #if ENABLED(PID_PARAMS_PER_EXTRUDER) - - float Temperature::Kp[EXTRUDERS] = ARRAY_BY_EXTRUDERS1(DEFAULT_Kp), - Temperature::Ki[EXTRUDERS] = ARRAY_BY_EXTRUDERS1((DEFAULT_Ki) * (PID_dT)), - Temperature::Kd[EXTRUDERS] = ARRAY_BY_EXTRUDERS1((DEFAULT_Kd) / (PID_dT)); - - #if ENABLED(PID_ADD_EXTRUSION_RATE) - float Temperature::Kc[EXTRUDERS] = ARRAY_BY_EXTRUDERS1(DEFAULT_Kc); - #endif - - #else - - float Temperature::Kp = DEFAULT_Kp, - Temperature::Ki = (DEFAULT_Ki) * (PID_dT), - Temperature::Kd = (DEFAULT_Kd) / (PID_dT); - - #if ENABLED(PID_ADD_EXTRUSION_RATE) - float Temperature::Kc = DEFAULT_Kc; - #endif - - #endif - -#endif +/** + * Class and Instance Methods + */ Temperature::Temperature() { } @@ -1045,7 +1162,17 @@ void Temperature::init() { #if ENABLED(THERMAL_PROTECTION_HOTENDS) || HAS_THERMALLY_PROTECTED_BED - void Temperature::thermal_runaway_protection(TRState* state, millis_t* timer, float temperature, float target_temperature, int heater_id, int period_seconds, int hysteresis_degc) { + #if ENABLED(THERMAL_PROTECTION_HOTENDS) + Temperature::TRState Temperature::thermal_runaway_state_machine[EXTRUDERS] = { TRInactive }; + millis_t Temperature::thermal_runaway_timer[EXTRUDERS] = { 0 }; + #endif + + #if HAS_THERMALLY_PROTECTED_BED + Temperature::TRState Temperature::thermal_runaway_bed_state_machine = TRInactive; + millis_t Temperature::thermal_runaway_bed_timer; + #endif + + void Temperature::thermal_runaway_protection(Temperature::TRState* state, millis_t* timer, float temperature, float target_temperature, int heater_id, int period_seconds, int hysteresis_degc) { static float tr_target_temperature[EXTRUDERS + 1] = { 0.0 }; @@ -1240,7 +1367,7 @@ void Temperature::set_current_temp_raw() { * - Check new temperature values for MIN/MAX errors * - Step the babysteps value for each axis towards 0 */ -ISR(TIMER0_COMPB_vect) { thermalManager.isr(); } +ISR(TIMER0_COMPB_vect) { Temperature::isr(); } void Temperature::isr() { diff --git a/Marlin/temperature.h b/Marlin/temperature.h index 1e9276a80..da5c01f3d 100644 --- a/Marlin/temperature.h +++ b/Marlin/temperature.h @@ -42,22 +42,22 @@ class Temperature { public: - int current_temperature_raw[EXTRUDERS] = { 0 }; - float current_temperature[EXTRUDERS] = { 0.0 }; - int target_temperature[EXTRUDERS] = { 0 }; + static int current_temperature_raw[EXTRUDERS]; + static float current_temperature[EXTRUDERS]; + static int target_temperature[EXTRUDERS]; - int current_temperature_bed_raw = 0; - float current_temperature_bed = 0.0; - int target_temperature_bed = 0; + static int current_temperature_bed_raw; + static float current_temperature_bed; + static int target_temperature_bed; #if ENABLED(TEMP_SENSOR_1_AS_REDUNDANT) - float redundant_temperature = 0.0; + static float redundant_temperature; #endif - unsigned char soft_pwm_bed; + static unsigned char soft_pwm_bed; #if ENABLED(FAN_SOFT_PWM) - unsigned char fanSpeedSoftPwm[FAN_COUNT]; + static unsigned char fanSpeedSoftPwm[FAN_COUNT]; #endif #if ENABLED(PIDTEMP) || ENABLED(PIDTEMPBED) @@ -70,7 +70,7 @@ class Temperature { static float Kp[EXTRUDERS], Ki[EXTRUDERS], Kd[EXTRUDERS]; #if ENABLED(PID_ADD_EXTRUSION_RATE) - float Kc[EXTRUDERS]; + static float Kc[EXTRUDERS]; #endif #define PID_PARAM(param, e) Temperature::param[e] @@ -93,117 +93,109 @@ class Temperature { #endif #if ENABLED(PIDTEMPBED) - float bedKp = DEFAULT_bedKp, - bedKi = ((DEFAULT_bedKi) * PID_dT), - bedKd = ((DEFAULT_bedKd) / PID_dT); + static float bedKp, bedKi, bedKd; #endif #if ENABLED(BABYSTEPPING) - volatile int babystepsTodo[3] = { 0 }; + static volatile int babystepsTodo[3]; #endif #if ENABLED(THERMAL_PROTECTION_HOTENDS) && WATCH_TEMP_PERIOD > 0 - int watch_target_temp[EXTRUDERS] = { 0 }; - millis_t watch_heater_next_ms[EXTRUDERS] = { 0 }; + static int watch_target_temp[EXTRUDERS]; + static millis_t watch_heater_next_ms[EXTRUDERS]; #endif #if ENABLED(THERMAL_PROTECTION_HOTENDS) && WATCH_BED_TEMP_PERIOD > 0 - int watch_target_bed_temp = 0; - millis_t watch_bed_next_ms = 0; + static int watch_target_bed_temp; + static millis_t watch_bed_next_ms; #endif #if ENABLED(PREVENT_DANGEROUS_EXTRUDE) - float extrude_min_temp = EXTRUDE_MINTEMP; - FORCE_INLINE bool tooColdToExtrude(uint8_t e) { return degHotend(e) < extrude_min_temp; } + static float extrude_min_temp; + static FORCE_INLINE bool tooColdToExtrude(uint8_t e) { return degHotend(e) < extrude_min_temp; } #else - FORCE_INLINE bool tooColdToExtrude(uint8_t e) { UNUSED(e); return false; } + static FORCE_INLINE bool tooColdToExtrude(uint8_t e) { UNUSED(e); return false; } #endif private: #if ENABLED(TEMP_SENSOR_1_AS_REDUNDANT) - int redundant_temperature_raw = 0; - float redundant_temperature = 0.0; + static int redundant_temperature_raw; + static float redundant_temperature; #endif - volatile bool temp_meas_ready = false; + static volatile bool temp_meas_ready; #if ENABLED(PIDTEMP) - float temp_iState[EXTRUDERS] = { 0 }; - float temp_dState[EXTRUDERS] = { 0 }; - float pTerm[EXTRUDERS]; - float iTerm[EXTRUDERS]; - float dTerm[EXTRUDERS]; + static float temp_iState[EXTRUDERS]; + static float temp_dState[EXTRUDERS]; + static float pTerm[EXTRUDERS]; + static float iTerm[EXTRUDERS]; + static float dTerm[EXTRUDERS]; #if ENABLED(PID_ADD_EXTRUSION_RATE) - float cTerm[EXTRUDERS]; - long last_position[EXTRUDERS]; - long lpq[LPQ_MAX_LEN]; - int lpq_ptr = 0; + static float cTerm[EXTRUDERS]; + static long last_position[EXTRUDERS]; + static long lpq[LPQ_MAX_LEN]; + static int lpq_ptr; #endif - float pid_error[EXTRUDERS]; - float temp_iState_min[EXTRUDERS]; - float temp_iState_max[EXTRUDERS]; - bool pid_reset[EXTRUDERS]; + static float pid_error[EXTRUDERS]; + static float temp_iState_min[EXTRUDERS]; + static float temp_iState_max[EXTRUDERS]; + static bool pid_reset[EXTRUDERS]; #endif #if ENABLED(PIDTEMPBED) - float temp_iState_bed = { 0 }; - float temp_dState_bed = { 0 }; - float pTerm_bed; - float iTerm_bed; - float dTerm_bed; - float pid_error_bed; - float temp_iState_min_bed; - float temp_iState_max_bed; + static float temp_iState_bed; + static float temp_dState_bed; + 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 - millis_t next_bed_check_ms; + static millis_t next_bed_check_ms; #endif - unsigned long raw_temp_value[4] = { 0 }; - unsigned long raw_temp_bed_value = 0; + static unsigned long raw_temp_value[4]; + static unsigned long raw_temp_bed_value; // Init min and max temp with extreme values to prevent false errors during startup - int minttemp_raw[EXTRUDERS] = ARRAY_BY_EXTRUDERS(HEATER_0_RAW_LO_TEMP , HEATER_1_RAW_LO_TEMP , HEATER_2_RAW_LO_TEMP, HEATER_3_RAW_LO_TEMP); - int maxttemp_raw[EXTRUDERS] = ARRAY_BY_EXTRUDERS(HEATER_0_RAW_HI_TEMP , HEATER_1_RAW_HI_TEMP , HEATER_2_RAW_HI_TEMP, HEATER_3_RAW_HI_TEMP); - int minttemp[EXTRUDERS] = { 0 }; - int maxttemp[EXTRUDERS] = ARRAY_BY_EXTRUDERS1(16383); + static int minttemp_raw[EXTRUDERS]; + static int maxttemp_raw[EXTRUDERS]; + static int minttemp[EXTRUDERS]; + static int maxttemp[EXTRUDERS]; #ifdef BED_MINTEMP - int bed_minttemp_raw = HEATER_BED_RAW_LO_TEMP; + static int bed_minttemp_raw; #endif #ifdef BED_MAXTEMP - int bed_maxttemp_raw = HEATER_BED_RAW_HI_TEMP; + static int bed_maxttemp_raw; #endif #if ENABLED(FILAMENT_WIDTH_SENSOR) - int meas_shift_index; // Index of a delayed sample in buffer + static int meas_shift_index; // Index of a delayed sample in buffer #endif #if HAS_AUTO_FAN - millis_t next_auto_fan_check_ms; + static millis_t next_auto_fan_check_ms; #endif - unsigned char soft_pwm[EXTRUDERS]; + static unsigned char soft_pwm[EXTRUDERS]; #if ENABLED(FAN_SOFT_PWM) - unsigned char soft_pwm_fan[FAN_COUNT]; + static unsigned char soft_pwm_fan[FAN_COUNT]; #endif #if ENABLED(FILAMENT_WIDTH_SENSOR) - int current_raw_filwidth = 0; //Holds measured filament diameter - one extruder only + static int current_raw_filwidth; //Holds measured filament diameter - one extruder only #endif public: - /** - * Static (class) methods - */ - static float analog2temp(int raw, uint8_t e); - static float analog2tempBed(int raw); - /** * Instance Methods */ @@ -212,19 +204,25 @@ class Temperature { void init(); + /** + * Static (class) methods + */ + static float analog2temp(int raw, uint8_t e); + static float analog2tempBed(int raw); + /** * Called from the Temperature ISR */ - void isr(); + static void isr(); /** * Call periodically to manage heaters */ - void manage_heater(); + static void manage_heater(); #if ENABLED(FILAMENT_WIDTH_SENSOR) - float analog2widthFil(); // Convert raw Filament Width to millimeters - int widthFil_to_size_ratio(); // Convert raw Filament Width to an extrusion ratio + static float analog2widthFil(); // Convert raw Filament Width to millimeters + static int widthFil_to_size_ratio(); // Convert raw Filament Width to an extrusion ratio #endif @@ -232,68 +230,68 @@ class Temperature { //inline so that there is no performance decrease. //deg=degreeCelsius - FORCE_INLINE float degHotend(uint8_t extruder) { return current_temperature[extruder]; } - FORCE_INLINE float degBed() { return current_temperature_bed; } + static FORCE_INLINE float degHotend(uint8_t extruder) { return current_temperature[extruder]; } + static FORCE_INLINE float degBed() { return current_temperature_bed; } #if ENABLED(SHOW_TEMP_ADC_VALUES) - FORCE_INLINE float rawHotendTemp(uint8_t extruder) { return current_temperature_raw[extruder]; } - FORCE_INLINE float rawBedTemp() { return current_temperature_bed_raw; } + static FORCE_INLINE float rawHotendTemp(uint8_t extruder) { return current_temperature_raw[extruder]; } + static FORCE_INLINE float rawBedTemp() { return current_temperature_bed_raw; } #endif - FORCE_INLINE float degTargetHotend(uint8_t extruder) { return target_temperature[extruder]; } - FORCE_INLINE float degTargetBed() { return target_temperature_bed; } + static FORCE_INLINE float degTargetHotend(uint8_t extruder) { return target_temperature[extruder]; } + static FORCE_INLINE float degTargetBed() { return target_temperature_bed; } #if ENABLED(THERMAL_PROTECTION_HOTENDS) && WATCH_TEMP_PERIOD > 0 - void start_watching_heater(int e = 0); + static void start_watching_heater(int e = 0); #endif #if ENABLED(THERMAL_PROTECTION_BED) && WATCH_BED_TEMP_PERIOD > 0 - void start_watching_bed(); + static void start_watching_bed(); #endif - FORCE_INLINE void setTargetHotend(const float& celsius, uint8_t extruder) { + static FORCE_INLINE void setTargetHotend(const float& celsius, uint8_t extruder) { target_temperature[extruder] = celsius; #if ENABLED(THERMAL_PROTECTION_HOTENDS) && WATCH_TEMP_PERIOD > 0 start_watching_heater(extruder); #endif } - FORCE_INLINE void setTargetBed(const float& celsius) { + static FORCE_INLINE void setTargetBed(const float& celsius) { target_temperature_bed = celsius; #if ENABLED(THERMAL_PROTECTION_BED) && WATCH_BED_TEMP_PERIOD > 0 start_watching_bed(); #endif } - FORCE_INLINE bool isHeatingHotend(uint8_t extruder) { return target_temperature[extruder] > current_temperature[extruder]; } - FORCE_INLINE bool isHeatingBed() { return target_temperature_bed > current_temperature_bed; } + static FORCE_INLINE bool isHeatingHotend(uint8_t extruder) { return target_temperature[extruder] > current_temperature[extruder]; } + static FORCE_INLINE bool isHeatingBed() { return target_temperature_bed > current_temperature_bed; } - FORCE_INLINE bool isCoolingHotend(uint8_t extruder) { return target_temperature[extruder] < current_temperature[extruder]; } - FORCE_INLINE bool isCoolingBed() { return target_temperature_bed < current_temperature_bed; } + static FORCE_INLINE bool isCoolingHotend(uint8_t extruder) { return target_temperature[extruder] < current_temperature[extruder]; } + static FORCE_INLINE bool isCoolingBed() { return target_temperature_bed < current_temperature_bed; } /** * The software PWM power for a heater */ - int getHeaterPower(int heater); + static int getHeaterPower(int heater); /** * Switch off all heaters, set all target temperatures to 0 */ - void disable_all_heaters(); + static void disable_all_heaters(); /** * Perform auto-tuning for hotend or bed in response to M303 */ #if HAS_PID_HEATING - void PID_autotune(float temp, int extruder, int ncycles, bool set_result=false); + static void PID_autotune(float temp, int extruder, int ncycles, bool set_result=false); #endif /** * Update the temp manager when PID values change */ - void updatePID(); + static void updatePID(); - FORCE_INLINE void autotempShutdown() { + static FORCE_INLINE void autotempShutdown() { #if ENABLED(AUTOTEMP) if (planner.autotemp_enabled) { planner.autotemp_enabled = false; @@ -305,7 +303,7 @@ class Temperature { #if ENABLED(BABYSTEPPING) - FORCE_INLINE void babystep_axis(AxisEnum axis, int distance) { + static FORCE_INLINE void babystep_axis(AxisEnum axis, int distance) { #if ENABLED(COREXY) || ENABLED(COREXZ) || ENABLED(COREYZ) #if ENABLED(BABYSTEP_XY) switch (axis) { @@ -337,40 +335,40 @@ class Temperature { private: - void set_current_temp_raw(); + static void set_current_temp_raw(); - void updateTemperaturesFromRawValues(); + static void updateTemperaturesFromRawValues(); #if ENABLED(HEATER_0_USES_MAX6675) - int read_max6675(); + static int read_max6675(); #endif - void checkExtruderAutoFans(); + static void checkExtruderAutoFans(); - float get_pid_output(int e); + static float get_pid_output(int e); #if ENABLED(PIDTEMPBED) - float get_pid_output_bed(); + static float get_pid_output_bed(); #endif - void _temp_error(int e, const char* serial_msg, const char* lcd_msg); - void min_temp_error(uint8_t e); - void max_temp_error(uint8_t e); + static void _temp_error(int e, const char* serial_msg, const char* lcd_msg); + static void min_temp_error(uint8_t e); + static void max_temp_error(uint8_t e); #if ENABLED(THERMAL_PROTECTION_HOTENDS) || HAS_THERMALLY_PROTECTED_BED typedef enum TRState { TRInactive, TRFirstHeating, TRStable, TRRunaway } TRstate; - void thermal_runaway_protection(TRState* state, millis_t* timer, float temperature, float target_temperature, int heater_id, int period_seconds, int hysteresis_degc); + static void thermal_runaway_protection(TRState* state, millis_t* timer, float temperature, float target_temperature, int heater_id, int period_seconds, int hysteresis_degc); #if ENABLED(THERMAL_PROTECTION_HOTENDS) - TRState thermal_runaway_state_machine[EXTRUDERS] = { TRInactive }; - millis_t thermal_runaway_timer[EXTRUDERS] = { 0 }; + static TRState thermal_runaway_state_machine[EXTRUDERS]; + static millis_t thermal_runaway_timer[EXTRUDERS]; #endif #if HAS_THERMALLY_PROTECTED_BED - TRState thermal_runaway_bed_state_machine = TRInactive; - millis_t thermal_runaway_bed_timer; + static TRState thermal_runaway_bed_state_machine; + static millis_t thermal_runaway_bed_timer; #endif #endif // THERMAL_PROTECTION