diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2008-05-19 03:59:52 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2008-05-19 03:59:52 +0000 |
commit | cb1b388a576e5a40765dd76f627f5604dadd09b2 (patch) | |
tree | 89104a9426fe08da6428d1c9a92a0611fb818434 | |
parent | d2f47da4e2ef4cbc4c4617b4fb221d391f062935 (diff) |
Get a good start on cleaning up powermgmt.c
1) Consolidate charging types' code as much as possible at the moment.
2) Remove the iRiver code for the moment. It so happens that it isn't being
compiled anyway so it's just noise. Grab it out of the revision history
when needed again.
3) A small fix to CHARGING_CONTROL where changing of #defines somewhere in
earlier revisions actually ended up having it use the wrong battery filter
constant. A few state variables that were locals but stay in scope for
the life of the power thread get static storage to help separate algorithms.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@17580 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r-- | firmware/powermgmt.c | 856 |
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 |