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