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