In reality the blinking part is just merely a means by which to indicate the application is working. The focus though is to bring these elements together;
- SLEEP in Power Down mode
- IRQ0 on falling edge
- WDT interrupt only
Code was developed for an Arduino or compatible using AS 7.0.1188. For brevities sake, I've redacted a lot of the code, but included the asm in attachments.
Essentially what I am going for is sort of a hand shaking functionality between WDT & IRQ0 which gave rise to it operating a lot like a dead man's switch. As most of my projects are going to be battery operated, utilizing SLEEP was the primary focus with triggering IRQ0 to wake up and finally using WDT as an interval timer. WDT was incorporated just because I could, but I don't really see this example as being the most practical use of watchdog, although it does work.
A: Turning off PRTWI, PRTM2, PRTM0, PRTM1, PRTSPI, PRUSART0, PRADC will probably become a staple in my applications and when needed, I'll just enable the ones I want or need. Notice that I've traded 0b11101111 for (1 << PRTWI) | (1 << PRTM2) etc, but in the source code it's documented with section and page number to datasheet.
ldi TEMP, 0b11101111 sts PRR, TEMP ; PRR is @ 0x64, beyond the reach of OUT
The button that I'm using is an EB0100410M that is only active while power is applied. This is why I've tied it to pin 2 to PORTD instead of toggling bit in EIMSK.
sbi PORTD, PORTD2 ; Pullup INT0 for falling edge. ldi TEMP, 0b0010 ; Table 31-2 pg 71 INT0 on falling edge sts EICRA, TEMP ; Set interrupt 0 sense control 69H ldi TEMP, 1 out EIMSK, TEMP ; Enable INT0 (13.2.2 pg 72)
and the final part of setup is to enable Power Down Sleep
ldi TEMP, 0b101 ; Set SM1 & SE Power Down Mode out SMCR, TEMP ; 10.11.1 pg 44
Now it's a matter of waiting for button press and then blink LED 5 times @ 2 sec intervals. I was surprised to notice how accurate WDT was, but every now and then I did get a bit of bounce in the button I think, but of the 20 times I tried, that only happened twice.
Loop: sbi PINB, PINB0 ; Enable push button, LED is on sei ; Re-enable interrupts sleep ; Wait for hit on INT0 or R20, R20 ; ISR modifies R20 so poll register brne PC - 1 ; until count is exhausted. // Functionally equivalent to code on pg 52, except I didn't reset // WDRF in MCUSR because WDT isn't set for RESET. cli ; Disable interrupts wdr ldi TEMP, T_Seq ; WDCE | WDE sts WDTCSR, TEMP ; Begin timed sequence sts WDTCSR, R20 ; Turn off WDT rjmp Loop ; Wait for another press on button
In a more comprehensive application this is definitely not the way to implement ISRs, but the focus was to get a handle of the principles of interrupts and to that end, they work.
Originally I had initialized prescaler in setup, but doing it this way saves code as I don't have to read WDTCSR and then write it back again uncessarily.
IRQ0_ISR: // Enable WDT with desired pre-scaler of 131072 cycles / tick. wdr ldi R20, T_Seq ; WDCE | WDE ldi TEMP, 0b1000110 ; WDIE = 1 and WDP 2:1 = 1, 3 & 0 = 0 sts WDTCSR, R20 ; Begin timed squence sts WDTCSR, TEMP ; Table 11-2 pg 55 // R20 is the count in seconds and LED will blink half as many times ldi R20, 10 ; Blink LED 5 times over 10 seconds sbi PINB, PINB0 ; Turn LED off, disabling button reti WDT_ISR: // Normally in any ISR registers that are altered should be preserved, // SREG. In this simple example it doesn't matter. dec R20 ; Decrement count sbi PINB, PINB1 ; Toggle indicator LED reti
Next rendition of this code will be to implement PWM so LED just has a short blink every second.