Interrupts

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