1 /* This file has been prepared for Doxygen automatic documentation generation.*/
\r
2 /*! \file *********************************************************************
\r
7 * Contains the functions related to the states defined in menu.h.\n
\r
8 * Also contains related functions, i.e. for checking jumpers, setting
\r
9 * error flags and "dozing".
\r
11 * \note The state function Charge() is in a separate file since it
\r
12 * should easily be changed with regard to battery type.
\r
14 * \par Application note:
\r
15 * AVR458: Charging Li-Ion Batteries with BC100 \n
\r
16 * AVR463: Charging NiMH Batteries with BC100
\r
18 * \par Documentation
\r
19 * For comprehensive code documentation, supported compilers, compiler
\r
20 * settings and supported devices see readme.html
\r
23 * Atmel Corporation: http://www.atmel.com \n
\r
24 * Support email: avr@atmel.com
\r
30 * $URL: http://svn.norway.atmel.com/AppsAVR8/avr458_Charging_Li-Ion_Batteries_with_BC100/tag/20070904_release_1.0/code/IAR/statefunc.c $
\r
31 * $Date: 2007-08-23 12:55:51 +0200 (to, 23 aug 2007) $\n
\r
32 ******************************************************************************/
\r
38 #include "structs.h"
\r
42 #include "statefunc.h"
\r
43 #include "battery.h"
\r
53 //******************************************************************************
\r
55 //******************************************************************************
\r
56 unsigned char ErrorFlags; //!< \brief Holds error flags.
\r
57 //!< \note See statefunc.h for definitions of flags.
\r
59 //! \brief Holds the state in which latest error flag was set.
\r
60 //! \note See menu.h for definitions of states.
\r
61 unsigned char ErrorState;
\r
63 //! \brief Set to 1 by the watchdog-timeout-ISR
\r
64 //! \note added by Martin Thomas
\r
65 volatile unsigned char WatchdogFlag;
\r
67 //******************************************************************************
\r
69 //******************************************************************************
\r
70 /*! \brief Initialization
\r
72 * - Sets the system clock prescaler to 1 (run at 8 MHz)
\r
73 * - Initializes the one-wire interface
\r
74 * - Clears on-chip EEPROM
\r
75 * - Sets battery enable pins as outputs, then disables batteries
\r
76 * - Initializes SPI according to \ref SPIMODE
\r
78 * - Initializes timers
\r
79 * - Reads battery data from both battery inputs (via ADC)
\r
80 * - Disables batteries again
\r
81 * - Sets battery A as the current one (\ref BattActive = 0)
\r
82 * - Clears ErrorFlags
\r
84 * \param inp Not used.
\r
86 * \retval ST_BATCON Next state in the sequence.
\r
88 unsigned char Initialize(unsigned char inp)
\r
90 unsigned char i, page;
\r
92 // Disable interrupts while setting prescaler.
\r
93 __disable_interrupt();
\r
95 CLKPR = (1<<CLKPCE); // Enable CLKPS bit modification.
\r
96 CLKPR = 0; // Set prescaler 1 => 8 MHz clock frequency.
\r
98 // Init 1-Wire(R) interface.
\r
101 // Clear on-chip EEPROM.
\r
102 for (page = 0; page < 4; page++) {
\r
103 for (i = 0; i < 32; i++) {
\r
104 // From mthomas GCC addition
\r
105 if (BattEEPROM[page][i] != 0) {
\r
106 BattEEPROM[page][i] = 0;
\r
111 DDRB = (1<<PB4) | (1<<PB5); // Set battery enable pins as outputs.
\r
112 DisableBatteries();
\r
117 // Attempt to get ADC-readings (also gets RID-data) from both batteries.
\r
118 for (i = 0; i < BATCONN; i++) {
\r
121 BatteryStatusRefresh();
\r
124 DisableBatteries();
\r
126 BattActive = 0; // We have to start somewhere..
\r
129 // Init complete! Go to ST_BATCON next.
\r
134 /*! \brief Tests jumper settings and batteries, starts charging if necessary.
\r
136 * First, JumperCheck() is called. If successful, the function checks if any
\r
137 * valid batteries are connected and attempts to charge these, if necessary.\n
\r
138 * If no charging is necessary, the charger goes to ST_SLEEP next.\n
\r
139 * ST_ERROR is next if either JumperCheck() fails or there are no valid
\r
140 * batteries. In this last case, the error is also flagged.
\r
142 * \param inp Not used.
\r
144 * \retval ST_ERROR Next state if either the jumper check failed, or there are
\r
145 * no valid batteries.
\r
146 * \retval ST_PREQUAL Next state if a battery is found to enabled and not fully
\r
148 * \retval ST_SLEEP Next state if battery/batteries are enabled and fully
\r
151 unsigned char BatteryControl(unsigned char inp)
\r
155 // Make sure ADC inputs are configured properly! (Will disables batteries.)
\r
156 if (!JumperCheck()) {
\r
157 return(ST_ERROR); // Error. Exit before damage is done!
\r
160 // If neither battery is valid, flag error and go to error state
\r
161 if ((!BattControl[0].Enabled) && (!BattControl[1].Enabled)) {
\r
162 SetErrorFlag(ERR_NO_BATTERIES_ENABLED);
\r
167 // Get ADC-readings, try to read EPROM, and start prequalification
\r
168 // of any uncharged battery.
\r
169 for (i = 0; i < BATCONN; i++) {
\r
170 if (BattControl[i].Enabled) {
\r
174 if (BatteryStatusRefresh()) {
\r
175 if (!BattData.Charged) {
\r
176 BatteryDataRefresh();
\r
178 return(ST_PREQUAL);
\r
184 // If we end up here, one or two batteries are found and fully charged.
\r
185 // Disconnect, so we don't drain them, and go to sleep.
\r
186 DisableBatteries();
\r
192 /*! \brief Start running on batteries
\r
194 * \todo Run on batteries, if battery voltage high enough.
\r
195 * \todo Jump here when mains voltage drops below threshold
\r
198 unsigned char Discharge(unsigned char inp)
\r
200 return(ST_BATCON); // Supply voltage restored, start charging
\r
204 /*! \brief Sleeps until either battery needs charging
\r
206 * Calls Doze(), then refreshes the status for both batteries on wakeup. If
\r
207 * connected batteries are both charged, the function will loop. If not, it's
\r
208 * back to ST_BATCON.
\r
210 * \param inp Not used.
\r
212 * \retval ST_BATCON Next state if a connected battery isn't fully charged.
\r
214 unsigned char Sleep(unsigned char inp)
\r
219 Doze(); // Take a nap (~8 seconds).
\r
221 // If any batteries need charging, go to ST_BATCON.
\r
222 // Otherwise, keep sleeping.
\r
223 for (i = 0; i < BATCONN; i++) {
\r
226 if ( BatteryStatusRefresh() ) {
\r
227 if ( !BattData.Charged ) {
\r
233 DisableBatteries(); // Disable both batteries before Doze()!
\r
237 /*! \brief Watchdog interrupt-service-routine
\r
239 * Called on watchdog timeout, added by Martin Thomas
\r
241 #pragma vector = WDT_vect
\r
242 __interrupt void WDT_ISR(void)
\r
248 /*! \brief Doze off for approx. 8 seconds (Vcc = 5 V)
\r
250 * Waits for ADC-cycles to complete, disables the ADC, then sleeps for
\r
251 * approx. 8 seconds (Vcc = 5 V) using the watchdog timer.
\r
252 * On wakeup, ADC is re-enabled.
\r
253 * Modification by Martin Thomas:
\r
254 * Do not enter standby mode if PB6 is low (MASTER_INT on BC100)
\r
255 * so SPI communication with the master-controller is still possible
\r
260 uint8_t sreg_saved;
\r
261 // Wait for this ADC cycle to complete, then halt after the next one.
\r
267 } while (ADCS.Flag == FALSE);
\r
269 WDTCR = (1<<WDP3)|(1<<WDP0); // 8.0 seconds at 5 volts VCC.
\r
270 WDTCR |= (1<<WDIF)|(1<<WDIE)|(1<<WDE); // Clear flag and enable watchdog.
\r
272 DDRB &= ~(1<<PB6); // MASTER_INT as input
\r
273 PORTB |= (1<<PB6); // enabled internal pullup
\r
275 if ( PINB & (1<<PB6) ) {
\r
276 MCUCR |= (1<<SE) | (1<<SM1) | (1<<SM0); // Sleep enable, mode = standby.
\r
277 __sleep(); // Go to sleep, wake up by WDT.
\r
280 // stay awake if PB6 is pulled low by master
\r
282 } while ( !(WatchdogFlag) );
\r
285 __watchdog_reset(); // Clear watchdog reset flag.
\r
286 PORTB &= ~(1<<PB6); // disabled internal pullup
\r
287 MCUSR &= ~(1<<WDRF);
\r
291 WDTCR |= (1<<WDCE)|(1<<WDE); // Watchdog change enable.
\r
292 WDTCR = 0; // Turn off watchdog.
\r
295 ADCS.Halt = FALSE; // Enable consecutive runs of ADC.
\r
296 ADCSRA |= (1<<ADEN)|(1<<ADSC); // Enable ADC & start conversion.
\r
298 // Wait for this cycle to complete.
\r
303 /*! \brief Handles errors
\r
305 * Stops PWM output and disables batteries. The function then goes into a loop
\r
306 * that starts with a call to Doze(), then attempts to handle each error. The
\r
307 * loop will reiterate until all flags are cleared.\n
\r
308 * The charger will reinitialize after this.
\r
310 * Jumper errors are handled by clearing the flag, then calling JumperCheck().
\r
311 * If unsuccessful, the error flag will now have been set again.\n
\r
313 * If there are no valid batteries, the loop will simply reiterate until a
\r
314 * valid battery is found. The error flag will then be cleared.\n
\r
316 * In the case of PWM controller or battery temperature errors, the error
\r
317 * flag is simply cleared. This is because the problem may have gone away during
\r
318 * Doze(), or after reinitializing.\n
\r
320 * If a battery is exhausted, we clear its exhausted-flag in \ref BattData,
\r
321 * and change batteries before clearing the error flag.
\r
323 * \param inp Not used.
\r
325 unsigned char Error(unsigned char inp)
\r
329 PWM_Stop(); // Stop charging.
\r
330 DisableBatteries(); // Disable all loads.
\r
333 Doze(); // Take a nap.
\r
335 // For each bit in ErrorFlags, starting with LSB, handle
\r
336 // associated error, if the flag is set.
\r
337 for (i = 0x01; i!=0; i<<=1) {
\r
338 if(i & ErrorFlags) {
\r
341 case ERR_JUMPER_MISMATCH:
\r
342 // Clear flag & recheck.
\r
348 case ERR_NO_BATTERIES_ENABLED:
\r
349 // Clear if any battery gets enabled.
\r
350 if ((BattControl[0].Enabled) || (BattControl[1].Enabled)) {
\r
356 case ERR_PWM_CONTROL:
\r
362 case ERR_BATTERY_TEMPERATURE:
\r
368 case ERR_BATTERY_EXHAUSTED:
\r
369 // Try the other battery.
\r
370 BattData.Exhausted = FALSE;
\r
371 BattActive = (BattActive + 1) % 2;
\r
381 } while (ErrorFlags);
\r
387 /*! \brief Sets the specified error-flag and saves the current state
\r
389 * Updates \ref ErrorFlags and \ref ErrorState.
\r
391 * \note Error flags are specified in statefunc.h.
\r
393 * \param Flag Specifies what error to flag.
\r
395 void SetErrorFlag(unsigned char Flag)
\r
397 ErrorFlags |= Flag;
\r
398 ErrorState = CurrentState;
\r
402 /*! \brief Checks on-board jumpers.
\r
404 * Checks on-board jumpers by disconnecting all loads, engaging the PWM and
\r
405 * increasing the duty cycle until conditioned output voltage equals conditioned
\r
406 * input voltage. At low PWM duty and no load buck output should be zero and,
\r
407 * when increasing PWM duty, should quickly jump to steady state output roughly
\r
408 * equal to input voltage. Will disable and leave disabled all batteries.
\r
410 * \retval FALSE If jumper or load mismatch.
\r
411 * \retval TRUE If everything OK.
\r
413 unsigned char JumperCheck(void)
\r
415 DisableBatteries(); // Disconnect, or loads may be destroyed!
\r
417 PWM_Start(); // Start PWM (controls the buck charger).
\r
419 // Use general timer: shouldn't take longer than (6 x 255) / 2500 ~= 0.62s.
\r
420 Time_Set(TIMER_GEN,0,1,0);
\r
423 // If the PWM output voltage saturates the ADC, stop PWM output and
\r
424 // report a failure.
\r
425 if (ADCS.rawVBAT == 1023) {
\r
430 // If the absolute difference between measured (VIN - VBAT) and the
\r
431 // typical value are below our set maximum, everything is OK.
\r
432 if (abs((signed int)(ADCS.VIN - VIN_VBAT_DIFF_TYP - ADCS.VBAT)) <
\r
433 VIN_VBAT_DIFF_MAX ) {
\r
439 // Charge current is too high -> check load and jumper J405 and J406.
\r
440 if (abs(ADCS.IBAT) > 100) {
\r
445 // If the PWM output can't be increased high enough -> check jumpers
\r
446 // J400-J404, J407 and J408.
\r
447 if (!PWM_IncrementDutyCycle()) {
\r
452 // Wait for ADC conversions to complete
\r
454 } while (Time_Left(TIMER_GEN));
\r
457 // If we end up here, the measurements took too long.
\r