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