Arduino Interrupts Paul Mac Dougal September 8 2014
Arduino Interrupts Paul Mac. Dougal 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 D 2) 3 External Interrupt Request 1 (pin D 3) 4 Pin Change Interrupt Request 0 (pins D 8 to D 13) 5 Pin Change Interrupt Request 1 (pins A 0 to A 5) 6 Pin Change Interrupt Request 2 (pins D 0 to D 7) 7 Watchdog Time-out Interrupt 8 Timer/Counter 2 Compare Match A … 18 SPI Serial Transfer Complete 19 USART Rx Complete … 25 2 -wire Serial Interface (I 2 C) …
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() { pin. Mode(LED, OUTPUT); pin. Mode(SW, INPUT_PULLUP); } void handle. SW() { digital. Write(LED, digital. Read(SW)); } void loop() { handle. SW(); }
Example 2 (no interrupts) const byte LED = 13, SW = 2; void handle. SW() { digital. Write(LED, digital. Read(SW)); } void handle. Other. Stuff() { delay(250); } void setup() { pin. Mode(LED, OUTPUT); pin. Mode(SW, INPUT_PULLUP); } void loop() { handle. SW(); handle. Other. Stuff(); }
Example 3 (interrupt) const byte LED = 13, SW = 2; void handle. SW() { // ISR digital. Write(LED, digital. Read(SW)); } void handle. Other. Stuff() { delay(250); } void setup() { pin. Mode(LED, OUTPUT); pin. Mode(SW, INPUT_PULLUP); attach. Interrupt(INT 0, handle. SW, CHANGE); } void loop() { // handle. SW(); commented out handle. Other. Stuff(); }
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 my. ISR () { count++; }
What we have learned • The hardware can call a routine for us based on activity on pin 2 (INT 0) • 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 handle. SW () { digital. Write(LED, digital. Read(SW)); count++; } void setup () { //Start up the serial port unsigned char last. Count = -1; Serial. begin(9600); void handle. Other. Stuff() { Serial. println(F(“Example 4")); if (count != last. Count) { Serial. print("Count "); pin. Mode (LED, OUTPUT); Serial. println(count); pin. Mode (SW, INPUT_PULLUP); last. Count = count; attach. Interrupt(INT 0, } handle. SW, CHANGE); } } void loop () { handle. Other. Stuff(); }
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) … 1 fa: 1 fe: 202: 204: 206: 80 90 80 91 69 91 10 01 91 11 01 50 40 f 5 lds r 24, 0 x 0110 ; count lower lds r 25, 0 x 0111 ; count upper subi r 24, 0 x 00 sbci r 25, 0 x 01 brne. +90
Sharing continued // Disable interrupts and copy no. Interrupts(); short int my. Count = count; interrupts(); if (my. Count == 256) … 1 fa: 1 fc: 200: 204: 206: 208: 20 a: f 8 80 90 78 80 91 69 94 91 10 01 91 11 01 94 50 40 f 5 cli lds r 24, 0 x 0110 lds r 25, 0 x 0111 sei subi r 24, 0 x 00 sbci r 25, 0 x 01 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 INT 0 Pin 3 is INT 1 But, what about pins 0, 1, 4, 5, 6, … Pin Change Interrupts can monitor all pins
Example 5 #include <Pin. Change. Int. h> const byte LED = 13, SW = 5; volatile unsigned char count = 0; void handle. SW () { digital. Write(LED, digital. Read(SW)); count++; void setup () { } //Start up the serial port Serial. begin(9600); unsigned char last. Count = -1; Serial. println(F(“Example 4")); void handle. Other. Stuff() { if (count != last. Count) { pin. Mode (LED, OUTPUT); Serial. print("Count "); pin. Mode (SW, INPUT_PULLUP); Serial. println(count); PCint. Port: : attach. Interrupt(SW, handle. SW, CHANGE); last. Count = count; } } } void loop () { handle. Other. Stuff(); }
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 <Pin. Change. Int. h> void wake() { // ISR sleep_disable(); // first thing after waking from sleep: PCint. Port: : detach. Interrupt(SW); // stop LOW interrupt } void sleep. Now() { set_sleep_mode(SLEEP_MODE_PWR_DOWN); no. Interrupts(); // stop interrupts sleep_enable(); // enables sleep bit in MCUCR PCint. Port: : attach. Interrupt(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 • Timer 0 – 8 bit – controls PWM on pins 5 and 6. Also controls millis() • Timer 1 – 16 bit – controls PWM on pins 9 and 10. • Timer 2 – 8 bit – controls PWM on pins 11 and 3.
Example 7 #include <Timer. One. h> const byte LED = 13; void handle. Other. Stuff() { delay(250); } unsigned int led = LOW; void timer. ISR() { digital. Write(LED, led); led ^= (HIGH^LOW); } void setup () { pin. Mode (LED, OUTPUT); Timer 1. initialize(); // breaks analog. Write() for digital pins 9 and 10 Timer 1. attach. Interrupt(timer. ISR, 500000); // attaches timer. ISR() as a timer overflow interrupt -blinks at 1 Hz } void loop () { handle. Other. Stuff(); } http: //playground. arduino. cc/Code/Timer 1 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 (15 ms, 30 ms, 60 ms, 120 ms, 250 ms, 500 ms, 1 s, 2 s, 4 s, 8 s) 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; uint 8_t led = LOW; ISR (WDT_vect) { wdt_setup(WDTO_500 MS); digital. Write(LED, led); led ^= (HIGH^LOW); } void setup () { // configure the pins pin. Mode (LED, OUTPUT); no. Interrupts(); wdt_setup(WDTO_500 MS); interrupts(); } void loop () { delay(250); } void wdt_setup(uint 8_t duration) { // interrupts should be disabled wdt_reset(); // kick the dog WDTCSR = (1<<WDCE) |(1<<WDIF); WDTCSR = (0<< WDE)|(1<<WDIE) |(duration&0 x 7) |((duration&0 x 8)<<2); }
Resources • Interrupts • http: //www. gammon. com. au/forum/? id=11 488 • Timers • http: //www. avrfreaks. net/index. php? name =PNphp. BB 2&file=viewtopic&t=50106
Q/A • Questions?
Notes • All examples were compiled using arduino 1. 0. 5 and run on an Arduino Uno R 3
#if defined(__AVR_ATtiny 45__) #error "__AVR_ATtiny 45" #elif defined(__AVR_ATtiny 84__) #error "__AVR_ATtiny 84" #elif defined(__AVR_ATtiny 85__) #error "__AVR_ATtiny 85" #elif defined (__AVR_ATtiny 2313__) #error "__AVR_ATtiny 2313" #elif defined (__AVR_ATtiny 2313 A__) #error "__AVR_ATtiny 2313 A" #elif defined (__AVR_ATmega 48__) #error "__AVR_ATmega 48" #elif defined (__AVR_ATmega 48 A__) #error "__AVR_ATmega 48 A" #elif defined (__AVR_ATmega 48 P__) #error "__AVR_ATmega 48 P" #elif defined (__AVR_ATmega 8__) #error "__AVR_ATmega 8" #elif defined (__AVR_ATmega 8 U 2__) #error "__AVR_ATmega 8 U 2" #elif defined (__AVR_ATmega 88__) #error "__AVR_ATmega 88" #elif defined (__AVR_ATmega 88 A__) #error "__AVR_ATmega 88 A" #elif defined (__AVR_ATmega 88 P__) #error "__AVR_ATmega 88 P"
Arduino main() • #include <Arduino. h> • • • int main(void) { init(); • • • #if defined(USBCON) USBDevice. attach(); #endif • setup(); • • for (; ; ) { loop(); if (serial. Event. Run) serial. Event. Run(); } • • return 0; }
ISR(INT 0_vect) { 2 e 8: 1 f 92 2 ea: 0 f 92 2 ec: 0 f b 6 2 ee: 0 f 92 2 f 0: 11 24 2 f 2: 2 f 93 2 f 4: 3 f 93 2 f 6: 4 f 93 2 f 8: 5 f 93 2 fa: 6 f 93 2 fc: 7 f 93 2 fe: 8 f 93 300: 9 f 93 302: af 93 304: bf 93 306: ef 93 308: ff 93 30 a: 80 91 13 30 e: 90 91 14 312: 89 2 b 314: 29 f 0 316: e 0 91 13 31 a: f 0 91 14 31 e: 09 95 320: ff 91 322: ef 91 324: bf 91 326: af 91 328: 9 f 91 32 a: 8 f 91 32 c: 7 f 91 32 e: 6 f 91 330: 5 f 91 332: 4 f 91 334: 3 f 91 336: 2 f 91 338: 0 f 90 33 a: 0 f be 33 c: 0 f 90 33 e: 1 f 90 340: 18 95 01 01 push r 0 in r 0, 0 x 3 f ; 63 push r 0 eor r 1, r 1 push r 18 push r 19 push r 20 push r 21 push r 22 push r 23 push r 24 push r 25 push r 26 push r 27 push r 30 push r 31 lds r 24, 0 x 0113 lds r 25, 0 x 0114 or r 24, r 25 breq. +10 ; 0 x 320 <__vector_1+0 x 38> lds r 30, 0 x 0113 lds r 31, 0 x 0114 icall pop r 31 pop r 30 pop r 27 pop r 26 pop r 25 pop r 24 pop r 23 pop r 22 pop r 21 pop r 20 pop r 19 pop r 18 pop r 0 out 0 x 3 f, r 0 ; 63 pop r 0 pop r 1 reti
- Slides: 30