Simplify powermgmt thread loops so it calls functions turn (no more power_thread_sleep). Do other target-friendly simplifications, generic battery switch handling and split sim-specific code. Whoever can, please verify charging on the Archos Recorder (due to change in the charger duty cycle code).
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19579 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index beee39a..abb6018 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -1603,10 +1603,10 @@
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 @@
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 @@
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 @@
"<unknown>",
};
+ 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 @@
}
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 @@
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 @@
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 4c15b1d..b22824d 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 @@
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 @@
}
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 992c740..230e9ae 100644
--- a/apps/screens.c
+++ b/apps/screens.c
@@ -209,8 +209,7 @@
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 @@
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 e169606..ac64f20 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 3cae14c..b290a24 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 9c327f1..7f91e35 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 d81fa07..b4a687b 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 fa0046a..05d75fc 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 f4ced65..a6e1028 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 5ec6b77..26cc9c5 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 03b8460..8b1f908 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 4a2207d..89b14f9 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 7d6515f..6017d0a 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 fa6ddd3..d04e562 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 634433f..8233728 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 3443b83..5fb7806 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 e5fa336..75aa2cf 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 d8c14dd..1854f59 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 7478879..edf43f8 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 @@
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 tuner_power(bool status);
#endif
-#endif
+#endif /* _POWER_H_ */
diff --git a/firmware/export/powermgmt.h b/firmware/export/powermgmt.h
index c333795..5be3a39 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 @@
/* 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 @@
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 @@
/* 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 0fed847..5aa85c8 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 @@
/* 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 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 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 @@
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_sleep_timer(int seconds)
{
- if(seconds) {
+ if (seconds) {
sleeptimer_active = true;
sleeptimer_endtick = current_tick + seconds * HZ;
}
@@ -336,7 +208,7 @@
int get_sleep_timer(void)
{
- if(sleeptimer_active)
+ if (sleeptimer_active)
return (sleeptimer_endtick - current_tick) / HZ;
else
return 0;
@@ -345,45 +217,46 @@
/* 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 @@
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 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 @@
#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 @@
current += CURRENT_REMOTE;
#endif
- return(current);
+ return current;
}
@@ -550,424 +414,76 @@
#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 @@
/* 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);
+ {
+ sleep(HZ/100);
+ }
+
+#if CONFIG_CHARGING
+ /* Initialize power input status before calling other routines. */
+ power_thread_inputs = power_input_status();
+#endif
/* 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 @@
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 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 @@
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 fa9c7b0..b4a6c61 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 @@
/* 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 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 @@
/* 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 @@
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 @@
if (millivolts != INT_MIN)
{
- set_filtered_battery_voltage(millivolts);
+ reset_battery_filter(millivolts);
return true;
}
@@ -357,13 +355,13 @@
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 @@
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 @@
#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 @@
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 @@
}
/* 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_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 @@
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_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 @@
{
/* 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 @@
break;
}
+ autorecharge_counter = 0;
+
charging_set_thread_priority(true);
if (stat_battery_reading(ADC_BATTERY) < BATT_VTRICKLE_CHARGE)
@@ -858,10 +817,12 @@
}
/* 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_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 8ad4af8..1b55a4e 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 deca325..1a1f6af 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 c3eb96b..46b99fb 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 03a5794..4e7172e 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 6522e65..49f7e2e 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 @@
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 abf5790..fe5460d 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 d90c029..8d7ea5f 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 @@
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 6de5cc8..7b18420 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 <sprintf.h>
+#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 @@
{
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 0000000..8fa2521
--- /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 f53a5ed..e4be785 100644
--- a/flash/bootbox/main.c
+++ b/flash/bootbox/main.c
@@ -75,7 +75,7 @@
do
{
-#if CONFIG_CHARGING == CHARGING_CONTROL
+#ifdef ARCHOS_RECORDER
if (charge_state == CHARGING)
msg = "charging";
else if (charge_state == TOPOFF)
@@ -84,7 +84,6 @@
msg = "trickle charge";
else
msg = "not charging";
-
#else
msg = "charging";
#endif
diff --git a/uisimulator/common/SOURCES b/uisimulator/common/SOURCES
index 24dac31..881049e 100644
--- a/uisimulator/common/SOURCES
+++ b/uisimulator/common/SOURCES
@@ -7,3 +7,5 @@
#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 0000000..c06f846
--- /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 <time.h>
+#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)
+{
+}