battery.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. /*
  2. * Battery management
  3. *
  4. * Copyright (c) 2018-2020 Michael Buesch <m@bues.ch>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License along
  17. * with this program; if not, write to the Free Software Foundation, Inc.,
  18. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  19. */
  20. #include "compat.h"
  21. #include "debug.h"
  22. #include "battery.h"
  23. #include "main.h"
  24. #include "arithmetic.h"
  25. #include "pwm.h"
  26. #include "adc.h"
  27. #include "watchdog.h"
  28. #include "curve.h"
  29. #include "curve_data_sp2batdrop.h"
  30. #include "movingavg.h"
  31. #define BAT_AVERAGE 3u
  32. static struct {
  33. DEFINE_MOVINGAVG(movingavg, BAT_AVERAGE);
  34. uint16_t interval_ms;
  35. uint16_t elapsed_ms;
  36. uint16_t avg_mv;
  37. uint16_t drop_mv;
  38. bool voltage_critical;
  39. } bat;
  40. /* Battery voltages below this threshold are critical: */
  41. #define BAT_CRITICAL_MIN_MV 3200u /* millivolts */
  42. /* Hysteresis for leaving critical-min state. */
  43. #define BAT_CRITICAL_HYST_MV 300u /* millivolts */
  44. /* Battery voltages above this threshold are not plausible: */
  45. #define BAT_PLAUS_MAX_MV 6500u /* millivolts */
  46. /* The maximum allowed voltage drop from the drop model. */
  47. #define BAT_DROP_MODEL_MAX_MV 400u /* millivolts */
  48. /* Battery monitoring intervals (in seconds). */
  49. #if 1
  50. #define BAT_INT_ON 3 /* During PWM output on */
  51. #define BAT_INT_OFF (60 * 2) /* During PWM output off (setpoint=0) */
  52. #define BAT_INT_CRIT (60 * 3) /* During low battery */
  53. #else
  54. #define BAT_INT_ON 1
  55. #define BAT_INT_OFF 1
  56. #define BAT_INT_CRIT 1
  57. #endif
  58. /* Set the interval that the battery voltage should be measured in.
  59. * Interrupts shall be disabled before calling this function. */
  60. void set_battery_mon_interval(uint16_t seconds)
  61. {
  62. if (USE_BAT_MONITOR)
  63. bat.interval_ms = lim_u16((uint32_t)seconds * 1000u);
  64. }
  65. /* Update the battery measurement interval based on the PWM setpoint.
  66. * Interrupts shall be disabled before calling this function. */
  67. void battery_update_setpoint(void)
  68. {
  69. uint8_t i;
  70. bool any_sp_nonzero;
  71. if (USE_BAT_MONITOR) {
  72. /* Reconfigure the battery measurement interval. */
  73. if (battery_voltage_is_critical()) {
  74. set_battery_mon_interval(BAT_INT_CRIT);
  75. } else {
  76. any_sp_nonzero = false;
  77. for (i = 0u; i < NR_PWM; i++)
  78. any_sp_nonzero |= pwm_sp_get(IF_MULTIPWM(i)) > 0u;
  79. if (any_sp_nonzero)
  80. set_battery_mon_interval(BAT_INT_ON);
  81. else
  82. set_battery_mon_interval(BAT_INT_OFF);
  83. }
  84. }
  85. }
  86. /* Returns true, if the battery voltage reached a critical level. */
  87. bool battery_voltage_is_critical(void)
  88. {
  89. return bat.voltage_critical && USE_BAT_MONITOR;
  90. }
  91. /* Evaluate the measured battery voltage.
  92. * May be called with interrupts enabled. */
  93. void evaluate_battery_voltage(uint16_t vcc_mv)
  94. {
  95. uint16_t setpoint;
  96. uint16_t drop_mv;
  97. uint16_t avg_vcc_mv;
  98. uint16_t noload_vcc_mv;
  99. uint8_t irq_state;
  100. uint8_t i;
  101. if (USE_BAT_MONITOR) {
  102. /* Calculate moving average. */
  103. avg_vcc_mv = movingavg_calc(&bat.movingavg, vcc_mv);
  104. /* Get the active setpoint.
  105. * If the battery voltage already is critical
  106. * and PWM is turned off, then this will be 0.
  107. * We add all PWM setpoints.
  108. * That's not physically correct, but good enough for now. */
  109. setpoint = 0u;
  110. for (i = 0u; i < NR_PWM; i++)
  111. setpoint = add_sat_u16(setpoint, pwm_sp_get(IF_MULTIPWM(i)));
  112. /* Calculate the battery voltage that we would have without load.
  113. * by adding the drop voltage from the drop model. */
  114. drop_mv = curve_interpolate(sp2batdrop_curve,
  115. ARRAY_SIZE(sp2batdrop_curve),
  116. setpoint);
  117. drop_mv = min(drop_mv, BAT_DROP_MODEL_MAX_MV);
  118. noload_vcc_mv = add_sat_u16(avg_vcc_mv, drop_mv);
  119. dprintf("Battery: vcc_mv=%u avg_vcc_mv=%u drop_mv=%u noload_vcc_mv=%u\r\n",
  120. vcc_mv, avg_vcc_mv, drop_mv, noload_vcc_mv);
  121. /* Evaluate the no-load battery voltage and set the
  122. * critical flag, if needed. */
  123. irq_state = irq_disable_save();
  124. if (noload_vcc_mv >= (BAT_CRITICAL_MIN_MV + BAT_CRITICAL_HYST_MV)) {
  125. if (bat.voltage_critical)
  126. dprintf("Battery voltage not critical anymore.\r\n");
  127. bat.voltage_critical = false;
  128. }
  129. if (noload_vcc_mv < BAT_CRITICAL_MIN_MV) {
  130. if (!bat.voltage_critical)
  131. dprintf("Battery voltage critical: Under-voltage.\r\n");
  132. bat.voltage_critical = true;
  133. }
  134. if (noload_vcc_mv > BAT_PLAUS_MAX_MV) {
  135. if (!bat.voltage_critical)
  136. dprintf("Battery voltage critical: Over-voltage.\r\n");
  137. bat.voltage_critical = true;
  138. }
  139. bat.avg_mv = avg_vcc_mv;
  140. bat.drop_mv = drop_mv;
  141. irq_restore(irq_state);
  142. }
  143. }
  144. /* Get the current battery voltage.
  145. * May be called with interrupts enabled. */
  146. void battery_get_voltage(uint16_t *avg_mv, uint16_t *drop_mv)
  147. {
  148. uint8_t irq_state;
  149. if (USE_BAT_MONITOR) {
  150. irq_state = irq_disable_save();
  151. *avg_mv = bat.avg_mv;
  152. *drop_mv = bat.drop_mv;
  153. irq_restore(irq_state);
  154. } else
  155. *avg_mv = *drop_mv = 0;
  156. }
  157. /* A watchdog interrupt just occurred. */
  158. void battery_handle_watchdog_interrupt(void)
  159. {
  160. if (USE_BAT_MONITOR) {
  161. /* Check if we need to measure the battery voltage. */
  162. bat.elapsed_ms = add_sat_u16(bat.elapsed_ms, watchdog_interval_ms());
  163. if (bat.elapsed_ms >= bat.interval_ms) {
  164. bat.elapsed_ms = 0;
  165. /* Must be called with interrupts disabled. */
  166. adc_request_battery_measurement();
  167. }
  168. }
  169. }
  170. void battery_init(void)
  171. {
  172. if (USE_BAT_MONITOR) {
  173. movingavg_init(&bat.movingavg, BAT_AVERAGE);
  174. set_battery_mon_interval(0);
  175. }
  176. }