Search code examples
arduinointerruptavrarduino-unoavr-gcc

Why main() function gets executed again and again only when interrupt- ISR(ADC_vect) gets executed?


I am making a digital clock based project, using an atmega328p on a breadboard. I connected 4 buttons to the ADC3 channel, and I'm using INT0 as a trigger to start ADC conversion. I also enabled the TimerCounter1 to calculate the time.

I am having trouble with increasing the time using the increase button, because the setTime(3, 30) function call is in the int main() function and after pressing the button this function setTime(3, 30) is getting executed again and again. This happens only when ISR(ADC_vect) gets executed.

This is the part of my code I think is relevant:

xxx.ino

#include <avr/io.h>

int main()
{
  Serial.begin(9600);
  
  // PB5
  DDRB |= (1<<DDB5);

  // ### Want to see how many times these functions - __init__timer(), setTime(), __init__button() gets executed after the Interrupt ###
  PORTB ^= (1<<PORTB5);

  __init__timer();
  
  //setTime(3, 30);

  __init__button();

  while(1)
  { 

  }

  return 0;
}

void __init__timer()
{ 
  // Enable the global Interrupt
  SREG |= (1<<7);
  // Enable the output compare match on comapare A 
  TIMSK1 |= (1<<OCIE1A);

  // Set TimerCounter1 in CTC mode
  TCCR1A &= ~(1<<WGM10);
  TCCR1A &= ~(1<<WGM11);
  TCCR1B |= (1<<WGM12);
  TCCR1B &= ~(1<<WGM13);

  // Set the prescalr to 1024 so we get 15625 Hz pulses(counts) per second
  TCCR1B |= (1<<CS10);
  TCCR1B &= ~(1<<CS11);
  TCCR1B |= (1<<CS12);

  // Assign the OCR1A to 46875 (3 seconds)
  OCR1A = 46875;
}

ISR(TIMER1_COMPA_vect)
{ 
  Serial.println("ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled");
}

void __init__button()
{
  // Select the voltage referance - AREF pin
  ADMUX &= ~(1<<REFS1);
  ADMUX &= ~(1<<REFS0);

  // Select the input channel - ADC3
  ADMUX |= (1<<MUX0);
  ADMUX |= (1<<MUX1);
  ADMUX &= ~(1<<MUX2);
  ADMUX &= ~(1<<MUX3);

  // Configure the ADCSRA registor

  SREG |= (1<<7);
  ADCSRA |= (1<<ADIE);

  // Set the prescalar to 128 - 125KHz
  ADCSRA |= (1<<ADPS0);
  ADCSRA |= (1<<ADPS1);
  ADCSRA |= (1<<ADPS2);

  // Enable the INT0 
  EIMSK |= (1<<INT0);
  EICRA |= (1<<ISC00);
  EICRA |= (1<<ISC01);

  ADCSRB &= ~(1<<ADTS0);
  ADCSRB |= (1<<ADTS1);
  ADCSRB &= ~(1<<ADTS2);
  ADCSRA |= (1<<ADATE);

  // This will start the conversion if trigger INT0 is pressed
  ADCSRA |= (1<<ADEN);
}

ISR(ADC_vect)
{
  if((ADC < 750) && (ADC > 600))
  {
    Serial.println("Button 1 -> PB5 toggled");
  }
  if((ADC < 500) && (ADC > 350))
  {
    Serial.println("Increase time button pressed... -> PB5 toggled");
    // increaseTime();
  }
  if((ADC < 300) && (ADC > 200))
  {
    Serial.println("Decrease button pressed... -> PB5 toggled");
    // decreaseTime();
  }
  if((ADC < 200) && (ADC > 100))
  {
    Serial.println("button 4 -> PB5 toggled");
  }
}

And this is the Serial output:

ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled
ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled
ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled
ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled
ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled
ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled
ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled
Increase time button pressed... -> PB5 toggled
ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled
ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled
Increase time button pressed... -> PB5 toggled
ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled
Increase time button pressed... -> PB5 toggled
I⸮Increase time button pressed... -> PB5 toggled
ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled
Increase time button pressed... -> PB5 toggled
ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled
ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled
ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled
ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled

My question:

Why are the instructions in the int main() function executed again and again when ISR(ADC_vect) gets executed, but not when ISR(TIMER1_COMPA_vect) gets executed? The PB5 pin toggles only when ISR(ADC_vect) gets executed.


Solution

  • Finally I found what was wrong - Why main() function was executing again only when ISR(ADC_vect) interrupt occurs.

    Just like the answer above by @djph said, main() function only executes once and then it loops forever in while(1); And when an interrupt occurs the program pause the current task and jump from the loop-while(1); to the respective interrupt routine -ISR() and after that the program jump back to the loop-while(1), means it will not execute the main() function again after an ISR() interrupt execution.

    So, In my case the main() function is executing again only if the ISR(ADC_vect) interrupt occurs. Why?

    The Answer:

    If we look at how I initialized the ADC ->

    void __init__button() function:

    // Select the voltage referance - AREF pin
      ADMUX &= ~(1<<REFS1);
      ADMUX &= ~(1<<REFS0);
    
      // Select the input channel - ADC3
      ADMUX |= (1<<MUX0);
      ADMUX |= (1<<MUX1);
      ADMUX &= ~(1<<MUX2);
      ADMUX &= ~(1<<MUX3);
    
      // Configure the ADCSRA registor
    
      SREG |= (1<<7);
      ADCSRA |= (1<<ADIE);
    
      // Set the prescalar to 128 - 125KHz
      ADCSRA |= (1<<ADPS0);
      ADCSRA |= (1<<ADPS1);
      ADCSRA |= (1<<ADPS2);
    
      // Enable the INT0 
      EIMSK |= (1<<INT0);
      EICRA |= (1<<ISC00);
      EICRA |= (1<<ISC01);
    
      ADCSRB &= ~(1<<ADTS0);
      ADCSRB |= (1<<ADTS1);
      ADCSRB &= ~(1<<ADTS2);
    
      // 
      ADCSRA |= (1<<ADATE);
    
      // This will start the conversion if trigger INT0 is pressed
      ADCSRA |= (1<<ADEN);
    

    ADC Auto Trigger Enable Bit, The ADC will start a conversion on a positive edge of the selected trigger signal - which is INT0.

    ADCSRA |= (1<<ADATE); 
    
      // Enable the INT0 
      EIMSK |= (1<<INT0);
      EICRA |= (1<<ISC00);
      EICRA |= (1<<ISC01);
    

    So that means if I press the button not only the ADC interrupt will occur but also the INT0 interrupt will occur too, But in my code there is no ISR(INT0_vect) routine. This is the problem... After adding ISR(INT0_vect) routine to my code the main() function is not executing again when ISR(ADC_vect) interrupt occurs,

    ISR(INT0_vect)
    { 
      // Do nothing
    }
    

    There may be better ways to do this but this works for me.

    I don't know why main() function executes again if that particular interrupt occurs of which we forget to add the respective routine - ISR() to the code.

    EDIT :

    I think I do know now why main() gets executed again - If an unexpected interrupt occurs (interrupt is enabled and no handler is installed, which usually indicates a bug), then the default action is to reset the device by jumping to the reset vector.

    ISR() interrupts in detail : www.nongnu.org/avr-libc/user-manual/group__avr__interrupts

    And this is the better way to handle empty interrupts -

    EMPTY_INTERRUPT(INT0_vect);