Here's a recap of the situation and what I finally did to resolve it.
First, I was trying to make use of the WDTON fuse to ensure that the WDT is always active. No problem setting the fuse but every time I programmed it, my device just continuously ran through RESET at the DEFAULT rate, not the rate I was trying to set. It was like the AVR was not performing my code to change the prescaler for the WDT.
Here is the code I was starting with, based loosely on the code in the ATtiny85 document page 46:
Code:
int main(void)
{
// Initialize the WDT
wdt_reset(); // Atmel docs say to execute WDR first
MCUSR = 0x00; // Then clear the MCUSR flags
WDTCR |= (1<<WDCE) | (1<<WDE); // Write WDCE and WDE to 1 to "unlock" WDTCR and let us change it
WDTCR = (1<<WDP2) | (1<<WDP1); // Set prescale for 128K (~1.0 sec) with 0110 in WDP3-WDP0
while(1)
{
//TODO:: Please write your application code
}
}
Since I'm in "Safety level 2" (described on page 45) I have to:
Quote:
1. In the same operation, write a logical one to WDCE and WDE. Even though the WDE
always is set, the WDE must be written to one to start the timed sequence.
2. Within the next four clock cycles, in the same operation, write the WDP bits as desired,
but with the WDCE bit cleared. The value written to the WDE bit is irrelevant.
I would have thought that the:
WDTCR = (1<<WDP2) | (1<<WDP1);
would be able to meet the 4 clock cycle limit imposed on the WDTCR register. But it sure
seemed like it didn't work!
Looking at the generated ASM code (see the .lss file in the project folder) you can see the
AVR ASM instructions generated:
Code:
WDTCR = (1<<WDP2) | (1<<WDP1); // Set prescale for 128K (~1.0 sec) with 0110 in WDP3-WDP0
54: 81 e4 ldi r24, 0x41 ; 65
56: 90 e0 ldi r25, 0x00 ; 0
58: 26 e0 ldi r18, 0x06 ; 6
5a: fc 01 movw r30, r24
5c: 20 83 st Z, r18
As you can see we have 3 LDIs which take 1 cycle each, a MOVW which takes 1, and
an ST which takes 2 cycles. That totals 6 CLOCK CYCLES! No wonder this doesn't work
we missed our "window" by 2 clocks.
I chalked this up to a crappy C compiler and just wrote it in ASM (I started out with ASM on
the MOS 6502 CPU a long long time ago.) I modified the code to be:
Code:
// Initialize the WDT
wdt_reset(); // Atmel docs say to execute WDR first
MCUSR = 0x00; // Then clear the MCUSR flags
WDTCR |= (1<<WDCE) | (1<<WDE); // Write WDCE and WDE to 1 to "unlock" WDTCR and let us change it
//WDTCR = (1<<WDP2) | (1<<WDP1); // Set prescale for 128K (~1.0 sec) with 0110 in WDP3-WDP0
// The above C code takes to long, try it in ASM
asm("PUSH R18"); // Save R18 in case the compiler assumes it's not changed (1 clock)
asm("LDI R18, 0x06"); // Get our value (1<<WDP2) | (1<<WDP1) into R18 (1 clock)
asm("OUT 0x21, R18"); // Save in WDTCR (address 0x21) (1 clock)
asm("POP R18"); // Restore R18. Takes 1 clock but doesn't matter now.
//
Now we get the 0x06 written to the WDTCR in 3 clocks which is within our 4 clock limit. And after testing I
find that this works. But it turns out this is the HARD way around. User clawson helpfully pointed out that
the default C optimization on the AVRGCC compiler is not a wise choice. So I tried with optimization turned on
instead. So I selected "-O1" from the compuiler optimization level and went back to my original code. And now
the compiler does a much better job of generating the code. Better than I did because it knows what registers
it needs to save:
Code:
WDTCR = (1<<WDP2) | (1<<WDP1); // Set prescale for 128K (~1.0 sec) with 0110 in WDP3-WDP0
3a: 86 e0 ldi r24, 0x06 ; 6
3c: 81 bd out 0x21, r24 ; 33
//asm("LDI R18, 0x06"); // Get our value (1<<WDP2) | (1<<WDP1) into R18 (1 clock)
Now we see a much more efficient bit of code but more importantly, code that gets the needed value
into WDTCR in LESS than the 4 clock limit.
I'll follow this up with a more detailed WDT/WDTON fuse bit article when I'm done with this. I really didn't find
any good articles on this topic, which is what got me started down this rabbit-hole to begin with...
Enjoy and I hope this helps someone else new to AVR programming out in some way.
As always, any feedback or comments are welcome. |