Transcript Interrupts

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; void handleOtherStuff() { Serial.begin(9600); Serial.println(F(“Example4")); if (count != lastCount) { Serial.print("Count "); Serial.println(count); pinMode (LED, OUTPUT); 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: 80 91 10 01 lds r24, 0x0110 ; count lower 1fe: 90 91 11 01 lds r25, 0x0111 ; count upper 202: 80 50 subi r24, 0x00 204: 91 40 sbci r25, 0x01 206: 69 f5 brne .+90

Sharing continued

// Disable interrupts and copy noInterrupts(); short int myCount = count; interrupts(); if (myCount == 256) … 1fa: f8 94 cli 1fc: 80 91 10 01 lds r24, 0x0110 200: 90 91 11 01 lds r25, 0x0111 204: 78 94 sei 206: 80 50 subi r24, 0x00 208: 91 40 sbci r25, 0x01 20a: 69 f5 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 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); Serial.println(F(“Example4")); unsigned char lastCount = -1; void handleOtherStuff() { if (count != lastCount) { Serial.print("Count "); Serial.println(count); lastCount = count; pinMode (LED, OUTPUT); pinMode (SW, INPUT_PULLUP); PCintPort::attachInterrupt(SW, handleSW, CHANGE); } } } 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 #include 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 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 94 165 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 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<

Resources

• • Interrupts http://www.gammon.com.au/forum/?id=114 88 • • Timers http://www.avrfreaks.net/index.php?name= PNphpBB2&file=viewtopic&t=50106

• Questions?

Q/A

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" #elif defined (__AVR_ATmega88PA__)

Arduino main()

• • • • • • • • • • • • • • } #include { int main(void) init(); #if defined(USBCON) USBDevice.attach(); #endif setup(); } for (;;) { loop(); if (serialEventRun) serialEventRun(); return 0;

ISR(INT0_vect) { 2e8: 1f 92 push r1 2ea: 0f 92 push r0 2ec: 0f b6 in r0, 0x3f ; 63 2ee: 0f 92 push r0 2f0: 11 24 eor r1, r1 2f2: 2f 93 push r18 2f4: 3f 93 push r19 2f6: 4f 93 push r20 2f8: 5f 93 push r21 2fa: 6f 93 push r22 2fc: 7f 93 push r23 2fe: 8f 93 push r24 300: 9f 93 push r25 302: af 93 push r26 304: bf 93 push r27 306: ef 93 push r30 308: ff 93 push r31 30a: 80 91 13 01 lds r24, 0x0113 30e: 90 91 14 01 lds r25, 0x0114 312: 89 2b or r24, r25 314: 29 f0 breq .+10 ; 0x320 <__vector_1+0x38> 316: e0 91 13 01 lds r30, 0x0113 31a: f0 91 14 01 lds r31, 0x0114 31e: 09 95 icall 320: ff 91 pop r31 322: ef 91 pop r30 324: bf 91 pop r27 326: af 91 pop r26 328: 9f 91 pop r25 32a: 8f 91 pop r24 32c: 7f 91 pop r23 32e: 6f 91 pop r22 330: 5f 91 pop r21 332: 4f 91 pop r20 334: 3f 91 pop r19 336: 2f 91 pop r18 338: 0f 90 pop r0 33a: 0f be out 0x3f, r0 ; 63 33c: 0f 90 pop r0 33e: 1f 90 pop r1 340: 18 95 reti