/ src / battswitch.c
battswitch.c
  1  /*
  2   Battery Voltage Watchdog for AVR AtTiny45
  3  
  4   Copyright (C)
  5     2015,                                Christian Thäter <ct@pipapo.org>
  6  
  7   battswitch 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 3 of the License, or
 10   (at your option) any later version.
 11  
 12   batswitch 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
 18   along with simavr.  If not, see <http://www.gnu.org/licenses/>.
 19  */
 20  
 21  /*
 22    Program description
 23  
 24    Everything is a state machine, transitions are only between states.
 25  
 26    The main() loop is woken by interrupts. Each loop iteration in main handles the ongoing things (checking current and voltage) for each states.
 27  
 28    entering a new state is done by calling change_state() with new state. change_state() is reponsible for setting up the new state, configuring I/O's, enabling Interrupts etc.
 29  
 30    Measuring voltage and current is done by a timer and interrupt driven, values are written into global variables and picked up in the main loop from there.
 31  */
 32  
 33  #include <stdint.h>
 34  #include <stdbool.h>
 35  
 36  #include <avr/io.h>
 37  #include <avr/boot.h>
 38  #include <avr/interrupt.h>
 39  #include <avr/sleep.h>
 40  #include <avr/eeprom.h>
 41  #include <avr/power.h>
 42  
 43  
 44  /*
 45    Configuration Defaults
 46  */
 47  
 48  #define TIMER_TICK                              60      // slow time ticks at 75kHz or 128kHz / 1024 / TIMER_TICK
 49  
 50  #define VOLTAGE_INTEGRATION                     8       // Voltage time integration interval, maximum 64
 51  
 52  #define CURRENT_INTEGRATION                     16      // Continous time integration interval
 53  
 54  #define CALIBRATION_VOLTAGE_mV                  15000
 55  
 56  #define ERROR_RESTART_TRIES                     3       // How many retries restarting after protection triggered
 57  #define ERROR_RESTART_COOLDOWN_TICKS            128     // How long to wait before restarting
 58  
 59  
 60  /*
 61    Default Voltage levels per cell
 62   */
 63  #define BATTERY_LEVEL_FULL      4000U
 64  #define BATTERY_LEVEL_HALF      3700U
 65  #define BATTERY_LEVEL_QUARTER   3600U
 66  #define BATTERY_LEVEL_RESERVE   3500U
 67  #define BATTERY_LEVEL_EMPTY     3400U
 68  #define BATTERY_LEVEL_DEAD      3200U
 69  
 70  
 71  /* I/O pins */
 72  #define PORTB_OUTPUT            _BV(PORTB0)
 73  #define PORTB_GREEN             _BV(PORTB1)
 74  #define PORTB_RED               _BV(PORTB2)
 75  #define PORTB_YELLOW            PORTB_GREEN | PORTB_RED
 76  
 77  #define PINB_GREEN              _BV(PINB1)
 78  #define PINB_RED                _BV(PINB2)
 79  #define PINB_YELLOW             PINB_RED | PINB_GREEN
 80  
 81  #define DDRB_OUTPUT             _BV(DDB0)
 82  #define DDRB_GREEN              _BV(DDB1)
 83  #define DDRB_RED                _BV(DDB2)
 84  
 85  #define DIDR_VOLTAGE            _BV(ADC3D)
 86  #define DIDR_CURRENT            _BV(ADC2D)
 87  
 88  #define MUX_VOLTAGE             PB3     //ADC3
 89  #define MUX_CURRENT             PB2     //ADC2
 90  
 91  #define VOLTAGE_DIVIDER_HI_OHM  5000UL
 92  #define VOLTAGE_DIVIDER_LO_OHM  330UL
 93  
 94  
 95  /*
 96    end of configuration
 97  */
 98  
 99  #define MUX_REF_1v1             _BV(REFS1)
100  #define REFERENCE_VOLTAGE       1100UL
101  #define MAX_VOLTAGE             (REFERENCE_VOLTAGE*(VOLTAGE_DIVIDER_LO_OHM+VOLTAGE_DIVIDER_HI_OHM)/VOLTAGE_DIVIDER_LO_OHM)
102  
103  #define MILLIVOLT_TO_ADC(mV)    (uint16_t)((mV)*1024UL/(MAX_VOLTAGE/VOLTAGE_INTEGRATION))
104  
105  // Calculate cell count from voltage, for 2S to 4S, caveat, this only works with cells which are above 3.2V
106  // Hooking up a completely empty cell gives the wrong cell count. One should look at the cell count blinks
107  // at startup when the state of the battery is uncertain
108  #define CELL_COUNT(adc)         (((adc) + MILLIVOLT_TO_ADC(4175U)) / MILLIVOLT_TO_ADC(4225U))
109  
110  #define VOLTAGE_CALIBRATION_DEFAULT             MILLIVOLT_TO_ADC(CALIBRATION_VOLTAGE_mV)
111  #define CURRENT_LIMIT_DEFAULT                   16*CURRENT_INTEGRATION
112  #define POWER_LIMIT_DEFAULT                     16*CURRENT_INTEGRATION * VOLTAGE_CALIBRATION_DEFAULT
113  
114  #define TIMER_OFF       0
115  #define TIMER_NODIV     _BV(CS00)
116  #define TIMER_DIV8      _BV(CS01)
117  #define TIMER_DIV64     _BV(CS01) | _BV(CS00)
118  #define TIMER_DIV256    _BV(CS02)
119  #define TIMER_DIV1024   _BV(CS02) | _BV(CS00)
120  
121  
122  
123  /*
124    states and possible state transitions
125  */
126  enum states {
127    STATE_INIT,                   // booting ->  STATE_CALIBRATE, STATE_ERROR, STATE_START
128    STATE_CALIBRATE,              // calibration -> STATE_ERROR, STATE_RESTART
129    STATE_START,                  // Fresh battery -> STATE_FULL, STATE_RESTART
130    STATE_FULL,                   // Full Battery -> STATE_HALF, STATE_RESTART
131    STATE_HALF,                   // Half Full Battery -> STATE_RESERVE, STATE_RESTART
132    STATE_QUARTER,                // 25% Full Battery -> STATE_RESERVE, STATE_RESTART
133    STATE_RESERVE,                // Almost Empty Battery -> STATE_EMPTY, STATE_RESTART
134    STATE_EMPTY,                  // Battery Empty -> STATE_DEAD, STATE_RESTART
135    STATE_DEAD,                   // Dangerously Empty Battery, all LED's off, MPU stopped in low power mode (or red blinking?)
136    STATE_RESTART,                // Restartable Error -> STATE_START, STATE_ERROR
137    STATE_ERROR,                  // Error (overcurrent) given up, or calibration error -> STATE_DEAD
138  };
139  
140  
141  /*
142    Calibration data is stored in EEprom and loaded on boot
143  */
144  struct calibration_data
145  {
146    uint16_t voltage_calibration;
147    uint16_t current_limit;
148    uint32_t power_limit;
149  };
150  
151  /* configuration data is stored in EEPROM */
152  struct
153  {
154    int16_t do_not_use;
155    struct calibration_data saved_calibration;
156  } eeprom EEMEM = {
157    0xefbe, /* don't use the first 2 bytes as these could be overwritten in rare cases */
158    {
159      .voltage_calibration = MILLIVOLT_TO_ADC(CALIBRATION_VOLTAGE_mV),
160      .current_limit = CURRENT_LIMIT_DEFAULT,
161      .power_limit = POWER_LIMIT_DEFAULT,
162    }
163  };
164  
165  /* no need fot initializing variables, we do all on our own */
166  #define NOINIT __attribute__ ((section (".noinit")))
167  
168  /* Global vars */
169  register uint8_t the_state asm("r2");
170  uint8_t mcusr NOINIT;                                   /* preserved status register, needed for detecting reboots from reset */
171  volatile uint16_t the_voltage NOINIT;                   /* adc reads integrated over VOLTAGE_INTEGRATION time */
172  volatile uint16_t the_current NOINIT;                   /* adc reads integrated over CURRENT_INTEGRATION time */
173  uint16_t next_voltage_level NOINIT;
174  struct calibration_data the_calibration NOINIT;
175  uint8_t the_cell_count NOINIT;
176  uint8_t restart_tries_left NOINIT;
177  uint8_t reset_count NOINIT;
178  volatile uint8_t blink NOINIT;                          /* every bit set here will be toggled with the timer overflow interrupt, blinking the output */
179  volatile uint8_t timer_counter NOINIT;                  /* the Timer Overflow interrupt will increment this, used for counting longer time intervals */
180  
181  
182  /* functions */
183  static void change_state (uint8_t new_state);
184  static void init (void);
185  static void timer_sleep (const uint8_t ticks, const uint8_t mode);
186  static uint16_t adc_sample (uint8_t mux, uint8_t integration);
187  static uint16_t adc_integrate (uint16_t value, uint8_t integration);
188  static void calibrate (void);
189  
190  
191  /*
192    Changing state to a new state, do all necessary initalization and configuration
193  */
194  void
195  change_state (uint8_t new_state)
196  {
197    // TODO delay port updates to main loop
198    blink = 0;
199  
200    switch (new_state)
201      {
202      case STATE_INIT:
203        init ();
204        break;
205  
206      case STATE_CALIBRATE:
207        PORTB = PORTB_YELLOW;
208        blink = PINB_RED;
209        break;
210  
211      case STATE_START:
212        PORTB = PORTB_OUTPUT;
213        the_current = 0;
214        next_voltage_level = MILLIVOLT_TO_ADC (BATTERY_LEVEL_FULL);
215        break;
216  
217      case STATE_FULL:
218        PORTB = PORTB_OUTPUT | PORTB_GREEN;
219        next_voltage_level = MILLIVOLT_TO_ADC (BATTERY_LEVEL_HALF);
220        break;
221  
222      case STATE_HALF:
223        PORTB = PORTB_OUTPUT | PORTB_YELLOW;
224        next_voltage_level = MILLIVOLT_TO_ADC (BATTERY_LEVEL_QUARTER);
225        break;
226  
227      case STATE_QUARTER:
228        PORTB = PORTB_OUTPUT | PORTB_RED;
229        next_voltage_level = MILLIVOLT_TO_ADC (BATTERY_LEVEL_RESERVE);
230        break;
231  
232      case STATE_RESERVE:
233        PORTB = PORTB_OUTPUT | PORTB_RED;
234        blink = PINB_RED;
235        next_voltage_level = MILLIVOLT_TO_ADC (BATTERY_LEVEL_EMPTY);
236        break;
237  
238      case STATE_EMPTY:
239        PORTB = PORTB_RED;
240        next_voltage_level = MILLIVOLT_TO_ADC (BATTERY_LEVEL_DEAD);
241        break;
242  
243      case STATE_DEAD:
244        PORTB = PORTB_RED;
245        blink = PINB_RED;
246        ADCSRA = 0;
247        PRR = _BV (PRTIM1) | _BV(PRUSI) | _BV(PRADC);
248        break;
249  
250      case STATE_RESTART:
251        PORTB = PORTB_YELLOW;
252        blink = PORTB_YELLOW;
253        break;
254  
255      case STATE_ERROR:
256        PORTB = PORTB_RED;
257        blink = PINB_RED;
258        break;
259      }
260  
261    the_state = new_state;
262  }
263  
264  static void
265  set_clkdiv (void)
266  {
267    uint8_t lfuse = boot_lock_fuse_bits_get (GET_LOW_FUSE_BITS);
268  
269    if (lfuse & ~FUSE_CKSEL2)
270      {
271        /* production, 128kHz / 1 */
272        clock_prescale_set (clock_div_1);
273      }
274    else //  if (lfuse & FUSE_CKSEL1)
275      {
276        // development, 8Mhz / 64 = 125khz
277        clock_prescale_set (clock_div_64);
278      }
279  }
280  
281  
282  int __attribute__((OS_main))
283  main (void)
284  {
285    set_clkdiv ();
286  
287    //  the_state = STATE_INIT;            // uninitialized, but not need
288    change_state (STATE_INIT);
289  
290    for (;;)
291      {
292        uint16_t calibrated_voltage = (uint32_t)the_voltage*MILLIVOLT_TO_ADC(CALIBRATION_VOLTAGE_mV)/the_calibration.voltage_calibration;
293  
294        switch (the_state)
295          {
296          case STATE_INIT:
297  
298            if (mcusr & _BV(PORF))
299              reset_count = 0;
300            else
301              ++reset_count;
302  
303            if (mcusr & _BV(EXTRF))
304              {
305                change_state (STATE_CALIBRATE);
306              }
307            else
308              {
309                // blink cell count
310                blink = PINB_GREEN;
311                timer_sleep (2*the_cell_count-1, SLEEP_MODE_IDLE);
312                blink = 0;
313  
314                timer_sleep (2, SLEEP_MODE_IDLE);
315                change_state (STATE_START);
316              }
317            continue;
318  
319          case STATE_CALIBRATE:
320            calibrate();
321            change_state (STATE_RESTART);
322            continue;
323  
324          case STATE_START:
325          case STATE_FULL:
326          case STATE_HALF:
327          case STATE_QUARTER:
328          case STATE_RESERVE:
329            if ((uint32_t)the_current*calibrated_voltage > the_calibration.power_limit ||
330                (uint32_t)the_current * MILLIVOLT_TO_ADC (CALIBRATION_VOLTAGE_mV) / calibrated_voltage > the_calibration.current_limit
331                )
332              {
333                change_state (STATE_RESTART);
334                continue;
335              }
336            /*else fallthrough */
337  
338          case STATE_EMPTY:
339            if (calibrated_voltage/the_cell_count < next_voltage_level)
340              {
341                change_state (the_state+1);
342                continue;
343              }
344            break;
345          case STATE_DEAD:
346            for (;;)
347              {
348                OCR0A = 1;
349                timer_sleep (1, SLEEP_MODE_IDLE);
350                OCR0A = 255;
351                timer_sleep (1, SLEEP_MODE_IDLE);
352              }
353            break;
354  
355          case STATE_RESTART:
356            if (--restart_tries_left)
357              {
358                timer_sleep (ERROR_RESTART_COOLDOWN_TICKS, SLEEP_MODE_IDLE);
359                change_state (STATE_START);
360              }
361            else
362              {
363                change_state (STATE_ERROR);
364              }
365            continue;
366  
367          default:
368          case STATE_ERROR:
369            if (calibrated_voltage/the_cell_count < MILLIVOLT_TO_ADC (BATTERY_LEVEL_DEAD))
370              {
371                change_state (STATE_DEAD);
372                continue;
373              }
374            break;
375          }
376        timer_sleep (2, SLEEP_MODE_IDLE);
377      }
378  }
379  
380  
381  void
382  calibrate (void)
383  {
384    uint16_t c_current;
385    uint16_t c_voltage;
386  
387  
388    // initial blinking
389    timer_sleep (20, SLEEP_MODE_IDLE);
390    blink = 0;
391  
392    switch (reset_count%3)
393      {
394      case 0:
395        // voltage calibration
396        PORTB = PORTB_GREEN;
397        blink = 0;
398        timer_sleep (2, SLEEP_MODE_IDLE);
399        the_calibration.voltage_calibration = adc_sample (MUX_REF_1v1 | MUX_VOLTAGE, VOLTAGE_INTEGRATION);
400        PORTB = PORTB_RED;
401        blink = PINB_YELLOW;
402        timer_sleep (20, SLEEP_MODE_IDLE);
403        eeprom_update_block (&the_calibration, &eeprom.saved_calibration, sizeof (the_calibration));
404  
405      case 1:
406        // current calibration
407        PORTB = PORTB_YELLOW;
408        blink = 0;
409        timer_sleep (1, SLEEP_MODE_IDLE);
410        PORTB |= PORTB_OUTPUT;
411        timer_sleep (1, SLEEP_MODE_IDLE);
412        c_current = adc_sample (MUX_REF_1v1 | MUX_CURRENT, CURRENT_INTEGRATION);
413        c_voltage = adc_sample (MUX_REF_1v1 | MUX_VOLTAGE, VOLTAGE_INTEGRATION);
414        the_calibration.current_limit = (uint32_t)c_current * MILLIVOLT_TO_ADC (CALIBRATION_VOLTAGE_mV) / c_voltage;
415        PORTB = PORTB_RED;
416        blink = PINB_YELLOW;
417        timer_sleep (20, SLEEP_MODE_IDLE);
418        eeprom_update_block (&the_calibration, &eeprom.saved_calibration, sizeof (the_calibration));
419  
420      case 2:
421        // power calibration
422        PORTB = PORTB_RED;
423        blink = 0;
424        timer_sleep (1, SLEEP_MODE_IDLE);
425        PORTB |= PORTB_OUTPUT;
426        timer_sleep (1, SLEEP_MODE_IDLE);
427        c_current = adc_sample (MUX_REF_1v1 | MUX_CURRENT, CURRENT_INTEGRATION);
428        c_voltage = adc_sample (MUX_REF_1v1 | MUX_VOLTAGE, VOLTAGE_INTEGRATION);
429        the_calibration.power_limit = (uint32_t)c_current * c_voltage;
430        PORTB = PORTB_RED;
431        blink = PINB_YELLOW;
432        timer_sleep (20, SLEEP_MODE_IDLE);
433        eeprom_update_block (&the_calibration, &eeprom.saved_calibration, sizeof (the_calibration));
434      }
435  
436  }
437  
438  void
439  timer_sleep (const uint8_t ticks, const uint8_t mode)
440  {
441    set_sleep_mode (mode);
442    timer_counter = 0;
443  
444    while (timer_counter < ticks)
445      {
446        sleep_cpu();
447      }
448  }
449  
450  
451  uint16_t
452  adc_sample (uint8_t mux, uint8_t integration)
453  {
454    uint16_t value = 0;
455    uint8_t adcsra = ADCSRA;
456    uint8_t adcsrb = ADCSRB;
457    uint8_t oldmux = ADMUX;
458  
459    ADCSRA = 0;
460    ADMUX = mux;
461  
462    ADCSRA = _BV(ADEN);
463  
464    // throw away the first readings
465    for (uint8_t i = 255; --i;)
466      {
467        ADCSRA |= _BV(ADSC);
468  
469        while (ADCSRA & _BV(ADSC));
470  
471        if (i < integration+1)
472          {
473            value += ADCL;
474            value += (uint16_t)ADCH*256;
475          }
476      }
477    ADCSRA = 0;
478    ADMUX = oldmux;
479    ADCSRB = adcsrb;
480    ADCSRA = adcsra;
481  
482    return value;
483  }
484  
485  
486  uint16_t
487  adc_integrate (uint16_t value, uint8_t integration)
488  {
489    value -= (value / integration);
490    value += ADCL;
491    value += (uint16_t)ADCH*256;
492    return value;
493  }
494  
495  
496  
497  void
498  init (void)
499  {
500    mcusr = MCUSR;
501    MCUSR = 0;
502  
503    // Power Reduction, shut off unneeded hardware
504    PRR = _BV (PRTIM1) | _BV(PRUSI);
505  
506    //initialize IO's
507    DDRB = DDRB_OUTPUT | DDRB_GREEN | DDRB_RED;
508    PORTB = PORTB_GREEN;
509    DIDR0 = DIDR_VOLTAGE | DIDR_CURRENT;
510  
511    eeprom_read_block (&the_calibration, &eeprom.saved_calibration, sizeof (the_calibration));
512  
513    restart_tries_left = ERROR_RESTART_TRIES+1;
514    blink = 0;
515  
516    sei ();
517  
518    the_voltage = adc_sample (MUX_REF_1v1 | MUX_VOLTAGE, VOLTAGE_INTEGRATION);
519  
520    the_cell_count = CELL_COUNT (the_voltage);
521  
522    // initialize timer
523    timer_counter = 0;
524    TIMSK = _BV(OCIE0A);
525    OCR0A = TIMER_TICK;
526    TCCR0A = _BV(WGM01);
527    TCCR0B = TIMER_DIV1024;
528  
529    // init adc, triggered by timer
530    ADMUX = MUX_REF_1v1 | MUX_VOLTAGE;
531    ADCSRB = _BV(ADTS1) | _BV(ADTS0);
532    ADCSRA = _BV(ADEN) | _BV(ADIE) | _BV(ADATE);
533  
534  }
535  
536  
537  
538  ISR (TIM0_COMPA_vect)
539  {
540    ++timer_counter;
541    PINB = blink;
542  }
543  
544  
545  ISR (ADC_vect)
546  {
547    uint8_t mux = ADMUX & (_BV(MUX3) | _BV(MUX2) | _BV(MUX1) |_BV(MUX0));
548  
549    if (mux == MUX_VOLTAGE)
550      {
551        the_voltage = adc_integrate (the_voltage, VOLTAGE_INTEGRATION);
552      }
553    else
554      {
555        the_current = adc_integrate (the_current, CURRENT_INTEGRATION);
556      }
557  
558    switch (the_state)
559      {
560      case STATE_START:
561      case STATE_FULL:
562      case STATE_HALF:
563      case STATE_QUARTER:
564      case STATE_RESERVE:
565        if (mux == MUX_VOLTAGE)
566          {
567            ADMUX = MUX_REF_1v1 | MUX_CURRENT;
568            break;
569          }
570        // else fallthrough
571      default:
572        ADMUX = MUX_REF_1v1 | MUX_VOLTAGE;
573        break;
574      }
575  }
576