Professional Documents
Culture Documents
Arduino Timer Interrupts
Arduino Timer Interrupts
Arduino Timer Interrupts
Version
12/11-2015
/ Valle Thor
Side 1 af 50
Arduino interrupts
Version
12/11-2015
Hvad er et interrupt?
Det program, der krer p en controller, er normalt en rkke af sekventielle udfrte instruktioner
efter hinanden.
Et interrupt er en event, udlst enten internt fra en timer / counter, - eller eksternt fra en pin.
Det program, der kres ved eventen, kaldes en interrupt service routine (ISR).
Nr en ISR er frdig, vil det krende program fortstte hvorfra det blev afbrudt.
Fr man kan benytte interrupts, skal der noget opstning til. Ved at stte forskellige bits i
forskellige special function registre kan man opstte processoren til at interrupte p forskellige
events.
Interrupts skal alts enables og den tilhrende interrupt maske skal enables, ligesom det skal
bestemmes, hvad processoren skal udfre, nr den interruptes.
Nr der udlses et interrupt, afbryder det igangvrende program jeblikkeligt hvad det er i gang
med, og hopper til en separat programstump kaldet en ISR.
Interrupt Service Routine (ISR)
Interrupts can generally enabled / disabled with the function interrupts() / noInterrupts(). By default
in the Arduino firmware interrupts are enabled. Interrupt masks are enabled / disabled by setting /
clearing bits in the Interrupt mask register (TIMSKx).
When an interrupt occurs, a flag in the interrupt flag register (TIFRx) is been set. This interrupt will
be automatically cleared when entering the ISR or by manually clearing the bit in the interrupt flag
register.
Pin-udlste interrupts.
Kilder til afsnittet:
Se: http://www.engblaze.com/we-interrupt-this-program-to-bring-you-a-tutorial-on-arduino-interrupts/
/ Valle Thor
Side 2 af 50
Arduino interrupts
Version
12/11-2015
Pinudlste interrupts kan sttes op til at ske ved en ndring p Arduino-pin 2 eller 3, henholdsvis
INT0 & INT1.
Der kan vlges rising, falling edge, eller pinchange.
Eksterne interrupts kan sttes op the arduino way eller man kan pille bit selv !!
Et eksempel, the Arduino Way:
void setup()
{
pinMode(13, OUTPUT);
// Pin 13 is output to which an LED is connected
digitalWrite(13, LOW);
// Make pin 13 low, switch LED off
pinMode(2, INPUT);
// Pin 2 is input to which a switch is connected = INT0
pinMode(3, INPUT);
// Pin 3 is input to which a switch is connected = INT1
attachInterrupt(0, blink1, RISING);
attachInterrupt(1, blink2, RISING);
// attachInterrupt(0, INT0_handler, CHANGE); //
}
void loop() {
/* Nothing to do: the program jumps automatically to Interrupt Service Routine
"blink" in case of a hardware interrupt on Ardiono pin 2 = INT0 */
}
void blink1(){
// Interrupt service routine
digitalWrite(13, HIGH);
}
void blink2(){
digitalWrite(13, LOW);
}
http://www.geertlangereis.nl/Electronics/Pin_Change_Interrupts/PinChange_en.html
Opstningen af eksterne interrupts kan ogs styres ved at stte bits i SFR-registre.
De SFRs der bruges hertil er:
/ Valle Thor
Side 3 af 50
Arduino interrupts
Version
12/11-2015
00
Ext Int 0
IC Pin 4
Mux
sei();
01
1
3
2
Arduino
Pin 2
10
Extern Interrupt
Flag Register
EIFR
1
11
EIMSK
Extern Sense
Control Register
B1
B0
Bit1
Int1
Bit3 B2
setBit(EICRA,0);
setBit(EICRA,ISC00);
EICRA
EICRA = B00001010;
B0
Bit1
Int0
Int1
setBit(EIMSK,0);
Ext Int 1
IC Pin 5
01
Mux
setBit(EIMSK,INT0);
INT1_VECT()
3
2
EIMSK = B00000011;
ISR(EXT_INT0_vect)
{
}
Sei();
asm("Sei");
Cli();
02
1
3
2
Arduino
Pin 3
B0
Int0
setBit(EIMSK,1);
1
INT0_VECT()
Extern Interrupt
Mask Register
11
Global Interrupt
Enable Bit
SREG
Bit 7
Status Register
12
/ Valle Thor
Side 4 af 50
Arduino interrupts
Version
12/11-2015
Kodeeksempler:
void setup(void)
{
pinMode(2, INPUT);
pinMode(13, OUTPUT);
digitalWrite(2, HIGH);
sei();
EIMSK |= 0b00000001;
EICRA |= 0b00000010;
}
//
//
//
//
void loop(void)
{
Do something
}
ISR(EXT_INT0_vect)
//
// Interrupt Service Routine attached to INT0 vector
{
digitalWrite(13, !digitalRead(13));
Arduino Interrupts
int pbIn = 0;
int ledOut = 4;
volatile int state = LOW;
void setup()
{
// Set up the digital pin 2 to an Interrupt and Pin 4 to an Output
pinMode(ledOut, OUTPUT);
//Attach the interrupt to the input pin and monitor for ANY Change
attachInterrupt(pbIn, stateChange, CHANGE);
}
void loop()
{
/ Valle Thor
Side 5 af 50
Arduino interrupts
Version
12/11-2015
/ Valle Thor
Side 6 af 50
Arduino interrupts
Version
12/11-2015
Timerudlste interrupts
Ved hjlp af Countere kan man opn, at man kan f et interrupt til at ske med jvne mellemrum.
Fx hvis man nsker at f opdateret et stopur hver 100-del af et sekund, eller man nsker at anvende
en uC som frekvensgenerator. Eller fx som cykelcomputer, hvor der skal tlles pulser i en bestemt
tid.
Sm perioder kan direkte udlses direkte ved hjlp af en timer. Men p grund af den ret hje
oscillatorfrekvens, kan lngere varigheder mellem interrupts opns ved fx at tlle antal interrupts i
en variabel, og s kun kre det rigtige interrupt-program hver 100. gang. Programmet kunne her fx
aflse en sensor !!
Indbyggede timere
Prescaler. / Frekvensdeler.
P Arduinoen sidder der et 16 MHz krystal. Derfor vil timeren / Counteren kre ret hurtigt, og give
fx overflow for en 16 bit register i lbet af ca. 4 mS.
/ Valle Thor
Side 7 af 50
Arduino interrupts
Version
12/11-2015
Men heldigvis er der indbygget mulighed for at vlge at neddele clockfrekvensen gennem en
frekvensdeler, eller som det kaldes en prescaler.
Hver counter har sin egen prescaler .
http://courses.cs.washington.edu/courses/csep567/10wi/lectures/Lecture7.pdf
Se fx http://forum.arduino.cc/index.php/topic,27390.0.html
http://www.engblaze.com/microcontroller-tutorial-avr-and-arduino-timer-interrupts/
Udregning af Prescaler-vrdi:
For at f affyret et interrupt med en korrekt frekvens, skal man udregne hvor meget man skal dele
krystallets frekvens ned, s timeren ikke nr at give overflow. Herefter kan en timers startvrdi
udregnes, eller en Compare-Match-vrdi.
Der kan findes hjlp p en rkke hjemmesider til at vlge den rette Prescaler til et bestemt forml.
Se fx:
/ Valle Thor
Side 8 af 50
Arduino interrupts
Version
12/11-2015
http://www.et06.dk/atmega_timers/
16
((. ) + 1)
Vrdien skal vre mindre end 256 hvis timer 0 eller 2 bruges, og mindre end 65.536 for Timer 1
for at det kan lade sig gre:
Eksempel:
Interrupt vlges i dette eksempel til 10 gange pr. sekund, dvs. hver 1/10
sekund, eller til 10 Hz.
Timer 1 giver overflow ved FFFFh + 1 = 65536d.
Frekvensen p krystallet er 16 MHz. Dvs. p 0,1 sek. kommer 1.600.000 pulser.
Det er ndvendig med en frekvensdeler, en prescaler, fordi der kommer for mange
pulser p 0,1 sekund.
Der skal mindst deles med
( 1.600.000 / 65.536 ) =
ca. 24,8
/ Valle Thor
Side 9 af 50
Arduino interrupts
Version
12/11-2015
5.
Side 10 af 50
Arduino interrupts
Version
12/11-2015
TCNTx
Timer/Counter Register.
Indeholder den aktuelle timer
vrdi, 16 bit.
TCNT1H og TCNT1L
OCRxx - Output Compare
Register
16 bit register
OCR1AH og OCR1AL
TIMSKx
Timer/Counter Interrupt Mask
Register. To enable/disable
timer interrupts.
Timer_Overflow_
TIFRx - Timer/Counter
Interrupt Flag Register.
Indicates a pending timer
interrupt.
http://courses.cs.washington.edu/courses/csep567/10wi/lectures/Lecture7.pdf
http://www.avrbeginners.net/architecture/timers/timers.html
https://arduino-info.wikispaces.com/Timers-Arduino
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Die_Timer_und_Z%C3%A4hler_des_AVR
http://www.eng.utah.edu/~cs5789/2010/slides/Interruptsx2.pdf
Med flgende diagram har jeg forsgt at lave et samlet diagram over en timer og interruptstrukturen:
/ Valle Thor
Side 11 af 50
Arduino interrupts
2
3
16 Bit
16 Bit
Compare
TCNT1L
Oscillator
16 MHz
St OCF1A
1 bit
St TOV1 Bit
Timer/Counter 1L
TCNT1 = 0;
Preload:TCNT1 = 25324;
ISR(TIMER1_OVF_vect)
{ TCNT1 = 25324;
}
1
Compare
Bit 1
Reset Counter
TIFR1
16 Bit
Timer/Counter 1H
1
Overflow
Bit 0
2
3
OCR1A
Disable Cli();
noInterrupts ();
ATMEGA328
TIMSK1
ATMEL AVR
Version
12/11-2015
Timer/Counter Interrupt
Flag Register
Output Compare Interrupt
Flag OCF1A og Overflov
flag TOV1 cleares af
hardware ved interrupt kald
Clock-pulser,
f = ( osc / prescale )
Frekvensdeler
= Prescaler
Fra Pins
TCCR1A
TCCR1B
Stop Timer
Divide by 1
8
64
256
1024
Ekstern clock p T1
Definerede
konstanter:
CS10 = 0
CS11 = 1
CS12 = 2
WGM12 = 3
Rev: 14/11-2014
/ Valle
cli();
TCNT1 = 0; // Virker p 16 bit.
OCR1A = 15624;
bitSet(TCCR1B,
bitSet(TCCR1B,
bitSet(TCCR1B,
bitSet(TIMSK1,
sei();
Alle registre og register-bit har navne som compileren kender, og den kender deres vrdier. Derfor
kan man fx skrive bitSet(TCCR1B, CS12); For definerede vrdier: se
setBit(TCCR1B, WGM12); stter et bit der vlger Timer Compare mode. Sttes denne ikke, er
der default valgt overflow mode.
WGM str for Waveform Generation Bit Mode ?
/ Valle Thor
Side 12 af 50
Arduino interrupts
Version
12/11-2015
Notice how the setups between the three timers differ slightly in the line which turns on CTC mode:
setBit(TCCR0A, WGM01);
setBit(TCCR1B, WGM12);
setBit(TCCR2A, WGM21);
//for timer0
//for timer1
//for timer2
This follows directly from the datasheet of the ATMEL 328/168 Kilde # .1
| bitwise OR
& bitwise AND
~ bitwise NOT
^ bitwise EXLUSIVE OR (XOR)
<< bit LEFT SHIFT
>> bit RIGHT SHIFT
Ved man hvilke bit der skal sttes i et register, kan man gre det med en af
flgende muligheder:
TCCR1A
TCCR1A
TCCR1B
TCCR1B
TCCR2A
TCCR2B
TCCR2B
= 0b10101010;
= 0;
= 0;
= 0x03;
= B01000011;
= B00001001;
|= 7;
bitSet(TCCR1A, WGM01);
TCCR0A = _BV(WGM01);
TCCR0B = _BV(CS02) | _BV(CS00);
// Mode = CTC
// _BV er en function, der ???
Fra: (http://www.instructables.com/id/Arduino-Timer-Interrupts/step2/Structuring-Timer-Interrupts/ )
Datablad:
/ Valle Thor
http://www.atmel.com/Images/doc8161.pdf
Side 13 af 50
Arduino interrupts
Version
12/11-2015
Interrupt subrutiner
Her er vist, hvordan man skriver en interrupt-subrutine, en Interrupt Service Rutine.
Eks:
ISR(TIMER1_COMPA_vect)
{
seconds++;
if (seconds == 10)
{
seconds = 0;
readMySensor();
}
}
ISR(TIMER1_OVF_vect)
{
/ Valle Thor
// Compare match
Side 14 af 50
Arduino interrupts
TCNT1 = timer1_counter;
Version
12/11-2015
/*
* Arduino 101: timer and interrupts
* Timer1 overflow interrupt example
* more infos: http://www.letmakerobots.com/node/28278
* created by RobotFreak
*/
#define ledPin 13
void setup()
{
pinMode(ledPin, OUTPUT);
// initialize timer1
noInterrupts();
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 34286;
setBit(TCCR1B, CS12);
setBit(TIMSK1, TOIE1);
/ Valle Thor
Side 15 af 50
Arduino interrupts
interrupts();
Version
12/11-2015
}
ISR(TIMER1_OVF_vect)
// interrupt service routine that wraps a user defined
function supplied by attachInterrupt
{
TCNT1 = 34286;
// preload timer
digitalWrite(ledPin, digitalRead(ledPin) ^ 1);
}
void loop()
{
// your program here...
}
// Se: http://www.hobbytronics.co.uk/arduino-timer-interrupts
#define ledPin 13
int timer1_counter;
void setup()
{
pinMode(ledPin, OUTPUT);
noInterrupts();
TCCR1A = 0;
TCCR1B = 0;
// initialize timer1
// disable all interrupts
// preload timer
// 256 prescaler
// enable timer overflow interrupt
// enable all interrupts
}
ISR(TIMER1_OVF_vect)
// interrupt service routine
{
TCNT1 = timer1_counter;
// preload timer
digitalWrite(ledPin, digitalRead(ledPin) ^ 1);
}
void loop()
{
/ Valle Thor
Side 16 af 50
Arduino interrupts
Version
12/11-2015
/*
Eksempel p Interrupt ved timer overflow.
Testet!!
Valle / 8/11-2013
*/
#define ledPin 13
int timer1_startvalue;
int sekund = 0;
int minut = 0;
volatile boolean flag = 0;
//boolean flag = 0;
void setup()
{
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}
// initialize timer1
noInterrupts();
TCCR1A = 0;
TCCR1B = 0;
//
//
//
//
preload timer
256 prescaler
enable timer1 overflow interrupt
enable all interrupts
}
ISR(TIMER1_OVF_vect)
// interrupt service routine
{
TCNT1 = timer1_startvalue;
// gen-load timer1
digitalWrite(ledPin, digitalRead(ledPin) ^ 1);
sekund++;
flag = HIGH;
if (sekund > 59) {
sekund = 0;
minut++;
/ Valle Thor
Side 17 af 50
Arduino interrupts
Version
12/11-2015
}
}
void loop()
{
while(flag==LOW) { // Wait til change !!
}
Serial.print(minut);
Serial.print(':');
if(sekund<10) Serial.print('0');
Serial.println(sekund);
flag=0;
// delay(1000);
// your program here...
}
// Se:
http://www.hobbytronics.co.uk/arduino-timer-interrupts
/ Valle Thor
Side 18 af 50
Arduino interrupts
Version
12/11-2015
{
pinMode(ledPin, OUTPUT);
// initialize timer1
noInterrupts();
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
OCR1A = 31250;
setBit(TCCR1B, WGM12);
setBit(TCCR1B, CS12);
setBit(TIMSK1, OCIE1A);
interrupts();
}
ISR(TIMER1_COMPA_vect)
// timer compare interrupt service routine
{
digitalWrite(ledPin, digitalRead(ledPin) ^ 1);
// toggle LED pin
}
void loop()
{
// your program here...
}
/ Valle Thor
Side 19 af 50
Arduino interrupts
Version
12/11-2015
void loop()
{
// do some crazy stuff while my LED keeps blinking
}
ISR(TIMER1_COMPA_vect)
{
digitalWrite(LEDPIN, !digitalRead(LEDPIN));
}
void setup()
{
//Use timer 5, output A
// initialize timer5
noInterrupts();
TCCR5A = 0;
TCCR5B = 0;
TCNT5 = 0;
OCR5A = 833;
// compare match register 16MHz/256/75Hz ~75.03Hz
setBit(TCCR5B, WGM12);
// CTC mode
setBit(TCCR5B, CS12);
// 256 prescaler
setBit(TIMSK5, OCIE5A); // enable timer compare interrupt mask
interrupts();
// enable all interrupts
}
void loop()
{
}
ISR(TIMER5_COMPA_vect)
{
/ Valle Thor
Side 20 af 50
Arduino interrupts
Version
12/11-2015
//
//
ISR(TIMER1_COMPA_vect)
{
static uint16_t milliseconds = 0; // mS value for timekeeping 1000mS/1S
static uint16_t clock_cal_counter = 0; // counting up the milliseconds to MS_ADJ
const uint16_t MS_ADJ = 35088;
// F_CPU / (F_CPU * PPM_ERROR)
const uint16_t MS_IN_SEC = 1000; // 1000mS/1S
milliseconds++;
clock_cal_counter++;
//
//
//
//
/ Valle Thor
Side 21 af 50
Arduino interrupts
Version
12/11-2015
Variables shared between ISR functions and normal functions should be declared "volatile". This
tells the compiler that such variables might change at any time, and thus the compiler must reload
the variable whenever you reference it, rather than relying upon a copy it might have in a processor
register.
For example:
volatile boolean flag;
// Interrupt Service Routine (ISR)
void isr ()
{
flag = true;
} // end of isr
void setup ()
{
attachInterrupt (0, isr, CHANGE);
} // end of setup
void loop ()
{
if (flag)
{
// interrupt has occurred
}
} // end of loop
/ Valle Thor
Side 22 af 50
Arduino interrupts
Version
12/11-2015
Flere noter:
(Note that there are two other OCR (OCR1B and OCR1C not used in this example. However they
may be used independent of OCR1A to generate another frequency or other Timer Counter
Functions.
When that value is reached the output toggles provided that the corresponding output pin is set to
output mode. )
Der er flere mder, hvorp man kan f udlst et interrupt p tid:
Det kan fx bruges, hvis man ikke kender placeringen af bittene i timer kontrol-registrene. Men det
krver til gengld, at man ved, at bit CSn2 skal sttes.
I eksemplet skiftes et 1-tal til venstre et antal gange, angivet af vrdien af CS12. Compileren
kender denne vrdi!
Prdefinerede vrdier, som compileren kender:
De er defineret i en include-fil:
#define CS00 0
#define CS01 1
#define CS02 2
#define TOIE1 0
Se: http://courses.cs.washington.edu/courses/csep567/10wi/lectures/Lecture7.pdf
Ved man hvilke bit der skal sttes i et register, kan man gre det med en af flgende muligheder:
TCCR1A = 0b10101010;
TCCR1A = 0;
/ Valle Thor
Side 23 af 50
Arduino interrupts
TCCR1B
TCCR1B
TCCR2A
TCCR2B
TCCR2B
= 0;
= 0x03;
= B01000011;
= B00001001;
|= 7;
Version
12/11-2015
TCCR0A = _BV(WGM01);
TCCR0B = _BV(CS02) | _BV(CS00);
// Mode = CTC
// _BV er en function, der ???
| bit OR
& bit AND
~ bit NOT
^ bit EXLUSIVE OR (XOR)
<< bit LEFT SHIFT
>> bit RIGHT SHIFT
http://tom-itx.dyndns.org:81/~webpage/avr/c_bits/bits_index.php
Interrupt subrutine:
En interrupt service rutine definers som flgende:
ISR(Timerx_ COMPA_VECT) {
Do something
}
Eksempel:
Arduino timer compare match interrupts:
//stop interrupts
/ Valle Thor
Side 24 af 50
OCR0A = 124;
setBit(TCCR0A,
setBit(TCCR0B,
setBit(TCCR0B,
setBit(TIMSK0,
Arduino interrupts
//
//
//
WGM01);
CS01);
CS00); //
OCIE0A);
setBit(TCCR1B,
setBit(TCCR1B,
setBit(TCCR1B,
setBit(TIMSK1,
Version
12/11-2015
to 0
0
for 1hz
(must be <65536)
WGM12);
// turn on CTC mode
CS12);
CS10); // Set CS10 og CS12 bits for 1024 presc.
OCIE1A);
// enable timer compare interrupt
at 8kHz
// set entire TCCR2A register
// same for TCCR2B
//initialize counter value to
// set compare match register
// increments
// = (16*10^6) / (8000*8) - 1
setBit(TCCR2A, WGM21);
setBit(TCCR2B, CS21);
setBit(TIMSK2, OCIE2A);
0
for 8khz
(must be <256)
sei();
//allow interrupts
//end setup
ISR(TIMER0_COMPA_vect){
to 0
http://www.instructables.com/id/Arduino-Timer-Interrupts/
Interrupts(); noInterrupts();
/ Valle Thor
Side 25 af 50
Arduino interrupts
Version
12/11-2015
// preload timer
void setup ()
{
pinMode (13, OUTPUT);
noInterrupts();
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 34286;
setBit(TCCR1B, CS12);
setBit(TIMSK1, TOIE1);
interrupts();
}
// end of setup
void loop ()
{
noInterrupts ();
// ; Beskyt loop-koden: = cli();
if (count > 4){
digitalWrite(13, digitalRead(13) ^ 1);
// Flip pin
count = 0;
}
interrupts ();
// Enable interrupt igen = sei();
// end of loop
}
/ Valle Thor
Side 26 af 50
Arduino interrupts
Version
12/11-2015
http://www.kner.at/home/40.avr/_architektur/ztcpwm.html
/ Valle Thor
Side 27 af 50
Arduino interrupts
Version
12/11-2015
http://www.kner.at/home/40.avr/_architektur/ztcbint.html
/ Valle Thor
Side 28 af 50
Arduino interrupts
Version
12/11-2015
Og en overflow match.
http://www.kner.at/home/40.avr/_architektur/ztcbint.html
Resten er endnu ikke helt gennemarbejdet, kan betragtes som Bonus Info
TIMSK1. This is the Timer/Counter1 Interrupt Mask Register. Setting the TOIE1 bit tells the timer to
/ Valle Thor
Side 29 af 50
Arduino interrupts
Bit 7
TOIE1 OCIE1A
---
---
TICIE1
---
TOIE0
Version
12/11-2015
Bit 0
---
TOIE1: Timer Overflow Interrupt Enable (Timer 1); If this bit is set and if global interrupts are enabled, the micro will jump
to the Timer Overflow 1 interrupt vector upon Timer 1 Overflow.
OCIE1A: Output Compare Interrupt Enable 1 A; If set and if global Interrupts are enabled, the micro will jump to the
Output Compare A Interrupt vetor upon compare match.
TICIE1: Timer 1 Input Capture Interrupt Enable; If set and if global Interrupts are enabled, the micro will jump to the Input
Capture Interrupt vector upon an Input Capture event.
TOIE0: Timer Overflow Interrupt Enable (Timer 0); Same as TOIE1, but for the 8-bit Timer 0.
TCCR1A = 0x00;
// COM1A1=0, COM1A0=0 => Disconnect Pin OC1 from Timer/Counter 1 -PWM11=0,PWM10=0 => PWM Operation disabled, ICNC1=0 => Capture Noise Canceler
disabled -- ICES1=0 => Input Capture Edge Select (not used) -- CTC1=0 => Clear
Timer/Counter 1 on Compare/Match, CS12=0 CS11=1 CS10=1 => Set prescaler to
clock/64
TCCR1B = 0x03;
// 16MHz clock with prescaler means TCNT1 increments every 4uS,
ICIE1=0 => Timer/Counter 1, Input Capture Interrupt Enable -- OCIE1A=0 => Output
Compare A Match Interrupt Enable -- OCIE1B=0 => Output Compare B Match Interrupt
Enable
TOIE1=0 => Timer 1 Overflow Interrupt Enable
TCCR3A register
COM3A1
COM3A0
TCCR3B register
ICNC3
ICES3
COM3B1
COM3B0
COM3C1
COM3C0
WGM11
WGM10
WGM13
WGM12
CS12
CS11
CS10
http://courses.cs.washington.edu/courses/csep567/10wi/lectures/Lecture7.pdf
/ Valle Thor
Side 30 af 50
Arduino interrupts
Version
12/11-2015
http://www.avrbeginners.net/architecture/timers/timers.html
16 bit timere er
lidt mere
complex.
/ Valle Thor
Side 31 af 50
Arduino interrupts
Version
12/11-2015
http://www.avrbeginners.net/architecture/timers/timers.html
http://www.avrbeginners.net/architecture/timers/timers.html
/ Valle Thor
Side 32 af 50
Arduino interrupts
Version
12/11-2015
http://www.nunoalves.com/classes/spring_2012_cpe355/cpe355-02-b.pdf
http://www.gammon.com.au/forum/?id=11488
http://www.protostack.com/blog/2010/09/timer-interrupts-on-an-atmega168/
Gode oversigter over register!
http://www.instructables.com/id/Arduino-Timer-Interrupts/
//storage variables
boolean toggle0 = 0;
boolean toggle1 = 0;
boolean toggle2 = 0;
void setup(){
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
pinMode(13, OUTPUT);
cli();
/ Valle Thor
//stop interrupts
Side 33 af 50
Arduino interrupts
Version
12/11-2015
sei();//allow interrupts
}//end setup
ISR(TIMER0_COMPA_vect){
if (toggle0){
digitalWrite(8,HIGH);
toggle0 = 0;
}
else{
digitalWrite(8,LOW);
toggle0 = 1;
}
}
ISR(TIMER1_COMPA_vect){
/ Valle Thor
Side 34 af 50
Arduino interrupts
Version
12/11-2015
if (toggle2){
digitalWrite(9,HIGH);
toggle2 = 0;
}
else{
digitalWrite(9,LOW);
toggle2 = 1;
}
}
void loop(){
//do other things here
}
The images above show the outputs from these timer interrupts. Fig 1 shows a square wave oscillating
between 0 and 5V at 1kHz (timer0 interrupt), fig 2 shows the LED attached to pin 13 turning on for one
second then turning off for one second (timer1 interrupt), fig 3 shows a pulse wave oscillating between
0 and 5V at a frequency of 4khz (timer2 interrupt).
Bike Speedometer
In this example I made an arduino bike speedometer. It works by attaching a magnet to the wheel and
measuring the amount of time it takes to pass by a magnetic switch mounted on the frame- the time for
one complete rotation of the wheel.
I set timer 1 to interrupt every ms (frequency of 1kHz) to measure the magnetic switch. If the magnet is
passing by the switch, the signal from the switch is high and the variable "time" gets set to zero. If the
magnet is not near the switch "time" gets incremented by 1. This way "time" is actually just a
measurement of the amount of time in milliseconds that has passed since the magnet last passed by
the magnetic switch. This info is used later in the code to calculate rpm and mph of the bike.
Here's the bit of code that sets up timer1 for 1kHz interrupts
Here's the complete code if you want to take a look:
//bike speedometer
//by Amanda Ghassaei 2012
/ Valle Thor
Side 35 af 50
Arduino interrupts
Version
12/11-2015
//http://www.instructables.com/id/Arduino-Timer-Interrupts/
//http://www.instructables.com/id/Arduino-Timer-Interrupts/
/*
*
*
*
*
*
*/
//sample calculations
//tire radius ~ 13.5 inches
//circumference = pi*2*r =~85 inches
//max speed of 35mph =~ 616inches/second
//max rps =~7.25
#define reed A0//pin connected to read switch
//storage variables
float radius = 13.5;// tire radius (in inches)- CHANGE THIS FOR YOUR OWN BIKE
int reedVal;
long time = 0;// time between one full rotation (in ms)
float mph = 0.00;
float circumference;
boolean backlight;
int maxReedCounter = 100;//min time (in ms) of one rotation (for debouncing)
int reedCounter;
void setup(){
reedCounter = maxReedCounter;
circumference = 2*3.14*radius;
pinMode(1,OUTPUT);//tx
pinMode(2,OUTPUT);//backlight switch
pinMode(reed,INPUT);//redd switch
checkBacklight();
Serial.write(12);//clear
// TIMER SETUP- the timer interrupt allows preceise timed measurements of the reed
switch
//for mor info about configuration of arduino timers see
http://arduino.cc/playground/Code/Timer1
cli();//stop interrupts
//set timer1 interrupt at 1kHz
TCCR1A = 0;// set entire TCCR1A register to 0
TCCR1B = 0;// same for TCCR1B
TCNT1 = 0;//initialize counter value to 0;
// set timer count for 1khz increments
OCR1A = 1999;// = (16*10^6) / (1000*8) - 1
// turn on CTC mode
TCCR1B |= (1 << WGM12);
// Set CS11 bit for 8 prescaler
TCCR1B |= (1 << CS11);
// enable timer compare interrupt
TIMSK1 |= (1 << OCIE1A);
sei();//allow interrupts
//END TIMER SETUP
/ Valle Thor
Side 36 af 50
Arduino interrupts
Version
12/11-2015
Serial.begin(9600);
}
void checkBacklight(){
backlight = digitalRead(2);
if (backlight){
Serial.write(17);//turn backlight on
}
else{
Serial.write(18);//turn backlight off
}
}
ISR(TIMER1_COMPA_vect) {//Interrupt at freq of 1kHz to measure reed switch
reedVal = digitalRead(reed);//get val of A0
if (reedVal){//if reed switch is closed
if (reedCounter == 0){//min time between pulses has passed
mph = (56.8*float(circumference))/float(time);//calculate miles per hour
time = 0;//reset timer
reedCounter = maxReedCounter;//reset reedCounter
}
else{
if (reedCounter > 0){//don't let reedCounter go negative
reedCounter -= 1;//decrement reedCounter
}
}
}
else{//if reed switch is open
if (reedCounter > 0){//don't let reedCounter go negative
reedCounter -= 1;//decrement reedCounter
}
}
if (time > 2000){
mph = 0;//if no new pulses from reed switch- tire is still, set mph to 0
}
else{
time += 1;//increment timer
}
}
void displayMPH(){
Serial.write(12);//clear
Serial.write("Speed =");
Serial.write(13);//start a new line
Serial.print(mph);
Serial.write(" MPH ");
//Serial.write("0.00 MPH ");
}
void loop(){
//print mph once a second
displayMPH();
delay(1000);
checkBacklight();
}
For this project, I used timer2 interrupts to periodically check if there was any incoming serial data, read
it, and store it in the matrix "ledData[]". If you take a look at the code you will see that the main loop of
/ Valle Thor
Side 37 af 50
Arduino interrupts
Version
12/11-2015
the sketch is what is actually responsible for using the info in ledData to light up the correct LEDs and
checking on the status of the buttons (a function called "shift()"). The interrupt routine is as short as
possible- just checking for incoming bytes and storing them appropriately.
Here is the setup for timer2:
cli();//stop interrupts
//set timer2 interrupt every 128us
TCCR2A = 0;// set entire TCCR2A register to 0
TCCR2B = 0;// same for TCCR2B
TCNT2 = 0;//initialize counter value to 0
// set compare match register for 7.8khz increments
OCR2A = 255;// = (16*10^6) / (7812.5*8) - 1 (must be <256)
// turn on CTC mode
TCCR2A |= (1 << WGM21);
// Set CS21 bit for 8 prescaler
TCCR2B |= (1 << CS21);
// enable timer compare interrupt
TIMSK2 |= (1 << OCIE2A);
sei();//allow interrupts
//this firmware will send data back and forth with the maxmsp patch "beat slicer"
//pin connections
#define ledLatchPin A1
#define ledClockPin A0
#define ledDataPin A2
#define buttonLatchPin 9
#define buttonClockPin 10
#define buttonDataPin A3
//looping variables
byte i;
byte j;
byte k;
byte ledByte;
//storage for led states, 4 bytes
byte ledData[] = {0, 0, 0, 0};
//storage for buttons, 4 bytes
byte buttonCurrent[] = {0,0,0,0};
byte buttonLast[] = {0,0,0,0};
byte buttonEvent[] = {0,0,0,0};
byte buttonState[] = {0,0,0,0};
//button debounce counter- 16 bytes
byte buttonDebounceCounter[4][4];
/ Valle Thor
Side 38 af 50
Arduino interrupts
Version
12/11-2015
void setup() {
DDRC = 0xF7;//set A0-2 and A4-5 output, A3 input
DDRB = 0xFF;//digital pins 8-13 output
Serial.begin(57600);
cli();//stop interrupts
//set timer2 interrupt every 128us
TCCR2A = 0;// set entire TCCR2A register to 0
TCCR2B = 0;// same for TCCR2B
TCNT2 = 0;//initialize counter value to 0
// set compare match register for 7.8khz increments
OCR2A = 255;// = (16*10^6) / (7812.5*8) - 1 (must be <256)
// turn on CTC mode
TCCR2A |= (1 << WGM21);
// Set CS21 bit for 8 prescaler
TCCR2B |= (1 << CS21);
// enable timer compare interrupt
TIMSK2 |= (1 << OCIE2A);
sei();//allow interrupts
}
// buttonCheck - checks the state of a given button.
//this buttoncheck function is largely copied from the monome 40h firmware by brian
crabtree and joe lake
void buttonCheck(byte row, byte index)
{
if (((buttonCurrent[row] ^ buttonLast[row]) & (1 << index)) &&
// if the current
physical button state is different from the
((buttonCurrent[row] ^ buttonState[row]) & (1 << index))) { // last physical button
state AND the current debounced state
if (buttonCurrent[row] & (1 << index)) {
// if the current
physical button state is depressed
buttonEvent[row] = 1 << index;
// queue up a new button event
immediately
buttonState[row] |= (1 << index);
// and set the debounced
state to down.
}
else{
buttonDebounceCounter[row][index] = 12;
} // otherwise the button was previously depressed and now
// has been released so we set our debounce counter.
}
else if (((buttonCurrent[row] ^ buttonLast[row]) & (1 << index)) == 0 && // if the
current physical button state is the same as
(buttonCurrent[row] ^ buttonState[row]) & (1 << index)) {
// the last physical
button state but the current physical
// button state is different from the current debounce
// state...
if (buttonDebounceCounter[row][index] > 0 && --buttonDebounceCounter[row][index] ==
0) { // if the the debounce counter has
// been decremented to 0 (meaning the
// the button has been up for
// kButtonUpDefaultDebounceCount
// iterations///
buttonEvent[row] = 1 << index;
/ Valle Thor
Side 39 af 50
Arduino interrupts
Version
12/11-2015
debounce state.
buttonState[row] |= (1 << index);
}
else{
buttonState[row] &= ~(1 << index);
}
}
}
}
void shift(){
for (i=0;i<4;i++){
buttonLast[i] = buttonCurrent[i];
byte dataToSend = (1 << (i+4)) | (15 & ~ledData[i]);
// set latch pin low so the LEDs don't change while sending in bits
digitalWrite(ledLatchPin, LOW);
// shift out the bits of dataToSend
shiftOut(ledDataPin, ledClockPin, LSBFIRST, dataToSend);
//set latch pin high so the LEDs will receive new data
digitalWrite(ledLatchPin, HIGH);
//once one row has been set high, receive data from buttons
//set latch pin high
digitalWrite(buttonLatchPin, HIGH);
//shift in data
buttonCurrent[i] = shiftIn(buttonDataPin, buttonClockPin, LSBFIRST) >> 3;
//latchpin low
digitalWrite(buttonLatchPin, LOW);
for (k=0;k<4;k++){
buttonCheck(i,k);
if (buttonEvent[i]<<k){
if (buttonState[i]&1<<k){
Serial.write(((3-k)<<3)+(i<<1)+1);
}
else{
Serial.write(((3-k)<<3)+(i<<1)+0);
}
buttonEvent[i] &= ~(1<<k);
}
}
}
}
ISR(TIMER2_COMPA_vect) {
do{
if (Serial.available()){
ledByte = Serial.read();//000xxyys
boolean ledstate = ledByte & 1;
byte ledy = (ledByte >> 1) & 3;
byte ledx = (ledByte >> 3) & 3;
if (ledstate){
ledData[ledy] |= 8 >> ledx;
}
else{
ledData[ledy] &= ~ (8 >> ledx);
}
}//end if serial available
}//end do
/ Valle Thor
Side 40 af 50
Arduino interrupts
Version
12/11-2015
One last thing to note- certain timer setups will actually disable some of the Arduino library
functions. Timer0 is used by the functions millis() and delay(), if you manually set up timer0, these
functions will not work correctly.
Additionally, all three timers underwrite the function analogWrite(). Manually setting up a timer will stop
analogWrite() from working.
If there is some portion of your code that you don't want interrupted, considering using cli() and sei() to
globally disable and enable interrupts.
You can read more about this on the Arduino website.
/ Valle Thor
Side 41 af 50
Arduino interrupts
Version
12/11-2015
Stopwatch http://playground.arduino.cc/Code/Stopwatch
A sketch that demonstrates how to do two (or more) things at once by using millis().
/* StopWatch
* Paul Badger 2008
* Demonstrates using millis(), pullup resistors,
* making two things happen at once, printing fractions
*
* Physical setup: momentary switch connected to pin 4, other side
connected to ground
* LED with series resistor between pin 13 and ground
*/
#define ledPin 13
#define buttonPin 4
void setup()
{
Serial.begin(9600);
pinMode(ledPin, OUTPUT);
pinMode(buttonPin, INPUT);
// not really necessary, pins default
to INPUT anyway
digitalWrite(buttonPin, HIGH);
// turn on pullup resistors. Wire
button so that press shorts pin to ground.
}
void loop()
{
// check for button press
buttonState = digitalRead(buttonPin);
button state and store
/ Valle Thor
// read the
Side 42 af 50
Arduino interrupts
Version
12/11-2015
startTime = millis();
the start time
blinking = true;
blinking while timing
delay(5);
delay to debounce switch
lastButtonState = buttonState;
buttonState in lastButtonState, to compare next time
// store
// turn on
// short
// store
}
else if (buttonState == LOW && lastButtonState == HIGH &&blinking == t
rue){
// check for a high to low transition
// if true then found a new button press while clock is running stop the clock and report
elapsedTime =
millis() - startTime;
elapsed time
blinking = false;
blinking, all done timing
lastButtonState = buttonState;
buttonState in lastButtonState, to compare next time
// store
// turn off
// store
// divide by
// print decimal
point
// use modulo operator to get fractional part of time
fractional = (int)(elapsedTime % 1000L);
// pad in leading zeros - wouldn't it be nice if
// Arduino language had a flag for this? :)
if (fractional == 0)
Serial.print("000");
// add three zero's
else if (fractional < 10)
// if fractional < 10 the 0 is
ignored giving a wrong time, so add the zeros
Serial.print("00");
// add two zeros
else if (fractional < 100)
Serial.print("0");
// add one zero
Serial.println(fractional);
}
else{
/ Valle Thor
Side 43 af 50
Arduino interrupts
lastButtonState = buttonState;
buttonState in lastButtonState, to compare next time
}
//
//
//
than
//
Version
12/11-2015
// store
// remember
// turn off
/ Valle Thor
Side 44 af 50
Arduino interrupts
Version
12/11-2015
LCD module. The I2C interface is suggested as this requires only 4 wires from the Arduino board to the LCD
the less wires the better.
Example 37.3
/*
Example 37.3 Basic speedometer using millis();
http://tronixstuff.wordpress.com/tutorials > chapter 37
John Boxall | CC by-sa-nc
*/
#include "Wire.h" // for I2C bus LCD
#include "LiquidCrystal_I2C.h" // for I2C bus LCD module - http://bit.ly/m7K5wt
LiquidCrystal_I2C lcd(0x27,16,2); // set the LCD address to 0x27 for a 16
chars and 2 line display
float start, finished;
float elapsed, time;
float circMetric=1.2; // wheel circumference relative to sensor position (in
meters)
float circImperial; // using 1 kilometer = 0.621371192 miles
float speedk, speedm;
// holds calculated speed vales in metric and imperial
void setup()
{
attachInterrupt(0, speedCalc, RISING); // interrupt called when sensors sends
digital 2 high (every wheel rotation)
start=millis();
// setup LCD
lcd.init();
// initialize the lcd
lcd.backlight(); // turn on LCD backlight
lcd.clear();
lcd.println(" Wear a helmet! ");
delay(3000);
lcd.clear();
Serial.begin(115200);
circImperial=circMetric*.62137; // convert metric to imperial for MPH
calculations
}
void speedCalc()
{
elapsed=millis()-start;
start=millis();
speedk=(3600*circMetric)/elapsed; // km/h
speedm=(3600*circImperial)/elapsed; // Miles per hour
}
void loop()
{
lcd.setCursor(0,0);
lcd.print(int(speedk));
lcd.print(" km/h ");
lcd.print(int(speedm));
/ Valle Thor
Side 45 af 50
Arduino interrupts
Version
12/11-2015
lcd.print(" MPH
");
lcd.setCursor(0,1);
lcd.print(int(elapsed));
lcd.print(" ms/rev
");
delay(1000); // adjust for personal preference to minimise flicker
}
There isnt that much going on every time the wheel completes one revolution the signal from the sensor will go
from low to high triggering an interrupt which calls the function speedCalc(). This takes a reading of millis() and
then calculates the difference between the current reading and the previous reading this value becomes the time
to cover the distance (which is the circumference of the wheel relative to the sensor stored in
float circMetric=1.2;
and is measured in metres). It finally calculates the speed in km/h and MPH. Between interrupts the sketch
displays the updated speed data on the LCD as well as the raw time value for each revolution for curiositys sake.
In real life I dont think anyone would mount an LCD on a bicycle, perhaps an LED display would be more relevant.
In the meanwhile, you can see how this example works in the following short video clip. Instead of a bike wheel
and reed switch/magnet combination, I have connected the square-wave output from a function generator to the
interrupt pin to simulate the pulses from the sensor, so you can get an idea of how it works:
http://letsmakerobots.com/node/28278
1 Reset
2 External Interrupt Request 0
3 External Interrupt Request 1
4 Pin Change Interrupt Request
5 Pin Change Interrupt Request
6 Pin Change Interrupt Request
7 Watchdog Time-out Interrupt
8 Timer/Counter2 Compare Match
9 Timer/Counter2 Compare Match
10 Timer/Counter2 Overflow
11 Timer/Counter1 Capture Event
12 Timer/Counter1 Compare Match
13 Timer/Counter1 Compare Match
14 Timer/Counter1 Overflow
15 Timer/Counter0 Compare Match
16 Timer/Counter0 Compare Match
17 Timer/Counter0 Overflow
18 SPI Serial Transfer Complete
19 USART Rx Complete
20 USART, Data Register Empty
21 USART, Tx Complete
/ Valle Thor
(pin D2)
(pin D3)
0 (pins D8 to D13)
1 (pins A0 to A5)
2 (pins D0 to D7)
A
B
A
B
A
B
(INT0_vect)
(INT1_vect)
(PCINT0_vect)
(PCINT1_vect)
(PCINT2_vect)
(WDT_vect)
(TIMER2_COMPA_vect)
(TIMER2_COMPB_vect)
(TIMER2_OVF_vect)
(TIMER1_CAPT_vect)
(TIMER1_COMPA_vect)
(TIMER1_COMPB_vect)
(TIMER1_OVF_vect)
(TIMER0_COMPA_vect)
(TIMER0_COMPB_vect)
(TIMER0_OVF_vect)
(SPI_STC_vect)
(USART_RX_vect)
(USART_UDRE_vect)
(USART_TX_vect)
Side 46 af 50
Arduino interrupts
22
23
24
25
26
Version
12/11-2015
(ADC_vect)
(EE_READY_vect)
(ANALOG_COMP_vect)
(TWI_vect)
(SPM_READY_vect)
Fra: http://www.gammon.com.au/forum/?id=11488
//Enable interrupts
//Fast PWM mode 3.
//fire INT on Compare match
//initialize Timer2 to 0
//Set compare A value
//Enable CompA interrupt
//Enable OVF interrupt
//Clock select. Internal, prescale
1024~64us
ISR(TIMER2_COMPA_vect)
{
digitalWrite(ledPin, HIGH);
}
ISR(TIMER2_OVF_vect)
{
digitalWrite(ledPin, LOW);
compAval = compAval + 4;
if(compAval > 254)
compAval = 10;
OCR2A = compAval;
}
void loop()
{
//Serial.println(TCNT2, DEC);
}
/ Valle Thor
Side 47 af 50
Arduino interrupts
Version
12/11-2015
http://courses.cs.washington.edu/courses/csep567/10wi/lectures/Lecture6.pdf
TCCR1B:
Bit 7
ICNC1
/ Valle Thor
ICES1
---
---
CTC1
CS12
CS11
Bit 0
CS10
Side 48 af 50
Arduino interrupts
Version
12/11-2015
ICNC1: Input Capture Noise Canceler; If set, the Noise Canceler on the ICP pin is activated. It will trigger the input
capture after 4 equal samples. The edge to be triggered on is selected by the ICES1 bit.
ICES1: Input Capture Edge Select;
When cleared, the contents of TCNT1 are transferred to ICR (Input Capture Register) on the falling edge of the ICP pin.
If set, the contents of TCNT1 are transferred on the rising edge of the ICP pin.
CTC1: Clear Timer/Counter 1 on Compare Match; If set, the TCNT1 register is cleared on compare match. Use this bit
to create repeated Interrupts after a certain time, e.g. to handle button debouncing or other frequently occuring events.
Timer 1 is also used in normal mode, remember to clear this bit when leaving compare match mode if it was set.
Otherwise the timer will never overflow and the timing is corrupted.
http://www.let-elektronik.dk/tutorials
Links:
God side om timere:
https://sites.google.com/site/qeewiki/books/avr-guide/timer-on-the-atmega8
http://www.engblaze.com/microcontroller-tutorial-avr-and-arduino-timer-interrupts/
Se eksempel p Cykel-computer:
http://www.instructables.com/id/Arduino-Bike-Speedometer/?ALLSTEPS
http://letsmakerobots.com/node/28278
/ Valle Thor
Side 49 af 50
Arduino interrupts
Version
12/11-2015
/ Valle Thor
Side 50 af 50