diff options
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/powermgmt.c | 150 | ||||
-rw-r--r-- | firmware/powermgmt.h | 17 |
2 files changed, 157 insertions, 10 deletions
diff --git a/firmware/powermgmt.c b/firmware/powermgmt.c index 93d3192904..0db751161c 100644 --- a/firmware/powermgmt.c +++ b/firmware/powermgmt.c | |||
@@ -41,6 +41,11 @@ int battery_level(void) | |||
41 | return 100; | 41 | return 100; |
42 | } | 42 | } |
43 | 43 | ||
44 | int battery_time(void) | ||
45 | { | ||
46 | return 500; | ||
47 | } | ||
48 | |||
44 | bool battery_level_safe(void) | 49 | bool battery_level_safe(void) |
45 | { | 50 | { |
46 | return true; | 51 | return true; |
@@ -58,18 +63,62 @@ static int poweroff_idle_timeout_value[15] = | |||
58 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 30, 45, 60 | 63 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 30, 45, 60 |
59 | }; | 64 | }; |
60 | 65 | ||
66 | static int percent_to_volt_nocharge[11] = /* voltages (centivolt) of 0%, 10%, ... 100% when charging disabled */ | ||
67 | { | ||
68 | 450, 481, 491, 497, 503, 507, 512, 514, 517, 528, 560 | ||
69 | }; | ||
70 | |||
71 | static int percent_to_volt_charge[11] = /* voltages (centivolt) of 0%, 10%, ... 100% when charging enabled */ | ||
72 | { | ||
73 | 476, 544, 551, 556, 561, 564, 566, 576, 582, 584, 585 | ||
74 | }; | ||
75 | |||
76 | int battery_lazyness[20] = /* how does the battery react when plugging in/out the charger */ | ||
77 | { | ||
78 | 0, 17, 31, 42, 52, 60, 67, 72, 77, 81, 84, 87, 89, 91, 92, 94, 95, 95, 96, 97 | ||
79 | }; | ||
80 | |||
61 | static char power_stack[DEFAULT_STACK_SIZE]; | 81 | static char power_stack[DEFAULT_STACK_SIZE]; |
62 | static char power_thread_name[] = "power"; | 82 | static char power_thread_name[] = "power"; |
63 | 83 | ||
64 | static int poweroff_timeout = 0; | 84 | static int poweroff_timeout = 0; |
65 | static long last_charge_time = 0; | 85 | static long last_charge_time = 0; |
86 | static int powermgmt_est_runningtime_min = -1; | ||
66 | 87 | ||
67 | unsigned short power_history[POWER_HISTORY_LEN]; | 88 | unsigned short power_history[POWER_HISTORY_LEN]; |
68 | #ifdef HAVE_CHARGE_CTRL | 89 | #ifdef HAVE_CHARGE_CTRL |
69 | char power_message[POWER_MESSAGE_LEN] = ""; | 90 | char power_message[POWER_MESSAGE_LEN] = ""; |
70 | char charge_restart_level = CHARGE_RESTART_HI; | 91 | char charge_restart_level = CHARGE_RESTART_HI; |
92 | |||
93 | int powermgmt_last_cycle_startstop_min = 20; /* how many minutes ago was the charging started or stopped? */ | ||
94 | int powermgmt_last_cycle_level = 0; /* which level had the batteries at this time? */ | ||
71 | #endif | 95 | #endif |
72 | 96 | ||
97 | |||
98 | int battery_time(void) | ||
99 | { | ||
100 | return powermgmt_est_runningtime_min; | ||
101 | } | ||
102 | |||
103 | /* look into the percent_to_volt_* table and get a realistic battery level percentage */ | ||
104 | int voltage_to_percent(int voltage, int* table) | ||
105 | { | ||
106 | if (voltage <= table[0]) | ||
107 | return 0; | ||
108 | else | ||
109 | if (voltage >= table[10]) | ||
110 | return 100; | ||
111 | else { | ||
112 | /* search nearest value */ | ||
113 | int i = 0; | ||
114 | while ((i < 10) && (table[i+1] < voltage)) | ||
115 | i++; | ||
116 | /* interpolate linear between the smaller and greater value */ | ||
117 | return i * 10 /* 10th */ | ||
118 | + (voltage - table[i]) * 10 / (table[i+1] - table[i]); /* 1th */ | ||
119 | } | ||
120 | } | ||
121 | |||
73 | /* Returns battery level in percent */ | 122 | /* Returns battery level in percent */ |
74 | int battery_level(void) | 123 | int battery_level(void) |
75 | { | 124 | { |
@@ -95,7 +144,33 @@ int battery_level(void) | |||
95 | if(level < BATTERY_LEVEL_EMPTY) | 144 | if(level < BATTERY_LEVEL_EMPTY) |
96 | level = BATTERY_LEVEL_EMPTY; | 145 | level = BATTERY_LEVEL_EMPTY; |
97 | 146 | ||
98 | return ((level-BATTERY_LEVEL_EMPTY) * 100) / BATTERY_RANGE; | 147 | /* level now stores the voltage in centivolts */ |
148 | /* let's calculate a percentage now with using the voltage arrays */ | ||
149 | |||
150 | #ifdef HAVE_CHARGE_CTRL | ||
151 | if (powermgmt_last_cycle_startstop_min < 20) { | ||
152 | /* the batteries are lazy, so take a value between the result of the two table lookups */ | ||
153 | if (charger_enabled) | ||
154 | level = (voltage_to_percent(level, percent_to_volt_charge) | ||
155 | * battery_lazyness[powermgmt_last_cycle_startstop_min] | ||
156 | + voltage_to_percent(level, percent_to_volt_nocharge) | ||
157 | * (100 - battery_lazyness[powermgmt_last_cycle_startstop_min])) / 100; | ||
158 | else | ||
159 | level = (voltage_to_percent(level, percent_to_volt_nocharge) | ||
160 | * battery_lazyness[powermgmt_last_cycle_startstop_min] | ||
161 | + voltage_to_percent(level, percent_to_volt_charge) | ||
162 | * (100 - battery_lazyness[powermgmt_last_cycle_startstop_min])) / 100; | ||
163 | } else { | ||
164 | if (charger_enabled) | ||
165 | level = voltage_to_percent(level, percent_to_volt_charge); | ||
166 | else | ||
167 | level = voltage_to_percent(level, percent_to_volt_nocharge); | ||
168 | } | ||
169 | #else | ||
170 | level = voltage_to_percent(level, percent_to_volt_nocharge); /* always use the nocharge table */ | ||
171 | #endif | ||
172 | |||
173 | return level; | ||
99 | } | 174 | } |
100 | 175 | ||
101 | /* Tells if the battery level is safe for disk writes */ | 176 | /* Tells if the battery level is safe for disk writes */ |
@@ -180,6 +255,8 @@ static void power_thread(void) | |||
180 | #ifdef HAVE_CHARGE_CTRL | 255 | #ifdef HAVE_CHARGE_CTRL |
181 | int delta; | 256 | int delta; |
182 | int charged_time = 0; | 257 | int charged_time = 0; |
258 | int charge_max_time_now = 0; | ||
259 | int charge_pause = 0; /* no charging pause at the beginning */ | ||
183 | #endif | 260 | #endif |
184 | 261 | ||
185 | while (1) | 262 | while (1) |
@@ -215,18 +292,43 @@ static void power_thread(void) | |||
215 | /* insert new value in the end, in centivolts 8-) */ | 292 | /* insert new value in the end, in centivolts 8-) */ |
216 | power_history[POWER_HISTORY_LEN-1] = (avg * BATTERY_SCALE_FACTOR) / 10000; | 293 | power_history[POWER_HISTORY_LEN-1] = (avg * BATTERY_SCALE_FACTOR) / 10000; |
217 | 294 | ||
295 | /* calculate estimated remaining running time */ | ||
296 | /* not charging: remaining running time */ | ||
297 | /* charging: remaining charging time */ | ||
298 | #ifdef HAVE_CHARGE_CTRL | ||
299 | if (charger_enabled) | ||
300 | /* if taking the nocharge battery level, charging lasts 30% longer than the value says */ | ||
301 | /* so consider it because there's the battery lazyness inside the the battery_level */ | ||
302 | if (powermgmt_last_cycle_startstop_min < 20) { | ||
303 | i = (100 - battery_lazyness[powermgmt_last_cycle_startstop_min]) * 30 / 100 ; /* 0..30 */ | ||
304 | powermgmt_est_runningtime_min = (100 - battery_level()) * BATTERY_CAPACITY / 100 * (100 + i) / 100 * 60 / CURRENT_CHARGING; | ||
305 | } else { | ||
306 | powermgmt_est_runningtime_min = (100 - battery_level()) * BATTERY_CAPACITY / 100 * 60 / CURRENT_CHARGING; | ||
307 | } | ||
308 | else | ||
309 | #endif | ||
310 | powermgmt_est_runningtime_min = battery_level() * BATTERY_CAPACITY / 100 * 60 / CURRENT_NORMAL; | ||
311 | |||
218 | #ifdef HAVE_CHARGE_CTRL | 312 | #ifdef HAVE_CHARGE_CTRL |
313 | |||
314 | if (charge_pause > 0) | ||
315 | charge_pause--; | ||
316 | |||
219 | if (charger_inserted()) { | 317 | if (charger_inserted()) { |
220 | if (charger_enabled) { | 318 | if (charger_enabled) { |
221 | /* charger inserted and enabled */ | 319 | /* charger inserted and enabled */ |
222 | charged_time++; | 320 | charged_time++; |
223 | if (charged_time > CHARGE_MAX_TIME) { | 321 | snprintf(power_message, POWER_MESSAGE_LEN, "Chg %dm max %dm", charged_time, charge_max_time_now); |
224 | DEBUGF("power: charged_time > CHARGE_MAX_TIME, enough!\n"); | 322 | |
323 | if (charged_time > charge_max_time_now) { | ||
324 | DEBUGF("power: charged_time > charge_max_time_now, enough!\n"); | ||
225 | /* have charged too long and deltaV detection did not work! */ | 325 | /* have charged too long and deltaV detection did not work! */ |
326 | powermgmt_last_cycle_level = battery_level(); | ||
327 | powermgmt_last_cycle_startstop_min = 0; | ||
226 | charger_enable(false); | 328 | charger_enable(false); |
227 | snprintf(power_message, POWER_MESSAGE_LEN, "Chg tmout %d min", CHARGE_MAX_TIME); | 329 | snprintf(power_message, POWER_MESSAGE_LEN, "Chg tmout %d min", charge_max_time_now); |
228 | /* Perhaps we should disable charging for several | 330 | /* disable charging for several hours from this point, just to be sure */ |
229 | hours from this point, just to be sure. */ | 331 | charge_pause = CHARGE_PAUSE_LEN; |
230 | } else { | 332 | } else { |
231 | if (charged_time > CHARGE_MIN_TIME) { | 333 | if (charged_time > CHARGE_MIN_TIME) { |
232 | /* have charged continuously over the minimum charging time, | 334 | /* have charged continuously over the minimum charging time, |
@@ -244,8 +346,12 @@ static void power_thread(void) | |||
244 | 346 | ||
245 | if (delta < -100) { /* delta < -10 mV */ | 347 | if (delta < -100) { /* delta < -10 mV */ |
246 | DEBUGF("power: short-term negative delta, enough!\n"); | 348 | DEBUGF("power: short-term negative delta, enough!\n"); |
349 | powermgmt_last_cycle_level = battery_level(); | ||
350 | powermgmt_last_cycle_startstop_min = 0; | ||
247 | charger_enable(false); | 351 | charger_enable(false); |
248 | snprintf(power_message, POWER_MESSAGE_LEN, "end negd %d %dmin", delta, charged_time); | 352 | snprintf(power_message, POWER_MESSAGE_LEN, "end negd %d %dmin", delta, charged_time); |
353 | /* disable charging for several hours from this point, just to be sure */ | ||
354 | charge_pause = CHARGE_PAUSE_LEN; | ||
249 | } else { | 355 | } else { |
250 | /* if we didn't disable the charger in the previous test, check for low positive delta */ | 356 | /* if we didn't disable the charger in the previous test, check for low positive delta */ |
251 | delta = ( power_history[POWER_HISTORY_LEN-1] * 100 | 357 | delta = ( power_history[POWER_HISTORY_LEN-1] * 100 |
@@ -256,8 +362,12 @@ static void power_thread(void) | |||
256 | 362 | ||
257 | if (delta < 1) { /* delta < 0.1 mV */ | 363 | if (delta < 1) { /* delta < 0.1 mV */ |
258 | DEBUGF("power: long-term small positive delta, enough!\n"); | 364 | DEBUGF("power: long-term small positive delta, enough!\n"); |
365 | powermgmt_last_cycle_level = battery_level(); | ||
366 | powermgmt_last_cycle_startstop_min = 0; | ||
259 | charger_enable(false); | 367 | charger_enable(false); |
260 | snprintf(power_message, POWER_MESSAGE_LEN, "end lowd %d %dmin", delta, charged_time); | 368 | snprintf(power_message, POWER_MESSAGE_LEN, "end lowd %d %dmin", delta, charged_time); |
369 | /* disable charging for several hours from this point, just to be sure */ | ||
370 | charge_pause = CHARGE_PAUSE_LEN; | ||
261 | } | 371 | } |
262 | } | 372 | } |
263 | } | 373 | } |
@@ -266,15 +376,33 @@ static void power_thread(void) | |||
266 | /* charged inserted but not enabled */ | 376 | /* charged inserted but not enabled */ |
267 | /* if battery is not full, enable charging */ | 377 | /* if battery is not full, enable charging */ |
268 | if (battery_level() < charge_restart_level) { | 378 | if (battery_level() < charge_restart_level) { |
379 | if (charge_pause) { | ||
380 | DEBUGF("power: batt level < restart level, but charge pause, not enabling\n"); | ||
381 | snprintf(power_message, POWER_MESSAGE_LEN, "chg pause %d min", charge_pause); | ||
382 | } else { | ||
383 | /* calculate max charge time depending on current battery level */ | ||
384 | /* take 20% more because battery level is not linear */ | ||
385 | charge_max_time_now = CHARGE_MAX_TIME * (100 + 30 - battery_level()) / 100; | ||
386 | if (charge_max_time_now > CHARGE_MAX_TIME) { | ||
387 | charge_max_time_now = CHARGE_MAX_TIME; | ||
388 | } | ||
389 | snprintf(power_message, POWER_MESSAGE_LEN, "ChgAt %d%% max %dm", battery_level(), charge_max_time_now); | ||
390 | |||
391 | /* enable the charger after the max time calc is done, because battery_level */ | ||
392 | /* depends on if the charger is on */ | ||
269 | DEBUGF("power: charger inserted and battery not full, enabling\n"); | 393 | DEBUGF("power: charger inserted and battery not full, enabling\n"); |
270 | charger_enable(true); | 394 | powermgmt_last_cycle_level = battery_level(); |
395 | powermgmt_last_cycle_startstop_min = 0; | ||
271 | charged_time = 0; | 396 | charged_time = 0; |
397 | |||
398 | charger_enable(true); | ||
399 | |||
272 | /* clear the power history so that we don't use values before | 400 | /* clear the power history so that we don't use values before |
273 | * discharge for the long-term delta | 401 | * discharge for the long-term delta |
274 | */ | 402 | */ |
275 | for (i = 0; i < POWER_HISTORY_LEN-1; i++) | 403 | for (i = 0; i < POWER_HISTORY_LEN-1; i++) |
276 | power_history[i] = power_history[POWER_HISTORY_LEN-1]; | 404 | power_history[i] = power_history[POWER_HISTORY_LEN-1]; |
277 | snprintf(power_message, POWER_MESSAGE_LEN, "Chg started at %d%%", battery_level()); | 405 | } |
278 | } | 406 | } |
279 | } | 407 | } |
280 | } else { | 408 | } else { |
@@ -282,11 +410,15 @@ static void power_thread(void) | |||
282 | if (charger_enabled) { | 410 | if (charger_enabled) { |
283 | /* charger not inserted but was enabled */ | 411 | /* charger not inserted but was enabled */ |
284 | DEBUGF("power: charger disconnected, disabling\n"); | 412 | DEBUGF("power: charger disconnected, disabling\n"); |
413 | powermgmt_last_cycle_level = battery_level(); | ||
414 | powermgmt_last_cycle_startstop_min = 0; | ||
285 | charger_enable(false); | 415 | charger_enable(false); |
286 | snprintf(power_message, POWER_MESSAGE_LEN, "Charger disc"); | 416 | snprintf(power_message, POWER_MESSAGE_LEN, "Charger disc"); |
287 | } | 417 | } |
288 | /* charger not inserted and disabled, so we're discharging */ | 418 | /* charger not inserted and disabled, so we're discharging */ |
289 | } | 419 | } |
420 | powermgmt_last_cycle_startstop_min++; | ||
421 | |||
290 | #endif /* HAVE_CHARGE_CTRL*/ | 422 | #endif /* HAVE_CHARGE_CTRL*/ |
291 | 423 | ||
292 | /* sleep for roughly a minute */ | 424 | /* sleep for roughly a minute */ |
@@ -303,6 +435,8 @@ void power_init(void) | |||
303 | /* initialize the history with a single sample to prevent level | 435 | /* initialize the history with a single sample to prevent level |
304 | flickering during the first minute of execution */ | 436 | flickering during the first minute of execution */ |
305 | power_history[POWER_HISTORY_LEN-1] = (adc_read(ADC_UNREG_POWER) * BATTERY_SCALE_FACTOR) / 10000; | 437 | power_history[POWER_HISTORY_LEN-1] = (adc_read(ADC_UNREG_POWER) * BATTERY_SCALE_FACTOR) / 10000; |
438 | /* calculate the remaining time to that the info screen displays something useful */ | ||
439 | powermgmt_est_runningtime_min = battery_level() * BATTERY_CAPACITY / 100 * 60 / CURRENT_NORMAL; | ||
306 | 440 | ||
307 | #ifdef HAVE_CHARGE_CTRL | 441 | #ifdef HAVE_CHARGE_CTRL |
308 | snprintf(power_message, POWER_MESSAGE_LEN, "Powermgmt started"); | 442 | snprintf(power_message, POWER_MESSAGE_LEN, "Powermgmt started"); |
diff --git a/firmware/powermgmt.h b/firmware/powermgmt.h index 329bf6005f..f0c34a4fb1 100644 --- a/firmware/powermgmt.h +++ b/firmware/powermgmt.h | |||
@@ -24,7 +24,7 @@ | |||
24 | #define BATTERY_LEVEL_SHUTDOWN 450 /* 4.5V */ | 24 | #define BATTERY_LEVEL_SHUTDOWN 450 /* 4.5V */ |
25 | #define BATTERY_LEVEL_EMPTY 465 /* 4.65V */ | 25 | #define BATTERY_LEVEL_EMPTY 465 /* 4.65V */ |
26 | #define BATTERY_LEVEL_DANGEROUS 475 /* 4.75V */ | 26 | #define BATTERY_LEVEL_DANGEROUS 475 /* 4.75V */ |
27 | #define BATTERY_LEVEL_FULL 520 /* 5.2V */ | 27 | #define BATTERY_LEVEL_FULL 585 /* 5.85V */ |
28 | 28 | ||
29 | #define BATTERY_RANGE (BATTERY_LEVEL_FULL - BATTERY_LEVEL_EMPTY) | 29 | #define BATTERY_RANGE (BATTERY_LEVEL_FULL - BATTERY_LEVEL_EMPTY) |
30 | 30 | ||
@@ -39,15 +39,27 @@ | |||
39 | 39 | ||
40 | #ifdef HAVE_CHARGE_CTRL | 40 | #ifdef HAVE_CHARGE_CTRL |
41 | #define POWER_MESSAGE_LEN 32 /* power thread status message */ | 41 | #define POWER_MESSAGE_LEN 32 /* power thread status message */ |
42 | #define CHARGE_MAX_TIME 6*60 /* minutes: maximum charging time */ | 42 | #define CHARGE_MAX_TIME 8*60 /* minutes: maximum charging time */ |
43 | #define CHARGE_MIN_TIME 10 /* minutes: minimum charging time */ | 43 | #define CHARGE_MIN_TIME 10 /* minutes: minimum charging time */ |
44 | #define CHARGE_RESTART_HI 95 /* %: when to restart charging in 'charge' mode */ | 44 | #define CHARGE_RESTART_HI 95 /* %: when to restart charging in 'charge' mode */ |
45 | #define CHARGE_RESTART_LO 10 /* %: when to restart charging in 'discharge' mode */ | 45 | #define CHARGE_RESTART_LO 10 /* %: when to restart charging in 'discharge' mode */ |
46 | #define CHARGE_PAUSE_LEN 60 /* how many minutes to pause between charging cycles */ | ||
46 | 47 | ||
47 | extern char power_message[POWER_MESSAGE_LEN]; | 48 | extern char power_message[POWER_MESSAGE_LEN]; |
48 | extern char charge_restart_level; | 49 | extern char charge_restart_level; |
50 | |||
51 | extern int powermgmt_last_cycle_startstop_min; /* how many minutes ago was the charging started or stopped? */ | ||
52 | extern int powermgmt_last_cycle_level; /* which level had the batteries at this time? */ | ||
53 | |||
54 | extern int battery_lazyness[20]; /* how does the battery react when plugging in/out the charger */ | ||
55 | |||
49 | #endif /* HAVE_CHARGE_CTRL */ | 56 | #endif /* HAVE_CHARGE_CTRL */ |
50 | 57 | ||
58 | #define BATTERY_CAPACITY 1800 /* battery capacity in mAh for runtime estimation */ | ||
59 | #define CURRENT_NORMAL 145 /* usual current in mA when using the AJB including some disk/backlight/... activity */ | ||
60 | #define CURRENT_BACKLIGHT 30 /* additional current when backlight is always on */ | ||
61 | #define CURRENT_CHARGING 300 /* charging current */ | ||
62 | |||
51 | extern unsigned short power_history[POWER_HISTORY_LEN]; | 63 | extern unsigned short power_history[POWER_HISTORY_LEN]; |
52 | 64 | ||
53 | /* Start up power management thread */ | 65 | /* Start up power management thread */ |
@@ -57,6 +69,7 @@ void power_init(void); | |||
57 | 69 | ||
58 | /* Returns battery level in percent */ | 70 | /* Returns battery level in percent */ |
59 | int battery_level(void); | 71 | int battery_level(void); |
72 | int battery_time(void); /* minutes */ | ||
60 | 73 | ||
61 | /* Tells if the battery level is safe for disk writes */ | 74 | /* Tells if the battery level is safe for disk writes */ |
62 | bool battery_level_safe(void); | 75 | bool battery_level_safe(void); |