Interrupts are an important tool. Whether or not you realize it, you already know what interrupts are. As an analogy, imagine you are busy at your home but you have asked someone to stop by and have a cup of coffee with you. Also, you never settled on a time to expect this person. How do you know when to stop what you're doing to answer the door when they come by? Obviously, when you hear a door bell, a knock, or get a text on your phone, you can stop what you're doing to drink a few cups of joe with your friend, then go back to what you were doing before they arrived.
The interrupt in this case is the door bell, the knock on the door, or the text message. We need something similar to a door bell in our code so we can be busy working on one thing while we listen for some other task to complete or information to arrive. When the microcontroller is alerted to the completed task or piece of information, it can switch from what it is doing and process the data from the interrupt. When the microcontroller is done with its interrupt task it goes right back to what it was doing before.
Types of interrupts:
Interrupts can be the type to set a flag or to only initiate an ISR if a certain condition exists long enough. To clarify, suppose you want a Red LED to light when you press the button once and release. Then suppose you want a Blue LED to light when you press and hold a button for 10 seconds. In the first case the button triggers an interrupt flag. In the second case the interrupt is triggered only if the button has been held for 10 seconds. Anything less than ten seconds and the Red LED will be lit. We refer to the second type as "conditional."
Additionally, interrupts can be external - like pressing a button - or internal - like waiting for a signal that your ADC calculation is complete. In this How-To we will be working with an external interrupt in the form of a button that sets a flag. When the button is pressed we will turn on an LED. When it is pressed again we will turn the LED off. When the system detects the button being pressed, it will stop what it is currently doing and start a new task called an Interrupt Service Routing (ISR). Our ISR will be the code required to toggle the ON/OFF state of the LED. When it completes the ISR it will return control to the original series of instructions it was chugging through before the button was pressed.
What you will need:
Blue LED
Red LED
Push Button
150ohm resistor (x2)
Blue, red and black wire
Completion of the previous How-Tos
Step 1 - Wiring Your Button and Button-Test LED
You need to wire the following:
(See the image below)
a. Red LED and 150 Ohm resistor to the button's right-hand lower pin
b. Pin 3 on the Atmega324p to the button's left-hand lower pin
c. The buttons upper pin to the power rail.
You should now have a breadboard that looks something like this:
Note: The capacitor at Pin 2 of the MAX232 should be connected to ground
and the capacitor connected to Pin 6 of the MAX232 should be connected
to VCC rather than Pin 8.
Step 2 - Write the Interrupt Code
The interrupt code is fairly straight forward. It requires that you initialize the registers for the interrupt on pin PB2 and provide the ISR to be run when the button press is detected.
The initialization code:
In your main.c file, add the following function below your main() function. (You can separate the interrupt code into another file later. For now, let's just get it working.)
int Ext_Int_2_Init(void){
//set direction of PORTD, Pin 3 to input
DDRB = DDRB & (0 << DDB2);
//disable the pull up resistor at Pin 3
PORTB = PORTB | (0<<PORTB2);
//enable the INT2 external interrupt (local)
EIMSK = EIMSK | (1<<INT2);
//set trigger to rising edge on pin 3
EICRA = EICRA | (3 << ISC21);
sei(); //global interrupt enable
return 0;
}
Now add your ISR function below the initialization function:
ISR(INT2_vect){
PORTD = PORTD ^ (1 << PD6); // toggle the current state of the LED
return 0;
}
The ISR in the definition tells the microcontroller this function is, specifically, an Interrupt Service Routine. The INT2_vect tells the microcontroller which vector is associated. See the details section for the vector table. The rest is written as though it were any other function.
Your main.c file should look similar to this now:
In this file, the initialization function is called and the success or failure of this function is recorded in the variable init with the line:
init = Ext_Int_2_Init();
If the initialization function was reached and returned okay, I print a statement saying as much to the serial console. I used this for testing to answer the question:
'Am I getting as far as the init function and is that function returning okay?'
It does not mean I have set the registers correctly but is a good test. If am not seeing that statement in the serial output, I know something is wrong in or before that function.
If the Red LED lights when the button is pressed, you know the button is operating correctly. If the Blue LED changes from ON to OFF or OFF to ON when pressing the button you also know your ISR function is executing. If the ADC values on the serial console are still updating when you move the thumb-wheel you also know your ISR is returning properly and your code is continuing to run.
Run a make clean and a make, then download your code to the Atmega324p as seen in the Hello World! project.
Step 3 - Now have a look at the Frequency Meter How To for an important understanding of interrupts, timing, and efficiency.