[CODE][ASM]Ubiquitous blink program with a twist Atmega328P

1 post / 0 new
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

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
 	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.

 // Enable WDT with desired pre-scaler of 131072 cycles / tick.

 	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

    // 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

Next rendition of this code will be to implement PWM so LED just has a short blink every second.