/firmware/src/shared/Heater.cc

http://github.com/makerbot/G3Firmware · C++ · 207 lines · 139 code · 34 blank · 34 comment · 23 complexity · 209f56c7f4503a7b435443bf75273377 MD5 · raw file

  1. /*
  2. * Copyright 2010 by Adam Mayer <adam@makerbot.com>
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>
  16. */
  17. #include "Configuration.hh"
  18. #include "Heater.hh"
  19. #include "HeatingElement.hh"
  20. #include "Thermistor.hh"
  21. //#include "ExtruderBoard.hh"
  22. #include "Eeprom.hh"
  23. #include "EepromMap.hh"
  24. #define DEFAULT_P 7.0
  25. #define DEFAULT_I 0.325
  26. #define DEFAULT_D 36.0
  27. // EEPROM map
  28. #define P_TERM_OFFSET 0
  29. #define I_TERM_OFFSET 2
  30. #define D_TERM_OFFSET 4
  31. /// Offset to compensate for range clipping and bleed-off
  32. #define HEATER_OFFSET_ADJUSTMENT 0
  33. /// PID bypass: If the set point is more than this many degrees over the
  34. /// current temperature, bypass the PID loop altogether.
  35. #define PID_BYPASS_DELTA 15
  36. /// Number of bad sensor readings we need to get in a row before shutting off the heater
  37. #define SENSOR_MAX_BAD_READINGS 5
  38. /// If we read a temperature higher than this, shut down the heater
  39. #define HEATER_CUTOFF_TEMPERATURE 280
  40. Heater::Heater(TemperatureSensor& sensor_in,
  41. HeatingElement& element_in,
  42. micros_t sample_interval_micros_in,
  43. uint16_t eeprom_base_in) :
  44. sensor(sensor_in),
  45. element(element_in),
  46. sample_interval_micros(sample_interval_micros_in),
  47. eeprom_base(eeprom_base_in)
  48. {
  49. reset();
  50. }
  51. void Heater::reset() {
  52. // TODO: Reset sensor, element here?
  53. current_temperature = 0;
  54. fail_state = false;
  55. fail_count = 0;
  56. float p = eeprom::getEepromFixed16(eeprom_base+P_TERM_OFFSET,DEFAULT_P);
  57. float i = eeprom::getEepromFixed16(eeprom_base+I_TERM_OFFSET,DEFAULT_I);
  58. float d = eeprom::getEepromFixed16(eeprom_base+D_TERM_OFFSET,DEFAULT_D);
  59. pid.reset();
  60. if (p == 0 && i == 0 && d == 0) {
  61. p = DEFAULT_P; i = DEFAULT_I; d = DEFAULT_D;
  62. }
  63. pid.setPGain(p);
  64. pid.setIGain(i);
  65. pid.setDGain(d);
  66. pid.setTarget(0);
  67. next_pid_timeout.start(UPDATE_INTERVAL_MICROS);
  68. next_sense_timeout.start(sample_interval_micros);
  69. }
  70. void Heater::set_target_temperature(int temp)
  71. {
  72. pid.setTarget(temp);
  73. }
  74. // We now define target hysteresis in absolute degrees. The original
  75. // implementation (+/-5%) was giving us swings of 10% in either direction
  76. // *before* any artifacts of process instability came in.
  77. #define TARGET_HYSTERESIS 2
  78. bool Heater::has_reached_target_temperature()
  79. {
  80. return (current_temperature >= (pid.getTarget() - TARGET_HYSTERESIS)) &&
  81. (current_temperature <= (pid.getTarget() + TARGET_HYSTERESIS));
  82. }
  83. int Heater::get_set_temperature() {
  84. return pid.getTarget();
  85. }
  86. int Heater::get_current_temperature()
  87. {
  88. return sensor.getTemperature();
  89. }
  90. int Heater::getPIDErrorTerm() {
  91. return pid.getErrorTerm();
  92. }
  93. int Heater::getPIDDeltaTerm() {
  94. return pid.getDeltaTerm();
  95. }
  96. int Heater::getPIDLastOutput() {
  97. return pid.getLastOutput();
  98. }
  99. void Heater::manage_temperature()
  100. {
  101. if (fail_state) {
  102. return;
  103. }
  104. if (next_sense_timeout.hasElapsed()) {
  105. next_sense_timeout.start(sample_interval_micros);
  106. switch (sensor.update()) {
  107. case TemperatureSensor::SS_ADC_BUSY:
  108. case TemperatureSensor::SS_ADC_WAITING:
  109. // We're waiting for the ADC, so don't update the temperature yet.
  110. current_temperature = 2;
  111. return;
  112. break;
  113. case TemperatureSensor::SS_OK:
  114. // Result was ok, so reset the fail counter, and continue.
  115. fail_count = 0;
  116. break;
  117. case TemperatureSensor::SS_ERROR_UNPLUGGED:
  118. default:
  119. // If we get too many bad readings in a row, shut down the heater.
  120. fail_count++;
  121. if (fail_count > SENSOR_MAX_BAD_READINGS) {
  122. fail();
  123. }
  124. current_temperature = 3;
  125. return;
  126. break;
  127. }
  128. current_temperature = get_current_temperature();
  129. if (current_temperature > HEATER_CUTOFF_TEMPERATURE) {
  130. fail();
  131. return;
  132. }
  133. }
  134. if (next_pid_timeout.hasElapsed()) {
  135. next_pid_timeout.start(UPDATE_INTERVAL_MICROS);
  136. int delta = pid.getTarget() - current_temperature;
  137. if( bypassing_PID && (delta < PID_BYPASS_DELTA) ) {
  138. bypassing_PID = false;
  139. pid.reset_state();
  140. }
  141. else if ( !bypassing_PID && (delta > PID_BYPASS_DELTA + 10) ) {
  142. bypassing_PID = true;
  143. }
  144. if( bypassing_PID ) {
  145. set_output(255);
  146. }
  147. else {
  148. int mv = pid.calculate(current_temperature);
  149. // offset value to compensate for heat bleed-off.
  150. // There are probably more elegant ways to do this,
  151. // but this works pretty well.
  152. mv += HEATER_OFFSET_ADJUSTMENT;
  153. // clamp value
  154. if (mv < 0) { mv = 0; }
  155. if (mv >255) { mv = 255; }
  156. if (pid.getTarget() == 0) { mv = 0; }
  157. set_output(mv);
  158. }
  159. }
  160. }
  161. void Heater::set_output(uint8_t value)
  162. {
  163. element.setHeatingElement(value);
  164. }
  165. void Heater::fail()
  166. {
  167. fail_state = true;
  168. set_output(0);
  169. }
  170. bool Heater::has_failed()
  171. {
  172. return fail_state;
  173. }