Browse Source

Chamber Heater PID (#21156)

Co-authored-by: Scott Lahteine <thinkyhead@users.noreply.github.com>
Ken Sanislo 3 years ago
parent
commit
a3a10b62f2
No account linked to committer's email address

+ 54
- 1
Marlin/Configuration.h View File

@@ -447,6 +447,10 @@
447 447
 #define TEMP_BED_WINDOW          1  // (°C) Temperature proximity for the "temperature reached" timer
448 448
 #define TEMP_BED_HYSTERESIS      3  // (°C) Temperature proximity considered "close enough" to the target
449 449
 
450
+#define TEMP_CHAMBER_RESIDENCY_TIME 10  // (seconds) Time to wait for chamber to "settle" in M191
451
+#define TEMP_CHAMBER_WINDOW      1  // (°C) Temperature proximity for the "temperature reached" timer
452
+#define TEMP_CHAMBER_HYSTERESIS  3  // (°C) Temperature proximity considered "close enough" to the target
453
+
450 454
 // Below this temperature the heater will be switched off
451 455
 // because it probably indicates a broken thermistor wire.
452 456
 #define HEATER_0_MINTEMP   5
@@ -458,6 +462,7 @@
458 462
 #define HEATER_6_MINTEMP   5
459 463
 #define HEATER_7_MINTEMP   5
460 464
 #define BED_MINTEMP        5
465
+#define CHAMBER_MINTEMP    5
461 466
 
462 467
 // Above this temperature the heater will be switched off.
463 468
 // This can protect components from overheating, but NOT from shorts and failures.
@@ -471,6 +476,7 @@
471 476
 #define HEATER_6_MAXTEMP 275
472 477
 #define HEATER_7_MAXTEMP 275
473 478
 #define BED_MAXTEMP      150
479
+#define CHAMBER_MAXTEMP  60
474 480
 
475 481
 //===========================================================================
476 482
 //============================= PID Settings ================================
@@ -544,7 +550,52 @@
544 550
   // FIND YOUR OWN: "M303 E-1 C8 S90" to run autotune on the bed at 90 degreesC for 8 cycles.
545 551
 #endif // PIDTEMPBED
546 552
 
547
-#if EITHER(PIDTEMP, PIDTEMPBED)
553
+//===========================================================================
554
+//==================== PID > Chamber Temperature Control ====================
555
+//===========================================================================
556
+
557
+/**
558
+ * PID Chamber Heating
559
+ *
560
+ * If this option is enabled set PID constants below.
561
+ * If this option is disabled, bang-bang will be used and CHAMBER_LIMIT_SWITCHING will enable
562
+ * hysteresis.
563
+ *
564
+ * The PID frequency will be the same as the extruder PWM.
565
+ * If PID_dT is the default, and correct for the hardware/configuration, that means 7.689Hz,
566
+ * which is fine for driving a square wave into a resistive load and does not significantly
567
+ * impact FET heating. This also works fine on a Fotek SSR-10DA Solid State Relay into a 200W
568
+ * heater. If your configuration is significantly different than this and you don't understand
569
+ * the issues involved, don't use chamber PID until someone else verifies that your hardware works.
570
+ */
571
+//#define PIDTEMPCHAMBER
572
+
573
+//#define CHAMBER_LIMIT_SWITCHING
574
+
575
+/**
576
+ * Max Chamber Power
577
+ * Applies to all forms of chamber control (PID, bang-bang, and bang-bang with hysteresis).
578
+ * When set to any value below 255, enables a form of PWM to the chamber heater that acts like a divider
579
+ * so don't use it unless you are OK with PWM on your heater. (See the comment on enabling PIDTEMPCHAMBER)
580
+ */
581
+#define MAX_CHAMBER_POWER 255 // limits duty cycle to chamber heater; 255=full current
582
+
583
+#if ENABLED(PIDTEMPCHAMBER)
584
+  #define MIN_CHAMBER_POWER 0
585
+  //#define PID_CHAMBER_DEBUG // Sends debug data to the serial port.
586
+
587
+  // Lasko "MyHeat Personal Heater" (200w) modified with a Fotek SSR-10DA to control only the heating element
588
+  // and placed inside the small Creality printer enclosure tent.
589
+  //
590
+  #define DEFAULT_chamberKp 37.04
591
+  #define DEFAULT_chamberKi 1.40
592
+  #define DEFAULT_chamberKd 655.17
593
+  // M309 P37.04 I1.04 D655.17
594
+
595
+  // FIND YOUR OWN: "M303 E-2 C8 S50" to run autotune on the chamber at 50 degreesC for 8 cycles.
596
+#endif // PIDTEMPCHAMBER
597
+
598
+#if ANY(PIDTEMP, PIDTEMPBED, PIDTEMPCHAMBER)
548 599
   //#define PID_DEBUG             // Sends debug data to the serial port. Use 'M303 D' to toggle activation.
549 600
   //#define PID_OPENLOOP          // Puts PID in open loop. M104/M140 sets the output power from 0 to PID_MAX
550 601
   //#define SLOW_PWM_HEATERS      // PWM with very low frequency (roughly 0.125Hz=8s) and minimum state time of approximately 1s useful for heaters driven by a relay
@@ -1624,11 +1675,13 @@
1624 1675
 #define PREHEAT_1_LABEL       "PLA"
1625 1676
 #define PREHEAT_1_TEMP_HOTEND 180
1626 1677
 #define PREHEAT_1_TEMP_BED     70
1678
+#define PREHEAT_1_TEMP_CHAMBER 35
1627 1679
 #define PREHEAT_1_FAN_SPEED     0 // Value from 0 to 255
1628 1680
 
1629 1681
 #define PREHEAT_2_LABEL       "ABS"
1630 1682
 #define PREHEAT_2_TEMP_HOTEND 240
1631 1683
 #define PREHEAT_2_TEMP_BED    110
1684
+#define PREHEAT_2_TEMP_CHAMBER 35
1632 1685
 #define PREHEAT_2_FAN_SPEED     0 // Value from 0 to 255
1633 1686
 
1634 1687
 /**

+ 12
- 5
Marlin/Configuration_adv.h View File

@@ -143,12 +143,19 @@
143 143
 //
144 144
 // Heated Chamber options
145 145
 //
146
+#if DISABLED(PIDTEMPCHAMBER)
147
+  #define CHAMBER_CHECK_INTERVAL 5000   // (ms) Interval between checks in bang-bang control
148
+  #if ENABLED(CHAMBER_LIMIT_SWITCHING)
149
+    #define CHAMBER_HYSTERESIS 2        // (°C) Only set the relevant heater state when ABS(T-target) > CHAMBER_HYSTERESIS
150
+  #endif
151
+#endif
152
+
146 153
 #if TEMP_SENSOR_CHAMBER
147
-  #define CHAMBER_MINTEMP             5
148
-  #define CHAMBER_MAXTEMP            60
149
-  #define TEMP_CHAMBER_HYSTERESIS     1   // (°C) Temperature proximity considered "close enough" to the target
150
-  //#define CHAMBER_LIMIT_SWITCHING
151
-  //#define HEATER_CHAMBER_PIN       44   // Chamber heater on/off pin
154
+  // Make sure you define where your heater is connected, the following works on a BTT SKR 1.4 Turbo
155
+  // using the secondary tool heater output. (FAN1 by default).
156
+  //#define FAN1_PIN                   -1   // Remove the fan signal on pin P2_04 (SKR 1.4 Turbo specific)
157
+  //#define HEATER_CHAMBER_PIN      P2_04   // Chamber heater on/off pin (HE1 connector on SKR 1.4 Turbo)
158
+
152 159
   //#define HEATER_CHAMBER_INVERTING false
153 160
 
154 161
   //#define CHAMBER_FAN               // Enable a fan on the chamber

+ 1
- 1
Marlin/src/core/language.h View File

@@ -221,7 +221,7 @@
221 221
 
222 222
 // temperature.cpp strings
223 223
 #define STR_PID_AUTOTUNE_START              "PID Autotune start"
224
-#define STR_PID_BAD_EXTRUDER_NUM            "PID Autotune failed! Bad extruder number"
224
+#define STR_PID_BAD_HEATER_ID               "PID Autotune failed! Bad heater id"
225 225
 #define STR_PID_TEMP_TOO_HIGH               "PID Autotune failed! Temperature too high"
226 226
 #define STR_PID_TIMEOUT                     "PID Autotune failed! timeout"
227 227
 #define STR_BIAS                            " bias: "

+ 13
- 0
Marlin/src/feature/leds/printer_event_leds.cpp View File

@@ -77,6 +77,19 @@ PrinterEventLEDs printerEventLEDs;
77 77
       pel_set_rgb(red, 0, 255);
78 78
     }
79 79
   }
80
+
81
+#endif
82
+
83
+#if HAS_HEATED_CHAMBER
84
+
85
+  void PrinterEventLEDs::onChamberHeating(const float &start, const float &current, const float &target) {
86
+    const uint8_t green = pel_intensity(start, current, target);
87
+    if (green != old_intensity) {
88
+      old_intensity = green;
89
+      pel_set_rgb(255, green, 255);
90
+    }
91
+  }
92
+
80 93
 #endif
81 94
 
82 95
 #endif // PRINTER_EVENT_LEDS

+ 6
- 1
Marlin/src/feature/leds/printer_event_leds.h View File

@@ -55,7 +55,12 @@ public:
55 55
     static void onBedHeating(const float &start, const float &current, const float &target);
56 56
   #endif
57 57
 
58
-  #if HAS_TEMP_HOTEND || HAS_HEATED_BED
58
+  #if HAS_HEATED_CHAMBER
59
+    static inline LEDColor onChamberHeatingStart() { old_intensity = 127; return leds.get_color(); }
60
+    static void onChamberHeating(const float &start, const float &current, const float &target);
61
+  #endif
62
+
63
+  #if HAS_TEMP_HOTEND || HAS_HEATED_BED || HAS_HEATED_CHAMBER
59 64
     static inline void onHeatingDone() { leds.set_white(); }
60 65
     static inline void onPidTuningDone(LEDColor c) { leds.set_color(c); }
61 66
   #endif

+ 48
- 0
Marlin/src/gcode/config/M309.cpp View File

@@ -0,0 +1,48 @@
1
+/**
2
+ * Marlin 3D Printer Firmware
3
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
+ *
5
+ * Based on Sprinter and grbl.
6
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
7
+ *
8
+ * This program is free software: you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation, either version 3 of the License, or
11
+ * (at your option) any later version.
12
+ *
13
+ * This program is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20
+ *
21
+ */
22
+
23
+#include "../../inc/MarlinConfig.h"
24
+
25
+#if ENABLED(PIDTEMPCHAMBER)
26
+
27
+#include "../gcode.h"
28
+#include "../../module/temperature.h"
29
+
30
+/**
31
+ * M309 - Set and/or Report the current Chamber PID values
32
+ *
33
+ *  P<pval> - Set the P value
34
+ *  I<ival> - Set the I value
35
+ *  D<dval> - Set the D value
36
+ */
37
+void GcodeSuite::M309() {
38
+  if (parser.seen('P')) thermalManager.temp_chamber.pid.Kp = parser.value_float();
39
+  if (parser.seen('I')) thermalManager.temp_chamber.pid.Ki = scalePID_i(parser.value_float());
40
+  if (parser.seen('D')) thermalManager.temp_chamber.pid.Kd = scalePID_d(parser.value_float());
41
+
42
+  SERIAL_ECHO_START();
43
+  SERIAL_ECHOLNPAIR(" p:", thermalManager.temp_chamber.pid.Kp,
44
+                    " i:", unscalePID_i(thermalManager.temp_chamber.pid.Ki),
45
+                    " d:", unscalePID_d(thermalManager.temp_chamber.pid.Kd));
46
+}
47
+
48
+#endif // PIDTEMPCHAMBER

+ 4
- 0
Marlin/src/gcode/gcode.cpp View File

@@ -682,6 +682,10 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) {
682 682
         case 304: M304(); break;                                  // M304: Set bed PID parameters
683 683
       #endif
684 684
 
685
+      #if ENABLED(PIDTEMPCHAMBER)
686
+        case 309: M309(); break;                                  // M309: Set chamber PID parameters
687
+      #endif
688
+
685 689
       #if ENABLED(PHOTO_GCODE)
686 690
         case 240: M240(); break;                                  // M240: Trigger a camera
687 691
       #endif

+ 3
- 0
Marlin/src/gcode/gcode.h View File

@@ -197,6 +197,7 @@
197 197
  * M303 - PID relay autotune S<temperature> sets the target temperature. Default 150C. (Requires PIDTEMP)
198 198
  * M304 - Set bed PID parameters P I and D. (Requires PIDTEMPBED)
199 199
  * M305 - Set user thermistor parameters R T and P. (Requires TEMP_SENSOR_x 1000)
200
+ * M309 - Set chamber PID parameters P I and D. (Requires PIDTEMPCHAMBER)
200 201
  * M350 - Set microstepping mode. (Requires digital microstepping pins.)
201 202
  * M351 - Toggle MS1 MS2 pins directly. (Requires digital microstepping pins.)
202 203
  * M355 - Set Case Light on/off and set brightness. (Requires CASE_LIGHT_PIN)
@@ -711,6 +712,8 @@ private:
711 712
 
712 713
   TERN_(HAS_USER_THERMISTORS, static void M305());
713 714
 
715
+  TERN_(PIDTEMPCHAMBER, static void M309());
716
+
714 717
   #if HAS_MICROSTEPS
715 718
     static void M350();
716 719
     static void M351();

+ 21
- 16
Marlin/src/gcode/temp/M303.cpp View File

@@ -40,19 +40,15 @@
40 40
  *  C<cycles>       Number of times to repeat the procedure. (Minimum: 3, Default: 5)
41 41
  *  U<bool>         Flag to apply the result to the current PID values
42 42
  *
43
- * With PID_DEBUG:
43
+ * With PID_DEBUG, PID_BED_DEBUG, or PID_CHAMBER_DEBUG:
44 44
  *  D               Toggle PID debugging and EXIT without further action.
45 45
  */
46 46
 
47
-#if ENABLED(PID_DEBUG)
48
-  bool pid_debug_flag = 0;
49
-#endif
50
-
51 47
 void GcodeSuite::M303() {
52 48
 
53
-  #if ENABLED(PID_DEBUG)
49
+  #if ANY(PID_DEBUG, PID_BED_DEBUG, PID_CHAMBER_DEBUG)
54 50
     if (parser.seen('D')) {
55
-      pid_debug_flag = !pid_debug_flag;
51
+      thermalManager.pid_debug_flag ^= true;
56 52
       SERIAL_ECHO_START();
57 53
       SERIAL_ECHOPGM("PID Debug ");
58 54
       serialprintln_onoff(pid_debug_flag);
@@ -60,25 +56,34 @@ void GcodeSuite::M303() {
60 56
     }
61 57
   #endif
62 58
 
63
-  #define SI TERN(PIDTEMPBED, H_BED, H_E0)
64
-  #define EI TERN(PIDTEMP, HOTENDS - 1, H_BED)
65
-  const heater_id_t e = (heater_id_t)parser.intval('E');
66
-  if (!WITHIN(e, SI, EI)) {
67
-    SERIAL_ECHOLNPGM(STR_PID_BAD_EXTRUDER_NUM);
68
-    TERN_(EXTENSIBLE_UI, ExtUI::onPidTuning(ExtUI::result_t::PID_BAD_EXTRUDER_NUM));
69
-    return;
59
+  const heater_id_t hid = (heater_id_t)parser.intval('E');
60
+  int16_t default_temp;
61
+  switch (hid) {
62
+    #if ENABLED(PIDTEMP)
63
+      case 0 ... HOTENDS - 1: default_temp = PREHEAT_1_TEMP_HOTEND; break;
64
+    #endif
65
+    #if ENABLED(PIDTEMPBED)
66
+      case H_BED: default_temp = PREHEAT_1_TEMP_BED; break;
67
+    #endif
68
+    #if ENABLED(PIDTEMPCHAMBER)
69
+      case H_CHAMBER: default_temp = PREHEAT_1_TEMP_CHAMBER; break;
70
+    #endif
71
+    default:
72
+      SERIAL_ECHOLNPGM(STR_PID_BAD_HEATER_ID);
73
+      TERN_(EXTENSIBLE_UI, ExtUI::onPidTuning(ExtUI::result_t::PID_BAD_EXTRUDER_NUM));
74
+      return;
70 75
   }
71 76
 
77
+  const int16_t temp = parser.celsiusval('S', default_temp);
72 78
   const int c = parser.intval('C', 5);
73 79
   const bool u = parser.boolval('U');
74
-  const int16_t temp = parser.celsiusval('S', e < 0 ? PREHEAT_1_TEMP_BED : PREHEAT_1_TEMP_HOTEND);
75 80
 
76 81
   #if DISABLED(BUSY_WHILE_HEATING)
77 82
     KEEPALIVE_STATE(NOT_BUSY);
78 83
   #endif
79 84
 
80 85
   LCD_MESSAGEPGM(MSG_PID_AUTOTUNE);
81
-  thermalManager.PID_autotune(temp, e, c, u);
86
+  thermalManager.PID_autotune(temp, hid, c, u);
82 87
   ui.reset_status();
83 88
 }
84 89
 

+ 14
- 7
Marlin/src/inc/Conditionals_post.h View File

@@ -1992,27 +1992,31 @@
1992 1992
     #define BED_OVERSHOOT 10
1993 1993
   #endif
1994 1994
   #define BED_MAX_TARGET (BED_MAXTEMP - (BED_OVERSHOOT))
1995
+#else
1996
+  #undef PIDTEMPBED
1995 1997
 #endif
1998
+
1996 1999
 #if HAS_HEATED_BED || HAS_TEMP_CHAMBER
1997 2000
   #define BED_OR_CHAMBER 1
1998 2001
 #endif
1999 2002
 #if HAS_TEMP_HOTEND || BED_OR_CHAMBER || HAS_TEMP_PROBE
2000 2003
   #define HAS_TEMP_SENSOR 1
2001 2004
 #endif
2005
+
2002 2006
 #if HAS_TEMP_CHAMBER && PIN_EXISTS(HEATER_CHAMBER)
2003 2007
   #define HAS_HEATED_CHAMBER 1
2008
+  #ifndef CHAMBER_OVERSHOOT
2009
+    #define CHAMBER_OVERSHOOT 10
2010
+  #endif
2011
+  #define CHAMBER_MAX_TARGET (CHAMBER_MAXTEMP - (CHAMBER_OVERSHOOT))
2012
+#else
2013
+  #undef PIDTEMPCHAMBER
2004 2014
 #endif
2005 2015
 
2006 2016
 // PID heating
2007
-#if !HAS_HEATED_BED
2008
-  #undef PIDTEMPBED
2009
-#endif
2010
-#if EITHER(PIDTEMP, PIDTEMPBED)
2017
+#if ANY(PIDTEMP, PIDTEMPBED, PIDTEMPCHAMBER)
2011 2018
   #define HAS_PID_HEATING 1
2012 2019
 #endif
2013
-#if BOTH(PIDTEMP, PIDTEMPBED)
2014
-  #define HAS_PID_FOR_BOTH 1
2015
-#endif
2016 2020
 
2017 2021
 // Thermal protection
2018 2022
 #if BOTH(HAS_HEATED_BED, THERMAL_PROTECTION_BED)
@@ -2346,6 +2350,9 @@
2346 2350
  * Heated chamber requires settings
2347 2351
  */
2348 2352
 #if HAS_HEATED_CHAMBER
2353
+  #ifndef MIN_CHAMBER_POWER
2354
+    #define MIN_CHAMBER_POWER 0
2355
+  #endif
2349 2356
   #ifndef MAX_CHAMBER_POWER
2350 2357
     #define MAX_CHAMBER_POWER 255
2351 2358
   #endif

+ 7
- 0
Marlin/src/inc/SanityCheck.h View File

@@ -1204,6 +1204,13 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS
1204 1204
 #endif
1205 1205
 
1206 1206
 /**
1207
+ * Chamber Heating Options - PID vs Limit Switching
1208
+ */
1209
+#if BOTH(PIDTEMPCHAMBER, CHAMBER_LIMIT_SWITCHING)
1210
+  #error "To use CHAMBER_LIMIT_SWITCHING you must disable PIDTEMPCHAMBER."
1211
+#endif
1212
+
1213
+/**
1207 1214
  * Kinematics
1208 1215
  */
1209 1216
 

+ 50
- 29
Marlin/src/lcd/menu/menu_advanced.cpp View File

@@ -177,18 +177,25 @@ void menu_backlash();
177 177
   #if ENABLED(PIDTEMPBED)
178 178
     int16_t autotune_temp_bed = PREHEAT_1_TEMP_BED;
179 179
   #endif
180
+  #if ENABLED(PIDTEMPCHAMBER)
181
+    int16_t autotune_temp_chamber = PREHEAT_1_TEMP_CHAMBER;
182
+  #endif
180 183
 
181 184
   #include "../../gcode/queue.h"
182 185
 
183
-  void _lcd_autotune(const int16_t e) {
186
+  void _lcd_autotune(const heater_id_t hid) {
184 187
     char cmd[30];
185
-    sprintf_P(cmd, PSTR("M303 U1 E%i S%i"), e,
186
-      #if HAS_PID_FOR_BOTH
187
-        e < 0 ? autotune_temp_bed : autotune_temp[e]
188
-      #else
189
-        TERN(PIDTEMPBED, autotune_temp_bed, autotune_temp[e])
188
+    int16_t tune_temp;
189
+    switch (hid) {
190
+      #if ENABLED(PIDTEMPBED)
191
+        case H_BED: tune_temp = autotune_temp_bed; break;
190 192
       #endif
191
-    );
193
+      #if ENABLED(PIDTEMPCHAMBER)
194
+        case H_CHAMBER: tune_temp = autotune_temp_chamber; break;
195
+      #endif
196
+      default: tune_temp = autotune_temp[hid]; break;
197
+    }
198
+    sprintf_P(cmd, PSTR("M303 U1 E%i S%i"), hid, tune_temp);
192 199
     queue.inject(cmd);
193 200
     ui.return_to_status();
194 201
   }
@@ -225,7 +232,7 @@ void menu_backlash();
225 232
 #if ENABLED(PID_AUTOTUNE_MENU)
226 233
   #define DEFINE_PIDTEMP_FUNCS(N) \
227 234
     _DEFINE_PIDTEMP_BASE_FUNCS(N); \
228
-    void lcd_autotune_callback_E##N() { _lcd_autotune(N); }
235
+    void lcd_autotune_callback_E##N() { _lcd_autotune(heater_id_t(N)); }
229 236
 #else
230 237
   #define DEFINE_PIDTEMP_FUNCS(N) _DEFINE_PIDTEMP_BASE_FUNCS(N);
231 238
 #endif
@@ -269,56 +276,70 @@ void menu_backlash();
269 276
     //
270 277
 
271 278
     #if ENABLED(PID_EDIT_MENU)
272
-      #define __PID_BASE_MENU_ITEMS(N) \
273
-        raw_Ki = unscalePID_i(TERN(PID_BED_MENU_SECTION, thermalManager.temp_bed.pid.Ki, PID_PARAM(Ki, N))); \
274
-        raw_Kd = unscalePID_d(TERN(PID_BED_MENU_SECTION, thermalManager.temp_bed.pid.Kd, PID_PARAM(Kd, N))); \
275
-        EDIT_ITEM_FAST_N(float41sign, N, MSG_PID_P_E, &TERN(PID_BED_MENU_SECTION, thermalManager.temp_bed.pid.Kp, PID_PARAM(Kp, N)), 1, 9990); \
279
+      #define _PID_EDIT_ITEMS_TMPL(N,T) \
280
+        raw_Ki = unscalePID_i(T.pid.Ki); \
281
+        raw_Kd = unscalePID_d(T.pid.Kd); \
282
+        EDIT_ITEM_FAST_N(float41sign, N, MSG_PID_P_E, &T.pid.Kp, 1, 9990); \
283
+        EDIT_ITEM_FAST_N(float52sign, N, MSG_PID_I_E, &raw_Ki, 0.01f, 9990, []{ copy_and_scalePID_i(N); }); \
284
+        EDIT_ITEM_FAST_N(float41sign, N, MSG_PID_D_E, &raw_Kd, 1, 9990, []{ copy_and_scalePID_d(N); })
285
+
286
+      #define __PID_HOTEND_MENU_ITEMS(N) \
287
+        raw_Ki = unscalePID_i(PID_PARAM(Ki, N)); \
288
+        raw_Kd = unscalePID_d(PID_PARAM(Kd, N)); \
289
+        EDIT_ITEM_FAST_N(float41sign, N, MSG_PID_P_E, &PID_PARAM(Kp, N), 1, 9990); \
276 290
         EDIT_ITEM_FAST_N(float52sign, N, MSG_PID_I_E, &raw_Ki, 0.01f, 9990, []{ copy_and_scalePID_i(N); }); \
277 291
         EDIT_ITEM_FAST_N(float41sign, N, MSG_PID_D_E, &raw_Kd, 1, 9990, []{ copy_and_scalePID_d(N); })
278 292
 
279 293
       #if ENABLED(PID_EXTRUSION_SCALING)
280
-        #define _PID_BASE_MENU_ITEMS(N) \
281
-          __PID_BASE_MENU_ITEMS(N); \
294
+        #define _PID_HOTEND_MENU_ITEMS(N) \
295
+          __PID_HOTEND_MENU_ITEMS(N); \
282 296
           EDIT_ITEM_N(float4, N, MSG_PID_C_E, &PID_PARAM(Kc, N), 1, 9990)
283 297
       #else
284
-        #define _PID_BASE_MENU_ITEMS(N) __PID_BASE_MENU_ITEMS(N)
298
+        #define _PID_HOTEND_MENU_ITEMS(N) __PID_HOTEND_MENU_ITEMS(N)
285 299
       #endif
286 300
 
287 301
       #if ENABLED(PID_FAN_SCALING)
288
-        #define _PID_EDIT_MENU_ITEMS(N) \
289
-          _PID_BASE_MENU_ITEMS(N); \
302
+        #define _HOTEND_PID_EDIT_MENU_ITEMS(N) \
303
+          _PID_HOTEND_MENU_ITEMS(N); \
290 304
           EDIT_ITEM_N(float4, N, MSG_PID_F_E, &PID_PARAM(Kf, N), 1, 9990)
291 305
       #else
292
-        #define _PID_EDIT_MENU_ITEMS(N) _PID_BASE_MENU_ITEMS(N)
306
+        #define _HOTEND_PID_EDIT_MENU_ITEMS(N) _PID_HOTEND_MENU_ITEMS(N)
293 307
       #endif
294 308
 
295 309
     #else
296 310
 
297
-      #define _PID_EDIT_MENU_ITEMS(N) NOOP
311
+      #define _HOTEND_PID_EDIT_MENU_ITEMS(N) NOOP
298 312
 
299 313
     #endif
300 314
 
301 315
     #if ENABLED(PID_AUTOTUNE_MENU)
302
-      #define PID_EDIT_MENU_ITEMS(N) \
303
-        _PID_EDIT_MENU_ITEMS(N); \
304
-        EDIT_ITEM_FAST_N(int3, N, MSG_PID_AUTOTUNE_E, &autotune_temp[N], 150, thermalManager.heater_maxtemp[N] - HOTEND_OVERSHOOT, []{ _lcd_autotune(MenuItemBase::itemIndex); });
316
+      #define HOTEND_PID_EDIT_MENU_ITEMS(N) \
317
+        _HOTEND_PID_EDIT_MENU_ITEMS(N); \
318
+        EDIT_ITEM_FAST_N(int3, N, MSG_PID_AUTOTUNE_E, &autotune_temp[N], 150, thermalManager.heater_maxtemp[N] - HOTEND_OVERSHOOT, []{ _lcd_autotune(heater_id_t(MenuItemBase::itemIndex)); });
305 319
     #else
306
-      #define PID_EDIT_MENU_ITEMS(N) _PID_EDIT_MENU_ITEMS(N);
320
+      #define HOTEND_PID_EDIT_MENU_ITEMS(N) _HOTEND_PID_EDIT_MENU_ITEMS(N);
307 321
     #endif
308 322
 
309
-    PID_EDIT_MENU_ITEMS(0);
323
+    HOTEND_PID_EDIT_MENU_ITEMS(0);
310 324
     #if ENABLED(PID_PARAMS_PER_HOTEND)
311
-      REPEAT_S(1, HOTENDS, PID_EDIT_MENU_ITEMS)
325
+      REPEAT_S(1, HOTENDS, HOTEND_PID_EDIT_MENU_ITEMS)
312 326
     #endif
313 327
 
314 328
     #if ENABLED(PIDTEMPBED)
315 329
       #if ENABLED(PID_EDIT_MENU)
316
-        #define PID_BED_MENU_SECTION
317
-        __PID_BASE_MENU_ITEMS(-1);
318
-        #undef PID_BED_MENU_SECTION
330
+        _PID_EDIT_ITEMS_TMPL(H_BED, thermalManager.temp_bed);
331
+      #endif
332
+      #if ENABLED(PID_AUTOTUNE_MENU)
333
+        EDIT_ITEM_FAST_N(int3, H_BED, MSG_PID_AUTOTUNE_E, &autotune_temp_bed, PREHEAT_1_TEMP_BED, BED_MAX_TARGET, []{ _lcd_autotune(H_BED); });
334
+      #endif
335
+    #endif
336
+
337
+    #if ENABLED(PIDTEMPCHAMBER)
338
+      #if ENABLED(PID_EDIT_MENU)
339
+        _PID_EDIT_ITEMS_TMPL(H_CHAMBER, thermalManager.temp_chamber);
319 340
       #endif
320 341
       #if ENABLED(PID_AUTOTUNE_MENU)
321
-        EDIT_ITEM_FAST_N(int3, -1, MSG_PID_AUTOTUNE_E, &autotune_temp_bed, PREHEAT_1_TEMP_BED, BED_MAX_TARGET, []{ _lcd_autotune(-1); });
342
+        EDIT_ITEM_FAST_N(int3, H_CHAMBER, MSG_PID_AUTOTUNE_E, &autotune_temp_chamber, PREHEAT_1_TEMP_CHAMBER, CHAMBER_MAX_TARGET, []{ _lcd_autotune(H_CHAMBER); });
322 343
       #endif
323 344
     #endif
324 345
 

+ 60
- 1
Marlin/src/module/settings.cpp View File

@@ -319,6 +319,11 @@ typedef struct SettingsDataStruct {
319 319
   PID_t bedPID;                                         // M304 PID / M303 E-1 U
320 320
 
321 321
   //
322
+  // PIDTEMPCHAMBER
323
+  //
324
+  PID_t chamberPID;                                     // M309 PID / M303 E-2 U
325
+
326
+  //
322 327
   // User-defined Thermistors
323 328
   //
324 329
   #if HAS_USER_THERMISTORS
@@ -927,6 +932,25 @@ void MarlinSettings::postprocess() {
927 932
     }
928 933
 
929 934
     //
935
+    // PIDTEMPCHAMBER
936
+    //
937
+    {
938
+      _FIELD_TEST(chamberPID);
939
+
940
+      const PID_t chamber_pid = {
941
+        #if DISABLED(PIDTEMPCHAMBER)
942
+          NAN, NAN, NAN
943
+        #else
944
+          // Store the unscaled PID values
945
+          thermalManager.temp_chamber.pid.Kp,
946
+          unscalePID_i(thermalManager.temp_chamber.pid.Ki),
947
+          unscalePID_d(thermalManager.temp_chamber.pid.Kd)
948
+        #endif
949
+      };
950
+      EEPROM_WRITE(chamber_pid);
951
+    }
952
+
953
+    //
930 954
     // User-defined Thermistors
931 955
     //
932 956
     #if HAS_USER_THERMISTORS
@@ -1788,6 +1812,22 @@ void MarlinSettings::postprocess() {
1788 1812
       }
1789 1813
 
1790 1814
       //
1815
+      // Heated Chamber PID
1816
+      //
1817
+      {
1818
+        PID_t pid;
1819
+        EEPROM_READ(pid);
1820
+        #if ENABLED(PIDTEMPCHAMBER)
1821
+          if (!validating && !isnan(pid.Kp)) {
1822
+            // Scale PID values since EEPROM values are unscaled
1823
+            thermalManager.temp_chamber.pid.Kp = pid.Kp;
1824
+            thermalManager.temp_chamber.pid.Ki = scalePID_i(pid.Ki);
1825
+            thermalManager.temp_chamber.pid.Kd = scalePID_d(pid.Kd);
1826
+          }
1827
+        #endif
1828
+      }
1829
+
1830
+      //
1791 1831
       // User-defined Thermistors
1792 1832
       //
1793 1833
       #if HAS_USER_THERMISTORS
@@ -2812,6 +2852,16 @@ void MarlinSettings::reset() {
2812 2852
   #endif
2813 2853
 
2814 2854
   //
2855
+  // Heated Chamber PID
2856
+  //
2857
+
2858
+  #if ENABLED(PIDTEMPCHAMBER)
2859
+    thermalManager.temp_chamber.pid.Kp = DEFAULT_chamberKp;
2860
+    thermalManager.temp_chamber.pid.Ki = scalePID_i(DEFAULT_chamberKi);
2861
+    thermalManager.temp_chamber.pid.Kd = scalePID_d(DEFAULT_chamberKd);
2862
+  #endif
2863
+
2864
+  //
2815 2865
   // User-Defined Thermistors
2816 2866
   //
2817 2867
   TERN_(HAS_USER_THERMISTORS, thermalManager.reset_user_thermistors());
@@ -3386,7 +3436,16 @@ void MarlinSettings::reset() {
3386 3436
         );
3387 3437
       #endif
3388 3438
 
3389
-    #endif // PIDTEMP || PIDTEMPBED
3439
+      #if ENABLED(PIDTEMPCHAMBER)
3440
+        CONFIG_ECHO_START();
3441
+        SERIAL_ECHOLNPAIR(
3442
+            "  M309 P", thermalManager.temp_chamber.pid.Kp
3443
+          , " I", unscalePID_i(thermalManager.temp_chamber.pid.Ki)
3444
+          , " D", unscalePID_d(thermalManager.temp_chamber.pid.Kd)
3445
+        );
3446
+      #endif
3447
+
3448
+    #endif // PIDTEMP || PIDTEMPBED || PIDTEMPCHAMBER
3390 3449
 
3391 3450
     #if HAS_USER_THERMISTORS
3392 3451
       CONFIG_ECHO_HEADING("User thermistors:");

+ 215
- 171
Marlin/src/module/temperature.cpp View File

@@ -371,10 +371,8 @@ const char str_t_thermal_runaway[] PROGMEM = STR_T_THERMAL_RUNAWAY,
371 371
     #ifdef CHAMBER_MAXTEMP
372 372
       int16_t Temperature::maxtemp_raw_CHAMBER = TEMP_SENSOR_CHAMBER_RAW_HI_TEMP;
373 373
     #endif
374
-    #if WATCH_CHAMBER
375
-      chamber_watch_t Temperature::watch_chamber{0};
376
-    #endif
377
-    millis_t Temperature::next_chamber_check_ms;
374
+    TERN_(WATCH_CHAMBER, chamber_watch_t Temperature::watch_chamber{0});
375
+    IF_DISABLED(PIDTEMPCHAMBER, millis_t Temperature::next_chamber_check_ms);
378 376
   #endif // HAS_HEATED_CHAMBER
379 377
 #endif // HAS_TEMP_CHAMBER
380 378
 
@@ -382,11 +380,6 @@ const char str_t_thermal_runaway[] PROGMEM = STR_T_THERMAL_RUNAWAY,
382 380
   probe_info_t Temperature::temp_probe; // = { 0 }
383 381
 #endif
384 382
 
385
-// Initialized by settings.load()
386
-#if ENABLED(PIDTEMP)
387
-  //hotend_pid_t Temperature::pid[HOTENDS];
388
-#endif
389
-
390 383
 #if ENABLED(PREVENT_COLD_EXTRUSION)
391 384
   bool Temperature::allow_cold_extrude = false;
392 385
   int16_t Temperature::extrude_min_temp = EXTRUDE_MINTEMP;
@@ -485,41 +478,44 @@ volatile bool Temperature::raw_temps_ready = false;
485 478
     millis_t next_temp_ms = millis(), t1 = next_temp_ms, t2 = next_temp_ms;
486 479
     long t_high = 0, t_low = 0;
487 480
 
488
-    long bias, d;
489 481
     PID_t tune_pid = { 0, 0, 0 };
490 482
     float maxT = 0, minT = 10000;
491 483
 
492 484
     const bool isbed = (heater_id == H_BED);
485
+    const bool ischamber = (heater_id == H_CHAMBER);
493 486
 
494
-    #if HAS_PID_FOR_BOTH
495
-      #define GHV(B,H) (isbed ? (B) : (H))
496
-      #define SHV(B,H) do{ if (isbed) temp_bed.soft_pwm_amount = B; else temp_hotend[heater_id].soft_pwm_amount = H; }while(0)
497
-      #define ONHEATINGSTART() (isbed ? printerEventLEDs.onBedHeatingStart() : printerEventLEDs.onHotendHeatingStart())
498
-      #define ONHEATING(S,C,T) (isbed ? printerEventLEDs.onBedHeating(S,C,T) : printerEventLEDs.onHotendHeating(S,C,T))
499
-    #elif ENABLED(PIDTEMPBED)
500
-      #define GHV(B,H) B
501
-      #define SHV(B,H) (temp_bed.soft_pwm_amount = B)
502
-      #define ONHEATINGSTART() printerEventLEDs.onBedHeatingStart()
503
-      #define ONHEATING(S,C,T) printerEventLEDs.onBedHeating(S,C,T)
487
+    #if ENABLED(PIDTEMPCHAMBER)
488
+      #define C_TERN(T,A,B) ((T) ? (A) : (B))
504 489
     #else
505
-      #define GHV(B,H) H
506
-      #define SHV(B,H) (temp_hotend[heater_id].soft_pwm_amount = H)
507
-      #define ONHEATINGSTART() printerEventLEDs.onHotendHeatingStart()
508
-      #define ONHEATING(S,C,T) printerEventLEDs.onHotendHeating(S,C,T)
490
+      #define C_TERN(T,A,B) (B)
509 491
     #endif
510
-    #define WATCH_PID BOTH(WATCH_BED, PIDTEMPBED) || BOTH(WATCH_HOTENDS, PIDTEMP)
492
+    #if ENABLED(PIDTEMPBED)
493
+      #define B_TERN(T,A,B) ((T) ? (A) : (B))
494
+    #else
495
+      #define B_TERN(T,A,B) (B)
496
+    #endif
497
+    #define GHV(C,B,H) C_TERN(ischamber, C, B_TERN(isbed, B, H))
498
+    #define SHV(V) C_TERN(ischamber, temp_chamber.soft_pwm_amount = V, B_TERN(isbed, temp_bed.soft_pwm_amount = V, temp_hotend[heater_id].soft_pwm_amount = V))
499
+    #define ONHEATINGSTART() C_TERN(ischamber, printerEventLEDs.onChamberHeatingStart(), B_TERN(isbed, printerEventLEDs.onBedHeatingStart(), printerEventLEDs.onHotendHeatingStart()))
500
+    #define ONHEATING(S,C,T) C_TERN(ischamber, printerEventLEDs.onChamberHeating(S,C,T), B_TERN(isbed, printerEventLEDs.onBedHeating(S,C,T), printerEventLEDs.onHotendHeating(S,C,T)))
501
+
502
+    #define WATCH_PID BOTH(WATCH_CHAMBER, PIDTEMPCHAMBER) || BOTH(WATCH_BED, PIDTEMPBED) || BOTH(WATCH_HOTENDS, PIDTEMP)
511 503
 
512 504
     #if WATCH_PID
513
-      #if ALL(THERMAL_PROTECTION_HOTENDS, PIDTEMP, THERMAL_PROTECTION_BED, PIDTEMPBED)
514
-        #define GTV(B,H) (isbed ? (B) : (H))
515
-      #elif BOTH(THERMAL_PROTECTION_HOTENDS, PIDTEMP)
516
-        #define GTV(B,H) (H)
505
+      #if BOTH(THERMAL_PROTECTION_CHAMBER, PIDTEMPCHAMBER)
506
+        #define C_GTV(T,A,B) ((T) ? (A) : (B))
517 507
       #else
518
-        #define GTV(B,H) (B)
508
+        #define C_GTV(T,A,B) (B)
519 509
       #endif
520
-      const uint16_t watch_temp_period = GTV(WATCH_BED_TEMP_PERIOD, WATCH_TEMP_PERIOD);
521
-      const uint8_t watch_temp_increase = GTV(WATCH_BED_TEMP_INCREASE, WATCH_TEMP_INCREASE);
522
-      const float watch_temp_target = target - float(watch_temp_increase + GTV(TEMP_BED_HYSTERESIS, TEMP_HYSTERESIS) + 1);
510
+      #if BOTH(THERMAL_PROTECTION_BED, PIDTEMPBED)
511
+        #define B_GTV(T,A,B) ((T) ? (A) : (B))
512
+      #else
513
+        #define B_GTV(T,A,B) (B)
514
+      #endif
515
+      #define GTV(C,B,H) C_GTV(ischamber, C, B_GTV(isbed, B, H))
516
+      const uint16_t watch_temp_period = GTV(WATCH_CHAMBER_TEMP_PERIOD, WATCH_BED_TEMP_PERIOD, WATCH_TEMP_PERIOD);
517
+      const uint8_t watch_temp_increase = GTV(WATCH_CHAMBER_TEMP_INCREASE, WATCH_BED_TEMP_INCREASE, WATCH_TEMP_INCREASE);
518
+      const float watch_temp_target = target - float(watch_temp_increase + GTV(TEMP_CHAMBER_HYSTERESIS, TEMP_BED_HYSTERESIS, TEMP_HYSTERESIS) + 1);
523 519
       millis_t temp_change_ms = next_temp_ms + SEC_TO_MS(watch_temp_period);
524 520
       float next_watch_temp = 0.0;
525 521
       bool heated = false;
@@ -527,7 +523,7 @@ volatile bool Temperature::raw_temps_ready = false;
527 523
 
528 524
     TERN_(HAS_AUTO_FAN, next_auto_fan_check_ms = next_temp_ms + 2500UL);
529 525
 
530
-    if (target > GHV(BED_MAX_TARGET, temp_range[heater_id].maxtemp - HOTEND_OVERSHOOT)) {
526
+    if (target > GHV(CHAMBER_MAX_TARGET, BED_MAX_TARGET, temp_range[heater_id].maxtemp - HOTEND_OVERSHOOT)) {
531 527
       SERIAL_ECHOLNPGM(STR_PID_TEMP_TOO_HIGH);
532 528
       TERN_(EXTENSIBLE_UI, ExtUI::onPidTuning(ExtUI::result_t::PID_TEMP_TOO_HIGH));
533 529
       return;
@@ -538,10 +534,11 @@ volatile bool Temperature::raw_temps_ready = false;
538 534
     disable_all_heaters();
539 535
     TERN_(AUTO_POWER_CONTROL, powerManager.power_on());
540 536
 
541
-    SHV(bias = d = (MAX_BED_POWER) >> 1, bias = d = (PID_MAX) >> 1);
537
+    long bias = GHV(MAX_CHAMBER_POWER, MAX_BED_POWER, PID_MAX) >> 1, d = bias;
538
+    SHV(bias);
542 539
 
543 540
     #if ENABLED(PRINTER_EVENT_LEDS)
544
-      const float start_temp = GHV(temp_bed.celsius, temp_hotend[heater_id].celsius);
541
+      const float start_temp = GHV(temp_chamber.celsius, temp_bed.celsius, temp_hotend[heater_id].celsius);
545 542
       LEDColor color = ONHEATINGSTART();
546 543
     #endif
547 544
 
@@ -557,7 +554,7 @@ volatile bool Temperature::raw_temps_ready = false;
557 554
         updateTemperaturesFromRawValues();
558 555
 
559 556
         // Get the current temperature and constrain it
560
-        current_temp = GHV(temp_bed.celsius, temp_hotend[heater_id].celsius);
557
+        current_temp = GHV(temp_chamber.celsius, temp_bed.celsius, temp_hotend[heater_id].celsius);
561 558
         NOLESS(maxT, current_temp);
562 559
         NOMORE(minT, current_temp);
563 560
 
@@ -572,67 +569,46 @@ volatile bool Temperature::raw_temps_ready = false;
572 569
           }
573 570
         #endif
574 571
 
575
-        if (heating && current_temp > target) {
576
-          if (ELAPSED(ms, t2 + 5000UL)) {
577
-            heating = false;
578
-            SHV((bias - d) >> 1, (bias - d) >> 1);
579
-            t1 = ms;
580
-            t_high = t1 - t2;
581
-            maxT = target;
582
-          }
572
+        if (heating && current_temp > target && ELAPSED(ms, t2 + 5000UL)) {
573
+          heating = false;
574
+          SHV((bias - d) >> 1);
575
+          t1 = ms;
576
+          t_high = t1 - t2;
577
+          maxT = target;
583 578
         }
584 579
 
585
-        if (!heating && current_temp < target) {
586
-          if (ELAPSED(ms, t1 + 5000UL)) {
587
-            heating = true;
588
-            t2 = ms;
589
-            t_low = t2 - t1;
590
-            if (cycles > 0) {
591
-              const long max_pow = GHV(MAX_BED_POWER, PID_MAX);
592
-              bias += (d * (t_high - t_low)) / (t_low + t_high);
593
-              LIMIT(bias, 20, max_pow - 20);
594
-              d = (bias > max_pow >> 1) ? max_pow - 1 - bias : bias;
595
-
596
-              SERIAL_ECHOPAIR(STR_BIAS, bias, STR_D_COLON, d, STR_T_MIN, minT, STR_T_MAX, maxT);
597
-              if (cycles > 2) {
598
-                const float Ku = (4.0f * d) / (float(M_PI) * (maxT - minT) * 0.5f),
599
-                            Tu = float(t_low + t_high) * 0.001f,
600
-                            pf = isbed ? 0.2f : 0.6f,
601
-                            df = isbed ? 1.0f / 3.0f : 1.0f / 8.0f;
602
-
603
-                SERIAL_ECHOPAIR(STR_KU, Ku, STR_TU, Tu);
604
-                if (isbed) { // Do not remove this otherwise PID autotune won't work right for the bed!
605
-                  tune_pid.Kp = Ku * 0.2f;
606
-                  tune_pid.Ki = 2 * tune_pid.Kp / Tu;
607
-                  tune_pid.Kd = tune_pid.Kp * Tu / 3;
608
-                  SERIAL_ECHOLNPGM("\n" " No overshoot"); // Works far better for the bed. Classic and some have bad ringing.
609
-                  SERIAL_ECHOLNPAIR(STR_KP, tune_pid.Kp, STR_KI, tune_pid.Ki, STR_KD, tune_pid.Kd);
610
-                }
611
-                else {
612
-                  tune_pid.Kp = Ku * pf;
613
-                  tune_pid.Kd = tune_pid.Kp * Tu * df;
614
-                  tune_pid.Ki = 2 * tune_pid.Kp / Tu;
615
-                  SERIAL_ECHOLNPGM("\n" STR_CLASSIC_PID);
616
-                  SERIAL_ECHOLNPAIR(STR_KP, tune_pid.Kp, STR_KI, tune_pid.Ki, STR_KD, tune_pid.Kd);
617
-                }
618
-
619
-                /**
620
-                tune_pid.Kp = 0.33 * Ku;
621
-                tune_pid.Ki = tune_pid.Kp / Tu;
622
-                tune_pid.Kd = tune_pid.Kp * Tu / 3;
623
-                SERIAL_ECHOLNPGM(" Some overshoot");
624
-                SERIAL_ECHOLNPAIR(" Kp: ", tune_pid.Kp, " Ki: ", tune_pid.Ki, " Kd: ", tune_pid.Kd, " No overshoot");
625
-                tune_pid.Kp = 0.2 * Ku;
626
-                tune_pid.Ki = 2 * tune_pid.Kp / Tu;
627
-                tune_pid.Kd = tune_pid.Kp * Tu / 3;
628
-                SERIAL_ECHOPAIR(" Kp: ", tune_pid.Kp, " Ki: ", tune_pid.Ki, " Kd: ", tune_pid.Kd);
629
-                */
630
-              }
580
+        if (!heating && current_temp < target && ELAPSED(ms, t1 + 5000UL)) {
581
+          heating = true;
582
+          t2 = ms;
583
+          t_low = t2 - t1;
584
+          if (cycles > 0) {
585
+            const long max_pow = GHV(MAX_CHAMBER_POWER, MAX_BED_POWER, PID_MAX);
586
+            bias += (d * (t_high - t_low)) / (t_low + t_high);
587
+            LIMIT(bias, 20, max_pow - 20);
588
+            d = (bias > max_pow >> 1) ? max_pow - 1 - bias : bias;
589
+
590
+            SERIAL_ECHOPAIR(STR_BIAS, bias, STR_D_COLON, d, STR_T_MIN, minT, STR_T_MAX, maxT);
591
+            if (cycles > 2) {
592
+              const float Ku = (4.0f * d) / (float(M_PI) * (maxT - minT) * 0.5f),
593
+                          Tu = float(t_low + t_high) * 0.001f,
594
+                          pf = ischamber ? 0.2f : (isbed ? 0.2f : 0.6f),
595
+                          df = ischamber ? 1.0f / 3.0f : (isbed ? 1.0f / 3.0f : 1.0f / 8.0f);
596
+
597
+              tune_pid.Kp = Ku * pf;
598
+              tune_pid.Ki = tune_pid.Kp * 2.0f / Tu;
599
+              tune_pid.Kd = tune_pid.Kp * Tu * df;
600
+
601
+              SERIAL_ECHOLNPAIR(STR_KU, Ku, STR_TU, Tu);
602
+              if (ischamber || isbed)
603
+                SERIAL_ECHOLNPGM(" No overshoot");
604
+              else
605
+                SERIAL_ECHOLNPGM(STR_CLASSIC_PID);
606
+              SERIAL_ECHOLNPAIR(STR_KP, tune_pid.Kp, STR_KI, tune_pid.Ki, STR_KD, tune_pid.Kd);
631 607
             }
632
-            SHV((bias + d) >> 1, (bias + d) >> 1);
633
-            cycles++;
634
-            minT = target;
635 608
           }
609
+          SHV((bias + d) >> 1);
610
+          cycles++;
611
+          minT = target;
636 612
         }
637 613
       }
638 614
 
@@ -649,14 +625,14 @@ volatile bool Temperature::raw_temps_ready = false;
649 625
       // Report heater states every 2 seconds
650 626
       if (ELAPSED(ms, next_temp_ms)) {
651 627
         #if HAS_TEMP_SENSOR
652
-          print_heater_states(isbed ? active_extruder : heater_id);
628
+          print_heater_states(ischamber ? active_extruder : (isbed ? active_extruder : heater_id));
653 629
           SERIAL_EOL();
654 630
         #endif
655 631
         next_temp_ms = ms + 2000UL;
656 632
 
657 633
         // Make sure heating is actually working
658 634
         #if WATCH_PID
659
-          if (BOTH(WATCH_BED, WATCH_HOTENDS) || isbed == DISABLED(WATCH_HOTENDS)) {
635
+          if (BOTH(WATCH_BED, WATCH_HOTENDS) || isbed == DISABLED(WATCH_HOTENDS) || ischamber == DISABLED(WATCH_HOTENDS)) {
660 636
             if (!heated) {                                            // If not yet reached target...
661 637
               if (current_temp > next_watch_temp) {                   // Over the watch temp?
662 638
                 next_watch_temp = current_temp + watch_temp_increase; // - set the next temp to watch for
@@ -686,43 +662,47 @@ volatile bool Temperature::raw_temps_ready = false;
686 662
       if (cycles > ncycles && cycles > 2) {
687 663
         SERIAL_ECHOLNPGM(STR_PID_AUTOTUNE_FINISHED);
688 664
 
689
-        #if HAS_PID_FOR_BOTH
690
-          const char * const estring = GHV(PSTR("bed"), NUL_STR);
665
+        #if EITHER(PIDTEMPBED, PIDTEMPCHAMBER)
666
+          PGM_P const estring = GHV(PSTR("chamber"), PSTR("bed"), NUL_STR);
691 667
           say_default_(); serialprintPGM(estring); SERIAL_ECHOLNPAIR("Kp ", tune_pid.Kp);
692 668
           say_default_(); serialprintPGM(estring); SERIAL_ECHOLNPAIR("Ki ", tune_pid.Ki);
693 669
           say_default_(); serialprintPGM(estring); SERIAL_ECHOLNPAIR("Kd ", tune_pid.Kd);
694
-        #elif ENABLED(PIDTEMP)
670
+        #else
695 671
           say_default_(); SERIAL_ECHOLNPAIR("Kp ", tune_pid.Kp);
696 672
           say_default_(); SERIAL_ECHOLNPAIR("Ki ", tune_pid.Ki);
697 673
           say_default_(); SERIAL_ECHOLNPAIR("Kd ", tune_pid.Kd);
698
-        #else
699
-          say_default_(); SERIAL_ECHOLNPAIR("bedKp ", tune_pid.Kp);
700
-          say_default_(); SERIAL_ECHOLNPAIR("bedKi ", tune_pid.Ki);
701
-          say_default_(); SERIAL_ECHOLNPAIR("bedKd ", tune_pid.Kd);
702 674
         #endif
703 675
 
704
-        #define _SET_BED_PID() do { \
705
-          temp_bed.pid.Kp = tune_pid.Kp; \
706
-          temp_bed.pid.Ki = scalePID_i(tune_pid.Ki); \
707
-          temp_bed.pid.Kd = scalePID_d(tune_pid.Kd); \
708
-        }while(0)
676
+        auto _set_hotend_pid = [](const uint8_t e, const PID_t &in_pid) {
677
+          #if ENABLED(PIDTEMP)
678
+            PID_PARAM(Kp, e) = in_pid.Kp;
679
+            PID_PARAM(Ki, e) = scalePID_i(in_pid.Ki);
680
+            PID_PARAM(Kd, e) = scalePID_d(in_pid.Kd);
681
+            updatePID();
682
+          #else
683
+            UNUSED(e); UNUSED(in_pid);
684
+          #endif
685
+        };
709 686
 
710
-        #define _SET_EXTRUDER_PID() do { \
711
-          PID_PARAM(Kp, heater_id) = tune_pid.Kp; \
712
-          PID_PARAM(Ki, heater_id) = scalePID_i(tune_pid.Ki); \
713
-          PID_PARAM(Kd, heater_id) = scalePID_d(tune_pid.Kd); \
714
-          updatePID(); }while(0)
687
+        #if ENABLED(PIDTEMPBED)
688
+          auto _set_bed_pid = [](const PID_t &in_pid) {
689
+            temp_bed.pid.Kp = in_pid.Kp;
690
+            temp_bed.pid.Ki = scalePID_i(in_pid.Ki);
691
+            temp_bed.pid.Kd = scalePID_d(in_pid.Kd);
692
+          };
693
+        #endif
694
+
695
+        #if ENABLED(PIDTEMPCHAMBER)
696
+          auto _set_chamber_pid = [](const PID_t &in_pid) {
697
+            temp_chamber.pid.Kp = in_pid.Kp;
698
+            temp_chamber.pid.Ki = scalePID_i(in_pid.Ki);
699
+            temp_chamber.pid.Kd = scalePID_d(in_pid.Kd);
700
+          };
701
+        #endif
715 702
 
716 703
         // Use the result? (As with "M303 U1")
717
-        if (set_result) {
718
-          #if HAS_PID_FOR_BOTH
719
-            if (isbed) _SET_BED_PID(); else _SET_EXTRUDER_PID();
720
-          #elif ENABLED(PIDTEMP)
721
-            _SET_EXTRUDER_PID();
722
-          #else
723
-            _SET_BED_PID();
724
-          #endif
725
-        }
704
+        if (set_result)
705
+          GHV(_set_chamber_pid(tune_pid), _set_bed_pid(tune_pid), _set_hotend_pid(heater_id, tune_pid));
726 706
 
727 707
         TERN_(PRINTER_EVENT_LEDS, printerEventLEDs.onPidTuningDone(color));
728 708
 
@@ -939,10 +919,11 @@ void Temperature::min_temp_error(const heater_id_t heater_id) {
939 919
   _temp_error(heater_id, PSTR(STR_T_MINTEMP), GET_TEXT(MSG_ERR_MINTEMP));
940 920
 }
941 921
 
922
+#if ANY(PID_DEBUG, PID_BED_DEBUG, PID_CHAMBER_DEBUG)
923
+  bool Temperature::pid_debug_flag; // = 0
924
+#endif
925
+
942 926
 #if HAS_HOTEND
943
-  #if ENABLED(PID_DEBUG)
944
-    extern bool pid_debug_flag;
945
-  #endif
946 927
 
947 928
   float Temperature::get_pid_output_hotend(const uint8_t E_NAME) {
948 929
     const uint8_t ee = HOTEND_INDEX;
@@ -1023,23 +1004,18 @@ void Temperature::min_temp_error(const heater_id_t heater_id) {
1023 1004
 
1024 1005
       #if ENABLED(PID_DEBUG)
1025 1006
         if (ee == active_extruder && pid_debug_flag) {
1026
-          SERIAL_ECHO_START();
1027
-          SERIAL_ECHOPAIR(STR_PID_DEBUG, ee, STR_PID_DEBUG_INPUT, temp_hotend[ee].celsius, STR_PID_DEBUG_OUTPUT, pid_output);
1028
-          #if DISABLED(PID_OPENLOOP)
1029
-          {
1030
-            SERIAL_ECHOPAIR(
1031
-              STR_PID_DEBUG_PTERM, work_pid[ee].Kp,
1032
-              STR_PID_DEBUG_ITERM, work_pid[ee].Ki,
1033
-              STR_PID_DEBUG_DTERM, work_pid[ee].Kd
1007
+          SERIAL_ECHO_MSG(STR_PID_DEBUG, ee, STR_PID_DEBUG_INPUT, temp_hotend[ee].celsius, STR_PID_DEBUG_OUTPUT, pid_output
1008
+            #if DISABLED(PID_OPENLOOP)
1009
+              , STR_PID_DEBUG_PTERM, work_pid[ee].Kp
1010
+              , STR_PID_DEBUG_ITERM, work_pid[ee].Ki
1011
+              , STR_PID_DEBUG_DTERM, work_pid[ee].Kd
1034 1012
               #if ENABLED(PID_EXTRUSION_SCALING)
1035 1013
                 , STR_PID_DEBUG_CTERM, work_pid[ee].Kc
1036 1014
               #endif
1037
-            );
1038
-          }
1039
-          #endif
1040
-          SERIAL_EOL();
1015
+            #endif
1016
+          );
1041 1017
         }
1042
-      #endif // PID_DEBUG
1018
+      #endif
1043 1019
 
1044 1020
     #else // No PID enabled
1045 1021
 
@@ -1099,13 +1075,76 @@ void Temperature::min_temp_error(const heater_id_t heater_id) {
1099 1075
     #endif // PID_OPENLOOP
1100 1076
 
1101 1077
     #if ENABLED(PID_BED_DEBUG)
1078
+      if (pid_debug_flag) {
1079
+        SERIAL_ECHO_MSG(
1080
+          " PID_BED_DEBUG : Input ", temp_bed.celsius, " Output ", pid_output
1081
+          #if DISABLED(PID_OPENLOOP)
1082
+            , STR_PID_DEBUG_PTERM, work_pid.Kp
1083
+            , STR_PID_DEBUG_ITERM, work_pid.Ki
1084
+            , STR_PID_DEBUG_DTERM, work_pid.Kd
1085
+          #endif
1086
+        );
1087
+      }
1088
+    #endif
1089
+
1090
+    return pid_output;
1091
+  }
1092
+
1093
+#endif // PIDTEMPBED
1094
+
1095
+#if ENABLED(PIDTEMPCHAMBER)
1096
+
1097
+  float Temperature::get_pid_output_chamber() {
1098
+
1099
+    #if DISABLED(PID_OPENLOOP)
1100
+
1101
+      static PID_t work_pid{0};
1102
+      static float temp_iState = 0, temp_dState = 0;
1103
+      static bool pid_reset = true;
1104
+      float pid_output = 0;
1105
+      const float max_power_over_i_gain = float(MAX_CHAMBER_POWER) / temp_chamber.pid.Ki - float(MIN_CHAMBER_POWER),
1106
+                  pid_error = temp_chamber.target - temp_chamber.celsius;
1107
+
1108
+      if (!temp_chamber.target || pid_error < -(PID_FUNCTIONAL_RANGE)) {
1109
+        pid_output = 0;
1110
+        pid_reset = true;
1111
+      }
1112
+      else if (pid_error > PID_FUNCTIONAL_RANGE) {
1113
+        pid_output = MAX_CHAMBER_POWER;
1114
+        pid_reset = true;
1115
+      }
1116
+      else {
1117
+        if (pid_reset) {
1118
+          temp_iState = 0.0;
1119
+          work_pid.Kd = 0.0;
1120
+          pid_reset = false;
1121
+        }
1122
+
1123
+        temp_iState = constrain(temp_iState + pid_error, 0, max_power_over_i_gain);
1124
+
1125
+        work_pid.Kp = temp_chamber.pid.Kp * pid_error;
1126
+        work_pid.Ki = temp_chamber.pid.Ki * temp_iState;
1127
+        work_pid.Kd = work_pid.Kd + PID_K2 * (temp_chamber.pid.Kd * (temp_dState - temp_chamber.celsius) - work_pid.Kd);
1128
+
1129
+        temp_dState = temp_chamber.celsius;
1130
+
1131
+        pid_output = constrain(work_pid.Kp + work_pid.Ki + work_pid.Kd + float(MIN_CHAMBER_POWER), 0, MAX_CHAMBER_POWER);
1132
+      }
1133
+
1134
+    #else // PID_OPENLOOP
1135
+
1136
+      const float pid_output = constrain(temp_chamber.target, 0, MAX_CHAMBER_POWER);
1137
+
1138
+    #endif // PID_OPENLOOP
1139
+
1140
+    #if ENABLED(PID_CHAMBER_DEBUG)
1102 1141
     {
1103 1142
       SERIAL_ECHO_MSG(
1104
-        " PID_BED_DEBUG : Input ", temp_bed.celsius, " Output ", pid_output,
1143
+        " PID_CHAMBER_DEBUG : Input ", temp_chamber.celsius, " Output ", pid_output
1105 1144
         #if DISABLED(PID_OPENLOOP)
1106
-          STR_PID_DEBUG_PTERM, work_pid.Kp,
1107
-          STR_PID_DEBUG_ITERM, work_pid.Ki,
1108
-          STR_PID_DEBUG_DTERM, work_pid.Kd,
1145
+          , STR_PID_DEBUG_PTERM, work_pid.Kp
1146
+          , STR_PID_DEBUG_ITERM, work_pid.Ki
1147
+          , STR_PID_DEBUG_DTERM, work_pid.Kd
1109 1148
         #endif
1110 1149
       );
1111 1150
     }
@@ -1114,7 +1153,7 @@ void Temperature::min_temp_error(const heater_id_t heater_id) {
1114 1153
     return pid_output;
1115 1154
   }
1116 1155
 
1117
-#endif // PIDTEMPBED
1156
+#endif // PIDTEMPCHAMBER
1118 1157
 
1119 1158
 /**
1120 1159
  * Manage heating activities for extruder hot-ends and a heated bed
@@ -1363,42 +1402,47 @@ void Temperature::manage_heater() {
1363 1402
       }
1364 1403
     #endif
1365 1404
 
1405
+
1406
+
1407
+
1408
+    #if ENABLED(PIDTEMPCHAMBER)
1409
+      // PIDTEMPCHAMBER doens't support a CHAMBER_VENT yet.
1410
+      temp_chamber.soft_pwm_amount = WITHIN(temp_chamber.celsius, CHAMBER_MINTEMP, CHAMBER_MAXTEMP) ? (int)get_pid_output_chamber() >> 1 : 0;
1411
+    #else
1366 1412
     if (ELAPSED(ms, next_chamber_check_ms)) {
1367 1413
       next_chamber_check_ms = ms + CHAMBER_CHECK_INTERVAL;
1368 1414
 
1369 1415
       if (WITHIN(temp_chamber.celsius, CHAMBER_MINTEMP, CHAMBER_MAXTEMP)) {
1370
-        if (flag_chamber_excess_heat) {
1371
-          temp_chamber.soft_pwm_amount = 0;
1372
-          #if ENABLED(CHAMBER_VENT)
1373
-            if (!flag_chamber_off) MOVE_SERVO(CHAMBER_VENT_SERVO_NR, temp_chamber.celsius <= temp_chamber.target ? 0 : 90);
1374
-          #endif
1416
+          if (flag_chamber_excess_heat) {
1417
+            temp_chamber.soft_pwm_amount = 0;
1418
+            #if ENABLED(CHAMBER_VENT)
1419
+              if (!flag_chamber_off) MOVE_SERVO(CHAMBER_VENT_SERVO_NR, temp_chamber.celsius <= temp_chamber.target ? 0 : 90);
1420
+            #endif
1421
+          }
1422
+          else {
1423
+            #if ENABLED(CHAMBER_LIMIT_SWITCHING)
1424
+              if (temp_chamber.celsius >= temp_chamber.target + TEMP_CHAMBER_HYSTERESIS)
1425
+                temp_chamber.soft_pwm_amount = 0;
1426
+              else if (temp_chamber.celsius <= temp_chamber.target - (TEMP_CHAMBER_HYSTERESIS))
1427
+                temp_chamber.soft_pwm_amount = (MAX_CHAMBER_POWER) >> 1;
1428
+            #else
1429
+              temp_chamber.soft_pwm_amount = temp_chamber.celsius < temp_chamber.target ? (MAX_CHAMBER_POWER) >> 1 : 0;
1430
+            #endif
1431
+            #if ENABLED(CHAMBER_VENT)
1432
+              if (!flag_chamber_off) MOVE_SERVO(CHAMBER_VENT_SERVO_NR, 0);
1433
+            #endif
1434
+          }
1375 1435
         }
1376 1436
         else {
1377
-          #if ENABLED(CHAMBER_LIMIT_SWITCHING)
1378
-            if (temp_chamber.celsius >= temp_chamber.target + TEMP_CHAMBER_HYSTERESIS)
1379
-              temp_chamber.soft_pwm_amount = 0;
1380
-            else if (temp_chamber.celsius <= temp_chamber.target - (TEMP_CHAMBER_HYSTERESIS))
1381
-              temp_chamber.soft_pwm_amount = (MAX_CHAMBER_POWER) >> 1;
1382
-          #else
1383
-            temp_chamber.soft_pwm_amount = temp_chamber.celsius < temp_chamber.target ? (MAX_CHAMBER_POWER) >> 1 : 0;
1384
-          #endif
1385
-          #if ENABLED(CHAMBER_VENT)
1386
-            if (!flag_chamber_off) MOVE_SERVO(CHAMBER_VENT_SERVO_NR, 0);
1387
-          #endif
1437
+          temp_chamber.soft_pwm_amount = 0;
1438
+          WRITE_HEATER_CHAMBER(LOW);
1388 1439
         }
1389
-      }
1390
-      else {
1391
-        temp_chamber.soft_pwm_amount = 0;
1392
-        WRITE_HEATER_CHAMBER(LOW);
1393
-      }
1394
-
1395
-      #if ENABLED(THERMAL_PROTECTION_CHAMBER)
1396
-        tr_state_machine[RUNAWAY_IND_CHAMBER].run(temp_chamber.celsius, temp_chamber.target, H_CHAMBER, THERMAL_PROTECTION_CHAMBER_PERIOD, THERMAL_PROTECTION_CHAMBER_HYSTERESIS);
1397
-      #endif
1398
-    }
1399 1440
 
1400
-    // TODO: Implement true PID pwm
1401
-    //temp_bed.soft_pwm_amount = WITHIN(temp_chamber.celsius, CHAMBER_MINTEMP, CHAMBER_MAXTEMP) ? (int)get_pid_output_chamber() >> 1 : 0;
1441
+     }
1442
+     #if ENABLED(THERMAL_PROTECTION_CHAMBER)
1443
+       tr_state_machine[RUNAWAY_IND_CHAMBER].run(temp_chamber.celsius, temp_chamber.target, H_CHAMBER, THERMAL_PROTECTION_CHAMBER_PERIOD, THERMAL_PROTECTION_CHAMBER_HYSTERESIS);
1444
+     #endif
1445
+   #endif
1402 1446
 
1403 1447
   #endif // HAS_HEATED_CHAMBER
1404 1448
 

+ 14
- 7
Marlin/src/module/temperature.h View File

@@ -210,7 +210,11 @@ struct PIDHeaterInfo : public HeaterInfo {
210 210
   typedef temp_info_t probe_info_t;
211 211
 #endif
212 212
 #if HAS_HEATED_CHAMBER
213
-  typedef heater_info_t chamber_info_t;
213
+  #if ENABLED(PIDTEMPCHAMBER)
214
+    typedef struct PIDHeaterInfo<PID_t> chamber_info_t;
215
+  #else
216
+    typedef heater_info_t chamber_info_t;
217
+  #endif
214 218
 #elif HAS_TEMP_CHAMBER
215 219
   typedef temp_info_t chamber_info_t;
216 220
 #endif
@@ -415,7 +419,7 @@ class Temperature {
415 419
 
416 420
     #if HAS_HEATED_CHAMBER
417 421
       TERN_(WATCH_CHAMBER, static chamber_watch_t watch_chamber);
418
-      static millis_t next_chamber_check_ms;
422
+      TERN(PIDTEMPCHAMBER,,static millis_t next_chamber_check_ms);
419 423
       #ifdef CHAMBER_MINTEMP
420 424
         static int16_t mintemp_raw_CHAMBER;
421 425
       #endif
@@ -751,6 +755,11 @@ class Temperature {
751 755
      * Perform auto-tuning for hotend or bed in response to M303
752 756
      */
753 757
     #if HAS_PID_HEATING
758
+
759
+      #if ANY(PID_DEBUG, PID_BED_DEBUG, PID_CHAMBER_DEBUG)
760
+        static bool pid_debug_flag;
761
+      #endif
762
+
754 763
       static void PID_autotune(const float &target, const heater_id_t heater_id, const int8_t ncycles, const bool set_result=false);
755 764
 
756 765
       #if ENABLED(NO_FAN_SLOWING_IN_PID_TUNING)
@@ -826,11 +835,9 @@ class Temperature {
826 835
 
827 836
     static void checkExtruderAutoFans();
828 837
 
829
-    static float get_pid_output_hotend(const uint8_t e);
830
-
831
-    TERN_(PIDTEMPBED, static float get_pid_output_bed());
832
-
833
-    TERN_(HAS_HEATED_CHAMBER, static float get_pid_output_chamber());
838
+    TERN_(HAS_HOTEND,     static float get_pid_output_hotend(const uint8_t e));
839
+    TERN_(PIDTEMPBED,     static float get_pid_output_bed());
840
+    TERN_(PIDTEMPCHAMBER, static float get_pid_output_chamber());
834 841
 
835 842
     static void _temp_error(const heater_id_t e, PGM_P const serial_msg, PGM_P const lcd_msg);
836 843
     static void min_temp_error(const heater_id_t e);

Loading…
Cancel
Save