Sleep mode and wdt interrupt problem

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

I'm using an ATMega328p and my development environment is Atmel Studio 6 with GCC. I need to minimize power use while periodically monitoring the state of some inputs. The plan is to go into sleep mode for 2120 ms and wake for 120 ms, look at my stuff, then go back to sleep. I'm using the watchdog timer for scheduling. I initially set the wdt for 2 sec, go to sleep, when I wake I set the wdt for 120ms and go back to sleep, then I wake up, do my stuff for 120ms and start the cycle again. All works until the third cycle when I want to do stuff. After I wake and go to my monitor function a watchdog reset occurs. I have verified this in the debugger by checking the MCUSR. I don't understand why the watchdog reset is occurring because I always set the WDTIE. Here is my code in hopes that someone can enlighten me about where I'm failing. Listen is the only variable changed in the ISR and it is declared volatile.

void EnterSleep(void)
{
	//enable interrupts for sensors and power good
	PCICR |= (1<<PCIE1);
	PCMSK1 = (1<<PCINT10);
	PCMSK2 = (1<<PCINT17)|(PCINT18);
	
	//shut down everything
	ADCSRA &= ~(1<<ADEN);
	PRR = 0xff;
	cli();
	
	//set some state variables
	Listen = 1;
	Naptime = true;
	NaptimeWait = false;
	
	//get ready for bed
	set_sleep_mode(SLEEP_MODE_PWR_DOWN);
	cli();
	wdt_disable();
	wdt_enable(WDTO_2S);
	WDTCSR |= (1<<WDIE);
	sleep_enable();
	sei();
	//nighty night
	sleep_cpu();
	//when I wake up I fall into the loop
	while(Naptime)
	{
		switch (Listen)
		{
		case(0):
			PORTD |= (1<<LEDB);
			wdt_enable(WDTO_2S);
			WDTCSR |= (1<<WDIE);
			Listen = 1;
			set_sleep_mode(SLEEP_MODE_PWR_DOWN);
			cli();
			sleep_enable();
			sei();
			//nighty night
			sleep_cpu();
			break;
			
		case(1):
			//PORTD |= (1<<LEDB);
			wdt_enable(WDTO_120MS);
			WDTCSR |= (1<<WDIE);
			Listen = 2;
			set_sleep_mode(SLEEP_MODE_PWR_DOWN);
			cli();
			sleep_enable();
			sei();
			//nighty night
			sleep_cpu();
			break;
			
		case(2):
			wdt_enable(WDTO_120MS);
			WDTCSR |= (1<<WDIE);
			Listen = 0;
			Sleep_Monitor();
			break;
		}
	}
}

void Sleep_Monitor(void)
{
	while(Naptime)
	{
		_delay_ms(1);
		//monitor some stuff
		
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

eejay wrote:
I don't understand why the watchdog reset is occurring because I always set the WDTIE.
You haven't shown an ISR for WDT_vect. You should, so we can see what's going on.
		case(2):
			wdt_enable(WDTO_120MS);
			WDTCSR |= (1<<WDIE);
			Listen = 0;
			Sleep_Monitor();
			break;
.
.
.
void Sleep_Monitor(void)
{
	while(Naptime)
	{
		_delay_ms(1);
		//monitor some stuff
		
	}
}

Your case(2): clause doesn't issue a sleep command, and it simply goes into an eternal loop (Naptime is always true). The call to wdt_enable() will enable the reset mode of the WDT (have a look at wdt.h). You subsequently set WDIE, placing the WDT into reset and interrupt mode. In this mode, the first WDT time-out will cause an interrupt. Hardware will then auto-clear WDIE, so that the next WDT time-out will cause a reset.

You should post whatever

//monitor some stuff

actually is. Does it change Naptime?

JJ

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

Quote:

eejay wrote:
I don't understand why the watchdog reset is occurring because I always set the WDTIE.

Quote:

wdt_enable(WDTO_120MS);
WDTCSR |= (1<<WDIE);


Consider again what is needed to change the watchdog--the 4-cycle-limited, double-write.

Then look at your generated code to do the WDIE.

Now, perhaps indeed the 4-cycle limitation doesn't apply. But if you want interrupt-only don't you need to clear WDE?

The GCC gurus will need to comment on the proper way to do this when the provided macros don't help.

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

Are you doing the wdt_disable() in .init3 like it tells you in the user manual for ?

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

The project is a security device with a rechargeable battery. If line power is lost it goes into this low power mode and monitors 2 sensors and the power good output from the battery charger. Those are the pin change interrupts. If any of those fire Naptime is set to false and it goes into normal power mode (not sleep). It also needs to check if a 40 khz signal is present on another pin. That's what the wake period and the Sleep_Monitor() function are for if that is present at the end of the period I want the micro to go back to sleep and the cycle starts over. I believe that the limitations to change the wdt only apply if the WDTON fuse is set (I could be wrong about that). It is not set. I will try clearing the WDTEN bit. I found it curious that there is no provision for using the wdt in interrupt mode in wdt.h. The wdt ISR is used for a timeout indicator for the I2C interface when not in sleep mode and nothing is executed when I am in sleep mode.

ISR(WDT_vect)
{
	if(!Naptime)
	{
		/*******************************/
		//if not sleep mode we use for the 12c timeout
	
		i2cFailed = true;
		i2cTimeOut = 0;
		WDTCSR |= (1<<WDIE);
	}
	
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I found it curious that there is no provision for using the wdt in interrupt mode in wdt.h.

Well, thanks for the background on the app. I guess you got what you paid for with your infinite-value toolchain. (But, also, CodeVision Wizard doesn't have interrupt-only provisions either.) Apparently the powers-that-be couldn't envision how real people might use this mode. :twisted:

There was some thrashing-out on this Forum about 5 years ago when this feature came into use. As I recall, the datasheet explanation takes a bit of re-reading and trials.

But the datasheet is fairly clear on interrupt-only. And AFIAK the timed sequence applies. (True, the datasheet just says it is needed for prescaler value..)

Quote:
• Bit 6 - WDIE: Watchdog Interrupt Enable
When this bit is written to one and the I-bit in the Status Register is set, the Watchdog Interrupt is enabled. If WDE is cleared in combination with thissetting, the Watchdog Timer is in Interrupt Mode, and the corresponding interrupt is executed if time-out in the Watchdog Timer occurs. If WDE is set, the Watchdog Timer is in Interrupt and System Reset Mode.

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

Quote:
I believe that the limitations to change the wdt only apply if the WDTON fuse is set
Thats true for some devices, but not all. Have a look at the datasheet for the ATtiny85, and compare it to the datasheet for the 328P.
Quote:
I will try clearing the WDTEN bit.
WDE.

Why use wdt_enable() which enables reset mode, only to clear it right after? Just have a look at avr/wdt.h to be sure you know how it works. Then write your own macro that does what you need.

Quote:
I found it curious that there is no provision for using the wdt in interrupt mode in wdt.h.
I've wondered about that myself. However, not all devices can generate a WDT interrupt. I guess they were going for the lowest common denominator. When I've needed it, I've just created my own macros. If you just avoid setting WDE altogether, you won't have to worry about a WDT reset...

ISR(WDT_vect)
{
	if(!Naptime)
	{
		/*******************************/
		//if not sleep mode we use for the 12c timeout
	
		i2cFailed = true;
		i2cTimeOut = 0;
		WDTCSR |= (1<<WDIE);
	}
	
}

... and you won't have to keep setting WDIE.

clawson wrote:
Are you doing the wdt_disable() in .init3 like it tells you in the user manual for ?
This is good advice, whether or not you ever intend to use the reset mode. A software bug, a low power condition, or some other problem could accidentally enable the WDT in reset mode, which would lead to an eternal loop of WDT resets. The datasheed is also clear on this:
Quote:
Note: If the Watchdog is accidentally enabled, for example by a runaway pointer or brown-out condition, the device will be reset and the Watchdog Timer will stay enabled. If the code is not set up to handle the Watchdog, this might lead to an eternal loop of time-out resets. To avoid this situation, the application software should always clear the Watchdog System Reset Flag (WDRF) and the WDE control bit in the initialization routine, even if the Watchdog is not in use.

JJ

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]