diff options
Diffstat (limited to 'firmware/powermgmt.c')
-rw-r--r-- | firmware/powermgmt.c | 819 |
1 files changed, 411 insertions, 408 deletions
diff --git a/firmware/powermgmt.c b/firmware/powermgmt.c index 2a15b9dfb4..0db2f03a7b 100644 --- a/firmware/powermgmt.c +++ b/firmware/powermgmt.c | |||
@@ -8,6 +8,7 @@ | |||
8 | * $Id$ | 8 | * $Id$ |
9 | * | 9 | * |
10 | * Copyright (C) 2002 by Heikki Hannikainen, Uwe Freese | 10 | * Copyright (C) 2002 by Heikki Hannikainen, Uwe Freese |
11 | * Revisions copyright (C) 2005 by Gerald Van Baren | ||
11 | * | 12 | * |
12 | * All files in this archive are subject to the GNU General Public License. | 13 | * All files in this archive are subject to the GNU General Public License. |
13 | * See the file COPYING in the source tree root for full license agreement. | 14 | * See the file COPYING in the source tree root for full license agreement. |
@@ -41,6 +42,22 @@ | |||
41 | #include "fmradio.h" | 42 | #include "fmradio.h" |
42 | #endif | 43 | #endif |
43 | 44 | ||
45 | /* | ||
46 | * Define DEBUG_FILE to create a csv (spreadsheet) with battery information | ||
47 | * in it (one sample per minute). This is only for very low level debug. | ||
48 | */ | ||
49 | #undef DEBUG_FILE | ||
50 | #if defined(DEBUG_FILE) && defined(HAVE_CHARGE_CTRL) | ||
51 | #include "file.h" | ||
52 | #define DEBUG_FILE_NAME "/powermgmt.csv" | ||
53 | #define DEBUG_MESSAGE_LEN 133 | ||
54 | static char debug_message[DEBUG_MESSAGE_LEN]; | ||
55 | #define DEBUG_STACK ((0x1000)/sizeof(long)) | ||
56 | static int fd; /* write debug information to this file */ | ||
57 | #else | ||
58 | #define DEBUG_STACK 0 | ||
59 | #endif | ||
60 | |||
44 | long last_event_tick; | 61 | long last_event_tick; |
45 | 62 | ||
46 | void reset_poweroff_timer(void) | 63 | void reset_poweroff_timer(void) |
@@ -48,7 +65,7 @@ void reset_poweroff_timer(void) | |||
48 | last_event_tick = current_tick; | 65 | last_event_tick = current_tick; |
49 | } | 66 | } |
50 | 67 | ||
51 | #ifdef SIMULATOR | 68 | #ifdef SIMULATOR /***********************************************************/ |
52 | 69 | ||
53 | int battery_level(void) | 70 | int battery_level(void) |
54 | { | 71 | { |
@@ -80,19 +97,14 @@ void set_car_adapter_mode(bool setting) | |||
80 | (void)setting; | 97 | (void)setting; |
81 | } | 98 | } |
82 | 99 | ||
83 | #else /* not SIMULATOR */ | 100 | #else /* not SIMULATOR ******************************************************/ |
84 | |||
85 | int battery_capacity = BATTERY_CAPACITY_MIN; /* only a default value */ | ||
86 | int battery_level_cached = -1; /* battery level of this minute, updated once | ||
87 | per minute */ | ||
88 | static bool car_adapter_mode_enabled = false; | ||
89 | 101 | ||
90 | static const int poweroff_idle_timeout_value[15] = | 102 | static const int poweroff_idle_timeout_value[15] = |
91 | { | 103 | { |
92 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 30, 45, 60 | 104 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 30, 45, 60 |
93 | }; | 105 | }; |
94 | 106 | ||
95 | static const short percent_to_volt_decharge[BATTERY_TYPES_COUNT][11] = | 107 | static const short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] = |
96 | /* voltages (centivolt) of 0%, 10%, ... 100% when charging disabled */ | 108 | /* voltages (centivolt) of 0%, 10%, ... 100% when charging disabled */ |
97 | { | 109 | { |
98 | #if CONFIG_BATTERY == BATT_LIION2200 | 110 | #if CONFIG_BATTERY == BATT_LIION2200 |
@@ -110,50 +122,44 @@ static const short percent_to_volt_decharge[BATTERY_TYPES_COUNT][11] = | |||
110 | #endif | 122 | #endif |
111 | }; | 123 | }; |
112 | 124 | ||
113 | void set_battery_capacity(int capacity) | ||
114 | { | ||
115 | battery_capacity = capacity; | ||
116 | if (battery_capacity > BATTERY_CAPACITY_MAX) | ||
117 | battery_capacity = BATTERY_CAPACITY_MAX; | ||
118 | if (battery_capacity < BATTERY_CAPACITY_MIN) | ||
119 | battery_capacity = BATTERY_CAPACITY_MIN; | ||
120 | } | ||
121 | |||
122 | #if BATTERY_TYPES_COUNT > 1 | ||
123 | static int battery_type = 0; | 125 | static int battery_type = 0; |
124 | 126 | ||
125 | void set_battery_type(int type) | 127 | #if defined(HAVE_CHARGE_CTRL) || CONFIG_BATTERY == BATT_LIION2200 |
126 | { | 128 | charge_state_type charge_state; /* charging mode */ |
127 | if (type != battery_type) { | 129 | int charge_timer; /* charge timer (minutes, dec to zero) */ |
128 | battery_type = type; | ||
129 | battery_level_cached = -1; /* reset on type change */ | ||
130 | } | ||
131 | } | ||
132 | #endif | 130 | #endif |
133 | 131 | ||
134 | #if defined(HAVE_CHARGE_CTRL) || CONFIG_BATTERY == BATT_LIION2200 | 132 | #ifdef HAVE_CHARGING |
135 | int charge_state = 0; /* at the beginning, the | 133 | /* Flag that the charger has been plugged in */ |
136 | charger does nothing */ | 134 | static bool charger_was_inserted = false; /* for power off logic */ |
135 | static bool charger_power_is_on; /* for car adapter mode */ | ||
137 | #endif | 136 | #endif |
138 | 137 | ||
138 | /* Power history: power_history[0] is the newest sample */ | ||
139 | unsigned short power_history[POWER_HISTORY_LEN]; | ||
140 | |||
139 | #ifdef HAVE_CHARGE_CTRL | 141 | #ifdef HAVE_CHARGE_CTRL |
140 | 142 | ||
143 | int long_delta; /* long term delta battery voltage */ | ||
144 | int short_delta; /* short term delta battery voltage */ | ||
145 | |||
141 | char power_message[POWER_MESSAGE_LEN] = ""; /* message that's shown in | 146 | char power_message[POWER_MESSAGE_LEN] = ""; /* message that's shown in |
142 | debug menu */ | 147 | debug menu */ |
143 | char charge_restart_level = CHARGE_RESTART_HI; /* level at which charging | 148 | static char charge_restart_level = CHARGE_RESTART_HI; |
149 | /* percentage at which charging | ||
144 | starts */ | 150 | starts */ |
145 | int powermgmt_last_cycle_startstop_min = 25; /* how many minutes ago was the | 151 | int powermgmt_last_cycle_startstop_min = 0; /* how many minutes ago was the |
146 | charging started or | 152 | charging started or |
147 | stopped? */ | 153 | stopped? */ |
148 | int powermgmt_last_cycle_level = 0; /* which level had the | 154 | int powermgmt_last_cycle_level = 0; /* which level had the |
149 | batteries at this time? */ | 155 | batteries at this time? */ |
150 | bool trickle_charge_enabled = true; | 156 | bool trickle_charge_enabled = true; |
151 | int trickle_sec = 0; /* how many seconds should the | 157 | int trickle_sec = 0; /* how many seconds should the |
152 | charger be enabled per | 158 | charger be enabled per |
153 | minute for trickle | 159 | minute for trickle |
154 | charging? */ | 160 | charging? */ |
155 | static const short percent_to_volt_charge[11] = | ||
156 | /* voltages (centivolt) of 0%, 10%, ... 100% when charging enabled */ | 161 | /* voltages (centivolt) of 0%, 10%, ... 100% when charging enabled */ |
162 | static const short percent_to_volt_charge[11] = | ||
157 | { | 163 | { |
158 | /* values guessed, see | 164 | /* values guessed, see |
159 | http://www.seattlerobotics.org/encoder/200210/LiIon2.pdf until someone | 165 | http://www.seattlerobotics.org/encoder/200210/LiIon2.pdf until someone |
@@ -161,23 +167,64 @@ static const short percent_to_volt_charge[11] = | |||
161 | 476, 544, 551, 556, 561, 564, 566, 576, 582, 584, 585 /* NiMH */ | 167 | 476, 544, 551, 556, 561, 564, 566, 576, 582, 584, 585 /* NiMH */ |
162 | }; | 168 | }; |
163 | 169 | ||
164 | void enable_trickle_charge(bool on) | 170 | #endif /* HAVE_CHARGE_CTRL || CONFIG_BATTERY == BATT_LIION2200 */ |
165 | { | 171 | |
166 | trickle_charge_enabled = on; | 172 | /* |
167 | } | 173 | * Average battery voltage and charger voltage, filtered via a digital |
168 | #endif /* HAVE_CHARGE_CTRL */ | 174 | * exponential filter. |
175 | */ | ||
176 | unsigned int battery_centivolts;/* filtered battery voltage, centvolts */ | ||
177 | static unsigned int avgbat; /* average battery voltage */ | ||
178 | #define BATT_AVE_SAMPLES 32 /* filter constant / @ 2Hz sample rate */ | ||
179 | |||
180 | int battery_capacity = BATTERY_CAPACITY_MIN; /* only a default value */ | ||
169 | 181 | ||
170 | static long power_stack[DEFAULT_STACK_SIZE/sizeof(long)]; | 182 | /* battery level (0-100%) of this minute, updated once per minute */ |
183 | static int battery_percent = -1; | ||
184 | |||
185 | static bool car_adapter_mode_enabled = false; | ||
186 | |||
187 | static char power_stack[DEFAULT_STACK_SIZE + DEBUG_STACK]; | ||
171 | static const char power_thread_name[] = "power"; | 188 | static const char power_thread_name[] = "power"; |
172 | 189 | ||
173 | static int poweroff_timeout = 0; | 190 | static int poweroff_timeout = 0; |
174 | static long last_charge_time = 0; | 191 | static long last_charge_time = 0; |
175 | int powermgmt_est_runningtime_min = -1; | 192 | int powermgmt_est_runningtime_min = -1; |
176 | 193 | ||
177 | static bool sleeptimer_active = false; | 194 | static bool sleeptimer_active = false; |
178 | static unsigned long sleeptimer_endtick; | 195 | static unsigned long sleeptimer_endtick; |
179 | 196 | ||
180 | unsigned short power_history[POWER_HISTORY_LEN]; | 197 | #ifdef HAVE_CHARGE_CTRL |
198 | |||
199 | void enable_deep_discharge(bool on) | ||
200 | { | ||
201 | charge_restart_level = on ? CHARGE_RESTART_LO : CHARGE_RESTART_HI; | ||
202 | } | ||
203 | |||
204 | void enable_trickle_charge(bool on) | ||
205 | { | ||
206 | trickle_charge_enabled = on; | ||
207 | } | ||
208 | #endif /* HAVE_CHARGE_CTRL */ | ||
209 | |||
210 | #if BATTERY_TYPES_COUNT > 1 | ||
211 | void set_battery_type(int type) | ||
212 | { | ||
213 | if (type != battery_type) { | ||
214 | battery_type = type; | ||
215 | battery_percent = -1; /* reset on type change */ | ||
216 | } | ||
217 | } | ||
218 | #endif | ||
219 | |||
220 | void set_battery_capacity(int capacity) | ||
221 | { | ||
222 | battery_capacity = capacity; | ||
223 | if (battery_capacity > BATTERY_CAPACITY_MAX) | ||
224 | battery_capacity = BATTERY_CAPACITY_MAX; | ||
225 | if (battery_capacity < BATTERY_CAPACITY_MIN) | ||
226 | battery_capacity = BATTERY_CAPACITY_MIN; | ||
227 | } | ||
181 | 228 | ||
182 | int battery_time(void) | 229 | int battery_time(void) |
183 | { | 230 | { |
@@ -208,23 +255,9 @@ int voltage_to_percent(int voltage, const short* table) | |||
208 | /* update battery level, called only once per minute */ | 255 | /* update battery level, called only once per minute */ |
209 | void battery_level_update(void) | 256 | void battery_level_update(void) |
210 | { | 257 | { |
211 | int level = 0; | 258 | int level; |
212 | unsigned short c = 0; | ||
213 | int i; | ||
214 | #if BATTERY_TYPES_COUNT == 1 /* single type */ | ||
215 | const int battery_type = 0; | ||
216 | #endif | ||
217 | |||
218 | /* calculate maximum over last 3 minutes (skip empty samples) */ | ||
219 | for (i = 0; i < 3; i++) | ||
220 | if (power_history[POWER_HISTORY_LEN-1-i] > c) | ||
221 | c = power_history[POWER_HISTORY_LEN-1-i]; | ||
222 | |||
223 | if (c) | ||
224 | level = c; | ||
225 | else /* history was empty, get a fresh sample */ | ||
226 | level = (adc_read(ADC_UNREG_POWER) * BATTERY_SCALE_FACTOR) / 10000; | ||
227 | 259 | ||
260 | level = battery_centivolts; | ||
228 | if(level > BATTERY_LEVEL_FULL) | 261 | if(level > BATTERY_LEVEL_FULL) |
229 | level = BATTERY_LEVEL_FULL; | 262 | level = BATTERY_LEVEL_FULL; |
230 | 263 | ||
@@ -232,27 +265,27 @@ void battery_level_update(void) | |||
232 | level = BATTERY_LEVEL_EMPTY; | 265 | level = BATTERY_LEVEL_EMPTY; |
233 | 266 | ||
234 | #ifdef HAVE_CHARGE_CTRL | 267 | #ifdef HAVE_CHARGE_CTRL |
235 | if (charge_state == 0) { /* decharge */ | 268 | if (charge_state == DISCHARGING) { |
236 | level = voltage_to_percent(level, | 269 | level = voltage_to_percent(level, |
237 | percent_to_volt_decharge[battery_type]); | 270 | percent_to_volt_discharge[battery_type]); |
238 | } | 271 | } |
239 | else if (charge_state == 1) { /* charge */ | 272 | else if (charge_state == CHARGING) { |
240 | level = voltage_to_percent(level, percent_to_volt_charge); | 273 | level = voltage_to_percent(level, percent_to_volt_charge); |
241 | } | 274 | } |
242 | else {/* in trickle charge, the battery is per definition 100% full */ | 275 | else {/* in trickle charge, the battery is by definition 100% full */ |
243 | battery_level_cached = level = 100; | 276 | battery_percent = level = 100; |
244 | } | 277 | } |
245 | #else | 278 | #else |
279 | /* always use the discharge table */ | ||
246 | level = voltage_to_percent(level, | 280 | level = voltage_to_percent(level, |
247 | percent_to_volt_decharge[battery_type]); | 281 | percent_to_volt_discharge[battery_type]); |
248 | /* always use the decharge table */ | ||
249 | #endif | 282 | #endif |
250 | 283 | ||
251 | #ifndef HAVE_MMC /* this adjustment is only needed for HD based */ | 284 | #ifndef HAVE_MMC /* this adjustment is only needed for HD based */ |
252 | if (battery_level_cached == -1) { /* first run of this procedure */ | 285 | if (battery_percent == -1) { /* first run of this procedure */ |
253 | /* the battery voltage is usually a little lower directly after | 286 | /* The battery voltage is usually a little lower directly after |
254 | turning on, because the disk was used heavily raise it by 5 % */ | 287 | turning on, because the disk was used heavily. Raise it by 5. % */ |
255 | battery_level_cached = (level > 95) ? 100 : level + 5; | 288 | battery_percent = (level > 95) ? 100 : level + 5; |
256 | } | 289 | } |
257 | else | 290 | else |
258 | #endif | 291 | #endif |
@@ -260,14 +293,14 @@ void battery_level_update(void) | |||
260 | /* the level is allowed to be -1 of the last value when usb not | 293 | /* the level is allowed to be -1 of the last value when usb not |
261 | connected and to be -3 of the last value when usb is connected */ | 294 | connected and to be -3 of the last value when usb is connected */ |
262 | if (usb_inserted()) { | 295 | if (usb_inserted()) { |
263 | if (level < battery_level_cached - 3) | 296 | if (level < battery_percent - 3) |
264 | level = battery_level_cached - 3; | 297 | level = battery_percent - 3; |
265 | } | 298 | } |
266 | else { | 299 | else { |
267 | if (level < battery_level_cached - 1) | 300 | if (level < battery_percent - 1) |
268 | level = battery_level_cached - 1; | 301 | level = battery_percent - 1; |
269 | } | 302 | } |
270 | battery_level_cached = level; | 303 | battery_percent = level; |
271 | } | 304 | } |
272 | } | 305 | } |
273 | 306 | ||
@@ -275,21 +308,16 @@ void battery_level_update(void) | |||
275 | int battery_level(void) | 308 | int battery_level(void) |
276 | { | 309 | { |
277 | #ifdef HAVE_CHARGE_CTRL | 310 | #ifdef HAVE_CHARGE_CTRL |
278 | if ((charge_state==1) && (battery_level_cached==100)) | 311 | if ((charge_state == CHARGING) && (battery_percent == 100)) |
279 | return 99; | 312 | return 99; |
280 | #endif | 313 | #endif |
281 | return battery_level_cached; | 314 | return battery_percent; |
282 | } | 315 | } |
283 | 316 | ||
284 | /* Tells if the battery level is safe for disk writes */ | 317 | /* Tells if the battery level is safe for disk writes */ |
285 | bool battery_level_safe(void) | 318 | bool battery_level_safe(void) |
286 | { | 319 | { |
287 | /* I'm pretty sure we don't want an average over a long time here */ | 320 | return battery_centivolts > BATTERY_LEVEL_DANGEROUS; |
288 | if (power_history[POWER_HISTORY_LEN-1]) | ||
289 | return power_history[POWER_HISTORY_LEN-1] > BATTERY_LEVEL_DANGEROUS; | ||
290 | else | ||
291 | return adc_read(ADC_UNREG_POWER) > (BATTERY_LEVEL_DANGEROUS * 10000L) / | ||
292 | BATTERY_SCALE_FACTOR; | ||
293 | } | 321 | } |
294 | 322 | ||
295 | void set_poweroff_timeout(int timeout) | 323 | void set_poweroff_timeout(int timeout) |
@@ -329,10 +357,12 @@ int get_sleep_timer(void) | |||
329 | static void handle_auto_poweroff(void) | 357 | static void handle_auto_poweroff(void) |
330 | { | 358 | { |
331 | long timeout = poweroff_idle_timeout_value[poweroff_timeout]*60*HZ; | 359 | long timeout = poweroff_idle_timeout_value[poweroff_timeout]*60*HZ; |
332 | int mpeg_stat = mpeg_status(); | 360 | int mpeg_stat = mpeg_status(); |
361 | #ifdef HAVE_CHARGING | ||
333 | bool charger_is_inserted = charger_inserted(); | 362 | bool charger_is_inserted = charger_inserted(); |
334 | static bool charger_was_inserted = false; | 363 | #endif |
335 | 364 | ||
365 | #ifdef HAVE_CHARGING | ||
336 | /* The was_inserted thing prevents the unit to shut down immediately | 366 | /* The was_inserted thing prevents the unit to shut down immediately |
337 | when the charger is extracted */ | 367 | when the charger is extracted */ |
338 | if(charger_is_inserted || charger_was_inserted) | 368 | if(charger_is_inserted || charger_was_inserted) |
@@ -340,13 +370,14 @@ static void handle_auto_poweroff(void) | |||
340 | last_charge_time = current_tick; | 370 | last_charge_time = current_tick; |
341 | } | 371 | } |
342 | charger_was_inserted = charger_is_inserted; | 372 | charger_was_inserted = charger_is_inserted; |
373 | #endif | ||
343 | 374 | ||
344 | if(timeout && | 375 | if(timeout && |
345 | #ifdef CONFIG_TUNER | 376 | #ifdef CONFIG_TUNER |
346 | (radio_get_status() != FMRADIO_PLAYING) && | 377 | (radio_get_status() != FMRADIO_PLAYING) && |
347 | #endif | 378 | #endif |
348 | !usb_inserted() && | 379 | !usb_inserted() && |
349 | (mpeg_stat == 0 || | 380 | ((mpeg_stat == 0) || |
350 | ((mpeg_stat == (MPEG_STATUS_PLAY | MPEG_STATUS_PAUSE)) && | 381 | ((mpeg_stat == (MPEG_STATUS_PLAY | MPEG_STATUS_PAUSE)) && |
351 | !sleeptimer_active))) | 382 | !sleeptimer_active))) |
352 | { | 383 | { |
@@ -365,6 +396,7 @@ static void handle_auto_poweroff(void) | |||
365 | if(TIME_AFTER(current_tick, sleeptimer_endtick)) | 396 | if(TIME_AFTER(current_tick, sleeptimer_endtick)) |
366 | { | 397 | { |
367 | mpeg_stop(); | 398 | mpeg_stop(); |
399 | #ifdef HAVE_CHARGING | ||
368 | if(charger_is_inserted) | 400 | if(charger_is_inserted) |
369 | { | 401 | { |
370 | DEBUGF("Sleep timer timeout. Stopping...\n"); | 402 | DEBUGF("Sleep timer timeout. Stopping...\n"); |
@@ -372,6 +404,7 @@ static void handle_auto_poweroff(void) | |||
372 | backlight_off(); /* Nighty, nighty... */ | 404 | backlight_off(); /* Nighty, nighty... */ |
373 | } | 405 | } |
374 | else | 406 | else |
407 | #endif | ||
375 | { | 408 | { |
376 | DEBUGF("Sleep timer timeout. Shutting off...\n"); | 409 | DEBUGF("Sleep timer timeout. Shutting off...\n"); |
377 | /* Make sure that the disk isn't spinning when | 410 | /* Make sure that the disk isn't spinning when |
@@ -390,8 +423,6 @@ void set_car_adapter_mode(bool setting) | |||
390 | car_adapter_mode_enabled = setting; | 423 | car_adapter_mode_enabled = setting; |
391 | } | 424 | } |
392 | 425 | ||
393 | static bool charger_power_is_on; | ||
394 | |||
395 | #ifdef HAVE_CHARGING | 426 | #ifdef HAVE_CHARGING |
396 | static void car_adapter_mode_processing(void) | 427 | static void car_adapter_mode_processing(void) |
397 | { | 428 | { |
@@ -442,13 +473,38 @@ static void car_adapter_mode_processing(void) | |||
442 | } | 473 | } |
443 | #endif | 474 | #endif |
444 | 475 | ||
476 | /* | ||
477 | * Estimate how much current we are drawing just to run. | ||
478 | */ | ||
479 | static int runcurrent(void) | ||
480 | { | ||
481 | int current; | ||
482 | |||
483 | current = CURRENT_NORMAL; | ||
484 | if(usb_inserted()) { | ||
485 | current = CURRENT_USB; | ||
486 | } | ||
487 | #ifdef HAVE_CHARGE_CTRL | ||
488 | if ((backlight_get_timeout() == 1) || /* LED always on */ | ||
489 | (charger_inserted() && backlight_get_on_when_charging())) { | ||
490 | current += CURRENT_BACKLIGHT; | ||
491 | } | ||
492 | #else | ||
493 | if (backlight_get_timeout() == 1) { /* LED always on */ | ||
494 | current += CURRENT_BACKLIGHT; | ||
495 | } | ||
496 | #endif | ||
497 | |||
498 | return(current); | ||
499 | } | ||
500 | |||
501 | |||
445 | /* Check to see whether or not we've received an alarm in the last second */ | 502 | /* Check to see whether or not we've received an alarm in the last second */ |
446 | #ifdef HAVE_ALARM_MOD | 503 | #ifdef HAVE_ALARM_MOD |
447 | static void power_thread_rtc_process(void) | 504 | static void power_thread_rtc_process(void) |
448 | { | 505 | { |
449 | |||
450 | if (rtc_check_alarm_flag()) { | 506 | if (rtc_check_alarm_flag()) { |
451 | rtc_enable_alarm(false); | 507 | rtc_enable_alarm(false); |
452 | } | 508 | } |
453 | } | 509 | } |
454 | #endif | 510 | #endif |
@@ -457,24 +513,56 @@ static void power_thread_rtc_process(void) | |||
457 | * This function is called to do the relativly long sleep waits from within the | 513 | * This function is called to do the relativly long sleep waits from within the |
458 | * main power_thread loop while at the same time servicing any other periodic | 514 | * main power_thread loop while at the same time servicing any other periodic |
459 | * functions in the power thread which need to be called at a faster periodic | 515 | * functions in the power thread which need to be called at a faster periodic |
460 | * rate than the slow periodic rate of the main power_thread loop | 516 | * rate than the slow periodic rate of the main power_thread loop. |
517 | * | ||
518 | * While we are waiting for the time to expire, we average the battery | ||
519 | * voltages. | ||
461 | */ | 520 | */ |
462 | static void power_thread_sleep(int ticks) | 521 | static void power_thread_sleep(int ticks) |
463 | { | 522 | { |
523 | int small_ticks; | ||
464 | #ifdef HAVE_CHARGING | 524 | #ifdef HAVE_CHARGING |
525 | unsigned int tmp; | ||
526 | bool charger_plugged; | ||
527 | #endif | ||
528 | |||
529 | #ifdef HAVE_CHARGING | ||
530 | charger_plugged = charger_inserted(); | ||
531 | #endif | ||
465 | while (ticks > 0) { | 532 | while (ticks > 0) { |
466 | int small_ticks = MIN(HZ/2, ticks); | 533 | small_ticks = MIN(HZ/2, ticks); |
467 | sleep(small_ticks); | 534 | sleep(small_ticks); |
468 | ticks -= small_ticks; | 535 | ticks -= small_ticks; |
469 | 536 | ||
537 | #ifdef HAVE_CHARGING | ||
470 | car_adapter_mode_processing(); | 538 | car_adapter_mode_processing(); |
539 | #endif | ||
471 | #ifdef HAVE_ALARM_MOD | 540 | #ifdef HAVE_ALARM_MOD |
472 | power_thread_rtc_process(); | 541 | power_thread_rtc_process(); |
473 | #endif | 542 | #endif |
474 | } | 543 | |
475 | #else | 544 | /* |
476 | sleep(ticks); /* no fast-processing functions, sleep the whole time */ | 545 | * Do a digital exponential filter. We don't sample the battery if |
546 | * the disk is spinning unless we are in USB mode (the disk will most | ||
547 | * likely always be spinning in USB mode). | ||
548 | * If the charging voltage is greater than 0x3F0 charging isn't active | ||
549 | * and that voltage isn't valid. | ||
550 | */ | ||
551 | if (!ata_disk_is_active() || usb_inserted()) { | ||
552 | avgbat = avgbat - (avgbat / BATT_AVE_SAMPLES) + | ||
553 | adc_read(ADC_UNREG_POWER); | ||
554 | /* | ||
555 | * battery_centivolts is the centivolt-scaled filtered battery value. | ||
556 | */ | ||
557 | battery_centivolts = ((avgbat / BATT_AVE_SAMPLES) * BATTERY_SCALE_FACTOR) / 10000; | ||
558 | } | ||
559 | |||
560 | #ifdef HAVE_CHARGING | ||
561 | /* If the charger was plugged in, exit now so we can start charging */ | ||
562 | if(!charger_plugged && charger_inserted()) | ||
563 | return; | ||
477 | #endif | 564 | #endif |
565 | } | ||
478 | } | 566 | } |
479 | 567 | ||
480 | 568 | ||
@@ -488,102 +576,60 @@ static void power_thread_sleep(int ticks) | |||
488 | static void power_thread(void) | 576 | static void power_thread(void) |
489 | { | 577 | { |
490 | int i; | 578 | int i; |
491 | int avg, ok_samples, spin_samples; | 579 | short *phps, *phpd; /* power history rotation pointers */ |
492 | int current = 0; | ||
493 | #if CONFIG_BATTERY == BATT_LIION2200 | 580 | #if CONFIG_BATTERY == BATT_LIION2200 |
494 | int charging_current; | 581 | int charging_current; |
495 | #endif | 582 | #endif |
496 | #ifdef HAVE_CHARGE_CTRL | 583 | #ifdef HAVE_CHARGE_CTRL |
497 | int delta; | ||
498 | int charged_time = 0; | ||
499 | int charge_max_time_now = 0; /* max. charging duration, calculated at | 584 | int charge_max_time_now = 0; /* max. charging duration, calculated at |
500 | beginning of charging */ | 585 | beginning of charging */ |
501 | int charge_pause = 0; /* no charging pause at the beginning */ | ||
502 | int trickle_time = 0; /* how many minutes trickle charging already? */ | ||
503 | #endif | 586 | #endif |
587 | |||
588 | /* initialize the voltages for the exponential filter */ | ||
504 | 589 | ||
590 | avgbat = adc_read(ADC_UNREG_POWER) * BATT_AVE_SAMPLES; | ||
591 | battery_centivolts = ((avgbat / BATT_AVE_SAMPLES) * BATTERY_SCALE_FACTOR) / 10000; | ||
592 | |||
593 | #if defined(DEBUG_FILE) && defined(HAVE_CHARGE_CTRL) | ||
594 | fd = -1; | ||
595 | #endif | ||
596 | |||
505 | while (1) | 597 | while (1) |
506 | { | 598 | { |
507 | /* never read power while disk is spinning, unless in USB mode */ | ||
508 | if (ata_disk_is_active() && !usb_inserted()) { | ||
509 | #ifdef HAVE_ALARM_MOD | ||
510 | power_thread_rtc_process(); | ||
511 | #endif | ||
512 | sleep(HZ); | ||
513 | continue; | ||
514 | } | ||
515 | |||
516 | /* Make POWER_AVG measurements and calculate an average of that to | ||
517 | * reduce the effect of backlights/disk spinning/other variation. | ||
518 | */ | ||
519 | ok_samples = spin_samples = avg = 0; | ||
520 | for (i = 0; i < POWER_AVG_N; i++) { | ||
521 | if (ata_disk_is_active()) { | ||
522 | if (!ok_samples) { | ||
523 | /* if we don't have any good non-disk-spinning samples, | ||
524 | * we take a sample anyway in case the disk is going | ||
525 | * to spin all the time. | ||
526 | */ | ||
527 | avg += adc_read(ADC_UNREG_POWER); | ||
528 | spin_samples++; | ||
529 | } | ||
530 | } else { | ||
531 | if (spin_samples) /* throw away disk-spinning samples */ | ||
532 | spin_samples = avg = 0; | ||
533 | avg += adc_read(ADC_UNREG_POWER); | ||
534 | ok_samples++; | ||
535 | } | ||
536 | power_thread_sleep(HZ*POWER_AVG_SLEEP); | ||
537 | } | ||
538 | avg = avg / ((ok_samples) ? ok_samples : spin_samples); | ||
539 | |||
540 | /* rotate the power history */ | 599 | /* rotate the power history */ |
600 | phpd = &power_history[POWER_HISTORY_LEN - 1]; | ||
601 | phps = phpd - 1; | ||
541 | for (i = 0; i < POWER_HISTORY_LEN-1; i++) | 602 | for (i = 0; i < POWER_HISTORY_LEN-1; i++) |
542 | power_history[i] = power_history[i+1]; | 603 | *phpd-- = *phps--; |
543 | |||
544 | /* insert new value in the end, in centivolts 8-) */ | ||
545 | power_history[POWER_HISTORY_LEN-1] = | ||
546 | (avg * BATTERY_SCALE_FACTOR) / 10000; | ||
547 | 604 | ||
548 | /* update battery level every minute, ignoring first 15 minutes after | 605 | /* insert new value at the start, in centivolts 8-) */ |
549 | start charge/decharge */ | 606 | power_history[0] = battery_centivolts; |
550 | #ifdef HAVE_CHARGE_CTRL | 607 | |
551 | if ((powermgmt_last_cycle_startstop_min > 25) || (charge_state > 1)) | 608 | /* update battery level every minute */ |
552 | #endif | 609 | battery_level_update(); |
553 | battery_level_update(); | ||
554 | 610 | ||
555 | /* calculate estimated remaining running time */ | 611 | /* calculate estimated remaining running time */ |
556 | /* decharging: remaining running time */ | 612 | /* discharging: remaining running time */ |
557 | /* charging: remaining charging time */ | 613 | /* charging: remaining charging time */ |
558 | 614 | ||
559 | #ifdef HAVE_CHARGE_CTRL | ||
560 | if (charge_state == 1) | ||
561 | powermgmt_est_runningtime_min = (100 - battery_level()) * | ||
562 | battery_capacity / 100 * 60 / CURRENT_CHARGING; | ||
563 | else { | ||
564 | current = usb_inserted() ? CURRENT_USB : CURRENT_NORMAL; | ||
565 | if ((backlight_get_timeout() == 1) || | ||
566 | (charger_inserted() && backlight_get_on_when_charging())) | ||
567 | /* LED always on or LED on when charger connected */ | ||
568 | current += CURRENT_BACKLIGHT; | ||
569 | powermgmt_est_runningtime_min = battery_level() * | ||
570 | battery_capacity / 100 * 60 / current; | ||
571 | #if MEM == 8 /* assuming 192 kbps, the running time is 22% longer with 8MB */ | ||
572 | powermgmt_est_runningtime_min = | ||
573 | powermgmt_est_runningtime_min * 122 / 100; | ||
574 | #endif /* MEM == 8 */ | ||
575 | } | ||
576 | #else | ||
577 | current = usb_inserted() ? CURRENT_USB : CURRENT_NORMAL; | ||
578 | if (backlight_get_timeout() == 1) /* LED always on */ | ||
579 | current += CURRENT_BACKLIGHT; | ||
580 | powermgmt_est_runningtime_min = battery_level() * | 615 | powermgmt_est_runningtime_min = battery_level() * |
581 | battery_capacity / 100 * 60 / current; | 616 | battery_capacity / 100 * 60 / runcurrent(); |
582 | #if MEM == 8 /* assuming 192 kbps, the running time is 22% longer with 8MB */ | 617 | #if MEM == 8 /* assuming 192 kbps, the running time is 22% longer with 8MB */ |
583 | powermgmt_est_runningtime_min = | 618 | powermgmt_est_runningtime_min = |
584 | powermgmt_est_runningtime_min * 122 / 100; | 619 | powermgmt_est_runningtime_min * 122 / 100; |
585 | #endif /* MEM == 8 */ | 620 | #endif /* MEM == 8 */ |
586 | #endif /* HAVE_CHARGE_CONTROL */ | 621 | |
622 | #ifdef HAVE_CHARGE_CTRL | ||
623 | /* | ||
624 | * If we are charging, the "runtime" is estimated time till the battery | ||
625 | * is recharged. | ||
626 | */ | ||
627 | // TBD: use real charging current estimate | ||
628 | if (charge_state == CHARGING) { | ||
629 | powermgmt_est_runningtime_min = (100 - battery_level()) * | ||
630 | battery_capacity / 100 * 60 / (CURRENT_MAX_CHG - runcurrent()); | ||
631 | } | ||
632 | #endif /* HAVE_CHARGE_CTRL */ | ||
587 | 633 | ||
588 | #if CONFIG_BATTERY == BATT_LIION2200 | 634 | #if CONFIG_BATTERY == BATT_LIION2200 |
589 | /* We use the information from the ADC_EXT_POWER ADC channel, which | 635 | /* We use the information from the ADC_EXT_POWER ADC channel, which |
@@ -595,262 +641,235 @@ static void power_thread(void) | |||
595 | if(charger_inserted()) { | 641 | if(charger_inserted()) { |
596 | charging_current = adc_read(ADC_EXT_POWER); | 642 | charging_current = adc_read(ADC_EXT_POWER); |
597 | if(charging_current < 0x80) { | 643 | if(charging_current < 0x80) { |
598 | charge_state = 2; /* Trickle */ | 644 | charge_state = TRICKLE; |
599 | } else { | 645 | } else { |
600 | charge_state = 1; /* Charging */ | 646 | charge_state = CHARGING; |
601 | } | 647 | } |
602 | } else { | 648 | } else { |
603 | charge_state = 0; /* Not charging */ | 649 | charge_state = DISCHARGING; |
604 | } | 650 | } |
605 | #else | 651 | #endif /* # if CONFIG_BATTERY == BATT_LIION2200 */ |
652 | |||
606 | #ifdef HAVE_CHARGE_CTRL | 653 | #ifdef HAVE_CHARGE_CTRL |
607 | 654 | ||
608 | if (charge_pause > 0) | ||
609 | charge_pause--; | ||
610 | |||
611 | if (charger_inserted()) { | 655 | if (charger_inserted()) { |
612 | if (charge_state == 1) { | 656 | /* |
613 | /* charger inserted and enabled */ | 657 | * Time to start charging again? |
614 | charged_time++; | 658 | * 1) If we are currently discharging but trickle is enabled, |
615 | snprintf(power_message, POWER_MESSAGE_LEN, | 659 | * the charger must have just been plugged in. |
616 | "Chg %dm max %dm", charged_time, charge_max_time_now); | 660 | * 2) If our battery level falls below the restart level, charge! |
617 | 661 | */ | |
618 | if (charged_time > charge_max_time_now) { | 662 | if (((charge_state == DISCHARGING) && trickle_charge_enabled) || |
619 | DEBUGF("power: charged_time > charge_max_time_now, " | 663 | (battery_level() < charge_restart_level)) { |
620 | "enough!\n"); | 664 | |
621 | /* have charged too long and deltaV detection did not | 665 | /* |
622 | work! */ | 666 | * If the battery level is nearly charged, just trickle. |
623 | powermgmt_last_cycle_level = battery_level(); | 667 | * If the battery is in between, top-off and then trickle. |
668 | */ | ||
669 | if(battery_percent > charge_restart_level) { | ||
670 | powermgmt_last_cycle_level = battery_percent; | ||
624 | powermgmt_last_cycle_startstop_min = 0; | 671 | powermgmt_last_cycle_startstop_min = 0; |
625 | charger_enable(false); | 672 | if(battery_percent >= 95) { |
626 | snprintf(power_message, POWER_MESSAGE_LEN, | 673 | trickle_sec = START_TRICKLE_SEC; |
627 | "Chg tmout %d min", charge_max_time_now); | 674 | charge_state = TRICKLE; |
628 | /* disable charging for several hours from this point, | 675 | } else { |
629 | just to be sure */ | 676 | trickle_sec = START_TOPOFF_SEC; |
630 | charge_pause = CHARGE_PAUSE_LEN; | 677 | charge_state = TOPOFF; |
631 | /* no trickle charge here, because the charging cycle | 678 | } |
632 | didn't end the right way */ | ||
633 | charge_state = 0; /* 0: decharging/charger off, 1: charge, | ||
634 | 2: top-off, 3: trickle */ | ||
635 | } else { | 679 | } else { |
636 | if (charged_time > CHARGE_MIN_TIME) { | 680 | /* |
637 | /* have charged continuously over the minimum charging | 681 | * Start the charger full strength |
638 | time, so we monitor for deltaV going | 682 | */ |
639 | negative. Multiply thingsby 100 to get more | 683 | i = CHARGE_MAX_TIME_1500 * battery_capacity / 1500; |
640 | accuracy without floating point arithmetic. | 684 | charge_max_time_now = |
641 | power_history[] contains centivolts so after | 685 | i * (100 + 35 - battery_percent) / 100; |
642 | multiplying by 100 the deltas are in tenths of | 686 | if (charge_max_time_now > i) { |
643 | millivolts (delta of 5 is 0.0005 V). | 687 | charge_max_time_now = i; |
644 | */ | 688 | } |
645 | delta = | 689 | snprintf(power_message, POWER_MESSAGE_LEN, |
646 | ( power_history[POWER_HISTORY_LEN-1] * 100 | 690 | "ChgAt %d%% max %dm", battery_level(), |
647 | + power_history[POWER_HISTORY_LEN-2] * 100 | 691 | charge_max_time_now); |
648 | - power_history[POWER_HISTORY_LEN-1- | 692 | |
649 | CHARGE_END_NEGD+1] * 100 | 693 | /* enable the charger after the max time calc is done, |
650 | - power_history[POWER_HISTORY_LEN-1- | 694 | because battery_level depends on if the charger is |
651 | CHARGE_END_NEGD] * 100 ) | 695 | on */ |
652 | / CHARGE_END_NEGD / 2; | 696 | DEBUGF("power: charger inserted and battery" |
653 | 697 | " not full, enabling\n"); | |
654 | if (delta < -100) { /* delta < -10 mV */ | 698 | powermgmt_last_cycle_level = battery_percent; |
699 | powermgmt_last_cycle_startstop_min = 0; | ||
700 | trickle_sec = 60; | ||
701 | charge_state = CHARGING; | ||
702 | } | ||
703 | } | ||
704 | if (charge_state == CHARGING) { | ||
705 | /* charger inserted and enabled 100% of the time */ | ||
706 | trickle_sec = 60; /* 100% on */ | ||
707 | |||
708 | snprintf(power_message, POWER_MESSAGE_LEN, | ||
709 | "Chg %dm, max %dm", powermgmt_last_cycle_startstop_min, | ||
710 | charge_max_time_now); | ||
711 | /* | ||
712 | * Sum the deltas over the last X minutes so we can do our | ||
713 | * end-of-charge logic based on the battery level change. | ||
714 | */ | ||
715 | long_delta = short_delta = 999999; | ||
716 | if (powermgmt_last_cycle_startstop_min > CHARGE_MIN_TIME) { | ||
717 | short_delta = power_history[0] - | ||
718 | power_history[CHARGE_END_NEGD - 1]; | ||
719 | } | ||
720 | if (powermgmt_last_cycle_startstop_min > CHARGE_END_ZEROD) { | ||
721 | /* | ||
722 | * Scan the history: if we have a big delta in the middle of | ||
723 | * our history, the long term delta isn't a valid end-of-charge | ||
724 | * indicator. | ||
725 | */ | ||
726 | long_delta = power_history[0] - | ||
727 | power_history[CHARGE_END_ZEROD - 1]; | ||
728 | for(i = 0; i < CHARGE_END_ZEROD; i++) { | ||
729 | if(((power_history[i] - power_history[i+1]) > 5) || | ||
730 | ((power_history[i] - power_history[i+1]) < -5)) { | ||
731 | long_delta = 888888; | ||
732 | break; | ||
733 | } | ||
734 | } | ||
735 | } | ||
736 | |||
737 | /* | ||
738 | * End of charge criteria (any qualify): | ||
739 | * 1) Charged a long time | ||
740 | * 2) DeltaV went negative for a short time | ||
741 | * 3) DeltaV was close to zero for a long time | ||
742 | * Note: short_delta and long_delta are centivolts | ||
743 | */ | ||
744 | if ((powermgmt_last_cycle_startstop_min > charge_max_time_now) || | ||
745 | (short_delta < -5) || (long_delta < 5)) | ||
746 | { | ||
747 | if (powermgmt_last_cycle_startstop_min > charge_max_time_now) { | ||
748 | DEBUGF("power: powermgmt_last_cycle_startstop_min > charge_max_time_now, " | ||
749 | "enough!\n"); | ||
750 | /* have charged too long and deltaV detection did not | ||
751 | work! */ | ||
752 | snprintf(power_message, POWER_MESSAGE_LEN, | ||
753 | "Chg tmout %d min", charge_max_time_now); | ||
754 | } else { | ||
755 | if(short_delta < -5) { | ||
655 | DEBUGF("power: short-term negative" | 756 | DEBUGF("power: short-term negative" |
656 | " delta, enough!\n"); | 757 | " delta, enough!\n"); |
657 | powermgmt_last_cycle_level = battery_level(); | ||
658 | powermgmt_last_cycle_startstop_min = 0; | ||
659 | charger_enable(false); | ||
660 | snprintf(power_message, POWER_MESSAGE_LEN, | 758 | snprintf(power_message, POWER_MESSAGE_LEN, |
661 | "end negd %d %dmin", delta, charged_time); | 759 | "end negd %d %dmin", short_delta, |
662 | /* disable charging for several hours from this | 760 | powermgmt_last_cycle_startstop_min); |
663 | point, just to be sure */ | ||
664 | charge_pause = CHARGE_PAUSE_LEN; | ||
665 | /* enable trickle charging */ | ||
666 | if (trickle_charge_enabled) { | ||
667 | trickle_sec = CURRENT_NORMAL * 60 / | ||
668 | CURRENT_CHARGING; | ||
669 | /* first guess, maybe consider if LED | ||
670 | backlight is on, disk is active,... */ | ||
671 | trickle_time = 0; | ||
672 | charge_state = 2; /* 0: decharging/charger | ||
673 | off, 1: charge, | ||
674 | 2: top-off, 3: trickle */ | ||
675 | } else { | ||
676 | charge_state = 0; /* 0: decharging/charger | ||
677 | off, 1: charge, | ||
678 | 2: top-off, 3: trickle */ | ||
679 | } | ||
680 | } else { | 761 | } else { |
681 | /* if we didn't disable the charger in the | 762 | DEBUGF("power: long-term small " |
682 | previous test, check for low positive delta */ | 763 | "positive delta, enough!\n"); |
683 | delta = | 764 | snprintf(power_message, POWER_MESSAGE_LEN, |
684 | ( power_history[POWER_HISTORY_LEN-1] * 100 | 765 | "end lowd %d %dmin", long_delta, |
685 | + power_history[POWER_HISTORY_LEN-2] * 100 | 766 | powermgmt_last_cycle_startstop_min); |
686 | - power_history[POWER_HISTORY_LEN-1- | ||
687 | CHARGE_END_ZEROD+1] * 100 | ||
688 | - power_history[POWER_HISTORY_LEN-1- | ||
689 | CHARGE_END_ZEROD] * 100 ) | ||
690 | / CHARGE_END_ZEROD / 2; | ||
691 | |||
692 | if (delta < 1) { /* delta < 0.1 mV */ | ||
693 | DEBUGF("power: long-term small " | ||
694 | "positive delta, enough!\n"); | ||
695 | powermgmt_last_cycle_level = battery_level(); | ||
696 | powermgmt_last_cycle_startstop_min = 0; | ||
697 | charger_enable(false); | ||
698 | snprintf(power_message, POWER_MESSAGE_LEN, | ||
699 | "end lowd %d %dmin", | ||
700 | delta, charged_time); | ||
701 | /* disable charging for several hours from | ||
702 | this point, just to be sure */ | ||
703 | charge_pause = CHARGE_PAUSE_LEN; | ||
704 | /* enable trickle charging */ | ||
705 | if (trickle_charge_enabled) { | ||
706 | trickle_sec = | ||
707 | CURRENT_NORMAL * 60 / CURRENT_CHARGING; | ||
708 | /* first guess, maybe consider if LED | ||
709 | backlight is on, disk is active,... */ | ||
710 | trickle_time = 0; | ||
711 | charge_state = 2; | ||
712 | /* 0: decharging/charger off, 1: charge, | ||
713 | 2: top-off, 3: trickle */ | ||
714 | } else { | ||
715 | charge_state = 0; | ||
716 | /* 0: decharging/charger off, 1: charge, | ||
717 | 2: top-off, 3: trickle */ | ||
718 | } | ||
719 | } | ||
720 | } | 767 | } |
721 | } | 768 | } |
769 | /* Switch to trickle charging. We skip the top-off | ||
770 | since we've effectively done the top-off operation | ||
771 | already since we charged for the maximum full | ||
772 | charge time. For trickle charging, we use 0.05C */ | ||
773 | powermgmt_last_cycle_level = battery_percent; | ||
774 | powermgmt_last_cycle_startstop_min = 0; | ||
775 | if (trickle_charge_enabled) { | ||
776 | trickle_sec = START_TRICKLE_SEC; | ||
777 | charge_state = TRICKLE; | ||
778 | } else { | ||
779 | /* If we don't trickle charge, we discharge */ | ||
780 | trickle_sec = 0; /* off */ | ||
781 | charge_state = DISCHARGING; | ||
782 | } | ||
722 | } | 783 | } |
723 | } | 784 | } |
724 | else if (charge_state > 1) { /* top off or trickle? */ | 785 | else if (charge_state > CHARGING) /* top off or trickle */ |
725 | /* adjust trickle charge time */ | 786 | { |
726 | if ( ((charge_state == 2) && | 787 | /* Time to switch from topoff to trickle? Note that we don't |
727 | (power_history[POWER_HISTORY_LEN-1] > TOPOFF_VOLTAGE)) | 788 | * adjust trickle_sec: it will get adjusted down by the |
728 | || ((charge_state == 3) && | 789 | * charge level adjustment in the loop and will drift down |
729 | (power_history[POWER_HISTORY_LEN-1] > | 790 | * from the topoff level to the trickle level. |
730 | TRICKLE_VOLTAGE)) ) { /* charging too much */ | 791 | */ |
731 | trickle_sec--; | 792 | if ((charge_state == TOPOFF) && |
732 | } | 793 | (powermgmt_last_cycle_startstop_min > TOPOFF_MAX_TIME)) |
733 | else { /* charging too less */ | 794 | { |
734 | trickle_sec++; | 795 | powermgmt_last_cycle_level = battery_percent; |
735 | } | ||
736 | |||
737 | if (trickle_sec > 24) | ||
738 | trickle_sec = 24; | ||
739 | |||
740 | if (trickle_sec < 1) | ||
741 | trickle_sec = 1; | ||
742 | |||
743 | /* charge the calculated amount of seconds */ | ||
744 | charger_enable(true); | ||
745 | power_thread_sleep(HZ * trickle_sec); | ||
746 | charger_enable(false); | ||
747 | |||
748 | /* trickle charging long enough? */ | ||
749 | |||
750 | if (trickle_time++ > TRICKLE_MAX_TIME + TOPOFF_MAX_TIME) { | ||
751 | trickle_sec = 0; /* show in debug menu that trickle is | ||
752 | off */ | ||
753 | charge_state = 0; /* 0: decharging/charger off, 1: charge, | ||
754 | 2: top-off, 3: trickle */ | ||
755 | powermgmt_last_cycle_startstop_min = 0; | 796 | powermgmt_last_cycle_startstop_min = 0; |
797 | charge_state = TRICKLE; | ||
756 | } | 798 | } |
757 | 799 | ||
758 | if ((charge_state == 2) && | 800 | /* Adjust trickle charge time. I considered setting the level |
759 | (trickle_time > TOPOFF_MAX_TIME)) /* change state? */ | 801 | * higher if the USB is plugged in, but it doesn't appear to |
760 | charge_state = 3; /* 0: decharging/charger off, 1: charge, | 802 | * be necessary and will generate more heat [gvb]. |
761 | 2: top-off, 3: trickle */ | 803 | */ |
762 | 804 | if(((charge_state == TOPOFF) && (battery_centivolts > TOPOFF_VOLTAGE)) || | |
763 | } else { /* charge_state == 0 */ | 805 | ((charge_state == TRICKLE) && (battery_centivolts > TRICKLE_VOLTAGE))) |
806 | { /* charging too much */ | ||
807 | if(trickle_sec > 0) | ||
808 | trickle_sec--; | ||
809 | } | ||
810 | else { /* charging too little */ | ||
811 | if(trickle_sec < 60) | ||
812 | trickle_sec++; | ||
813 | } | ||
764 | 814 | ||
815 | } else if (charge_state == DISCHARGING) { | ||
816 | trickle_sec = 0; | ||
765 | /* the charger is enabled here only in one case: if it was | 817 | /* the charger is enabled here only in one case: if it was |
766 | turned on at boot time (power_init) */ | 818 | turned on at boot time (power_init) */ |
767 | /* turn it off now */ | 819 | /* turn it off now */ |
768 | if (charger_enabled) | 820 | if (charger_enabled) |
769 | charger_enable(false); | 821 | charger_enable(false); |
770 | } | 822 | } |
771 | |||
772 | /* Start new charge cycle? This must be possible also in | ||
773 | trickle/top-off, because when usb connected, */ | ||
774 | /* the trickle charge amount may not be enough */ | ||
775 | |||
776 | if ((charge_state == 0) || (charge_state > 1)) | ||
777 | /* if battery is not full, enable charging */ | ||
778 | /* make sure charging starts if 1%-lazyness in | ||
779 | battery_level_update() is too slow */ | ||
780 | if ( (battery_level() < charge_restart_level) | ||
781 | || (power_history[POWER_HISTORY_LEN-1] < | ||
782 | BATTERY_LEVEL_DANGEROUS)) { | ||
783 | if (charge_pause) { | ||
784 | DEBUGF("power: batt level < restart level," | ||
785 | " but charge pause, not enabling\n"); | ||
786 | snprintf(power_message, POWER_MESSAGE_LEN, | ||
787 | "chg pause %d min", charge_pause); | ||
788 | } else { | ||
789 | /* calculate max charge time depending on current | ||
790 | battery level */ | ||
791 | /* take 35% more because some more energy is used for | ||
792 | heating up the battery */ | ||
793 | i = CHARGE_MAX_TIME_1500 * battery_capacity / 1500; | ||
794 | charge_max_time_now = | ||
795 | i * (100 + 35 - battery_level()) / 100; | ||
796 | if (charge_max_time_now > i) { | ||
797 | charge_max_time_now = i; | ||
798 | } | ||
799 | snprintf(power_message, POWER_MESSAGE_LEN, | ||
800 | "ChgAt %d%% max %dm", battery_level(), | ||
801 | charge_max_time_now); | ||
802 | |||
803 | /* enable the charger after the max time calc is done, | ||
804 | because battery_level depends on if the charger is | ||
805 | on */ | ||
806 | DEBUGF("power: charger inserted and battery" | ||
807 | " not full, enabling\n"); | ||
808 | powermgmt_last_cycle_level = battery_level(); | ||
809 | powermgmt_last_cycle_startstop_min = 0; | ||
810 | charged_time = 0; | ||
811 | |||
812 | charger_enable(true); | ||
813 | charge_state = 1; /* 0: decharging/charger off, 1: | ||
814 | charge, 2: top-off, 3: trickle */ | ||
815 | /* clear the power history so that we don't use values | ||
816 | before discharge for the long-term delta | ||
817 | */ | ||
818 | for (i = 0; i < POWER_HISTORY_LEN-1; i++) | ||
819 | power_history[i] = | ||
820 | power_history[POWER_HISTORY_LEN-1]; | ||
821 | } | ||
822 | } | ||
823 | |||
824 | } else { | 823 | } else { |
825 | /* charger not inserted */ | 824 | if (charge_state != DISCHARGING) { |
826 | if (charge_state > 0) { | ||
827 | /* charger not inserted but was enabled */ | 825 | /* charger not inserted but was enabled */ |
828 | DEBUGF("power: charger disconnected, disabling\n"); | 826 | DEBUGF("power: charger disconnected, disabling\n"); |
829 | powermgmt_last_cycle_level = battery_level(); | 827 | |
828 | charger_enable(false); | ||
829 | powermgmt_last_cycle_level = battery_percent; | ||
830 | powermgmt_last_cycle_startstop_min = 0; | 830 | powermgmt_last_cycle_startstop_min = 0; |
831 | /* show in debug menu that trickle is off */ | ||
832 | trickle_sec = 0; | 831 | trickle_sec = 0; |
833 | charger_enable(false); | 832 | charge_state = DISCHARGING; |
834 | charge_state = 0; /* 0: decharging/charger off, 1: charge, 2: | 833 | snprintf(power_message, POWER_MESSAGE_LEN, "Charger: discharge"); |
835 | top-off, 3: trickle */ | ||
836 | snprintf(power_message, POWER_MESSAGE_LEN, "Charger disc"); | ||
837 | } | 834 | } |
838 | /* charger not inserted and disabled, so we're discharging */ | ||
839 | } | 835 | } |
840 | powermgmt_last_cycle_startstop_min++; | 836 | powermgmt_last_cycle_startstop_min++; |
841 | 837 | ||
842 | #endif /* HAVE_CHARGE_CTRL*/ | 838 | #endif /* HAVE_CHARGE_CTRL*/ |
843 | #endif /* # if CONFIG_BATTERY == BATT_LIION2200 */ | 839 | |
844 | 840 | /* sleep for a minute */ | |
845 | /* sleep for roughly a minute */ | 841 | |
846 | #ifdef HAVE_CHARGE_CTRL | 842 | #ifdef HAVE_CHARGE_CTRL |
847 | i = 60 - trickle_sec - POWER_AVG_N * POWER_AVG_SLEEP; | 843 | if(trickle_sec > 0) { |
844 | charger_enable(true); | ||
845 | power_thread_sleep(HZ * trickle_sec); | ||
846 | } | ||
847 | if(trickle_sec < 60) | ||
848 | charger_enable(false); | ||
849 | power_thread_sleep(HZ * (60 - trickle_sec)); | ||
848 | #else | 850 | #else |
849 | i = 60 - POWER_AVG_N * POWER_AVG_SLEEP; | 851 | power_thread_sleep(HZ * 60); |
850 | #endif | 852 | #endif |
851 | if (i > 0) | ||
852 | power_thread_sleep(HZ*(i)); | ||
853 | 853 | ||
854 | #if defined(DEBUG_FILE) && defined(HAVE_CHARGE_CTRL) | ||
855 | if((fd < 0) && !usb_inserted()) { | ||
856 | fd = open(DEBUG_FILE_NAME, O_WRONLY | O_APPEND | O_CREAT); | ||
857 | snprintf(debug_message, DEBUG_MESSAGE_LEN, | ||
858 | "cycle_min, bat_centivolts, bat_percent, chgr, chg_state, trickle_sec\n"); | ||
859 | write(fd, debug_message, strlen(debug_message)); | ||
860 | fsync(fd); | ||
861 | } else if((fd >= 0) && !usb_inserted()) { | ||
862 | snprintf(debug_message, DEBUG_MESSAGE_LEN, "%d, %d, %d, %d, %d, %d\n", | ||
863 | powermgmt_last_cycle_startstop_min, battery_centivolts, | ||
864 | battery_percent, charger_inserted(), charge_state, trickle_sec); | ||
865 | write(fd, debug_message, strlen(debug_message)); | ||
866 | fsync(fd); | ||
867 | } else if((fd >= 0) && usb_inserted()) { | ||
868 | /* NOTE: It is probably already TOO LATE to close the file */ | ||
869 | close(fd); | ||
870 | fd = -1; | ||
871 | } | ||
872 | #endif | ||
854 | handle_auto_poweroff(); | 873 | handle_auto_poweroff(); |
855 | } | 874 | } |
856 | } | 875 | } |
@@ -862,31 +881,9 @@ void powermgmt_init(void) | |||
862 | /* init history to 0 */ | 881 | /* init history to 0 */ |
863 | memset(power_history, 0x00, sizeof(power_history)); | 882 | memset(power_history, 0x00, sizeof(power_history)); |
864 | 883 | ||
865 | #if 0 | 884 | #ifdef HAVE_CHARGING |
866 | /* initialize the history with a single sample to prevent level | ||
867 | flickering during the first minute of execution */ | ||
868 | power_history[POWER_HISTORY_LEN-1] = | ||
869 | (adc_read(ADC_UNREG_POWER) * BATTERY_SCALE_FACTOR) / 10000; | ||
870 | /* calculate the first battery level */ | ||
871 | battery_level_update(); | ||
872 | /* calculate the remaining time to that the info screen displays something | ||
873 | useful */ | ||
874 | powermgmt_est_runningtime_min = | ||
875 | battery_level() * battery_capacity / 100 * 60 / CURRENT_NORMAL; | ||
876 | #if MEM == 8 /* assuming 192 kbps, the running time is 22% longer with 8MB */ | ||
877 | powermgmt_est_runningtime_min = powermgmt_est_runningtime_min * 122 / 100; | ||
878 | #endif | ||
879 | |||
880 | #ifdef HAVE_CHARGE_CTRL | ||
881 | snprintf(power_message, POWER_MESSAGE_LEN, "Powermgmt started"); | ||
882 | |||
883 | /* if the battery is nearly empty, start charging immediately */ | ||
884 | if (power_history[POWER_HISTORY_LEN-1] < BATTERY_LEVEL_DANGEROUS) | ||
885 | charger_enable(true); | ||
886 | #endif | ||
887 | #endif | ||
888 | |||
889 | charger_power_is_on = charger_inserted(); | 885 | charger_power_is_on = charger_inserted(); |
886 | #endif | ||
890 | 887 | ||
891 | create_thread(power_thread, power_stack, sizeof(power_stack), | 888 | create_thread(power_thread, power_stack, sizeof(power_stack), |
892 | power_thread_name); | 889 | power_thread_name); |
@@ -898,11 +895,17 @@ void powermgmt_init(void) | |||
898 | void shutdown_hw(void) | 895 | void shutdown_hw(void) |
899 | { | 896 | { |
900 | #ifndef SIMULATOR | 897 | #ifndef SIMULATOR |
898 | #if defined(DEBUG_FILE) && defined(HAVE_CHARGE_CTRL) | ||
899 | if(fd > 0) { | ||
900 | close(fd); | ||
901 | fd = 0; | ||
902 | } | ||
903 | #endif | ||
901 | mpeg_stop(); | 904 | mpeg_stop(); |
902 | ata_flush(); | 905 | ata_flush(); |
903 | ata_spindown(1); | 906 | ata_spindown(1); |
904 | while(ata_disk_is_active()) | 907 | while(ata_disk_is_active()) |
905 | sleep(HZ/10); | 908 | sleep(HZ/10); |
906 | 909 | ||
907 | mp3_shutdown(); | 910 | mp3_shutdown(); |
908 | #if CONFIG_KEYPAD == ONDIO_PAD | 911 | #if CONFIG_KEYPAD == ONDIO_PAD |