Professional Documents
Culture Documents
12/11-2015
Hvordan får man Arduino til fx at udføre et interrupt og køre en programstump fx nøjagtig 100
gange i sekundet.
Det program, der normalt kører på en controller, er normalt en række af sekventielle instruktioner
udført efter hinanden i et loop. Den tid, et loop tager, er derfor afhængig af hvor stort programmet
er.
Derfor er man nødt til at anvende Interrupts, hvis man skal være sikker på timingen.
Et interrupt er en event – eller hændelse, udløst enten internt fra en tæller / counter, - eller eksternt
fra en pin, der ændrer status.
Fx kan et program køre – og samtidig kan der af en timer udløses et interrupt, der blinker en LED.
Det program, der køres når eventen opstår, kaldes en interrupt service routine (ISR).
Når et Interrupt opstår, afbrydes det kørende program øjeblikkeligt, og interrupt service rutinen,
dvs. en subrutine, kaldes.
Når en ISR er færdig, vil det kørende program fortsætte hvorfra det blev afbrudt.
Typisk er der gjort brug af interrupts allerede i de tidligere opgaver, der er lavet.
Det er fordi, Arduino´s underlæggende compiler automatisk indbygger mulighed for at bruge
funktioner som, millis() , micros() og delayMicroseconds(). Disse funktioner gør brug af interrupts
ved brug af timere.
PWM-funktionen analogWrite() bruger timere, ligesom tone(), noTone() og Servo library gør.
Hvad er et interrupt?
Det program, der kører på en controller, er normalt en række af sekventielle udførte instruktioner
efter hinanden.
Et interrupt er en event, udløst enten internt fra en timer / counter, - eller eksternt fra en pin.
Det program, der køres ved eventen, kaldes en interrupt service routine (ISR).
Når en ISR er færdig, vil det kørende program fortsætte hvorfra det blev afbrudt.
Før man kan benytte interrupts, skal der noget opsætning til. Ved at sætte forskellige bits i
forskellige special function registre kan man opsætte processoren til at interrupte på forskellige
events.
Interrupts skal altså enables og den tilhørende interrupt maske skal enables, ligesom det skal
bestemmes, hvad processoren skal udføre, når den interruptes.
Når der udløses et interrupt, afbryder det igangværende program øjeblikkeligt hvad det er i gang
med, og hopper til en separat programstump kaldet en 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.
Fordelen ved brug af interrupts er, at man kan have et Loop-program til fx at update et 7-segment
display, - og så kan interrupts fx udløst hver 100-del sekund opdatere variable, indeholdende 100-
del sekunder, sekunder, minutter osv.
Pin-udløste interrupts.
Pinudløste interrupts kan sættes op til at ske ved en ændring på Arduino-pin 2 eller 3, henholdsvis
INT0 & INT1.
Eksterne interrupts kan sættes op ” the arduino way ” – eller man kan pille bit selv !!
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 */
}
http://www.geertlangereis.nl/Electronics/Pin_Change_Interrupts/PinChange_en.html
Opsætningen af eksterne interrupts kan også styres ved at sætte bits i SFR-registre.
1 2 00
Mux
Ext Int 0
IC Pin 4 01
sei(); // Enable Global Interrupt
1
3
2
Arduino 10 Extern Interrupt
Pin 2 Flag Register
EIFR
11 1
3
INT0_VECT() {
EIMSK 2
1 0 Extern Interrupt
Mask Register
Extern Sense
Control Register Bit3 B2 B1 B0 Bit1 B0 Bit1 B0 1
INT1_VECT() {
ISC11 ISC10 ISC01 ISC00 Int1 Int0 Int1 Int0 3
setBit(EICRA,0); 2
setBit(EICRA,ISC00);
EICRA setBit(EIMSK,0);
EICRA = B00001010; Flag cleares automatisk ISR(EXT_INT0_vect)
setBit(EIMSK,1); ved Interruptkald
{
1 0 setBit(EIMSK,INT0); SetBit(EIFR,0); // Clr Flag
Mux SetBit(EIFR,1);
1 2 01 EIMSK = B00000011; }
Sei();
asm("Sei");
Ext Int 1 Cli();
1
IC Pin 5 02
3
2
Global Interrupt
Enable Bit
Arduino 11
SREG Bit 7
Pin 3 Status Register
12
Variable, der bruges i og
udenfor en interruptrutine
skal defineres Globale,
"Volatile"
Kodeeksempler:
void setup(void)
{
pinMode(2, INPUT);
pinMode(13, OUTPUT);
digitalWrite(2, HIGH); // Enable pullup resistor
sei(); // Enable global interrupts
EIMSK |= 0b00000001; // Enable external interrupt INT0
EICRA |= 0b00000010; // Trigger INT0 on falling edge
}
void loop(void)
{
Do something
}
//
ISR(EXT_INT0_vect) // Interrupt Service Routine attached to INT0 vector
{
digitalWrite(13, !digitalRead(13)); // Toggle LED on pin 13
}
Arduino Interrupts
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()
{
void stateChange()
{
state = !state;
digitalWrite(ledOut, state);
}
http://www.dave-auld.net/index.php?option=com_content&view=article&id=107:arduino-interrupts&catid=53:arduino-input-output-
basics&Itemid=107
Timerudløste interrupts
Ved hjælp af Countere kan man opnå, at man kan få et interrupt til at ske med jævne 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 tælles pulser i en bestemt
tid.
Små perioder kan direkte udløses direkte ved hjælp af en timer. Men på grund af den ret høje
oscillatorfrekvens, kan længere varigheder mellem interrupts opnås ved fx at tælle antal interrupts i
en variabel, og så kun køre det rigtige interrupt-program hver 100. gang. Programmet kunne her fx
aflæse en sensor !!
Man kan indstille en startværdi for Counteren, og så få et interrupt ved overflow, eller man kan
tælle fra 0, og så få et interrupt, når tælleren når en bestemt værdi, indstillet i et register, et
Compare-register.
Indbyggede timere
Timer 0: 8 bit
Timer 1: 16 bit.
Timer 2: 8 bit
Timerne kan indstilles til at tælle eksterne pulser, eller der kan tælles på krystallets frekvens.
Prescaler. / Frekvensdeler.
På Arduinoen sidder der et 16 MHz krystal. Derfor vil timeren / Counteren køre ret hurtigt, og give
fx overflow for en 16 bit register i løbet af ca. 4 mS.
Men heldigvis er der indbygget mulighed for at vælge at neddele clockfrekvensen gennem en
frekvensdeler, eller som det kaldes en prescaler.
Bittene hedder:
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-værdi:
For at få affyret et interrupt med en korrekt frekvens, skal man udregne hvor meget man skal dele
krystallets frekvens ned, så timeren ikke når at give overflow. Herefter kan en timers startværdi
udregnes, eller en Compare-Match-værdi.
Der kan findes hjælp på en række hjemmesider til at vælge den rette Prescaler til et bestemt formål.
Se fx:
http://www.et06.dk/atmega_timers/
16 𝑀𝐻𝑧
𝑖𝑛𝑡𝑒𝑟𝑟𝑢𝑝𝑡 𝑓𝑟𝑒𝑘𝑣𝑒𝑛𝑠 =
𝑃𝑟𝑒𝑠𝑐𝑎𝑙𝑒𝑟 ∙ ((𝐶. 𝑀𝑎𝑡𝑐ℎ 𝑟𝑒𝑔) + 1)
16.000.000
𝐶𝑀𝑅 = ( )−1
𝑃𝑟𝑒𝑠𝑐𝑎𝑙𝑒𝑟 ∙ 𝑖𝑛𝑡𝑒𝑟𝑟𝑢𝑝𝑡 𝑓𝑟𝑒𝑘𝑣.
Værdien skal være mindre end 256 hvis timer 0 eller 2 bruges, og mindre end 65.536 for Timer 1
for at det kan lade sig gøre:
Eksempel:
Interrupt vælges i dette eksempel til 10 gange pr. sekund, dvs. hver 1/10
sekund, eller til 10 Hz.
Det er nødvendig med en frekvensdeler, en prescaler, fordi der kommer for mange
pulser på 0,1 sekund.
fx vælges 64
Vælges Output Compare, tælles fra 0000h og til 25.000. Compare registeret skal
derfor indstilles til 25.000.
Different clock sources can be selected for each timer independently. To calculate the timer frequency (for
example 2Hz using timer1) you will need:
Timer setup code is done inside the setup(){} function in an Arduino sketch.
For at man kan styre timerne er der en del kontrol-registre, hvis bit – eller indhold skal bestemmes.
TCCR1A og TCCR1B
Timer/Counter Control Register.
Pre-scaleren konfigureres her.
TCNTx
Timer/Counter Register.
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 følgende diagram har jeg forsøgt at lave et samlet diagram over en timer og interrupt-
strukturen:
TIMSK1 2
Compare match: TIMSK1 |= ( 1 << OCIE1A );
bitSet(TIMSK1, OCIE1A); 2 1
1 3 Ov erf low Vector
TIMSK1 |= B00000010; Overflow
3 Bit 0 ISR(TIMER1_OVF_vect)
Overflow: bitSet(TIMSK1, TOIE1); { TCNT1 = 25324;
TIMSK1 |= B00000001; 2 }
2 1
1 3 Output Compare Vector
( Der er også en kanal B & C ) Compare
( Kun Kanal A Clearer timeren ) OCRnC 3 Bit 1 ISR(Timer1_COMPA_Vect)
Output Compare Register OCRnB {
}
OCR1B OCR1A TIFR1
Timer Compare Value
Eks: OCR1A = 15624; 16 Bit 2 Sæt OCF1A
16 Bit 1 bit Timer/Counter Interrupt
16 Bit 3 Compare Flag Register
Output Compare Interrupt
Timer Overflow Bit Flag Sæt TOV1 Bit Flag OCF1A og Overflov
flag TOV1 cleares af
Reset Counter hardware ved interrupt kald
TCNT1H TCNT1L
Timer/Counter 1H Timer/Counter 1L Clock-pulser, Timer0 bruges til delay(); millis(); & micros();
TCNT1 = 0; f = ( osc / prescale ) Timer1 til servo();
Preload:TCNT1 = 25324; Timer2 til tone();
Timer 0 & 2
er kun 8 bit
Oscillator Frekvensdeler
16 MHz = Prescaler
Compare værdien skal indstilles OCR1A = 15624; // OCR1A = 0x3D08; på hex form;
Vælg CTC Mode bitSet(TCCR1B, WGM12); // WGM12 = 3
Prescaler-værdien skal indstilles bitSet(TCCR1B, CS10); // = 00000001
bitSet(TCCR1B, CS12); //TCCR1B er nu 00000101
Timer interrupt skal enables bitSet(TIMSK1, OCIE1A);
Enabel Global Timer interrupt sei(); // CS12=2, CS11=0, CS10=1
Alle registre og register-bit har navne som compileren kender, og den kender deres værdier. Derfor
kan man fx skrive bitSet(TCCR1B, CS12); For definerede værdier: se
setBit(TCCR1B, WGM12); sætter et bit der vælger Timer Compare mode. Sættes denne ikke, er
der default valgt overflow mode.
WGM står for Waveform Generation Bit Mode ?
*1 Se datablad: http://www.atmel.com/Images/doc8161.pdf
Timerne kan arbejde i et større antal modes, derfor skal man vælge: CTC Mode ( Counter-Timer-
Compare ?? )
Notice how the setups between the three timers differ slightly in the line which turns on CTC mode:
This follows directly from the datasheet of the ATMEL 328/168 Kilde # .1
| bitwise OR
Bitmanipulation sker med disse instruktioner: & bitwise AND
~ bitwise NOT
Fra side om C-bitmanipulation. Se : ^ bitwise EXLUSIVE OR (XOR)
http://tom-itx.dyndns.org:81/~webpage/avr/c_bits/bits_index.php << bit LEFT SHIFT
>> bit RIGHT SHIFT
Ved man hvilke bit der skal sættes i et register, kan man gøre det med en af
følgende muligheder:
TCCR1A = 0b10101010;
TCCR1A = 0; // sæt hele TCCR1A register til 0
TCCR1B = 0;
TCCR1B = 0x03;
TCCR2A = B01000011; // her kendes hvilke bits, der skal sættes.
TCCR2B = B00001001;
TCCR2B |= 7; //Clock select. Internal, prescale 1024~64us
bitSet(TCCR1A, WGM01);
TCCR0A = _BV(WGM01); // Mode = CTC
TCCR0B = _BV(CS02) | _BV(CS00); // _BV er en function, der ???
1
Fra: (http://www.instructables.com/id/Arduino-Timer-Interrupts/step2/Structuring-Timer-Interrupts/ )
Datablad: http://www.atmel.com/Images/doc8161.pdf
http://www.atmel.com/images/atmel-8271-8-bit-avr-microcontroller-atmega48a-48pa-88a-88pa-168a-168pa-328-328p_datasheet_complete.pdf
Interrupt subrutiner
Eks:
void loop()
{
// your program here...
}
Timer overflow means the timer has reached is limit value. When a timer overflow interrupt occurs,
the timer overflow bit TOVx will be set in the interrupt flag register TIFRx. When the timer
overflow interrupt enable bit TOIEx in the interrupt mask register TIMSKx is set, the timer
overflow interrupt service routine ISR(TIMERx_OVF_vect) will be called.
Der kan også tilsluttes en ekstern klock-frekvens. Dvs. man kan tælle eksterne pulser.
/*
* 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(); // disable all interrupts
TCCR1A = 0;
TCCR1B = 0;
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);
// initialize timer1
noInterrupts(); // disable all interrupts
TCCR1A = 0;
TCCR1B = 0;
void loop()
{
/*
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(); // disable all interrupts
TCCR1A = 0;
TCCR1B = 0;
sekund++;
flag = HIGH;
if (sekund > 59) {
sekund = 0;
minut++;
}
}
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
When a output compare match interrupt occurs, the OCFxy flag will be set in the interrupt flag
register TIFRx . When the output compare interrupt enable bit OCIExy in the interrupt mask
register TIMSKx is set, the output compare match interrupt service ISR(TIMERx_COMPy_vect)
routine will be called.
Timerne kan udløse et interrupt hver gang, en timer når op til samme værdi, som er gemt i et timer-
match register, - eller hvis de når deres max count-værdi, og giver overflow.
Når en timer når et match,- bliver det resat på det næste klockpuls – og fortsætter så opad igen fra 0
til næste match.
Ved at vælge Compare match værdien, - og vælge klock-frekvensen ( 16 MHz ) sammen med en
frekvensdeler, en prescaler, kan man kontrollere interruptfrekvensen.
Timeren sætter ved overflow en overflow bit som evt. kan tjekkes manuelt af programmet.
#define ledPin 13
void setup()
{
pinMode(ledPin, OUTPUT);
// initialize timer1
noInterrupts(); // disable all interrupts
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
void loop()
{
// your program here...
}
#define LEDPIN 2
void setup()
{
pinMode(LEDPIN, OUTPUT);
// initialize Timer1
cli(); // disable global interrupts
TCCR1A = 0; // set entire TCCR1A register to 0
TCCR1B = 0; // same for TCCR1B
OCR1A = 15624; // set compare match register to desired timer count:
setBit(TCCR1B, WGM12); // turn on CTC mode:
setBit(TCCR1B, CS10); // Set CS10 and CS12 bits for 1024 prescaler:
setBit(TCCR1B, CS12);
setBit(TIMSK1, OCIE1A); // enable timer compare interrupt:
sei(); // enable global interrupts:
}
void loop()
{
// do some crazy stuff while my LED keeps blinking
}
ISR(TIMER1_COMPA_vect)
{
digitalWrite(LEDPIN, !digitalRead(LEDPIN));
}
Eksempel på, at tælle antal interrupts, for at få noget til at foregå langsommere!
ISR(TIMER1_COMPA_vect)
{
seconds++;
if (seconds == 10)
{
seconds = 0;
readMySensor();
}
}
void setup()
{
//Use timer 5, output A (pin 44) ( Ikke for Uno )
// initialize timer5
noInterrupts(); // disable all interrupts
TCCR5A = 0;
TCCR5B = 0;
TCNT5 = 0;
void loop()
{
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++;
if( ss > 59 )
{
mm++; // increment minutes
ss = 0; // reset seconds
}
if( mm > 59 )
{
hh++; // increment hours
mm = 0; // reset minutes
}
if( hh > 23 )
{
// increment day
hh = 0; // reset hours
}
}
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:
void setup ()
{
attachInterrupt (0, isr, CHANGE); // attach interrupt handler
} // end of setup
void loop ()
{
if (flag)
{
// interrupt has occurred
}
} // end of loop
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. )
I flere Kode-eksempler er der vist en metode til at indstille registre-bits til interrupt-styring ved at
skifte en kode til venstre.
Fx:
TCCR1B |= (1 << CS12); // 256 prescaler , skift et ‘1’-tal så mange pladser til
venstre, som værdien af CS12. |= betyder Bitwise or.)
Det kan fx bruges, hvis man ikke kender placeringen af bittene i timer kontrol-registrene. Men det
kræver til gengæld, at man ved, at bit CSn2 skal sættes.
I eksemplet skiftes et 1-tal til venstre et antal gange, angivet af værdien af CS12. Compileren
kender denne værdi!
Ved man hvilke bit der skal sættes i et register, kan man gøre det med en af følgende muligheder:
TCCR1A = 0b10101010;
TCCR1A = 0; // sæt hele TCCR1A register til 0
TCCR1B = 0;
TCCR1B = 0x03;
TCCR2A = B01000011; // her kendes hvilke bits, der skal sættes.
TCCR2B = B00001001;
TCCR2B |= 7; //Clock select. Internal, prescale 1024~64us
| bit OR
Bitmanipulation sker med disse instruktioner: & bit AND
~ bit NOT
^ bit EXLUSIVE OR (XOR)
<< bit LEFT SHIFT
>> bit RIGHT SHIFT
Interrupt subrutine:
Eksempel:
void setup(){
} //end setup
http://www.instructables.com/id/Arduino-Timer-Interrupts/
Interrupts(); noInterrupts();
*/
ISR (TIMER1_OVF_vect)
{
TCNT1 = 34286; // preload timer
count++;
} // end of TIMER1_OVF_vect
void setup ()
{
pinMode (13, OUTPUT);
} // 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
}
http://www.kner.at/home/40.avr/_architektur/ztcpwm.html
http://www.kner.at/home/40.avr/_architektur/ztcbint.html
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
trigger an interrupt when the timer overflows.
The Timer Interrupt Mask Register (TIMSK) and Timer Interrupt Flag (TIFR) Register are used to control which interrupts
are "valid" by setting their bits in TIMSK and to determine which interrupts are currently pending (TIFR).
Bit 7 Bit 0
TOIE1 OCIE1A --- --- TICIE1 --- TOIE0 ---
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 COM3B1 COM3B0 COM3C1 COM3C0 WGM11 WGM10
TCCR3B register
ICNC3 ICES3 – WGM13 WGM12 CS12 CS11 CS10
http://courses.cs.washington.edu/courses/csep567/10wi/lectures/Lecture7.pdf
http://www.avrbeginners.net/architecture/timers/timers.html
16 bit timere er
lidt mere
complex.
http://www.avrbeginners.net/architecture/timers/timers.html
http://www.avrbeginners.net/architecture/timers/timers.html
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/
//timer interrupts
//by Amanda Ghassaei
//June 2012
//http://www.instructables.com/id/Arduino-Timer-Interrupts/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
*/
//storage variables
boolean toggle0 = 0;
boolean toggle1 = 0;
boolean toggle2 = 0;
void setup(){
TCCR0B |= (1 << CS01) | (1 << CS00); // Set CS01 and CS00 bits for 64
// prescaler
sei();//allow interrupts
}//end setup
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
//bike speedometer
//by Amanda Ghassaei 2012
//http://www.instructables.com/id/Arduino-Timer-Interrupts/
//http://www.instructables.com/id/Arduino-Timer-Interrupts/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
*/
//sample calculations
//tire radius ~ 13.5 inches
//circumference = pi*2*r =~85 inches
//max speed of 35mph =~ 616inches/second
//max rps =~7.25
//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
sei();//allow interrupts
//END TIMER SETUP
Serial.begin(9600);
}
void checkBacklight(){
backlight = digitalRead(2);
if (backlight){
Serial.write(17);//turn backlight on
}
else{
Serial.write(18);//turn backlight off
}
}
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
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.
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 program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
*/
//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;
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
sei();//allow interrupts
debounce state.
buttonState[row] |= (1 << index);
}
else{
buttonState[row] &= ~(1 << index);
}
}
}
}
void shift(){
for (i=0;i<4;i++){
buttonLast[i] = buttonCurrent[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
void loop() {
shift();//updates leds and receives data from buttons
}
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.
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
*/
void setup()
{
Serial.begin(9600);
void loop()
{
// check for button press
buttonState = digitalRead(buttonPin); // read the
button state and store
else{
if (blinking == true){
previousMillis = millis(); // remember
the last time we blinked the LED
circumference = 2πr
where r is the radius of the circle. Now that you have the wheel circumference, this value can be considered as our
‘fixed distance’, and therefore the speed can be calculated by measuring the elapsed time between of a full
rotation.
Your sensor – once fitted – should act in the same method as a normally-open button that is pushed every
rotation. Our sketch will measure the time elapsed between every pulse from the sensor. To do this, our example
will have the sensor output connected to digital pin 2 – as it will trigger an interrupt to calculate the speed.
(Interrupts? See chapter three). The sketch will otherwise be displaying the speed on a normal I2C-interface
LCD module. The I2C interface is suggested as this requires only 4 wires from the Arduino board to the LCD –
Example 37.3
/*
Example 37.3 – Basic speedometer using millis();
http://tronixstuff.wordpress.com/tutorials > chapter 37
John Boxall | CC by-sa-nc
*/
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));
There isn’t 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 curiosity’s sake.
In real life I don’t 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 (pin D2) (INT0_vect)
3 External Interrupt Request 1 (pin D3) (INT1_vect)
4 Pin Change Interrupt Request 0 (pins D8 to D13) (PCINT0_vect)
5 Pin Change Interrupt Request 1 (pins A0 to A5) (PCINT1_vect)
6 Pin Change Interrupt Request 2 (pins D0 to D7) (PCINT2_vect)
7 Watchdog Time-out Interrupt (WDT_vect)
8 Timer/Counter2 Compare Match A (TIMER2_COMPA_vect)
9 Timer/Counter2 Compare Match B (TIMER2_COMPB_vect)
10 Timer/Counter2 Overflow (TIMER2_OVF_vect)
11 Timer/Counter1 Capture Event (TIMER1_CAPT_vect)
12 Timer/Counter1 Compare Match A (TIMER1_COMPA_vect)
13 Timer/Counter1 Compare Match B (TIMER1_COMPB_vect)
14 Timer/Counter1 Overflow (TIMER1_OVF_vect)
15 Timer/Counter0 Compare Match A (TIMER0_COMPA_vect)
16 Timer/Counter0 Compare Match B (TIMER0_COMPB_vect)
17 Timer/Counter0 Overflow (TIMER0_OVF_vect)
18 SPI Serial Transfer Complete (SPI_STC_vect)
19 USART Rx Complete (USART_RX_vect)
20 USART, Data Register Empty (USART_UDRE_vect)
21 USART, Tx Complete (USART_TX_vect)
Fra: http://www.gammon.com.au/forum/?id=11488
#include <avr/io.h>
#include <avr/interrupt.h>
int ledPin = 8;
volatile int compAval = 10;
void setup() {
Serial.begin(9600);
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
sei(); //Enable interrupts
TCCR2A |= 3; //Fast PWM mode 3.
TCCR2A |= (3 << 6); //fire INT on Compare match
TCNT2 = 0; //initialize Timer2 to 0
OCR2A = compAval; //Set compare A value
TIMSK2 |= (1 << 1); //Enable CompA interrupt
TIMSK2 |= 1; //Enable OVF interrupt
TCCR2B |= 7; //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);
}
http://courses.cs.washington.edu/courses/csep567/10wi/lectures/Lecture6.pdf
TCCR1B:
Bit 7 Bit 0
ICNC1 ICES1 --- --- CTC1 CS12 CS11 CS10
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.
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
en samling links til Jeremy Bloms videos:
Se: http://harperjiangnew.blogspot.dk/2013/05/arduino-using-atmegas-internal.html
http://tom-itx.dyndns.org:81/~webpage/abcminiuser/articles/avr_timers_index.php
http://www.youtube.com/watch?v=CRJUdf5TTQQ
http://www.uchobby.com/index.php/2007/11/24/arduino-interrupts/
http://www.protostack.com/blog/2010/09/timer-interrupts-on-an-atmega168/
http://www.instructables.com/id/Arduino-Timer-Interrupts/step2/Structuring-Timer-Interrupts/
http://www.instructables.com/id/Arduino-Timer-Interrupts/
http://www.engblaze.com/microcontroller-tutorial-avr-and-arduino-timer-interrupts/
http://arduino-info.wikispaces.com/Timers-Arduino
Links:
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
http://www.let-elektronik.dk/tutorials
en samling links til Jeremy Bloms videos:
http://harperjiangnew.blogspot.dk/2013/05/arduino-using-atmegas-internal.html
http://tom-itx.dyndns.org:81/~webpage/abcminiuser/articles/avr_timers_index.php
http://www.youtube.com/watch?v=CRJUdf5TTQQ