diff --git a/apps/debug_menu.c b/apps/debug_menu.c index beee39a09b..abb6018a10 100644 --- a/apps/debug_menu.c +++ b/apps/debug_menu.c @@ -1603,10 +1603,10 @@ static bool view_battery(void) lcd_puts(0, 2, buf); #endif #if CONFIG_CHARGING -#if CONFIG_CHARGING == CHARGING_CONTROL +#if defined ARCHOS_RECORDER snprintf(buf, 30, "Chgr: %s %s", charger_inserted() ? "present" : "absent", - charger_enabled ? "on" : "off"); + charger_enabled() ? "on" : "off"); lcd_puts(0, 3, buf); snprintf(buf, 30, "short delta: %d", short_delta); lcd_puts(0, 5, buf); @@ -1616,13 +1616,11 @@ static bool view_battery(void) snprintf(buf, 30, "USB Inserted: %s", usb_inserted() ? "yes" : "no"); lcd_puts(0, 8, buf); -#if defined IRIVER_H300_SERIES +#elif defined IRIVER_H300_SERIES snprintf(buf, 30, "USB Charging Enabled: %s", usb_charging_enabled() ? "yes" : "no"); lcd_puts(0, 9, buf); -#endif -#else /* CONFIG_CHARGING != CHARGING_CONTROL */ -#if defined IPOD_NANO || defined IPOD_VIDEO +#elif defined IPOD_NANO || defined IPOD_VIDEO int usb_pwr = (GPIOL_INPUT_VAL & 0x10)?true:false; int ext_pwr = (GPIOL_INPUT_VAL & 0x08)?false:true; int dock = (GPIOA_INPUT_VAL & 0x10)?true:false; @@ -1644,12 +1642,8 @@ static bool view_battery(void) snprintf(buf, 30, "Headphone: %s", headphone ? "connected" : "disconnected"); lcd_puts(0, 7, buf); -#else - snprintf(buf, 30, "Charger: %s", - charger_inserted() ? "present" : "absent"); - lcd_puts(0, 3, buf); -#if defined TOSHIBA_GIGABEAT_S - int line = 4; +#elif defined TOSHIBA_GIGABEAT_S + int line = 3; unsigned int st; static const unsigned char * const chrgstate_strings[] = @@ -1663,6 +1657,10 @@ static bool view_battery(void) "", }; + snprintf(buf, 30, "Charger: %s", + charger_inserted() ? "present" : "absent"); + lcd_puts(0, line++, buf); + st = power_input_status() & (POWER_INPUT_CHARGER | POWER_INPUT_BATTERY); snprintf(buf, 30, "%s%s", @@ -1730,9 +1728,11 @@ static bool view_battery(void) } lcd_puts(0, line++, buf); -#endif /* defined TOSHIBA_GIGABEAT_S */ -#endif /* defined IPOD_NANO || defined IPOD_VIDEO */ -#endif /* CONFIG_CHARGING != CHARGING_CONTROL */ +#else + snprintf(buf, 30, "Charger: %s", + charger_inserted() ? "present" : "absent"); + lcd_puts(0, 3, buf); +#endif /* target type */ #endif /* CONFIG_CHARGING */ break; @@ -1750,7 +1750,7 @@ static bool view_battery(void) case 3: /* remaining time estimation: */ -#if CONFIG_CHARGING == CHARGING_CONTROL +#ifdef ARCHOS_RECORDER snprintf(buf, 30, "charge_state: %d", charge_state); lcd_puts(0, 0, buf); @@ -1765,7 +1765,7 @@ static bool view_battery(void) snprintf(buf, 30, "Trickle sec: %d/60", trickle_sec); lcd_puts(0, 4, buf); -#endif /* CONFIG_CHARGING == CHARGING_CONTROL */ +#endif /* ARCHOS_RECORDER */ snprintf(buf, 30, "Last PwrHist: %d.%03dV", power_history[0] / 1000, diff --git a/apps/menus/main_menu.c b/apps/menus/main_menu.c index 4c15b1de4b..b22824d8bd 100644 --- a/apps/menus/main_menu.c +++ b/apps/menus/main_menu.c @@ -27,6 +27,7 @@ #include "lang.h" #include "action.h" #include "settings.h" +#include "power.h" #include "powermgmt.h" #include "menu.h" #include "misc.h" @@ -199,15 +200,14 @@ static char* info_getname(int selected_item, void *data, if (charge_state == CHARGING) return (char *)str(LANG_BATTERY_CHARGE); else -#if CONFIG_CHARGING == CHARGING_CONTROL +#ifdef ARCHOS_RECORDER if (charge_state == TOPOFF) return (char *)str(LANG_BATTERY_TOPOFF_CHARGE); - else -#endif - if (charge_state == TRICKLE) + else if (charge_state == TRICKLE) return (char *)str(LANG_BATTERY_TRICKLE_CHARGE); else -#endif +#endif /* ARCHOS_RECORDER */ +#endif /* CONFIG_CHARGING = */ if (battery_level() >= 0) snprintf(buffer, buffer_len, (char *)str(LANG_BATTERY_TIME), battery_level(), battery_time() / 60, battery_time() % 60); @@ -282,22 +282,21 @@ static int info_speak_item(int selected_item, void * data) } case INFO_BATTERY: /* battery */ #if CONFIG_CHARGING == CHARGING_SIMPLE - if (charger_input_state == CHARGER) + if (charger_inserted()) talk_id(LANG_BATTERY_CHARGE, true); else #elif CONFIG_CHARGING >= CHARGING_MONITOR if (charge_state == CHARGING) talk_id(LANG_BATTERY_CHARGE, true); else -#if CONFIG_CHARGING == CHARGING_CONTROL +#ifdef ARCHOS_RECORDER if (charge_state == TOPOFF) talk_id(LANG_BATTERY_TOPOFF_CHARGE, true); - else -#endif - if (charge_state == TRICKLE) + else if (charge_state == TRICKLE) talk_id(LANG_BATTERY_TRICKLE_CHARGE, true); else -#endif +#endif /* ARCHOS_RECORDER */ +#endif /* CONFIG_CHARGING = */ if (battery_level() >= 0) { talk_id(LANG_BATTERY_TIME, false); diff --git a/apps/screens.c b/apps/screens.c index 992c740460..230e9ae53c 100644 --- a/apps/screens.c +++ b/apps/screens.c @@ -209,8 +209,7 @@ static void charging_display_info(bool animate) lcd_puts(0, 7, buf); } -#if CONFIG_CHARGING == CHARGING_CONTROL - +#ifdef ARCHOS_RECORER snprintf(buf, 32, "Charge mode:"); lcd_puts(0, 2, buf); @@ -224,10 +223,9 @@ static void charging_display_info(bool animate) snprintf(buf, 32, "not charging"); lcd_puts(0, 3, buf); - if (!charger_enabled) + if (!charger_enabled()) animate = false; -#endif /* CONFIG_CHARGING == CHARGING_CONTROL */ - +#endif /* ARCHOS_RECORER */ /* middle part */ memset(charging_logo+3, 0x00, 32); diff --git a/firmware/export/config-c200.h b/firmware/export/config-c200.h index e169606510..ac64f20e62 100644 --- a/firmware/export/config-c200.h +++ b/firmware/export/config-c200.h @@ -129,6 +129,11 @@ /* define this if the unit can be powered or charged via USB */ #define HAVE_USB_POWER +/* define current usage levels */ +#define CURRENT_NORMAL 45 /* Should be nearly identical to E200 */ +#define CURRENT_BACKLIGHT 40 /* Screen is about 20, blue LEDs are another 20, so 40 if both */ +#define CURRENT_RECORD 40 /* flash player, so this is just unboosted current*/ + /** Non-simulator section **/ #ifndef SIMULATOR diff --git a/firmware/export/config-e200.h b/firmware/export/config-e200.h index 3cae14cc87..b290a24952 100644 --- a/firmware/export/config-e200.h +++ b/firmware/export/config-e200.h @@ -123,6 +123,11 @@ /* Hardware controlled charging? FIXME */ #define CONFIG_CHARGING CHARGING_SIMPLE +/* define current usage levels */ +#define CURRENT_NORMAL 45 /* Mike's measurements in Jan 2008 */ +#define CURRENT_BACKLIGHT 40 /* Screen is about 20, blue LEDs are another 20, so 40 if both */ +#define CURRENT_RECORD 40 /* flash player, so this is just unboosted current*/ + /* define this if the unit can be powered or charged via USB */ #define HAVE_USB_POWER diff --git a/firmware/export/config-h100.h b/firmware/export/config-h100.h index 9c327f11fd..7f91e35fbf 100644 --- a/firmware/export/config-h100.h +++ b/firmware/export/config-h100.h @@ -110,9 +110,16 @@ #define BATTERY_TYPES_COUNT 1 /* only one type */ /* Hardware controlled charging */ -//#define CONFIG_CHARGING CHARGING_SIMPLE + #define CONFIG_CHARGING CHARGING_MONITOR /* FIXME: remove that once monitoring is fixed properly */ +/* define current usage levels */ +#define CURRENT_NORMAL 80 /* 16h playback on 1300mAh battery */ +#define CURRENT_BACKLIGHT 23 /* from IriverBattery twiki page */ +#define CURRENT_SPDIF_OUT 10 /* optical SPDIF output on */ +#define CURRENT_RECORD 105 /* additional current while recording */ +#define CURRENT_REMOTE 8 /* additional current when remote connected */ + #ifndef SIMULATOR /* Define this if you have a Motorola SCF5249 */ diff --git a/firmware/export/config-h120.h b/firmware/export/config-h120.h index d81fa07d93..b4a687b6d7 100644 --- a/firmware/export/config-h120.h +++ b/firmware/export/config-h120.h @@ -110,9 +110,15 @@ #define BATTERY_TYPES_COUNT 1 /* only one type */ /* Hardware controlled charging */ -//#define CONFIG_CHARGING CHARGING_SIMPLE #define CONFIG_CHARGING CHARGING_MONITOR /* FIXME: remove that once monitoring is fixed properly */ +/* define current usage levels */ +#define CURRENT_NORMAL 80 /* 16h playback on 1300mAh battery */ +#define CURRENT_BACKLIGHT 23 /* from IriverBattery twiki page */ +#define CURRENT_SPDIF_OUT 10 /* optical SPDIF output on */ +#define CURRENT_RECORD 105 /* additional current while recording */ +#define CURRENT_REMOTE 8 /* additional current when remote connected */ + #ifndef SIMULATOR /* Define this if you have a Motorola SCF5249 */ diff --git a/firmware/export/config-h300.h b/firmware/export/config-h300.h index fa0046ac03..05d75fc4f0 100644 --- a/firmware/export/config-h300.h +++ b/firmware/export/config-h300.h @@ -112,6 +112,13 @@ /* define this if the unit can be powered or charged via USB */ #define HAVE_USB_POWER +/* define current usage levels */ +#define CURRENT_NORMAL 80 /* 16h playback on 1300mAh battery from IriverRuntime wiki page */ +#define CURRENT_BACKLIGHT 23 /* FIXME: This needs to be measured, copied from H100 */ +#define CURRENT_RECORD 110 /* additional current while recording */ +#define CURRENT_MAX_CHG 650 /* maximum charging current */ +#define CURRENT_REMOTE 8 /* additional current when remote connected */ + /* define this if the unit can have USB charging disabled by user - * if USB/MAIN power is discernable and hardware doesn't compel charging */ #define HAVE_USB_CHARGING_ENABLE diff --git a/firmware/export/config-iaudiom3.h b/firmware/export/config-iaudiom3.h index f4ced651d0..a6e1028897 100644 --- a/firmware/export/config-iaudiom3.h +++ b/firmware/export/config-iaudiom3.h @@ -102,6 +102,9 @@ /* Hardware controlled charging? FIXME */ #define CONFIG_CHARGING CHARGING_SIMPLE +/* define current usage levels */ +#define CURRENT_REMOTE 8 /* additional current when remote connected */ + #ifndef SIMULATOR /* Define this if your LCD can set contrast */ diff --git a/firmware/export/config-iaudiom5.h b/firmware/export/config-iaudiom5.h index 5ec6b77862..26cc9c5da6 100644 --- a/firmware/export/config-iaudiom5.h +++ b/firmware/export/config-iaudiom5.h @@ -105,6 +105,9 @@ /* Hardware controlled charging? FIXME */ #define CONFIG_CHARGING CHARGING_SIMPLE +/* define current usage levels */ +#define CURRENT_REMOTE 8 /* additional current when remote connected */ + #ifndef SIMULATOR /* Define this if your LCD can set contrast */ diff --git a/firmware/export/config-iaudiox5.h b/firmware/export/config-iaudiox5.h index 03b84606c9..8b1f9086bb 100644 --- a/firmware/export/config-iaudiox5.h +++ b/firmware/export/config-iaudiox5.h @@ -122,6 +122,9 @@ /* Hardware controlled charging? FIXME */ #define CONFIG_CHARGING CHARGING_SIMPLE +/* define current usage levels */ +#define CURRENT_REMOTE 8 /* additional current when remote connected */ + #ifndef SIMULATOR /* define this if the backlight thread is used for fade, not for sim, needs diff --git a/firmware/export/config-ipod4g.h b/firmware/export/config-ipod4g.h index 4a2207d1d6..89b14f9907 100644 --- a/firmware/export/config-ipod4g.h +++ b/firmware/export/config-ipod4g.h @@ -120,6 +120,13 @@ /* define this if the unit can be powered or charged via USB */ #define HAVE_USB_POWER +/* define current usage levels */ +#define CURRENT_NORMAL 100 /* MP3: ~10.5h out of 1100mAh battery */ +#define CURRENT_BACKLIGHT 20 /* FIXME: this needs adjusting */ +#if defined(HAVE_RECORDING) +#define CURRENT_RECORD 35 /* FIXME: this needs adjusting */ +#endif + #ifndef SIMULATOR /* Define this if you have a PortalPlayer PP5020 */ diff --git a/firmware/export/config-ipodnano.h b/firmware/export/config-ipodnano.h index 7d6515fc0f..6017d0a2e8 100644 --- a/firmware/export/config-ipodnano.h +++ b/firmware/export/config-ipodnano.h @@ -115,6 +115,12 @@ /* define this if the unit can be powered or charged via USB */ #define HAVE_USB_POWER +#define CURRENT_NORMAL 32 /* MP3: ~9h playback out of 300mAh battery */ +#define CURRENT_BACKLIGHT 20 /* FIXME: this needs adjusting */ +#if defined(HAVE_RECORDING) +#define CURRENT_RECORD 35 /* FIXME: this needs adjusting */ +#endif + #ifndef SIMULATOR /* Define this if you have a PortalPlayer PP5022 */ diff --git a/firmware/export/config-ipodvideo.h b/firmware/export/config-ipodvideo.h index fa6ddd304f..d04e562086 100644 --- a/firmware/export/config-ipodvideo.h +++ b/firmware/export/config-ipodvideo.h @@ -123,6 +123,13 @@ /* define this if the unit can be powered or charged via USB */ #define HAVE_USB_POWER +/* define current usage levels */ +#define CURRENT_NORMAL 35 /* MP3: ~11h out of 400mAh battery (30GB) or ~17h out of 600mAh (60GB) */ +#define CURRENT_BACKLIGHT 20 /* FIXME: this needs adjusting */ +#if defined(HAVE_RECORDING) +#define CURRENT_RECORD 35 /* FIXME: this needs adjusting */ +#endif + #ifndef SIMULATOR /* Define this if you have a PortalPlayer PP5022 */ diff --git a/firmware/export/config-ondiofm.h b/firmware/export/config-ondiofm.h index 634433fcdb..8233728f1b 100644 --- a/firmware/export/config-ondiofm.h +++ b/firmware/export/config-ondiofm.h @@ -69,6 +69,11 @@ /* define this if the unit can be powered or charged via USB */ #define HAVE_USB_POWER +/* define current usage levels */ +#define CURRENT_NORMAL 95 /* average, nearly proportional to 1/U */ +#define CURRENT_USB 1 /* host powered in USB mode; avoid zero-div */ +#define CURRENT_BACKLIGHT 0 /* no backlight */ + #ifndef SIMULATOR /* Define this if you have a SH7034 */ diff --git a/firmware/export/config-ondiosp.h b/firmware/export/config-ondiosp.h index 3443b839b6..5fb7806d6a 100644 --- a/firmware/export/config-ondiosp.h +++ b/firmware/export/config-ondiosp.h @@ -58,6 +58,11 @@ /* define this if the unit can be powered or charged via USB */ #define HAVE_USB_POWER +/* define current usage levels */ +#define CURRENT_NORMAL 95 /* average, nearly proportional to 1/U */ +#define CURRENT_USB 1 /* host powered in USB mode; avoid zero-div */ +#define CURRENT_BACKLIGHT 0 /* no backlight */ + #ifndef SIMULATOR /* Define this if you have a SH7034 */ diff --git a/firmware/export/config-recorder.h b/firmware/export/config-recorder.h index e5fa3361cc..75aa2cf789 100644 --- a/firmware/export/config-recorder.h +++ b/firmware/export/config-recorder.h @@ -72,7 +72,7 @@ #define NO_LOW_BATTERY_SHUTDOWN /* Software controlled charging */ -#define CONFIG_CHARGING CHARGING_CONTROL +#define CONFIG_CHARGING CHARGING_TARGET #ifndef SIMULATOR diff --git a/firmware/export/config.h b/firmware/export/config.h index d8c14ddcef..1854f59d13 100644 --- a/firmware/export/config.h +++ b/firmware/export/config.h @@ -114,11 +114,13 @@ #define X5_REMOTE 3 /* CONFIG_CHARGING */ + +/* Generic types */ #define CHARGING_SIMPLE 1 /* Simple, hardware controlled charging */ #define CHARGING_MONITOR 2 /* Hardware controlled charging with monitoring */ -#define CHARGING_CONTROL 3 /* Software controlled charging */ -#define CHARGING_TARGET 4 /* Anything the target implements that is not - a generic implementation */ + +/* Mostly target-specific code in the /target tree */ +#define CHARGING_TARGET 3 /* CONFIG_LCD */ #define LCD_SSD1815 1 /* as used by Archos Recorders and Ondios */ diff --git a/firmware/export/power.h b/firmware/export/power.h index 747887921d..edf43f8cc8 100644 --- a/firmware/export/power.h +++ b/firmware/export/power.h @@ -21,11 +21,6 @@ #ifndef _POWER_H_ #define _POWER_H_ -#if CONFIG_CHARGING == CHARGING_CONTROL -extern bool charger_enabled; -void charger_enable(bool on); -#endif - #if CONFIG_CHARGING enum power_input_flags { /* No external power source? Default. */ @@ -82,9 +77,9 @@ bool power_input_present(void); void power_off(void); void ide_power_enable(bool on); -# if CONFIG_CHARGING == CHARGING_MONITOR +#if CONFIG_CHARGING >= CHARGING_MONITOR bool charging_state(void); -# endif +#endif #ifndef SIMULATOR @@ -102,4 +97,4 @@ bool spdif_powered(void); bool tuner_power(bool status); #endif -#endif +#endif /* _POWER_H_ */ diff --git a/firmware/export/powermgmt.h b/firmware/export/powermgmt.h index c333795ad7..5be3a39c1d 100644 --- a/firmware/export/powermgmt.h +++ b/firmware/export/powermgmt.h @@ -25,149 +25,103 @@ #define POWER_HISTORY_LEN 2*60 /* 2 hours of samples, one per minute */ -#define CHARGE_END_SHORTD 6 /* stop when N minutes have passed with - * avg delta being < -0.05 V */ -#define CHARGE_END_LONGD 50 /* stop when N minutes have passed with - * avg delta being < -0.02 V */ - -typedef enum { /* sorted by increasing charging current */ +enum charge_state_type +{ + /* sorted by increasing charging current */ #if CONFIG_CHARGING >= CHARGING_MONITOR - CHARGE_STATE_DISABLED = -2, /* Disable charger use */ - CHARGE_STATE_ERROR = -1, /* Some error occurred that should not allow - further attempts without user intervention */ + CHARGE_STATE_DISABLED = -2, /* Disable charger use (safety measure) */ + CHARGE_STATE_ERROR = -1, /* Some error occurred that should not allow + turning on the charger again by software + without user intervention (ie. replug) */ #endif DISCHARGING = 0, #if CONFIG_CHARGING >= CHARGING_MONITOR - TRICKLE, /* Can occur for CONFIG_CHARGING >= CHARGING_MONITOR */ - /* For LiIon, the low-current precharge mode if battery - was very low */ - TOPOFF, /* Can occur for CONFIG_CHARGING == CHARGING_CONTROL */ + TRICKLE, /* For NiCd, battery maintenence phase */ + /* For LiIon, low-current precharge phase */ + TOPOFF, /* For NiCd, waiting for dead zone */ /* For LiIon, constant voltage phase */ - CHARGING, /* Can occur for all CONFIG_CHARGING options */ - /* For LiIon, the constant current phase */ + CHARGING, /* For NiCd, main charge phase */ + /* For LiIon, constant current phase */ #endif -} charge_state_type; +}; /* tells what the charger is doing */ -extern charge_state_type charge_state; +extern enum charge_state_type charge_state; #ifdef CONFIG_CHARGING /* * Flag that the charger has been plugged in/removed: this is set for exactly * one time through the power loop when the charger has been plugged in. */ -typedef enum { +enum charger_input_state_type +{ NO_CHARGER = 0, /* No charger is present */ CHARGER_UNPLUGGED, /* Transitional state during CHARGER=>NO_CHARGER */ CHARGER_PLUGGED, /* Transitional state during NO_CHARGER=>CHARGER */ CHARGER /* Charger is present */ -} charger_input_state_type; +}; /* tells the state of the charge input */ -extern charger_input_state_type charger_input_state; -#endif +extern enum charger_input_state_type charger_input_state; -#ifndef SIMULATOR +/* Power input status saved on the power thread each loop */ +extern unsigned int power_thread_inputs; -#if CONFIG_CHARGING == CHARGING_CONTROL -#define START_TOPOFF_CHG 85 /* Battery % to start at top-off */ -#define START_TRICKLE_CHG 95 /* Battery % to start at trickle */ - -#define POWER_MESSAGE_LEN 32 /* power thread status message */ -#define CHARGE_MAX_TIME_1500 450 /* minutes: maximum charging time for 1500 mAh batteries */ - /* actual max time depends also on BATTERY_CAPACITY! */ -#define CHARGE_MIN_TIME 10 /* minutes: minimum charging time */ -#define TOPOFF_MAX_TIME 90 /* After charging, go to top off charge. How long should top off charge be? */ -#define TOPOFF_VOLTAGE 5650 /* which voltage is best? (millivolts) */ -#define TRICKLE_MAX_TIME 12*60 /* After top off charge, go to trickle charge. How long should trickle charge be? */ -#define TRICKLE_VOLTAGE 5450 /* which voltage is best? (millivolts) */ - -#define START_TOPOFF_SEC 25 /* initial trickle_sec for topoff */ -#define START_TRICKLE_SEC 15 /* initial trickle_sec for trickle */ - -#define PID_DEADZONE 4 /* PID proportional deadzone */ - -extern char power_message[POWER_MESSAGE_LEN]; - -extern int long_delta; /* long term delta battery voltage */ -extern int short_delta; /* short term delta battery voltage */ - -extern int powermgmt_last_cycle_startstop_min; /* how many minutes ago was the charging started or stopped? */ -extern int powermgmt_last_cycle_level; /* which level had the batteries at this time? */ - -extern int pid_p; /* PID proportional term */ -extern int pid_i; /* PID integral term */ -extern int trickle_sec; /* trickle charge: How many seconds per minute are we charging actually? */ - -#endif /* CONFIG_CHARGING == CHARGING_CONTROL */ - -#if defined(ARCHOS_ONDIOSP) || defined(ARCHOS_ONDIOFM) /* Values for Ondio */ -# define CURRENT_NORMAL 95 /* average, nearly proportional to 1/U */ -# define CURRENT_USB 1 /* host powered in USB mode; avoid zero-div */ -# define CURRENT_BACKLIGHT 0 /* no backlight */ -#else /* Values for HD based jukeboxes */ -#ifdef IRIVER_H100_SERIES -# define CURRENT_NORMAL 80 /* 16h playback on 1300mAh battery */ -# define CURRENT_BACKLIGHT 23 /* from IriverBattery twiki page */ -# define CURRENT_SPDIF_OUT 10 /* optical SPDIF output on */ -# define CURRENT_RECORD 105 /* additional current while recording */ -#elif defined(IRIVER_H300_SERIES) -# define CURRENT_NORMAL 80 /* 16h playback on 1300mAh battery from IriverRuntime wiki page */ -# define CURRENT_BACKLIGHT 23 /* FIXME: This needs to be measured, copied from H100 */ -# define CURRENT_RECORD 110 /* additional current while recording */ -#elif defined(IPOD_NANO) /* iPOD Nano */ -# define CURRENT_NORMAL 32 /* MP3: ~9h playback out of 300mAh battery */ -# define CURRENT_BACKLIGHT 20 /* FIXME: this needs adjusting */ -#if defined(HAVE_RECORDING) -# define CURRENT_RECORD 35 /* FIXME: this needs adjusting */ -#endif -#elif defined(IPOD_VIDEO) /* iPOD Video */ -# define CURRENT_NORMAL 35 /* MP3: ~11h out of 400mAh battery (30GB) or ~17h out of 600mAh (60GB) */ -# define CURRENT_BACKLIGHT 20 /* FIXME: this needs adjusting */ -#if defined(HAVE_RECORDING) -# define CURRENT_RECORD 35 /* FIXME: this needs adjusting */ -#endif -#elif defined(SANSA_E200) /* Sandisk E200v1 */ -# define CURRENT_NORMAL 45 /* Mike's measurements in Jan 2008 */ -# define CURRENT_BACKLIGHT 40 /* Screen is about 20, blue LEDs are another 20, so 40 if both */ -# define CURRENT_RECORD 40 /* flash player, so this is just unboosted current*/ -#elif defined(SANSA_C200) /* Sandisk C200v1 */ -# define CURRENT_NORMAL 45 /* Should be nearly identical to E200 */ -# define CURRENT_BACKLIGHT 40 /* Screen is about 20, blue LEDs are another 20, so 40 if both */ -# define CURRENT_RECORD 40 /* flash player, so this is just unboosted current*/ -#elif defined(IPOD_4G) /* iPOD 4G */ -# define CURRENT_NORMAL 100 /* MP3: ~10.5h out of 1100mAh battery */ -# define CURRENT_BACKLIGHT 20 /* FIXME: this needs adjusting */ -#if defined(HAVE_RECORDING) -# define CURRENT_RECORD 35 /* FIXME: this needs adjusting */ -#endif -#else /* Not iriver H1x0, H3x0, nor Archos Ondio, nor iPod nano/Video/4G, nor Sansas */ -# define CURRENT_NORMAL 145 /* usual current in mA when using the AJB including some disk/backlight/... activity */ -# define CURRENT_BACKLIGHT 30 /* additional current when backlight always on */ -#if defined(HAVE_RECORDING) -# define CURRENT_RECORD 35 /* FIXME: this needs adjusting */ -#endif -#endif /* Not Archos Ondio */ -#define CURRENT_USB 500 /* usual current in mA in USB mode */ -#ifdef HAVE_REMOTE_LCD -# define CURRENT_REMOTE 8 /* add. current when H100-remote connected */ -#endif /* HAVE_REMOTE_LCD */ - -# define CURRENT_MIN_CHG 70 /* minimum charge current */ -# define MIN_CHG_V 8500 /* at 8.5v charger voltage get CURRENT_MIN_CHG */ -# ifdef IRIVER_H300_SERIES -# define CURRENT_MAX_CHG 650 /* maximum charging current */ -# else -# define CURRENT_MAX_CHG 350 /* maximum charging current */ -# endif -# define MAX_CHG_V 10250 /* anything over 10.25v gives CURRENT_MAX_CHG */ -#endif /* not ONDIO */ +#endif /* CONFIG_CHARGING */ #if CONFIG_CHARGING == CHARGING_TARGET /* Include target-specific definitions */ #include "powermgmt-target.h" #endif +#ifndef SIMULATOR + +/* Generic current values that are really rather meaningless - config header + * should define proper numbers. */ +#ifndef CURRENT_NORMAL +#define CURRENT_NORMAL 145 /* usual current in mA */ +#endif + +#ifndef CURRENT_BACKLIGHT +#define CURRENT_BACKLIGHT 30 /* additional current when backlight always on */ +#endif + +#ifdef HAVE_RECORDING +#ifndef CURRENT_RECORD +#define CURRENT_RECORD 35 /* additional recording current */ +#endif +#endif /* HAVE_RECORDING */ + +#ifndef CURRENT_USB +#define CURRENT_USB 500 /* usual current in mA in USB mode */ +#endif + +#ifdef HAVE_REMOTE_LCD +#define CURRENT_REMOTE 8 /* additional current when remote connected */ +#endif /* HAVE_REMOTE_LCD */ + +#if CONFIG_CHARGING +#ifndef CURRENT_MAX_CHG +#define CURRENT_MAX_CHG 350 /* maximum charging current */ +#endif +#endif /* CONFIG_CHARGING */ + +#ifdef CHARGING_DEBUG_FILE +#define POWERMGMT_DEBUG_STACK ((0x1000)/sizeof(long)) +#else +#define POWERMGMT_DEBUG_STACK 0 +#endif + +#ifndef BATT_AVE_SAMPLES +/* slw filter constant unless otherwise specified */ +#define BATT_AVE_SAMPLES 128 +#endif + +#ifndef POWER_THREAD_STEP_TICKS +/* 2HZ sample rate unless otherwise specified */ +#define POWER_THREAD_STEP_TICKS (HZ/2) +#endif + extern unsigned short power_history[POWER_HISTORY_LEN]; extern const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT]; extern const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT]; @@ -179,12 +133,6 @@ extern const unsigned short percent_to_volt_charge[11]; /* Start up power management thread */ void powermgmt_init(void); -/* Do target portion of init (for CHARGING_TARGET) - called on power thread */ -void powermgmt_init_target(void); - -/* Handle frequent tasks and call charging_algorithm_small_step */ -void power_thread_sleep(int ticks); - #endif /* SIMULATOR */ /* Returns battery statust */ @@ -193,9 +141,15 @@ int battery_time(void); /* minutes */ unsigned int battery_adc_voltage(void); /* voltage from ADC in millivolts */ 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 */ + /* Set the filtered battery voltage (to adjust it before beginning a charge - cycle for instance where old, loaded readings will likely be invalid). */ -void set_filtered_battery_voltage(int millivolts); + * 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 */ /* read unfiltered battery info */ void battery_read_info(int *voltage, int *level); @@ -203,13 +157,10 @@ void battery_read_info(int *voltage, int *level); /* Tells if the battery level is safe for disk writes */ bool battery_level_safe(void); -#ifdef TARGET_POWERMGMT_FILTER_CHARGE_STATE -int powermgmt_filter_charge_state(void); -#endif - void set_poweroff_timeout(int timeout); void set_battery_capacity(int capacity); /* set local battery capacity value */ -void set_battery_type(int type); /* set local battery type */ +int get_battery_capacity(void); /* get local battery capacity value */ +void set_battery_type(int type); /* set local battery type */ void set_sleep_timer(int seconds); int get_sleep_timer(void); diff --git a/firmware/powermgmt.c b/firmware/powermgmt.c index 0fed847973..5aa85c883d 100644 --- a/firmware/powermgmt.c +++ b/firmware/powermgmt.c @@ -20,18 +20,14 @@ * ****************************************************************************/ #include "config.h" -#include "cpu.h" +#include "system.h" #include "kernel.h" #include "thread.h" -#include "system.h" #include "debug.h" -#include "panic.h" #include "adc.h" #include "string.h" -#include "sprintf.h" #include "storage.h" #include "power.h" -#include "button.h" #include "audio.h" #include "mp3_playback.h" #include "usb.h" @@ -57,166 +53,39 @@ #include "lcd-remote-target.h" #endif -/* - * Define DEBUG_FILE to create a csv (spreadsheet) with battery information - * in it (one sample per minute). This is only for very low level debug. - */ -#undef DEBUG_FILE -#if defined(DEBUG_FILE) && (CONFIG_CHARGING == CHARGING_CONTROL) -#include "file.h" -#define DEBUG_FILE_NAME "/powermgmt.csv" -#define DEBUG_MESSAGE_LEN 133 -static char debug_message[DEBUG_MESSAGE_LEN]; -#define DEBUG_STACK ((0x1000)/sizeof(long)) -static int fd = -1; /* write debug information to this file */ -static int wrcount = 0; -#else -#define DEBUG_STACK 0 -#endif - -static int shutdown_timeout = 0; -#if CONFIG_CHARGING >= CHARGING_MONITOR -charge_state_type charge_state = DISCHARGING; /* charging mode */ -#endif - -static void send_battery_level_event(void); -static int last_sent_battery_level = 100; +/** Shared by sim **/ +int last_sent_battery_level = 100; +/* battery level (0-100%) */ +int battery_percent = -1; +void send_battery_level_event(void); #if CONFIG_CHARGING -charger_input_state_type charger_input_state IDATA_ATTR; +/* State of the charger input as seen by the power thread */ +enum charger_input_state_type charger_input_state; +/* Power inputs as seen by the power thread */ +unsigned int power_thread_inputs; +#if CONFIG_CHARGING >= CHARGING_MONITOR +/* Charging state (mode) as seen by the power thread */ +enum charge_state_type charge_state = DISCHARGING; #endif +#endif /* CONFIG_CHARGING */ -#ifdef SIMULATOR /***********************************************************/ - -#define BATT_MINMVOLT 2500 /* minimum millivolts of battery */ -#define BATT_MAXMVOLT 4500 /* maximum millivolts of battery */ -#define BATT_MAXRUNTIME (10 * 60) /* maximum runtime with full battery in minutes */ - -static unsigned int battery_millivolts = (unsigned int)BATT_MAXMVOLT; -static int battery_percent = 100; /* battery capacity level in percent */ -static int powermgmt_est_runningtime_min = BATT_MAXRUNTIME; /* estimated remaining time in minutes */ - -static void battery_status_update(void) -{ - static time_t last_change = 0; - static bool charging = false; - time_t now; - - time(&now); - if (last_change < now) - { - last_change = now; - - /* change the values: */ - if (charging) - { - if (battery_millivolts >= BATT_MAXMVOLT) - { - /* Pretend the charger was disconnected */ - charging = false; - queue_broadcast(SYS_CHARGER_DISCONNECTED, 0); - last_sent_battery_level = 100; - } - } - else - { - if (battery_millivolts <= BATT_MINMVOLT) - { - /* Pretend the charger was connected */ - charging = true; - queue_broadcast(SYS_CHARGER_CONNECTED, 0); - last_sent_battery_level = 0; - } - } - if (charging) - battery_millivolts += (BATT_MAXMVOLT - BATT_MINMVOLT) / 50; - else - battery_millivolts -= (BATT_MAXMVOLT - BATT_MINMVOLT) / 100; - - battery_percent = 100 * (battery_millivolts - BATT_MINMVOLT) / - (BATT_MAXMVOLT - BATT_MINMVOLT); - powermgmt_est_runningtime_min = battery_percent * BATT_MAXRUNTIME / 100; - } - send_battery_level_event(); -} - -void battery_read_info(int *voltage, int *level) -{ - battery_status_update(); - - if (voltage) - *voltage = battery_millivolts; - - if (level) - *level = battery_percent; -} - -unsigned int battery_voltage(void) -{ - battery_status_update(); - return battery_millivolts; -} - -int battery_level(void) -{ - battery_status_update(); - return battery_percent; -} - -int battery_time(void) -{ - battery_status_update(); - return powermgmt_est_runningtime_min; -} - -bool battery_level_safe(void) -{ - return battery_level() >= 10; -} - -void set_poweroff_timeout(int timeout) -{ - (void)timeout; -} - -void set_battery_capacity(int capacity) -{ - (void)capacity; -} - -#if BATTERY_TYPES_COUNT > 1 -void set_battery_type(int type) -{ - (void)type; -} -#endif - -void reset_poweroff_timer(void) -{ -} - -#ifdef HAVE_ACCESSORY_SUPPLY -void accessory_supply_set(bool enable) -{ - (void)enable; -} -#endif - -#else /* not SIMULATOR ******************************************************/ - +#ifndef SIMULATOR +static int shutdown_timeout = 0; /* * Average battery voltage and charger voltage, filtered via a digital * exponential filter (aka. exponential moving average, scaled): * avgbat = y[n] = (N-1)/N*y[n-1] + x[n]. battery_millivolts = y[n] / N. */ -static unsigned int avgbat; /* average battery voltage (filtering) */ -static unsigned int battery_millivolts;/* filtered battery voltage, millivolts */ +static unsigned int avgbat; +/* filtered battery voltage, millivolts */ +static unsigned int battery_millivolts; +/* default value, mAh */ +static int battery_capacity = BATTERY_CAPACITY_DEFAULT; + -/* battery level (0-100%) of this minute, updated once per minute */ -static int battery_percent = -1; -static int battery_capacity = BATTERY_CAPACITY_DEFAULT; /* default value, mAh */ #if BATTERY_TYPES_COUNT > 1 -static int battery_type = 0; +static int battery_type = 0; #else #define battery_type 0 #endif @@ -224,7 +93,7 @@ static int battery_type = 0; /* Power history: power_history[0] is the newest sample */ unsigned short power_history[POWER_HISTORY_LEN]; -static char power_stack[DEFAULT_STACK_SIZE/2 + DEBUG_STACK]; +static char power_stack[DEFAULT_STACK_SIZE/2 + POWERMGMT_DEBUG_STACK]; static const char power_thread_name[] = "power"; static int poweroff_timeout = 0; @@ -239,19 +108,6 @@ static int voltage_to_battery_level(int battery_millivolts); static void battery_status_update(void); static int runcurrent(void); -#ifndef TARGET_POWERMGMT_FILTER_CHARGE_STATE -static inline int powermgmt_filter_charge_state(void) -{ -#if CONFIG_CHARGING >= CHARGING_MONITOR - /* No adjustment of state */ - return charge_state; -#else - /* Always discharging */ - return DISCHARGING; -#endif -} -#endif /* TARGET_POWERMGMT_FILTER_CHARGE_STATE */ - void battery_read_info(int *voltage, int *level) { int millivolts = battery_adc_voltage(); @@ -272,20 +128,30 @@ void reset_poweroff_timer(void) void set_battery_type(int type) { if (type != battery_type) { + if ((unsigned)type >= BATTERY_TYPES_COUNT) + type = 0; + battery_type = type; - battery_status_update(); /* recalculate the battery status */ + battery_status_update(); /* recalculate the battery status */ } } #endif void set_battery_capacity(int capacity) { + if (capacity > BATTERY_CAPACITY_MAX) + capacity = BATTERY_CAPACITY_MAX; + if (capacity < BATTERY_CAPACITY_MIN) + capacity = BATTERY_CAPACITY_MIN; + battery_capacity = capacity; - if (battery_capacity > BATTERY_CAPACITY_MAX) - battery_capacity = BATTERY_CAPACITY_MAX; - if (battery_capacity < BATTERY_CAPACITY_MIN) - battery_capacity = BATTERY_CAPACITY_MIN; - battery_status_update(); /* recalculate the battery status */ + + battery_status_update(); /* recalculate the battery status */ +} + +int get_battery_capacity(void) +{ + return battery_capacity; } int battery_time(void) @@ -309,13 +175,19 @@ unsigned int battery_voltage(void) return battery_millivolts; } -#ifndef TARGET_BATTERY_LEVEL_SAFE /* Tells if the battery level is safe for disk writes */ bool battery_level_safe(void) { +#if defined(NO_LOW_BATTERY_SHUTDOWN) + return true; +#elif defined(HAVE_BATTERY_SWITCH) + /* Cannot rely upon the battery reading to be valid and the + * device could be powered externally. */ + return input_millivolts() > battery_level_dangerous[battery_type]; +#else return battery_millivolts > battery_level_dangerous[battery_type]; -} #endif +} void set_poweroff_timeout(int timeout) { @@ -324,7 +196,7 @@ void set_poweroff_timeout(int timeout) void set_sleep_timer(int seconds) { - if(seconds) { + if (seconds) { sleeptimer_active = true; sleeptimer_endtick = current_tick + seconds * HZ; } @@ -336,7 +208,7 @@ void set_sleep_timer(int seconds) int get_sleep_timer(void) { - if(sleeptimer_active) + if (sleeptimer_active) return (sleeptimer_endtick - current_tick) / HZ; else return 0; @@ -345,45 +217,46 @@ int get_sleep_timer(void) /* look into the percent_to_volt_* table and get a realistic battery level */ static int voltage_to_percent(int voltage, const short* table) { - if (voltage <= table[0]) + if (voltage <= table[0]) { return 0; - else - if (voltage >= table[10]) - return 100; - else { - /* search nearest value */ - int i = 0; - while ((i < 10) && (table[i+1] < voltage)) - i++; - /* interpolate linear between the smaller and greater value */ - return (i * 10) /* Tens digit, 10% per entry */ - + (((voltage - table[i]) * 10) - / (table[i+1] - table[i])); /* Ones digit: interpolated */ - } + } + else if (voltage >= table[10]) { + return 100; + } + else { + /* search nearest value */ + int i = 0; + + while (i < 10 && table[i+1] < voltage) + i++; + + /* interpolate linear between the smaller and greater value */ + /* Tens digit, 10% per entry, ones digit: interpolated */ + return i*10 + (voltage - table[i])*10 / (table[i+1] - table[i]); + } } /* update battery level and estimated runtime, called once per minute or * when battery capacity / type settings are changed */ static int voltage_to_battery_level(int battery_millivolts) { - const int state = powermgmt_filter_charge_state(); int level; - if (state == DISCHARGING) { - level = voltage_to_percent(battery_millivolts, - percent_to_volt_discharge[battery_type]); - } #if CONFIG_CHARGING >= CHARGING_MONITOR - else if (state == CHARGING) { + if (charging_state()) { /* battery level is defined to be < 100% until charging is finished */ - level = MIN(voltage_to_percent(battery_millivolts, - percent_to_volt_charge), 99); + level = voltage_to_percent(battery_millivolts, + percent_to_volt_charge); + if (level > 99) + level = 99; } - else { - /* in topoff/trickle charge, battery is by definition 100% full */ - level = 100; + else +#endif /* CONFIG_CHARGING >= CHARGING_MONITOR */ + { + /* DISCHARGING or error state */ + level = voltage_to_percent(battery_millivolts, + percent_to_volt_discharge[battery_type]); } -#endif return level; } @@ -393,26 +266,25 @@ static void battery_status_update(void) int level = voltage_to_battery_level(battery_millivolts); /* calculate estimated remaining running time */ - /* discharging: remaining running time */ - /* charging: remaining charging time */ #if CONFIG_CHARGING >= CHARGING_MONITOR - if (powermgmt_filter_charge_state() == CHARGING) { - powermgmt_est_runningtime_min = (100 - level) * battery_capacity * 60 - / 100 / (CURRENT_MAX_CHG - runcurrent()); + if (charging_state()) { + /* charging: remaining charging time */ + powermgmt_est_runningtime_min = (100 - level)*battery_capacity*60 + / 100 / (CURRENT_MAX_CHG - runcurrent()); } else #endif - { - if ((battery_millivolts + 20) > percent_to_volt_discharge[0][0]) - powermgmt_est_runningtime_min = (level + battery_percent) * 60 * - battery_capacity / 200 / runcurrent(); - - else if (battery_millivolts <= battery_level_shutoff[0]) - powermgmt_est_runningtime_min = 0; - - else - powermgmt_est_runningtime_min = (battery_millivolts - - battery_level_shutoff[0]) / 2; + /* discharging: remaining running time */ + if ((battery_millivolts + 20) > percent_to_volt_discharge[0][0]) { + powermgmt_est_runningtime_min = (level + battery_percent)*60 + * battery_capacity / 200 / runcurrent(); + } + else if (battery_millivolts <= battery_level_shutoff[0]) { + powermgmt_est_runningtime_min = 0; + } + else { + powermgmt_est_runningtime_min = + (battery_millivolts - battery_level_shutoff[0]) / 2; } battery_percent = level; @@ -434,62 +306,55 @@ static void battery_status_update(void) static void handle_auto_poweroff(void) { long timeout = poweroff_timeout*60*HZ; - int audio_stat = audio_status(); + int audio_stat = audio_status(); + long tick = current_tick; #if CONFIG_CHARGING /* * Inhibit shutdown as long as the charger is plugged in. If it is * unplugged, wait for a timeout period and then shut down. */ - if(charger_input_state == CHARGER || audio_stat == AUDIO_STATUS_PLAY) { + if (charger_input_state == CHARGER || audio_stat == AUDIO_STATUS_PLAY) { last_event_tick = current_tick; } #endif - if( !shutdown_timeout && query_force_shutdown()) { + if (!shutdown_timeout && query_force_shutdown()) { backlight_on(); sys_poweroff(); } - if(timeout && -#if CONFIG_TUNER && !defined(BOOTLOADER) - (!(get_radio_status() & FMRADIO_PLAYING)) && + if (timeout && +#if CONFIG_TUNER + !(get_radio_status() & FMRADIO_PLAYING) && #endif - !usb_inserted() && - ((audio_stat == 0) || - ((audio_stat == (AUDIO_STATUS_PLAY | AUDIO_STATUS_PAUSE)) && - !sleeptimer_active))) - { - if(TIME_AFTER(current_tick, last_event_tick + timeout) && - TIME_AFTER(current_tick, storage_last_disk_activity() + timeout)) - { + !usb_inserted() && + (audio_stat == 0 || + (audio_stat == (AUDIO_STATUS_PLAY | AUDIO_STATUS_PAUSE) && + !sleeptimer_active))) { + + if (TIME_AFTER(tick, last_event_tick + timeout) && + TIME_AFTER(tick, storage_last_disk_activity() + timeout)) { sys_poweroff(); } } - else - { + else if (sleeptimer_active) { /* Handle sleeptimer */ - if(sleeptimer_active) - { - if(TIME_AFTER(current_tick, sleeptimer_endtick)) - { - audio_stop(); - if (usb_inserted() + if (TIME_AFTER(tick, sleeptimer_endtick)) { + audio_stop(); + + if (usb_inserted() #if CONFIG_CHARGING && !defined(HAVE_POWEROFF_WHILE_CHARGING) - || ((charger_input_state == CHARGER) || - (charger_input_state == CHARGER_PLUGGED)) + || charger_input_state != NO_CHARGER #endif - ) - { - DEBUGF("Sleep timer timeout. Stopping...\n"); - set_sleep_timer(0); - backlight_off(); /* Nighty, nighty... */ - } - else - { - DEBUGF("Sleep timer timeout. Shutting off...\n"); - sys_poweroff(); - } + ) { + DEBUGF("Sleep timer timeout. Stopping...\n"); + set_sleep_timer(0); + backlight_off(); /* Nighty, nighty... */ + } + else { + DEBUGF("Sleep timer timeout. Shutting off...\n"); + sys_poweroff(); } } } @@ -504,25 +369,24 @@ static int runcurrent(void) #if MEM == 8 && !(defined(ARCHOS_ONDIOSP) || defined(ARCHOS_ONDIOFM)) /* assuming 192 kbps, the running time is 22% longer with 8MB */ - current = (CURRENT_NORMAL*100/122); + current = CURRENT_NORMAL*100 / 122; #else current = CURRENT_NORMAL; #endif /* MEM == 8 */ - if(usb_inserted() -#if defined(HAVE_USB_POWER) - #if (CURRENT_USB < CURRENT_NORMAL) + if (usb_inserted() +#ifdef HAVE_USB_POWER + #if (CURRENT_USB < CURRENT_NORMAL) || usb_powered() - #else + #else && !usb_powered() - #endif + #endif #endif - ) - { + ) { current = CURRENT_USB; } -#if defined(HAVE_BACKLIGHT) && !defined(BOOTLOADER) +#if defined(HAVE_BACKLIGHT) if (backlight_get_current_timeout() == 0) /* LED always on */ current += CURRENT_BACKLIGHT; #endif @@ -542,7 +406,7 @@ static int runcurrent(void) current += CURRENT_REMOTE; #endif - return(current); + return current; } @@ -550,424 +414,76 @@ static int runcurrent(void) #ifdef HAVE_RTC_ALARM static void power_thread_rtc_process(void) { - if (rtc_check_alarm_flag()) { + if (rtc_check_alarm_flag()) rtc_enable_alarm(false); - } } #endif -#ifndef TARGET_QUERY_FORCE_SHUTDOWN +/* switch off unit if battery level is too low for reliable operation */ bool query_force_shutdown(void) { -#ifndef NO_LOW_BATTERY_SHUTDOWN - /* switch off unit if battery level is too low for reliable operation */ - return battery_millivolts < battery_level_shutoff[battery_type]; -#else +#if defined(NO_LOW_BATTERY_SHUTDOWN) return false; +#elif defined(HAVE_BATTERY_SWITCH) + /* Cannot rely upon the battery reading to be valid and the + * device could be powered externally. */ + return input_millivolts() < battery_level_shutoff[battery_type]; +#else + return battery_millivolts < battery_level_shutoff[battery_type]; #endif } -#endif /* TARGET_QUERY_FORCE_SHUTDOWN */ +#ifdef HAVE_BATTERY_SWITCH /* - * This power thread maintains a history of battery voltage - * and implements a charging algorithm. + * Reset the battery voltage filter to a new value and update the + * status. */ -#if CONFIG_CHARGING == CHARGING_CONTROL -#define BATT_AVE_SAMPLES 32 /* filter constant / @ 2Hz sample rate */ - -/* - * For a complete description of the charging algorithm read - * docs/CHARGING_ALGORITHM. - */ -int long_delta; /* long term delta battery voltage */ -int short_delta; /* short term delta battery voltage */ -bool disk_activity_last_cycle = false; /* flag set to aid charger time - * calculation */ -char power_message[POWER_MESSAGE_LEN] = ""; /* message that's shown in - debug menu */ - /* percentage at which charging - starts */ -int powermgmt_last_cycle_startstop_min = 0; /* how many minutes ago was the - charging started or - stopped? */ -int powermgmt_last_cycle_level = 0; /* which level had the - batteries at this time? */ -int trickle_sec = 0; /* how many seconds should the - charger be enabled per - minute for trickle - charging? */ -int pid_p = 0; /* PID proportional term */ -int pid_i = 0; /* PID integral term */ - -static inline void charging_algorithm_small_step(void) -{ - if (storage_disk_is_active()) { - /* flag hdd use for charging calculation */ - disk_activity_last_cycle = true; - } - -#if defined(DEBUG_FILE) - /* - * If we have a lot of pending writes or if the disk is spining, - * fsync the debug log file. - */ - if((wrcount > 10) || ((wrcount > 0) && storage_disk_is_active())) { - fsync(fd); - wrcount = 0; - } -#endif /* defined(DEBUG_FILE) */ -} - -static inline void charging_algorithm_big_step(void) -{ - static unsigned int target_voltage = TRICKLE_VOLTAGE; /* desired topoff/trickle - * voltage level */ - static int charge_max_time_idle = 0; /* max. charging duration, calculated at - * beginning of charging */ - static int charge_max_time_now = 0; /* max. charging duration including - * hdd activity */ - static int minutes_disk_activity = 0; /* count minutes of hdd use during - * charging */ - static int last_disk_activity = CHARGE_END_LONGD + 1; /* last hdd use x mins ago */ - int i; - - if (charger_input_state == CHARGER_PLUGGED) { - pid_p = 0; - pid_i = 0; - snprintf(power_message, POWER_MESSAGE_LEN, "Charger plugged in"); - /* - * The charger was just plugged in. If the battery level is - * nearly charged, just trickle. If the battery is low, start - * a full charge cycle. If the battery level is in between, - * top-off and then trickle. - */ - if(battery_percent > START_TOPOFF_CHG) { - powermgmt_last_cycle_level = battery_percent; - powermgmt_last_cycle_startstop_min = 0; - if(battery_percent >= START_TRICKLE_CHG) { - charge_state = TRICKLE; - target_voltage = TRICKLE_VOLTAGE; - } else { - charge_state = TOPOFF; - target_voltage = TOPOFF_VOLTAGE; - } - } else { - /* - * Start the charger full strength - */ - i = CHARGE_MAX_TIME_1500 * battery_capacity / 1500; - charge_max_time_idle = - i * (100 + 35 - battery_percent) / 100; - if (charge_max_time_idle > i) { - charge_max_time_idle = i; - } - charge_max_time_now = charge_max_time_idle; - - snprintf(power_message, POWER_MESSAGE_LEN, - "ChgAt %d%% max %dm", battery_level(), - charge_max_time_now); - - /* enable the charger after the max time calc is done, - because battery_level depends on if the charger is - on */ - DEBUGF("power: charger inserted and battery" - " not full, charging\n"); - powermgmt_last_cycle_level = battery_percent; - powermgmt_last_cycle_startstop_min = 0; - trickle_sec = 60; - long_delta = short_delta = 999999; - charge_state = CHARGING; - } - } - - if (charge_state == CHARGING) { - /* alter charge time max length with extra disk use */ - if (disk_activity_last_cycle) { - minutes_disk_activity++; - charge_max_time_now = charge_max_time_idle + - (minutes_disk_activity * 2 / 5); - disk_activity_last_cycle = false; - last_disk_activity = 0; - } else { - last_disk_activity++; - } - /* - * Check the delta voltage over the last X minutes so we can do - * our end-of-charge logic based on the battery level change. - *(no longer use minimum time as logic for charge end has 50 - * minutes minimum charge built in) - */ - if (powermgmt_last_cycle_startstop_min > CHARGE_END_SHORTD) { - short_delta = power_history[0] - - power_history[CHARGE_END_SHORTD - 1]; - } - - if (powermgmt_last_cycle_startstop_min > CHARGE_END_LONGD) { - /* - * Scan the history: the points where measurement is taken need to - * be fairly static. (check prior to short delta 'area') - * (also only check first and last 10 cycles - delta in middle OK) - */ - long_delta = power_history[0] - - power_history[CHARGE_END_LONGD - 1]; - - for(i = CHARGE_END_SHORTD; i < CHARGE_END_SHORTD + 10; i++) { - if(((power_history[i] - power_history[i+1]) > 50) || - ((power_history[i] - power_history[i+1]) < -50)) { - long_delta = 777777; - break; - } - } - for(i = CHARGE_END_LONGD - 11; i < CHARGE_END_LONGD - 1 ; i++) { - if(((power_history[i] - power_history[i+1]) > 50) || - ((power_history[i] - power_history[i+1]) < -50)) { - long_delta = 888888; - break; - } - } - } - - snprintf(power_message, POWER_MESSAGE_LEN, - "Chg %dm, max %dm", powermgmt_last_cycle_startstop_min, - charge_max_time_now); - /* - * End of charge criteria (any qualify): - * 1) Charged a long time - * 2) DeltaV went negative for a short time ( & long delta static) - * 3) DeltaV was negative over a longer period (no disk use only) - * Note: short_delta and long_delta are millivolts - */ - if ((powermgmt_last_cycle_startstop_min >= charge_max_time_now) || - (short_delta <= -50 && long_delta < 50 ) || (long_delta < -20 && - last_disk_activity > CHARGE_END_LONGD)) { - if (powermgmt_last_cycle_startstop_min > charge_max_time_now) { - DEBUGF("power: powermgmt_last_cycle_startstop_min > charge_max_time_now, " - "enough!\n"); - /* - *have charged too long and deltaV detection did not - *work! - */ - snprintf(power_message, POWER_MESSAGE_LEN, - "Chg tmout %d min", charge_max_time_now); - /* - * Switch to trickle charging. We skip the top-off - * since we've effectively done the top-off operation - * already since we charged for the maximum full - * charge time. - */ - powermgmt_last_cycle_level = battery_percent; - powermgmt_last_cycle_startstop_min = 0; - charge_state = TRICKLE; - - /* - * set trickle charge target to a relative voltage instead - * of an arbitrary value - the fully charged voltage may - * vary according to ambient temp, battery condition etc - * trickle target is -0.15v from full voltage acheived - * topup target is -0.05v from full voltage - */ - target_voltage = power_history[0] - 150; - - } else { - if(short_delta <= -5) { - DEBUGF("power: short-term negative" - " delta, enough!\n"); - snprintf(power_message, POWER_MESSAGE_LEN, - "end negd %d %dmin", short_delta, - powermgmt_last_cycle_startstop_min); - target_voltage = power_history[CHARGE_END_SHORTD - 1] - - 50; - } else { - DEBUGF("power: long-term small " - "positive delta, enough!\n"); - snprintf(power_message, POWER_MESSAGE_LEN, - "end lowd %d %dmin", long_delta, - powermgmt_last_cycle_startstop_min); - target_voltage = power_history[CHARGE_END_LONGD - 1] - - 50; - } - /* - * Switch to top-off charging. - */ - powermgmt_last_cycle_level = battery_percent; - powermgmt_last_cycle_startstop_min = 0; - charge_state = TOPOFF; - } - } - } - else if (charge_state != DISCHARGING) /* top off or trickle */ - { - /* - *Time to switch from topoff to trickle? - */ - if ((charge_state == TOPOFF) && - (powermgmt_last_cycle_startstop_min > TOPOFF_MAX_TIME)) - { - powermgmt_last_cycle_level = battery_percent; - powermgmt_last_cycle_startstop_min = 0; - charge_state = TRICKLE; - target_voltage = target_voltage - 100; - } - /* - * Adjust trickle charge time (proportional and integral terms). - * Note: I considered setting the level higher if the USB is - * plugged in, but it doesn't appear to be necessary and will - * generate more heat [gvb]. - */ - - pid_p = ((signed)target_voltage - (signed)battery_millivolts) / 5; - if((pid_p <= PID_DEADZONE) && (pid_p >= -PID_DEADZONE)) - pid_p = 0; - - if((unsigned) battery_millivolts < target_voltage) { - if(pid_i < 60) { - pid_i++; /* limit so it doesn't "wind up" */ - } - } else { - if(pid_i > 0) { - pid_i--; /* limit so it doesn't "wind up" */ - } - } - - trickle_sec = pid_p + pid_i; - - if(trickle_sec > 60) { - trickle_sec = 60; - } - if(trickle_sec < 0) { - trickle_sec = 0; - } - - } else if (charge_state == DISCHARGING) { - trickle_sec = 0; - /* - * The charger is enabled here only in one case: if it was - * turned on at boot time (power_init). Turn it off now. - */ - if (charger_enabled) - charger_enable(false); - } - - if (charger_input_state == CHARGER_UNPLUGGED) { - /* - * The charger was just unplugged. - */ - DEBUGF("power: charger disconnected, disabling\n"); - - charger_enable(false); - powermgmt_last_cycle_level = battery_percent; - powermgmt_last_cycle_startstop_min = 0; - trickle_sec = 0; - pid_p = 0; - pid_i = 0; - charge_state = DISCHARGING; - snprintf(power_message, POWER_MESSAGE_LEN, "Charger: discharge"); - } - - /* sleep for a minute */ - if(trickle_sec > 0) { - charger_enable(true); - power_thread_sleep(HZ * trickle_sec); - } - if(trickle_sec < 60) - charger_enable(false); - power_thread_sleep(HZ * (60 - trickle_sec)); - -#if defined(DEBUG_FILE) - if(usb_inserted()) { - if(fd >= 0) { - /* It is probably too late to close the file but we can try...*/ - close(fd); - fd = -1; - } - } else { - if(fd < 0) { - fd = open(DEBUG_FILE_NAME, O_WRONLY | O_APPEND | O_CREAT); - if(fd >= 0) { - snprintf(debug_message, DEBUG_MESSAGE_LEN, - "cycle_min, bat_millivolts, bat_percent, chgr_state" - " ,charge_state, pid_p, pid_i, trickle_sec\n"); - write(fd, debug_message, strlen(debug_message)); - wrcount = 99; /* force a flush */ - } - } - if(fd >= 0) { - snprintf(debug_message, DEBUG_MESSAGE_LEN, - "%d, %d, %d, %d, %d, %d, %d, %d\n", - powermgmt_last_cycle_startstop_min, battery_millivolts, - battery_percent, charger_input_state, charge_state, - pid_p, pid_i, trickle_sec); - write(fd, debug_message, strlen(debug_message)); - wrcount++; - } - } -#endif /* defined(DEBUG_FILE) */ - - powermgmt_last_cycle_startstop_min++; -} - -/* - * Prepare charging for poweroff - */ -static inline void charging_algorithm_close(void) -{ -#if defined(DEBUG_FILE) - if(fd >= 0) { - close(fd); - fd = -1; - } -#endif -} -#elif CONFIG_CHARGING == CHARGING_TARGET -extern void charging_algorithm_big_step(void); -extern void charging_algorithm_small_step(void); -extern void charging_algorithm_close(void); - -void set_filtered_battery_voltage(int millivolts) +void reset_battery_filter(int millivolts) { avgbat = millivolts * BATT_AVE_SAMPLES; battery_millivolts = millivolts; battery_status_update(); } +#endif /* HAVE_BATTERY_SWITCH */ -#else -#define BATT_AVE_SAMPLES 128 /* slw filter constant for all others */ - -static inline void charging_algorithm_small_step(void) +/** Generic charging algorithms for common charging types **/ +#if CONFIG_CHARGING == CHARGING_SIMPLE +static inline void charging_algorithm_step(void) { -#if CONFIG_CHARGING == CHARGING_MONITOR - switch (charger_input_state) - { - case CHARGER_UNPLUGGED: - case NO_CHARGER: - charge_state = DISCHARGING; - break; - case CHARGER_PLUGGED: - case CHARGER: - if (charging_state()) { - charge_state = CHARGING; - } else { - charge_state = DISCHARGING; - } - break; - } -#endif /* CONFIG_CHARGING == CHARGING_MONITOR */ + /* Nothing to do */ } -static inline void charging_algorithm_big_step(void) -{ - /* sleep for a minute */ - power_thread_sleep(HZ * 60); -} - -/* - * Prepare charging for poweroff - */ static inline void charging_algorithm_close(void) { /* Nothing to do */ } -#endif /* CONFIG_CHARGING == CHARGING_CONTROL */ +#elif CONFIG_CHARGING == CHARGING_MONITOR +/* + * Monitor CHARGING/DISCHARGING state. + */ +static inline void charging_algorithm_step(void) +{ + switch (charger_input_state) + { + case CHARGER_PLUGGED: + case CHARGER: + if (charging_state()) { + charge_state = CHARGING; + break; + } + /* Fallthrough */ + case CHARGER_UNPLUGGED: + case NO_CHARGER: + charge_state = DISCHARGING; + break; + } +} + +static inline void charging_algorithm_close(void) +{ + /* Nothing to do */ +} +#endif /* CONFIG_CHARGING == * */ #if CONFIG_CHARGING /* Shortcut function calls - compatibility, simplicity. */ @@ -975,201 +491,281 @@ static inline void charging_algorithm_close(void) /* Returns true if any power input is capable of charging. */ bool charger_inserted(void) { - return power_input_status() & POWER_INPUT_CHARGER; + return power_thread_inputs & POWER_INPUT_CHARGER; } /* Returns true if any power input is connected - charging-capable * or not. */ bool power_input_present(void) { - return power_input_status() & POWER_INPUT; + return power_thread_inputs & POWER_INPUT; +} + +/* + * Detect charger inserted. Return true if the state is transistional. + */ +static inline bool detect_charger(unsigned int pwr) +{ + /* + * Detect charger plugged/unplugged transitions. On a plugged or + * unplugged event, we return immediately, run once through the main + * loop (including the subroutines), and end up back here where we + * transition to the appropriate steady state charger on/off state. + */ + if (pwr & POWER_INPUT_CHARGER) { + switch (charger_input_state) + { + case NO_CHARGER: + case CHARGER_UNPLUGGED: + charger_input_state = CHARGER_PLUGGED; + break; + + case CHARGER_PLUGGED: + queue_broadcast(SYS_CHARGER_CONNECTED, 0); + last_sent_battery_level = 0; + charger_input_state = CHARGER; + break; + + case CHARGER: + /* Steady state */ + return false; + } + } + else { /* charger not inserted */ + switch (charger_input_state) + { + case NO_CHARGER: + /* Steady state */ + return false; + + case CHARGER_UNPLUGGED: + queue_broadcast(SYS_CHARGER_DISCONNECTED, 0); + last_sent_battery_level = 100; + charger_input_state = NO_CHARGER; + break; + + case CHARGER_PLUGGED: + case CHARGER: + charger_input_state = CHARGER_UNPLUGGED; + break; + } + } + + /* Transitional state */ + return true; } #endif /* CONFIG_CHARGING */ /* - * This function is called to do the relativly long sleep waits from within the - * main power_thread loop while at the same time servicing any other periodic - * functions in the power thread which need to be called at a faster periodic - * rate than the slow periodic rate of the main power_thread loop. - * - * While we are waiting for the time to expire, we average the battery - * voltages. + * Monitor the presence of a charger and perform critical frequent steps + * such as running the battery voltage filter. */ -void power_thread_sleep(int ticks) +static inline void power_thread_step(void) { - long tick_return = current_tick + ticks; + /* If the power off timeout expires, the main thread has failed + to shut down the system, and we need to force a power off */ + if (shutdown_timeout) { + shutdown_timeout -= POWER_THREAD_STEP_TICKS; - do - { -#if CONFIG_CHARGING - /* - * Detect charger plugged/unplugged transitions. On a plugged or - * unplugged event, we return immediately, run once through the main - * loop (including the subroutines), and end up back here where we - * transition to the appropriate steady state charger on/off state. - */ - if(power_input_status() & POWER_INPUT_CHARGER) { - switch(charger_input_state) { - case NO_CHARGER: - case CHARGER_UNPLUGGED: - charger_input_state = CHARGER_PLUGGED; - tick_return = current_tick; - goto do_small_step; /* Algorithm should see transition */ - case CHARGER_PLUGGED: - queue_broadcast(SYS_CHARGER_CONNECTED, 0); - last_sent_battery_level = 0; - charger_input_state = CHARGER; - break; - case CHARGER: - break; - } - } else { /* charger not inserted */ - switch(charger_input_state) { - case NO_CHARGER: - break; - case CHARGER_UNPLUGGED: - queue_broadcast(SYS_CHARGER_DISCONNECTED, 0); - last_sent_battery_level = 100; - charger_input_state = NO_CHARGER; - break; - case CHARGER_PLUGGED: - case CHARGER: - charger_input_state = CHARGER_UNPLUGGED; - tick_return = current_tick; - goto do_small_step; /* Algorithm should see transition */ - } - } -#endif /* CONFIG_CHARGING */ - - ticks = tick_return - current_tick; - - if (ticks > 0) { - ticks = MIN(HZ/2, ticks); - sleep(ticks); - } - - /* If the power off timeout expires, the main thread has failed - to shut down the system, and we need to force a power off */ - if(shutdown_timeout) { - shutdown_timeout -= MAX(ticks, 1); - if(shutdown_timeout <= 0) - power_off(); - } + if (shutdown_timeout <= 0) + power_off(); + } #ifdef HAVE_RTC_ALARM - power_thread_rtc_process(); + power_thread_rtc_process(); #endif - /* - * Do a digital exponential filter. We don't sample the battery if - * the disk is spinning unless we are in USB mode (the disk will most - * likely always be spinning in USB mode) or charging. - */ - if (!storage_disk_is_active() || usb_inserted() + /* + * Do a digital exponential filter. We don't sample the battery if + * the disk is spinning unless we are in USB mode (the disk will most + * likely always be spinning in USB mode) or charging. + */ + if (!storage_disk_is_active() || usb_inserted() #if CONFIG_CHARGING >= CHARGING_MONITOR - || charger_input_state == CHARGER + || charger_input_state == CHARGER #endif - ) { - avgbat += battery_adc_voltage() - (avgbat / BATT_AVE_SAMPLES); - /* - * battery_millivolts is the millivolt-scaled filtered battery value. - */ - battery_millivolts = avgbat / BATT_AVE_SAMPLES; + ) { + avgbat += battery_adc_voltage() - avgbat / BATT_AVE_SAMPLES; + /* + * battery_millivolts is the millivolt-scaled filtered battery value. + */ + battery_millivolts = avgbat / BATT_AVE_SAMPLES; - /* update battery status every time an update is available */ - battery_status_update(); - } - else if (battery_percent < 8) { - /* If battery is low, observe voltage during disk activity. - * Shut down if voltage drops below shutoff level and we are not - * using NiMH or Alkaline batteries. - */ - battery_millivolts = (battery_adc_voltage() + - battery_millivolts + 1) / 2; - - /* update battery status every time an update is available */ - battery_status_update(); - - if (!shutdown_timeout && query_force_shutdown()) { - sys_poweroff(); - } - else { - avgbat += battery_millivolts - (avgbat / BATT_AVE_SAMPLES); - } - } - -#if CONFIG_CHARGING - do_small_step: -#endif - charging_algorithm_small_step(); + /* update battery status every time an update is available */ + battery_status_update(); } - while (TIME_BEFORE(current_tick, tick_return)); -} + else if (battery_percent < 8) { + /* + * If battery is low, observe voltage during disk activity. + * Shut down if voltage drops below shutoff level and we are not + * using NiMH or Alkaline batteries. + */ + battery_millivolts = (battery_adc_voltage() + + battery_millivolts + 1) / 2; + + /* update battery status every time an update is available */ + battery_status_update(); + + if (!shutdown_timeout && query_force_shutdown()) { + sys_poweroff(); + } + else { + avgbat += battery_millivolts - avgbat / BATT_AVE_SAMPLES; + } + } +} /* power_thread_step */ static void power_thread(void) { + long next_power_hist; + /* Delay reading the first battery level */ #ifdef MROBE_100 - while(battery_adc_voltage()>4200) /* gives false readings initially */ + while (battery_adc_voltage() > 4200) /* gives false readings initially */ +#endif + { + sleep(HZ/100); + } + +#if CONFIG_CHARGING + /* Initialize power input status before calling other routines. */ + power_thread_inputs = power_input_status(); #endif - sleep(HZ/100); /* initialize the voltages for the exponential filter */ avgbat = battery_adc_voltage() + 15; #ifdef HAVE_DISK_STORAGE /* this adjustment is only needed for HD based */ - /* The battery voltage is usually a little lower directly after - turning on, because the disk was used heavily. Raise it by 5% */ + /* The battery voltage is usually a little lower directly after + turning on, because the disk was used heavily. Raise it by 5% */ #if CONFIG_CHARGING - if(!charger_inserted()) /* only if charger not connected */ + if (!charger_inserted()) /* only if charger not connected */ #endif + { avgbat += (percent_to_volt_discharge[battery_type][6] - percent_to_volt_discharge[battery_type][5]) / 2; + } #endif /* HAVE_DISK_STORAGE */ avgbat = avgbat * BATT_AVE_SAMPLES; battery_millivolts = avgbat / BATT_AVE_SAMPLES; + power_history[0] = battery_millivolts; #if CONFIG_CHARGING - if(charger_inserted()) { - battery_percent = voltage_to_percent(battery_millivolts, - percent_to_volt_charge); - } else + if (charger_inserted()) { + battery_percent = voltage_to_percent(battery_millivolts, + percent_to_volt_charge); + } + else #endif - { battery_percent = voltage_to_percent(battery_millivolts, - percent_to_volt_discharge[battery_type]); - battery_percent += (battery_percent < 100); + { + battery_percent = voltage_to_percent(battery_millivolts, + percent_to_volt_discharge[battery_type]); + battery_percent += battery_percent < 100; } #if CONFIG_CHARGING == CHARGING_TARGET powermgmt_init_target(); #endif + next_power_hist = current_tick + HZ*60; + while (1) { +#if CONFIG_CHARGING >= CHARGING_MONITOR + unsigned int pwr = power_input_status(); +#ifdef HAVE_BATTERY_SWITCH + if ((pwr ^ power_thread_inputs) & POWER_INPUT_BATTERY) { + sleep(HZ/10); + reset_battery_filter(battery_adc_voltage()); + } +#endif + power_thread_inputs = pwr; + + if (!detect_charger(pwr)) +#endif /* CONFIG_CHARGING */ + { + /* Steady state */ + sleep(POWER_THREAD_STEP_TICKS); + + /* Do common power tasks */ + power_thread_step(); + } + + /* Perform target tasks */ + charging_algorithm_step(); + + if (TIME_BEFORE(current_tick, next_power_hist)) + continue; + + /* increment to ensure there is a record for every minute + * rather than go forward from the current tick */ + next_power_hist += HZ*60; + /* rotate the power history */ - memmove(power_history + 1, power_history, + memmove(&power_history[1], &power_history[0], sizeof(power_history) - sizeof(power_history[0])); /* insert new value at the start, in millivolts 8-) */ power_history[0] = battery_millivolts; - charging_algorithm_big_step(); - handle_auto_poweroff(); } -} +} /* power_thread */ void powermgmt_init(void) { /* init history to 0 */ - memset(power_history, 0x00, sizeof(power_history)); + memset(power_history, 0, sizeof(power_history)); create_thread(power_thread, power_stack, sizeof(power_stack), 0, power_thread_name IF_PRIO(, PRIORITY_SYSTEM) IF_COP(, CPU)); } -#endif /* SIMULATOR */ +/* Various hardware housekeeping tasks relating to shutting down the player */ +void shutdown_hw(void) +{ + charging_algorithm_close(); + audio_stop(); + + if (battery_level_safe()) { /* do not save on critical battery */ +#ifdef HAVE_LCD_BITMAP + glyph_cache_save(); +#endif + if (storage_disk_is_active()) + storage_spindown(1); + } + + while (storage_disk_is_active()) + sleep(HZ/10); + +#if CONFIG_CODEC == SWCODEC + audiohw_close(); +#else + mp3_shutdown(); +#endif + + /* If HD is still active we try to wait for spindown, otherwise the + shutdown_timeout in power_thread_step will force a power off */ + while (storage_disk_is_active()) + sleep(HZ/10); + +#ifndef HAVE_LCD_COLOR + lcd_set_contrast(0); +#endif +#ifdef HAVE_REMOTE_LCD + lcd_remote_set_contrast(0); +#endif +#ifdef HAVE_LCD_SHUTDOWN + lcd_shutdown(); +#endif + + /* Small delay to make sure all HW gets time to flush. Especially + eeprom chips are quite slow and might be still writing the last + byte. */ + sleep(HZ/4); + power_off(); +} void sys_poweroff(void) { @@ -1177,12 +773,11 @@ void sys_poweroff(void) logf("sys_poweroff()"); /* If the main thread fails to shut down the system, we will force a power off after an 20 second timeout - 28 seconds if recording */ - if (shutdown_timeout == 0) - { -#if (defined(IAUDIO_X5) || defined(IAUDIO_M5)) && !defined (SIMULATOR) + if (shutdown_timeout == 0) { +#if defined(IAUDIO_X5) || defined(IAUDIO_M5) pcf50606_reset_timeout(); /* Reset timer on first attempt only */ #endif -#if defined(HAVE_RECORDING) && !defined(BOOTLOADER) +#ifdef HAVE_RECORDING if (audio_status() & AUDIO_STATUS_RECORD) shutdown_timeout += HZ*8; #endif @@ -1195,9 +790,9 @@ void sys_poweroff(void) void cancel_shutdown(void) { - logf("sys_cancel_shutdown()"); + logf("cancel_shutdown()"); -#if (defined(IAUDIO_X5) || defined(IAUDIO_M5)) && !defined (SIMULATOR) +#if defined(IAUDIO_X5) || defined(IAUDIO_M5) /* TODO: Move some things to target/ tree */ if (shutdown_timeout) pcf50606_reset_timeout(); @@ -1205,66 +800,23 @@ void cancel_shutdown(void) shutdown_timeout = 0; } - -/* Various hardware housekeeping tasks relating to shutting down the jukebox */ -void shutdown_hw(void) -{ -#ifndef SIMULATOR - charging_algorithm_close(); - audio_stop(); - if (battery_level_safe()) { /* do not save on critical battery */ -#ifdef HAVE_LCD_BITMAP - glyph_cache_save(); -#endif - if(storage_disk_is_active()) - storage_spindown(1); - } - while(storage_disk_is_active()) - sleep(HZ/10); - -#if CONFIG_CODEC != SWCODEC - mp3_shutdown(); -#else - audiohw_close(); -#endif - - /* If HD is still active we try to wait for spindown, otherwise the - shutdown_timeout in power_thread_sleep will force a power off */ - while(storage_disk_is_active()) - sleep(HZ/10); -#ifndef HAVE_LCD_COLOR - lcd_set_contrast(0); -#endif -#ifdef HAVE_REMOTE_LCD - lcd_remote_set_contrast(0); -#endif - -#ifdef HAVE_LCD_SHUTDOWN - lcd_shutdown(); -#endif - - /* Small delay to make sure all HW gets time to flush. Especially - eeprom chips are quite slow and might be still writing the last - byte. */ - sleep(HZ/4); - power_off(); -#endif /* #ifndef SIMULATOR */ -} +#endif /* SIMULATOR */ /* Send system battery level update events on reaching certain significant - levels. This must be called after battery_percent has been updated. */ -static void send_battery_level_event(void) + levels. This must be called after battery_percent has been updated. */ +void send_battery_level_event(void) { static const int levels[] = { 5, 15, 30, 50, 0 }; const int *level = levels; + while (*level) { - if (battery_percent <= *level && last_sent_battery_level > *level) - { + if (battery_percent <= *level && last_sent_battery_level > *level) { last_sent_battery_level = *level; queue_broadcast(SYS_BATTERY_UPDATE, last_sent_battery_level); break; } + level++; } } diff --git a/firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c b/firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c index fa9c7b045f..b4a6c61fbb 100644 --- a/firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c +++ b/firmware/target/arm/imx31/gigabeat-s/powermgmt-imx31.c @@ -168,7 +168,7 @@ int battery_adc_temp(void) /* All code has a preference for the main charger being connected over * USB. USB is considered in the algorithm only if it is the sole source. */ static uint32_t int_sense0 = 0; /* Interrupt Sense 0 bits */ -static unsigned int power_status = POWER_INPUT_NONE; /* Detect input changes */ +static unsigned int last_inputs = POWER_INPUT_NONE; /* Detect input changes */ static int charger_total_timer = 0; /* Total allowed charging time */ static int icharger_ave = 0; /* Filtered charging current */ static bool charger_close = false; /* Shutdown notification */ @@ -181,7 +181,7 @@ static int autorecharge_counter = 0 ; /* Battery < threshold debounce */ static int chgcurr_timer = 0; /* Countdown to CHGCURR error */ #define AUTORECHARGE_COUNTDOWN (10*2) /* 10s debounce */ #define WATCHDOG_TIMEOUT (10*2) /* If not serviced, poweroff in 10s */ -#define CHGCURR_TIMEOUT (2*2) /* 2s debounce */ +#define CHGCURR_TIMEOUT (4*2) /* 4s debounce */ /* Temperature monitoring */ static enum @@ -217,7 +217,7 @@ static bool charger_current_filter_step(void) /* Return true if the main charger is connected. */ static bool main_charger_connected(void) { - return (power_status & + return (last_inputs & POWER_INPUT_MAIN_CHARGER & POWER_INPUT_CHARGER) != 0; } @@ -233,16 +233,14 @@ static unsigned int auto_recharge_voltage(void) return BATT_USB_VAUTO_RECHARGE; } -#ifndef NO_LOW_BATTERY_SHUTDOWN /* Return greater of supply (BP) or filtered battery voltage. */ -static unsigned int input_millivolts(void) +unsigned int input_millivolts(void) { unsigned int app_millivolts = application_supply_adc_voltage(); unsigned int bat_millivolts = battery_voltage(); return MAX(app_millivolts, bat_millivolts); } -#endif /* Get smoothed readings for initializing filtered data. */ static int stat_battery_reading(int type) @@ -292,7 +290,7 @@ static bool update_filtered_battery_voltage(void) if (millivolts != INT_MIN) { - set_filtered_battery_voltage(millivolts); + reset_battery_filter(millivolts); return true; } @@ -357,13 +355,13 @@ static bool adjust_charger_current(void) int usb_select; uint32_t i; - usb_select = ((power_status & POWER_INPUT) == POWER_INPUT_USB) + usb_select = ((last_inputs & POWER_INPUT) == POWER_INPUT_USB) ? 1 : 0; if (charge_state == DISCHARGING && usb_select == 1) { /* USB-only, DISCHARGING, = maintaining battery */ - int select = (power_status & POWER_INPUT_CHARGER) ? 0 : 1; + int select = (last_inputs & POWER_INPUT_CHARGER) ? 0 : 1; charger_setting = charger_bits[CHARGING+1][select]; } else @@ -458,7 +456,7 @@ static bool charging_ok(void) if (ok) { /* Is the battery even connected? */ - ok = (power_status & POWER_INPUT_BATTERY) != 0; + ok = (last_inputs & POWER_INPUT_BATTERY) != 0; } if (ok) @@ -591,20 +589,6 @@ void powermgmt_init_target(void) #endif } -/* Returns CHARGING or DISCHARGING since that's all we really do. */ -int powermgmt_filter_charge_state(void) -{ - switch(charge_state) - { - case TRICKLE: - case TOPOFF: - case CHARGING: - return CHARGING; - default: - return DISCHARGING; - } -} - /* Returns true if the unit is charging the batteries. */ bool charging_state(void) { @@ -625,24 +609,6 @@ int battery_charge_current(void) return icharger_ave / ICHARGER_AVE_SAMPLES; } -bool query_force_shutdown(void) -{ -#ifndef NO_LOW_BATTERY_SHUTDOWN - return input_millivolts() < battery_level_shutoff[0]; -#else - return false; -#endif -} - -bool battery_level_safe(void) -{ -#ifndef NO_LOW_BATTERY_SHUTDOWN - return input_millivolts() > battery_level_dangerous[0]; -#else - return true; -#endif -} - static void charger_plugged(void) { adc_enable_channel(ADC_BATTERY_TEMP, true); @@ -662,7 +628,7 @@ static void charger_unplugged(void) } /* Might need to reevaluate these bits in charger_none. */ - power_status &= ~(POWER_INPUT | POWER_INPUT_CHARGER); + last_inputs &= ~(POWER_INPUT | POWER_INPUT_CHARGER); temp_state = TEMP_STATE_NORMAL; autorecharge_counter = 0; chgcurr_timer = 0; @@ -672,15 +638,11 @@ static void charger_unplugged(void) static void charger_none(void) { - unsigned int pwr = power_input_status(); + unsigned int pwr = power_thread_inputs; - if (power_status != pwr) + if (last_inputs != pwr) { - /* If battery switch state changed, reset filter. */ - if ((power_status ^ pwr) & POWER_INPUT_BATTERY) - update_filtered_battery_voltage(); - - power_status = pwr; + last_inputs = pwr; if (charge_state == CHARGE_STATE_DISABLED) return; @@ -696,7 +658,7 @@ static void charger_none(void) else { charger_unplugged(); - power_status = pwr; /* Restore status */ + last_inputs = pwr; /* Restore status */ } } else if (charger_setting != 0) @@ -716,17 +678,13 @@ static void charger_none(void) static void charger_control(void) { - unsigned int pwr = power_input_status(); + unsigned int pwr = power_thread_inputs; - if (power_status != pwr) + if (last_inputs != pwr) { - unsigned int changed = power_status ^ pwr; + unsigned int changed = last_inputs ^ pwr; - power_status = pwr; - - /* If battery switch state changed, reset filter. */ - if (changed & POWER_INPUT_BATTERY) - update_filtered_battery_voltage(); + last_inputs = pwr; if (charger_setting != 0) charger_setting = CHARGER_ADJUST; @@ -771,12 +729,11 @@ static void charger_control(void) { /* Battery voltage may have dropped and a charge cycle should * start again. Debounced. */ - if (autorecharge_counter < 0) + if (autorecharge_counter < 0 && + battery_adc_voltage() < BATT_FULL_VOLTAGE) { - /* Try starting a cycle now regardless of battery level to - * allow user to ensure the battery is topped off. It - * will soon turn off if already full. */ - autorecharge_counter = 0; + /* Try starting a cycle now if battery isn't already topped + * off to allow user to ensure the battery is full. */ } else if (battery_voltage() > auto_recharge_voltage()) { @@ -791,6 +748,8 @@ static void charger_control(void) break; } + autorecharge_counter = 0; + charging_set_thread_priority(true); if (stat_battery_reading(ADC_BATTERY) < BATT_VTRICKLE_CHARGE) @@ -858,10 +817,12 @@ static void charger_control(void) } /* Main charging algorithm - called from powermgmt.c */ -void charging_algorithm_small_step(void) +void charging_algorithm_step(void) { +#ifdef IMX31_ALLOW_CHARGING if (service_wdt) watchdog_service(); +#endif /* Switch by input state */ switch (charger_input_state) @@ -909,12 +870,6 @@ void charging_algorithm_small_step(void) } } -void charging_algorithm_big_step(void) -{ - /* Sleep for one minute */ - power_thread_sleep(HZ*60); -} - /* 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) diff --git a/firmware/target/arm/imx31/gigabeat-s/powermgmt-target.h b/firmware/target/arm/imx31/gigabeat-s/powermgmt-target.h index 8ad4af8d18..1b55a4ed24 100644 --- a/firmware/target/arm/imx31/gigabeat-s/powermgmt-target.h +++ b/firmware/target/arm/imx31/gigabeat-s/powermgmt-target.h @@ -53,6 +53,7 @@ #define BATT_VTRICKLE_CHARGE 2900 /* Must charge slowly */ #define BATT_VSLOW_CHARGE 3500 /* Lower-current charge mode below * this level */ +#define BATT_FULL_VOLTAGE 4161 /* Battery already topped */ #define BATT_VAUTO_RECHARGE 4100 /* When to begin another cycle */ #define BATT_USB_VAUTO_RECHARGE 4000 /* When to cycle with USB only */ #define BATT_USB_VSTOP 4140 /* When to "stop" when USB only */ @@ -92,23 +93,13 @@ #define BATT_AVE_SAMPLES 32 #define ICHARGER_AVE_SAMPLES 32 +void powermgmt_init_target(void); +void charging_algorithm_step(void); +void charging_algorithm_close(void); + /* Provide filtered charge current */ int battery_charge_current(void); -#ifndef SIMULATOR -/* Indicate various functions that require implementation at the target level. - * This is because the battery could be low or the battery switch is off but - * with the main charger attached which implies safe power for anything. The - * true battery reading is always reported for voltage readings and not the - * value at the application supply. */ -#define TARGET_QUERY_FORCE_SHUTDOWN - -/* For this the application supply is read out if the charger is attached or - * the battery read if not (completely hardware selected at BP). */ -#define TARGET_BATTERY_LEVEL_SAFE - -/* The state should be adjusted to CHARGING or DISCHARGING */ -#define TARGET_POWERMGMT_FILTER_CHARGE_STATE -#endif /* SIMULATOR */ +#define CURRENT_MAX_CHG battery_charge_current() #endif /* POWERMGMT_TARGET_H */ diff --git a/firmware/target/arm/iriver/h10/power-h10.c b/firmware/target/arm/iriver/h10/power-h10.c index deca3258e2..1a1f6afbfb 100644 --- a/firmware/target/arm/iriver/h10/power-h10.c +++ b/firmware/target/arm/iriver/h10/power-h10.c @@ -33,10 +33,6 @@ #include "logf.h" #include "usb.h" -#if CONFIG_CHARGING == CHARGING_CONTROL -bool charger_enabled; -#endif - #if CONFIG_TUNER bool tuner_power(bool status) diff --git a/firmware/target/arm/olympus/mrobe-100/power-mr100.c b/firmware/target/arm/olympus/mrobe-100/power-mr100.c index c3eb96b03c..46b99fbd46 100644 --- a/firmware/target/arm/olympus/mrobe-100/power-mr100.c +++ b/firmware/target/arm/olympus/mrobe-100/power-mr100.c @@ -29,10 +29,6 @@ #include "logf.h" #include "usb.h" -#if CONFIG_CHARGING == CHARGING_CONTROL -bool charger_enabled; -#endif - void power_init(void) { /* Enable power-off bit */ diff --git a/firmware/target/arm/philips/hdd1630/power-hdd1630.c b/firmware/target/arm/philips/hdd1630/power-hdd1630.c index 03a5794791..4e7172ef6f 100755 --- a/firmware/target/arm/philips/hdd1630/power-hdd1630.c +++ b/firmware/target/arm/philips/hdd1630/power-hdd1630.c @@ -29,10 +29,6 @@ #include "logf.h" #include "usb.h" -#if CONFIG_CHARGING == CHARGING_CONTROL -bool charger_enabled; -#endif - void power_init(void) { /* power off bit */ diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/powermgmt-meg-fx.c b/firmware/target/arm/s3c2440/gigabeat-fx/powermgmt-meg-fx.c index 6522e6534b..49f7e2e049 100644 --- a/firmware/target/arm/s3c2440/gigabeat-fx/powermgmt-meg-fx.c +++ b/firmware/target/arm/s3c2440/gigabeat-fx/powermgmt-meg-fx.c @@ -19,9 +19,10 @@ * KIND, either express or implied. * ****************************************************************************/ - #include "config.h" +#include "system.h" #include "adc.h" +#include "power.h" #include "powermgmt.h" const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] = @@ -58,3 +59,17 @@ unsigned int battery_adc_voltage(void) return (adc_read(ADC_UNREG_POWER) * BATTERY_SCALE_FACTOR) >> 10; } +unsigned int input_millivolts(void) +{ + + unsigned int batt_millivolts = battery_voltage(); + + if ((power_thread_inputs & POWER_INPUT_BATTERY) == 0) { + /* Just return a safe value if battery isn't connected */ + return 4050; + } + + return batt_millivolts; +} + + diff --git a/firmware/target/arm/tatung/tpj1022/power-tpj1022.c b/firmware/target/arm/tatung/tpj1022/power-tpj1022.c index abf5790702..fe5460d609 100644 --- a/firmware/target/arm/tatung/tpj1022/power-tpj1022.c +++ b/firmware/target/arm/tatung/tpj1022/power-tpj1022.c @@ -33,10 +33,6 @@ #include "logf.h" #include "usb.h" -#if CONFIG_CHARGING == CHARGING_CONTROL -bool charger_enabled; -#endif - void power_init(void) { } diff --git a/firmware/target/sh/archos/recorder/power-recorder.c b/firmware/target/sh/archos/recorder/power-recorder.c index d90c029890..8d7ea5fc51 100644 --- a/firmware/target/sh/archos/recorder/power-recorder.c +++ b/firmware/target/sh/archos/recorder/power-recorder.c @@ -25,9 +25,10 @@ #include "kernel.h" #include "system.h" #include "power.h" +#include "powermgmt-target.h" #include "usb.h" -bool charger_enabled; +static bool charger_on; void power_init(void) { @@ -48,13 +49,18 @@ void charger_enable(bool on) if(on) { and_b(~0x20, &PBDRL); - charger_enabled = 1; } else { or_b(0x20, &PBDRL); - charger_enabled = 0; } + + charger_on = on; +} + +bool charger_enabled(void) +{ + return charger_on; } void ide_power_enable(bool on) diff --git a/firmware/target/sh/archos/recorder/powermgmt-recorder.c b/firmware/target/sh/archos/recorder/powermgmt-recorder.c index 6de5cc8037..7b1842016c 100644 --- a/firmware/target/sh/archos/recorder/powermgmt-recorder.c +++ b/firmware/target/sh/archos/recorder/powermgmt-recorder.c @@ -19,9 +19,13 @@ * KIND, either express or implied. * ****************************************************************************/ - #include "config.h" +#include "system.h" +#include +#include "debug.h" +#include "storage.h" #include "adc.h" +#include "power.h" #include "powermgmt.h" const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] = @@ -60,3 +64,434 @@ unsigned int battery_adc_voltage(void) { return (adc_read(ADC_UNREG_POWER) * BATTERY_SCALE_FACTOR) >> 10; } + +/** Charger control **/ +#ifdef CHARGING_DEBUG_FILE +#include "file.h" +#define DEBUG_FILE_NAME "/powermgmt.csv" +#define DEBUG_MESSAGE_LEN 133 +static char debug_message[DEBUG_MESSAGE_LEN]; +static int fd = -1; /* write debug information to this file */ +static int wrcount = 0; +#endif /* CHARGING_DEBUG_FILE */ + +/* + * For a complete description of the charging algorithm read + * docs/CHARGING_ALGORITHM. + */ +int long_delta; /* long term delta battery voltage */ +int short_delta; /* short term delta battery voltage */ +bool disk_activity_last_cycle = false; /* flag set to aid charger time + * calculation */ +char power_message[POWER_MESSAGE_LEN] = ""; /* message that's shown in + debug menu */ + /* percentage at which charging + starts */ +int powermgmt_last_cycle_startstop_min = 0; /* how many minutes ago was the + charging started or + stopped? */ +int powermgmt_last_cycle_level = 0; /* which level had the + batteries at this time? */ +int trickle_sec = 0; /* how many seconds should the + charger be enabled per + minute for trickle + charging? */ +int pid_p = 0; /* PID proportional term */ +int pid_i = 0; /* PID integral term */ + +static unsigned int target_voltage = TRICKLE_VOLTAGE; /* desired topoff/trickle + * voltage level */ +static int charge_max_time_idle = 0; /* max. charging duration, calculated at + * beginning of charging */ +static int charge_max_time_now = 0; /* max. charging duration including + * hdd activity */ +static int minutes_disk_activity = 0; /* count minutes of hdd use during + * charging */ +static int last_disk_activity = CHARGE_END_LONGD + 1; /* last hdd use x mins ago */ + +#ifdef CHARGING_DEBUG_FILE +static void debug_file_close(void) +{ + if (fd >= 0) { + close(fd); + fd = -1; + } +} + +static void debug_file_log(void) +{ + if (usb_inserted()) { + /* It is probably too late to close the file but we can try... */ + debug_file_close(); + } + else if (fd < 0) { + fd = open(DEBUG_FILE_NAME, O_WRONLY | O_APPEND | O_CREAT); + + if (fd >= 0) { + snprintf(debug_message, DEBUG_MESSAGE_LEN, + "cycle_min, bat_millivolts, bat_percent, chgr_state" + " ,charge_state, pid_p, pid_i, trickle_sec\n"); + write(fd, debug_message, strlen(debug_message)); + wrcount = 99; /* force a flush */ + } + } + else { + snprintf(debug_message, DEBUG_MESSAGE_LEN, + "%d, %d, %d, %d, %d, %d, %d, %d\n", + powermgmt_last_cycle_startstop_min, battery_voltage(), + battery_level(), charger_input_state, charge_state, + pid_p, pid_i, trickle_sec); + write(fd, debug_message, strlen(debug_message)); + wrcount++; + } +} + +static void debug_file_sync(void) +{ + /* + * If we have a lot of pending writes or if the disk is spining, + * fsync the debug log file. + */ + if (wrcount > 10 || (wrcount > 0 && storage_disk_is_active())) { + if (fd >= 0) + fsync(fd); + + wrcount = 0; + } +} +#else /* !CHARGING_DEBUG_FILE */ +#define debug_file_close() +#define debug_file_log() +#define debug_file_sync() +#endif /* CHARGING_DEBUG_FILE */ + +/* + * Do tasks that should be done every step. + */ +static void do_frequent_tasks(void) +{ + if (storage_disk_is_active()) { + /* flag hdd use for charging calculation */ + disk_activity_last_cycle = true; + } + + debug_file_sync(); +} + +/* + * The charger was just plugged in. If the battery level is + * nearly charged, just trickle. If the battery is low, start + * a full charge cycle. If the battery level is in between, + * top-off and then trickle. + */ +static void charger_plugged(void) +{ + int battery_percent = battery_level(); + + pid_p = 0; + pid_i = 0; + powermgmt_last_cycle_level = battery_percent; + powermgmt_last_cycle_startstop_min = 0; + + snprintf(power_message, POWER_MESSAGE_LEN, "Charger plugged in"); + + if (battery_percent > START_TOPOFF_CHG) { + + if (battery_percent >= START_TRICKLE_CHG) { + charge_state = TRICKLE; + target_voltage = TRICKLE_VOLTAGE; + } + else { + charge_state = TOPOFF; + target_voltage = TOPOFF_VOLTAGE; + } + } + else { + /* + * Start the charger full strength + */ + int i = CHARGE_MAX_MIN_1500 * get_battery_capacity() / 1500; + charge_max_time_idle = i * (100 + 35 - battery_percent) / 100; + + if (charge_max_time_idle > i) + charge_max_time_idle = i; + + charge_max_time_now = charge_max_time_idle; + + snprintf(power_message, POWER_MESSAGE_LEN, + "ChgAt %d%% max %dm", battery_percent, + charge_max_time_now); + + /* + * Enable the charger after the max time calc is done, + * because battery_level depends on if the charger is + * on. + */ + DEBUGF("power: charger inserted and battery" + " not full, charging\n"); + trickle_sec = 60; + long_delta = short_delta = 999999; + charge_state = CHARGING; + } +} + +/* + * The charger was just unplugged. + */ +static void charger_unplugged(void) +{ + DEBUGF("power: charger disconnected, disabling\n"); + + charger_enable(false); + powermgmt_last_cycle_level = battery_level(); + powermgmt_last_cycle_startstop_min = 0; + trickle_sec = 0; + pid_p = 0; + pid_i = 0; + charge_state = DISCHARGING; + snprintf(power_message, POWER_MESSAGE_LEN, "Charger: discharge"); +} + +static void charging_step(void) +{ + int i; + + /* alter charge time max length with extra disk use */ + if (disk_activity_last_cycle) { + minutes_disk_activity++; + charge_max_time_now = charge_max_time_idle + + minutes_disk_activity*2 / 5; + disk_activity_last_cycle = false; + last_disk_activity = 0; + } + else { + last_disk_activity++; + } + + /* + * Check the delta voltage over the last X minutes so we can do + * our end-of-charge logic based on the battery level change + * (no longer use minimum time as logic for charge end has 50 + * minutes minimum charge built in). + */ + if (powermgmt_last_cycle_startstop_min > CHARGE_END_SHORTD) { + short_delta = power_history[0] - + power_history[CHARGE_END_SHORTD - 1]; + } + + if (powermgmt_last_cycle_startstop_min > CHARGE_END_LONGD) { + /* + * Scan the history: the points where measurement is taken need to + * be fairly static. Check prior to short delta 'area'. Also only + * check first and last 10 cycles (delta in middle OK). + */ + long_delta = power_history[0] - + power_history[CHARGE_END_LONGD - 1]; + + for (i = CHARGE_END_SHORTD; i < CHARGE_END_SHORTD + 10; i++) + { + if ((power_history[i] - power_history[i+1]) > 50 || + (power_history[i] - power_history[i+1]) < -50) { + long_delta = 777777; + break; + } + } + + for (i = CHARGE_END_LONGD - 11; i < CHARGE_END_LONGD - 1 ; i++) + { + if ((power_history[i] - power_history[i+1]) > 50 || + (power_history[i] - power_history[i+1]) < -50) { + long_delta = 888888; + break; + } + } + } + + snprintf(power_message, POWER_MESSAGE_LEN, + "Chg %dm, max %dm", powermgmt_last_cycle_startstop_min, + charge_max_time_now); + + /* + * End of charge criteria (any qualify): + * 1) Charged a long time + * 2) DeltaV went negative for a short time ( & long delta static) + * 3) DeltaV was negative over a longer period (no disk use only) + * + * Note: short_delta and long_delta are millivolts + */ + if (powermgmt_last_cycle_startstop_min >= charge_max_time_now || + (short_delta <= -50 && long_delta < 50) || + (long_delta < -20 && last_disk_activity > CHARGE_END_LONGD)) { + + int battery_percent = battery_level(); + + if (powermgmt_last_cycle_startstop_min > charge_max_time_now) { + DEBUGF("power: powermgmt_last_cycle_startstop_min > charge_max_time_now, " + "enough!\n"); + /* + * Have charged too long and deltaV detection did not + * work! + */ + snprintf(power_message, POWER_MESSAGE_LEN, + "Chg tmout %d min", charge_max_time_now); + /* + * Switch to trickle charging. We skip the top-off + * since we've effectively done the top-off operation + * already since we charged for the maximum full + * charge time. + */ + powermgmt_last_cycle_level = battery_percent; + powermgmt_last_cycle_startstop_min = 0; + charge_state = TRICKLE; + + /* + * Set trickle charge target to a relative voltage instead + * of an arbitrary value - the fully charged voltage may + * vary according to ambient temp, battery condition etc. + * Trickle target is -0.15v from full voltage acheived. + * Topup target is -0.05v from full voltage. + */ + target_voltage = power_history[0] - 150; + + } + else { + if(short_delta <= -5) { + DEBUGF("power: short-term negative" + " delta, enough!\n"); + snprintf(power_message, POWER_MESSAGE_LEN, + "end negd %d %dmin", short_delta, + powermgmt_last_cycle_startstop_min); + target_voltage = power_history[CHARGE_END_SHORTD - 1] - 50; + } + else { + DEBUGF("power: long-term small " + "positive delta, enough!\n"); + snprintf(power_message, POWER_MESSAGE_LEN, + "end lowd %d %dmin", long_delta, + powermgmt_last_cycle_startstop_min); + target_voltage = power_history[CHARGE_END_LONGD - 1] - 50; + } + + /* + * Switch to top-off charging. + */ + powermgmt_last_cycle_level = battery_percent; + powermgmt_last_cycle_startstop_min = 0; + charge_state = TOPOFF; + } + } +} + +static void topoff_trickle_step(void) +{ + unsigned int millivolts; + + /* + *Time to switch from topoff to trickle? + */ + if (charge_state == TOPOFF && + powermgmt_last_cycle_startstop_min > TOPOFF_MAX_MIN) { + + powermgmt_last_cycle_level = battery_level(); + powermgmt_last_cycle_startstop_min = 0; + charge_state = TRICKLE; + target_voltage = target_voltage - 100; + } + /* + * Adjust trickle charge time (proportional and integral terms). + * Note: I considered setting the level higher if the USB is + * plugged in, but it doesn't appear to be necessary and will + * generate more heat [gvb]. + */ + millivolts = battery_voltage(); + + pid_p = ((signed)target_voltage - (signed)millivolts) / 5; + if (pid_p <= PID_DEADZONE && pid_p >= -PID_DEADZONE) + pid_p = 0; + + if ((unsigned)millivolts < target_voltage) { + if (pid_i < 60) + pid_i++; /* limit so it doesn't "wind up" */ + } + else { + if (pid_i > 0) + pid_i--; /* limit so it doesn't "wind up" */ + } + + trickle_sec = pid_p + pid_i; + + if (trickle_sec > 60) + trickle_sec = 60; + + if (trickle_sec < 0) + trickle_sec = 0; +} + +void charging_algorithm_step(void) +{ + static int pwm_counter = 0; /* PWM total cycle in steps */ + static int pwm_duty = 0; /* PWM duty cycle in steps */ + + switch (charger_input_state) + { + case CHARGER_PLUGGED: + charger_plugged(); + break; + + case CHARGER_UNPLUGGED: + charger_unplugged(); + break; + + case CHARGER: + case NO_CHARGER: + do_frequent_tasks(); + + if (pwm_counter > 0) { + if (pwm_duty > 0 && --pwm_duty <= 0) + charger_enable(false); /* Duty cycle expired */ + + if (--pwm_counter > 0) + return; + + /* PWM cycle is complete */ + powermgmt_last_cycle_startstop_min++; + debug_file_log(); + } + break; + } + + switch (charge_state) + { + case CHARGING: + charging_step(); + break; + + case TOPOFF: + case TRICKLE: + topoff_trickle_step(); + break; + + case DISCHARGING: + default: + break; + } + + /* If 100%, ensure pwm_on never expires and briefly disables the + * charger. */ + pwm_duty = (trickle_sec < 60) ? trickle_sec*2 : 0; + pwm_counter = 60*2; + charger_enable(trickle_sec > 0); +} + +#ifdef CHARGING_DEBUG_FILE +void charging_algorithm_close(void) +{ + debug_file_close(); +} +#endif /* CHARGING_DEBUG_FILE */ + +/* Returns true if the unit is charging the batteries. */ +bool charging_state(void) +{ + return charge_state == CHARGING; +} diff --git a/firmware/target/sh/archos/recorder/powermgmt-target.h b/firmware/target/sh/archos/recorder/powermgmt-target.h new file mode 100644 index 0000000000..8fa2521f09 --- /dev/null +++ b/firmware/target/sh/archos/recorder/powermgmt-target.h @@ -0,0 +1,101 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Heikki Hannikainen, Uwe Freese + * Revisions copyright (C) 2005 by Gerald Van Baren + * + * 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 + +/* + * Define CHARGING_DEBUG_FILE to create a csv (spreadsheet) with battery + * information in it (one sample per minute/connect/disconnect). + * + * This is only for very low level debug. + */ +#undef CHARGING_DEBUG_FILE + + +/* stop when N minutes have passed with avg delta being < -0.05 V */ +#define CHARGE_END_SHORTD 6 +/* stop when N minutes have passed with avg delta being < -0.02 V */ +#define CHARGE_END_LONGD 50 + +/* Battery % to start at top-off */ +#define START_TOPOFF_CHG 85 +/* Battery % to start at trickle */ +#define START_TRICKLE_CHG 95 +/* power thread status message */ +#define POWER_MESSAGE_LEN 32 +/* minutes: maximum charging time for 1500 mAh batteries + * actual max time depends also on BATTERY_CAPACITY! */ +#define CHARGE_MAX_MIN_1500 450 +/* minutes: minimum charging time */ +#define CHARGE_MIN_MIN 10 +/* After charging, go to top off charge. How long should top off charge be? */ +#define TOPOFF_MAX_MIN 90 +/* which voltage is best? (millivolts) */ +#define TOPOFF_VOLTAGE 5650 +/* After top off charge, go to trickle harge. How long should trickle + * charge be? */ +#define TRICKLE_MAX_MIN 720 /* 12 hrs */ +/* which voltage is best? (millivolts) */ +#define TRICKLE_VOLTAGE 5450 +/* initial trickle_sec for topoff */ +#define START_TOPOFF_SEC 25 +/* initial trickle_sec for trickle */ +#define START_TRICKLE_SEC 15 + +#define PID_DEADZONE 4 /* PID proportional deadzone */ + +extern char power_message[POWER_MESSAGE_LEN]; + +extern int long_delta; /* long term delta battery voltage */ +extern int short_delta; /* short term delta battery voltage */ + +extern int powermgmt_last_cycle_startstop_min; /* how many minutes ago was + the charging started or + stopped? */ +extern int powermgmt_last_cycle_level; /* which level had the batteries + at this time? */ + +extern int pid_p; /* PID proportional term */ +extern int pid_i; /* PID integral term */ +extern int trickle_sec; /* how many seconds should the + charger be enabled per + minute for trickle + charging? */ +void charger_enable(bool on); +bool charger_enabled(void); + +/* Battery filter lengths in samples */ +#define BATT_AVE_SAMPLES 32 + +/* No init to do */ +static inline void powermgmt_init_target(void) {} +void charging_algorithm_step(void); + +#ifdef CHARGING_DEBUG_FILE +/* Need to flush and close debug file */ +void charging_algorithm_close(void); +#else +/* No poweroff operation to do */ +static inline void charging_algorithm_close(void) {} +#endif + +#endif /* POWERMGMT_TARGET_H */ diff --git a/flash/bootbox/main.c b/flash/bootbox/main.c index f53a5edda2..e4be785beb 100644 --- a/flash/bootbox/main.c +++ b/flash/bootbox/main.c @@ -75,7 +75,7 @@ static void charging_screen(void) do { -#if CONFIG_CHARGING == CHARGING_CONTROL +#ifdef ARCHOS_RECORDER if (charge_state == CHARGING) msg = "charging"; else if (charge_state == TOPOFF) @@ -84,7 +84,6 @@ static void charging_screen(void) msg = "trickle charge"; else msg = "not charging"; - #else msg = "charging"; #endif diff --git a/uisimulator/common/SOURCES b/uisimulator/common/SOURCES index 24dac31cca..881049ec59 100644 --- a/uisimulator/common/SOURCES +++ b/uisimulator/common/SOURCES @@ -7,3 +7,5 @@ lcd-playersim.c #endif sim_icons.c stubs.c +powermgmt-sim.c + diff --git a/uisimulator/common/powermgmt-sim.c b/uisimulator/common/powermgmt-sim.c new file mode 100644 index 0000000000..c06f84670d --- /dev/null +++ b/uisimulator/common/powermgmt-sim.c @@ -0,0 +1,159 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Heikki Hannikainen, Uwe Freese + * Revisions copyright (C) 2005 by Gerald Van Baren + * + * 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 +#include "kernel.h" +#include "powermgmt.h" + +#define BATT_MINMVOLT 2500 /* minimum millivolts of battery */ +#define BATT_MAXMVOLT 4500 /* maximum millivolts of battery */ +#define BATT_MAXRUNTIME (10 * 60) /* maximum runtime with full battery in + minutes */ + +extern void send_battery_level_event(void); +extern int last_sent_battery_level; +extern int battery_percent; + +static unsigned int battery_millivolts = BATT_MAXMVOLT; +/* estimated remaining time in minutes */ +static int powermgmt_est_runningtime_min = BATT_MAXRUNTIME; + +static void battery_status_update(void) +{ + static time_t last_change = 0; + static bool charging = false; + time_t now; + + time(&now); + + if (last_change < now) { + last_change = now; + + /* change the values: */ + if (charging) { + if (battery_millivolts >= BATT_MAXMVOLT) { + /* Pretend the charger was disconnected */ + charging = false; + queue_broadcast(SYS_CHARGER_DISCONNECTED, 0); + last_sent_battery_level = 100; + } + } + else { + if (battery_millivolts <= BATT_MINMVOLT) { + /* Pretend the charger was connected */ + charging = true; + queue_broadcast(SYS_CHARGER_CONNECTED, 0); + last_sent_battery_level = 0; + } + } + + if (charging) { + battery_millivolts += (BATT_MAXMVOLT - BATT_MINMVOLT) / 50; + } + else { + battery_millivolts -= (BATT_MAXMVOLT - BATT_MINMVOLT) / 100; + } + + battery_percent = 100 * (battery_millivolts - BATT_MINMVOLT) / + (BATT_MAXMVOLT - BATT_MINMVOLT); + + powermgmt_est_runningtime_min = + battery_percent * BATT_MAXRUNTIME / 100; + } + + send_battery_level_event(); +} + +void battery_read_info(int *voltage, int *level) +{ + battery_status_update(); + + if (voltage) + *voltage = battery_millivolts; + + if (level) + *level = battery_percent; +} + +unsigned int battery_voltage(void) +{ + battery_status_update(); + return battery_millivolts; +} + +int battery_level(void) +{ + battery_status_update(); + return battery_percent; +} + +int battery_time(void) +{ + battery_status_update(); + return powermgmt_est_runningtime_min; +} + +bool battery_level_safe(void) +{ + return battery_level() >= 10; +} + +void set_poweroff_timeout(int timeout) +{ + (void)timeout; +} + +void set_battery_capacity(int capacity) +{ + (void)capacity; +} + +#if BATTERY_TYPES_COUNT > 1 +void set_battery_type(int type) +{ + (void)type; +} +#endif + +#ifdef HAVE_ACCESSORY_SUPPLY +void accessory_supply_set(bool enable) +{ + (void)enable; +} +#endif + +void reset_poweroff_timer(void) +{ +} + +void shutdown_hw(void) +{ +} + +void sys_poweroff(void) +{ +} + +void cancel_shutdown(void) +{ +}