Arduino Interrupts Paul MacDougal September 8, 2014

Preview:

Citation preview

ArduinoInterrupts

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 4const byte LED = 13, SW = 2;

volatile unsigned char count = 0;void handleSW () { digitalWrite(LED, digitalRead(SW)); count+

+;}

unsigned char lastCount = -1;void handleOtherStuff() { if (count != lastCount) { Serial.print("Count "); Serial.println(count); lastCount = count; }}

void loop () { handleOtherStuff();}

void setup () { //Start up the serial port Serial.begin(9600); Serial.println(F(“Example4")); pinMode (LED, OUTPUT); pinMode (SW, INPUT_PULLUP); attachInterrupt(INT0, handleSW, CHANGE);}

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 lower1fe: 90 91 11 01 lds r25, 0x0111 ; count upper202: 80 50 subi r24, 0x00204: 91 40 sbci r25, 0x01206: 69 f5 brne .+90

Sharing continued// Disable interrupts and copynoInterrupts();short int myCount = count;interrupts();if (myCount == 256) …

1fa: f8 94 cli1fc: 80 91 10 01 lds r24, 0x0110200: 90 91 11 01 lds r25, 0x0111204: 78 94 sei206: 80 50 subi r24, 0x00208: 91 40 sbci r25, 0x0120a: 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 <PinChangeInt.h>const byte LED = 13, SW = 5;

volatile unsigned char count = 0;void handleSW () { digitalWrite(LED, digitalRead(SW)); count++;}

unsigned char lastCount = -1;void handleOtherStuff() { if (count != lastCount) { Serial.print("Count "); Serial.println(count); lastCount = count; }}

void loop () { handleOtherStuff();}

void setup () { //Start up the serial port Serial.begin(9600); Serial.println(F(“Example4")); pinMode (LED, OUTPUT); pinMode (SW, INPUT_PULLUP); PCintPort::attachInterrupt(SW,

handleSW, CHANGE);}

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/Timer1http://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 <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=11488

• 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"#elif defined (__AVR_ATmega88PA__) #error "__AVR_ATmega88PA"#elif defined (__AVR_ATmega16__) #error "__AVR_ATmega16"#elif defined (__AVR_ATmega168__) #error "__AVR_ATmega168"#elif defined (__AVR_ATmega168A__) #error "__AVR_ATmega168A"#elif defined (__AVR_ATmega168P__) #error "__AVR_ATmega168P"#elif defined (__AVR_ATmega32__) #error "__AVR_ATmega32"#elif defined (__AVR_ATmega328__) #error "__AVR_ATmega328"#elif defined (__AVR_ATmega328P__) #error "__AVR_ATmega328P"#elif defined (__AVR_ATmega32U2__) #error "__AVR_ATmega32U2"#elif defined (__AVR_ATmega32U4__) #error "__AVR_ATmega32U4"#elif defined (__AVR_ATmega32U6__) #error "__AVR_ATmega32U6"#elif defined (__AVR_ATmega128__) #error "__AVR_ATmega128"#elif defined (__AVR_ATmega1280__) #error "__AVR_ATmega1280"#elif defined (__AVR_ATmega2560__) #error "__AVR_ATmega2560"#else #error "Unknown processor"#endif

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 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

Recommended