diff options
author | Jörg Hohensohn <hohensoh@rockbox.org> | 2005-03-03 07:25:43 +0000 |
---|---|---|
committer | Jörg Hohensohn <hohensoh@rockbox.org> | 2005-03-03 07:25:43 +0000 |
commit | 2584896920724cd5e72caaf9b64c3ef81b45ee9f (patch) | |
tree | 672817ced4c15a673fb557e87b0091cf90e43339 | |
parent | 384de102469fee4e0792df8fe38586d3206774ed (diff) | |
download | rockbox-2584896920724cd5e72caaf9b64c3ef81b45ee9f.tar.gz rockbox-2584896920724cd5e72caaf9b64c3ef81b45ee9f.zip |
More aggressive Recorder V1 charging (patch #1116884 from Jerry Van Baren)
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@6105 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r-- | apps/debug_menu.c | 53 | ||||
-rw-r--r-- | apps/settings.c | 3 | ||||
-rw-r--r-- | apps/settings_menu.c | 3 | ||||
-rwxr-xr-x | docs/CHARGING_ALGORITHM | 82 | ||||
-rw-r--r-- | firmware/export/adc.h | 2 | ||||
-rw-r--r-- | firmware/export/powermgmt.h | 39 | ||||
-rw-r--r-- | firmware/powermgmt.c | 819 |
7 files changed, 507 insertions, 494 deletions
diff --git a/apps/debug_menu.c b/apps/debug_menu.c index 815eb5af67..7b81869e69 100644 --- a/apps/debug_menu.c +++ b/apps/debug_menu.c | |||
@@ -968,7 +968,7 @@ bool dbg_mas_codec(void) | |||
968 | * The power_history array is updated in power_thread of powermgmt.c. | 968 | * The power_history array is updated in power_thread of powermgmt.c. |
969 | */ | 969 | */ |
970 | 970 | ||
971 | #define BAT_FIRST_VAL MAX(POWER_HISTORY_LEN - LCD_WIDTH - 1, 0) | 971 | #define BAT_LAST_VAL MIN(LCD_WIDTH, POWER_HISTORY_LEN) |
972 | #define BAT_YSPACE (LCD_HEIGHT - 20) | 972 | #define BAT_YSPACE (LCD_HEIGHT - 20) |
973 | 973 | ||
974 | bool view_battery(void) | 974 | bool view_battery(void) |
@@ -986,30 +986,32 @@ bool view_battery(void) | |||
986 | switch (view) { | 986 | switch (view) { |
987 | case 0: /* voltage history graph */ | 987 | case 0: /* voltage history graph */ |
988 | /* Find maximum and minimum voltage for scaling */ | 988 | /* Find maximum and minimum voltage for scaling */ |
989 | maxv = minv = 0; | 989 | maxv = 0; |
990 | for (i = BAT_FIRST_VAL; i < POWER_HISTORY_LEN; i++) { | 990 | minv = 65535; |
991 | for (i = 0; i < BAT_LAST_VAL; i++) { | ||
991 | if (power_history[i] > maxv) | 992 | if (power_history[i] > maxv) |
992 | maxv = power_history[i]; | 993 | maxv = power_history[i]; |
993 | if ((minv == 0) || ((power_history[i]) && | 994 | if (power_history[i] && (power_history[i] < minv)) |
994 | (power_history[i] < minv)) ) | ||
995 | { | 995 | { |
996 | minv = power_history[i]; | 996 | minv = power_history[i]; |
997 | } | 997 | } |
998 | } | 998 | } |
999 | 999 | ||
1000 | if (minv < 1) | 1000 | if ((minv < 1) || (minv >= 65535)) |
1001 | minv = 1; | 1001 | minv = 1; |
1002 | if (maxv < 2) | 1002 | if (maxv < 2) |
1003 | maxv = 2; | 1003 | maxv = 2; |
1004 | 1004 | ||
1005 | lcd_clear_display(); | 1005 | lcd_clear_display(); |
1006 | lcd_puts(0, 0, "Battery voltage:"); | 1006 | snprintf(buf, 30, "Battery %d.%02d", power_history[0] / 100, |
1007 | power_history[0] % 100); | ||
1008 | lcd_puts(0, 0, buf); | ||
1007 | snprintf(buf, 30, "scale %d.%02d-%d.%02d V", | 1009 | snprintf(buf, 30, "scale %d.%02d-%d.%02d V", |
1008 | minv / 100, minv % 100, maxv / 100, maxv % 100); | 1010 | minv / 100, minv % 100, maxv / 100, maxv % 100); |
1009 | lcd_puts(0, 1, buf); | 1011 | lcd_puts(0, 1, buf); |
1010 | 1012 | ||
1011 | x = 0; | 1013 | x = 0; |
1012 | for (i = BAT_FIRST_VAL+1; i < POWER_HISTORY_LEN; i++) { | 1014 | for (i = BAT_LAST_VAL - 1; i >= 0; i--) { |
1013 | y = (power_history[i] - minv) * BAT_YSPACE / (maxv - minv); | 1015 | y = (power_history[i] - minv) * BAT_YSPACE / (maxv - minv); |
1014 | lcd_clearline(x, LCD_HEIGHT-1, x, 20); | 1016 | lcd_clearline(x, LCD_HEIGHT-1, x, 20); |
1015 | lcd_drawline(x, LCD_HEIGHT-1, x, | 1017 | lcd_drawline(x, LCD_HEIGHT-1, x, |
@@ -1035,31 +1037,15 @@ bool view_battery(void) | |||
1035 | snprintf(buf, 30, "Charger: %s", | 1037 | snprintf(buf, 30, "Charger: %s", |
1036 | charger_inserted() ? "present" : "absent"); | 1038 | charger_inserted() ? "present" : "absent"); |
1037 | lcd_puts(0, 3, buf); | 1039 | lcd_puts(0, 3, buf); |
1040 | #endif | ||
1038 | #ifdef HAVE_CHARGE_CTRL | 1041 | #ifdef HAVE_CHARGE_CTRL |
1039 | snprintf(buf, 30, "Charging: %s", | 1042 | snprintf(buf, 30, "Charging: %s", |
1040 | charger_enabled ? "yes" : "no"); | 1043 | charger_enabled ? "yes" : "no"); |
1041 | lcd_puts(0, 4, buf); | 1044 | lcd_puts(0, 4, buf); |
1042 | #endif | 1045 | snprintf(buf, 30, "short delta: %d", short_delta); |
1043 | #endif | ||
1044 | y = ( power_history[POWER_HISTORY_LEN-1] * 100 | ||
1045 | + power_history[POWER_HISTORY_LEN-2] * 100 | ||
1046 | - power_history[POWER_HISTORY_LEN-1-CHARGE_END_NEGD+1] * 100 | ||
1047 | - power_history[POWER_HISTORY_LEN-1-CHARGE_END_NEGD] * 100 ) | ||
1048 | / CHARGE_END_NEGD / 2; | ||
1049 | |||
1050 | snprintf(buf, 30, "short delta: %d", y); | ||
1051 | lcd_puts(0, 5, buf); | 1046 | lcd_puts(0, 5, buf); |
1052 | 1047 | snprintf(buf, 30, "long delta: %d", long_delta); | |
1053 | y = ( power_history[POWER_HISTORY_LEN-1] * 100 | ||
1054 | + power_history[POWER_HISTORY_LEN-2] * 100 | ||
1055 | - power_history[POWER_HISTORY_LEN-1-CHARGE_END_ZEROD+1] * 100 | ||
1056 | - power_history[POWER_HISTORY_LEN-1-CHARGE_END_ZEROD] * 100 ) | ||
1057 | / CHARGE_END_ZEROD / 2; | ||
1058 | |||
1059 | snprintf(buf, 30, "long delta: %d", y); | ||
1060 | lcd_puts(0, 6, buf); | 1048 | lcd_puts(0, 6, buf); |
1061 | |||
1062 | #ifdef HAVE_CHARGE_CTRL | ||
1063 | lcd_puts(0, 7, power_message); | 1049 | lcd_puts(0, 7, power_message); |
1064 | #endif | 1050 | #endif |
1065 | break; | 1051 | break; |
@@ -1069,8 +1055,7 @@ bool view_battery(void) | |||
1069 | lcd_puts(0, 0, "Voltage deltas:"); | 1055 | lcd_puts(0, 0, "Voltage deltas:"); |
1070 | 1056 | ||
1071 | for (i = 0; i <= 6; i++) { | 1057 | for (i = 0; i <= 6; i++) { |
1072 | y = power_history[POWER_HISTORY_LEN-1-i] - | 1058 | y = power_history[i] - power_history[i+i]; |
1073 | power_history[POWER_HISTORY_LEN-1-i-1]; | ||
1074 | snprintf(buf, 30, "-%d min: %s%d.%02d V", i, | 1059 | snprintf(buf, 30, "-%d min: %s%d.%02d V", i, |
1075 | (y < 0) ? "-" : "", ((y < 0) ? y * -1 : y) / 100, | 1060 | (y < 0) ? "-" : "", ((y < 0) ? y * -1 : y) / 100, |
1076 | ((y < 0) ? y * -1 : y ) % 100); | 1061 | ((y < 0) ? y * -1 : y ) % 100); |
@@ -1088,19 +1073,19 @@ bool view_battery(void) | |||
1088 | snprintf(buf, 30, "Cycle time: %d m", powermgmt_last_cycle_startstop_min); | 1073 | snprintf(buf, 30, "Cycle time: %d m", powermgmt_last_cycle_startstop_min); |
1089 | lcd_puts(0, 1, buf); | 1074 | lcd_puts(0, 1, buf); |
1090 | 1075 | ||
1091 | snprintf(buf, 30, "Lev.at cycle start: %d%%", powermgmt_last_cycle_level); | 1076 | snprintf(buf, 30, "Lvl@cyc st: %d%%", powermgmt_last_cycle_level); |
1092 | lcd_puts(0, 2, buf); | 1077 | lcd_puts(0, 2, buf); |
1093 | #endif | 1078 | #endif |
1094 | 1079 | ||
1095 | snprintf(buf, 30, "Last PwrHist val: %d.%02d V", | 1080 | snprintf(buf, 30, "Last PwrHist: %d.%02d V", |
1096 | power_history[POWER_HISTORY_LEN-1] / 100, | 1081 | power_history[0] / 100, |
1097 | power_history[POWER_HISTORY_LEN-1] % 100); | 1082 | power_history[0] % 100); |
1098 | lcd_puts(0, 3, buf); | 1083 | lcd_puts(0, 3, buf); |
1099 | 1084 | ||
1100 | snprintf(buf, 30, "battery level: %d%%", battery_level()); | 1085 | snprintf(buf, 30, "battery level: %d%%", battery_level()); |
1101 | lcd_puts(0, 5, buf); | 1086 | lcd_puts(0, 5, buf); |
1102 | 1087 | ||
1103 | snprintf(buf, 30, "Est. remaining: %d m", battery_time()); | 1088 | snprintf(buf, 30, "Est. remain: %d m", battery_time()); |
1104 | lcd_puts(0, 6, buf); | 1089 | lcd_puts(0, 6, buf); |
1105 | 1090 | ||
1106 | #ifdef HAVE_CHARGE_CTRL | 1091 | #ifdef HAVE_CHARGE_CTRL |
diff --git a/apps/settings.c b/apps/settings.c index 86c1178745..866f87879e 100644 --- a/apps/settings.c +++ b/apps/settings.c | |||
@@ -763,8 +763,7 @@ void settings_apply(void) | |||
763 | 763 | ||
764 | set_poweroff_timeout(global_settings.poweroff); | 764 | set_poweroff_timeout(global_settings.poweroff); |
765 | #ifdef HAVE_CHARGE_CTRL | 765 | #ifdef HAVE_CHARGE_CTRL |
766 | charge_restart_level = global_settings.discharge ? | 766 | enable_deep_discharge(global_settings.discharge); |
767 | CHARGE_RESTART_LO : CHARGE_RESTART_HI; | ||
768 | enable_trickle_charge(global_settings.trickle_charge); | 767 | enable_trickle_charge(global_settings.trickle_charge); |
769 | #endif | 768 | #endif |
770 | 769 | ||
diff --git a/apps/settings_menu.c b/apps/settings_menu.c index b19f2eda3c..4d164180dc 100644 --- a/apps/settings_menu.c +++ b/apps/settings_menu.c | |||
@@ -727,8 +727,7 @@ static bool deep_discharge(void) | |||
727 | { | 727 | { |
728 | bool result; | 728 | bool result; |
729 | result = set_bool( str(LANG_DISCHARGE), &global_settings.discharge ); | 729 | result = set_bool( str(LANG_DISCHARGE), &global_settings.discharge ); |
730 | charge_restart_level = global_settings.discharge ? | 730 | enable_deep_discharge(global_settings.discharge); |
731 | CHARGE_RESTART_LO : CHARGE_RESTART_HI; | ||
732 | return result; | 731 | return result; |
733 | } | 732 | } |
734 | static bool trickle_charge(void) | 733 | static bool trickle_charge(void) |
diff --git a/docs/CHARGING_ALGORITHM b/docs/CHARGING_ALGORITHM index a1a6e2b56e..4a89cac149 100755 --- a/docs/CHARGING_ALGORITHM +++ b/docs/CHARGING_ALGORITHM | |||
@@ -8,12 +8,13 @@ miss some information here, write to mail@uwe-freese.de. | |||
8 | 8 | ||
9 | [INTRODUCTION] | 9 | [INTRODUCTION] |
10 | 10 | ||
11 | This doc describes how the charging works for the recorder. The algorithm can | 11 | This doc describes how the charging works for the recorder. The algorithm |
12 | be found in firmware/powermgmt.[c|h]. Debug output is done in | 12 | can be found in firmware/powermgmt.[c|h]. Debug output is done in |
13 | apps/debug_menu.c. | 13 | apps/debug_menu.c. |
14 | Charging for the player and the FM/V2 recorder is done by the hardware and | 14 | |
15 | therefore isn't implemented in rockbox. Only the functions that calculate the | 15 | Charging for the player and the FM/V2 recorder is done by the hardware and |
16 | battery level are also used for these models. | 16 | therefore isn't implemented in rockbox. Only the functions that |
17 | calculate the battery level are also used for these models. | ||
17 | 18 | ||
18 | All following information is related to the recorder. | 19 | All following information is related to the recorder. |
19 | 20 | ||
@@ -38,15 +39,13 @@ voltage. Both voltage curves (charging and decharging) are used here. | |||
38 | [CHARGE OVERVIEW] | 39 | [CHARGE OVERVIEW] |
39 | 40 | ||
40 | - If voltage drops under a certain value (with "deep discharge" option on the | 41 | - If voltage drops under a certain value (with "deep discharge" option on the |
41 | value is lower), charging is started. | 42 | value is a lot lower), charging is started. |
42 | - If end of charge is detected, go to top off charge. | 43 | - If end of charge is detected, go to top off charge. |
43 | - Make the batteries completely full. 90 minutes of top off charge (voltage | 44 | - Make the batteries completely full. 90 minutes of top off charge (voltage |
44 | regulation at a high value). | 45 | regulation at a higher value). |
45 | - After that, do trickle charge (max. 12 hours with voltage regulation at a | 46 | - After that, trickle charge (voltage regulation at a nominal battery value). |
46 | lower value). | 47 | The trickle charge will continue as long as the charger is plugged in (this |
47 | - When trickle charge is done and you did not disconnect or shut off your AJB | 48 | is a change from the original charge algorithm). |
48 | by now, the AJB decharges normally since it reaches a low voltage and | ||
49 | everything starts from the beginning. | ||
50 | 49 | ||
51 | 50 | ||
52 | [NORMAL CHARGE] | 51 | [NORMAL CHARGE] |
@@ -89,7 +88,7 @@ Two facts on batteries are the reason why this works: | |||
89 | goes down when the temperature goes up. | 88 | goes down when the temperature goes up. |
90 | 89 | ||
91 | NiMH batteries have a smaller delta peak than NiCd, but is is enough for | 90 | NiMH batteries have a smaller delta peak than NiCd, but is is enough for |
92 | Rockbox to detect that the batteries are full. | 91 | Rockbox to detect that the batteries are full (in theory :-). |
93 | 92 | ||
94 | Related documents on the web: | 93 | Related documents on the web: |
95 | 94 | ||
@@ -114,20 +113,33 @@ It goes on again and then the archos firmware charger code would charge again. | |||
114 | So we have trickle charge in rockbox. | 113 | So we have trickle charge in rockbox. |
115 | 114 | ||
116 | In simple words, rockbox charges about 15 seconds per minute in trickle mode. | 115 | In simple words, rockbox charges about 15 seconds per minute in trickle mode. |
117 | An AJB consumes 100 mA when it's on and the charging current is about 300mA. | 116 | An AJB consumes 100 mA when it's on and the charging current is about 350mA. |
118 | So charging 15 s and decharge 45 s will keep the batteries full. | 117 | So charging 15 s and decharge 45 s will keep the batteries full. |
119 | 118 | ||
120 | But the number of seconds the charger is on in trickle charge mode is also | 119 | But the number of seconds the charger is on in trickle charge mode is |
121 | adjusted dynamically (between 1 and 24 sec). Rockbox tries to hold the battery | 120 | also adjusted dynamically. Rockbox tries to hold the battery level at |
122 | level at 5,65 V (top off charge, that means "make the batteries completely | 121 | 5,65 V (top off charge, that means "make the batteries completely full") |
123 | full") for 90 minutes, then a level of 5,45 V. If the voltage drops below the | 122 | for 90 minutes, then a level of 5,45 V. If the voltage drops below the |
124 | wanted value, rockbox will charge one second more the next minute. If is is | 123 | desired value, rockbox will charge one second more the next minute. If |
125 | greater than this value, is will charge one second less. | 124 | is is greater than this value, is will charge one second less. |
126 | 125 | ||
127 | Trickle charging runs 12 hours after finishing the normal charging. That | 126 | The number of seconds the charger is on in top off and trickle charge |
128 | should be enough for charging the AJB over night and then unplug the charger | 127 | modes is also dependant on the charger's output voltage: if the charger |
129 | sometime in this 12 hour trickle charge time. It is not recommended to trickle | 128 | supplies less than about 10v, the current into the batteries is less and |
130 | charge over days, that's because it is stopped after 12 hours. | 129 | thus the percentage on is increased to maintain the proper current into |
130 | the batteries. | ||
131 | |||
132 | The original recharging algorithm stopped trickle charging after 12 hours, | ||
133 | at which time the battery would be discharged until the the batteries | ||
134 | fell below the "start charging" level. At that time the charge cycle | ||
135 | would be repeated. | ||
136 | |||
137 | The time limit was removed by Jerry Van Baren (along with other changes) | ||
138 | in the February, 2005 timeframe. The rationale for this is that the | ||
139 | trickle charge level is very low. In addition, it is disconcerting to | ||
140 | have a AJR plugged in and "recharged" only to find out that the battery | ||
141 | is only 86% full. This was giving the Rockbox recharging algorithm a | ||
142 | bad name and frustrating our users. | ||
131 | 143 | ||
132 | Many chargers do top off and trickle charge by feeding a constant (low) | 144 | Many chargers do top off and trickle charge by feeding a constant (low) |
133 | current to the batteries. Rockbox, as described, makes a voltage regulation. | 145 | current to the batteries. Rockbox, as described, makes a voltage regulation. |
@@ -177,25 +189,23 @@ because it uses the raw voltages): | |||
177 | always set the battery level to 100% | 189 | always set the battery level to 100% |
178 | - the battery level is only allowed to change 1% per minute (exception: when | 190 | - the battery level is only allowed to change 1% per minute (exception: when |
179 | usb is connected, it is allowed to go 3% down/min) | 191 | usb is connected, it is allowed to go 3% down/min) |
180 | - if charging just started (or stopped), ignore the battery voltage for the | ||
181 | first 25 minutes | ||
182 | - after turning on the device, add another 5% to the battery level, because | 192 | - after turning on the device, add another 5% to the battery level, because |
183 | the drive is used heavily when booting and the voltage usually gets a | 193 | the drive is used heavily when booting and the voltage usually gets a |
184 | little higher after that | 194 | little higher after that (rebounds) |
185 | 195 | ||
186 | 196 | ||
187 | [WHICH CHARGING MODE TO USE] | 197 | [WHICH CHARGING MODE TO USE] |
188 | 198 | ||
189 | If you use your AJB connected to the power supply the whole time, select "deep | 199 | Jerry Van Baren's revised recommendation: Select "deep discharge OFF" |
190 | discharge on" and "trickle charge off". | 200 | and "trickle charge ON". This will keep your batteries charged up and |
201 | IMHO will not damage them. | ||
191 | 202 | ||
192 | If you want to charge your AJB over night and take it with you the next day, | 203 | Original recommendation: |
193 | select "deep discharge off" (that it starts charging immediately) and "trickle | ||
194 | charge on" (that the batteries remain full). | ||
195 | 204 | ||
196 | A special case: If you fill up the batteries that are still nearly full every | 205 | A special case: If you use your AJR connected to the power supply all |
206 | the time or if you fill up the batteries that are still nearly full every | ||
197 | night, it is recommended that you make a complete charge cycle from time to | 207 | night, it is recommended that you make a complete charge cycle from time to |
198 | time. Select "deep discharge on" and "trickle charge on" and wait till the | 208 | time. Select "deep discharge ON" and "trickle charge OFF" and wait till the |
199 | whole cycle is over (you can speed up the discharging a little bit by turning | 209 | whole cycle is over (you can speed up the discharging a little bit by turning |
200 | on the LED backlight). Even if the battery sellers say NiMH cells don't show a | 210 | on the LED backlight). Even if the battery sellers say NiMH cells don't show a |
201 | memory effect, I recommend making this procedure from time to time (every 10th | 211 | memory effect, I recommend making this procedure from time to time (every 10th |
diff --git a/firmware/export/adc.h b/firmware/export/adc.h index a18cb1995a..40bc3e108a 100644 --- a/firmware/export/adc.h +++ b/firmware/export/adc.h | |||
@@ -63,7 +63,7 @@ | |||
63 | #define ADC_BUTTON_ROW2 5 /* Used for scanning the keys, different | 63 | #define ADC_BUTTON_ROW2 5 /* Used for scanning the keys, different |
64 | voltages for different keys */ | 64 | voltages for different keys */ |
65 | #define ADC_UNREG_POWER 6 /* Battery voltage with a better scaling */ | 65 | #define ADC_UNREG_POWER 6 /* Battery voltage with a better scaling */ |
66 | #define ADC_EXT_POWER 7 /* The external power voltage, V=X*0.0148 */ | 66 | #define ADC_EXT_POWER 7 /* The external power voltage, 0v or 2.7v */ |
67 | 67 | ||
68 | #endif | 68 | #endif |
69 | 69 | ||
diff --git a/firmware/export/powermgmt.h b/firmware/export/powermgmt.h index 922630097a..59f9465d49 100644 --- a/firmware/export/powermgmt.h +++ b/firmware/export/powermgmt.h | |||
@@ -48,13 +48,12 @@ | |||
48 | #define BATTERY_RANGE (BATTERY_LEVEL_FULL - BATTERY_LEVEL_EMPTY) | 48 | #define BATTERY_RANGE (BATTERY_LEVEL_FULL - BATTERY_LEVEL_EMPTY) |
49 | 49 | ||
50 | #define POWER_HISTORY_LEN 2*60 /* 2 hours of samples, one per minute */ | 50 | #define POWER_HISTORY_LEN 2*60 /* 2 hours of samples, one per minute */ |
51 | #define POWER_AVG_N 4 /* how many samples to take for each measurement */ | ||
52 | #define POWER_AVG_SLEEP 9 /* how long do we sleep between each measurement */ | ||
53 | 51 | ||
54 | #define CHARGE_END_NEGD 6 /* stop when N minutes have passed with | 52 | #define CHARGE_END_NEGD 6 /* stop when N minutes have passed with |
55 | * avg delta being < -0.05 V */ | 53 | * avg delta being < -0.05 V */ |
56 | #define CHARGE_END_ZEROD 50 /* stop when N minutes have passed with | 54 | #define CHARGE_END_ZEROD 50 /* stop when N minutes have passed with |
57 | * avg delta being < 0.005 V */ | 55 | * avg delta being < 0.005 V */ |
56 | |||
58 | #ifndef SIMULATOR | 57 | #ifndef SIMULATOR |
59 | 58 | ||
60 | #ifdef HAVE_CHARGE_CTRL | 59 | #ifdef HAVE_CHARGE_CTRL |
@@ -65,27 +64,40 @@ | |||
65 | #define CHARGE_RESTART_HI 85 /* %: when to restart charging in 'charge' mode */ | 64 | #define CHARGE_RESTART_HI 85 /* %: when to restart charging in 'charge' mode */ |
66 | /* attention: if set too high, normal charging is started in trickle mode */ | 65 | /* attention: if set too high, normal charging is started in trickle mode */ |
67 | #define CHARGE_RESTART_LO 10 /* %: when to restart charging in 'discharge' mode */ | 66 | #define CHARGE_RESTART_LO 10 /* %: when to restart charging in 'discharge' mode */ |
68 | #define CHARGE_PAUSE_LEN 60 /* how many minutes to pause between charging cycles */ | ||
69 | #define TOPOFF_MAX_TIME 90 /* After charging, go to top off charge. How long should top off charge be? */ | 67 | #define TOPOFF_MAX_TIME 90 /* After charging, go to top off charge. How long should top off charge be? */ |
70 | #define TOPOFF_VOLTAGE 565 /* which voltage is best? (centivolts) */ | 68 | #define TOPOFF_VOLTAGE 565 /* which voltage is best? (centivolts) */ |
71 | #define TRICKLE_MAX_TIME 12*60 /* After top off charge, go to trickle charge. How long should trickle charge be? */ | 69 | #define TRICKLE_MAX_TIME 12*60 /* After top off charge, go to trickle charge. How long should trickle charge be? */ |
72 | #define TRICKLE_VOLTAGE 545 /* which voltage is best? (centivolts) */ | 70 | #define TRICKLE_VOLTAGE 545 /* which voltage is best? (centivolts) */ |
73 | 71 | ||
72 | #define START_TOPOFF_SEC 25 /* initial trickle_sec for topoff */ | ||
73 | #define START_TRICKLE_SEC 15 /* initial trickle_sec for trickle */ | ||
74 | |||
74 | extern char power_message[POWER_MESSAGE_LEN]; | 75 | extern char power_message[POWER_MESSAGE_LEN]; |
75 | extern char charge_restart_level; | 76 | |
77 | extern int long_delta; /* long term delta battery voltage */ | ||
78 | extern int short_delta; /* short term delta battery voltage */ | ||
76 | 79 | ||
77 | extern int powermgmt_last_cycle_startstop_min; /* how many minutes ago was the charging started or stopped? */ | 80 | extern int powermgmt_last_cycle_startstop_min; /* how many minutes ago was the charging started or stopped? */ |
78 | extern int powermgmt_last_cycle_level; /* which level had the batteries at this time? */ | 81 | extern int powermgmt_last_cycle_level; /* which level had the batteries at this time? */ |
79 | 82 | ||
80 | extern int battery_lazyness[20]; /* how does the battery react when plugging in/out the charger */ | 83 | void enable_deep_discharge(bool on); /* deep discharge the battery */ |
84 | |||
81 | void enable_trickle_charge(bool on); | 85 | void enable_trickle_charge(bool on); |
82 | extern int trickle_sec; /* trickle charge: How many seconds per minute are we charging actually? */ | 86 | extern int trickle_sec; /* trickle charge: How many seconds per minute are we charging actually? */ |
83 | 87 | ||
84 | #endif /* HAVE_CHARGE_CTRL */ | 88 | #endif /* HAVE_CHARGE_CTRL */ |
85 | 89 | ||
86 | #if defined(HAVE_CHARGE_CTRL) || CONFIG_BATTERY == BATT_LIION2200 | 90 | #if defined(HAVE_CHARGE_CTRL) || (CONFIG_BATTERY == BATT_LIION2200) |
87 | extern int charge_state; /* tells what the charger is doing (for info display): 0: decharging/charger off, 1: charge, 2: top-off, 3: trickle */ | 91 | typedef enum { |
88 | #endif | 92 | DISCHARGING, |
93 | CHARGING, | ||
94 | TOPOFF, | ||
95 | TRICKLE | ||
96 | } charge_state_type; | ||
97 | |||
98 | /* tells what the charger is doing */ | ||
99 | extern charge_state_type charge_state; | ||
100 | #endif /* defined(HAVE_CHARGE_CTRL) || (CONFIG_BATTERY == BATT_LIION2200) */ | ||
89 | 101 | ||
90 | #ifdef HAVE_MMC /* Values for Ondio */ | 102 | #ifdef HAVE_MMC /* Values for Ondio */ |
91 | #define CURRENT_NORMAL 95 /* average, nearly proportional to 1/U */ | 103 | #define CURRENT_NORMAL 95 /* average, nearly proportional to 1/U */ |
@@ -95,9 +107,14 @@ extern int charge_state; /* tells what the charger is doing (for info di | |||
95 | #define CURRENT_NORMAL 145 /* usual current in mA when using the AJB including some disk/backlight/... activity */ | 107 | #define CURRENT_NORMAL 145 /* usual current in mA when using the AJB including some disk/backlight/... activity */ |
96 | #define CURRENT_USB 500 /* usual current in mA in USB mode */ | 108 | #define CURRENT_USB 500 /* usual current in mA in USB mode */ |
97 | #define CURRENT_BACKLIGHT 30 /* additional current when backlight is always on */ | 109 | #define CURRENT_BACKLIGHT 30 /* additional current when backlight is always on */ |
98 | #define CURRENT_CHARGING 300 /* charging current */ | ||
99 | #endif | ||
100 | 110 | ||
111 | #define CURRENT_MIN_CHG 70 /* minimum charge current */ | ||
112 | #define MIN_CHG_V 8500 /* at 8.5v charger voltage get CURRENT_MIN_CHG */ | ||
113 | #define CURRENT_MAX_CHG 350 /* maximum charging current */ | ||
114 | #define MAX_CHG_V 10250 /* anything over 10.25v gives CURRENT_MAX_CHG */ | ||
115 | #endif /* HAVE_MMC */ | ||
116 | |||
117 | extern unsigned int bat; /* filtered battery voltage, centivolts */ | ||
101 | extern unsigned short power_history[POWER_HISTORY_LEN]; | 118 | extern unsigned short power_history[POWER_HISTORY_LEN]; |
102 | 119 | ||
103 | /* Start up power management thread */ | 120 | /* Start up power management thread */ |
@@ -114,7 +131,7 @@ bool battery_level_safe(void); | |||
114 | 131 | ||
115 | void set_poweroff_timeout(int timeout); | 132 | void set_poweroff_timeout(int timeout); |
116 | void set_battery_capacity(int capacity); /* set local battery capacity value */ | 133 | void set_battery_capacity(int capacity); /* set local battery capacity value */ |
117 | void set_battery_type(int type); /* set local battery type */ | 134 | void set_battery_type(int type); /* set local battery type */ |
118 | 135 | ||
119 | void set_sleep_timer(int seconds); | 136 | void set_sleep_timer(int seconds); |
120 | int get_sleep_timer(void); | 137 | int get_sleep_timer(void); |
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 |