From 7d7969114da36a99afc4d80576ff9b089cb09e50 Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Sun, 11 Jan 2009 10:07:22 +0000 Subject: c200v1/e200v1: Add battery charging. This should be usable on v2 players but those should be evaluated for current, endpoint voltage and whether or not accurate battery readings may always be obtained (which determines algorithm setup and behavior). git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19748 a1c6a512-1295-4272-9138-f99709370657 --- firmware/target/arm/powermgmt-ascodec.c | 231 +++++++++++++++++++++ firmware/target/arm/sandisk/powermgmt-target.h | 59 ++++++ .../target/arm/sandisk/sansa-c200/powermgmt-c200.c | 13 +- .../target/arm/sandisk/sansa-e200/powermgmt-e200.c | 11 - 4 files changed, 291 insertions(+), 23 deletions(-) create mode 100644 firmware/target/arm/powermgmt-ascodec.c create mode 100644 firmware/target/arm/sandisk/powermgmt-target.h (limited to 'firmware/target/arm') diff --git a/firmware/target/arm/powermgmt-ascodec.c b/firmware/target/arm/powermgmt-ascodec.c new file mode 100644 index 0000000000..ab9fd7b490 --- /dev/null +++ b/firmware/target/arm/powermgmt-ascodec.c @@ -0,0 +1,231 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2009 by Michael Sevakis + * Copyright (C) 2008 by Bertrik Sikken + * + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "config.h" +#include "system.h" +#include "thread.h" +#include "as3514.h" +#include "ascodec.h" +#include "adc.h" +#include "powermgmt.h" +#include "power.h" + +/*=========================================================================== + * These parameters may be defined per target: + * + * BATT_FULL_VOLTAGE - Upon connect a charge cycle begins if the reading is + * lower than this value (millivolts). + * + * BATT_VAUTO_RECHARGE - While left plugged after cycle completion, the + * charger restarts automatically if the reading drops + * below this value (millivolts). Must be less than + * BATT_FULL_VOLTAGE. + * + * ADC_BATTERY - ADC channel from which to read the battery voltage + * + * BATT_CHG_V - Charger voltage regulation setting (as3514 regval) + * + * BATT_CHG_I - Charger current regulation setting (as3514 regval) + * + * CHARGER_TOTAL_TIMER - Maximum allowed charging time (1/2-second steps) + *=========================================================================== + */ + +/* This code assumes USB power input is not distinguishable from main + * power and charger connect cannot wait for USB configuration before + * considering USB charging available. Where they are distinguishable, + * things get more complicated. */ +static bool charger_close = false; /* Shutting down? */ +static int charger_total_timer = 0; /* Timeout in algorithm steps */ + +/* Current battery threshold for (re)charge: + * First plugged = BATT_FULL_VOLTAGE + * After charge cycle or non-start = BATT_VAUTO_RECHARGE + */ +static unsigned int batt_threshold = 0; + +/* ADC should read 0x3ff=5.12V */ +/* full-scale ADC readout (2^10) in millivolt */ + +/* Returns battery voltage from ADC [millivolts] */ +unsigned int battery_adc_voltage(void) +{ + return (adc_read(ADC_BATTERY) * 5125 + 512) >> 10; +} + +/* Returns true if the unit is charging the batteries. */ +bool charging_state(void) +{ + return charge_state == CHARGING; +} + +/* Reset the battery filter to a new voltage */ +static void battery_voltage_sync(void) +{ + int i; + unsigned int mv; + + for (i = 0, mv = 0; i < 5; i++) + mv += battery_adc_voltage(); + + reset_battery_filter(mv / 5); +} + +/* Disable charger and minimize all settings. Reset timers, etc. */ +static void disable_charger(void) +{ + ascodec_write(AS3514_IRQ_ENRD0, 0); + ascodec_write(AS3514_CHARGER, + TMPSUP_OFF | CHG_I_50MA | CHG_V_3_90V | CHG_OFF); + + if (charge_state > DISCHARGING) + charge_state = DISCHARGING; /* Not an error state already */ + + charger_total_timer = 0; + battery_voltage_sync(); +} + +/* Enable charger with specified settings. Start timers, etc. */ +static void enable_charger(void) +{ + ascodec_write(AS3514_CHARGER, BATT_CHG_I | BATT_CHG_V); + /* Watch for end of charge. Temperature supervision is handled in + * hardware. Charger status can be read and has no interrupt enable. */ + ascodec_write(AS3514_IRQ_ENRD0, CHG_ENDOFCH); + + sleep(HZ/10); /* Allow charger turn-on time (it could be gradual). */ + + ascodec_read(AS3514_IRQ_ENRD0); /* Clear out interrupts (important!) */ + + charge_state = CHARGING; + charger_total_timer = CHARGER_TOTAL_TIMER; + battery_voltage_sync(); +} + +void powermgmt_init_target(void) +{ + /* Everything CHARGER, OFF! */ + ascodec_write(AS3514_IRQ_ENRD0, 0); + ascodec_write(AS3514_CHARGER, + TMPSUP_OFF | CHG_I_50MA | CHG_V_3_90V | CHG_OFF); +} + +static inline void charger_plugged(void) +{ + batt_threshold = BATT_FULL_VOLTAGE; /* Start with topped value. */ + battery_voltage_sync(); +} + +static inline void charger_control(void) +{ + switch (charge_state) + { + case DISCHARGING: + { + unsigned int millivolts; + unsigned int thresh = batt_threshold; + + if (BATT_FULL_VOLTAGE == thresh) + { + /* Wait for CHG_status to be indicated. */ + if ((ascodec_read(AS3514_IRQ_ENRD0) & CHG_STATUS) == 0) + break; + + batt_threshold = BATT_VAUTO_RECHARGE; + } + + millivolts = battery_voltage(); + + if (millivolts <= thresh) + enable_charger(); + break; + } /* DISCHARGING: */ + + case CHARGING: + { + if ((ascodec_read(AS3514_IRQ_ENRD0) & CHG_ENDOFCH) == 0) + { + if (--charger_total_timer > 0) + break; + + /* Timer ran out - require replug. */ + charge_state = CHARGE_STATE_ERROR; + } + /* else end of charge */ + + disable_charger(); + break; + } /* CHARGING: */ + + default: + /* DISABLED, ERROR */ + break; + } +} + +static inline void charger_unplugged(void) +{ + disable_charger(); + if (charge_state >= CHARGE_STATE_ERROR) + charge_state = DISCHARGING; /* Reset error */ +} + +/* Main charging algorithm - called from powermgmt.c */ +void charging_algorithm_step(void) +{ + switch (charger_input_state) + { + case NO_CHARGER: + /* Nothing to do */ + break; + + case CHARGER_PLUGGED: + charger_plugged(); + break; + + case CHARGER: + charger_control(); + break; + + case CHARGER_UNPLUGGED: + charger_unplugged(); + break; + } + + if (charger_close) + { + /* Disable further charging and ack. */ + charge_state = CHARGE_STATE_DISABLED; + disable_charger(); + charger_close = false; + } +} + +/* Disable the charger and prepare for poweroff - called off-thread so we + * signal the charging thread to prepare to quit. */ +void charging_algorithm_close(void) +{ + charger_close = true; + + /* Power management thread will set it false again. */ + while (charger_close) + sleep(HZ/10); +} diff --git a/firmware/target/arm/sandisk/powermgmt-target.h b/firmware/target/arm/sandisk/powermgmt-target.h new file mode 100644 index 0000000000..aa6a0e0e3d --- /dev/null +++ b/firmware/target/arm/sandisk/powermgmt-target.h @@ -0,0 +1,59 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2009 by Michael Sevakis + * Copyright (C) 2008 by Bertrik Sikken + * + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef POWERMGMT_TARGET_H +#define POWERMGMT_TARGET_H + +#if defined(SANSA_C200) +/* This configuration triggers a single charge cycle upon plugging and stops. + * The true voltage cannot be read accurately and so monitoring isn't really + * possible. The battery filter is still synced to have a proper reading + * when disconnecting. */ +#define BATT_FULL_VOLTAGE 5121 /* Won't read this high - force start */ +#define BATT_VAUTO_RECHARGE 0 /* Won't read this low - force one cycle */ +#define BATT_CHG_V CHG_V_4_20V +#define BATT_CHG_I CHG_I_300MA +#define CHARGER_TOTAL_TIMER (4*3600*2) /* 4 hours enough? */ +#define ADC_BATTERY ADC_BVDD +#elif defined(SANSA_E200) +/* PREFERRED - Check if topped-off and monitor voltage while plugged. */ +#define BATT_FULL_VOLTAGE 4160 +#define BATT_VAUTO_RECHARGE 4100 +#define BATT_CHG_V CHG_V_4_20V +#define BATT_CHG_I CHG_I_300MA +#define CHARGER_TOTAL_TIMER (4*3600*2) /* 4 hours enough? */ +/* On e200 ADC_RTCSUP seems to represent battery voltage better than + * ADC_BVDD during charging (ADC_BVDD is way too high) and appears the + * same in normal use. + */ +#define ADC_BATTERY ADC_RTCSUP +#endif + +void powermgmt_init_target(void); +void charging_algorithm_step(void); +void charging_algorithm_close(void); + +/* We want to be able to reset the averaging filter */ +#define HAVE_RESET_BATTERY_FILTER + +#define BATT_AVE_SAMPLES 32 + +#endif /* POWERMGMT_TARGET_H */ diff --git a/firmware/target/arm/sandisk/sansa-c200/powermgmt-c200.c b/firmware/target/arm/sandisk/sansa-c200/powermgmt-c200.c index 963e7218de..9d7a0e2965 100644 --- a/firmware/target/arm/sandisk/sansa-c200/powermgmt-c200.c +++ b/firmware/target/arm/sandisk/sansa-c200/powermgmt-c200.c @@ -38,8 +38,8 @@ const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] = const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] = { + /* NOTE: why is the top voltage 4237? That's just too high. */ { 3286, 3679, 3734, 3764, 3788, 3824, 3886, 3950, 4014, 4098, 4237 }, - }; /* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */ @@ -48,14 +48,3 @@ const unsigned short percent_to_volt_charge[11] = /* Sansa c200 has a 530 mAh LiPo battery */ 3300, 3390, 3480, 3570, 3660, 3750, 3840, 3930, 4020, 4110, 4200 }; - -/* ADC should read 0x3ff=5.12V */ -#define BATTERY_SCALE_FACTOR 5125 -/* full-scale ADC readout (2^10) in millivolt */ - -/* Returns battery voltage from ADC [millivolts] */ -unsigned int battery_adc_voltage(void) -{ - return (adc_read(ADC_UNREG_POWER) * BATTERY_SCALE_FACTOR) >> 10; -} - diff --git a/firmware/target/arm/sandisk/sansa-e200/powermgmt-e200.c b/firmware/target/arm/sandisk/sansa-e200/powermgmt-e200.c index 43f37463f8..144ca37ebf 100644 --- a/firmware/target/arm/sandisk/sansa-e200/powermgmt-e200.c +++ b/firmware/target/arm/sandisk/sansa-e200/powermgmt-e200.c @@ -47,14 +47,3 @@ const unsigned short percent_to_volt_charge[11] = /* Sansa Li Ion 750mAH FIXME */ 3300, 3680, 3740, 3760, 3780, 3810, 3870, 3930, 3970, 4070, 4160 }; - -/* ADC should read 0x3ff=5.12V */ -#define BATTERY_SCALE_FACTOR 5125 -/* full-scale ADC readout (2^10) in millivolt */ - -/* Returns battery voltage from ADC [millivolts] */ -unsigned int battery_adc_voltage(void) -{ - return (adc_read(ADC_UNREG_POWER) * BATTERY_SCALE_FACTOR) >> 10; -} - -- cgit v1.2.3