/**************************************************************************** Device : AT90S4433 File name : LiIon.c Ver nr. : 1.1 Description : Source file for LiIon charging algorithme - FAST_charge - TRICKLE_charge Compiler : GCC 3 Author : Asmund Saetre / Terje Frostad / Dietmar Koenig Modified for GCC by Don Carveth Change log : 02.02.2000 Changed to fit Battery Charger refrence design board AS 17.04.2000 Debugged by AS 16.05.2000 Final test and review AS June 2002 Modified by Don Carveth for GCC *************************************************************************************/ #include "LiIon.h" //Battery specific definitions #include "StdDefs.h" #include "BC.H" #include "SBComm.h" // Extern struct prototypes extern time_struct time; unsigned int CELLS = 1; unsigned int LiIon_CELL_VOLT = 4100; unsigned int CAPACITY = 300; unsigned int MAX_VOLT_ABS; unsigned int MIN_I_FAST; unsigned int VOLT_TOLERANCE; unsigned int I_FAST; unsigned int I_TRICKLE; unsigned int I, V, T, v; unsigned char SBConnected = 0; int sbTemp, sbVolts, sbCurrent, sbChargePercent, sbRemainCap, sbChargeCur, sbChargeVolts, sbStatus, sbCycleCount; // Fast Charge Algorithme //*************************************************************************** void FAST_charge(void) { unsigned int temp = 0; unsigned char fast_finish_hour = 0; unsigned char fast_finish_min = 0; unsigned char delay_hour = 0; unsigned char delay_min = 0; unsigned char last_min = 0; time.sec = 0x00; time.min = 0x00; time.hour = 0x00; time.t_count = 0x27; // Talk to battery - if battery responds use parameters from battery // instead of board settings. if(!GetSmartBattery()) { PrintSmartBattery(); SBConnected = 1; if(sbChargeVolts > 14000) CELLS = 4; else if(sbChargeVolts > 11000) CELLS = 3; else if(sbChargeVolts > 7000) CELLS = 2; else CELLS = 1; LiIon_CELL_VOLT = sbChargeVolts / CELLS; if(sbChargeCur) { CAPACITY = sbChargeCur; } else { // If battery is fully charged (voltage above Charge voltage) // it reports back the charge current as zero. The charger // doesn't like this, so exit. putstr("Bty Full"); LED_ERR_ON(); return; } } else // Use charger board settings GetBatteryData(); // Convert decimal values to 0 - 1023 values for use in ADC calcs. MAX_VOLT_ABS = (unsigned int)((100*CELLS * (long)LiIon_CELL_VOLT)/ VOLTAGE_STEP) + 2; // 80 ma per 1600 mAH capacity CAPACITY/1600 4500/1600 * 8000 / CURRENT_STEP MIN_I_FAST = (unsigned int)((CAPACITY * 5) / CURRENT_STEP); VOLT_TOLERANCE = (unsigned int)(((long)CELLS * 5000)/ VOLTAGE_STEP); // If battery capacity is higher than charger then limit charge rate // to board max charge rate. if(CAPACITY > CHARGER_MAX_CURRENT) CAPACITY = CHARGER_MAX_CURRENT; I_FAST = (unsigned int)(100 * (long)CAPACITY / CURRENT_STEP); I_TRICKLE = (unsigned int)(((long)CAPACITY * 2)/ (CURRENT_STEP * 5)); CRLF(); putstr("Cells: "); putchar(CELLS + 0x30); putstr(" CHG V: "); putBCD(LiIon_CELL_VOLT * CELLS, 6, 2); putstr(" CHG I: "); putBCD(CAPACITY, 6, 1); putstr(" I FAST: "); putBCD(I_FAST, 6, 1); SETBIT(CHARGE_STATUS,CONST_C); CLRBIT(CHARGE_STATUS,DELAY); // if TEMPERATURE within absolute limits temp = Battery(TEMPERATURE); if ((temp < MIN_TEMP_ABS) && (temp > MAX_TEMP_ABS)) { //putchar('T'); //putBCD(temp, 6,1); // if VOLTAGE lower than absolute VOLTAGE temp = Battery(VOLTAGE); if (temp <= (MAX_VOLT_ABS + VOLT_TOLERANCE)) { //putchar('V'); //putBCD(temp, 6,1); // if FAST charge TEMPERATURE high enough temp = Battery(TEMPERATURE); if (temp < MIN_TEMP_FAST) { //putchar('t'); OC_REG_1 = 0x00; TCCR1B = 0x01; //counter1 clock prescale = 1 // calculate FAST charge finish time fast_finish_min = (time.min + MAX_TIME_FAST); fast_finish_hour = time.hour; while (fast_finish_min > 60) { fast_finish_min = fast_finish_min - 60; fast_finish_hour++; } //putchar('f'); //putBCD(CHARGE_STATUS, 6, 1); SETBIT(CHARGE_STATUS,FAST); //Added Atmel while ((CHKBIT(CHARGE_STATUS,FAST)) && (!(CHKBIT(CHARGE_STATUS,ERROR)))) { //putchar('v'); // Charge with constant current algorithme if (CHKBIT(CHARGE_STATUS,CONST_C)) { if(SBConnected) PrintSBEveryX(1000); printout(1000); run_led(50, 15); //fast flash // set I_FAST (with "soft start") do { temp = Battery(CURRENT); if ((temp < I_FAST)&&(OC_REG_1 < 0xFF)) OC_REG_1++; if ((temp > I_FAST)&&(OC_REG_1 > 0x00)) OC_REG_1--; }while (temp != I_FAST); // I_FAST is set now I = (long)temp * CURRENT_STEP / 100; //if VOLTAGE within range change from constant //CURRENT charge mode to constant VOLTAGE charge mode temp = Battery(VOLTAGE); V = (long)temp * VOLTAGE_STEP / 1000; temp = Battery(VOLTAGE_WITH_PWM_TURNOFF); v = (long)temp * VOLTAGE_STEP / 1000; if (temp >= MAX_VOLT_ABS) // Changed from Atmel { CLRBIT(CHARGE_STATUS,CONST_C); SETBIT(CHARGE_STATUS,CONST_V); } } // Charge with constant voltage algorithme else if (CHKBIT(CHARGE_STATUS,CONST_V)) { if(CHKBIT(CHARGE_STATUS,DELAY)) run_led(3000, 13000); else run_led(2500, 2500); if(SBConnected) PrintSBEveryX(5000); printout(5000); // set VOLT_FAST (with "soft start") do { temp = Battery(VOLTAGE); if ((temp < (MAX_VOLT_ABS - VOLT_TOLERANCE/4))&&(OC_REG_1 < 0xFF)) OC_REG_1++; else if ((temp > (MAX_VOLT_ABS + VOLT_TOLERANCE/4))&&(OC_REG_1 > 0x00)) //Changed Atmel OC_REG_1--; }while ((temp <= (MAX_VOLT_ABS -(VOLT_TOLERANCE/4)))||(temp >= (MAX_VOLT_ABS + (VOLT_TOLERANCE/4)))); //Changed Atmel // VOLT_TRICKLE is set now V = (long)temp * VOLTAGE_STEP / 1000; v = V; I = (long)Battery(CURRENT) * CURRENT_STEP / 100; } // Check for error and charge termination conditions //If above max charge time, flag error if ((time.hour == fast_finish_hour) && (time.min >= fast_finish_min)) { //Stop the PWM, flag max time charge termination and //ERROR. Save the termination value and the max limit //value for debug information Stop_PWM(); SETBIT(TERMINATION,TIME_MAX); SETBIT(CHARGE_STATUS,ERROR); putstr("MaxTime"); LED_ERR_ON(); } //If above max charge temperature, flag error temp = Battery(TEMPERATURE); T = temp; if ( temp < MAX_TEMP_ABS) { //Stop the PWM, flag max temperature charge //termination and ERROR. Save the termination value and //the max limit value for debug information Stop_PWM(); SETBIT(TERMINATION,TEMP_MAX); SETBIT(CHARGE_STATUS,ERROR); putstr("HiTemp"); putBCD(temp, 6, 1); LED_ERR_ON(); } //If beyond min charge temperature, flag error if (temp > MIN_TEMP_FAST) { //Stop the PWM, flag min temperature charge //termination and ERROR. Save the termination value and //the max limit value for debug information Stop_PWM(); SETBIT(TERMINATION,TEMP_MIN); SETBIT(CHARGE_STATUS,ERROR); putstr("LowTemp"); putBCD(temp, 6, 1); LED_ERR_ON(); } //Every min check if MIN_I_FAST is reached, if so //calculate the delay time. //Check every 60 seconds if delay time after reached //MIN_I_FAST is over, if so change to trickle charge if (time.min != last_min) { last_min = time.min; if (((CHKBIT(CHARGE_STATUS,CONST_V)) && (!(CHKBIT(CHARGE_STATUS,DELAY))) && (Battery(CURRENT) <= MIN_I_FAST))) { // calculate DELAY finish time delay_min = (time.min + FAST_TIME_DELAY); delay_hour = time.hour; while (delay_min > 60) { delay_min = delay_min - 60; delay_hour++; } SETBIT(CHARGE_STATUS,DELAY); } // Check if delay time after min_I_FAST is done, if so change to trickle charge if ((time.hour == delay_hour)&&(time.min == delay_min)&&(CHKBIT(CHARGE_STATUS,DELAY))) { //Stop PWM and change from constant VOLTAGE charge //mode back to constant CURRENT charge mode. Change //charge mode from "FAST" to "TRICKLE" Save the //termination value and the max limit value for debug //information Stop_PWM(); SETBIT(TERMINATION,DELAY); CLRBIT(CHARGE_STATUS,CONST_V); SETBIT(CHARGE_STATUS,CONST_C); CLRBIT(CHARGE_STATUS,FAST); SETBIT(CHARGE_STATUS,TRICKLE); putstr("DelayTime"); } } } // While } else if(!(CHKBIT(CHARGE_STATUS,ERROR))) { //Flag min temperature termination and ERROR. Save //the termination value and the max limit value for debug //information SETBIT(TERMINATION,TEMP_MIN); SETBIT(CHARGE_STATUS,ERROR); putstr("LowTemp"); putBCD(temp, 6, 1); LED_ERR_ON(); } } else if(!(CHKBIT(CHARGE_STATUS,ERROR))) { //Flag max charge voltage charge termination and ERROR. Save //the termination value and the max limit value for debug //information SETBIT(TERMINATION,VOLT_MAX); SETBIT(CHARGE_STATUS,ERROR); putstr("OverVolt"); putBCD(temp, 6, 1); LED_ERR_ON(); } } else if(!(CHKBIT(CHARGE_STATUS,ERROR))) { if (temp < MIN_TEMP_ABS) { //Flag min temperature termination and save the limit value SETBIT(TERMINATION,TEMP_MIN); putstr("LowTemp"); putBCD(temp, 6, 1); LED_ERR_ON(); } else { //Flag max temperature termination and save the limit value SETBIT(CHARGE_STATUS,TEMP_MAX); putstr("HiTemp"); putBCD(temp, 6, 1); LED_ERR_ON(); } //Flag ERROR and save the measured value causing the error for debug SETBIT(CHARGE_STATUS,ERROR); LED_ERR_ON(); //Turn on error LED } } // Trickle Charge Algorithme //************************************************************************************ void TRICKLE_charge(void) { /* unsigned int temp = 0; unsigned char trickle_finish_min = 0; unsigned char trickle_finish_hour = 0; time.sec = 0x00; time.min = 0x00; time.hour = 0x00; time.t_count = 0x19; OCR1 = 0x00; TCCR1B = 0x01; //counter1 clock prescale = 1 while ((CHKBIT(CHARGE_STATUS,TRICKLE)) && (!(CHKBIT(CHARGE_STATUS,ERROR)))) { // if TEMPERATURE within absolute limits temp = Battery(TEMPERATURE); if ((temp < MIN_TEMP_ABS) || (temp > MAX_TEMP_ABS)) { // if charge voltage lower than absolute max charge voltage if (Battery(VOLTAGE) <= (VOLT_TRICKLE + VOLT_TOLERANCE)) { //Charge with constant current algorithme if (CHKBIT(CHARGE_STATUS,CONST_C)) { // set I_TRICKLE (with "soft start") do { temp = Battery(CURRENT); if ((temp < I_TRICKLE)&&(OCR1 < 0xFF)) { OCR1++; } if ((temp > I_TRICKLE)&&(OCR1 > 0x00)) { OCR1--; } }while (temp != I_TRICKLE); // I_TRICKLE is set now //if VOLTAGE within range change from constant //CURRENT charge mode to constant VOLTAGE charge mode temp = Battery(VOLTAGE_WITH_PWM_TURNOFF); if ((temp >= (MAX_VOLT_ABS - VOLT_TOLERANCE)) && (temp <= (MAX_VOLT_ABS + VOLT_TOLERANCE))) { CLRBIT(CHARGE_STATUS,CONST_C); SETBIT(CHARGE_STATUS,CONST_V); } } //Charge with constant current algorithme if (CHKBIT(CHARGE_STATUS,CONST_V)) { // set VOLT_TRICKLE (with "soft start") do // set VOLT_TRICKLE { temp = Battery(VOLTAGE); if ((temp < MAX_VOLT_ABS)&&(OCR1 < 0xFF)) { OCR1++; } if ((temp > MAX_VOLT_ABS)&&(OCR1 > 0x00)) { OCR1--; } }while ((temp <= (MAX_VOLT_ABS-(VOLT_TOLERANCE/4)))||(temp >= (MAX_VOLT_ABS+(VOLT_TOLERANCE/4)))); // VOLT_TRICKLE is set now } } // Check for error and charge termination conditions if ((time.hour == trickle_finish_hour) && (time.min == trickle_finish_min)) { //Stop the PWM, flag max time charge termination and //ERROR. Save the termination value and the max limit //value for debug information Stop_PWM(); SETBIT(TERMINATION,TIME_MAX); SETBIT(CHARGE_STATUS,ERROR); } temp = Battery(TEMPERATURE); if ( temp < MAX_TEMP_ABS) { //Stop the PWM, flag max temperature charge //termination and ERROR. Save the termination value and //the max limit value for debug information Stop_PWM(); SETBIT(TERMINATION,TEMP_MAX); SETBIT(CHARGE_STATUS,ERROR); } if (temp > MIN_TEMP_FAST) { //Stop the PWM, flag min temperature charge //termination and ERROR. Save the termination value and //the max limit value for debug information Stop_PWM(); SETBIT(TERMINATION,TEMP_MIN); SETBIT(CHARGE_STATUS,ERROR); } } else if(!(CHKBIT(CHARGE_STATUS,ERROR))) { //Flag max charge voltage charge termination and ERROR. Save //the termination value and the max limit value for debug //information SETBIT(TERMINATION,VOLT_MAX); SETBIT(CHARGE_STATUS,ERROR); } } else if(!(CHKBIT(CHARGE_STATUS,ERROR))) { if (temp < MIN_TEMP_ABS) { //Flag min temperature termination and save the limit value SETBIT(TERMINATION,TEMP_MIN); } else { //Flag max temperature termination and save the limit value SETBIT(CHARGE_STATUS,TEMP_MAX); } //Flag ERROR and save the measured value causing the error for debug SETBIT(CHARGE_STATUS,ERROR); } } */ } void GetBatteryData(void) { int temp; if (CUSTOM) // Get settings from switches / pot { CELLS = 1 + (((~PINB) >> 4) & 0x03); putchar('C'); putBCD(CELLS, 6, 1); if (CELL_VOLTS_SWITCH) LiIon_CELL_VOLT = 4200; else LiIon_CELL_VOLT = 4100; putBCD(LiIon_CELL_VOLT, 6, 1); ADMUX = ISET; ADCSR |= 0x40; // start new A/D conversion while (!(ADCSR & (1<> 5) & 0x07, 6, 1); switch (((~PIND) >> 5) & 0x07) { case 0: CELLS = 3; LiIon_CELL_VOLT = 4200; CAPACITY = 1000; break; case 1: CELLS = 3; LiIon_CELL_VOLT = 4200; CAPACITY = 2000; break; case 2: CELLS = 3; LiIon_CELL_VOLT = 4200; CAPACITY = 3000; break; case 3: CELLS = 2; LiIon_CELL_VOLT = 4200; CAPACITY = 800; break; case 4: CELLS = 2; LiIon_CELL_VOLT = 4200; CAPACITY = 1200; break; case 5: CELLS = 2; LiIon_CELL_VOLT = 4200; CAPACITY = 1800; break; case 6: CELLS = 1; LiIon_CELL_VOLT = 4200; CAPACITY = 400; break; case 7: CELLS = 1; LiIon_CELL_VOLT = 4200; CAPACITY = 800; break; } } } // Print charger data every x accesses void printout(int x) { static int c; if (c > x) { putstr("Charger: "); putBCD(time.hour, 2, 0); putchar(':'); putBCD(time.min, 2, 0); putchar(':'); putBCD(time.sec, 2, 0); putchar(' '); putchar('I'); putBCD(I, 6 ,1); putchar('V'); putBCD(V, 6 ,1); putchar('v'); putBCD(v, 6 ,1); putchar('T'); putBCD(T, 6 ,1); CRLF(); c = 0; } else c++; } unsigned char GetSmartBattery(void) { // Use this line to test if smart battery connected if((SBRead(BTY_TEMP) > 2730) && (SBRead(BTY_TEMP) < 3300)) { sbTemp = (SBRead(BTY_TEMP)/10) - 273; sbVolts = SBRead(BTY_VOLTAGE); sbCurrent = SBRead(BTY_CURRENT); sbChargePercent = SBRead(BTY_REL_STATE_CHG); sbRemainCap = SBRead(BTY_REMAIN_CAP); sbChargeCur = SBRead(BTY_CHG_CURRENT); sbChargeVolts = SBRead(BTY_CHG_VOLTS); sbStatus = SBRead(BTY_STATUS); sbCycleCount = SBRead(BTY_CYCLE_COUNT); return 0; } else return 1; // Error - assume smart battery is not connected } void PrintSmartBattery(void) { static int c = 11; if (c > 10) { CRLF(); putstr(" Amps Volts Temp Chg% RemCap ChgmA ChgV Status Cycles"); CRLF(); c = 0; } else c++; putstr("SB: "); putBCD(sbCurrent, 6, 1); putBCD(sbVolts, 6, 1); putBCD(sbTemp, 6, 1); putBCD(sbChargePercent, 6, 1); putBCD(sbRemainCap, 6, 1); putBCD(sbChargeCur, 6, 1); putBCD(sbChargeVolts, 8, 1); putBCD(sbStatus, 6, 1); putBCD(sbCycleCount, 6, 1); CRLF(); } void PrintSBEveryX(int X) { static int c; if (c > X) { GetSmartBattery(); PrintSmartBattery(); c = 0; } else c++; }