1 /* This file has been prepared for Doxygen automatic documentation generation.*/
\r
2 /*! \file *********************************************************************
\r
5 * Functions related to battery control and data acquisition.
\r
7 * Contains functions for enabling/disabling batteries, and looking up
\r
8 * their status and specifications using the ADC.
\r
10 * \par Application note:
\r
11 * AVR458: Charging Li-Ion Batteries with BC100 \n
\r
12 * AVR463: Charging NiMH Batteries with BC100
\r
15 * \par Documentation
\r
16 * For comprehensive code documentation, supported compilers, compiler
\r
17 * settings and supported devices see readme.html
\r
20 * Atmel Corporation: http://www.atmel.com \n
\r
21 * Support email: avr@atmel.com
\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
34 #include "structs.h"
\r
38 #include "battery.h"
\r
44 #include "NIMHspecs.h"
\r
48 #include "LIIONspecs.h"
\r
53 //******************************************************************************
\r
55 //******************************************************************************
\r
56 /* Control-struct for batteries */
\r
57 /*! \brief Holds control data for both batteries
\r
58 * \note Stored in EEPROM.
\r
60 __eeprom Battery_t BattControl[2] = {{TRUE, TRUE, FALSE},
\r
61 {TRUE, TRUE, FALSE}};
\r
63 /* Data-struct for battery */
\r
64 Batteries_t BattData; //!< Holds data for the current battery
\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
71 __eeprom unsigned char BattEEPROM[4][32];
\r
74 //! Global that indicates current battery (0 = battery A, 1 = B)
\r
75 unsigned char BattActive;
\r
78 /*! \brief RID lookup-table
\r
80 * Contains Resistor ID data specified by manufacturer.
\r
82 * \note Values have been calculated assuming a +/- 15% tolerance.
\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
92 /*! \brief NTC lookup-table
\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
98 * \note This was calculated for a Mitsubishi RH16-3H103FB NTC.
\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
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
114 // FOR MITSUBISHI NTC
\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
125 //******************************************************************************
\r
127 //******************************************************************************
\r
128 /*! \brief Checks if battery has changed
\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
133 * \retval FALSE Battery is disconnected, or capacity has changed.
\r
134 * \retval TRUE All OK.
\r
136 unsigned char BatteryCheck(void)
\r
138 unsigned char success = TRUE;
\r
139 unsigned int oldCapacity;
\r
141 // Save to see if battery data has changed.
\r
142 oldCapacity = BattData.Capacity;
\r
144 if (!BatteryStatusRefresh()) {
\r
145 success = FALSE; // Battery not present or RID was invalid.
\r
148 if (oldCapacity != BattData.Capacity) {
\r
149 success = FALSE; // Battery configuration has changed.
\r
156 /*! \brief Refreshes battery status information
\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
163 * \retval FALSE No battery present.
\r
164 * \retval TRUE Battery present, status refreshed.
\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
169 unsigned char BatteryStatusRefresh(void)
\r
171 // Assume the worst..
\r
172 unsigned char success = FALSE;
\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
185 BattData.HasRID = RIDLookUp();
\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
192 // Is the battery charged?
\r
193 if (ADCS.VBAT >= BAT_VOLTAGE_LOW) {
\r
194 BattData.Charged = TRUE;
\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
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
205 if (((OCR1B == 0) && (!BattData.Low)) ||
\r
206 ((OCR1B != 0) && (ADCS.avgIBAT > 0))) {
\r
207 BattData.Present = TRUE;
\r
210 BattData.Low = FALSE; // (This is just a technicality..)
\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
225 /*! \brief Refreshes battery data in the EEPROM
\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
231 * \todo Updating BattData with these data. Specs needed.
\r
233 * \retval FALSE Refresh failed.
\r
234 * \retval TRUE Refresh successful.
\r
236 unsigned char BatteryDataRefresh(void)
\r
238 unsigned char offset;
\r
239 unsigned char i, crc, family, temp, page;
\r
240 unsigned char success;
\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
246 if (OWI_DetectPresence(OWIBUS) == OWIBUS) {
\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
253 for (i = 0; i < 6; i++) {
\r
254 crc = OWI_ComputeCRC8(OWI_ReceiveByte(OWIBUS),crc);
\r
257 // CRC ok, device found.
\r
258 if (OWI_ComputeCRC8(OWI_ReceiveByte(OWIBUS),crc) == 0) {
\r
259 BattData.Circuit = family;
\r
261 // For now, we only read data from DS2505 EPROMs.
\r
262 if (BattData.Circuit == OW_DS2505) {
\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
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
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
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
285 if (OWI_ComputeCRC8(OWI_ReceiveByte(OWIBUS),crc) == 0) {
\r
286 success = TRUE; // Data read OK
\r
288 } else { // Not able to start reading data.
\r
290 } else { // Wrong device type.
\r
292 } else { // No device found.
\r
294 } else { // No presence detected on one-wire bus.
\r
297 // Erase local EEPROM page if there were any errors during transfer.
\r
299 for (i=0; i<32; i++) {
\r
300 // mthomas: avoid unneeded writes
\r
301 if ( eeprom_read_byte( &BattEEPROM[page][i] ) != 0 ) {
\r
302 BattEEPROM[page][i] = 0;
\r
312 /*! \brief Enables specified battery
\r
314 * Updates \ref BattActive to specified battery, then sets PB4/PB5 and clears
\r
315 * PB5/PB4 in PORTB, depending on which battery is specified.\n
\r
316 * The function takes 100 ms to allow the port switch to settle.
\r
318 * \param bat Specifies which battery to enable (0 = battery A, 1 = B)
\r
320 void EnableBattery(unsigned char bat)
\r
322 // Use general timer, set timeout to 100ms.
\r
323 Time_Set(TIMER_GEN,0,0,100);
\r
325 // Set specified battery as the active one.
\r
328 // Enable current battery in hardware, light LED & connect battery.
\r
329 PORTB |= (1 << (PB4+bat));
\r
331 // Disconnect other battery.
\r
332 PORTB &= ~(1<<(PB5-bat));
\r
334 do { // Let port switch settle.
\r
335 } while (Time_Left(TIMER_GEN));
\r
339 /*! \brief Disables both batteries
\r
341 * Clears PB4 and PB5 in PORTB, disabling both batteries.
\r
343 void DisableBatteries(void)
\r
345 // Turn off LEDs and disconnect batteries.
\r
346 PORTB &= ~((1<<PB4)|(1<<PB5));
\r
350 /*! \brief Looks up battery data from RID table
\r
352 * Attempts to find data for the battery from the RID lookup-table.\n
\r
353 * If no valid entry is found, default data (defined in battery.h)
\r
356 * \retval TRUE Entry found, battery data updated.
\r
357 * \retval FALSE No entry found, using defaults for battery data.
\r
359 unsigned char RIDLookUp (void)
\r
361 unsigned char i, found = FALSE;
\r
363 // Lookup in the RID-table. If measured RID is within the limits
\r
364 // of an entry, those data are used, and TRUE is returned.
\r
365 for (i = 0 ; i < RID_TABLE_SIZE; i++) {
\r
366 if (ADCS.rawRID >= RID[i].Low) {
\r
367 if (ADCS.rawRID <= RID[i].High) {
\r
368 BattData.Capacity = RID[i].Capacity;
\r
369 BattData.MaxCurrent = RID[i].Icharge;
\r
370 BattData.MaxTime = RID[i].tCutOff;
\r
371 BattData.MinCurrent = RID[i].ICutOff;
\r
378 // If no valid entry is found, use defaults and return FALSE.
\r
380 BattData.Capacity = DEF_BAT_CAPACITY;
\r
381 BattData.MaxCurrent = DEF_BAT_CURRENT_MAX;
\r
382 BattData.MaxTime = DEF_BAT_TIME_MAX;
\r
383 BattData.MinCurrent = DEF_BAT_CURRENT_MIN;
\r
390 /*! \brief Calculates temperature from a lookup table
\r
392 * Looks up the highest NTC value below or equal to the measured one.\n
\r
393 * With the current lookup table, temperature is calculated with the formula:\n
\r
394 * 4*(index of entry) - 2*(measured NTC - NTC from entry) / (ADCsteps of entry)
\r
396 * \note If the NTC-measurement is saturated, with the current lookup table,
\r
397 * the temperature will be reported as -1 C.
\r
399 * \note If no valid entry is found, battery temperature is set to 80.
\r
401 void NTCLookUp (void)
\r
404 unsigned char found = FALSE;
\r
406 // Lookup in the NTC-table. Use the first entry which is equal or below
\r
407 // sampled NTC. Calculate temperature by using the index number, and the
\r
408 // difference between the measured NTC value and the one in the entry.
\r
409 for (i=0 ; (i < NTC_TABLE_SIZE) && (!found); i++) {
\r
410 if (ADCS.rawNTC >= NTC[i].ADC) {
\r
411 BattData.Temperature = (i<<2) ;
\r
412 BattData.ADCSteps = NTC[i].ADCsteps;
\r
413 BattData.Temperature -= ((ADCS.rawNTC - NTC[i].ADC)<<1) / BattData.ADCSteps;
\r
415 found = TRUE; // Could be done with a break, but that violates MISRA.
\r
419 // For safety, is temperature is greater than the NTC
\r
421 BattData.Temperature = 80;
\r