diff --git a/apps/debug_menu.c b/apps/debug_menu.c index 10d69b018c..7477bb001c 100644 --- a/apps/debug_menu.c +++ b/apps/debug_menu.c @@ -106,7 +106,7 @@ #include "debug-target.h" #endif -#if defined(SANSA_E200) || defined(PHILIPS_SA9200) +#if defined(SANSA_E200) || defined(SANSA_C200) || defined(PHILIPS_SA9200) #include "ascodec.h" #include "as3514.h" #endif @@ -1251,10 +1251,6 @@ extern unsigned char serbuf[]; lcd_puts(0, line++, buf); snprintf(buf, sizeof(buf), "ADC_VBAT: %4d", adc_read(ADC_VBAT)); lcd_puts(0, line++, buf); - snprintf(buf, sizeof(buf), "CHARGER: %02X/%02X", - ascodec_read(AS3514_CHARGER), - ascodec_read(AS3514_IRQ_ENRD0)); - lcd_puts(0, line++, buf); #endif #endif lcd_update(); @@ -1757,6 +1753,32 @@ static bool view_battery(void) } lcd_puts(0, line++, buf); +#elif defined(SANSA_E200) || defined(SANSA_C200) + const int first = CHARGE_STATE_DISABLED; + static const char * const chrgstate_strings[] = + { + [CHARGE_STATE_DISABLED-first] = "Disabled", + [CHARGE_STATE_ERROR-first] = "Error", + [DISCHARGING-first] = "Discharging", + [CHARGING-first] = "Charging", + }; + const char *str = NULL; + + snprintf(buf, 30, "Charger: %s", + charger_inserted() ? "present" : "absent"); + lcd_puts(0, 3, buf); + + y = charge_state - first; + if ((unsigned)y < ARRAYLEN(chrgstate_strings)) + str = chrgstate_strings[y]; + + snprintf(buf, sizeof(buf), "State: %s", + str ? str : ""); + lcd_puts(0, 4, buf); + + snprintf(buf, sizeof(buf), "CHARGER: %02X", + ascodec_read(AS3514_CHARGER)); + lcd_puts(0, 5, buf); #else snprintf(buf, 30, "Charger: %s", charger_inserted() ? "present" : "absent"); diff --git a/firmware/SOURCES b/firmware/SOURCES index f4d86bc2cd..3f558566f8 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -485,17 +485,18 @@ target/sh/archos/ondio/fmradio_i2c-ondio.c #ifdef SANSA_E200 #ifndef SIMULATOR -target/arm/lcd-as-memframe.S -target/arm/ata-sd-pp.c -target/arm/sandisk/sansa-e200/lcd-e200.c target/arm/adc-as3514.c target/arm/ascodec-pp.c -target/arm/sandisk/backlight-c200_e200.c -target/arm/usb-fw-pp502x.c -target/arm/sandisk/sansa-e200/button-e200.c -target/arm/sandisk/power-c200_e200.c -target/arm/sandisk/sansa-e200/powermgmt-e200.c +target/arm/ata-sd-pp.c +target/arm/lcd-as-memframe.S +target/arm/powermgmt-ascodec.c target/arm/i2s-pp.c +target/arm/usb-fw-pp502x.c +target/arm/sandisk/backlight-c200_e200.c +target/arm/sandisk/power-c200_e200.c +target/arm/sandisk/sansa-e200/lcd-e200.c +target/arm/sandisk/sansa-e200/button-e200.c +target/arm/sandisk/sansa-e200/powermgmt-e200.c #ifndef BOOTLOADER target/arm/sandisk/audio-c200_e200.c #endif /* BOOTLOADER */ @@ -504,17 +505,18 @@ target/arm/sandisk/audio-c200_e200.c #ifdef SANSA_C200 #ifndef SIMULATOR -target/arm/ata-sd-pp.c -target/arm/sandisk/sansa-c200/lcd-c200.c -target/arm/sandisk/sansa-c200/lcd-as-c200.S target/arm/adc-as3514.c target/arm/ascodec-pp.c -target/arm/sandisk/backlight-c200_e200.c -target/arm/usb-fw-pp502x.c -target/arm/sandisk/sansa-c200/button-c200.c -target/arm/sandisk/power-c200_e200.c -target/arm/sandisk/sansa-c200/powermgmt-c200.c +target/arm/ata-sd-pp.c target/arm/i2s-pp.c +target/arm/powermgmt-ascodec.c +target/arm/usb-fw-pp502x.c +target/arm/sandisk/backlight-c200_e200.c +target/arm/sandisk/power-c200_e200.c +target/arm/sandisk/sansa-c200/lcd-c200.c +target/arm/sandisk/sansa-c200/lcd-as-c200.S +target/arm/sandisk/sansa-c200/button-c200.c +target/arm/sandisk/sansa-c200/powermgmt-c200.c #ifndef BOOTLOADER target/arm/sandisk/audio-c200_e200.c #endif /* BOOTLOADER */ diff --git a/firmware/export/as3514.h b/firmware/export/as3514.h index 07aa0ebfe2..9489d1ae0d 100644 --- a/firmware/export/as3514.h +++ b/firmware/export/as3514.h @@ -249,8 +249,37 @@ extern void audiohw_set_frequency(int fsel); #define ADC_I_MICSUP2 11 /* Current of MicSup2 for remote control detection */ #define ADC_VBAT 12 /* Single cell battery voltage */ -#define ADC_UNREG_POWER ADC_BVDD /* For compatibility */ +/* AS3514_CHARGER */ +#define TMPSUP_OFF (0x1 << 7) +#define CHG_I (0x7 << 4) +#define CHG_I_400MA (0x7 << 4) +#define CHG_I_350MA (0x6 << 4) +#define CHG_I_300MA (0x5 << 4) +#define CHG_I_250MA (0x4 << 4) +#define CHG_I_200MA (0x3 << 4) +#define CHG_I_150MA (0x2 << 4) +#define CHG_I_100MA (0x1 << 4) +#define CHG_I_50MA (0x0 << 4) +#define CHG_V (0x7 << 1) +#define CHG_V_4_25V (0x7 << 1) +#define CHG_V_4_20V (0x6 << 1) +#define CHG_V_4_15V (0x5 << 1) +#define CHG_V_4_10V (0x4 << 1) +#define CHG_V_4_05V (0x3 << 1) +#define CHG_V_4_00V (0x2 << 1) +#define CHG_V_3_95V (0x1 << 1) +#define CHG_V_3_90V (0x0 << 1) +#define CHG_OFF (0x1 << 0) +/* AS3514_IRQ_ENRD0 */ +#define CHG_TMPHIGH (0x1 << 7) +#define CHG_ENDOFCH (0x1 << 6) +#define CHG_STATUS (0x1 << 5) +#define CHG_CHANGED (0x1 << 4) +#define USB_STATUS (0x1 << 3) +#define USB_CHANGED (0x1 << 2) +#define RVDD_WASLOW (0x1 << 1) +#define BVDD_ISLOW (0x1 << 0) #define AS3514_I2C_ADDR 0x46 diff --git a/firmware/export/config-c200.h b/firmware/export/config-c200.h index f4ab33fb56..d68d21455f 100644 --- a/firmware/export/config-c200.h +++ b/firmware/export/config-c200.h @@ -123,8 +123,9 @@ #define BATTERY_CAPACITY_INC 0 /* capacity increment */ #define BATTERY_TYPES_COUNT 1 /* only one type */ -/* Hardware controlled charging? FIXME */ -#define CONFIG_CHARGING CHARGING_SIMPLE +/* Charging implemented in a target-specific algorithm */ +#define CONFIG_CHARGING CHARGING_TARGET +#define HAVE_POWEROFF_WHILE_CHARGING /* define this if the unit can be powered or charged via USB */ #define HAVE_USB_POWER diff --git a/firmware/export/config-e200.h b/firmware/export/config-e200.h index 602a84707c..2be64d95cc 100644 --- a/firmware/export/config-e200.h +++ b/firmware/export/config-e200.h @@ -120,8 +120,9 @@ #define BATTERY_CAPACITY_INC 0 /* capacity increment */ #define BATTERY_TYPES_COUNT 1 /* only one type */ -/* Hardware controlled charging? FIXME */ -#define CONFIG_CHARGING CHARGING_SIMPLE +/* Charging implemented in a target-specific algorithm */ +#define CONFIG_CHARGING CHARGING_TARGET +#define HAVE_POWEROFF_WHILE_CHARGING /* define current usage levels */ #define CURRENT_NORMAL 30 /* Toni's measurements in Nov 2008 */ diff --git a/firmware/export/powermgmt.h b/firmware/export/powermgmt.h index 5be3a39c1d..39e2e6eab9 100644 --- a/firmware/export/powermgmt.h +++ b/firmware/export/powermgmt.h @@ -27,7 +27,7 @@ enum charge_state_type { - /* sorted by increasing charging current */ + /* sorted by increasing charging current (do not change!) */ #if CONFIG_CHARGING >= CHARGING_MONITOR CHARGE_STATE_DISABLED = -2, /* Disable charger use (safety measure) */ CHARGE_STATE_ERROR = -1, /* Some error occurred that should not allow @@ -143,13 +143,16 @@ unsigned int battery_voltage(void); /* filtered batt. voltage in millivolts */ #ifdef HAVE_BATTERY_SWITCH unsigned int input_millivolts(void); /* voltage that device is running from */ +#endif /* HAVE_BATTERY_SWITCH */ +#if defined(HAVE_BATTERY_SWITCH) || defined(HAVE_RESET_BATTERY_FILTER) /* Set the filtered battery voltage (to adjust it before beginning a charge * cycle for instance where old, loaded readings will likely be invalid). * Also readjust when battery switch is opened or closed. */ void reset_battery_filter(int millivolts); -#endif /* HAVE_BATTERY_SWITCH */ +#endif /* HAVE_BATTERY_SWITCH || HAVE_RESET_BATTERY_FILTER */ + /* read unfiltered battery info */ void battery_read_info(int *voltage, int *level); diff --git a/firmware/powermgmt.c b/firmware/powermgmt.c index f71eb45430..3fc216080b 100644 --- a/firmware/powermgmt.c +++ b/firmware/powermgmt.c @@ -435,7 +435,7 @@ bool query_force_shutdown(void) #endif } -#ifdef HAVE_BATTERY_SWITCH +#if defined(HAVE_BATTERY_SWITCH) || defined(HAVE_RESET_BATTERY_FILTER) /* * Reset the battery voltage filter to a new value and update the * status. 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; -} -