summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--firmware/powermgmt.c856
1 files changed, 402 insertions, 454 deletions
diff --git a/firmware/powermgmt.c b/firmware/powermgmt.c
index 3d08c11ff5..026381c2ff 100644
--- a/firmware/powermgmt.c
+++ b/firmware/powermgmt.c
@@ -69,8 +69,8 @@
#define DEBUG_MESSAGE_LEN 133
static char debug_message[DEBUG_MESSAGE_LEN];
#define DEBUG_STACK ((0x1000)/sizeof(long))
-static int fd; /* write debug information to this file */
-static int wrcount;
+static int fd = -1; /* write debug information to this file */
+static int wrcount = 0;
#else
#define DEBUG_STACK 0
#endif
@@ -134,7 +134,8 @@ static void battery_status_update(void)
else
battery_millivolts -= (BATT_MAXMVOLT - BATT_MINMVOLT) / 100;
- battery_percent = 100 * (battery_millivolts - BATT_MINMVOLT) / (BATT_MAXMVOLT - BATT_MINMVOLT);
+ battery_percent = 100 * (battery_millivolts - BATT_MINMVOLT) /
+ (BATT_MAXMVOLT - BATT_MINMVOLT);
powermgmt_est_runningtime_min = battery_percent * BATT_MAXRUNTIME / 100;
}
send_battery_level_event();
@@ -204,41 +205,16 @@ void accessory_supply_set(bool enable)
#else /* not SIMULATOR ******************************************************/
-#if CONFIG_CHARGING == CHARGING_CONTROL
-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 */
-#endif /* CONFIG_CHARGING == CHARGING_CONTROL */
+static void power_thread_sleep(int ticks);
/*
* Average battery voltage and charger voltage, filtered via a digital
- * exponential filter.
+ * 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 */
-#ifdef HAVE_CHARGE_CTRL
-#define BATT_AVE_SAMPLES 32 /* filter constant / @ 2Hz sample rate */
-#else
-#define BATT_AVE_SAMPLES 128 /* slw filter constant for all others */
-#endif
-
/* 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 */
@@ -376,36 +352,7 @@ static int voltage_to_battery_level(int battery_millivolts)
{
int level;
-#if defined(CONFIG_CHARGER) \
- && (defined(IRIVER_H100_SERIES) || defined(IRIVER_H300_SERIES))
- /* Checking for iriver is a temporary kludge.
- * This code needs rework/unification */
- if (charger_input_state == NO_CHARGER) {
- /* discharging. calculate new battery level and average with last */
- level = voltage_to_percent(battery_millivolts,
- percent_to_volt_discharge[battery_type]);
- if (level != (battery_percent - 1))
- level = (level + battery_percent + 1) / 2;
- }
- else if (charger_input_state == CHARGER_UNPLUGGED) {
- /* just unplugged. adjust filtered values */
- battery_millivolts -= percent_to_volt_charge[battery_percent/10] -
- percent_to_volt_discharge[0][battery_percent/10];
- avgbat = battery_millivolts * 1000 * BATT_AVE_SAMPLES;
- level = battery_percent;
- }
- else if (charger_input_state == CHARGER_PLUGGED) {
- /* just plugged in. adjust battery values */
- battery_millivolts += percent_to_volt_charge[battery_percent/10] -
- percent_to_volt_discharge[0][battery_percent/10];
- avgbat = battery_millivolts * 1000 * BATT_AVE_SAMPLES;
- level = MIN(12 * battery_percent / 10, 99);
- }
- else { /* charging. calculate new battery level */
- level = voltage_to_percent(battery_millivolts,
- percent_to_volt_charge);
- }
-#elif CONFIG_CHARGING >= CHARGING_MONITOR
+#if CONFIG_CHARGING >= CHARGING_MONITOR
if (charge_state == DISCHARGING) {
level = voltage_to_percent(battery_millivolts,
percent_to_volt_discharge[battery_type]);
@@ -422,7 +369,7 @@ static int voltage_to_battery_level(int battery_millivolts)
/* always use the discharge table */
level = voltage_to_percent(battery_millivolts,
percent_to_volt_discharge[battery_type]);
-#endif
+#endif /* CONFIG_CHARGING ... */
return level;
}
@@ -440,47 +387,7 @@ static void battery_status_update(void)
/ 100 / (CURRENT_MAX_CHG - runcurrent());
}
else
-#elif CONFIG_CHARGING \
- && (defined(IRIVER_H100_SERIES) || defined(IRIVER_H300_SERIES))
- /* Checking for iriver is a temporary kludge.
- * This code needs rework/unification */
- if (charger_inserted()) {
-#ifdef IRIVER_H300_SERIES
- /* H300_SERIES use CURRENT_MAX_CHG for basic charge time (80%)
- * plus 110 min top off charge time */
- powermgmt_est_runningtime_min = ((100-level) * battery_capacity * 80
- /100 / CURRENT_MAX_CHG) + 110;
-#else
- /* H100_SERIES scaled for 160 min basic charge time (80%) on
- * 1600 mAh battery plus 110 min top off charge time */
- powermgmt_est_runningtime_min = ((100 - level) * battery_capacity
- / 993) + 110;
#endif
- level = (level * 80) / 100;
- if (level > 72) { /* > 91% */
- int i = POWER_HISTORY_LEN;
- int d = 1;
-#ifdef HAVE_CHARGE_STATE
- if (charge_state == DISCHARGING)
- d = -2;
-#endif
- while ((i > 2) && (d > 0)) /* search zero or neg. delta */
- d = power_history[0] - power_history[--i];
- if ((((d == 0) && (i > 6)) || (d == -1)) && (i < 118)) {
- /* top off charging */
- level = MIN(80 + (i*19 / 113), 99); /* show 81% .. 99% */
- powermgmt_est_runningtime_min = MAX(116 - i, 0);
- }
- else if ((d < 0) || (i > 117)) {
- /* charging finished */
- level = 100;
- powermgmt_est_runningtime_min = battery_capacity * 60
- / runcurrent();
- }
- }
- }
- else
-#endif /* BATT_LIPOL1300 */
{
if ((battery_millivolts + 20) > percent_to_volt_discharge[0][0])
powermgmt_est_runningtime_min = (level + battery_percent) * 60 *
@@ -639,6 +546,395 @@ static void power_thread_rtc_process(void)
#endif
/*
+ * This power thread maintains a history of battery voltage
+ * and implements a charging algorithm.
+ */
+#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 (ata_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) && ata_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
+}
+#else
+#define BATT_AVE_SAMPLES 128 /* slw filter constant for all others */
+
+static inline void charging_algorithm_small_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 */
+}
+
+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 */
+
+/*
* 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
@@ -663,10 +959,8 @@ static void power_thread_sleep(int ticks)
if(charger_inserted()
#ifdef HAVE_USB_POWER /* USB powered or USB inserted both provide power */
|| usb_powered()
-#if CONFIG_CHARGING
|| (usb_inserted() && usb_charging_enabled())
#endif
-#endif
) {
switch(charger_input_state) {
case NO_CHARGER:
@@ -696,24 +990,7 @@ static void power_thread_sleep(int ticks)
return;
}
}
-#endif
-#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 */
+#endif /* CONFIG_CHARGING */
small_ticks = MIN(HZ/2, ticks);
sleep(small_ticks);
@@ -766,48 +1043,12 @@ static void power_thread_sleep(int ticks)
avgbat += battery_millivolts - (avgbat / BATT_AVE_SAMPLES);
}
-#if CONFIG_CHARGING == CHARGING_CONTROL
- if (ata_disk_is_active()) {
- /* flag hdd use for charging calculation */
- disk_activity_last_cycle = true;
- }
-#endif
-#if defined(DEBUG_FILE) && (CONFIG_CHARGING == CHARGING_CONTROL)
- /*
- * If we have a lot of pending writes or if the disk is spining,
- * fsync the debug log file.
- */
- if((wrcount > 10) || ((wrcount > 0) && ata_disk_is_active())) {
- fsync(fd);
- wrcount = 0;
- }
-#endif
+ charging_algorithm_small_step();
}
}
-
-/*
- * This power thread maintains a history of battery voltage
- * and implements a charging algorithm.
- * For a complete description of the charging algorithm read
- * docs/CHARGING_ALGORITHM.
- */
-
static void power_thread(void)
{
-#if CONFIG_CHARGING == CHARGING_CONTROL
- int i;
- unsigned int target_voltage = TRICKLE_VOLTAGE; /* desired topoff/trickle
- * voltage level */
- int charge_max_time_idle = 0; /* max. charging duration, calculated at
- * beginning of charging */
- int charge_max_time_now = 0; /* max. charging duration including
- * hdd activity */
- int minutes_disk_activity = 0; /* count minutes of hdd use during
- * charging */
- int last_disk_activity = CHARGE_END_LONGD + 1; /* last hdd use x mins ago */
-#endif
-
/* Delay reading the first battery level */
#ifdef MROBE_100
while(battery_adc_voltage()>4200) /* gives false readings initially */
@@ -834,10 +1075,6 @@ static void power_thread(void)
if(charger_inserted()) {
battery_percent = voltage_to_percent(battery_millivolts,
percent_to_volt_charge);
-#if defined(IRIVER_H100_SERIES) || defined(IRIVER_H300_SERIES)
- /* Checking for iriver is a temporary kludge. */
- charger_input_state = CHARGER;
-#endif
} else
#endif
{ battery_percent = voltage_to_percent(battery_millivolts,
@@ -845,11 +1082,6 @@ static void power_thread(void)
battery_percent += (battery_percent < 100);
}
-#if defined(DEBUG_FILE) && (CONFIG_CHARGING == CHARGING_CONTROL)
- fd = -1;
- wrcount = 0;
-#endif
-
while (1)
{
/* rotate the power history */
@@ -859,288 +1091,9 @@ static void power_thread(void)
/* insert new value at the start, in millivolts 8-) */
power_history[0] = battery_millivolts;
-#if CONFIG_CHARGING == CHARGING_CONTROL
- 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;
+ charging_algorithm_big_step();
- 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");
- }
-
-#endif /* CONFIG_CHARGING == CHARGING_CONTROL */
-
- /* sleep for a minute */
-
-#if CONFIG_CHARGING == CHARGING_CONTROL
- 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));
-#else
- power_thread_sleep(HZ * 60);
-#endif
-
-#if defined(DEBUG_FILE) && (CONFIG_CHARGING == CHARGING_CONTROL)
- 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
handle_auto_poweroff();
-
-#if CONFIG_CHARGING == CHARGING_CONTROL
- powermgmt_last_cycle_startstop_min++;
-#endif
}
}
@@ -1192,12 +1145,7 @@ void cancel_shutdown(void)
void shutdown_hw(void)
{
#ifndef SIMULATOR
-#if defined(DEBUG_FILE) && (CONFIG_CHARGING == CHARGING_CONTROL)
- if(fd >= 0) {
- close(fd);
- fd = -1;
- }
-#endif
+ charging_algorithm_close();
audio_stop();
if (battery_level_safe()) { /* do not save on critical battery */
#ifdef HAVE_LCD_BITMAP