summaryrefslogtreecommitdiff
path: root/firmware/powermgmt.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/powermgmt.c')
-rw-r--r--firmware/powermgmt.c150
1 files changed, 142 insertions, 8 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
44int battery_time(void)
45{
46 return 500;
47}
48
44bool battery_level_safe(void) 49bool 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
66static 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
71static 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
76int 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
61static char power_stack[DEFAULT_STACK_SIZE]; 81static char power_stack[DEFAULT_STACK_SIZE];
62static char power_thread_name[] = "power"; 82static char power_thread_name[] = "power";
63 83
64static int poweroff_timeout = 0; 84static int poweroff_timeout = 0;
65static long last_charge_time = 0; 85static long last_charge_time = 0;
86static int powermgmt_est_runningtime_min = -1;
66 87
67unsigned short power_history[POWER_HISTORY_LEN]; 88unsigned short power_history[POWER_HISTORY_LEN];
68#ifdef HAVE_CHARGE_CTRL 89#ifdef HAVE_CHARGE_CTRL
69char power_message[POWER_MESSAGE_LEN] = ""; 90char power_message[POWER_MESSAGE_LEN] = "";
70char charge_restart_level = CHARGE_RESTART_HI; 91char charge_restart_level = CHARGE_RESTART_HI;
92
93int powermgmt_last_cycle_startstop_min = 20; /* how many minutes ago was the charging started or stopped? */
94int powermgmt_last_cycle_level = 0; /* which level had the batteries at this time? */
71#endif 95#endif
72 96
97
98int 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 */
104int 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 */
74int battery_level(void) 123int 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");