Virginia, USA
Offline
Jr. Member
Karma: 0
Posts: 90
|
 |
« on: November 09, 2011, 09:05:39 am » |
I have seen several other posts and tutorials regarding putting an ATMEga328P to sleep and I have tried to follow those but my consumption is still quite high. My sketch is heavily based on a sparkfun tutorial I found but I can not replicate the results. My project requires the use of a small lipo battery with a solar cell to recharge it so I need to get the micro into the deepest sleep possible to conserve battery. I have moved the micro off the Arduino board and bread boarded it so I can accurately measure consumption. I loaded a boot loader from the Arduino tutorial which runs an 8MHz internal clock and I use a 32.768 external crystal for timer2. I only need it to wake up if timer2 overflows or if it gets an external interupt so I have tried to shut down everything else. My goal is to get this into the single digit uA range and at the moment I am only getting down to 1.2 mah. I thought that maybe my DMM was the issue so I bought a second one which reads the same. I have made sure to remove everything from the board including the programming leads. To check current I connect the battery GND directly to the bread board and I put my DMM inline with VCC. I posted my sketch below and hope that someone has some suggestions.
#include <avr/sleep.h> //Needed for sleep_mode #include <avr/power.h> //Needed for powering down perihperals such as the ADC/TWI and Timers
#define TRUE 1 #define FALSE 0
int show_the_time = FALSE;
volatile long seconds = 0;
//The very important 32.686kHz interrupt handler SIGNAL(TIMER2_OVF_vect){ seconds++; }
//The interrupt occurs when you push the button ISR(INT1_vect){ if(show_the_time == FALSE)show_the_time = TRUE; }
//The interrupt occurs when you push the button ISR(INT0_vect){ if(show_the_time == FALSE)show_the_time = TRUE; }
void setup() { //To reduce power, setup all pins as inputs with no pullups for(int x = 1 ; x < 18 ; x++){ pinMode(x, INPUT); digitalWrite(x, LOW); } //Power down various bits of hardware to lower power usage set_sleep_mode(SLEEP_MODE_PWR_SAVE); sleep_enable();
//Shut off ADC, TWI, SPI, Timer0, Timer1
ADCSRA &= ~(1<<ADEN); //Disable ADC ACSR = (1<<ACD); //Disable the analog comparator DIDR0 = 0x3F; //Disable digital input buffers on all ADC0-ADC5 pins DIDR1 = (1<<AIN1D)|(1<<AIN0D); //Disable digital input buffer on AIN1/0 power_twi_disable(); power_spi_disable(); power_usart0_disable(); power_timer0_disable(); power_timer1_disable(); //power_timer2_disable(); //Needed for asynchronous 32kHz operation
//Setup TIMER2 TCCR2A = 0x00; TCCR2B = (1<<CS22)|(1<<CS21)|(1<<CS20); //Set CLK/1024 or overflow interrupt every 8s ASSR = (1<<AS2); //Enable asynchronous operation, 32kHz xtal needed TIMSK2 = (1<<TOIE2); //Enable the timer 2 interrupt
//Setup external INT1 (pin3) interrupt EICRA = (1<<ISC11)|(1<<ISC01)|(1<<ISC10)|(1<<ISC00); //Interrupt on rising edge EIMSK = (1<<INT0)|(1<<INT1); //Enable INT interrupts //need pull downs if active high, active low can enable internal pulllups //digitalWrite(3, HIGH); //digitalWrite(2, HIGH);
sei(); //Enable global interrupts }
void loop() { sleep_mode(); //Stop everything and go to sleep. Wake up if the Timer2 buffer overflows or if you hit the button // 10000 ~= 5 hours if(seconds > 3){ reset_5s(); } if(show_the_time == TRUE) { reset_5s(); } }
void reset_5s(){ pinMode(A0, OUTPUT); digitalWrite(A0, LOW); // needs to be LOW fake_msdelay(1000); digitalWrite(A0, HIGH); // needs to be HIGH pinMode(A0, INPUT); seconds = 0; show_the_time = FALSE; }
//This is a not-so-accurate delay routine //Calling fake_msdelay(100) will delay for about 100ms //Assumes 8MHz clock void fake_msdelay(int x){ for( ; x > 0 ; x--) fake_usdelay(1000); }
//This is a not-so-accurate delay routine //Calling fake_usdelay(100) will delay for about 100us //Assumes 8MHz clock void fake_usdelay(int x){ for( ; x > 0 ; x--) { __asm__("nop\n\t"); __asm__("nop\n\t"); __asm__("nop\n\t"); __asm__("nop\n\t"); __asm__("nop\n\t"); __asm__("nop\n\t"); __asm__("nop\n\t"); } }
|
|
|
|
|
Logged
|
|
|
|
|
Leighton Buzzard, UK
Offline
Edison Member
Karma: 11
Posts: 1049
|
 |
« Reply #1 on: November 09, 2011, 09:26:47 am » |
so much kinder to wrap it in [ code ] ... [ / code ]  #include <avr/sleep.h> //Needed for sleep_mode #include <avr/power.h> //Needed for powering down perihperals such as the ADC/TWI and Timers
#define TRUE 1 #define FALSE 0
int show_the_time = FALSE;
volatile long seconds = 0;
//The very important 32.686kHz interrupt handler SIGNAL(TIMER2_OVF_vect){ seconds++; }
//The interrupt occurs when you push the button ISR(INT1_vect){ if(show_the_time == FALSE)show_the_time = TRUE; }
//The interrupt occurs when you push the button ISR(INT0_vect){ if(show_the_time == FALSE)show_the_time = TRUE; }
void setup() { //To reduce power, setup all pins as inputs with no pullups for(int x = 1 ; x < 18 ; x++){ pinMode(x, INPUT); digitalWrite(x, LOW); } //Power down various bits of hardware to lower power usage set_sleep_mode(SLEEP_MODE_PWR_SAVE); sleep_enable();
//Shut off ADC, TWI, SPI, Timer0, Timer1
ADCSRA &= ~(1<<ADEN); //Disable ADC ACSR = (1<<ACD); //Disable the analog comparator DIDR0 = 0x3F; //Disable digital input buffers on all ADC0-ADC5 pins DIDR1 = (1<<AIN1D)|(1<<AIN0D); //Disable digital input buffer on AIN1/0 power_twi_disable(); power_spi_disable(); power_usart0_disable(); power_timer0_disable(); power_timer1_disable(); //power_timer2_disable(); //Needed for asynchronous 32kHz operation
//Setup TIMER2 TCCR2A = 0x00; TCCR2B = (1<<CS22)|(1<<CS21)|(1<<CS20); //Set CLK/1024 or overflow interrupt every 8s ASSR = (1<<AS2); //Enable asynchronous operation, 32kHz xtal needed TIMSK2 = (1<<TOIE2); //Enable the timer 2 interrupt
//Setup external INT1 (pin3) interrupt EICRA = (1<<ISC11)|(1<<ISC01)|(1<<ISC10)|(1<<ISC00); //Interrupt on rising edge EIMSK = (1<<INT0)|(1<<INT1); //Enable INT interrupts //need pull downs if active high, active low can enable internal pulllups //digitalWrite(3, HIGH); //digitalWrite(2, HIGH);
sei(); //Enable global interrupts }
void loop() { sleep_mode(); //Stop everything and go to sleep. Wake up if the Timer2 buffer overflows or if you hit the button // 10000 ~= 5 hours if(seconds > 3){ reset_5s(); } if(show_the_time == TRUE) { reset_5s(); } }
void reset_5s(){ pinMode(A0, OUTPUT); digitalWrite(A0, LOW); // needs to be LOW fake_msdelay(1000); digitalWrite(A0, HIGH); // needs to be HIGH pinMode(A0, INPUT); seconds = 0; show_the_time = FALSE; }
//This is a not-so-accurate delay routine //Calling fake_msdelay(100) will delay for about 100ms //Assumes 8MHz clock void fake_msdelay(int x){ for( ; x > 0 ; x--) fake_usdelay(1000); }
//This is a not-so-accurate delay routine //Calling fake_usdelay(100) will delay for about 100us //Assumes 8MHz clock void fake_usdelay(int x){ for( ; x > 0 ; x--) { __asm__("nop\n\t"); __asm__("nop\n\t"); __asm__("nop\n\t"); __asm__("nop\n\t"); __asm__("nop\n\t"); __asm__("nop\n\t"); __asm__("nop\n\t"); } }
|
|
|
|
|
Logged
|
there are only 10 types of people them that understands binary and them that doesn't
|
|
|
|
Virginia, USA
Offline
Jr. Member
Karma: 0
Posts: 90
|
 |
« Reply #2 on: November 09, 2011, 09:34:35 am » |
Sorry about that  , I will make sure to do that in the future.
|
|
|
|
|
Logged
|
|
|
|
|
Grand Blanc, MI, USA
Offline
Edison Member
Karma: 43
Posts: 2494
"We're a proud service of the Lost Electricity Reclamation Agency"
|
 |
« Reply #3 on: November 09, 2011, 06:05:32 pm » |
Here's a basic sketch that perhaps you can use parts of. //ATmega328P sleep demo // //Wire the usual Arduino LED from PB5 (DIP pin 19, Arduino pin 13) to ground, through //an appropriate current-limiting resistor. //Wire a tactile button switch from INT0/PD2 (DIP pin 4, Arduino pin 2) to ground. // //The sketch will blink the LED three times, then go to sleep. //Press the button to generate an interrupt to wake the MCU. //Best on a breadboard. Note that voltage regulators and other circuitry on a real Arduino //will continue to draw current and so the current consumed by just the MCU will be //difficult to measure. //While sleeping, I measure < 0.5uA supply current. // //Jack Christensen 11Sep2011 // //Scotchware License: If we meet some day, and you think this is worth it, you can buy me a scotch.
#include <avr/sleep.h>
byte intCounter, adcsra, mcucr1, mcucr2;
void setup(void) { for (byte i=0; i<20; i++) { pinMode(i, INPUT); //make all pins input pins digitalWrite(i, HIGH); //with pullup resistors to minimize power consumption } pinMode(13, OUTPUT); //except LED pin Serial.begin(115200); EICRA = 0x00; //configure INT0 to trigger on low level }
void loop(void) { Serial.print("interrupts: "); Serial.println(intCounter, DEC); for (byte i=0; i<3; i++) { digitalWrite(13, LOW); delay(1000); digitalWrite(13, HIGH); delay(1000); } digitalWrite(13, LOW); sleep_enable(); set_sleep_mode(SLEEP_MODE_PWR_DOWN); EIMSK |= _BV(INT0); //enable INT0 adcsra = ADCSRA; //save the ADC Control and Status Register A ADCSRA = 0; //disable ADC cli(); mcucr1 = MCUCR | _BV(BODS) | _BV(BODSE); //turn off the brown-out detector mcucr2 = mcucr1 & ~_BV(BODSE); MCUCR = mcucr1; MCUCR = mcucr2; sei(); //ensure interrupts enabled so we can wake up again sleep_cpu(); //go to sleep sleep_disable(); //wake up here ADCSRA = adcsra; //restore ADCSRA digitalWrite(13, LOW); pinMode(13, OUTPUT); //so we can blink the LED some more }
ISR(INT0_vect) { intCounter++; EIMSK &= ~_BV(INT0); //one interrupt to wake up only }
|
|
|
|
|
Logged
|
|
|
|
|
Virginia, USA
Offline
Jr. Member
Karma: 0
Posts: 90
|
 |
« Reply #4 on: November 09, 2011, 09:00:11 pm » |
That sketch works MUCH better than what I was using. I am reading 98uA which is a big step forward for me. I will work off of this and keep you posted on my progress. Thanks a lot for posting!
|
|
|
|
|
Logged
|
|
|
|
|
Grand Blanc, MI, USA
Offline
Edison Member
Karma: 43
Posts: 2494
"We're a proud service of the Lost Electricity Reclamation Agency"
|
 |
« Reply #5 on: November 10, 2011, 07:21:11 am » |
Cool, but at the same time 98uA is a lot higher than what I was reading. I just had an ATmega328P on a breadboard, with only the LED and switch hooked to it. (In addition to the essentials that is: a crystal, bypass caps, reset switch). Might there be something else drawing current?
|
|
|
|
|
Logged
|
|
|
|
|
Virginia, USA
Offline
Jr. Member
Karma: 0
Posts: 90
|
 |
« Reply #6 on: November 10, 2011, 08:20:01 am » |
yeah, I know I am doing something wrong but I cant quite figure it out yet. My breadboard is bare except for the 328P, capacitors, 32.768 crystal and a 10k resistor on the reset pin. I remove the programmer completely and then power it with a lipo battery. What boot loader are you using? I have an ISP programmer, do I need to use that to change any fuse settings?
|
|
|
|
|
Logged
|
|
|
|
|
Grand Blanc, MI, USA
Offline
Edison Member
Karma: 43
Posts: 2494
"We're a proud service of the Lost Electricity Reclamation Agency"
|
 |
« Reply #7 on: November 10, 2011, 08:52:20 am » |
What's the 32.768kHz crystal for, are you using the Low-Freq Xtal Osc as the system clock? I have the 10K pullup on the reset pin too, that should not pose a problem. I'm using the standard Uno Optiboot bootloader. Actually I'm using westfw's beta, but should be same difference for this purpose. Standard Uno fuses too: uno.bootloader.low_fuses=0xff uno.bootloader.high_fuses=0xde uno.bootloader.extended_fuses=0x05
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Full Member
Karma: 1
Posts: 172
www.rocketscream.com
|
 |
« Reply #8 on: November 10, 2011, 12:27:09 pm » |
The brownout detect consumes about 20uA++. The default bootloader enables them. You can either choose to hardware disable them or do it on the fly using software.
But, somewhere else is sucking that extra 70uA++.
|
|
|
|
|
Logged
|
|
|
|
|
Virginia, USA
Offline
Jr. Member
Karma: 0
Posts: 90
|
 |
« Reply #9 on: November 10, 2011, 12:33:59 pm » |
This is great, with your suggestions I got it down to 0.2uA! I was orginally using the internal 8MHz bootloader found in the Arduino tutorial, I changed that to the standard UNO bootloader and replaced the crytal with a 16MHz which did the trick. No matter what I did with the other bootloader I could not get consumption below 1mah. I am using the 32.768 khz crystal for a timer which I am hoping to get to run for as much as a year or more before it overflows and wakes up.
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Faraday Member
Karma: 16
Posts: 3196
20 LEDs are enough
|
 |
« Reply #10 on: November 10, 2011, 12:59:28 pm » |
What are you going to do and how important is reasonable exact timing for your project?
|
|
|
|
|
Logged
|
|
|
|
|
Virginia, USA
Offline
Jr. Member
Karma: 0
Posts: 90
|
 |
« Reply #11 on: November 10, 2011, 02:16:15 pm » |
I have 2 applications for this and neither of them require any kind of precision timing. The first application is for a GSM modem I am using the gets hung periodiclly and it is in a remote location so it cant be manually reset. The modem runs every few hours to transmit some data so I am using this as an external watchdog. So I will set the Mega328 to look for an interupt from the modem and if it does not get one within 24 hours it indicates that it got hung and it will reboot it.
The second application is basically being used as a simple timer which will wake up in say 18 months. It will take a pin high which triggers some events and then its work is done. Timing is not critical at all and if it fires a few days early or late it doesn't matter. I know most people will say to just use a timer but I think this will work better for my overall needs.
|
|
|
|
|
Logged
|
|
|
|
|
|