carduinomicrocontrollerinterruptarduino-uno

Why is a 'while' loop needed in main() for calling an ISR in Arduino Uno?


I am writing C code that gets uploaded to an Arduino Uno. It was a simple exercise in learning how to call the ISR in C. Here is the code:

#include <stdio.h>
#include <stdlib.h>
#include <avr/interrupt.h>
#include <avr/io.h>

int main() {
    DDRB     = 0;
    PORTB    = 0;
    DDRB     = (1<<5);
    PORTB    = (0<<5);

    // Resetting the Timer/Counter1
    TCNT1H   = 0;
    TCNT1L   = 0;

    // Disabling all global interrupts
    SREG     = 0;

    // Defining prescalar

    //TCCR1B: ICNC1 ICES1 – WGM13 WGM12 CS12 CS11 CS10
    TCCR1B   = 0;
    //TCCR1B_reg = CS11_val; WORKING SETTINGS
    TCCR1B   = 0b00000101; // (1<<CS10_val) | (1<<CS12_val);

    // Setting up PWM mode

    //TCCR1A: COM1A1 COM1A0 COM1B1 COM1B0 COM1C1 COM1C0 WGM11 WGM10
    TCCR1A   = 0; // This is for waveform generation and CTC setting up mode;
    //TCCR1A = 0b10000000; // (1<<COM1A1_val);
    TCCR1A   = COM1A1;

    OCR1AH   = 0b10011100; // 0b00000000;
    OCR1AL   = 0b01000000; // 0b01000000;

    TIMSK1   = 0b00100010; // (1<<ICIE1_val) | (1<<OCIE1A_val); // 0b00100010; // Writing so that output compare A is set up

    // Enable global interrupts
    SREG     = (1<<7);

    while(1) {
    }

    return 0;
}

ISR(TIMER1_COMPA_vect) {
    PORTB = PORTB^(1<<5);
}

The interesting part is when I get rid of the "while(1){}", the code doesn't seem to work to toggle PinB5 (built-in LED on Arduino Uno). As soon as I added in the while loop, I saw pin B5 toggling. The weird part is that when I want to use the timer to make a PWM that outputs directly to a pin, I don't need to use the while loop.

Here is how I upload to Arduino Uno:

avr-gcc -Os -DF_CPU=16000000UL -mmcu=atmega328p -c -o ISR_example.o ISR_example.c
avr-gcc -mmcu=atmega328p ISR_example.o -o ISR_example
avr-objcopy -O ihex -R .eeprom ISR_example ISR_example.hex

read -p "Get ready to flash!"

# Flashing the Arduino:
avrdude -C/home/ashwini/Downloads/arduino-1.8.3/hardware/tools/avr/etc/avrdude.conf -v -patmega328p -carduino -P/dev/ttyACM0 -b115200 -D -Uflash:w:ISR_example.hex:i

Solution

  • It is a very simple answer. When main returns on most AVR toolchains, it executes an cli instruction, disabling the interrupts and then ends in the infinite loop. This is the most common epilogue routine in the AVR bare metal toolchains.

    This is what is happening when you exit from the main:

    00000078 <_exit>:
      78:    f8 94           cli
    
    0000007a <__stop_program>:
      7a:    ff cf           rjmp    .-2          ; 0x7a <__stop_program>
    

    You can change it by amending the startup assembly file (it contains the epilogue as well).