As part of a recent AVR project, I wanted a way to increment a counter and store it even if the power was lost. This could be done with an external flash memory device, but I wanted to use the internal EEPROM of the AVR.
The internal EEPROM is limited to around 100,000 writes. Independent tests have shown this can be doubled, but 100k writes is only about 27 hours if saved once every second, so not practical for a device I wanted to last at least five years.
It takes a maximum of 34ms (8448us per byte) to write a 32 bit (4 byte) integer to EEPROM. Due to the low power requirements of an AVR, I was confident that a decent sized (say 2200uF) capacitor would allow an AVR plenty of running time to complete EEPROM writes/saves before power is totally lost.
I decided to use the Analog Comparator on an AVR. The analog comparator works by comparing the voltage presented to AIN1 and AIN0. A “falling edge” event is the point where the voltage at AIN1 drops below the voltage at AIN0. This allows me to fire an interrupt on loss of power. I had a spare ATMEGA328P to use, but provided your application doesn’t require the extra I/O, even an ATTINY85 has the necessary analog comparator to do this.
I first tried a very basic circuit where the AVR was powered off a 2200uF capacitor which was charged through a Schottky diode – the diode is to prevent the capacitor’s charge going back into the supply on loss of power. AIN0 was connected to the capacitor +ve while AIN1 was connected direct to the supply voltage. Adding the diode is a double edged sword as it means the AVR is now running off a supply a diode voltage drop (~0.3v for a Schottky diode) lower than usual, reducing the running time off the capacitor, but it also means that the analog comparator shouldn’t be noisy due to the generous 0.3v difference.
This circuit worked for total loss of power such as a broken supply wire, but when the supply was simply turned off it didn’t work. My oscilloscope showed this was due to the large capacitors in the power supply causing the voltage to drop too slowly meaning that the supply voltage never dropped below the capacitor voltage – both voltages simply dropped in unison. A solution to this could be to add more capacitors to the circuit, but I decided to increase my supply voltage to 12 volts and use a 5 volt linear regulator. This would allow me to detect power failure while the regulator is still outputting a clean 5 volts.
Here is my circuit:
An LM7805 regulator outputs a stable 5 volts provided the input voltage is above 8 volts. It will continue to work (with a bit of output voltage ripple) down to 7 volts, and is unreliable below 7 volts. The regulator shouldn’t even need a heatsink as we’re drawing well under 50mA of power – With a 7 volt difference between vIn and vOut, this is only 350mW (The LM7805 temperature will typically rise approx 50 degrees C per watt dissipated).
I’m aiming for my circult to detect a supply voltage below ~8v as a loss of power.
This circuit provides a ~2.5 volt reference voltage to AIN0 by dividing the 5v from the regulator via R4 (10K) and R5 (10K).
The supply voltage is dropped around 0.3v through a Schottky diode and then divided at a 10:32 ratio (~1/3) via R1 (22K) and R2 (10K) and presented to AIN1.
For the capacitor C1, I suggest a the largest electrolytic capacitor you can get (within reason). My local electronics store (Jaycar) sells 16 volt, 4700uF electrolytic caps for a reasonable ($2.50) price so I went with one of these.
Here is some source code demonstrating how to enable and use the Analog comparator interrupt. This code is for an ATMega328, but should work on most other devices with minimal changes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> #include <avr/eeprom.h> void init() { cli(); //SET UP THE ANALOG COMPARATOR INTERRUPT ACSR = (0<<ACD)| //Comparator ON (1<ACI)| //Stop interrupt being called immediately (1<<ACIE)| //Enable Comparator Interrupt (1<<ACIS1)|(0<<ACIS0); //Comparator Interrupt on Falling Output Edge. //ENABLE INTERRUPTS ACSR |= (1<ACI); } int main (void) { init(); sei(); //Set the global interrupt flag while(1) { //Main program loop } return 1; } // Interrupt handler for Analog Comparator ANA_COMP_vect ISR(ANALOG_COMP_vect) { //Power loss has been detected!!!! //saveImportantValue(); } |
1 Comment