ATmega1284p Watchdog : Curious problem with prescaler

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

Hi there,

 

I tried to set the watchdog timeout interval but was unable to do so. The watchdog does reset at ~16ms but I couldn't set it to any other time intervals. My code is as follows:

#include <avr/wdt.h>
#include <avr/io.h>
#define F_CPU 8000000L
#include <util/delay.h>

#define DDR_SW DDRB
#define PORT_SW PORTB
#define PIN_SW PINB
#define B_SW PINB1

#define DDR_LED DDRB
#define PORT_LED PORTB
#define B_LED PORTB3

int main(void)
{
    MCUSR &= ~(1<<WDRF);
    wdt_disable();
    
    DDR_SW &= ~_BV(B_SW);		
    DDR_LED |= _BV(B_LED);		
    PORT_SW |= _BV(B_SW);
    PORT_LED |= _BV(B_LED);

    // continues executing after a button is pressed
    while((PIN_SW & _BV(B_SW))){
    }
    _delay_ms(1000);
    
    wdt_enable(WDTO_2S);
    while(1)
    {

    }
}

I am aware that the WDCE bit needs to be set when writing the prescaler value (by calling the function below) but that too, yielded no result. 

 

 

void WDT_enableInterrupt(uint8_t timeout){
	cli();
    
    uint8_t b = _BV(WDCE) | _BV(WDIE);
    b |= timeout;
	WDTCSR = b; 
    
	sei();
}

 

Anyone has some insights on this problem? Thanks in advance.

 

Edit:

Thanks Joey for pointing out. I have changed

PORT_SW to PORTB,

PIN_SW to PINB,

B_SW to PINB1

and the same to B_LED for consistency.

 

This topic has a solution.

A newbie exploring the AVR land using Atmel Studio 6 (Version: 6.2.1548 - Service Pack 2)

Last Edited: Tue. May 19, 2015 - 01:29 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Does it reset immediately after a power-up?  Or does it reset after you press the button?

 

Is the WDTON fuse programmed?

 

Post the .lss for your code (the first one).

"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]

 

Last Edited: Mon. May 18, 2015 - 05:55 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Joey

 

It resets only after I press the button and the WDTON bit in the fuse is unprogrammed. 

 

I have attached the lss. Thanks.

Attachment(s): 

A newbie exploring the AVR land using Atmel Studio 6 (Version: 6.2.1548 - Service Pack 2)

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

Hmm.  I can't spot the problem.  As far as I can tell it should work.

 

One oddity (not related to WDT):

#define PORT_SW PINB
#define PIN_SW PINB1
.
.
.
    PORT_SW |= _BV(PIN_SW);

 

This becomes:

    PINB |= _BV(PINB1);

 

While this has the effect of setting the bit in PORTB for the switch, thus enabling the internal pullup, that is only by virtue of the fact that the bit was cleared.  Writing to a PINx register will toggle bits, not set them.  You should not confuse your code by defining a macro to make a pin register look like a port register.

 

 

"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

Let's see the complete test program that failed.  Including the ISR.

 

From the fragments posted, we cannot tell whether there is a problem setting up the WD in interrupt-only mode, or merely a missing ISR.

 

Indeed, when I first worked with WD interrupt, it took some trial-and-error to find the correct combination.  Noone AFAIK has taken up the mantle to add interrupt to GCC WD setup functionality.  There should be extensive threads on this, but perhaps not many for your AVR family.

 

CodeVision Wizard generates:

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

where the #pragmas assure the timed sequence.  I bet it will work.  (I seem to recall that the timeout value needs to be set in both instructions.  Can't hurt...)

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

The OP's first code block is the complete test program.  No interrupts.  He only mentioned that it also didn't work with the interrupt enable code in the second block.

 

The .lss seems to reflect the test program, and my own .lss generated from the same code.  Can't see what could be stopping it from working.

 

I've written my own header which adds to AVR Libc's wdt.h and gives support for interrupt only mode, but only for a few devices.

"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

Hi Joey and theusch

 

Maybe my microcontroller is just broken. sad

 

From the assembly, I can understand writing to WDCE on line 0xDE and and then the WDE and WDP bits on line 0xE4,

but why are we saving and restoring SREG on line 0xD8 and 0xE2?

    wdt_enable(WDTO_2S);
  d2:	2f e0       	ldi	r18, 0x0F	; 15
  d4:	88 e1       	ldi	r24, 0x18	; 24
  d6:	90 e0       	ldi	r25, 0x00	; 0
  d8:	0f b6       	in	r0, 0x3f	; 63
  da:	f8 94       	cli
  dc:	a8 95       	wdr
  de:	80 93 60 00 	sts	0x0060, r24
  e2:	0f be       	out	0x3f, r0	; 63
  e4:	20 93 60 00 	sts	0x0060, r18
  e8:	ff cf       	rjmp	.-2      	; 0xe8 <main+0x4a>
 

A newbie exploring the AVR land using Atmel Studio 6 (Version: 6.2.1548 - Service Pack 2)

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

The global interrupt bit 'I' is in SREG.  The whole register is saved, in order to save the state of global interrupts.  The CLI then disables interrupts during the timed sequence to prevent an interrupt from breaking that timed sequence.  SREG is thereafter restored, which restores the previous state of global interrupts i.e. if it was enabled before, it will be enabled again.  If it was disabled before, it will still be disabled.

 

You may note that SREG is restored before the last write which is part of the timed sequence.  This takes advantage of the fact that the hardware guarantees that, after interrupts are enabled, the next instruction will be executed before any pending interrupts are serviced.  This shortens the amount of time during which interrupts are disabled by 2 cycles, always a good thing.

 

Still don't know why it's not working for you... but I doubt it's a bad chip.  Just for kicks, do you have another chip to test?

"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

So those instructions are due to cli() and sei()? That makes sense.

Anyway, I did as you said and tested in on a new chip and it worked. Believing that my old chip to be faulty, I tested it once more to make sure only to find it working as well. frown

 

Thanks guys. 

A newbie exploring the AVR land using Atmel Studio 6 (Version: 6.2.1548 - Service Pack 2)

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

I then moved on to test the watchdog interrupt, but no luck on both chips. Here is the complete code:

 

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

#define DDR_SW DDRB
#define PORT_SW PORTB
#define PIN_SW PINB
#define B_SW PINB1

#define DDR_LED DDRB
#define PORT_LED PORTB
#define B_LED PORTB3

#define WATCHDOG_PERIOD WDTO_2S

#define wdt_enable_int(value)   \
__asm__ __volatile__ (  \
"in __tmp_reg__,__SREG__" "\n\t"    \
"cli" "\n\t"    \
"wdr" "\n\t"    \
"sts %0,%1" "\n\t"  \
"out __SREG__,__tmp_reg__" "\n\t"   \
"sts %0,%2" "\n\t" \
: /* no outputs */  \
: "M" (_SFR_MEM_ADDR(_WD_CONTROL_REG)), \
"r" (_BV(_WD_CHANGE_BIT) | _BV(WDIE)), \
"r" ((uint8_t) ((value & 0x08 ? _WD_PS3_MASK : 0x00) | \
_BV(WDIE) | (value & 0x07)) ) \
: "r0"  \
)


void WDT_disableInterrupt(){   
    cli();
    MCUSR &= ~(1<<WDRF);
    wdt_reset();
    WDTCSR |= (1<<WDCE);
    WDTCSR = 0x00;
    sei();
}

ISR(WDT_vect){
    PORT_LED ^= _BV(B_LED);
}

int main(void)
{
    WDT_disableInterrupt();
    
    DDR_SW &= ~_BV(B_SW);		// set PA1 as input
    DDR_LED |= _BV(B_LED);		// set PA7 as output
    PORT_SW |= _BV(B_SW);
    PORT_LED |= _BV(B_LED);
    
    wdt_enable_int(WATCHDOG_PERIOD);
    while(1)
    {
    }
}

 

 

As far as I can tell, their are identical except every WDE bit is replaced with WDIE. 

 

// modified function to generate interrupt
    wdt_enable_int(WATCHDOG_PERIOD);
  e0:	27 e4       	ldi	r18, 0x47	; 71
  e2:	80 e5       	ldi	r24, 0x50	; 80
  e4:	90 e0       	ldi	r25, 0x00	; 0
  e6:	0f b6       	in	r0, 0x3f	; 63
  e8:	f8 94       	cli
  ea:	a8 95       	wdr
  ec:	80 93 60 00 	sts	0x0060, r24
  f0:	0f be       	out	0x3f, r0	; 63
  f2:	20 93 60 00 	sts	0x0060, r18
  f6:	ff cf       	rjmp	.-2      	; 0xf6 <main+0x20>
// original function: watchdog reset
    wdt_enable(WDTO_2S);
  d2:	2f e0       	ldi	r18, 0x0F	; 15
  d4:	88 e1       	ldi	r24, 0x18	; 24
  d6:	90 e0       	ldi	r25, 0x00	; 0
  d8:	0f b6       	in	r0, 0x3f	; 63
  da:	f8 94       	cli
  dc:	a8 95       	wdr
  de:	80 93 60 00 	sts	0x0060, r24
  e2:	0f be       	out	0x3f, r0	; 63
  e4:	20 93 60 00 	sts	0x0060, r18
  e8:	ff cf       	rjmp	.-2      	; 0xe8 <main+0x4a>

 

 

Attachment(s): 

A newbie exploring the AVR land using Atmel Studio 6 (Version: 6.2.1548 - Service Pack 2)

Last Edited: Tue. May 19, 2015 - 07:15 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As far as I can tell, their are identical except every WDE bit is replaced with WDIE. 

But that isn't the CV sequence (which has worked for me on several AVR8 models, tho I don't have '1284 apps).

 

WDCE and no WDE in the first write; WDIE and no WDE in the second write.  (Is the CV correct?  Dunno.)

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.

Last Edited: Tue. May 19, 2015 - 12:58 PM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Whether or not you intend to enable the WDT reset via the WDE bit, you must still write the WDE and WDCE bits as 1 in the first step of the timed sequence required to change the prescaler.

 

#define wdt_enable_int(value)                                                 \
  __asm__ __volatile__ ("in   __tmp_reg__, __SREG__                     \n\t" \
                        "cli                                            \n\t" \
                        "wdr                                            \n\t" \
                        "sts  %0,          %1                           \n\t" \
                        "out  __SREG__,    __tmp_reg__                  \n\t" \
                        "sts  %0,          %2                           \n\t" \
                      : /* no outputs */                                      \
                      : "M" (_SFR_MEM_ADDR(_WD_CONTROL_REG)),                 \
                        "r" ((uint8_t) (_BV(_WD_CHANGE_BIT) | _BV(WDE))),     \
                        "r" ((uint8_t) ((value & 0x08 ? _WD_PS3_MASK : 0x00)  \
                                        | _BV(WDIE) | (value & 0x07)) )       \
                      : "r0"                                                  \
                       )

 

"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

You sir, are a legend.

 

The Watchdog always on (WDTON) fuse, if programmed, will force the Watchdog Timer to System Reset mode. With the fuse programmed the System Reset mode bit (WDE) and Interrupt mode bit (WDIE) are locked to 1 and 0 respectively. The sequence for clearing WDE and changing time-out configuration is as follows:

  1. In the same operation, write a logic one to the Watchdog change enable bit (WDCE) and WDE. A logic one must be written to WDE regardless of the previous value of the WDE bit.
  2. Within the next four clock cycles, write the WDE and Watchdog prescaler bits (WDP) as desired, but with the WDCE bit cleared. This must be done in one operation.

 

I think I subconsciously dismissed this section in the datasheet as irrelevant, since it was referring to the WDTON = 1 always on mode.

 

Thanks once again.

 

 

A newbie exploring the AVR land using Atmel Studio 6 (Version: 6.2.1548 - Service Pack 2)

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

To be fair, the datasheets are at times not as clear as they could be :)

 

Although there are some variations between different devices w.r.t. the various peripheral modules, by and large they are similar or identical from one AVR to the other.  If you run into confusion, it can be helpful to peruse the datasheets of other devices.  You will find some of them may have been written in a slightly different voice, which could help clarify a point or two.

 

Be wary though of actual differences.  The WDT in the ATiny25/45/85 is (IINM) different from the WDT in the ATmega48/88/168/328.

 

 

"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]

 

Last Edited: Wed. May 20, 2015 - 02:18 AM