3
\$\begingroup\$

Sorry for any noobiness, I'm more of a software engineer.

How do I use button interrupts in general?

As in, if I put my microcontroller to sleep, I'd like a button to wake it up.

How should I go about doing this?

Edit

Adding this to main, thanks to @jippie

DDRD &= ~(1<<PIND2)
PORTD |= 1<<PIND2
GICR  |=   ( 1 << INT0 );   // Enable INT0
MCUCR = (0<<ISC00)|(1<<ISC01);

sei();

And this outside of main

ISR(INT0_vect) {
  sleep_disable(); // If ISR got called while it was sleeping, this would work
  lcd.string("works");
};

ISR does work, but only if the device is not sleeping.

FIXED

Changing from INT0 to INT2 ( and moving from PIND2 to PINB2 ), it works...

According to documentation, INT0 and INT1 use level interrupts but PINB uses edge... so.. that's maybe why?

My voltage meter drops to .2 while in sleep mode, then wakes up from ISR.

\$\endgroup\$
2

2 Answers 2

2
\$\begingroup\$

\$\overline{\mathsf{RESET}}\$

A push button from \$\overline{\mathsf{RESET}}\$ to ground is the easiest way, but it forgets current state entirely.

Interrupts

For interrupts to work you need a pin that attributed as INTn and for ATmega32 you are limited to PB2, PD2 or PD3. Not all sleep modes support waking up by external interrupt and different sleep modes support edge or level triggered interrupts.

For thes pins you have to:

  • Enable the interrupt in the General Interrupt Control Register GICR;
  • Review the Interrupt Sense Control bits in MCUCR and MCUCSR.

You don't need to add an complete ISR (interrupt service routine) if all you want to do is wake up and continue where you left off, but you shouldn't rely on the default vector. It depends on the compiler what actually happens on a bad interrupt. Refer here for the name of the interrupt vector to use.

ISR() { INT0_vect , EMPTY_INTERRUPT };

For details check the chapter titled 'External Interrupts' in the ATmega32 datasheet.

For a button to ground on PD2 (INT0) code might look a bit like this (untested):

DDRD  &= ~ ( 1 << PD2 );    // Configure PD2 as input
PORTB |=   ( 1 << PD2 );    // Enable pull up resistor
// MCUCR default triggers on LOW level. Changing to edge trigger requires different sleep mode to wake up.
GICR  |=   ( 1 << INT0 );   // Enable INT0
\$\endgroup\$
3
  • \$\begingroup\$ So.. if a button is already connected to PIND5.. what would some example code look like to do this? \$\endgroup\$
    – dansch
    Mar 3, 2014 at 18:04
  • \$\begingroup\$ So, for PD2, is it possible to wake up from power_down mode? \$\endgroup\$
    – dansch
    Mar 3, 2014 at 18:19
  • 1
    \$\begingroup\$ @Funkodebat you can't use PD5 as interrupt input. I updated my answer. Added some (untested) sample code). \$\endgroup\$
    – jippie
    Mar 3, 2014 at 18:39
2
\$\begingroup\$

jippie's answer has the technical details, but it's possible that you're misunderstanding the sleep modes of the AVRs. The microcontrollers operate on a different realm than we're used to desktop computers working, and in fact clobber some of the same words to mean things that software developers don't expect.

The following is what I've gathered, as a software guy myself. I whole-heartedly welcome modifications to my post to correct things that I've gotten wrong!

The silicon is divided up into various building blocks for functionality. Timers, IO, SRAM, clock generator, execution core. The embedded world considers these peripherals. I got this wrong constantly, assuming peripherals were the things plugged into the outside of the MCU, since, well, that's what they are on a desktop computer.

What this means, in many cases, is that when the AVR is going to sleep it will shut down these other parts of the silicon. The first to go are some timers, followed perhaps by the main clock generator. The quick shutdown and startup of these peripherals means that microcontrollers are constantly flitting in and out of various sleep states, contrary to a big SLEEP, NOT SLEEP like a desktop computer. A pseudocode example making a dozen incorrect assumptions, of a ficticous serial communication:

// bad, the loop burns CPU time for no benefit
int message = 2830;
int counter = 0;
while (counter++ < 32) {
    sendBit(PIN_TX, message & (2 << counter));
    _delay_us(2500); // cpu spinwait is here
}

// better, uses sleeps
int message = 2830;
int counter = 0;

function sendData() {
    sendBit(PIN_TX, message & (2 << counter++));
    if (counter >= 32)  
         TIMER_A = DISABLED;
}

TIMER_A = TIMER_INCREMENT | TIMER_OVERFLOW_CALLBACK | ONE_PER_MICROSECOND;
TIMER_A_MAX = 2500;
TIMER_A_CALLBACK = &sendData;
DEFAULT_POWERSTATE = SLEEP3;

Again, you'll have to forgive the vague handwavey code. The idea here is that the serial communication has to wait a given number of microseconds between bits, and there's two ways to do that. One is by a spinwait, the other is interrupts. An interrupt will allow the MCU to service other interrupts while its waiting, or if nothing is going on, it will power down to a lower sleep mode. The corresponding equivalent in software development is yielding a thread vs. while (true);

Very little of this answers your original question, and I'm writing it as an answer to help attach additional information about how sleep can impact microcontrollers in ways that aren't necessarily intuitive to software developers.

edit:

To accomplish the wake-up cycle you're asking about would look something like the following:

function buttonPress() { _set_power_mode(FULL); }

void main() {
    while (true) {
        _set_power_mode(SLEEP2);
        // button press logic here
    }
}   

This is vaguely how I remember my MSP430 working, and all things considered the microcontrollers tend to follow roughly the same rules. _set_power_mode of a deep enough sleep was a blocking call. In spirit true to how my projects usually go, the AVR probably does it completely differently and my answer is totally moot.

\$\endgroup\$
3
  • \$\begingroup\$ So is it possible to listen to a button interrupt while the device is in sleep or do I need to periodically come out of sleep? \$\endgroup\$
    – dansch
    Mar 3, 2014 at 19:41
  • 1
    \$\begingroup\$ I'll update my post, check in a few minutes. \$\endgroup\$ Mar 3, 2014 at 19:53
  • \$\begingroup\$ its fine i got it working. I had the isr working right but it wasn't triggering from sleep mode, but a different pin did. something about edge vs level listening. \$\endgroup\$
    – dansch
    Mar 3, 2014 at 20:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.