2008-03-14 Martin Thomas <mthomas@rhrk.uni-kl.de>
[avr_bc100.git] / BaseTinyFirmware / GCC / battery.c
1 /* This file has been prepared for Doxygen automatic documentation generation.*/\r
2 /*! \file *********************************************************************\r
3  *\r
4  * \brief\r
5  *      Functions related to battery control and data acquisition.\r
6  *\r
7  *      Contains functions for enabling/disabling batteries, and looking up\r
8  *      their status and specifications using the ADC.\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  *\r
15  * \par Documentation\r
16  *      For comprehensive code documentation, supported compilers, compiler \r
17  *      settings and supported devices see readme.html\r
18  *\r
19  * \author\r
20  *      Atmel Corporation: http://www.atmel.com \n\r
21  *      Support email: avr@atmel.com\r
22  *\r
23  * \r
24  * $Name$\r
25  * $Revision: 2299 $\r
26  * $RCSfile$\r
27  * $URL: http://svn.norway.atmel.com/AppsAVR8/avr458_Charging_Li-Ion_Batteries_with_BC100/tag/20070904_release_1.0/code/IAR/battery.c $\r
28  * $Date: 2007-08-23 12:55:51 +0200 (to, 23 aug 2007) $\n\r
29  ******************************************************************************/\r
30 \r
31 #include <avr/io.h>\r
32 #include <avr/eeprom.h>\r
33 #include <avr/interrupt.h>\r
34 \r
35 #include "structs.h"\r
36 #include "enums.h"\r
37 \r
38 #include "ADC.h"\r
39 #include "battery.h"\r
40 #include "main.h"\r
41 #include "OWI.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 //******************************************************************************\r
55 // Variables\r
56 //******************************************************************************\r
57 /* Control-struct for batteries */\r
58 /*! \brief Holds control data for both batteries\r
59  * \note Stored in EEPROM.\r
60  */\r
61 \r
62 Battery_t EEMEM BattControl[2] = {(BIT_BATTERY_ENABLED | BIT_BATTERY_DISCONNECT_ALLOWED | BIT_BATTERY_CHARGE_INHIBIT),\r
63                                   (BIT_BATTERY_ENABLED | BIT_BATTERY_DISCONNECT_ALLOWED | BIT_BATTERY_CHARGE_INHIBIT),\r
64 };\r
65 \r
66 /* Data-struct for battery */\r
67 Batteries_t BattData; //!< Holds data for the current battery\r
68 \r
69 \r
70 /* Storage for battery EPROM */\r
71 /*! \brief Storage space for data from the batteries' own EPROMs.\r
72  * \note Stored in EEPROM.\r
73  */\r
74 unsigned char EEMEM BattEEPROM[4][32];\r
75 \r
76 \r
77 //! Global that indicates current battery (0 = battery A, 1 = B)\r
78 unsigned char BattActive;\r
79 \r
80 \r
81 /*! \brief RID lookup-table\r
82  *\r
83  * Contains Resistor ID data specified by manufacturer.\r
84  *\r
85  * \note Values have been calculated assuming a +/- 15% tolerance.\r
86  */\r
87 const RID_Lookup_t RID[RID_TABLE_SIZE] = {\r
88   {558, 659, 3900, 550, 260, 300, 10},\r
89   {744, 843, 6800, 750, 360, 300, 14},\r
90   {869, 958, 10000, 1000, 475, 300, 19},\r
91   {1097, 1153, 24000, 2000, 475, 420, 38}\r
92 };\r
93 \r
94 \r
95 /*! \brief NTC lookup-table\r
96  *\r
97  * The first entry is 0 degrees. For every entry after, temperature increases\r
98  * with 4 degrees. With 20 entries, the last one equals (20-1)*4 = 76 degrees.\n\r
99  * It must be sorted in descending ADC order.\r
100  *\r
101  * \note This was calculated for a Mitsubishi RH16-3H103FB NTC.\r
102  *\r
103  * \note NTCLookUp() must be modified if this table is changed so that:\r
104  * - first entry is no longer 0 degrees.\r
105  * - temperature difference between entries is no longer 4 degrees.\r
106  * - ADCsteps no longer specifies ADC steps per half degree.\r
107  */\r
108 // FOR VARTA POLYFLEX NTC\r
109 const NTC_Lookup_t NTC[NTC_TABLE_SIZE] = {\r
110         {1002, 23}, {953, 25}, {902, 26}, {849, 27}, {796, 27},\r
111         {742, 27}, {689, 26}, {637, 26}, {587, 25}, {539, 24},\r
112         {494, 22}, {451, 21}, {412, 19}, {375, 18}, {341, 17},\r
113         {310, 15}, {282, 14}, {256, 13}, {233, 11}, {212, 10}\r
114 };\r
115 \r
116 \r
117 // FOR MITSUBISHI NTC\r
118 /*\r
119 const NTC_Lookup_t NTC[NTC_TABLE_SIZE] = {\r
120   {1004, 24}, {954, 25}, {903, 26}, {850, 27}, {796, 27},\r
121   {742, 27}, {689, 27}, {637, 26}, {587, 25}, {539, 24},\r
122   {493, 22}, {451, 21}, {411, 20}, {374, 18}, {340, 17},\r
123   {309, 15}, {281, 14}, {255, 13}, {232, 11}, {211, 10}\r
124 };\r
125 */\r
126 \r
127  \r
128 //******************************************************************************\r
129 // Functions\r
130 //******************************************************************************\r
131 /*! \brief Checks if battery has changed\r
132  *\r
133  * Stores current capacity, then attempts to refresh battery status.\n\r
134  * If the refresh is successful, old capacity is compared with the new one.\r
135  * \r
136  * \retval FALSE Battery is disconnected, or capacity has changed.\r
137  * \retval TRUE All OK.\r
138  */\r
139 unsigned char BatteryCheck(void)\r
140 {\r
141         unsigned char success = TRUE;\r
142         unsigned int  oldCapacity;\r
143         \r
144         // Save to see if battery data has changed.\r
145         oldCapacity = BattData.Capacity;  \r
146         \r
147         if (!BatteryStatusRefresh()) {\r
148                 success = FALSE;              // Battery not present or RID was invalid.\r
149         }\r
150         \r
151         if (oldCapacity != BattData.Capacity) {\r
152                 success = FALSE;              // Battery configuration has changed.\r
153         }\r
154 \r
155         return(success);\r
156 }\r
157 \r
158 \r
159 /*! \brief Refreshes battery status information\r
160  *\r
161  * Refreshes battery status information, if it is present, based on\r
162  * RID and NTC (read by ADC).\n\r
163  * The battery must have been enabled and a complete set of ADC data must have\r
164  * been collected before calling.\n\r
165  *\r
166  * \retval FALSE No battery present.\r
167  * \retval TRUE Battery present, status refreshed.\r
168  *\r
169  * \note If ALLOW_NO_RID is defined, charging will NOT stop if no fitting entry\r
170  * is found in the lookup table. Instead, default battery data will be used.\r
171  */\r
172 unsigned char BatteryStatusRefresh(void)\r
173 {\r
174         // Assume the worst..\r
175         unsigned char success = FALSE;\r
176         unsigned char sreg_saved;\r
177         unsigned int adcs_VBAT_tmp;\r
178         signed int adcs_avgIBAT_tmp;\r
179         \r
180         BattData.Present = FALSE;\r
181         BattData.Charged = FALSE;\r
182         BattData.Low = TRUE;\r
183         BattData.Circuit = OW_NONE;\r
184         BattData.Temperature = 0;\r
185         BattData.Capacity = 0;\r
186         BattData.MaxCurrent = 0;\r
187         BattData.MaxTime = 0;\r
188         BattData.MinCurrent = 0;\r
189 \r
190         NTCLookUp();\r
191         BattData.HasRID = RIDLookUp();\r
192 \r
193         sreg_saved = SREG;\r
194         cli();\r
195         adcs_VBAT_tmp = ADCS.VBAT;\r
196         adcs_avgIBAT_tmp = ADCS.avgIBAT;\r
197         SREG = sreg_saved;\r
198 \r
199         // Is the battery voltage above minimum safe cell voltage?\r
200         if (adcs_VBAT_tmp >= BAT_VOLTAGE_MIN) {\r
201                 BattData.Low = FALSE;\r
202         }\r
203 \r
204         // Is the battery charged?\r
205         if (adcs_VBAT_tmp >= BAT_VOLTAGE_LOW) {\r
206                 BattData.Charged = TRUE;\r
207         }\r
208 \r
209         // If we are not charging, yet VBAT is above safe limit, battery is present.\r
210         // If we are charging and there's a current flowing, the battery is present.\r
211         \r
212         /*! \todo If ABORT_IF_PWM_MAX is defined this last check battery presence\r
213         * check is redundant since charging will be aborted due to low current at\r
214         * max duty cycle. That is preferrable since the charge current reading is\r
215         * not 100% proof.\r
216         */\r
217         if (((OCR1B == 0) && (!BattData.Low)) ||\r
218             ((OCR1B != 0) && (adcs_avgIBAT_tmp > 0))) {\r
219                 BattData.Present = TRUE;\r
220                 success = TRUE;\r
221         } else {\r
222                 BattData.Low = FALSE;  // (This is just a technicality..)\r
223                 success = FALSE;\r
224         }\r
225 \r
226 #ifndef ALLOW_NO_RID\r
227         // Return FALSE if no valid RID entry was found, to stop charging.\r
228         if(!BattData.HasRID) {\r
229                 success = FALSE;\r
230         }\r
231 #endif\r
232 \r
233         return(success);\r
234 }\r
235 \r
236 \r
237 /*! \brief Refreshes battery data in the EEPROM\r
238  *\r
239  * Attempts to read 4 pages of 32 bytes each from the battery's EPROM and store\r
240  * these data in on-chip EEPROM.\n\r
241  * If unsuccessful (CRC doesn't check out), the on-chip EEPROM is cleared.\r
242  *\r
243  * \todo Updating BattData with these data. Specs needed.\r
244  *\r
245  * \retval FALSE Refresh failed.\r
246  * \retval TRUE Refresh successful.\r
247  */\r
248 unsigned char BatteryDataRefresh(void)\r
249 {\r
250         unsigned char offset;\r
251         unsigned char i, crc, family, temp, page;\r
252         unsigned char success;\r
253         \r
254         // Look for EPROM and read 4 pages of 32 bytes each worth of data, if found.\r
255         for (page = 0; page < 4; page++)        {\r
256                 success = FALSE;\r
257         \r
258                 if (OWI_DetectPresence(OWIBUS) == OWIBUS) {\r
259                         \r
260                         // Presence detected, check type and CRC.\r
261                         OWI_SendByte(OWI_ROM_READ, OWIBUS);\r
262                         family = OWI_ReceiveByte(OWIBUS);\r
263                         crc = OWI_ComputeCRC8(family,0);\r
264 \r
265                         for (i = 0; i < 6; i++) {\r
266                                 crc = OWI_ComputeCRC8(OWI_ReceiveByte(OWIBUS),crc);\r
267                         }\r
268 \r
269                         // CRC ok, device found.\r
270                         if (OWI_ComputeCRC8(OWI_ReceiveByte(OWIBUS),crc) == 0) {\r
271                                 BattData.Circuit = family;\r
272 \r
273                                 // For now, we only read data from DS2505 EPROMs.\r
274                                 if (BattData.Circuit == OW_DS2505) {\r
275                                         offset = page*32;\r
276                                         OWI_SendByte(DS2505_DATA_READ, OWIBUS);  // Command: read data.\r
277                                         OWI_SendByte(offset, OWIBUS);            // Data: low address.\r
278                                         OWI_SendByte(0, OWIBUS);                 // Data: high address.\r
279 \r
280                                         // Calculate checksums.\r
281                                         crc = OWI_ComputeCRC8(DS2505_DATA_READ,0);\r
282                                         crc = OWI_ComputeCRC8(offset,crc);\r
283                                         crc = OWI_ComputeCRC8(0,crc);\r
284 \r
285                                         // Command received succesfully, now start reading data\r
286                                         // and writing it to EEPROM.\r
287                                         if (OWI_ComputeCRC8(OWI_ReceiveByte(OWIBUS),crc) == 0) {\r
288                                                 crc = 0;\r
289                                                 \r
290                                                 // Fill page with data.\r
291                                                 for (i=0; i<32; i++) {\r
292                                                         temp = OWI_ReceiveByte(OWIBUS);\r
293                                                         crc = OWI_ComputeCRC8(temp, crc);\r
294                                                         eeprom_write_byte(&BattEEPROM[page][i], temp);\r
295                                                 }\r
296                                                 \r
297                                                 if (OWI_ComputeCRC8(OWI_ReceiveByte(OWIBUS),crc) == 0) {\r
298                                                                 success = TRUE;  // Data read OK\r
299                                                 }\r
300                                         } else { // Not able to start reading data.\r
301                                         }\r
302                                 } else { // Wrong device type.\r
303                                 }\r
304                         } else { // No device found.             \r
305                         }\r
306                 } else { // No presence detected on one-wire bus.\r
307                 }\r
308 \r
309           // Erase local EEPROM page if there were any errors during transfer.\r
310                 if (!success) {\r
311                         for (i=0; i<32; i++) {\r
312                                 eeprom_write_byte(&BattEEPROM[page][i], 0);\r
313                         }\r
314                 }\r
315         }\r
316 \r
317         return(success);\r
318 }\r
319 \r
320 \r
321 /*! \brief Enables specified battery\r
322  *\r
323  * Updates \ref BattActive to specified battery, then sets PB4/PB5 and clears\r
324  * PB5/PB4 in PORTB, depending on which battery is specified.\n\r
325  * The function takes 100 ms to allow the port switch to settle.\r
326  *\r
327  * \param bat Specifies which battery to enable (0 = battery A, 1 = B)\r
328  */\r
329 void EnableBattery(unsigned char bat)\r
330 {\r
331         // Use general timer, set timeout to 100ms.\r
332         Time_Set(TIMER_GEN,0,0,100);\r
333 \r
334         // Set specified battery as the active one.\r
335         BattActive = bat;\r
336 \r
337         // Enable current battery in hardware, light LED & connect battery.\r
338         PORTB |= (1 << (PB4+bat));\r
339 \r
340         // Disconnect other battery.\r
341         PORTB &= ~(1<<(PB5-bat));     \r
342 \r
343         do { // Let port switch settle.\r
344         } while (Time_Left(TIMER_GEN));  \r
345 }\r
346 \r
347 \r
348 /*! \brief Disables both batteries\r
349  *\r
350  * Clears PB4 and PB5 in PORTB, disabling both batteries.\r
351  */\r
352 void DisableBatteries(void)\r
353 {\r
354         // Turn off LEDs and disconnect batteries.\r
355         PORTB &= ~((1<<PB4)|(1<<PB5));  \r
356 }\r
357 \r
358 \r
359 /*! \brief Looks up battery data from RID table\r
360  *\r
361  * Attempts to find data for the battery from the RID lookup-table.\n\r
362  * If no valid entry is found, default data (defined in battery.h)\r
363  * are used.\r
364  *\r
365  * \retval TRUE Entry found, battery data updated.\r
366  * \retval FALSE No entry found, using defaults for battery data.\r
367  */\r
368 unsigned char RIDLookUp (void)\r
369 {\r
370         unsigned char i, found = FALSE;\r
371         unsigned int adcs_rawRID_tmp;\r
372         unsigned char sreg_saved;\r
373                 \r
374         // Lookup in the RID-table. If measured RID is within the limits\r
375         // of an entry, those data are used, and TRUE is returned.\r
376         for (i = 0 ; i < RID_TABLE_SIZE; i++) {\r
377                 sreg_saved = SREG;\r
378                 cli();\r
379                 adcs_rawRID_tmp = ADCS.rawRID;\r
380                 SREG = sreg_saved;\r
381                 if (adcs_rawRID_tmp >= RID[i].Low) {\r
382                         if (adcs_rawRID_tmp <= RID[i].High) {\r
383                                 BattData.Capacity = RID[i].Capacity;\r
384                                 BattData.MaxCurrent = RID[i].Icharge;\r
385                                 BattData.MaxTime = RID[i].tCutOff;\r
386                                 BattData.MinCurrent = RID[i].ICutOff;\r
387                                 \r
388                                 found = TRUE;\r
389                         }\r
390                 }\r
391         }\r
392         \r
393         // If no valid entry is found, use defaults and return FALSE.\r
394         if (!found) {\r
395                 BattData.Capacity = DEF_BAT_CAPACITY;\r
396                 BattData.MaxCurrent = DEF_BAT_CURRENT_MAX;\r
397                 BattData.MaxTime = DEF_BAT_TIME_MAX;\r
398                 BattData.MinCurrent = DEF_BAT_CURRENT_MIN;\r
399         }\r
400         \r
401         return(found);\r
402 }\r
403 \r
404 \r
405 /*! \brief Calculates temperature from a lookup table\r
406  *\r
407  * Looks up the highest NTC value below or equal to the measured one.\n\r
408  * With the current lookup table, temperature is calculated with the formula:\n\r
409  * 4*(index of entry) - 2*(measured NTC - NTC from entry) / (ADCsteps of entry)\r
410  *\r
411  * \note If the NTC-measurement is saturated, with the current lookup table,\r
412  * the temperature will be reported as -1 C.\r
413  *\r
414  * \note If no valid entry is found, battery temperature is set to 80.\r
415  */\r
416 void NTCLookUp (void)\r
417 {\r
418         unsigned char i;\r
419         unsigned char found = FALSE;\r
420         unsigned char adcs_rawNTC_tmp;\r
421         unsigned char sreg_saved;\r
422         \r
423         // Lookup in the NTC-table. Use the first entry which is equal or below\r
424         // sampled NTC. Calculate temperature by using the index number, and the\r
425         // difference between the measured NTC value and the one in the entry.\r
426         for (i=0 ; (i < NTC_TABLE_SIZE) && (!found); i++)       {\r
427                 sreg_saved = SREG;\r
428                 cli();\r
429                 adcs_rawNTC_tmp = ADCS.rawNTC;\r
430                 SREG = sreg_saved;\r
431                 if (adcs_rawNTC_tmp >= NTC[i].ADCV) {\r
432                         BattData.Temperature = (i<<2) ;\r
433                         BattData.ADCSteps = NTC[i].ADCsteps;  \r
434                         BattData.Temperature -= ((adcs_rawNTC_tmp - NTC[i].ADCV)<<1) / BattData.ADCSteps;\r
435                         \r
436                         found = TRUE;  // Could be done with a break, but that violates MISRA.\r
437                 }\r
438         }\r
439         \r
440         // For safety, is temperature is greater than the NTC \r
441         if (!found) {\r
442                 BattData.Temperature = 80;\r
443         }\r
444 }\r