/*************************************************************************** * Copyright (C) 01/2009 by Olaf Rempel * * razzor@kopf-tisch.de * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; version 2 of the License, * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include #include "alix-usv.h" #include "eeprom.h" #include "usi-i2c-slave.h" extern volatile struct ee_param params; static const uint8_t adc_mux[] = { CH_CURRENT_P, CH_CURRENT_N, CH_VOLTAGE_BAT, CH_VOLTAGE_SUP, }; static volatile uint8_t adc_update; static volatile int16_t adc_value[3]; enum { ADC_CURRENT = 0, ADC_UBAT = 1, ADC_UIN = 2, }; static volatile uint8_t sys_state; enum { STATE_IDLE = 0x01, STATE_TEST = 0x02, STATE_CHARGE = 0x04, STATE_DISCHARGE = 0x08, STATE_POWEROFF = 0x10, }; static uint32_t adc_buf[4]; ISR(ADC_vect) { static uint8_t cnt; /* use result of second conversion (switching ADC-gain needs time..) */ if (cnt & 0x01) { /* moving average filter */ uint8_t ch = (cnt >> 1); adc_buf[ch] = ((adc_buf[ch] * 7) + ((uint32_t)ADC << 8)) /8; uint16_t value = (adc_buf[ch] >> 8) + ((adc_buf[ch] & 0x80) ? 1 : 0); if (ch == 0 && (adc_buf[0] >= adc_buf[1])) { /* charging: positive current = ADC * 1.25 */ adc_value[ADC_CURRENT] = value + (value >> 2); } else if (ch == 1 && (adc_buf[0] < adc_buf[1])) { /* discharging: negative current = ADC * -1.25 */ adc_value[ADC_CURRENT] = -(value + (value >> 2)); } else if (ch == 2) { /* Ubat = (ADC * 21.28) - (Ishunt * 0.1) */ adc_value[ADC_UBAT] = (value * 21) + ((value * 9) >> 5) - (adc_value[ADC_CURRENT] / 10); } else if (ch == 3) { /* Uin = ADC * 21.28 */ adc_value[ADC_UIN] = (value * 21) + ((value * 9) >> 5); /* all values updated */ adc_update = 1; } } /* trigger next conversion */ cnt = (cnt +1) & 0x07; ADMUX = adc_mux[cnt >> 1]; ADCSRA |= (1< discharge */ if (sys_state & (STATE_IDLE | STATE_TEST | STATE_CHARGE)) if (adc_value[ADC_UIN] < params.uin_loss) sys_state = STATE_DISCHARGE; /* external power restored => charge */ if (sys_state & (STATE_DISCHARGE | STATE_POWEROFF)) if (adc_value[ADC_UIN] > params.uin_restore) sys_state = STATE_CHARGE; /* battery is low => charge */ if (sys_state & (STATE_IDLE | STATE_TEST)) if (adc_value[ADC_UBAT] < params.ubat_low) sys_state = STATE_CHARGE; switch (sys_state) { case STATE_IDLE: PORTB = EN_POWER; break; case STATE_TEST: PORTB = EN_POWER | EN_TEST; break; case STATE_CHARGE: PORTB = EN_POWER | EN_CHARGER; /* battery is full => idle */ if (adc_value[ADC_UBAT] > params.ubat_full && adc_value[ADC_CURRENT] < params.ibat_full) sys_state = STATE_IDLE; break; case STATE_DISCHARGE: PORTB = EN_POWER; /* battery is critical low => poweroff */ if (adc_value[ADC_UBAT] < params.ubat_critical) sys_state = STATE_POWEROFF; break; case STATE_POWEROFF: PORTB = 0x00; break; default: sys_state = STATE_IDLE; break; } /* re-enable interrupts */ sei(); } }