Timer2 wake from sleep fails with any optimization

Go To Last Post
23 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have an Arduino board with mega328p that I've replaced the crystal and caps with a 32.768 KHz crystal. The cpu is clocking off the internal osc and Timer 2 is running off the crystal. I wrote a simple program that tests the Timer 2 interrupts and toggles the LED. I wrote it in a way that makes it easy to add power-save sleep. Compiling with avr-gcc 4.8.1 in Atmel Studio, it works as expected at -O0. But with any optimization the cpu fails to wake from sleep. Looking at the disassembly, nothing jumps out at me. I've replaced pretty much every line, one at a time, with assembler, and it persists in working at -O0 and not working at -O1 or above. In case it's an out-of-sequence problem I've also tried the memory barrier trick between each line to no avail. I keep thinking I am not setting up the chip correctly, but reading the Timer 2, interrupts, and power-save sleep notes from Atmel and other people's code from the web doesn't show me what I've done wrong. Rule number one is that it's never the compiler that's at fault, so please tell me what I've done wrong. Here's the test program. Commenting out the sleep line, or compiliing at -O0 the LED blinks. Thanks for any insight.

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>

volatile unsigned char tick;

ISR (TIMER2_OVF_vect)
{
	tick++;
}

int main(void)
{
	DDRB = 0x20;			// pin B5 is output
	TCCR2B = 2;				// set T2 prescaler to 8
	ASSR = (1 << AS2);		// enable 32khz xtal osc for Timer 2
	//while (ASSR & ((1 << TCN2UB)|(1 << TCR2BUB)));	// wait for osc not busy
	set_sleep_mode(SLEEP_MODE_PWR_SAVE);	//SMCR=6;
	TIMSK2 = (1 << TOIE2);	// enable Timer 2 interrupt
	sei();					// global interrupt enable
	sleep_enable();
	while(1)
	{
		if(tick & 0x1)
		{
			PORTB &= ~0x20;	// LED off
		} else
		{
			PORTB |= 0x20;	// LED on
		}
		sleep_cpu();
	}
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
	//while (ASSR & ((1 << TCN2UB)|(1 << TCR2BUB)));	// wait for osc not busy

Why commented out?

 

Also, are you sure the 32kHz oscillator is up and stable at the time you enter sleep? It may take surprisingly long... (and the datasheet warns about this).

 

JW

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wek3 wrote:

	//while (ASSR & ((1 << TCN2UB)|(1 << TCR2BUB)));	// wait for osc not busy

Why commented out?

It's the most complicated line in the program, assembler-wise, so I sometimes commented it out before simulating to make the disassembly window easier to follow. When the program runs, it appears to run fine without it.

wek3 wrote:

Also, are you sure the 32kHz oscillator is up and stable at the time you enter sleep? It may take surprisingly long... (and the datasheet warns about this).

That's an interesting thought. I have checked with an oscilloscope and the crystal is always oscillating but that doesn't address startup. But since the cpu is always running from internal osc, wouldn't it just be longer in sleep the first time?

I will try putting in some delay today when I get home from work.

Thanks for the idea and the reply!

Bob

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It's the most complicated line in the program, assembler-wise, so I sometimes commented it out before simulating to make the disassembly window easier to follow. When the program runs, it appears to run fine without it.

 

wait:
    LDS  r24, ASSR
    ANDI r24, (1<<TCN2UB)|(1<<TCR2UB)
    BRNE wait
    

 Untested.    But it hardly looks complicated to me!

 

I would think like you.   i.e.   the first blink doesn't arrive until the crystal has started oscillating.

 

I can see no reason for -O0 to work but -O1 does not.    Except for extra cycles from the brain-dead -O0.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

make sure that the compiler don't rearrange your code when optimized.

For the compiler the port things and "sleep_cpu()" are unrelated.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Adding a one second delay after turning on Timer 2 oscillator just gave me an extra second before nothing happens. However, I got it to work, sort of, by changing the mask that I compare "tick" with to 4 instead of 1. This is strange because many hours ago I put in little tests to see if sleep and wake were happening. Sleep worked but there was no wake. Now it seems I can get sleep and wake by changing something that should have no connection. And changing the mask to 8, 16, 32, etc slows the LED down as one would expect. But if the mask is 1 or 2 it doesn't blink.

 

Furthermore, if I comment out the sleep line, or switch optimization off, it changes the blink rate, which should have any effect.

 

I'm going to output another timer off the internal clock see if it is stable. More news as it happens.

 

Thanks to everyone who replied!

 

Bob

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What happens if you *don't* put the processor into sleep?

 

Wire up another LED, switch it ON before and OFF after sleep_cpu().

 

And post the disassembly.

 

JW

Last Edited: Tue. Nov 4, 2014 - 07:51 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I was fascinated by this thread.    So I put a mega168 and a 32kHz crystal in my STK500.

 

Sure enough,   compiling with -O0 in AS4.19 gives a flashing LED.

Compiling with -O1 or -Os gives a dim flashing LED.

 

I will look at the disassembly.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What is the frequency of blinking? Did it change with the change of optimization? What is the reason of "dimming" - could you please look at it with an oscope?

 

Jan

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have rearranged the code slightly.    I can't manage watching a LED blink at 16Hz.    So I reduced the ISR() frequency to 1Hz.

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>

volatile unsigned char tick;
#define LEDMASK (1<<PB5)
#define WAKEMASK (1<<PB4)

ISR (TIMER2_OVF_vect)
{
	tick++;
}

int main(void)
{
	DDRB = LEDMASK | WAKEMASK;			// pin B5 is output
	TCCR2B = (5<<CS20);		// set T2 prescaler to 128
	ASSR = (1 << AS2);		// enable 32khz xtal osc for Timer 2
	while (ASSR & ((1 << TCN2UB)|(1 << TCR2BUB)));	// wait for osc not busy
	set_sleep_mode(SLEEP_MODE_PWR_SAVE);	//SMCR=6;
	TIMSK2 = (1 << TOIE2);	// enable Timer 2 interrupt
	sei();					// global interrupt enable
	sleep_enable();
	while(1)
	{
		if(tick & 0x1)
		{
			PORTB &= ~LEDMASK;	// LED on (STK500 active-low)
		} else
		{
			PORTB |= LEDMASK;	// LED off
		}
		PORTB |= WAKEMASK;		// light off before we go to sleep
		sleep_cpu();
		PORTB &= ~WAKEMASK;		// light on when we wake up
//	    asm("nop");         // -O0 needs no delay
		_delay_us(21);      // -O1 effectively 168 cycles 
//		_delay_us(22);      // -O2 effectively 176 cycles 
	}
}

 

I am totally confused.    With -O0,   the ISR() takes a few extra cycles but not significant in the scheme of things.

With -O1 or -O2,   it actually needs those deliberate delays!

With -O0,   the main loop() is obviously fairly inefficient.    But it certainly does not take an extra 150 cycles.

 

The STK500 has conventional active-low LEDs that are actually driven by an external buffer.

The 'dim' version is simply the on-time being VERY brief.     Even I can see the brief blink.

 

I must have missed something somewhere about SLEEP behaviour.

 

David.

Last Edited: Tue. Nov 4, 2014 - 10:35 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi. David ..

I nthis week I work with RTC oscilator too on ATmega328p ..

I create my sensor board with nRF24L01+ ... powered with 2xAA batteries.. but if use 32kHz crystal to controll my program also count seccond and have problem .. set 1sec cycle .. but not working ..

I use AVR Studio 4.19 .. and -O0 optimization .. not working .. but I check that if go sleep after interupt very fast.. (only few cycle ..) then generate interupt again .. extra interupt .. then change my code and

add more cycle .. then all work .. still have problem with optimization .. don't execute any code line .. and don't know ..

 

check crystal with osciloscope... use fuse bit in mcu and set MCU clock by 32kHz crystal and set clock output to TCO .. and check if output frequenci in this pin is exactly 32kHz .. if all working then problem is only your code ..

if run your code (in simulator or real with emulator ) then show wiev by dissasembler .. and you will see .. real asm code..

 

regards..
 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So if you compile with -O0, then you see the PB5 LED on for 1 second and off for 1 second? And PB4 LED off permanently, with maybe a very, very faint blink whet it is not in sleep, i.e. once in a second?

 

And if you compile with -O1 etc. *without* the delays, then what do you see? PB5 blinking briefly once in a second or once in two seconds? What does the oscilloscope say? And what do you see on PB4? A blink at the same time as PB5, less faint than in previous case; or something else?

 

My guess is, that the raw cause is the low input clock frequency (32768Hz) of Timer2. If I guess the "overflow" logic in TIM2 correctly, it keeps the interrupt flag on until an input clock arrives into the prescaler. And that takes around 244 cycles of the main clock source (8MHz/32768Hz = cca 244). Thus, what we see is, that multiple interrupts/wakeups happen in rapid succession, within a 1/32768Hz period; and it's only a chance whether the number of these interrupts is even or odd, so we see the LED blinking or not.

 

You could confirm this by feeding a very slow adjustable clock (e.g. STK500's "software" oscillator) into PB6, the required delay should change accordingly. You could also try to output the state of "tick" counter to the LEDs, instead of blinking.

 

Jan

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes,   Jan.    I can see the brief flash on PB4 (WAKE) every 1 second.

Without the delay,    I see a brief flash on PB5 every 1 second (when there should be an edge).

With the delay,     I see PB5 blink at 0.5Hz (1 sec on, 1 sec off)

 

I really can't be *rsed to get my old analog oscilloscope out.   However I will look at the signals with a Saleae.

 

Surely the OVF2 wakes the AVR.    The 8MHz RC starts up after 6 cycles (SUT).    The AVR responds to the interrupt immediately.     The OVF2 flag is cleared automagically.

So I would expect the ISR() to be entered before PORTB &= ~WAKEMASK; is executed.

 

So all that I need to do is shake a pin in the ISR() and examine the time relationships.

 

First I will make a cup of tea.     Then I will try my plan.    Afterwards I will use the STK500 External Clock.     As far as I can see,   it will still have the same 6 cycle (SUT).

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

David,

 

after tea, please don't toggle any more LEDs, but output the "tick" variable onto PORTB (all the LEDs).

 

Jan

 

Last Edited: Tue. Nov 4, 2014 - 12:02 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well,   I actually just connected the LA to PORTB.    So I saw states rather than LEDs.

 

I was amazed.     The ISR fires immediately after the AVR wakes up.    And then continues to fire for ~30us (one 32kHz period).

Regardless of resetting TOV2 or disabling TOIE2,   it just fires !!!

 

So depending on Optimisation settings,    it can manage an odd or even number of ISR()s.    With -O1 it does 4 ISR()s when I have the shake_pin statements in the ISR().

 

I am sure that I have done several "RTC" projects with ASSR.     And they probably ticked once per second and incremented a counter with nested sec / min/ hour/ day/ month/year.     None of 'those' ISR()s would have taken 30us.    And yet I have never encountered this before.

 

I might re-write this in ASM to see how many ISR()s I can get in 30us.     After all,   I can do the shake and tick with SBI, CBI and no pushes.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My lasting conclusion is that the loop() that wakes up with TOV2 and goes to sleep again must remain awake for 30us !    Or it has a multiple interrupt.

ISR (TIMER2_OVF_vect, ISR_NAKED)
{
	PORTB &= ~ISRMASK;		// on
	PORTB |= ISRMASK;		// off
	reti();
}
    
        ...
	while(1)
	{
		PINB = LEDMASK;         // toggle it
		PORTB |= WAKEMASK;		// light off before we go to sleep
		sleep_cpu();
		PORTB &= ~WAKEMASK;		// light on when we wake up
	}
}

Ok,   it just shakes some pins so that I can see what is happening with a Logic Analyser.

Since the ISR() and the foreground loop() body is pretty swift,   I get 7 ISR()s firing for every asynchronous TOV2.

 

So if you are obsessed with minimal waking time,    you need to get as much done in this 30us.     But there is no point in doing it quicker !!!

 

Let's face it.    Most apps are going to do something worthwhile.    You can always prescale by 1024.   i.e. update RTC every 8 seconds.   This is a duty-cycle of 3.75 ppm.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Very interesting results.  [darned site; can't see all the thread pages...] IIRC, you tested with '328.   I've had some '88 and '48 production apps with watch crystal.  But I probably never ran into the situation because the watch crystal was only used for aux timekeeping, to calibrate the internal oscillator periodically for UART work.  Wakeup was done with events (pin change) and watchdog.

 

If I have a convenient '88 app on the bench I'll see if I can reproduce.  If as described by the experimental results and OP's situation, then this is errata territory isn't it?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

IIRC, you tested with '328.

Hmmm...  I downloaded the latest datasheet, just to look at the errata.  Apparently '328/'328P are at chip revision D.  For rev. A and B there is an interesting errata re 32kHz oscillator being "inaccurate"...

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No,   it was a DIP28 ATmega168 vintage wk11 2008.    I suppose I could swap a DIP28 ATmega328P from a UNO clone.

 

There are several differences between the 168 and the later 168P.     Whereas the 328P arrived before the 328.

I bet that they all behave the same.

 

Mind you,    most apps will do something when they wake up.     And if you are running at 1MHz,    most of your 30us is tied up in a real ISR().

 

I just assumed that I could disable TOIE2 as soon as I entered the ISR() and that would stop the wakeup behaviour.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It's a tremendous relief to know that this problem is repeatable. I am not insane or a total idiot. Thank you!

Bob

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

...problem is repeatable. I am not insane or a total idiot...

Insanity: doing the same thing over and over again and expecting different results.

Albert Einstein

Read more at http://www.brainyquote.com/quote...

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
Very interesting results.

 

+1.

 

Thanks David for hunting this down.

 

I'd say someone should tell Atmel, but given my last experience I'd say, they are not interested.

 

JW

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well,   I ran the "fast loop" program on a Mega32 and a Mega324PA.

The 324PA (wk43 2010) was similar to the 168.    i.e.  it managed 5 loops in the 30us.

The 32 (wk28 2005) just did a single ISR.    i.e. it behaved nicely !

 

I really can't be bothered to hook up different chips.    It might be interesting if someone has a 2014 manufacture chip.

 

In practice,   it is unlikely that you will ever see this feature.    After all,   you are probably going to increment a counter and set a flag.    Which is going to involve PUSHES, POPs etc in the ISR().    And you will at least test the flag before going back to sleep.     If you are running @ 1MHz,   you have used up your 30us !

 

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>
#if defined(__AVR_ATmega32__)
#define TIMSK2 TIMSK
#define TCCR2B TCCR2
#define TCR2BUB TCR2UB
#endif

volatile unsigned char tick;
#define LEDMASK  (1<<PB5)
#define WAKEMASK (1<<PB4)
#define ISRMASK  (1<<PB3)

ISR (TIMER2_OVF_vect, ISR_NAKED)
{
	PORTB &= ~ISRMASK;		// on
	PORTB |= ISRMASK;		// off
	reti();
}

int main(void)
{
	DDRB = LEDMASK | WAKEMASK | ISRMASK;			// pin B5 is output
	TCCR2B = (5<<CS20);		// set T2 prescaler to 128
	ASSR = (1 << AS2);		// enable 32khz xtal osc for Timer 2
	while (ASSR & ((1 << TCN2UB)|(1 << TCR2BUB)));	// wait for osc not busy
	set_sleep_mode(SLEEP_MODE_PWR_SAVE);	//SMCR=6;
	TIMSK2 = (1 << TOIE2);	// enable Timer 2 interrupt
	sei();					// global interrupt enable
	sleep_enable();
	while(1)
	{
#if defined(__AVR_ATmega32__)
		PORTB ^= LEDMASK;         // toggle it
#else
		PINB = LEDMASK;         // toggle it on modern AVR
#endif
		PORTB |= WAKEMASK;		// light off before we go to sleep
		sleep_cpu();
		PORTB &= ~WAKEMASK;		// light on when we wake up
	}
}

David.