gammon-forum From: http://www.gammon.com.au/interrupts
Interrupts Postings by administrators only. [Refresh] Refresh page Posted by Nick Gammon Australia (23,016 posts) [Biography] bio Forum Administrator Date Sun 08 Jan 2012 03:19 AM (UTC) Amended on Tue 25 Oct 2016 06:38 AM (UTC) by Nick Gammon
Message This article discusses interrupts on the Arduino Uno (Atmega328) and similar processors, using the Arduino IDE. The concepts however are very general. The code examples provided should compile on the Arduino IDE (Integrated Development Environment). This page can be quickly reached from the link: http://www.gammon.com.au/interruptsTL;DR : When writing an Interrupt Service Routine (ISR):
- Keep it short
- Don't use delay ()
- Don't do serial prints
- Make variables shared with the main code volatile
- Variables shared with main code may need to be protected by "critical sections" (see below)
- Don't try to turn interrupts off or on
What are interrupts? Most processors have interrupts. Interrupts let you respond to "external" events while doing something else. For example, if you are cooking dinner you may put the potatoes on to cook for 20 minutes. Rather than staring at the clock for 20 minutes you might set a timer, and then go watch TV. When the timer rings you "interrupt" your TV viewing to do something with the potatoes.
What interrupts are NOT Interrupts are not for simply changing your mind and doing something different. For example:
| 1 | Reset | |
| 2 | External Interrupt Request 0 (pin D2) | (INT0_vect) |
| 3 | External Interrupt Request 1 (pin D3) | (INT1_vect) |
| 4 | Pin Change Interrupt Request 0 (pins D8 to D13) | (PCINT0_vect) |
| 5 | Pin Change Interrupt Request 1 (pins A0 to A5) | (PCINT1_vect) |
| 6 | Pin Change Interrupt Request 2 (pins D0 to D7) | (PCINT2_vect) |
| 7 | Watchdog Time-out Interrupt | (WDT_vect) |
| 8 | Timer/Counter2 Compare Match A | (TIMER2_COMPA_vect) |
| 9 | Timer/Counter2 Compare Match B | (TIMER2_COMPB_vect) |
| 10 | Timer/Counter2 Overflow | (TIMER2_OVF_vect) |
| 11 | Timer/Counter1 Capture Event | (TIMER1_CAPT_vect) |
| 12 | Timer/Counter1 Compare Match A | (TIMER1_COMPA_vect) |
| 13 | Timer/Counter1 Compare Match B | (TIMER1_COMPB_vect) |
| 14 | Timer/Counter1 Overflow | (TIMER1_OVF_vect) |
| 15 | Timer/Counter0 Compare Match A | (TIMER0_COMPA_vect) |
| 16 | Timer/Counter0 Compare Match B | (TIMER0_COMPB_vect) |
| 17 | Timer/Counter0 Overflow | (TIMER0_OVF_vect) |
| 18 | SPI Serial Transfer Complete | (SPI_STC_vect) |
| 19 | USART Rx Complete | (USART_RX_vect) |
| 20 | USART, Data Register Empty | (USART_UDRE_vect) |
| 21 | USART, Tx Complete | (USART_TX_vect) |
| 22 | ADC Conversion Complete | (ADC_vect) |
| 23 | EEPROM Ready | (EE_READY_vect) |
| 24 | Analog Comparator | (ANALOG_COMP_vect) |
| 25 | 2-wire Serial Interface (I2C) | (TWI_vect) |
| 26 | Store Program Memory Ready | (SPM_READY_vect) |
Above is an image of a switch bouncing, captured on the logic analyzer. You
can see from the above, that a simple keypress might result in a dozen or
so transitions. On the image each bounce is around 5 mS apart. So we
really need to wait for a longer interval to elapse, in which the switch
doesn't open/close again.
The debounce handler above waits for 20 milliseconds for the switch to stop
bouncing, and if the switch closes again, resets the time interval so it
waits another 20 milliseconds. This seemed to work quite well.
The code also demonstrates putting the processor to sleep if it isn't
needed (eg. at midnight) so it uses less power. It also shows how the
"low" interrupt can be used to wake it up, and the falling interrupt to
notice if the switch is pressed while the pump is running (eg., you have
watered your plants enough already).
- Nick Gammon
www.gammon.com.au, www.mushclient.com
[Go to top] top
Posted by Nick Gammon Australia (23,016 posts) [Biography] bio Forum
Administrator
Date Reply #2 on Tue 10 Jan 2012 03:20 AM (UTC)
Amended on Wed 04 Sep 2013 04:33 AM (UTC) by Nick Gammon
Message
[EDIT] Changes made on 19th October 2014.
The sketch uses the digitalwritefast library to do quick writes to the
spark pin. It also uses the ISR (INT0_vect) function rather than
attachInterrupt, because attachInterrupt takes longer to run.
https://code.google.com/p/digitalwritefast/
There is a tweaking figure of 4 µS (isrDelayFactor) which you can
determine empirically (by measuring) to compensate for the few
microseconds that it takes to enter the ISR. By subtracting that from the
required timer interval, the timing is more accurate.
- Nick Gammon
www.gammon.com.au, www.mushclient.com
[Go to top] top
Posted by Nick Gammon Australia (23,016 posts) [Biography] bio Forum
Administrator
Date Reply #4 on Sat 14 Jan 2012 09:02 PM (UTC)
Amended on Tue 25 Oct 2016 06:42 AM (UTC) by Nick Gammon
Message
The sketch below times intervals between RISING interrupts (the resistance
goes up when less light shines on the LDR):
/*H***************************************************
Robot race timer
Author: Nick Gammon
Date: 1st February 2012
*******************************************************/
// ==================== DEFINES ==========================
// ==================== PROTOTYPES ==========================
void isr();
// ==================== VARIABLES ==========================
unsigned long lastTriggerTime;
volatile unsigned long triggerTime;
volatile boolean triggered;
/*F*****************************************************
*
*******************************************************/
void
setup()
{
digitalWrite( 2, HIGH ); // pull-up
attachInterrupt( digitalPinToInterrupt( 2 ), isr, RISING);
Serial.begin( 115200 );
Serial.println( "Started timing ...");
} // end of setup
/*F*****************************************************
*
*******************************************************/
void
loop()
{
if( !triggered )
return;
unsigned long elapsed = triggerTime - lastTriggerTime;
if( elapsed < 1000000L )
{
triggered = false;
return; // IGNORE IF LESS THAN A SECOND
}
lastTriggerTime = triggerTime;
triggered = false; // RE-ARM FOR NEXT TIME
Serial.print( "Took: ");
Serial.print( elapsed);
Serial.print( " microseconds. ");
unsigned long minutes, seconds, ms;
minutes = elapsed / (1000000L * 60);
elapsed -= minutes * (1000000L * 60);
seconds = elapsed / 1000000L;
elapsed -= seconds * 1000000L;
ms = elapsed / 1000;
elapsed -= ms * 1000;
Serial.print( minutes);
Serial.print( "m ");
Serial.print( seconds);
Serial.print( "s ");
Serial.print( ms);
Serial.println( "ms.");
} // END OF LOOP
/*F*****************************************************
*
*******************************************************/
void
isr()
{
if( triggered ) // WAIT UNTIL WE NOTICED LAST ONE
return;
triggerTime = micros();
triggered = true;
} // END OF ISR
Example output:
Started timing ...
Took: 3375112 microseconds. 0m 3s 375ms.
Took: 1577852 microseconds. 0m 1s 577ms.
Took: 1267868 microseconds. 0m 1s 267ms.
Took: 1293936 microseconds. 0m 1s 293ms.
Took: 2013680 microseconds. 0m 2s 13ms.
Took: 3785196 microseconds. 0m 3s 785ms.
Took: 12562240 microseconds. 0m 12s 562ms.
I shone a cheap 1 mW laser pointer onto the LDR. The resistance is low with
the light on it (I read about 1.9V on pin D2) which registers as a LOW
input. When you break the beam the resistance is high and the pin gets
about 3V (being a HIGH). Thus the beam is broken on a RISING interrupt.
The code times the intervals between rising interrupts and reports them to
the serial monitor. Of course you could use a LCD or something. There is a
test for short intervals (which might be the light passing through a part
of the robot) so that if the time is less than a second it doesn't start
timing again.
- Nick Gammon
www.gammon.com.au, www.mushclient.com
[Go to top] top
Posted by Nick Gammon Australia (23,016 posts) [Biography] bio Forum
Administrator
Date Reply #5 on Wed 18 Apr 2012 09:00 PM (UTC)
Amended on Wed 04 Sep 2013 04:33 AM (UTC) by Nick Gammon
Message
| D0 | PCINT16 (PCMSK2 / PCIF2 / PCIE2) |
| D1 | PCINT17 (PCMSK2 / PCIF2 / PCIE2) |
| D2 | PCINT18 (PCMSK2 / PCIF2 / PCIE2) |
| D3 | PCINT19 (PCMSK2 / PCIF2 / PCIE2) |
| D4 | PCINT20 (PCMSK2 / PCIF2 / PCIE2) |
| D5 | PCINT21 (PCMSK2 / PCIF2 / PCIE2) |
| D6 | PCINT22 (PCMSK2 / PCIF2 / PCIE2) |
| D7 | PCINT23 (PCMSK2 / PCIF2 / PCIE2) |
| D8 | PCINT0 (PCMSK0 / PCIF0 / PCIE0) |
| D9 | PCINT1 (PCMSK0 / PCIF0 / PCIE0) |
| D10 | PCINT2 (PCMSK0 / PCIF0 / PCIE0) |
| D11 | PCINT3 (PCMSK0 / PCIF0 / PCIE0) |
| D12 | PCINT4 (PCMSK0 / PCIF0 / PCIE0) |
| D13 | PCINT5 (PCMSK0 / PCIF0 / PCIE0) |
| A0 | PCINT8 (PCMSK1 / PCIF1 / PCIE1) |
| A1 | PCINT9 (PCMSK1 / PCIF1 / PCIE1) |
| A2 | PCINT10 (PCMSK1 / PCIF1 / PCIE1) |
| A3 | PCINT11 (PCMSK1 / PCIF1 / PCIE1) |
| A4 | PCINT12 (PCMSK1 / PCIF1 / PCIE1) |
| A5 | PCINT13 (PCMSK1 / PCIF1 / PCIE1) |