[Solved] ATTiny2313A - Not waking up from sleep (POWER DOWN) with ISR(WDT_OVERFLOW_vect)

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

Hey everybody

 

I have an issue waking my ATTiny2313A up from POWER DOWN sleep. 

 

My original code was for an ATTiny85 with a tinycore on the arduino compiler.

Now I am converting my code to be used within Atmel Studio 7 with a ATTiny2313A.

 

Here is my code:


#include <avr/io.h>
#define F_CPU 8000000UL  // 8 MHz
#include <util/delay.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
#include <avr/interrupt.h> //for ISR interrupt)

// ATtiny2313A outputs for PWM
const char RedPin = PB2;
const char GreenPin = PD5;
const char BluePin = PB3;

// Variables for PWM color
unsigned char Red = 0;
unsigned char Blue = 0;
unsigned char Green = 0;

// Other variables
unsigned char SleepTime = 8;

ISR(WDT_OVERFLOW_vect)
{
	// = executed on wake-up
}

void setup_watchdog(unsigned char timerPrescaler)
{
	if (timerPrescaler > 9 ) timerPrescaler = 9; //Limit incoming amount to legal settings

	unsigned char bb = timerPrescaler & 7;
	if (timerPrescaler > 7) bb |= (1<<5); //Set the special 5th bit if necessary

	//This order of commands is important and cannot be combined
	MCUSR &= ~(1<<WDRF); //Clear the watch dog reset
	WDTCR |= (1<<WDCE) | (1<<WDE); //Set WD_change enable, set WD enable
	WDTCR = bb; //Set new watchdog timeout value
	WDTCR |= _BV(WDIE); //Set the interrupt enable, this will keep unit from resetting after each int

	//Sets the watchdog timer to wake us up, but not reset
	//0=16ms, 1=32ms, 2=64ms, 3=128ms, 4=250ms, 5=500ms
	//6=1sec, 7=2sec, 8=4sec, 9=8sec
	//From: http://interface.khm.de/index.php/lab/experiments/sleep_watchdog_battery/
}

int main(void)
{

	DDRB |= (1 << RedPin);                  // PWM output on PB2 - 0C0A
	DDRD |= (1 << GreenPin);				// PWM output on PD5 - 0C0B
	DDRB |= (1 << BluePin);					// PWM output on PD5 - 0C1B
	TCCR0A = (15 << COM0B0) | (3 << WGM00);  // fast inverted PWM on 0C0 A&B
	TCCR0B = (1 << CS00);   // clock source = CLK, start PWM -> check libraries do not want another value
	TCCR1A = (3 << COM1A0) | (1 << WGM10);
	TCCR1B = (1 << CS10) | (1 << WGM12);

	OCR0A=0xFF; //Red
	OCR0B=0xFF; //Green
	OCR1A=0xFF; //Blue

	Red=0x00;
	Green=0x55;
	Blue=0xAA;

	set_sleep_mode(SLEEP_MODE_PWR_DOWN);

	while (1)
	{
		if(Red==0xFF) Red=0x00;
		else Red++;
		if(Green==0xFF) Green=0x00;
		else Green++;
		if(Blue==0xFF) Blue=0x00;
		else Blue++;

		OCR0A=~(Red); //Red
		OCR0B=~(Green); //Green
		OCR1A=~(Blue); //Blue

		_delay_ms(10);

		if (Red == 0xFF)
		{  //sleepy mode
		DDRB &= ~(1 << RedPin);                 // PWM input on PB2 - 0C0A
		DDRD &= ~(1 << GreenPin);				// PWM input on PD5 - 0C0B
		DDRB &= ~(1 << BluePin);				// PWM input on PD5 - 0C1B
		setup_watchdog(SleepTime); //Setup watchdog to go off after Xsec
		sleep_mode(); //Go to sleep! Wake up Xsec later and continue
		DDRB |= (1 << RedPin);                  // PWM output on PB2 - 0C0A
		DDRD |= (1 << GreenPin);				// PWM output on PD5 - 0C0B
		DDRB |= (1 << BluePin);					// PWM output on PD5 - 0C1B
		}
	}
}

 

The PWM works perfectly till the Red = 0xFF trigger is reached.

The LED's switch off and the AVR powers down. (I tested this with a current meter, it does enter the POWER DOWN mode)

 

But when I expect it to wake up, and continue with the code, nothing happens.

 

I have searched this forum and google, but nowhere I can find answers that work.

 

Some notes: 

 - First I was still using the WDT_vect (for all tinies except the 2313..) I switched this to WDT_OVERFLOW_vect   -> Did not solve the issue

 - WDTCR vs WDTCSR: The manual for the 2313A talks about WDTCSR. I still had WDTCR in my code as it was originally for and ATTiny85. But as it now happens, the iotn2313a.h file has it defined as WDTCR. So no adjustment should be needed. I checked the register and it is the correct bit. Except for the 2313 it is defined as 0x21 and for the 2313A as 0x021. But this does not matter as it is the same value I presume.

 

 

Thanks for the help in advance.

Last Edited: Tue. Feb 2, 2016 - 08:43 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It might be helpful to know optimization level, and show the .LSS fragment for the watchdog setup to ensure that timing is being met.

 

Are you sure you have the correct vector name now?  Peek into the chip-include; and/or no warning about vector name during build?

 

WDT interrupt is indeed a bit tricky, and doesn't seem to be orthogonal across AVR8 models.

 

I haven't used this model, but let's see what sequence CodeVision Wizard generates...

// Watchdog Timer initialization
// Watchdog Timer Prescaler: OSC/1024k
// Watchdog timeout action: Interrupt
#pragma optsize-
WDTCR=(0<<WDIF) | (0<<WDIE) | (1<<WDP3) | (1<<WDCE) | (0<<WDE) | (0<<WDP2) | (0<<WDP1) | (1<<WDP0);
WDTCR=(1<<WDIF) | (1<<WDIE) | (1<<WDP3) | (0<<WDCE) | (0<<WDE) | (0<<WDP2) | (0<<WDP1) | (1<<WDP0);
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif

Arbitrarily I picked the longest timeout value (which is usually ~8 seconds).

 

My tool only does a two-step init, that boils down to

WDTCR= (1<<WDP3) | (1<<WDCE) | (1<<WDP0);
WDTCR= (1<<WDIF) | (1<<WDIE) | (1<<WDP3)  | (1<<WDP0);

 

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

Hey Theusch

 

Thank you for your answer. I tried some more thing, still does not work.. :-)

 

theusch wrote:

It might be helpful to know optimization level, and show the .LSS fragment for the watchdog setup to ensure that timing is being met.

 

You kind of lost me here, I still am very new to AVR programming. Could you clarify what you want me to do here?

 

theusch wrote:

Are you sure you have the correct vector name now?  Peek into the chip-include; and/or no warning about vector name during build?

 

Pretty sure:

 

I checked the iotn2313a.h file

#define WDT_OVERFLOW_vect_num  18
#define WDT_OVERFLOW_vect      _VECTOR(18)  /* Watchdog Timer Overflow */

On page 48 of the ATTiny2313a manual is stated that the WDT overflow vector is vector 19, address 0x0012.

 

The 18 in the iotn2313a.h file is decimal for 0x0012. 0x0012 = 18

 

Please correct me if I am making false assumptions here :-)

 

NOTE: No warnings during build.

 

theusch wrote:

WDT interrupt is indeed a bit tricky, and doesn't seem to be orthogonal across AVR8 models.

 

I haven't used this model, but let's see what sequence CodeVision Wizard generates...

// Watchdog Timer initialization
// Watchdog Timer Prescaler: OSC/1024k
// Watchdog timeout action: Interrupt
#pragma optsize-
WDTCR=(0<<WDIF) | (0<<WDIE) | (1<<WDP3) | (1<<WDCE) | (0<<WDE) | (0<<WDP2) | (0<<WDP1) | (1<<WDP0);
WDTCR=(1<<WDIF) | (1<<WDIE) | (1<<WDP3) | (0<<WDCE) | (0<<WDE) | (0<<WDP2) | (0<<WDP1) | (1<<WDP0);
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif

Arbitrarily I picked the longest timeout value (which is usually ~8 seconds).

 

My tool only does a two-step init, that boils down to

WDTCR= (1<<WDP3) | (1<<WDCE) | (1<<WDP0);
WDTCR= (1<<WDIF) | (1<<WDIE) | (1<<WDP3)  | (1<<WDP0);

 

I see some differences here with the code that I am using.

 

(Note, the timeout value changes in my code, depending on which function calls the timeout. That is why I thought the code I used was pretty handy.)

 

First of all, shouldn't all statements be like WDTCR |= ... ?

Because now the second statement overwrites the first one as you are not OR'ing them? Or is this a wrong assumption from my part?

 

Anyways I also didn't use the WDIF bit in my code. I integrated it and it still didn't work. Here are some codes I tried:

 

	MCUSR &= ~(1<<WDRF); //Clear the watch dog reset
	WDTCR |= (1<<WDCE) | (1<<WDE); //Set WD_change enable, set WD enable
	WDTCR |= (1<<bb); //Set new watchdog timeout value
	WDTCR |= (1<<WDIF) | (1<<WDIE); //Set the interrupt enable, this will keep unit from resetting after each int

 

	MCUSR &= ~(1<<WDRF); //Clear the watch dog reset
	WDTCR |= (1<<WDCE) | (1<<WDE); //Set WD_change enable, set WD enable
	WDTCR = (1<<bb); //Set new watchdog timeout value
	WDTCR |= (1<<WDIF) | (1<<WDIE); //Set the interrupt enable, this will keep unit from resetting after each int

 

And some more of all possible combinations..

 

 

Last Edited: Mon. Feb 1, 2016 - 09:46 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Mardec wrote:
And some more of all possible combinations..

Including the ones that my Wizard generated?

 

No, you don't want an |= in the sequence anywhere.

 

Mardec wrote:
You kind of lost me here, I still am very new to AVR programming. Could you clarify what you want me to do here?

 

Make a complete small test program.  In the .LSS file, you should be able to find this code section. 

 

 

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

Yes I tried your code too:

 

void setup_watchdog(unsigned char timerPrescaler)
{
	if (timerPrescaler > 9 ) timerPrescaler = 9; //Limit incoming amount to legal settings

	unsigned char bb = timerPrescaler & 7;
	if (timerPrescaler > 7) bb |= (1<<5); //Set the special 5th bit if necessary

	//This order of commands is important and cannot be combined
	//MCUSR &= ~(1<<WDRF); //Clear the watch dog reset
	//WDTCR |= (1<<WDCE) | (1<<WDE); //Set WD_change enable, set WD enable
	//WDTCR = (bb) | (1<<WDIF); //Set new watchdog timeout value
	//WDTCR |= _BV(WDIE); //Set the interrupt enable, this will keep unit from resetting after each int
	WDTCR= (1<<WDP3) | (1<<WDCE) | (1<<WDP0);
	WDTCR= (1<<WDIF) | (1<<WDIE) | (1<<WDP3)  | (1<<WDP0);
	//Sets the watchdog timer to wake us up, but not reset
	//0=16ms, 1=32ms, 2=64ms, 3=128ms, 4=250ms, 5=500ms
	//6=1sec, 7=2sec, 8=4sec, 9=8sec
	//From: http://interface.khm.de/index.php/lab/experiments/sleep_watchdog_battery/
}

Same effect, MCU goes to sleep, and like me in the morning, it does not want to wake up.

 

May I ask why you do not use a |= in this case? I thought this was necessary for almost all register adjustments?

 

 

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

Data from the .LSS file:

 

ISR(WDT_OVERFLOW_vect) 
{
  5e:	1f 92       	push	r1
  60:	0f 92       	push	r0
  62:	0f b6       	in	r0, 0x3f	; 63
  64:	0f 92       	push	r0
  66:	11 24       	eor	r1, r1
	// = executed on wake-up
}
	WDTCR= (1<<WDP3) | (1<<WDCE) | (1<<WDP0);
 124:	31 bd       	out	0x21, r19	; 33
	WDTCR= (1<<WDIF) | (1<<WDIE) | (1<<WDP3)  | (1<<WDP0);
 126:	21 bd       	out	0x21, r18	; 33

 

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

;)  Why don't I see an SEI or equivalent in your code?

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

Mardec wrote:
May I ask why you do not use a |= in this case? I thought this was necessary for almost all register adjustments?

Is there a sale on |= in full-reel quantities or something?

 

I've opined on this before.  If you are going to initialize a register -- then initialize it.  Don't introduce possible RMW (read-modify-write) considerations.  Compare the .LSS for your |= sequences with the direct assignaments you posted.

 

And in this case, |= only adds bits, and you have to turn off the WDE when doing the WDIE, for sure, right?  (AVR models vary a bit, but see the datasheet....)

 

 

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:

;)  Why don't I see an SEI or equivalent in your code?

 

Theusch

 

Thank you very much :-)

 

I saw the usage of sei and cli in some example codes, but didn't really understand what they were for.

And didn't include the atomic.h file so I got build errors when I did.

Newer used them with the Arduino compiler, as I assume now, the tinyCore did that for me..

 

But the code is fully functional now. Thank you for the help and tips! I appreciate it :-)

 

Code now:

 

if (timerPrescaler > 9 ) timerPrescaler = 9; //Limit incoming amount to legal settings

	unsigned char bb = timerPrescaler & 7;
	if (timerPrescaler > 7) bb |= (1<<5); //Set the special 5th bit if necessary

	cli();
	//This order of commands is important and cannot be combined
	MCUSR &= ~(1<<WDRF); //Clear the watch dog reset
	WDTCR |= (1<<WDCE) | (1<<WDE); //Set WD_change enable, set WD enable
	WDTCR = bb; //Set new watchdog timeout value
	WDTCR |= _BV(WDIE); //Set the interrupt enable, this will keep unit from resetting after each int
	sei();

 

Last Edited: Tue. Feb 2, 2016 - 08:43 PM