Presentation is loading. Please wait.

Presentation is loading. Please wait.

Arduino Interrupts I am amazed by how much I learned in preparing for this talk. I would encourage each of you to consider making a presentation, however.

Similar presentations


Presentation on theme: "Arduino Interrupts I am amazed by how much I learned in preparing for this talk. I would encourage each of you to consider making a presentation, however."— Presentation transcript:

1 Arduino Interrupts I am amazed by how much I learned in preparing for this talk. I would encourage each of you to consider making a presentation, however short and simple. You will learn so much while collecting information, formulating your thoughts, and putting it on “paper”. So, tonight’s presentation is on interrupts on the Arduino Uno. Much of what I cover will apply to other versions of Arduino and other microcontrollers. 1

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

3 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) External interrupts, pin change interrupts, timer interrupts, I/O interrupts, … 3

4 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 … We will be looking at examples of these. 4

5 Basic functions: detachInterrupt(pin) - Turns off the given interrupt. attachInterrupt(pin, ISR, mode) - Turns on the given interrupt by calling ISR function

6 attachInterrupt(pin, ISR, mode) ;
ISR: is the function to be called without brackets Modes: LOW to trigger the interrupt whenever the pin is low, CHANGE to trigger the interrupt whenever the pin changes value RISING to trigger when the pin goes from low to high, FALLING for when the pin goes from high to low.

7 Interrupt vector is the address of memory location where the interrupt info is located.
Pin2 and 3 corresponds to INT0 and INT1. So it is the memory reference where the Arduino processor has to see if the interrupt occured

8 ISRs should be short and sweet
ISRs should be short and sweet. You don't want to derail the main loop for too long! There are no input variables or returned values. All changes have to be made on global variables. 

9 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(); //initialize digital pin 2 as an input with //the internal pull-up resistor enabled Say we want a program where a pushbutton is used as input. Using good Human/Machine Interface design, we want to acknowledge the input ASAP. We decide to do that by turning off an LED (we could turn on an LED, but that takes a tiny bit more code and I am being frugal). So, let’s start with a simple program that reads a switch and toggles an LED Pin 13 is the onboard LED Pin 2 has a NO momentary switch connected to ground. Note that the internal pullup is enabled. This is how I typically organize my programs. Loop just makes calls to handleFoo routines. We have handleSW that reads pin 2 and writes the result to pin 13 9

10 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(); Here, we add an additional handler function. We have handleOtherStuff that takes a long time (1/4th of a second in this case) This version feels sluggish and will miss inputs occasionally. One potential solution is to split up handleOtherStuff and call handleSW more times handleSW() handleThis() handleThat() But that is ugly and still has issues. The shorter the pulse, the more often we have to poll. 10

11 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(); We remove the call to handleSW from loop() and add it as an ISR for external interrupt 0 using attachInterrupt() On an Arduino Uno, pin 2 is INT0. Other chips differ in the numbering. Pin 3 is INT1. The mode argument to attachInterrupt() can be RISING, FALLING, CHANGE, HIGH, or LOW. So, your code will be looping (and delaying). When the switch pulls pin2 low, the microcontroller hardware generates an interrupt, which interrupts your code, saves the state of the machine, calls handleSW, restores the state of the machine, and resumes your code. The result is much more responsive! It is as if we have two threads, one handling the switch and one handling the other stuff. 11

12 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(), Serial etc… This one is good: void myISR () { count++; } You can call millis() to record the current time. But, since the millisecond counter is updated by an interrupt, the current time will not increment while you are in an ISR, so you cannot use it do measure elapsed time. Pending interrupts are handled in priority order. Lowest one first. 12

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

14 volatile` is a C keyword applied to variables
volatile` is a C keyword applied to variables. It means that the value of that variable is not entirely within a program's control. It reflects that the value could change, and the program can't predict.

15 Example 4 const 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 () { Serial.begin(9600); Serial.println(“Example4"); pinMode (LED, OUTPUT); pinMode (SW, INPUT_PULLUP); attachInterrupt(INT0, handleSW, CHANGE); } Volatile is used to mark variables that may change outside the normal program flow (like in an ISR). The compiler reads the variable from memory each time it uses it rather than saving it in a register. Anything that we share between ISR and loop() should be marked volatile. Each time the switch pin changes, handleSW is called and count is incremented. Before we run the demo, what do you expect to see if the switch is pressed and released three times? Switches bounce. We are getting multiple interrupts from what we think of as one event. 15

16 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 Library is needed for pin change interrupt ATTinys have INT0, but not INT1. For my rain gauge, I wanted to awaken on either a rain bucket tip or a switch press. I had to use pin change interrupts. 17

17 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(“Example4"); pinMode (LED, OUTPUT); pinMode (SW, INPUT_PULLUP); PCintPort::attachInterrupt(SW, handleSW, CHANGE); } Using the PinChangeInt library, you can do a simple replacement of what we had before. It is not going to be quite as quick, as the ISR has to handle which pin caused the interrupt. If we look back at slide 3, we can see three pin change interrupts, handling 6, 6, and 8 pins. Note that we really have no idea which pin caused the interrupt or what the pins’ values were when the interrupt was generated 18

18 What we have learned We can monitor any pin and have it generate an interrupt Different pins can have different ISRs 19

19 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()) ICP1 is pin 8 21

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

21 Example 7 http://playground.arduino.cc/Code/Timer1
#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, ); // attaches timerISR() as a timer overflow interrupt -- blinks at 1 Hz void loop () { handleOtherStuff(); There are several timer libraries available. Some are SW implementations that just look at millis() and do not use interrupts. TimerOne is available on code.google.com You could change to 13 and get a 38kHz blink rate. I wonder why that might be interesting… There is some confusion on the net about using pins 9 and 10 when you take over timer 1. You can use them as inputs. You can use them as outputs with digitalWrite. You cannot use them as outputs with analogWrite (i.e. PWM). 23

22 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 in the datasheet). They can be used for lots of cool things. For example, you can make a square wave generator with no software required (other than configuring the timer). 24

23 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. The classic use is to have the system reset if the watchdog timer expires. The SW kicks the dog within loop(). If something happens to the SW and it stops kicking the dog, the system will reset. The normal Arduino bootloader does not kick the dog and it does not disable watchdog timer reset. On reset, the system comes up with the shortest timeout duration. If WDT reset is enabled, the bootloader will not complete and you will suffer and endless loop of resets until you cycle power. The watchdog timer interrupt can be used to awaken a sleeping system. 25

24 Example 8 Blink the LED at 1Hz using the watchdog interrupt
#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(); 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); } Blink the LED at 1Hz using the watchdog interrupt The datasheet says not to set WDIE from within the ISR, but we are going to ignore that The two writes to WDTCSR have to happen within 4 cycles of each other. I had issues with the compiler on my laptop doing all of the shifting and masking between the writes. 26

25 Resources Interrupts http://www.gammon.com.au/forum/?id=114 88 Timers
PNphpBB2&file=viewtopic&t=50106

26 Q/A Questions?

27 Notes All examples were compiled using arduino and run on an Arduino Uno R3

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

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

30 Push a bunch of stuff (registers) onto the stack Call the user code
ISR(INT0_vect) { 2e8: 1f push r1 2ea: 0f push r0 2ec: 0f b in r0, 0x3f ; 63 2ee: 0f push r0 2f0: eor r1, r1 2f2: 2f push r18 2f4: 3f push r19 2f6: 4f push r20 2f8: 5f push r21 2fa: 6f push r22 2fc: 7f push r23 2fe: 8f push r24 300: 9f push r25 302: af push r26 304: bf push r27 306: ef push r30 308: ff push r31 30a: lds r24, 0x0113 30e: lds r25, 0x0114 312: b or r24, r25 314: 29 f breq ; 0x320 <__vector_1+0x38> 316: e lds r30, 0x0113 31a: f lds r31, 0x0114 31e: icall 320: ff pop r31 322: ef pop r30 324: bf pop r27 326: af pop r26 328: 9f pop r25 32a: 8f pop r24 32c: 7f pop r23 32e: 6f pop r22 330: 5f pop r21 332: 4f pop r20 334: 3f pop r19 336: 2f pop r18 338: 0f pop r0 33a: 0f be out 0x3f, r0 ; 63 33c: 0f pop r0 33e: 1f pop r1 340: reti This is the code for the ISR handler. The call to your routine is at 0x31e. Push a bunch of stuff (registers) onto the stack Call the user code Pop a bunch of stuff off of the stack 32


Download ppt "Arduino Interrupts I am amazed by how much I learned in preparing for this talk. I would encourage each of you to consider making a presentation, however."

Similar presentations


Ads by Google