1 Bruk av interrupt og Timer i Arduino-program. Når vi skal utføre handlinger som kan inntreffe tilfeldig (ikke forutsigbare hendelser), slik som å håndtere alarmer, at IO ønsker service etc kan vi benytte Polling som et prinsipp eller vi kan mer effektivt benytte Interrupt. Hva er Polling? Polling betyr at CPU en sjekker ved jevne mellomrom om noen trenger å bli betjent. CPU en bruker følgelig masse tid på å spørre enhetene om de trenger oppmerksomhet. Noe som de normalt ikke trenger. Bortkastet tid. Polling er enkelt å programmere, da det kun består i å «spørre» enheter om de trenger å bli betjent. Hvis svaret er «ja» kjøres en rutine som betjener enheten. Kan for eksempel gjøre dette med digitalread() i loop()-funksjonen. Interrupt (avbrudd). Betyr at CPU en sjekker ved jevne mellomrom om noen trenger å bli betjent. CPU en sjekker om et avbruddssignal er signalert etter utførelsen av hver instruksjon. Det betyr at det vil gå svært liten tid fra avbrudd er signalert til det blir oppdaget av CPU en. Avbrudd er mer effektiv metode enn polling. CPU en utfører sine oppgaver som normalt, og vil kun avbrytes hvis det kommer et avbruddsignal (interrupt) som varsler en «unormal» hendelse. Ved interrupt vil normalt kjørende program bli avbrutt for at en avbruddsrutine (ISR Interrupt Service Routine) skal kjøres for å betjene enheten som har signalert avbrudd. Etter at avbruddet er betjent fortsetter CPU en med oppgaven den utførte før avbruddet inntraff. CPU er har en egen avbruddslinje (fysisk linje) som brukes for å varsle interrupt (INTR) fra eksterne enheter.
2 Figur som viser at kjørende program blir avbrutt ved interrupt. Tilgjengelige Interrupt på Atmega 328 i prioritert orden. 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) 22 ADC Conversion Complete (ADC_vect) 23 EEPROM Ready (EE_READY_vect) 24 Analog Comparator (ANALOG_COMP_vect) 25 2-wire Serial Interface (I2C) (TWI_vect) 26 Store Program Memory Ready (SPM_READY_vect) Arduino Uno gir oss mulighet til å benytte pinne 2 og 3 til egne Interrupt.
3 Hvordan sette opp Interrupt på Arduino Uno.? Vi benytter funksjonen: attachinterrupt(). Den kobler et fysisk avbrudd på digital IO-pinne 2 eller 3 til en avbruddsrutine. attachinterrupt() har 3 parametre som vist i følgende kode. attachinterrupt (BUTTON_INT, navn på avbruddsrutine, type avbruddspuls ); BUTTON_INT skal være enten 0 eller 1 med Arduino Uno. Dette angir henholdsvis at digital inngang pinne 2 eller 3 skal detektere interrupt. ( Kun 2 innganger til interrupt på Uno.) Avbruddsrutine navn på funksjonen som utgjør avbruddsrutina Avbruddsrutine er en funksjon som ikke skal ha paramtere og heller ikke noen returverdi. Type avbruddspuls. Må være en av følgende 4 valg. RISING angir at interrupt skal detekteres på en puls som går fra lav til høy (stigende flanke). FALLING. - Puls som går fra Høy til Lav. Fallende flanke. CHANGE Interrupt når signalet endrer seg fra lav/høy eller motsatt. LOW Intterupt hvis signaler er lavt. Eksempel 1. const int BUTTON_INT=0; // Interrupt 0 (pin 2 på Arduino Uno) // Interrupt 1 (pin 3 på Arduino Uno) // Set up interrupt attachinterrupt(button_int, tempalarm, RISING); void tempalarm() // Interrupt function... Kode som håndterer interrupt-kilden Kan ikke inneholde delay() Bør være hurtig, og lite kode....
4 Eksempel 2. const int ledpin = 13; const int interruptpin = 2; // Interrupt kobles til Dig.IO pin 2 volatile int state = LOW; void setup() pinmode(ledpin, OUTPUT); pinmode(interruptpin, INPUT_PULLUP); attachinterrupt(digitalpintointerrupt(interruptpin), blink, CHANGE); void loop() digitalwrite(ledpin, state); void blink() // Interrupt-rutina state =!state; digitalpintointerrupt(interruptpin)- er en funksjon som kobler interruptpin til riktig Interrupt på mikrokontrolleren. - Pinne 2 er Interrupt 0. - Pinne 3 er Interrupt 1.
5 Timer. En Timer benyttes for å tidsstyre kjøring av rutiner. Vi kan sette en Timer-tid fra mikrosekunder opp til minutter/timer. For eksempel kan vi sette opp en rutine til å kjøre periodisk hvert 100 msek. Vi kan dermed enkelt lage rutiner som går periodisk, for eksempel styring, måling, regulering, sampling. Hvordan sette opp en Timer? Atmega 328 mikrokontroller har 3 timere innebygd. De kan være forholdsvis knotete å sette opp fra bunn av. En enkel måte å sette opp timere på er å benytte et bibliotek som noen har utviklet. Et enkelt bibliotek er: TimerOne Søk etter TimerOne. Last ned zip-fila og pakk ut innholdet. zip-fila ligger også på hjemmesiden under tidsplan. Pakk ut zip-fila. Og du skal se følgende filer: Innholdet i zip-fila.
6 Kopier disse filene til en katalog du kaller TimerOne som må ligge under Libraries under Arduino katalogene som vist på figuren under. Vi kan benytte Timer-funksjonen på følgende vis: #include <TimerOne.h> // Må være med // Initiere og sette opp en timer Timer1.initialize(500000); // 500000 mikrosek = 0.5 sekund Timer1.attachInterrupt(changePitch); // Kjører "changepitch" hvert 0.5 sek ----------------- Timer1.initialize(tid) Timer1.attachInterrupt(timer_rutine) angir periodetiden i mikrosekunder angir navnet på funksjonen som skal kjøres periodisk
7 Eksempel med timer. /* Enkel test av Timer. Lar timer-rutina styre Led 11. Tid oppgis i mikrosekunder. loop() styrer en frekvens som benyttes av tone() RR mars 2018 Fil: timer1.io */ #include <TimerOne.h> const int LED=11; const int tonepin = 8; int frekvens = 50; void setup() Serial.begin(9600); pinmode(led, OUTPUT); Timer1.initialize(2000000); // periodetid for timer 2 sekunder Timer1.attachInterrupt(blinkLed); // blinkled er timerfunksjonen som blir utført void loop() tone(tonepin, frekvens,400); frekvens += 50; // Generer frekvens på tonepin if (frekvens >= 4000) frekvens = 100; Serial.print("Frekvensen er: "); Serial.println(frekvens); delay(500);
8 //Timer funksjonen void blinkled() static bool ledonoff = 0; ledonoff =!ledonoff; digitalwrite(led, ledonoff); Serial.print("Led er: "); Serial.println(ledOnOff); // Toggle LED State Eksempel som benytter både interrupt og timer. /* Enkel test av Timer og Interrupt. Lar timer-rutina styre Blå Led pinne 11. Interrupt-rutina slår rødt lys på pinne 12 av/på. loop() styrer en frekvens på lyden mellom 100 og 4000 Hz. RR mars 2018 Fil: test_timer_interrupt_enekel_ver_mars2018 */ //Using Timer Interrupts with the Arduino #include <TimerOne.h> const int blueled=11; const int redled=12; const int tonepin = 8; int frekvens = 50; const int INT_BUTTON = 0; // Interrupt0 er koblet til pinne 2 på Uno. void setup() Serial.begin(9600); pinmode(blueled, OUTPUT); pinmode(redled, OUTPUT); Timer1.initialize(1500000); // set a timer of length 1500000 microseconds (1.5 second) Timer1.attachInterrupt(blinkLed); //Runs "blinkled" on each timer interrupt // Setter opp interrupt-rutina, // Benytter IO-pinne 2 som utgjør Int 0. Rising angir puls fra lav til høy. attachinterrupt(int_button, intrutine, RISING );
9 void loop() tone(tonepin, frekvens,800); frekvens += 50; if (frekvens >= 3000) frekvens = 100; Serial.print("Frekvensen er: "); Serial.println(frekvens); delay(800); //Timer interrupt function void blinkled() static boolean ledonoff = 0; ledonoff =!ledonoff; digitalwrite(blueled, ledonoff); //Toggle blueled On/Off Serial.println(""); Serial.println("Timer-rutine blir utfoert."); Serial.print("blueLed er: "); Serial.println(ledOnOff); // Interrupt-rutine som switcher Rød Led på pinne 12 av/på void intrutine(void) static boolean ledonoff = 0; ledonoff =!ledonoff; Serial.println(""); Serial.println("int_routine blir utfoert."); Serial.print("RedLed er: "); Serial.println(ledOnOff); digitalwrite(redled, ledonoff); //Toggle redled State
10 Sjekker monitoren.
11 Program som skal hinder prelling når en bryter trykkes /* Test av prelling, og styring av Led med PWM. RR mars 2018. Fil: bounce_test_1.io */ const int BLUELED = 9; const int BUTTON = 12; int ledverdi = 0; boolean lastbuttonstate = false; boolean ledon = false; void setup() pinmode(blueled, OUTPUT); pinmode(button, INPUT); void loop() bool currentbutton = buttononoff(); if ( lastbuttonstate == LOW && currentbutton == HIGH) ledon =!ledon; lastbuttonstate = currentbutton; // Bruke PWM på led bruker analogwrite() // BLUELED kjøres fra 0 -> Max lys. // Hvis bryter er trykket starter den fra 0 igjen. if ( ledon == true) analogwrite(blueled, ledverdi); ledverdi++; if (ledverdi == 255) ledverdi = 0; else ledverdi = 0; analogwrite(blueled, ledverdi); delay(10);
12 // Sjekker tilstand på bryter. Leser 2 ganger. Unngår prelling. boolean buttononoff(void) boolean current = digitalread(button); if ( current!= lastbuttonstate) delay(5); // delay for å unngå prelling på bryter current = digitalread(button); // Leser 1 gang til for å være sikker på verdien return current; Eksempel 2. Hindre prelling. Venter x antall msek etter endring. /* Hindre prelling p bryter Fil: test_prelling_15mars2018.ino */ int inpin = 2; int outpin = 11; // the number of the input pin // the number of the output pin int counter = 0; int reading; int current_state = LOW; // how many times we have seen new value // the current value read from the input pin // the debounced input value // the following variable is a long because the time, measured in milliseconds, // will quickly become a bigger number than can be stored in an int. long time = 0; // the last time the output pin was sampled int debounce_count = 10; // number of millis/samples to consider before declaring a debounced input
void setup() Serial.begin(9600); pinmode(inpin, INPUT); pinmode(outpin, OUTPUT); digitalwrite(outpin, current_state); // setup the Output LED for initial state 13 void loop() // If we have gone on to the next millisecond if(millis()!= time) reading = digitalread(inpin); if(reading == current_state && counter > 0) // Skal sikre at det går 10 msek etter siste prelling. Serial.println(" counter-- "); counter--; if(reading!= current_state) // Teller opp, fordi det er en endring i tilstand Serial.println(" counter++ "); counter++; // If the Input has shown the same value for long enough let's switch it if(counter >= debounce_count) counter = 0; current_state = reading; digitalwrite(outpin, current_state); Serial.println(" counter > 10 "); time = millis(); // Leser av tiden for å sikre at det går 1 ms mellom hver sjekk
14 Sjekker monitoren.