controller_current.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. /*
  2. * Xytronic LF-1600
  3. * Current controller
  4. *
  5. * Copyright (c) 2015-2017 Michael Buesch <m@bues.ch>
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License along
  18. * with this program; if not, write to the Free Software Foundation, Inc.,
  19. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  20. */
  21. #include "controller_current.h"
  22. #include "pid.h"
  23. #include "timer.h"
  24. #include "pwm_current.h"
  25. #include "scale.h"
  26. #include "filter.h"
  27. #include "debug_uart.h"
  28. #include <string.h>
  29. /* Design note:
  30. *
  31. * The current controller is a PID controller in the lower
  32. * setpoint area. However in the upper area the current is
  33. * set without feedback evaluation. This is due to our feedback
  34. * hardware not working correctly for high currents.
  35. * The cut-off-setpoint where the PID is switched off/on
  36. * is defined via CONTRCURR_PID_CUTOFF_HI/LO.
  37. *
  38. * CONTRCURR_PID_CUTOFF_HI is configured via CONF_CURRCUTOFF.
  39. */
  40. enum current_r_state {
  41. RSTATE_DISABLED,
  42. RSTATE_SYNCING,
  43. RSTATE_ENABLED,
  44. };
  45. struct current_contr_context {
  46. bool enabled;
  47. bool restricted;
  48. uint8_t emergency_flags;
  49. struct pid pid;
  50. enum current_r_state r_state;
  51. fixpt_t feedback;
  52. fixpt_t prev_y;
  53. struct timer timer;
  54. struct lp_filter_fixpt model;
  55. /* Debug state */
  56. int24_t old_current_real_feedback;
  57. int24_t old_current_used_feedback;
  58. int24_t old_current_control;
  59. int8_t old_r_state;
  60. };
  61. static struct current_contr_context contrcurr;
  62. static const struct pid_k_set __flash contrcurr_factors = {
  63. .kp = FLOAT_TO_FIXPT(CONTRCURR_PID_KP),
  64. .ki = FLOAT_TO_FIXPT(CONTRCURR_PID_KI),
  65. .kd = FLOAT_TO_FIXPT(CONTRCURR_PID_KD),
  66. .d_decay_div = FLOAT_TO_FIXPT(CONTRCURR_PID_D_DECAY),
  67. };
  68. static void contrcurr_run(fixpt_t real_r)
  69. {
  70. fixpt_t dt, y, r;
  71. if (!contrcurr.enabled)
  72. return;
  73. if (contrcurr.emergency_flags)
  74. return;
  75. /* Get delta-t that elapsed since last run, in seconds */
  76. dt = fixpt_div(int_to_fixpt(timer_ms_since(&contrcurr.timer)),
  77. int_to_fixpt(1000));
  78. timer_set_now(&contrcurr.timer);
  79. r = real_r;
  80. /* Check whether the actual r or the model shall be used. */
  81. switch (contrcurr.r_state) {
  82. case RSTATE_DISABLED:
  83. /* Usage of real_r is disabled. Use the model r.
  84. * Once real_r drops below LO state start an r resync.
  85. */
  86. if (real_r <= float_to_fixpt(CONTRCURR_PID_CUTOFF_LO)) {
  87. contrcurr.r_state = RSTATE_SYNCING;
  88. } else {
  89. /* We are in the upper setpoint area.
  90. * Do not use the real feedback. Use a PT1 model instead. */
  91. r = lp_filter_fixpt_run(&contrcurr.model, contrcurr.prev_y,
  92. int_to_fixpt(16));
  93. break;
  94. }
  95. /* fall through... */
  96. case RSTATE_SYNCING:
  97. /* Slowly re-synchronize the model r to the real_r.
  98. * Once we are reasomably close, switch back to 'enabled' state.
  99. */
  100. r = lp_filter_fixpt_run(&contrcurr.model, real_r,
  101. int_to_fixpt(24));
  102. if (fixpt_abs(fixpt_sub(r, real_r)) < float_to_fixpt(AMPERE(0.1)))
  103. contrcurr.r_state = RSTATE_ENABLED;
  104. break;
  105. case RSTATE_ENABLED:
  106. default:
  107. /* Use real_r unless it rises above HI.
  108. */
  109. if (real_r >= float_to_fixpt(CONTRCURR_PID_CUTOFF_HI)) {
  110. contrcurr.r_state = RSTATE_DISABLED;
  111. lp_filter_fixpt_set(&contrcurr.model, r);
  112. }
  113. break;
  114. }
  115. debug_report_int8(DEBUG_PFX1("rs"), &contrcurr.old_r_state,
  116. (int8_t)contrcurr.r_state);
  117. debug_report_fixpt(DEBUG_PFX1("cr2"),
  118. &contrcurr.old_current_used_feedback, r);
  119. /* Run the PID controller */
  120. y = pid_run(&contrcurr.pid, dt, r);
  121. if (contrcurr.restricted) {
  122. /* We are in restricted mode.
  123. * Limit current to the restricted lower area. */
  124. if (y > float_to_fixpt(CONTRCURR_RESTRICT_MAXCURR))
  125. y = float_to_fixpt(CONTRCURR_RESTRICT_MAXCURR);
  126. }
  127. debug_report_fixpt(DEBUG_PFX1("cy"),
  128. &contrcurr.old_current_control, y);
  129. /* Reconfigure the PWM unit to output the
  130. * requested heater current (y). */
  131. contrcurr.prev_y = y;
  132. pwmcurr_set(y);
  133. }
  134. void contrcurr_set_feedback(fixpt_t r)
  135. {
  136. if (r != contrcurr.feedback) {
  137. contrcurr.feedback = r;
  138. debug_report_fixpt(DEBUG_PFX1("cr1"),
  139. &contrcurr.old_current_real_feedback, r);
  140. }
  141. /* Run the controller. */
  142. contrcurr_run(r);
  143. }
  144. void contrcurr_set_setpoint(fixpt_t w)
  145. {
  146. pid_set_setpoint(&contrcurr.pid, w);
  147. }
  148. void contrcurr_set_restricted(bool restricted)
  149. {
  150. contrcurr.restricted = restricted;
  151. }
  152. void contrcurr_set_enabled(bool enable,
  153. uint8_t disabled_curr_percent)
  154. {
  155. fixpt_t y;
  156. if (enable != contrcurr.enabled) {
  157. contrcurr.enabled = enable;
  158. /* Reset the controller. */
  159. pwmcurr_set(int_to_fixpt(0));
  160. pid_reset(&contrcurr.pid);
  161. contrcurr.r_state = RSTATE_ENABLED;
  162. contrcurr.prev_y = int_to_fixpt(0);
  163. timer_set_now(&contrcurr.timer);
  164. }
  165. if (!enable) {
  166. /* The controller is disabled.
  167. * Set the disabled-current permanently.
  168. */
  169. y = scale(disabled_curr_percent,
  170. 0, 100,
  171. float_to_fixpt(CONTRCURR_NEGLIM),
  172. float_to_fixpt(CONTRCURR_POSLIM));
  173. pwmcurr_set(y);
  174. }
  175. }
  176. void contrcurr_set_emerg(uint8_t emergency_flags)
  177. {
  178. if (emergency_flags != contrcurr.emergency_flags) {
  179. contrcurr.emergency_flags = emergency_flags;
  180. if (emergency_flags) {
  181. /* In an emergency situation, disable the
  182. * heater current to avoid damage.
  183. */
  184. pwmcurr_set(float_to_fixpt(CONTRCURR_NEGLIM));
  185. }
  186. }
  187. }
  188. uint8_t contrcurr_get_emerg(void)
  189. {
  190. return contrcurr.emergency_flags;
  191. }
  192. void contrcurr_init(void)
  193. {
  194. struct pid_k_set k_set;
  195. k_set = contrcurr_factors;
  196. pid_init(&contrcurr.pid,
  197. #if CONF_DEBUG
  198. PSTR("pid-c"),
  199. #endif
  200. &k_set,
  201. float_to_fixpt(CONTRCURR_NEGLIM_I),
  202. float_to_fixpt(CONTRCURR_POSLIM_I),
  203. float_to_fixpt(CONTRCURR_NEGLIM),
  204. float_to_fixpt(CONTRCURR_POSLIM));
  205. /* Enable the controller. */
  206. contrcurr_set_enabled(true, 0);
  207. contrcurr_set_emerg(false);
  208. /* Assume the iron is cold and start
  209. * with restricted current.
  210. */
  211. contrcurr_set_restricted(true);
  212. }