Initial import
[avr_bc100.git] / BaseTinyFirmware / GCC / chargefunc.c
1 /* This file has been prepared for Doxygen automatic documentation generation.*/\r
2 /*! \file *********************************************************************\r
3  *\r
4  * \brief\r
5  *      Charge functions\r
6  *\r
7  *      Contains the functions for charging with constant current and voltage,\r
8  *      and for deciding when to halt.\r
9  *\r
10  * \par Application note:\r
11  *      AVR458: Charging Li-Ion Batteries with BC100 \n\r
12  *      AVR463: Charging NiMH Batteries with BC100\r
13  *\r
14  * \par Documentation\r
15  *      For comprehensive code documentation, supported compilers, compiler \r
16  *      settings and supported devices see readme.html\r
17  *\r
18  * \author\r
19  *      Atmel Corporation: http://www.atmel.com \n\r
20  *      Support email: avr@atmel.com\r
21  *\r
22  * \r
23  * $Name$\r
24  * $Revision: 2299 $\r
25  * $RCSfile$\r
26  * $URL: http://svn.norway.atmel.com/AppsAVR8/avr458_Charging_Li-Ion_Batteries_with_BC100/tag/20070904_release_1.0/code/IAR/chargefunc.c $\r
27  * $Date: 2007-08-23 12:55:51 +0200 (to, 23 aug 2007) $\n\r
28  ******************************************************************************/\r
29 \r
30 #include <avr/io.h>\r
31 \r
32 #include "enums.h"\r
33 #include "structs.h"\r
34 \r
35 #include "ADC.h"\r
36 #include "battery.h"\r
37 #include "chargefunc.h"\r
38 #include "main.h"\r
39 #include "menu.h"\r
40 #include "PWM.h"\r
41 #include "statefunc.h"\r
42 #include "time.h"\r
43 \r
44 #ifdef NIMH\r
45 #include "NIMHspecs.h"\r
46 #endif // NIMH\r
47 \r
48 #ifdef LIION\r
49 #include "LIIONspecs.h"\r
50 #endif // LIION\r
51 \r
52 \r
53 //******************************************************************************\r
54 // Variables\r
55 //******************************************************************************\r
56 //! Struct that holds parameters for ConstantCurrent() and ConstantVoltage().\r
57 ChargeParameters_t ChargeParameters;\r
58 \r
59 //! Struct that holds parameters for HaltNow().\r
60 HaltParameters_t HaltParameters;\r
61 \r
62 \r
63 //******************************************************************************\r
64 // Functions\r
65 //******************************************************************************\r
66 /*! \brief Charges battery with a constant current.\r
67  *\r
68  * This function applies a constant current (set in ChargeParameters.Current)\r
69  * to the battery until HaltNow() returns TRUE, or a PWM error occurs and\r
70  * \ref ABORT_IF_PWM_MIN or \ref ABORT_IF_PWM_MAX is defined.\n\r
71  * The charge current can vary with +/- \ref BAT_CURRENT_HYST.\n\r
72  * If the Master inhibits charging, timers are stopped and PWM output dropped.\r
73  * Once the battery is no longer flagged for charge inhibit, timers are\r
74  * started again and charging resumed.\r
75  *\r
76  * \retval ChargeParameters.NextState Next state once this stage is done.\r
77  * If no errors occured, this will be whatever was set in Charge(). Otherwise,\r
78  * HaltNow() will have set a new next state.\r
79  */\r
80 unsigned char ConstantCurrent(void)\r
81 {\r
82         unsigned char error = FALSE,\r
83                       wasStopped = FALSE;\r
84         \r
85         do      {\r
86       // Wait for ADC conversions to complete.\r
87                 ADC_Wait();\r
88 \r
89                 // If Master has flagged for a charge inhibit, pause charging.\r
90                 // (This is to prevent damage during prolonged serial communication.)\r
91                 if (eeprom_read_byte(&BattControl[BattActive]) & BIT_BATTERY_CHARGE_INHIBIT) {\r
92                         wasStopped = TRUE;\r
93                         Time_Stop();\r
94                         OCR1B = 0;\r
95                 } else {\r
96                         // Continue charging!\r
97                         if (wasStopped) {\r
98                                 wasStopped = FALSE;\r
99                                 \r
100                                 // Timer variables are not reset by this.\r
101                                 Time_Start();\r
102                         }\r
103          \r
104                         // Adjust the charge current to within ChargeParameters.Current\r
105                         // +/- BAT_CURRENT_HYST.\r
106                         if ((ADCS.avgIBAT < 0) ||\r
107                             (ADCS.avgIBAT < (ChargeParameters.Current - BAT_CURRENT_HYST))) {\r
108                                          \r
109                                 if(!PWM_IncrementDutyCycle()) {\r
110 #ifdef ABORT_IF_PWM_MAX\r
111                                 // If the duty cycle cannot be incremented, flag error and\r
112                                 // go to error state.\r
113                                         SetErrorFlag(ERR_PWM_CONTROL);\r
114                                         ChargeParameters.NextState = ST_ERROR;\r
115                                         error = TRUE;\r
116 #endif\r
117                                 }\r
118                         } else if ((ADCS.avgIBAT >= 0) &&\r
119                                  (ADCS.avgIBAT > (ChargeParameters.Current + BAT_CURRENT_HYST))) {\r
120                                          \r
121                                 if(!PWM_DecrementDutyCycle()) {\r
122 #ifdef ABORT_IF_PWM_MIN\r
123                                         // If the duty cycle cannot be decremented, flag error and\r
124                                         // go to error state.\r
125                                         SetErrorFlag(ERR_PWM_CONTROL);\r
126                                         ChargeParameters.NextState = ST_ERROR;\r
127                                         error = TRUE;\r
128 #endif\r
129                                 }\r
130                         }\r
131                 }\r
132         } while (!HaltNow() && !error);\r
133 \r
134         // Return the next state to Charge(). If an error has occured, this will\r
135         // point to some other state than the next state of charging.\r
136         return(ChargeParameters.NextState);\r
137 }\r
138 \r
139 \r
140 /*! \brief Charges battery with a constant voltage\r
141  *\r
142  * This function applies a constant voltage (set in ChargeParameters.Voltage)\r
143  * to the battery until HaltNow() returns TRUE, or a PWM error occurs and\r
144  * \ref ABORT_IF_PWM_MIN or \ref ABORT_IF_PWM_MAX is defined.\n\r
145  * The charge voltage can vary with +/- \ref BAT_VOLTAGE_HYST.\n\r
146  * If the Master inhibits charging, timers are stopped and PWM output dropped.\r
147  * Once the battery is no longer flagged for charge inhibit, timers are\r
148  * started again and charging resumed.\r
149  *\r
150  * \retval ChargeParameters.NextState Next state once this stage is done.\r
151  * If no errors occured, this will be whatever was set in Charge(). Otherwise,\r
152  * HaltNow() will have set a new next state.\r
153  */\r
154 unsigned char ConstantVoltage(void)\r
155 {\r
156         unsigned char error = FALSE,\r
157                       wasStopped = FALSE;\r
158         \r
159         do{\r
160                 \r
161                 // Wait for ADC conversions to complete.\r
162                 ADC_Wait();\r
163                 \r
164                 // If Master has flagged for a charge inhibit, pause charging.\r
165                 // (This is to prevent damage during prolonged serial communication.)\r
166                 if (eeprom_read_byte(&BattControl[BattActive]) & BIT_BATTERY_CHARGE_INHIBIT) {\r
167                         wasStopped = TRUE;\r
168                         Time_Stop();\r
169                         OCR1B = 0;\r
170                 }\r
171                 \r
172                 else {\r
173                         // Continue charging!\r
174                         if (wasStopped) {\r
175                                 wasStopped = FALSE;\r
176                                 \r
177                                 // Timer variables aren't reset by this.\r
178                                 Time_Start();\r
179                         }\r
180                         \r
181                         // Adjust the charge voltage to within ChargeParameters.Voltage\r
182                         // +/- BAT_VOLTAGE_HYST.\r
183                         if (ADCS.VBAT < (ChargeParameters.Voltage - BAT_VOLTAGE_HYST)) {\r
184 \r
185                                 if(!PWM_IncrementDutyCycle()) {\r
186 #ifdef ABORT_IF_PWM_MAX\r
187                                 // Flag PWM control error and go to error-state if the duty\r
188                                 // cycle cannot be incremented.\r
189                                         SetErrorFlag(ERR_PWM_CONTROL);\r
190                                         ChargeParameters.NextState = ST_ERROR;\r
191                                         error = TRUE;\r
192 #endif\r
193                                 }\r
194                         } else if (ADCS.VBAT > (ChargeParameters.Voltage + BAT_VOLTAGE_HYST)) {\r
195 \r
196                                 if(!PWM_DecrementDutyCycle()) {\r
197 #ifdef ABORT_IF_PWM_MIN\r
198                                         // Flag PWM control error and go to error-state if duty\r
199                                         // cycle cannot be decremented.\r
200                                         SetErrorFlag(ERR_PWM_CONTROL);\r
201                                         ChargeParameters.NextState = ST_ERROR;\r
202                                         error = TRUE;\r
203 #endif\r
204                                 }\r
205                         }\r
206                 }\r
207 \r
208         } while (!HaltNow() && !error);\r
209 \r
210         // Return the next state to Charge(). If an error has occured, this will\r
211         // point to some other state than the next state of charging.\r
212         return(ChargeParameters.NextState);\r
213 }\r
214 \r
215 \r
216 /*! \brief Determines when to halt charging.\r
217  *\r
218  * This function evaluates parameters depending on what has been flagged in\r
219  * HaltParameters.HaltFlags, and returns TRUE or FALSE if the charging should\r
220  * halt or not.\n\r
221  * In addition, error flagging on timeout (battery exhaustion) can be set.\n\r
222  *\r
223  * The function also checks if the battery temperature is within limits,\r
224  * if mains is OK, and if BatteryCheck() returns TRUE.\r
225  * If an error is detected, the associated errorflag is set and \r
226  * ChargeParameters.NextState is changed to an appropriate state.\r
227  *\r
228  * \retval TRUE Halt now.\r
229  * \retval FALSE Don't halt now.\r
230  *\r
231  * \note See chargefunc.h for definitions of halt flags.\r
232  * \note It is generally a bad idea not to halt on a timeout.\r
233  * \note If HALT_ON_VOLTAGE_DROP is set, HaltParameters.VBATMax should be\r
234  * reset in Charge() before calling a charging-function.\r
235  *\r
236  * \todo "Priorities" of standard error checks OK?\r
237  */\r
238 unsigned char HaltNow(void)\r
239 {\r
240         unsigned char i, halt = FALSE;\r
241         \r
242         // Wait for a full ADC-cycle to finish.\r
243         ADC_Wait();\r
244 \r
245         // Evaluate ADC readings according to HaltFlags. Flag errors if selected.\r
246         // If an error is flagged, ChargeParameters.NextState is set to ST_ERROR.\r
247         // (Gets overridden if either mains is failing, or the battery changes.)\r
248         for (i = 0x01; i != 0; i <<= 1) {\r
249                 if (HaltParameters.HaltFlags & i) {\r
250                         switch (i) {\r
251                         // Is VBAT less than the recorded maximum?\r
252                         case HALT_VOLTAGE_DROP:\r
253 \r
254                                 // Update VBATMax if VBAT is higher. Evaluate for halt otherwise.\r
255                                 if (ADCS.VBAT > HaltParameters.VBATMax) {\r
256                                         HaltParameters.VBATMax = ADCS.VBAT;\r
257                                 } else if((HaltParameters.VBATMax - ADCS.VBAT) >= \r
258                                           HaltParameters.VoltageDrop) {\r
259                                         halt = TRUE;\r
260                                 }\r
261                         break;\r
262 \r
263                         \r
264                         // Has VBAT reached the maximum limit?\r
265                         case HALT_VOLTAGE_MAX:  \r
266                                 \r
267                                 if (ADCS.VBAT >= HaltParameters.VoltageMax) {\r
268                                         halt = TRUE;\r
269                                 }\r
270                                 break;\r
271 \r
272 \r
273                         // Has IBAT reached the minimum limit?\r
274                         case HALT_CURRENT_MIN:\r
275                                 \r
276                                 if (ADCS.avgIBAT <= HaltParameters.CurrentMin) {\r
277                                         halt = TRUE;\r
278                                 }\r
279                                 break;\r
280         \r
281                                 \r
282                         // Is the temperature rising too fast?\r
283                         case HALT_TEMPERATURE_RISE:\r
284 \r
285                                 // If rawNTC has increased, the temperature has dropped.\r
286                                 // We can store this value for now, and start the timer.\r
287                                 // Otherwise, check if NTC has changed too fast.\r
288                                 if (ADCS.rawNTC > HaltParameters.LastNTC) {\r
289                                         HaltParameters.LastNTC = ADCS.rawNTC;\r
290                                         Time_Set(TIMER_TEMP,0,30,0);\r
291                                         \r
292                                 // Is the increase in temperature greater than the set threshold?\r
293                                 } else  if ((HaltParameters.LastNTC - ADCS.rawNTC) >=\r
294                                   (BattData.ADCSteps * HaltParameters.TemperatureRise)) {\r
295                                         \r
296                                         // If this happened within a timeframe of 30 seconds, the \r
297                                         // temperature is rising faster than we want.\r
298                                         // If not, update LastNTC and reset timer.\r
299                                         if (Time_Left(TIMER_TEMP))  {\r
300                                                 halt = TRUE;\r
301                                         } else {\r
302                                                 HaltParameters.LastNTC = ADCS.rawNTC;\r
303                                                 Time_Set(TIMER_TEMP,0,30,0);\r
304                                         }\r
305                                 }\r
306                         break;\r
307         \r
308                         \r
309                         // Is there any time left?\r
310                         case HALT_TIME:  \r
311                                 \r
312                                 if (!Time_Left(TIMER_CHG)) {\r
313                                         halt = TRUE;\r
314                                         \r
315                                         // If exhaustion flagging is selected, stop the PWM, disable the \r
316                                         // battery and flag it as exhausted. Make ST_ERROR next state.\r
317                                         if (HaltParameters.HaltFlags & HALT_FLAG_EXHAUSTION) {\r
318                                                         PWM_Stop();\r
319                                                         Battery_t tmp = eeprom_read_byte(&BattControl[BattActive]);\r
320                                                         tmp &= ~BIT_BATTERY_ENABLED; // Enabled = FALSE;\r
321                                                         eeprom_write_byte(&BattControl[BattActive], tmp);\r
322                                                         BattData.Exhausted = TRUE;\r
323                                                         SetErrorFlag(ERR_BATTERY_EXHAUSTED);\r
324                                                         ChargeParameters.NextState = ST_ERROR;\r
325                                         }\r
326                                 }\r
327                         break;\r
328                         \r
329                         \r
330                         default:  // Shouldn't end up here, but is needed for MISRA compliance.\r
331                         break;\r
332                         }\r
333                 }\r
334         }\r
335 \r
336         // Standard checks:\r
337 \r
338         // Battery too cold or hot?\r
339         if ((BattData.Temperature <= HaltParameters.TemperatureMin) ||\r
340             (BattData.Temperature >= HaltParameters.TemperatureMax)) {\r
341                         \r
342                 PWM_Stop();\r
343                 SetErrorFlag(ERR_BATTERY_TEMPERATURE);\r
344                 ChargeParameters.NextState = ST_ERROR;\r
345                 halt = TRUE;\r
346         }\r
347 \r
348         // Battery not OK?\r
349         if (!BatteryCheck()) {\r
350                 PWM_Stop();\r
351                 ChargeParameters.NextState = ST_INIT;\r
352                 halt = TRUE;\r
353         }\r
354 \r
355         // Is mains voltage OK?\r
356         if (!ADCS.Mains) {\r
357                 PWM_Stop();\r
358                 ChargeParameters.NextState = ST_SLEEP;\r
359                 halt = TRUE;\r
360         }\r
361 \r
362         return(halt);\r
363 }\r