Arduino Interrupts Paul MacDougal September 8, 2014 What are they? • Interrupts are a way for a microcontroller to temporarily stop what it is doing to handle another task. • The currently executing program is paused, an ISR (interrupt service routine) is executed, and then your program continues, none the wiser. Kinds of interrupts • There are 26 different interrupts on an Arduino Uno – – – – – – – – – – – – – – 1 Reset 2 External Interrupt Request 0 (pin D2) 3 External Interrupt Request 1 (pin D3) 4 Pin Change Interrupt Request 0 (pins D8 to D13) 5 Pin Change Interrupt Request 1 (pins A0 to A5) 6 Pin Change Interrupt Request 2 (pins D0 to D7) 7 Watchdog Time-out Interrupt 8 Timer/Counter2 Compare Match A … 18 SPI Serial Transfer Complete 19 USART Rx Complete … 25 2-wire Serial Interface (I2C) … When would you use one? • Interrupts can detect brief pulses on input pins. Polling may miss the pulse while you are doing other calculations. • Interrupts are useful for waking a sleeping processor. • Interrupts can be generated at a fixed interval for repetitive processing. • And more … Example 1 (no interrupts) const byte LED = 13, SW = 2; void setup() { pinMode(LED, OUTPUT); pinMode(SW, INPUT_PULLUP); } void handleSW() { digitalWrite(LED, digitalRead(SW)); } void loop() { handleSW(); } Example 2 (no interrupts) const byte LED = 13, SW = 2; void handleSW() { digitalWrite(LED, digitalRead(SW)); } void handleOtherStuff() { delay(250); } void setup() { pinMode(LED, OUTPUT); pinMode(SW, INPUT_PULLUP); } void loop() { handleSW(); handleOtherStuff(); } Example 3 (interrupt) const byte LED = 13, SW = 2; void handleSW() { // ISR digitalWrite(LED, digitalRead(SW)); } void handleOtherStuff() { delay(250); } void setup() { pinMode(LED, OUTPUT); pinMode(SW, INPUT_PULLUP); attachInterrupt(INT0, handleSW, CHANGE); } void loop() { // handleSW(); commented out handleOtherStuff(); } ISR • Interrupt Service Routines should be kept short. Interrupts are disabled when the ISR is called, so other interrupts are postponed. • Do not call millis() or delay() or Serial or … • This one is good: void myISR () { count++; } What we have learned • The hardware can call a routine for us based on activity on pin 2 (INT0) • Our loop() code does not need to know what is happening • But, we often want to know what is going on. How do we share that information? Example 4 const byte LED = 13, SW = 2; volatile unsigned char count = 0; void handleSW () { digitalWrite(LED, digitalRead(SW)); count++; } void setup () { //Start up the serial port unsigned char lastCount = -1; Serial.begin(9600); void handleOtherStuff() { Serial.println(F(“Example4")); if (count != lastCount) { Serial.print("Count "); pinMode (LED, OUTPUT); Serial.println(count); pinMode (SW, INPUT_PULLUP); lastCount = count; attachInterrupt(INT0, } handleSW, CHANGE); } } void loop () { handleOtherStuff(); } A little more on sharing data • An interrupt can happen at any time. • If you share a multi-byte value (e.g. short int) between an ISR and your code, you have to take additional precautions. volatile short count; if (count == 256) … 1fa: 1fe: 202: 204: 206: 80 90 80 91 69 91 10 01 91 11 01 50 40 f5 lds r24, 0x0110 ; count lower lds r25, 0x0111 ; count upper subi r24, 0x00 sbci r25, 0x01 brne .+90 Sharing continued // Disable interrupts and copy noInterrupts(); short int myCount = count; interrupts(); if (myCount == 256) … 1fa: 1fc: 200: 204: 206: 208: 20a: f8 80 90 78 80 91 69 94 91 10 01 91 11 01 94 50 40 f5 cli lds r24, 0x0110 lds r25, 0x0111 sei subi r24, 0x00 sbci r25, 0x01 brne .+90 What we have learned • Switches bounce and we may be interrupted more often than expected • We must take precautions when sharing data between an ISR and the main code Pin Change Interrupt • • • • Pin 2 is INT0 Pin 3 is INT1 But, what about pins 0,1,4,5,6,… Pin Change Interrupts can monitor all pins Example 5 #include <PinChangeInt.h> const byte LED = 13, SW = 5; volatile unsigned char count = 0; void handleSW () { digitalWrite(LED, digitalRead(SW)); count++; void setup () { } //Start up the serial port Serial.begin(9600); unsigned char lastCount = -1; Serial.println(F(“Example4")); void handleOtherStuff() { if (count != lastCount) { pinMode (LED, OUTPUT); Serial.print("Count "); pinMode (SW, INPUT_PULLUP); Serial.println(count); PCintPort::attachInterrupt(SW, handleSW, CHANGE); lastCount = count; } } } void loop () { handleOtherStuff(); } What we have learned • We can monitor any pin and have it generate an interrupt • Different pins can have different ISRs Example 6 #include <avr/sleep.h> #include <PinChangeInt.h> void wake() { // ISR sleep_disable(); // first thing after waking from sleep: PCintPort::detachInterrupt(SW); // stop LOW interrupt } void sleepNow() { set_sleep_mode(SLEEP_MODE_PWR_DOWN); noInterrupts(); // stop interrupts sleep_enable(); // enables sleep bit in MCUCR PCintPort::attachInterrupt(SW, wake, LOW); interrupts(); // allow interrupts sleep_cpu(); // here the device is put to sleep } Timer Interrupts • There are three timers on an Uno. Two are 8 bit and one is 16 bit. They can generate an interrupt when they overflow or when they match a set value. • The frequency at which the timers increment is programmable • Arduino uses the timers for PWM and for timing (delay(), millis(), micros()) Timers • Timer0 – 8 bit – controls PWM on pins 5 and 6. Also controls millis() • Timer1 – 16 bit – controls PWM on pins 9 and 10. • Timer2 – 8 bit – controls PWM on pins 11 and 3. Example 7 #include <TimerOne.h> const byte LED = 13; void handleOtherStuff() { delay(250); } unsigned int led = LOW; void timerISR() { digitalWrite(LED, led); led ^= (HIGH^LOW); } void setup () { pinMode (LED, OUTPUT); Timer1.initialize(); // breaks analogWrite() for digital pins 9 and 10 Timer1.attachInterrupt(timerISR, 500000); // attaches timerISR() as a timer overflow interrupt -blinks at 1 Hz } void loop () { handleOtherStuff(); } http://playground.arduino.cc/Code/Timer1 http://code.google.com/p/arduino-timerone What have we learned • The fundamental Arduino code uses each of the timers. • We can sacrifice some functionality and use them for our own purposes. • The timers are very complex (pages 94165 in the datasheet). They can be used for lots of cool things. Watchdog Timer • The watchdog timer is a separate timer. • A selectable timeout is programmable (15ms, 30ms, 60ms, 120ms, 250ms, 500ms, 1s, 2s, 4s, 8s) Times are approx. • If the SW does not reset the WDT (kick the dog) within the timeout period, an interrupt or a reset (or both) occur. Example 8 #include <avr/wdt.h> const byte LED = 13; uint8_t led = LOW; ISR (WDT_vect) { wdt_setup(WDTO_500MS); digitalWrite(LED, led); led ^= (HIGH^LOW); } void setup () { // configure the pins pinMode (LED, OUTPUT); noInterrupts(); wdt_setup(WDTO_500MS); interrupts(); } void loop () { delay(250); } void wdt_setup(uint8_t duration) { // interrupts should be disabled wdt_reset(); // kick the dog WDTCSR = (1<<WDCE) |(1<<WDE) |(1<<WDIF); WDTCSR = (0<< WDE)|(1<<WDIE) |(duration&0x7) |((duration&0x8)<<2); } Resources • Interrupts • http://www.gammon.com.au/forum/?id=114 88 • Timers • http://www.avrfreaks.net/index.php?name= PNphpBB2&file=viewtopic&t=50106 Q/A • Questions? Notes • All examples were compiled using arduino 1.0.5 and run on an Arduino Uno R3 #if defined(__AVR_ATtiny45__) #error "__AVR_ATtiny45" #elif defined(__AVR_ATtiny84__) #error "__AVR_ATtiny84" #elif defined(__AVR_ATtiny85__) #error "__AVR_ATtiny85" #elif defined (__AVR_ATtiny2313__) #error "__AVR_ATtiny2313" #elif defined (__AVR_ATtiny2313A__) #error "__AVR_ATtiny2313A" #elif defined (__AVR_ATmega48__) #error "__AVR_ATmega48" #elif defined (__AVR_ATmega48A__) #error "__AVR_ATmega48A" #elif defined (__AVR_ATmega48P__) #error "__AVR_ATmega48P" #elif defined (__AVR_ATmega8__) #error "__AVR_ATmega8" #elif defined (__AVR_ATmega8U2__) #error "__AVR_ATmega8U2" #elif defined (__AVR_ATmega88__) #error "__AVR_ATmega88" #elif defined (__AVR_ATmega88A__) #error "__AVR_ATmega88A" #elif defined (__AVR_ATmega88P__) #error "__AVR_ATmega88P" Arduino main() • #include <Arduino.h> • • • int main(void) { init(); • • • #if defined(USBCON) USBDevice.attach(); #endif • setup(); • • • • for (;;) { loop(); if (serialEventRun) serialEventRun(); } • • return 0; } ISR(INT0_vect) { 2e8: 1f 92 2ea: 0f 92 2ec: 0f b6 2ee: 0f 92 2f0: 11 24 2f2: 2f 93 2f4: 3f 93 2f6: 4f 93 2f8: 5f 93 2fa: 6f 93 2fc: 7f 93 2fe: 8f 93 300: 9f 93 302: af 93 304: bf 93 306: ef 93 308: ff 93 30a: 80 91 13 30e: 90 91 14 312: 89 2b 314: 29 f0 316: e0 91 13 31a: f0 91 14 31e: 09 95 320: ff 91 322: ef 91 324: bf 91 326: af 91 328: 9f 91 32a: 8f 91 32c: 7f 91 32e: 6f 91 330: 5f 91 332: 4f 91 334: 3f 91 336: 2f 91 338: 0f 90 33a: 0f be 33c: 0f 90 33e: 1f 90 340: 18 95 01 01 01 01 push r1 push r0 in r0, 0x3f ; 63 push r0 eor r1, r1 push r18 push r19 push r20 push r21 push r22 push r23 push r24 push r25 push r26 push r27 push r30 push r31 lds r24, 0x0113 lds r25, 0x0114 or r24, r25 breq .+10 ; 0x320 <__vector_1+0x38> lds r30, 0x0113 lds r31, 0x0114 icall pop r31 pop r30 pop r27 pop r26 pop r25 pop r24 pop r23 pop r22 pop r21 pop r20 pop r19 pop r18 pop r0 out 0x3f, r0 ; 63 pop r0 pop r1 reti