Arduino interrupts Version 5/11-2014 Arduino Timer interrupts. Hvordan får man Arduino til fx at udføre et interrupt og køre en programstump fx 100 gange i sekundet. Interrupts kan udløses på flere forskellige måder. Fx via et signal på en Input-pin, - eller udløst af en af de indbyggede timere. Interrupts er programstumper, der udløses af en hændelse og kører asynkront, ikke i et loop. De udløses fx af et key-tryk, - eller af en tid gået, - timer-overflow, osv. Interrupts udløses – og udføres et vilkårligt sted i et kørende program – og uafhængig heraf. Fx kan et program køre – og samtidig kan der af en timer udløses et interrupt, der blinker en LED. 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. Det skal fortælle hvordan og fra hvad, der skal udløses interrupts. Interrupts skal enables og den tilhørende interrupt-maske skal enables. / Valle Thorø Side 1 af 40 Arduino interrupts Version 5/11-2014 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 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. Der findes to generelt forskellige interrupts: Pinudløste, - og timer/counter udløste interrupts. Pin-udløste interrupts. Pinudløste interrupts kan sættes op til at ske ved en ændring på Arduino-pin 2 eller 3. Hhv. INT0 & INT1. Der kan vælges rising eller falling edge. Eller pinchange Se: http://www.engblaze.com/we-interrupt-this-program-to-bring-you-a-tutorial-on-arduino-interrupts/ Arduino-funktionerne attachInterrupt() og detachInterrupt() bruges kun til pin-udløste interrupts. Kodeeksempler: void setup(void) { pinMode(2, INPUT); pinMode(13, OUTPUT); digitalWrite(2, HIGH); sei(); EIMSK |= (1 << INT0); EICRA |= (1 << ISC01); } // // // // Enable pullup resistor Enable global interrupts Enable external interrupt INT0 Trigger INT0 on falling edge void loop(void) { } / Valle Thorø Side 2 af 40 Arduino interrupts ISR(EXT_INT0_vect) Version 5/11-2014 // // Interrupt Service Routine attached to INT0 vector { digitalWrite(13, !digitalRead(13)); // Toggle LED on pin 13 } http://www.engblaze.com/we-interrupt-this-program-to-bring-you-a-tutorial-on-arduino-interrupts/ Et andet eksempel: 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); } // Interrupt service routine http://www.geertlangereis.nl/Electronics/Pin_Change_Interrupts/PinChange_en.html Men det er muligt, at få flere pins til at udføre interrupts. Se http://www.geertlangereis.nl/Electronics/Pin_Change_Interrupts/PinChange_en.html Arduino Interrupts int pbIn = 0; / Valle Thorø // Interrupt 0 is on DIGITAL PIN 2! Side 3 af 40 Arduino interrupts int ledOut = 4; volatile int state = LOW; Version 5/11-2014 // The output LED pin // The input state toggle 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() { //Simulate a long running process or complex task for (int i = 0; i < 100; i++) { // do nothing but waste some time delay(10); } } 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-outputbasics&Itemid=107 / Valle Thorø Side 4 af 40 Arduino interrupts Version 5/11-2014 Timerudløste interrupts Vha. timere 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 af en timer, - men længere varigheder skal udløses vha. at oprette en variabel, tæller et antal interrupts. Fx incrementes en tæller 1 gang pr sekund, og når den fx når 60, kan man fx læse en sensor. En timer/counter kan arbejde på to forskellige måder, når en tid skal udmåles. 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 Compareregister. Timer Overflow Timer Compare Match Timerens startværdi indstilles, og når timeren giver overflow, kan der udløses et interrupt. Der indsættes en værdi i et Compare-register. Timerens værdi sammenlignes med Compareregisteret, og ved match resettes timeren, og der kan udløses et interrupt. Indbyggede timere Arduino Uno har 3 timere indbygget, timer0, timer1 og timer2. Timer 0: 8 bit Timer 1: 16 bit. Timer 2: 8 bit AT Mega 2560 har yderligere timer 3, 4 og 5, som er 16 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. / Valle Thorø Side 5 af 40 Arduino interrupts Version 5/11-2014 Prescaleren styres af nogle bit i to SFR-registre, TimerCounterControlRegister, TCCRnA eller B Bittene hedder: CSn0, CSn1 og CSn2, hvor tallet n er timerens nummer. CS står for Clock Select bit. http://courses.cs.washington.edu/courses/csep567/10wi/lectures/Lecture7.pdf Der er 2 8-bit setupregistre. TCCRxA og TCCRxB hvor x er timer-nummeret. TCCR står for Timer/Counter Control Register Se fx http://forum.arduino.cc/index.php/topic,27390.0.html CS12 CS11 CS10 Mode Description 0 0 0 Stop Timer/Counter 1 0 0 1 No Prescaler (Timer Clock = System Clock) 0 1 0 divide clock by 8 0 1 1 divide clock by 64 1 0 0 divide clock by 256 1 0 1 divide clock by 1024 1 1 0 increment timer 1 on T1 Pin falling edge 1 1 1 increment timer 1 on T1 Pin rising edge Prescaler- styre-bit er vist her for timer 1: http://www.avrbeginners.net/architecture/timers/timers.html / Valle Thorø Side 6 af 40 Arduino interrupts Version 5/11-2014 Hjælp til valg af prescaler til forskellige interruptfrekvenser, se fx calculator på: http://www.et06.dk/atmega_timers/ Default er disse bit sat til 0. Prescaler kan vælges til 1, 8, 64, 256, 1024. ( 1024 kan ikke laves på timer 2 ) 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 precaler til et bestemt formål. Se fx: http://www.et06.dk/atmega_timers/ Skal forbedres! Udregningen sker som flg: (timer speed (Hz)) = (Arduino clock speed (16MHz)) / prescaler 𝑖𝑛𝑡𝑒𝑟𝑟𝑢𝑝𝑡 𝑓𝑟𝑒𝑘𝑣𝑒𝑛𝑠 = 16 𝑀𝐻𝑧 𝑃𝑟𝑒𝑠𝑐𝑎𝑙𝑒𝑟 ∙ ((𝐶. 𝑀𝑎𝑡𝑐ℎ 𝑟𝑒𝑔) + 1) ( + 1 fordi det tager 1 clock cycle at resette. / Valle Thorø Side 7 af 40 Arduino interrupts Version 5/11-2014 Ligningen løst for Compare match register, CMR: 16.000.000 𝐶𝑀𝑅 = ( )−1 𝑃𝑟𝑒𝑠𝑐𝑎𝑙𝑒𝑟 ∙ 𝑖𝑛𝑡𝑒𝑟𝑟𝑢𝑝𝑡 𝑓𝑟𝑒𝑘𝑣. Værdien skal være mindre end 256 for timer 0 og 2, og mindre end 65.536 for Timer 1 for at det kan lade sig gøre: Eksempel: Clock select and timer frequency Different clock sources can be selected for each timer independently. To calculate the timer frequency (for example 2Hz using timer1) you will need: 1. 2. 3. 4. CPU frequency 16Mhz for Arduino maximum timer counter value (256 for 8bit, 65536 for 16bit timer) Divide CPU frequency through the choosen prescaler (16000000 / 256 = 62500) Divide result through the desired frequency (62500 / 2Hz = 31250) Verify the result against the maximum timer counter value (31250 < 65536 success) if fail, choose bigger prescaler. 5. Eksempel på opsætning til 1 HZ interrupt: Interrupt every second (frequency of 1Hz): compare match register = [16,000,000 / (prescaler * 1) ] -1 with a prescaler of 1024 you get: compare match register = [16,000,000 / (1024 * 1) ] -1 = 15,624 since 256 < 15,624 < 65,536, you must use timer1 for this interrupt. Timer setup code is done inside the setup(){} function in an Arduino sketch. Control registrere til timerne mm. For at man kan styre timerne er der en del kontrol-registre, hvis bit – eller indhold skal bestemmes. Hver timer har et antal registre tilknyttet. TCCRx - Timer/Counter Control Register. The pre-scaler can be configured here. TCNTx - Timer/Counter Register. The actual timer value is stored here. OCRx - Output Compare Register / Valle Thorø Side 8 af 40 Arduino interrupts Version 5/11-2014 TIMSKx - Timer/Counter Interrupt Mask Register. To enable/disable timer interrupts. TIFRx - Timer/Counter Interrupt Flag Register. Indicates a pending timer interrupt. Følgende diagram er et forsøg på at lave et samlet diagram over en timer og interrupt-struktur Arduino Timer1 Interrupt-Opsætning ATMEL AVR ATMEGA328 CTC Mode Interrupt ( Clear Timer on Compare Match ) Enable Interrupt i reg: Enable / Disable Global Interrupt Timer Interrupt Mask register interrupts (); noInterrupts (); TIMSK1-Bit: [xxxx x, OCIE1B, OCIE1A, TOIE1] Sei(); Cli();Disable Compare_Match: TIMSK1 = B00000010; Brug kun i Setup() ?? TIMSK1 TIMSK1 |= ( 1 << OCIE1A ); Overflow bit, Bit 0 bitSet(TCCR1B, OCIE1A); Bit OCIE1A Bit OCIE1A TIMSK1 = B00000010; TIMSK1 = B00000001; 2 bitSet(TCCR1B, TOIE1); 2 1 ( Der er også en kanal B & C ) Vector 1 3 ( Kun Kanal A Clearer timeren ) 3 Eks: OCR1A = 15624; OCRnC Timer/Counter Interrupt ISR(Timer1_COMPA_Vect) Flag Register { Output Compare Register OCRnB } Output Compare Interrupt OCR1A TIFR1 Flag OCF1A og Overflov flag TOV1 cleares af Timer Compare Value ISR(TIMER1_OVF_vect) hardware ved interrupt kald 2 16 Bit { Sæt OCF1A 1 bit } 16 Bit 3 16 Bit Compare Sæt TOV1 Bit Timer Overflow Bit Flag TCNT1H TCNT1L Timer/Counter 1H Timer/Counter 1L TCNT1 = 0; TCNT1 = 25324; Oscillator 16 MHz Frekvensdeler = Prescaler Reset Counter Clock-pulser, f = ( osc / prescale ) Timer0 bruges til delay(); millis(); & micros(); Timer1 til servo(); Timer2 til tone(); Timer 0 & 2 er kun 8 bit [ CSxx = Clock Select bit ] TCCR1B-Bit[xxxx WGM12, CS12, CS11, CS10 ] TCCR1B |= B*****xxx; TCCR1B |= ( 1 << CS12 ) | ( 1 << CS10 );// = 1024 TCCR1B |= 0x05; // = 1024 bitSet(TCCR1B, CS10); bitSet(TCCR1B, CS12); Pins ( Bit 7 - 0 ) DDRB Data Direction Reg. DDRB |= B00001111; ( 1 = output ) PORTB Port Reg PORTB = B00001111; Fra Pins TCCR1A TCCR1B Timer/Counter Control Register A/B Mode select registre [CS12, CS11, CS10] 000 Stop Timer 001 Divide by 1 010 8 011 64 100 256 101 1024 11* Ekstern clock på T1 CS10 = 0 CS11 = 1 CS12 = 2 TCCR1B |= ( 1 << WGM12 ); Turn on Compare ( CTC ) Mode( Wave Generation Mode ) TCCR1B |= B00001000; bitSet(TCCR1B, WGM12); Default er Counter Overflow Mode Valgt ( Normal mode ) / Valle Sept 2013 Rev: 02/04-2014 Tegningen viser de forskellige registre involveret i opsætningen, og der er givet forskellige eksempler på hvordan man kan manipulere med bittene i registrene: Her er en kort oversigt over, hvad der skal opsættes: Hvad skal gøres? Kode-eksempel Disable global interrupt cli(); Nulstil tæller TCNT1 = 0; Compare værdien skal indstilles OCR1A = 15624; * Vælg CTC Mode TCCR1B |= (1 << WGM12); ** Prescaler-værdien skal indstilles TCCR1B |= (1 << CS10); TCCR1B |= (1 << CS12); / Valle Thorø Side 9 af 40 Arduino interrupts Version 5/11-2014 Timer interrupt skal enables TIMSK1 |= (1 << OCIE1A); Enabel Global Timer interrupt sei(); * Timerne kan arbejde i et større antal modes, derfor skal man vælge: CTC Mode ( Counter-TimerCompare ?? ) ** Notice how the setups between the three timers differ slightly in the line which turns on CTC mode: TCCR0A |= (1 << WGM01); TCCR1B |= (1 << WGM12); TCCR2A |= (1 << WGM21); //for timer0 //for timer1 //for timer2 This follows directly from the datasheet of the ATMEL 328/168 Kilde # .1 Timer Mode select: Waveform Generation Bit setup: Timerne kan tillige indstilles til at hav af forskellige funktioner. Det styres af ” Waveform generation bits” også i reg. TCCR1A og TCCR1B. Det eneste bit der har interesse her er WGM12, som vælger CTC mode. Interrupt subrutiner Her er vist, hvordan man skriver en interrupt-subrutine, en Interrupt Service Rutine. Eks: ISR(TIMER1_COMPA_vect) { seconds++; 1 // Compare match Fra: (http://www.instructables.com/id/Arduino-Timer-Interrupts/step2/Structuring-Timer-Interrupts/ ) Datablad: / Valle Thorø http://www.atmel.com/Images/doc8161.pdf Side 10 af 40 Arduino interrupts Version 5/11-2014 if (seconds == 10) { seconds = 0; readMySensor(); } } ISR(TIMER1_OVF_vect) { TCNT1 = timer1_counter; // Overflow interrupt service routine // preload timer med værdi, fx med 23445 digitalWrite(ledPin, digitalRead(ledPin) ^ 1); } void loop() { // your program here... } Timer Overflow eksempler: 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. I denne mode presettes timerne til en startværdi, - og overflow udløser så et interrupt. Der kan også tilsluttes en ekstern klock-frekvens. Dvs. man kan tælle eksterne pulser. Men de fleste gange bliver den interne oscillator-frekvens brugt. /* * 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() / Valle Thorø Side 11 af 40 Arduino interrupts Version 5/11-2014 { pinMode(ledPin, OUTPUT); // initialize timer1 noInterrupts(); TCCR1A = 0; TCCR1B = 0; TCNT1 = 34286; TCCR1B |= (1 << CS12); TIMSK1 |= (1 << TOIE1); interrupts(); // disable all interrupts // // // // preload timer 65536-16MHz/256/2Hz 256 prescaler enable timer overflow interrupt enable all interrupts } 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 // Set timer1_counter to the correct value for our interrupt interval //timer1_counter = 64886; // preload timer 65536-16MHz/256/100Hz //timer1_counter = 64286; // preload timer 65536-16MHz/256/50Hz timer1_counter = 34286; // preload timer 65536-16MHz/256/2Hz TCNT1 = timer1_counter; TCCR1B |= (1 << CS12); TIMSK1 |= (1 << TOIE1); / Valle Thorø // preload timer // 256 prescaler ( |= betyder Bitwise or. ) // enable timer overflow interrupt Side 12 af 40 Arduino interrupts interrupts(); Version 5/11-2014 // enable all interrupts } ISR(TIMER1_OVF_vect) // interrupt service routine { TCNT1 = timer1_counter; // preload timer digitalWrite(ledPin, digitalRead(ledPin) ^ 1); } void loop() { // your program here... } Output Compare Match eksempler: 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 timermatch 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. ISR-en ( Interrupt Service Routine resetter overflowbit. /* Arduino 101: timer and interrupts Timer1 compare match interrupt example more infos: http://www.letmakerobots.com/node/28278 created by RobotFreak */ #define ledPin 13 void setup() { / Valle Thorø Side 13 af 40 Arduino interrupts Version 5/11-2014 pinMode(ledPin, OUTPUT); // initialize timer1 noInterrupts(); TCCR1A = 0; TCCR1B = 0; TCNT1 = 0; OCR1A = 31250; TCCR1B |= (1 << WGM12); TCCR1B |= (1 << CS12); TIMSK1 |= (1 << OCIE1A); interrupts(); // disable all interrupts // // // // // compare match register 16MHz/256/2Hz CTC mode 256 prescaler enable timer compare interrupt enable all interrupts } ISR(TIMER1_COMPA_vect) // timer compare interrupt service routine { digitalWrite(ledPin, digitalRead(ledPin) ^ 1); // toggle LED pin } void loop() { // your program here... } // Arduino timer CTC interrupt example #define LEDPIN 2 void setup() { pinMode(LEDPIN, OUTPUT); cli(); TCCR1A = 0; TCCR1B = 0; OCR1A = 15624; TCCR1B |= (1 << TCCR1B |= (1 << TCCR1B |= (1 << TIMSK1 |= (1 << sei(); // initialize Timer1 // disable global interrupts // set entire TCCR1A register to 0 // same for TCCR1B // set compare match register to desired timer count: WGM12); // turn on CTC mode: CS10); // Set CS10 and CS12 bits for 1024 prescaler: CS12); OCIE1A); // enable timer compare interrupt: // enable global interrupts: } void loop() / Valle Thorø Side 14 af 40 Arduino interrupts Version 5/11-2014 { // 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 // initialize timer5 noInterrupts(); TCCR5A = 0; TCCR5B = 0; TCNT5 = 0; (pin 44) // disable all interrupts OCR5A = 833; // compare match register 16MHz/256/75Hz ~75.03Hz TCCR5B |= (1 << WGM12); // CTC mode TCCR5B |= (1 << CS12); // 256 prescaler TIMSK5 |= (1 << OCIE5A); // enable timer compare interrupt mask interrupts(); // enable all interrupts } void loop() { } ISR(TIMER5_COMPA_vect) // timer compare ISR { Serial.print(" et eller andet"); //print RTI fire time / Valle Thorø Side 15 af 40 Arduino interrupts Version 5/11-2014 } // // Denne interrupt kaldes ved 1kHz http://petemills.blogspot.dk/2011/05/real-time-clock-part-1.html 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( milliseconds >= MS_IN_SEC ) { milliseconds = 0; ss++; // increment seconds toggle_led(); // toggle led 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 } } // // // // milliseconds must be less than 999 to avoid missing an adjustment. eg if milliseconds were to be 999 and we increment it here to 1000 the next ISR call will make it 1001 and reset to zero just as if it would for 1000 and the adjustment would be effectively canceled out. / Valle Thorø Side 16 af 40 Arduino interrupts Version 5/11-2014 if( ( clock_cal_counter >= MS_ADJ ) && ( milliseconds < MS_IN_SEC - 1 ) ) { milliseconds++; // it may be that clock_cal_counter is > than MS_ADJ in which case // I want to count the tick towards the next adjustment // should always be 1 or 0 clock_cal_counter = clock_cal_counter - MS_ADJ; } } What are "volatile" variables? 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 // attach interrupt handler void loop () { if (flag) { // interrupt has occurred } } // end of loop / Valle Thorø Side 17 af 40 Arduino interrupts Version 5/11-2014 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 måder, hvorpå man kan få udløst et interrupt på tid: Kode til at sætte værdier i kontrolregistre: 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! Prædefinerede værdier, 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 sættes i et register, kan man gøre det med en af følgende muligheder: TCCR1A = 0b10101010; TCCR1A = 0; TCCR1B = 0; / Valle Thorø // sæt hele TCCR1A register til 0 Side 18 af 40 Arduino interrupts TCCR1B TCCR2A TCCR2B TCCR2B = 0x03; = B01000011; = B00001001; |= 7; Version 5/11-2014 // her kendes hvilke bits, der skal sættes. //Clock select. Internal, prescale 1024~64us TCCR0A = _BV(WGM01); TCCR0B = _BV(CS02) | _BV(CS00); // Mode = CTC // _BV er en function, der ??? /* _BV(x) er en funktion, der skifter et 1-tal x pladser til venstre. Det betyder, at fx _BV(6) returnerer 0b01000000 */ Bitmanipulation sker med disse instruktioner: Fra side om C-bitmanipulation: | 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 følgende: ISR(Timerx_ COMPA_VECT) { Do something } Eksempel: Arduino timer compare match interrupts: Eksempler på opsætning af Prescaler: void setup(){ cli(); //stop interrupts //set timer0 interrupt at 2kHz TCCR0A = 0; // set entire TCCR0A register to 0 TCCR0B = 0; // same for TCCR0B / Valle Thorø Side 19 af 40 Arduino interrupts Version 5/11-2014 TCNT0 = 0; OCR0A = 124; //initialize counter value to 0 // set compare match register for 2khz // increments // = (16*10^6) / (2000*64) - 1 (must be <256) TCCR0A |= (1 << WGM01); // turn on CTC mode TCCR0B |= (1 << CS01) | (1 << CS00); // Set CS01 and CS00 bits for 64 presc. TIMSK0 |= (1 << OCIE0A); // enable timer compare interrupt //set timer1 interrupt at 1Hz TCCR1A = 0; TCCR1B = 0; TCNT1 = 0; OCR1A = 15624; // set entire TCCR1A register // same for TCCR1B //initialize counter value to // set compare match register // increments // = (16*10^6) / (1*1024) - 1 to 0 0 for 1hz (must be <65536) TCCR1B |= (1 << WGM12); // turn on CTC mode TCCR1B |= (1 << CS12) | (1 << CS10); // Set CS10 og CS12 bits for 1024 presc. TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt //set timer2 interrupt TCCR2A = 0; TCCR2B = 0; TCNT2 = 0; OCR2A = 249; at 8kHz // set entire TCCR2A register // same for TCCR2B //initialize counter value to // set compare match register // increments // = (16*10^6) / (8000*8) - 1 TCCR2A |= (1 << WGM21); TCCR2B |= (1 << CS21); TIMSK2 |= (1 << OCIE2A); 0 for 8khz (must be <256) // turn on CTC mode // Set CS21 bit for 8 prescaler // enable timer compare interrupt sei(); //allow interrupts } //end setup ISR(TIMER0_COMPA_vect){ to 0 //change the 0 to 1 for timer1 and 2 for timer2 //interrupt commands here } http://www.instructables.com/id/Arduino-Timer-Interrupts/ Resten er endnu ikke helt gennemarbejdet, kan betragtes som Bonus Info / Valle Thorø Side 20 af 40 Arduino interrupts Version 5/11-2014 TIMSK1. This is the Timer/Counter1 Interrupt Mask Register. Setting the TOIE1 bit tells the timer to trigger an interrupt when the timer overflows. TIMSK and TIFR 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 TOIE1 OCIE1A --- --- TICIE1 --- TOIE0 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 / Valle Thorø COM3B1 – COM3B0 COM3C1 COM3C0 WGM11 WGM10 WGM13 WGM12 CS12 CS11 CS10 Side 21 af 40 Arduino interrupts Version 5/11-2014 http://courses.cs.washington.edu/courses/csep567/10wi/lectures/Lecture7.pdf based on the AT90S2313. http://www.avrbeginners.net/architecture/timers/timers.html 16 bit timere er lidt mere complex. / Valle Thorø Side 22 af 40 Arduino interrupts Version 5/11-2014 Timer overflow og interrupt udløses : http://www.avrbeginners.net/architecture/timers/timers.html Output Compare Mode The Output Compare mode is used to perform repeated timing. The value in TCNT1 (which is counting up if not stopped by the prescaler select bits) is permanently compared to the value in OCR1A. When these values are equal to each other, the Output Compare Interrupt Flag (OCF in TIFR) is set and an ISR can be called. By setting the CTC1 bit in TCCR1B, the timer can be automatically cleared upon compare match. http://www.avrbeginners.net/architecture/timers/timers.html / Valle Thorø Side 23 af 40 Arduino interrupts Version 5/11-2014 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/ Example- the following sketch sets up and executes 3 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. //timer setup for timer0, timer1, and timer2. //For arduino uno or any board with ATMEL 328/168.. diecimila, duemilanove, lilypad, nano, mini... //this code will enable //timer0 will interrupt //timer1 will interrupt //timer2 will interrupt all three arduino timer interrupts. at 2kHz at 1Hz at 8kHz //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ø //set pins as outputs //stop interrupts Side 24 af 40 Arduino interrupts Version 5/11-2014 //set timer0 interrupt at 2kHz TCCR0A = 0; TCCR0B = 0; TCNT0 = 0; OCR0A = 124; // set entire TCCR2A register to 0 // same for TCCR2B //initialize counter value to 0 // set compare match register for 2khz // increments // = (16*10^6) / (2000*64) - 1 (must be <256) TCCR0A |= (1 << WGM01); // turn on CTC mode TCCR0B |= (1 << CS01) | (1 << CS00); TIMSK0 |= (1 << OCIE0A); // Set CS01 and CS00 bits for 64 // prescaler // enable timer compare interrupt //set timer1 interrupt at 1Hz TCCR1A = 0; TCCR1B = 0; TCNT1 = 0; // set entire TCCR1A register to 0 // same for TCCR1B //initialize counter value to 0 // set compare match register for 1hz // increments OCR1A = 15624; // = (16*10^6) / (1*1024) - 1 (must be <65536) TCCR1B |= (1 << WGM12); // turn on CTC mode TCCR1B |= (1 << CS12) | (1 << CS10); // Set CS12 and CS10 bits for 1024 //prescaler TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt //set timer2 interrupt at 8kHz TCCR2A = 0; TCCR2B = 0; TCNT2 = 0; OCR2A = 249; TCCR2A |= (1 << WGM21); TCCR2B |= (1 << CS21); TIMSK2 |= (1 << OCIE2A); // set entire TCCR2A register to 0 // same for TCCR2B //initialize counter value to 0 // set compare match register for 8khz // increments // = (16*10^6) / (8000*8) - 1 (must be <256) // turn on CTC mode // Set CS21 bit for 8 prescaler // enable timer compare interrupt sei();//allow interrupts }//end setup ISR(TIMER0_COMPA_vect){ //timer0 interrupt 2kHz toggles pin 8 //generates pulse wave of frequency 2kHz/2 = //1kHz (takes two cycles for full wave- toggle // high then toggle low) if (toggle0){ digitalWrite(8,HIGH); toggle0 = 0; } else{ digitalWrite(8,LOW); toggle0 = 1; } } ISR(TIMER1_COMPA_vect){ / Valle Thorø //timer1 interrupt 1Hz toggles pin 13 (LED) Side 25 af 40 Arduino interrupts Version 5/11-2014 //generates pulse wave of frequency 1Hz/2 = //0.5kHz (takes two cycles for full wave//toggle high then toggle low) if (toggle1){ digitalWrite(13,HIGH); toggle1 = 0; } else{ digitalWrite(13,LOW); toggle1 = 1; } } ISR(TIMER2_COMPA_vect){ //timer1 interrupt 8kHz toggles pin 9 //generates pulse wave of frequency 8kHz/2 = //4kHz (takes two cycles for full wave- toggle // high then toggle low) 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 26 af 40 Arduino interrupts Version 5/11-2014 //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 #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 27 af 40 Arduino interrupts Version 5/11-2014 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 28 af 40 Arduino interrupts Version 5/11-2014 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 Here's the complete Arduino sketch: //BUTTON TEST w/ 74HC595 and 74HC165 and serial communication //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 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; //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 29 af 40 Arduino interrupts Version 5/11-2014 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; // queue up a button state change event if (buttonCurrent[row] & (1 << index)){ / Valle Thorø // and toggle the buttons Side 30 af 40 Arduino interrupts Version 5/11-2014 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 31 af 40 Arduino interrupts Version 5/11-2014 while (Serial.available() > 8); } 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. You can read more about this on the Arduino website. / Valle Thorø Side 32 af 40 Arduino interrupts Version 5/11-2014 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 // LED connected to digital pin 13 // button on pin 4 int value = LOW; int buttonState; int lastButtonState; state int blinking; timing long interval = 100; long previousMillis = 0; was updated long startTime ; long elapsedTime ; int fractional; part of time // previous value of the LED // variable to store button state // variable to store last button // condition for blinking - timer is // blink interval - change to suit // variable to store last time LED // start time for stop watch // elapsed time for stop watch // variable used to store fractional void setup() { Serial.begin(9600); pinMode(ledPin, OUTPUT); // sets the digital pin as 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 33 af 40 Arduino interrupts Version 5/11-2014 if (buttonState == LOW && lastButtonState == HIGH && blinking== fals // check for a high to low transition // if true then found a new button press while clock is not running - start the clock e){ 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 // routine to report elapsed time Serial.print( (int)(elapsedTime / 1000L)); 1000 to convert to seconds - then cast to an int to print Serial.print("."); // 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); // print fractional part of time } else{ / Valle Thorø Side 34 af 40 Arduino interrupts lastButtonState = buttonState; buttonState in lastButtonState, to compare next time } // // // than // Version 5/11-2014 // store blink routine - blink the LED while timing check to see if it's time to blink the LED; that is, the difference between the current time and last time we blinked the LED is larger the interval at which we want to blink the LED. if ( (millis() - previousMillis > interval) ) { if (blinking == true){ previousMillis = millis(); the last time we blinked the LED // remember // if the LED is off turn it on and vice-versa. if (value == LOW) value = HIGH; else value = LOW; digitalWrite(ledPin, value); } else{ digitalWrite(ledPin, LOW); LED when not blinking } } // turn off } Brug af milis: cykelcomputer: Interrupt Triggers hver puls fra føler: To calculate the circumference – use the formula: 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 / Valle Thorø Side 35 af 40 Arduino interrupts Version 5/11-2014 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 36 af 40 Arduino interrupts Version 5/11-2014 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 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 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 37 af 40 Arduino interrupts 22 23 24 25 26 ADC Conversion Complete EEPROM Ready Analog Comparator 2-wire Serial Interface (I2C) Store Program Memory Ready Version 5/11-2014 (ADC_vect) (EE_READY_vect) (ANALOG_COMP_vect) (TWI_vect) (SPM_READY_vect) Fra: http://www.gammon.com.au/forum/?id=11488 God og stor samling Tutorials: http://tronixstuff.com/tutorials/ #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(); TCCR2A |= 3; TCCR2A |= (3 << 6); TCNT2 = 0; OCR2A = compAval; TIMSK2 |= (1 << 1); TIMSK2 |= 1; TCCR2B |= 7; } //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 38 af 40 Arduino interrupts Version 5/11-2014 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 39 af 40 Arduino interrupts Version 5/11-2014 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 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 CTC mode: http://maxembedded.com/2011/06/28/avr-timers-timer1/ 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 / Valle Thorø Side 40 af 40
© Copyright 2024