Timer2 sleep

I’m attempting to build a battery operated clock and so, I want to save current as much as possible.
Here on a example sketch I’m using timer2 on a Arduino Nano with 8Mhz internal clock to generate an interrupt every 250ms, from an external 32k crystal. The sketch will wake up, check if a second has passed, update counter every second, print it on display and then it will sleep the rest of the time until the next timer interrupt.

Or that’s how it is supposed to work. It works if I have “goToSleep();” commented, but then it won’t sleep of course. When I un-comment it, it will sleep, but then it counts too fast.

What am I doing wrong here ?

#define F_CPU 8000000UL
#include <util/delay.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <LiquidCrystal.h>

LiquidCrystal lcd(5, 6, 7, 8, 9, 10); //RS, E, D4, D5, D6, D7

byte contrast = 18;
volatile boolean quarterSecPassed = false;
byte quarterSecCntr = 0;
long counter = 0;

void setup()
{
  DDRC &= B11110000; //A0-A3 to inputs (for keys)
  PORTC |= B00001111; //A0-A3 pullup
  DDRD |= B00011000; //led backlight PD4 and buzzer PD3 to output
  PORTD &= B11100111; //led backlight PD4 and buzzer PD3 low

  //set up display
  lcd.begin(8, 1); //uses DOGM081
  lcd.command(0x21); //function set, switch to instruction table 1
  _delay_us(30);
  lcd.command(0x1C); //bias set
  _delay_us(30);
  lcd.command(0x51); //power control (Icon off)
  _delay_us(30);
  lcd.command(0x6A); //follower control
  _delay_us(30);
  lcd.command(0x70); //contrast set, initialize to 32 (0-63, 0 = totally blank)
  _delay_us(30);
  lcd.command(0x20); //function set, switch back to instruction table 0
  _delay_us(30);
  setContrast(contrast);

  //set up Timer2
  noInterrupts(); //disable all interrupts
  ASSR |= (1 << AS2); //clock input to Timer2 from XTAL1/XTAL2
  TCCR2A = 0;
  TCCR2B = 0;
  TCNT2 = 0;

  OCR2A = 7;// compare match register 32,768kHz/1024/8 = 4Hz
  TCCR2A |= (1 << WGM21); //CTC mode
  TCCR2B |= ((1 << CS20) | (1 << CS21) | (1 << CS22)); //1024 prescaler
  TIMSK2 |= (1 << OCIE2A); //enable timer compare interrupt
  interrupts(); //enable all interrupts

  TIMSK0 &= ~(1 << OCIE0A); //disable TIMER0
}

ISR(TIMER2_COMPA_vect) //update time
{
  quarterSecPassed = true;
}

void loop()
{
  if (quarterSecPassed)
  {
    quarterSecPassed = false;
    quarterSecCntr++;
    if (quarterSecCntr == 4)
    {
      counter++;
      lcd.setCursor(0, 0);
      lcd.print(counter);
      quarterSecCntr = 0;
    }
    
    
  }
  //goToSleep();
}

void setContrast(int value)
{
  if (( value >= 0x00) && (value <= 0x3F))
  {
    int val_lower = value & 0x0F;
    int val_upper = value >> 4;
    lcd.command(0x21); //function set, switch to instruction table 1
    _delay_us(30);
    lcd.command(0x70 | val_lower); //contrast set, initialize to 32 (0-63, 0 = totally blank)
    _delay_us(30);
    lcd.command(0x50 | val_upper); //power control
    _delay_us(30);
    lcd.command(0x20); //function set, switch back to instruction table 0
    _delay_us(30);
  }
}

void goToSleep()
{
  ADCSRA &= (0 << ADEN); //turn off ADC
  ACSR = (1 << ADC); //turn off comparator
  
  //power_all_disable();
  power_adc_disable(); // ADC converter
  power_spi_disable(); // SPI
  power_usart0_disable(); // Serial (USART)
  power_timer0_disable(); // Timer 0
  power_timer1_disable(); // Timer 1
  //power_timer2_disable(); // Timer 2
  power_twi_disable(); // TWI (I2C)

  set_sleep_mode(SLEEP_MODE_PWR_SAVE);
  sleep_enable();
  sleep_cpu();
  sleep_disable();

  power_all_enable();
  //power_adc_enable(); // ADC converter
  //power_spi_enable(); // SPI
  //power_usart0_enable(); // Serial (USART)
  //power_timer0_enable(); // Timer 0
  //power_timer1_enable(); // Timer 1
  //power_timer2_enable(); // Timer 2
  //power_twi_enable(); // TWI (I2C)
}

I'm using timer2 on a Arduino Nano with 8Mhz internal clock to generate an interrupt every 250ms, from an external 32k crystal.

How the heck are you using external clock for Timer2 with a Nano? Please explain a bit better: Which MCU/board? What is wired to it?

Danois90: How the heck are you using external clock for Timer2 with a Nano? Please explain a bit better: Which MCU/board? What is wired to it?

Surface mount version of ATmega328, with bootloader from here ("minimal circuit"): https://www.arduino.cc/en/Tutorial/ArduinoToBreadboard

32,768 kHz crystal connected to OSC1 / OSC2 pins

Have you tried to use an overflow interrupt with prescaler 32 to generate an interrupt each .25 second, or prescaler 128 to interrupt once per second?

Danois90:
Have you tried to use an overflow interrupt with prescaler 32 to generate an interrupt each .25 second, or prescaler 128 to interrupt once per second?

Tried that, it goes immediately to sleep and remains in sleep, doesn’t count, display is blank.
EDIT: It works if I don’t put Arduino to sleep.

(Here are the modified parts as compared to original version)

void setup()
{
  DDRC &= B11110000; //A0-A3 to inputs
  PORTC |= B00001111; //A0-A3 pullup
  DDRD |= B00011000; //led backlight PD4 and buzzer PD3 to output
  PORTD &= B11100111; //led backlight PD4 and buzzer PD3 low

  lcd.begin(8, 1); //uses DOGM081
  //all values shown here are for 5V supply
  lcd.command(0x21); //function set, switch to instruction table 1
  _delay_us(30);
  lcd.command(0x1C); //bias set
  _delay_us(30);
  lcd.command(0x51); //power control (Icon off)
  _delay_us(30);
  lcd.command(0x6A); //follower control
  _delay_us(30);
  lcd.command(0x70); //contrast set, initialize to 32 (0-63, 0 = totally blank)
  _delay_us(30);
  lcd.command(0x20); //function set, switch back to instruction table 0
  _delay_us(30);
  setContrast(contrast);

  //set up Timer2
  noInterrupts(); //disable all interrupts
  ASSR |= (1 << AS2); //clock input to Timer2 from XTAL1/XTAL2
  TCCR2A = 0;
  TCCR2B = 0;
  
  TCNT2 = 0; //preload timer 256 - 32,768kHz/32/4Hz
  TCCR2B |= ((1 << CS20) | (1 << CS21)); //32 prescaler
  TIMSK2 |= (1 << TOIE2); //enable timer overflow interrupt
  interrupts(); //enable all interrupts

  TIMSK0 &= ~(1 << OCIE0A); //disable TIMER0
}

ISR(TIMER2_OVF_vect) //update time
{
  TCNT2 = 0; //preload timer 256 - 32,768kHz/32/4Hz
  quarterSecPassed = true;
}

Try this:

volatile unsigned long quaterSecPassed = 0;
unsigned long lastQuaterSec = 0;

ISR(TIMER2_OVF_vect)
{
  quarterSecPassed++;
}

void setup()
{

  Serial.begin(9600);
  
  TIMSK2 = 0; //Disable Timer2 interrupts
  
  ASSR = (1 << AS2); //Enable external crystal
  delay(2000); //Wait for the crystal to settle
  
  
  TCNT2 = 0; //Reset Timer2 counter
  TCCR2A = 0; //Disable Timer2 compare / PWM
  TCCR2B = (1 << CS20) | (1 << CS21); //Set prescaler
  
  //Synchronize registers
  while (ASSR & ( (1 << TCN2UB) | (1 << TCR2AUB) | (1 << TCR2BUB) )) ;

  
  TIFR2 = 0; //Clear interrupt flags
  TIMSK2 = (1 << TOIE2); //Enable overflow interrupt

}

void loop()
{
  if (lastQuaterSec != quarterSecPassed)
  {
    lastQuaterSec = quarterSecPassed;
    Serial.print(F("Quater seconds passed: "));
    Serial.println(quarterSecPassed);
  }
}

Danois90: Try this:

Tried it, it works. But then again, it doesn't use sleep. Also you are using delay(), which I am trying to avoid at all costs, because then Arduino would wake up every millisecond.

I forgot to mention on my previous post, that it worked if I didn't put it to sleep.

The delay was used to prevent flooding, I've removed it from the post.. Your sleep code must be wrong if it does not work, the code I posted works perfectly with sleep..

Have you set the fuses for a low power 32 kHz crystal and do you have the correct capacitors on the crystal pins? That is essential.

I use the attached RTC code for sensors, using a bares bones ATmega328, with a watch crystal on the OSC pins. It wakes up every second. Also includes code to calibrate the internal RC oscillator using the 32kHz xtal as a reference.

simple_RTC_test.ino (9.41 KB)

jremington: Have you set the fuses for a low power 32 kHz crystal and do you have the correct capacitors on the crystal pins? That is essential.

No. If he has followed the ArduinoToBreadboard guide (event though I do not like it, this one is better) and burned the bootloader, the fuses will be set to use the internal 8MHz (as OP says) oscillator. Timer2 does not require extra fuses to be set/cleared in order to use an external 32KHz oscillator for timing - it does, however, require the right amount of capacitance and this depends on which crystal OP is using. I think the way OP is putting the chip to sleep is the real problem. A lot of stuff is disabled prior to sleep for no reason. This is the sleep code I usually use:

void lullaby()
{
  ADCSRA &= ~bit(ADEN); //Disable ADC
  MCUSR = 0; //Clear reset flags

  set_sleep_mode(SLEEP_MODE_PWR_SAVE); //Set sleep mode
  noInterrupts(); //Disable interrupts
  sleep_enable(); //Enable sleep

  //Disable BOD
  MCUCR = bit(BODS) | bit(BODSE);
  MCUCR = bit(BODS);
  
  interrupts(); //Enable interrupts
  sleep_cpu(); //Go to sleep
  sleep_disable(); //Disable sleep mode after wakeup
}

Prior to sleep, OP should also switch all possible pins to input and disable pullups in order to prevent any current leakage - after wakeup all pins must be restored to what they where before sleep or functionality may break.

A very good and informative page with info on power saving and sleep cycles can be found here.

Timer2 does not require extra fuses to be set/cleared in order to use an external 32KHz oscillator for timing - it does, however, require the right amount of capacitance and this depends on which crystal OP is using.

Sorry, I wrote "fuses" but was thinking of setting the AS2 bit of ASSR as required to select the low power oscillator on the OSC1/OSC2 pins, for asynchronous operation of Timer2. It is done correctly in the OP.

Tried this sketch:

#define F_CPU 8000000UL
#include <util/delay.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <LiquidCrystal.h>

LiquidCrystal lcd(5, 6, 7, 8, 9, 10); //RS, E, D4, D5, D6, D7

byte contrast = 18;
volatile boolean quarterSecPassed = false;
byte quarterSecCntr = 0;
long counter = 0;

void setup()
{
  //setup ports
  DDRC &= B11110000; //A0-A3 to inputs
  PORTC |= B00001111; //A0-A3 pullup
  DDRD |= B00011000; //led backlight PD4 and buzzer PD3 to output
  PORTD &= B11100111; //led backlight PD4 and buzzer PD3 low

  //setup display
  lcd.begin(8, 1); //uses DOGM081
  //all values shown here are for 5V supply
  lcd.command(0x21); //function set, switch to instruction table 1
  _delay_us(30);
  lcd.command(0x1C); //bias set
  _delay_us(30);
  lcd.command(0x51); //power control (Icon off)
  _delay_us(30);
  lcd.command(0x6A); //follower control
  _delay_us(30);
  lcd.command(0x70); //contrast set, initialize to 32 (0-63, 0 = totally blank)
  _delay_us(30);
  lcd.command(0x20); //function set, switch back to instruction table 0
  _delay_us(30);
  setContrast(contrast);

  //set up Timer2
  noInterrupts(); //disable all interrupts
  TCCR2B = 0; //stop TIMER2
 
  TIMSK2 = 0; //disable TIMER2
  ASSR |= (1 << AS2); //clock input to Timer2 from XTAL1/XTAL2
  delayms(2000); //Wait for the crystal to settle
  TCNT2 = 0; //preload timer [256 - (32,768kHz/32/4)] == 4Hz
  TCCR2A = 0; //normal count up mode, no port output
  TCCR2B |= ((1 << CS20) | (1 << CS21)); //32 prescaler
  while (ASSR & ((1 << TCN2UB) | (1 << TCR2AUB) | (1 << TCR2BUB) ));
  TIFR2 = 0; // clear interrupt-flag 
  TIMSK2 = (1<<TOIE2);  // enable TIMER2 overflow interrupt
  interrupts(); //enable all interrupts
}

ISR(TIMER2_OVF_vect) //update time
{
  quarterSecPassed = true;
}

void loop()
{
  if (quarterSecPassed)
  {
    quarterSecPassed = false;
    quarterSecCntr++;
    if (quarterSecCntr == 4)
    {
      counter++;
      lcd.setCursor(0, 0);
      lcd.print(counter);
      quarterSecCntr = 0;
    }
  }
  goToSleep();
}

void setContrast(int value)
{
  if (( value >= 0x00) && (value <= 0x3F))
  {
    int val_lower = value & 0x0F;
    int val_upper = value >> 4;
    lcd.command(0x21); //function set, switch to instruction table 1
    _delay_us(30);
    lcd.command(0x70 | val_lower); //contrast set, initialize to 32 (0-63, 0 = totally blank)
    _delay_us(30);
    lcd.command(0x50 | val_upper); //power control
    _delay_us(30);
    lcd.command(0x20); //function set, switch back to instruction table 0
    _delay_us(30);
  }
}

void goToSleep()
{
  set_sleep_mode(SLEEP_MODE_PWR_SAVE);
  sleep_enable();
  //cli();  //Disable interrupts
  //MCUCR = bit(BODS) | bit(BODSE);
  //MCUCR = bit(BODS);
  //sei();  //Enable interrupts
  sleep_cpu();
  sleep_disable();
}

void delayms(int ms) 
{
  for (int i=0; i<ms; i++) _delay_ms(1);
}

This sketch behaves exactly as my first sketch, it counts twice as fast than it should.
Accidentally I noticed that if I uncomment the lines in goToSleep() , it works as it should.
Is it necessary/mandatory to disable BOD while in sleep mode ? (If possible I would like not to disable it.)
Does it have some other effect, than just saving current (and not having BOD active)?

About the capacitors between ground and OSC1/OSC2 pins: what would be a suitable value for them
when using a 32,768kHz crystal ? (Now using 22p for both)
The crystal is a generic no-brand crystal, the component seller can’t provide datasheets for it.

Makes no sense, the code I've posted works exactely as it should and if it does not work for you, you must have other code messing things up then. You do not need to disable BOD, but it is just a waste of power to leave it on during sleep. I posted a link in #9 about sleep cycles - you should look into that.