NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » Application Resetting after Adding Interrupt Handler

September 14, 2012
by jlaskowski
jlaskowski's Avatar

I'm connecting my ATMega168 to a CC3000 WiFi chip. The start-up sequence is that the MCU raises a line, called power-enable (PWR_EN), on the CC3000 and the CC3000 soon lowers its SPI_IRQ line back to MCU to indicate the CC3000 is ready to talk SPI with the MCU. I have the CC3000's SPI_IRQ connected to PD2.

When I raise PWR_EN, I can tell that the CC3000 is lowering SPI_IRQ by polling PD2 and turning on a yellow LED when it goes low:

while (1) {
  spi_irq_value = PIND & (1<<PD2);
  if (spi_irq_value == 0) {
    turn_on_yellow();
    break;
  }
}
while (1) {
   delay_ms(1000);
}

It worked fine, but I want to have PD2 serviced by an interrupt handler instead of polling PD2 like I do in the above code. So, I added this to my start-up configuration code:

// Set the Global Interrupt Flag to allow interrupts
SREG |= (1<<SREG_I);

// enable interrupt from CC3000's SPI_IRQ on PD2
EIMSK |= (1<<INT0);    // Enable INT0
EICRA |= (1<<ISC01);   // Trigger on falling edge
EICRA &= ~(1<<ISC00);  // Trigger on falling edge

I also added a red LED on PC2 and this code:

// set PC2 as output
DDRC |= (1<<DDC2);

void turn_on_red() {
  PORTC |= (1<<PC2);
}

void turn_off_red() {
  PORTC &= ~(1<<PC2);
}

...and here's my interrupt handler:

ISR(PCINT0_vect)
{
  turn_on_red();
}

Instead of turning on the red light, it restarts the application. Since the interrupt handler is only triggered on the falling edge, when the application is restarted, SPI_IRQ is already low and so it's not constantly restarting.

Any idea why my application is restarting instead of the interrupt handler being executed? Is my handler defined incorrectly?

September 14, 2012
by BobaMosfet
BobaMosfet's Avatar

jlaskowski--

while(1)
   {
   spi_irq_value = PIND & (1<<PD2);
   if(spi_irq_value == 0)
      {
      turn_on_yellow();
      break;
      };
   };

while(1)
   delay_ms(1000);

Side Notes:

You only need to use '{' adn '}' around compound statements, although sometimes you might put them to avoid confusion in how the code looks.

Your top statement could be done using a do...while():

do
   {
   spi_irq_value = PIND & (1<<PD2);
   }while(spi_irq_value);

Notice, I don't use an '==' to test against zero? That's because everything is tested against zero. It's either zero, or it isn't. In other words, the statement says "do...while(!zero)". In case that doesn't make sense, '!' = 'not'. Just say 'not' when you see it.

As for your problem with it resetting; could be current or code related. I don't know enough about your problem to say more.

BM

September 14, 2012
by BobaMosfet
BobaMosfet's Avatar

Just a thought, as I've been away from this MCU for about 3 months, can your ISR make that call to your routine turn_on_red()? At all? In order for it to call that function, perhaps it's stack frame at the time of ISR code being executed must know about your LED function via the jumptable available to it. There might be something more you need to do, to tell the chip what context you're referring to, to reach that function.

BM

September 14, 2012
by jlaskowski
jlaskowski's Avatar

Thanks for the "C" programming style tips.

I switched from using INT0, which is supposed to allow interrupts on falling edge or rising edge, to a Pin Change Interrupt PCINT, which triggers when the pin value changes. I was able to get that to work:

PCICR |= _BV(PCIE2);   //Enable PCINT2
PCMSK2 |= _BV(PCINT18); //Trigger on change of PCINT18 (PD2)
sei();  // enable global interrupts

and here's the interrupt handler:

ISR(PCINT2_vect)
{
  turn_on_red();
}

Since I wanted the interrupt to trigger only on falling-edge (when the PD2 goes from high to low), I can just add a test inside the interrupt handler:

ISR(PCINT2_vect)
{
    if (! (PIND & (1<<PD2))) {
        turn_on_red();
    }
}

I'm not sure why INT0 didn't work. If anyone can tell, please do.

September 14, 2012
by Noter
Noter's Avatar

I think you were setting the wrong interrupt vector so the right one didn't have anything in it thus when the interrupt occurred it reset the mcu. INT0_vect is the correct vector for PD2. The best way to be sure of your vectors is to look at them in the iom168p.h include file.

ISR(INT0_vect)
{
  turn_on_red();
}

This is from iom168p.h:

/* Interrupt Vector 0 is the reset vector. */
#define INT0_vect         _VECTOR(1)   /* External Interrupt Request 0 */
#define INT1_vect         _VECTOR(2)   /* External Interrupt Request 1 */
#define PCINT0_vect       _VECTOR(3)   /* Pin Change Interrupt Request 0 */
#define PCINT1_vect       _VECTOR(4)   /* Pin Change Interrupt Request 0 */
#define PCINT2_vect       _VECTOR(5)   /* Pin Change Interrupt Request 1 */
#define WDT_vect          _VECTOR(6)   /* Watchdog Time-out Interrupt */
#define TIMER2_COMPA_vect _VECTOR(7)   /* Timer/Counter2 Compare Match A */
#define TIMER2_COMPB_vect _VECTOR(8)   /* Timer/Counter2 Compare Match A */
#define TIMER2_OVF_vect   _VECTOR(9)   /* Timer/Counter2 Overflow */
#define TIMER1_CAPT_vect  _VECTOR(10)  /* Timer/Counter1 Capture Event */
#define TIMER1_COMPA_vect _VECTOR(11)  /* Timer/Counter1 Compare Match A */
#define TIMER1_COMPB_vect _VECTOR(12)  /* Timer/Counter1 Compare Match B */ 
#define TIMER1_OVF_vect   _VECTOR(13)  /* Timer/Counter1 Overflow */
#define TIMER0_COMPA_vect _VECTOR(14)  /* TimerCounter0 Compare Match A */
#define TIMER0_COMPB_vect _VECTOR(15)  /* TimerCounter0 Compare Match B */
#define TIMER0_OVF_vect   _VECTOR(16)  /* Timer/Couner0 Overflow */
#define SPI_STC_vect      _VECTOR(17)  /* SPI Serial Transfer Complete */
#define USART_RX_vect     _VECTOR(18)  /* USART Rx Complete */
#define USART_UDRE_vect   _VECTOR(19)  /* USART, Data Register Empty */
#define USART_TX_vect     _VECTOR(20)  /* USART Tx Complete */
#define ADC_vect          _VECTOR(21)  /* ADC Conversion Complete */
#define EE_READY_vect     _VECTOR(22)  /* EEPROM Ready */
#define ANALOG_COMP_vect  _VECTOR(23)  /* Analog Comparator */
#define TWI_vect          _VECTOR(24)  /* Two-wire Serial Interface */
#define SPM_READY_vect    _VECTOR(25)  /* Store Program Memory Read */
September 15, 2012
by jlaskowski
jlaskowski's Avatar

I'm sure that's it! I had PCINT0 instead of INT0. Thanks!

Post a Reply

Please log in to post a reply.

Did you know that you can connect to certain car computers via the OBD-II port with a microcontroller? Learn more...