In the first part of this article we managed to put the AVR into power-down mode and were thereby able to lower the current consumption in idle mode to 0.0 mA. While this is great compared to the 22 mA before any optimization, there is no way to wake up the controller yet to do some actual work. This is what we're going to do in this episode.

The goal is to wake up the AVR via a button connected to one of the GPIOs, turn on an LED for two seconds and then put the controller back into sleep mode. For this to work, the first action is to configure the GPIOs for both the wake-up button and the LED.

static void configurePortPins() {
	DDRB |= (1<<PB6); // Output LED
	DDRB &= ~(1<<PB2); // INT2 input
	PORTB |= (1<<PB2); // pull-up for INT2
}

PB2 has been chosen to serve as the button input because it can be used as an external interrupt source called INT2. As the other pin of the wake-up button is connected to GND, the internal pull-up resistor is enabled.

The next step is the interrupt configuration. Note that INT2 is the only external interrupt pin that can be triggered by an input edge while the controller is in power-down mode (data sheet page 66). For INT0 and INT1, edge triggering is not an option because they rely on the I/O clock (which is halted in power-down mode).

static void configureInterrupts() {
	// Enable interrupt on falling edge of INT2
	MCUCSR &= ~(1<<ISC2);
	GICR |= (1<<INT2);

	// Enable interrupts globally
	sei();
}

Now the controller can be woken up via a falling edge on PB2. After triggering, program execution continues after the sleep instruction issued withing the main loop. Although there is no actual interrupt handling required, an interrupt service routine (ISR) implementation must be provided (why?):

ISR(INT2_vect) {
}

The new main() function looks like this:

int main(void) {
	configurePortPins();
	configureInterrupts();
	while(1) {
		setSleepModePowerDown();
		enterSleepMode();
		disableSleepMode();
		toggleLED();
	}
	return 0;
}

The sleep instruction mentioned above is executed inside of enterSleepMode(). On wake-up, this function returns so toggleLED() is called. Refer to the example repository at github.com/ronalterde/avr-sleep for the complete working program. As promised, I extended the repo so it now contains both the code for the first part and also for the current one.

Conclusion

While the microcontroller still consumes almost nothing in idle mode, it can be immediately woken up by a single key press. See the following video how this looks like (note the current measurement in the background):

As always, if you have any thoughts on my blog post, feel free to contact me on twitter: @ronalterde.

References