summaryrefslogtreecommitdiff
path: root/firmware/target/sh/archos/recorder/powermgmt-recorder.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/sh/archos/recorder/powermgmt-recorder.c')
-rw-r--r--firmware/target/sh/archos/recorder/powermgmt-recorder.c501
1 files changed, 0 insertions, 501 deletions
diff --git a/firmware/target/sh/archos/recorder/powermgmt-recorder.c b/firmware/target/sh/archos/recorder/powermgmt-recorder.c
deleted file mode 100644
index ca6067a4cc..0000000000
--- a/firmware/target/sh/archos/recorder/powermgmt-recorder.c
+++ /dev/null
@@ -1,501 +0,0 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 by Heikki Hannikainen, Uwe Freese
11 * Revisions copyright (C) 2005 by Gerald Van Baren
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22#include "config.h"
23#include "system.h"
24#include <stdio.h>
25#include "debug.h"
26#include "storage.h"
27#include "adc.h"
28#include "power.h"
29#include "powermgmt.h"
30
31const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
32{
33 4750
34};
35
36const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] =
37{
38 4400
39};
40
41/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */
42const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] =
43{
44 /* original values were taken directly after charging, but it should show
45 100% after turning off the device for some hours, too */
46 { 4500, 4810, 4910, 4970, 5030, 5070, 5120, 5140, 5170, 5250, 5400 }
47 /* orig. values: ...,5280,5600 */
48};
49
50/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */
51const unsigned short percent_to_volt_charge[11] =
52{
53 /* values guessed, see
54 http://www.seattlerobotics.org/encoder/200210/LiIon2.pdf until someone
55 measures voltages over a charging cycle */
56 4760, 5440, 5510, 5560, 5610, 5640, 5660, 5760, 5820, 5840, 5850 /* NiMH */
57};
58
59#define BATTERY_SCALE_FACTOR 6620
60/* full-scale ADC readout (2^10) in millivolt */
61
62/* Returns battery voltage from ADC [millivolts] */
63int _battery_voltage(void)
64{
65 return (adc_read(ADC_UNREG_POWER) * BATTERY_SCALE_FACTOR) >> 10;
66}
67
68void powermgmt_init_target(void)
69{
70}
71
72/** Charger control **/
73#ifdef CHARGING_DEBUG_FILE
74#include "file.h"
75#define DEBUG_FILE_NAME "/powermgmt.csv"
76#define DEBUG_MESSAGE_LEN 133
77static char debug_message[DEBUG_MESSAGE_LEN];
78static int fd = -1; /* write debug information to this file */
79static int wrcount = 0;
80#endif /* CHARGING_DEBUG_FILE */
81
82/*
83 * For a complete description of the charging algorithm read
84 * docs/CHARGING_ALGORITHM.
85 */
86int long_delta; /* long term delta battery voltage */
87int short_delta; /* short term delta battery voltage */
88static bool disk_activity_last_cycle = false; /* flag set to aid charger time
89 * calculation */
90char power_message[POWER_MESSAGE_LEN] = ""; /* message that's shown in
91 debug menu */
92 /* percentage at which charging
93 starts */
94int powermgmt_last_cycle_startstop_min = 0; /* how many minutes ago was the
95 charging started or
96 stopped? */
97int powermgmt_last_cycle_level = 0; /* which level had the
98 batteries at this time? */
99int trickle_sec = 0; /* how many seconds should the
100 charger be enabled per
101 minute for trickle
102 charging? */
103int pid_p = 0; /* PID proportional term */
104int pid_i = 0; /* PID integral term */
105
106static unsigned int target_voltage = TRICKLE_VOLTAGE; /* desired topoff/trickle
107 * voltage level */
108static int charge_max_time_idle = 0; /* max. charging duration, calculated at
109 * beginning of charging */
110static int charge_max_time_now = 0; /* max. charging duration including
111 * hdd activity */
112static int minutes_disk_activity = 0; /* count minutes of hdd use during
113 * charging */
114static int last_disk_activity = CHARGE_END_LONGD + 1; /* last hdd use x mins ago */
115
116#ifdef CHARGING_DEBUG_FILE
117static void debug_file_close(void)
118{
119 if (fd >= 0) {
120 close(fd);
121 fd = -1;
122 }
123}
124
125static void debug_file_log(void)
126{
127 if (usb_inserted()) {
128 /* It is probably too late to close the file but we can try... */
129 debug_file_close();
130 }
131 else if (fd < 0) {
132 fd = open(DEBUG_FILE_NAME, O_WRONLY | O_APPEND | O_CREAT, 0666);
133
134 if (fd >= 0) {
135 snprintf(debug_message, DEBUG_MESSAGE_LEN,
136 "cycle_min, bat_millivolts, bat_percent, chgr_state"
137 " ,charge_state, pid_p, pid_i, trickle_sec\n");
138 write(fd, debug_message, strlen(debug_message));
139 wrcount = 99; /* force a flush */
140 }
141 }
142 else {
143 snprintf(debug_message, DEBUG_MESSAGE_LEN,
144 "%d, %d, %d, %d, %d, %d, %d, %d\n",
145 powermgmt_last_cycle_startstop_min, battery_voltage(),
146 battery_level(), charger_input_state, charge_state,
147 pid_p, pid_i, trickle_sec);
148 write(fd, debug_message, strlen(debug_message));
149 wrcount++;
150 }
151}
152
153static void debug_file_sync(void)
154{
155 /*
156 * If we have a lot of pending writes or if the disk is spining,
157 * fsync the debug log file.
158 */
159 if (wrcount > 10 || (wrcount > 0 && storage_disk_is_active())) {
160 if (fd >= 0)
161 fsync(fd);
162
163 wrcount = 0;
164 }
165}
166#else /* !CHARGING_DEBUG_FILE */
167#define debug_file_close()
168#define debug_file_log()
169#define debug_file_sync()
170#endif /* CHARGING_DEBUG_FILE */
171
172/*
173 * Do tasks that should be done every step.
174 */
175static void do_frequent_tasks(void)
176{
177 if (storage_disk_is_active()) {
178 /* flag hdd use for charging calculation */
179 disk_activity_last_cycle = true;
180 }
181
182 debug_file_sync();
183}
184
185/*
186 * The charger was just plugged in. If the battery level is
187 * nearly charged, just trickle. If the battery is low, start
188 * a full charge cycle. If the battery level is in between,
189 * top-off and then trickle.
190 */
191static void charger_plugged(void)
192{
193 int battery_percent = battery_level();
194
195 pid_p = 0;
196 pid_i = 0;
197 powermgmt_last_cycle_level = battery_percent;
198 powermgmt_last_cycle_startstop_min = 0;
199
200 snprintf(power_message, POWER_MESSAGE_LEN, "Charger plugged in");
201
202 if (battery_percent > START_TOPOFF_CHG) {
203
204 if (battery_percent >= START_TRICKLE_CHG) {
205 charge_state = TRICKLE;
206 target_voltage = TRICKLE_VOLTAGE;
207 }
208 else {
209 charge_state = TOPOFF;
210 target_voltage = TOPOFF_VOLTAGE;
211 }
212 }
213 else {
214 /*
215 * Start the charger full strength
216 */
217 int i = CHARGE_MAX_MIN_1500 * get_battery_capacity() / 1500;
218 charge_max_time_idle = i * (100 + 35 - battery_percent) / 100;
219
220 if (charge_max_time_idle > i)
221 charge_max_time_idle = i;
222
223 charge_max_time_now = charge_max_time_idle;
224
225 snprintf(power_message, POWER_MESSAGE_LEN,
226 "ChgAt %d%% max %dm", battery_percent,
227 charge_max_time_now);
228
229 /*
230 * Enable the charger after the max time calc is done,
231 * because battery_level depends on if the charger is
232 * on.
233 */
234 DEBUGF("power: charger inserted and battery"
235 " not full, charging\n");
236 trickle_sec = 60;
237 long_delta = short_delta = 999999;
238 charge_state = CHARGING;
239 }
240}
241
242/*
243 * The charger was just unplugged.
244 */
245static void charger_unplugged(void)
246{
247 DEBUGF("power: charger disconnected, disabling\n");
248
249 charger_enable(false);
250 powermgmt_last_cycle_level = battery_level();
251 powermgmt_last_cycle_startstop_min = 0;
252 trickle_sec = 0;
253 pid_p = 0;
254 pid_i = 0;
255 charge_state = DISCHARGING;
256 snprintf(power_message, POWER_MESSAGE_LEN, "Charger: discharge");
257}
258
259static void charging_step(void)
260{
261 int i;
262
263 /* alter charge time max length with extra disk use */
264 if (disk_activity_last_cycle) {
265 minutes_disk_activity++;
266 charge_max_time_now = charge_max_time_idle +
267 minutes_disk_activity*2 / 5;
268 disk_activity_last_cycle = false;
269 last_disk_activity = 0;
270 }
271 else {
272 last_disk_activity++;
273 }
274
275 /*
276 * Check the delta voltage over the last X minutes so we can do
277 * our end-of-charge logic based on the battery level change
278 * (no longer use minimum time as logic for charge end has 50
279 * minutes minimum charge built in).
280 */
281 if (powermgmt_last_cycle_startstop_min > CHARGE_END_SHORTD) {
282 short_delta = power_history[0] -
283 power_history[CHARGE_END_SHORTD - 1];
284 }
285
286 if (powermgmt_last_cycle_startstop_min > CHARGE_END_LONGD) {
287 /*
288 * Scan the history: the points where measurement is taken need to
289 * be fairly static. Check prior to short delta 'area'. Also only
290 * check first and last 10 cycles (delta in middle OK).
291 */
292 long_delta = power_history[0] -
293 power_history[CHARGE_END_LONGD - 1];
294
295 for (i = CHARGE_END_SHORTD; i < CHARGE_END_SHORTD + 10; i++)
296 {
297 if ((power_history[i] - power_history[i+1]) > 50 ||
298 (power_history[i] - power_history[i+1]) < -50) {
299 long_delta = 777777;
300 break;
301 }
302 }
303
304 for (i = CHARGE_END_LONGD - 11; i < CHARGE_END_LONGD - 1 ; i++)
305 {
306 if ((power_history[i] - power_history[i+1]) > 50 ||
307 (power_history[i] - power_history[i+1]) < -50) {
308 long_delta = 888888;
309 break;
310 }
311 }
312 }
313
314 snprintf(power_message, POWER_MESSAGE_LEN,
315 "Chg %dm, max %dm", powermgmt_last_cycle_startstop_min,
316 charge_max_time_now);
317
318 /*
319 * End of charge criteria (any qualify):
320 * 1) Charged a long time
321 * 2) DeltaV went negative for a short time ( & long delta static)
322 * 3) DeltaV was negative over a longer period (no disk use only)
323 *
324 * Note: short_delta and long_delta are millivolts
325 */
326 if (powermgmt_last_cycle_startstop_min >= charge_max_time_now ||
327 (short_delta <= -50 && long_delta < 50) ||
328 (long_delta < -20 && last_disk_activity > CHARGE_END_LONGD)) {
329
330 int battery_percent = battery_level();
331
332 if (powermgmt_last_cycle_startstop_min > charge_max_time_now) {
333 DEBUGF("power: powermgmt_last_cycle_startstop_min > charge_max_time_now, "
334 "enough!\n");
335 /*
336 * Have charged too long and deltaV detection did not
337 * work!
338 */
339 snprintf(power_message, POWER_MESSAGE_LEN,
340 "Chg tmout %d min", charge_max_time_now);
341 /*
342 * Switch to trickle charging. We skip the top-off
343 * since we've effectively done the top-off operation
344 * already since we charged for the maximum full
345 * charge time.
346 */
347 powermgmt_last_cycle_level = battery_percent;
348 powermgmt_last_cycle_startstop_min = 0;
349 charge_state = TRICKLE;
350
351 /*
352 * Set trickle charge target to a relative voltage instead
353 * of an arbitrary value - the fully charged voltage may
354 * vary according to ambient temp, battery condition etc.
355 * Trickle target is -0.15v from full voltage acheived.
356 * Topup target is -0.05v from full voltage.
357 */
358 target_voltage = power_history[0] - 150;
359
360 }
361 else {
362 if(short_delta <= -5) {
363 DEBUGF("power: short-term negative"
364 " delta, enough!\n");
365 snprintf(power_message, POWER_MESSAGE_LEN,
366 "end negd %d %dmin", short_delta,
367 powermgmt_last_cycle_startstop_min);
368 target_voltage = power_history[CHARGE_END_SHORTD - 1] - 50;
369 }
370 else {
371 DEBUGF("power: long-term small "
372 "positive delta, enough!\n");
373 snprintf(power_message, POWER_MESSAGE_LEN,
374 "end lowd %d %dmin", long_delta,
375 powermgmt_last_cycle_startstop_min);
376 target_voltage = power_history[CHARGE_END_LONGD - 1] - 50;
377 }
378
379 /*
380 * Switch to top-off charging.
381 */
382 powermgmt_last_cycle_level = battery_percent;
383 powermgmt_last_cycle_startstop_min = 0;
384 charge_state = TOPOFF;
385 }
386 }
387}
388
389static void topoff_trickle_step(void)
390{
391 unsigned int millivolts;
392
393 /*
394 *Time to switch from topoff to trickle?
395 */
396 if (charge_state == TOPOFF &&
397 powermgmt_last_cycle_startstop_min > TOPOFF_MAX_MIN) {
398
399 powermgmt_last_cycle_level = battery_level();
400 powermgmt_last_cycle_startstop_min = 0;
401 charge_state = TRICKLE;
402 target_voltage = target_voltage - 100;
403 }
404 /*
405 * Adjust trickle charge time (proportional and integral terms).
406 * Note: I considered setting the level higher if the USB is
407 * plugged in, but it doesn't appear to be necessary and will
408 * generate more heat [gvb].
409 */
410 millivolts = battery_voltage();
411
412 pid_p = ((signed)target_voltage - (signed)millivolts) / 5;
413 if (pid_p <= PID_DEADZONE && pid_p >= -PID_DEADZONE)
414 pid_p = 0;
415
416 if ((unsigned)millivolts < target_voltage) {
417 if (pid_i < 60)
418 pid_i++; /* limit so it doesn't "wind up" */
419 }
420 else {
421 if (pid_i > 0)
422 pid_i--; /* limit so it doesn't "wind up" */
423 }
424
425 trickle_sec = pid_p + pid_i;
426
427 if (trickle_sec > 60)
428 trickle_sec = 60;
429
430 if (trickle_sec < 0)
431 trickle_sec = 0;
432}
433
434void charging_algorithm_step(void)
435{
436 static int pwm_counter = 0; /* PWM total cycle in steps */
437 static int pwm_duty = 0; /* PWM duty cycle in steps */
438
439 switch (charger_input_state)
440 {
441 case CHARGER_PLUGGED:
442 charger_plugged();
443 break;
444
445 case CHARGER_UNPLUGGED:
446 charger_unplugged();
447 break;
448
449 case CHARGER:
450 case NO_CHARGER:
451 do_frequent_tasks();
452
453 if (pwm_counter > 0) {
454 if (pwm_duty > 0 && --pwm_duty <= 0)
455 charger_enable(false); /* Duty cycle expired */
456
457 if (--pwm_counter > 0)
458 return;
459
460 /* PWM cycle is complete */
461 powermgmt_last_cycle_startstop_min++;
462 debug_file_log();
463 }
464 break;
465 }
466
467 switch (charge_state)
468 {
469 case CHARGING:
470 charging_step();
471 break;
472
473 case TOPOFF:
474 case TRICKLE:
475 topoff_trickle_step();
476 break;
477
478 case DISCHARGING:
479 default:
480 break;
481 }
482
483 /* If 100%, ensure pwm_on never expires and briefly disables the
484 * charger. */
485 pwm_duty = (trickle_sec < 60) ? trickle_sec*2 : 0;
486 pwm_counter = 60*2;
487 charger_enable(trickle_sec > 0);
488}
489
490void charging_algorithm_close(void)
491{
492#ifdef CHARGING_DEBUG_FILE
493 debug_file_close();
494#endif /* CHARGING_DEBUG_FILE */
495}
496
497/* Returns true if the unit is charging the batteries. */
498bool charging_state(void)
499{
500 return charge_state == CHARGING;
501}