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
32 #include <avr/eeprom.h>
\r
33 #include <avr/interrupt.h>
\r
35 #include "structs.h"
\r
39 #include "battery.h"
\r
45 #include "NIMHspecs.h"
\r
49 #include "LIIONspecs.h"
\r
54 //******************************************************************************
\r
56 //******************************************************************************
\r
57 /* Control-struct for batteries */
\r
58 /*! \brief Holds control data for both batteries
\r
59 * \note Stored in EEPROM.
\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
66 /* Data-struct for battery */
\r
67 Batteries_t BattData; //!< Holds data for the current battery
\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
74 unsigned char EEMEM BattEEPROM[4][32];
\r
77 //! Global that indicates current battery (0 = battery A, 1 = B)
\r
78 unsigned char BattActive;
\r
81 /*! \brief RID lookup-table
\r
83 * Contains Resistor ID data specified by manufacturer.
\r
85 * \note Values have been calculated assuming a +/- 15% tolerance.
\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
95 /*! \brief NTC lookup-table
\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
101 * \note This was calculated for a Mitsubishi RH16-3H103FB NTC.
\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
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
117 // FOR MITSUBISHI NTC
\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
128 //******************************************************************************
\r
130 //******************************************************************************
\r
131 /*! \brief Checks if battery has changed
\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
136 * \retval FALSE Battery is disconnected, or capacity has changed.
\r
137 * \retval TRUE All OK.
\r
139 unsigned char BatteryCheck(void)
\r
141 unsigned char success = TRUE;
\r
142 unsigned int oldCapacity;
\r
144 // Save to see if battery data has changed.
\r
145 oldCapacity = BattData.Capacity;
\r
147 if (!BatteryStatusRefresh()) {
\r
148 success = FALSE; // Battery not present or RID was invalid.
\r
151 if (oldCapacity != BattData.Capacity) {
\r
152 success = FALSE; // Battery configuration has changed.
\r
159 /*! \brief Refreshes battery status information
\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
166 * \retval FALSE No battery present.
\r
167 * \retval TRUE Battery present, status refreshed.
\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
172 unsigned char BatteryStatusRefresh(void)
\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
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
191 BattData.HasRID = RIDLookUp();
\r
195 adcs_VBAT_tmp = ADCS.VBAT;
\r
196 adcs_avgIBAT_tmp = ADCS.avgIBAT;
\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
204 // Is the battery charged?
\r
205 if (adcs_VBAT_tmp >= BAT_VOLTAGE_LOW) {
\r
206 BattData.Charged = TRUE;
\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
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
217 if (((OCR1B == 0) && (!BattData.Low)) ||
\r
218 ((OCR1B != 0) && (adcs_avgIBAT_tmp > 0))) {
\r
219 BattData.Present = TRUE;
\r
222 BattData.Low = FALSE; // (This is just a technicality..)
\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
237 /*! \brief Refreshes battery data in the EEPROM
\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
243 * \todo Updating BattData with these data. Specs needed.
\r
245 * \retval FALSE Refresh failed.
\r
246 * \retval TRUE Refresh successful.
\r
248 unsigned char BatteryDataRefresh(void)
\r
250 unsigned char offset;
\r
251 unsigned char i, crc, family, temp, page;
\r
252 unsigned char success;
\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
258 if (OWI_DetectPresence(OWIBUS) == OWIBUS) {
\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
265 for (i = 0; i < 6; i++) {
\r
266 crc = OWI_ComputeCRC8(OWI_ReceiveByte(OWIBUS),crc);
\r
269 // CRC ok, device found.
\r
270 if (OWI_ComputeCRC8(OWI_ReceiveByte(OWIBUS),crc) == 0) {
\r
271 BattData.Circuit = family;
\r
273 // For now, we only read data from DS2505 EPROMs.
\r
274 if (BattData.Circuit == OW_DS2505) {
\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
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
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
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
297 if (OWI_ComputeCRC8(OWI_ReceiveByte(OWIBUS),crc) == 0) {
\r
298 success = TRUE; // Data read OK
\r
300 } else { // Not able to start reading data.
\r
302 } else { // Wrong device type.
\r
304 } else { // No device found.
\r
306 } else { // No presence detected on one-wire bus.
\r
309 // Erase local EEPROM page if there were any errors during transfer.
\r
311 for (i=0; i<32; i++) {
\r
312 eeprom_write_byte(&BattEEPROM[page][i], 0);
\r
321 /*! \brief Enables specified battery
\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
327 * \param bat Specifies which battery to enable (0 = battery A, 1 = B)
\r
329 void EnableBattery(unsigned char bat)
\r
331 // Use general timer, set timeout to 100ms.
\r
332 Time_Set(TIMER_GEN,0,0,100);
\r
334 // Set specified battery as the active one.
\r
337 // Enable current battery in hardware, light LED & connect battery.
\r
338 PORTB |= (1 << (PB4+bat));
\r
340 // Disconnect other battery.
\r
341 PORTB &= ~(1<<(PB5-bat));
\r
343 do { // Let port switch settle.
\r
344 } while (Time_Left(TIMER_GEN));
\r
348 /*! \brief Disables both batteries
\r
350 * Clears PB4 and PB5 in PORTB, disabling both batteries.
\r
352 void DisableBatteries(void)
\r
354 // Turn off LEDs and disconnect batteries.
\r
355 PORTB &= ~((1<<PB4)|(1<<PB5));
\r
359 /*! \brief Looks up battery data from RID table
\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
365 * \retval TRUE Entry found, battery data updated.
\r
366 * \retval FALSE No entry found, using defaults for battery data.
\r
368 unsigned char RIDLookUp (void)
\r
370 unsigned char i, found = FALSE;
\r
371 unsigned int adcs_rawRID_tmp;
\r
372 unsigned char sreg_saved;
\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
379 adcs_rawRID_tmp = ADCS.rawRID;
\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
393 // If no valid entry is found, use defaults and return FALSE.
\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
405 /*! \brief Calculates temperature from a lookup table
\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
411 * \note If the NTC-measurement is saturated, with the current lookup table,
\r
412 * the temperature will be reported as -1 C.
\r
414 * \note If no valid entry is found, battery temperature is set to 80.
\r
416 void NTCLookUp (void)
\r
419 unsigned char found = FALSE;
\r
420 unsigned char adcs_rawNTC_tmp;
\r
421 unsigned char sreg_saved;
\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
429 adcs_rawNTC_tmp = ADCS.rawNTC;
\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
436 found = TRUE; // Could be done with a break, but that violates MISRA.
\r
440 // For safety, is temperature is greater than the NTC
\r
442 BattData.Temperature = 80;
\r