Timere

Timere i mikrokontroller-sammenheng er i praksis tellere som oppdateres automatisk og kontinuerlig, uavhengig av hva CPU’en holder på med.

ATmega328, som brukes på Arduino UNO, har to 8-bits timere og en 16-bits timer

  • 8-bits timere teller fra 0 til 255
  • 16-bits timere teller fra 0 til 65535

Det finnes biblioteker som håndterer timer-interrupt, men her skal vi se på hvordan vi bruker registere for å sette verdier direkte, og sette opp pulsbredde-modulering. For å finne ut hvilke registere, og hvilke verdier vi skal bruke, må vi bruke databladet til mikrokontrolleren (ATMega328). Det kan du finne her: https://ww1.microchip.com/downloads/aemDocuments/documents/MCU08/ProductDocuments/DataSheets/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061B.pdf 

Prinsippet, sammen med en del av de forskjellige registerene vises på flytskjemaet under

Flytskjema

Som vi ser på figuren over tar vi utgangspunkt i klokkefrekvensen, som på en Arduino Uno er 16 MHz. Det vil si at hver klokkepuls er 62.5 nanosekunder. Med timere på 8 og 16 bit vil det gi oss en tid på ca 58 mikrosekunder for de to 8 bits timerene og ca 4 millisekunder for 16 bits timeren. Dette er ofte for lite til å gi oss frekvensene vi er ute etter. Derfor har mikrokontrolleren innebygd noe som heter prescaler. Denne kan dele klokkefrekvensen på 8, 64, 256 eller 1024. Det vil si at vi kan gå fra 62,5 nanosekunder til 64 mikrosekunder for hvert "tick" på telleren. Dermed kan vi få periodetider på i overkant av 16 millisekunder for 8 bits og over 4 sekunder for 16 bits timeren.

I databladet refereres det til BOTTOM og TOP:

  • Bottom er 0. 
  • TOP er enten høyeste mulige tellerverdi, eller variabel (satt i software)

 

Timer 0 og 2

Timer 0 og 2 er 8-bits, og har 6 forskjellige modi:

  • 0: Normal
  • 1: PWM Fase-korrekt
  • 2: CTC (Clear Timer on Compare Match)
  • 3: Fast PWM
  • 5: PWM, Fase-korrekt (variabel TOP)
  • 7: Fast PWM (variabel TOP)
  • (4 og 6 er reservert)

Timer 1

Timer 1 er 16-bits og har 15 modi (modus 13 reservert)

PWM

PWM står for Pulse Width Modulation (Pulsbredde- eller pulsvidde-modulering på norsk)

-styring av et firkantpulstog på en slik måte at de aktive pulslengdene endres, mens periodetiden (og derfor grunnfrekvensen) er konstant ( http://no.wikipedia.org/wiki/PWM )

Forholdet med om av- og på-tid kalles Duty Cycle (kan oversettes direkte med Arbeidssyklus, men det engelske uttrykket brukes)

Eksempler på bruksområder:

  • Regulering av hastighet på motorer
  • Regulering av lysstyrke
  • Styring av servo
  • Kommunikasjon
  • Spenningsregulering
  • Lyd

Duty cycle – forholdet mellom periodetid og pulslengde:

Duty Cycle

Oppgaver:

Oppgave 1:  
Skriv om eksempelet i den første fliken, slik at telleren økes bare med 1 hver gang knappen trykkes.

Oppgave 2:
Legg til en knapp til. Bruk denne til å telle ned når knappen trykkes.

Løsningsforslag:

Oppgave 1:  
Skriv om eksempelet i den første fliken, slik at telleren økes bare med 1 hver gang knappen trykkes.

Så lenge vi er inne i ISR'en, vil ikke funksjoner som delay() og millis() fungere, dvs. verdiene blir ikke oppdatert. Vi kan likevel kalle millis() i starten for å registere når knappen er trykt inn. Denne verdien har jeg lagret i variabelen "sisteTrykk" i løsningsforslaget. Neste gang ISR'en kjøres sammenlignes denne med

const int interruptPinne = 2;  // Definer hvilken pinne som skal brukes for interrupt
//Alle variabler som kan endres inne i en ISR må deklareres som "volatile"
volatile int interruptTeller = 0;  // Variabel for å telle antall ganger interruptet har blitt utløst
volatile long sisteTrykk = 0;
void setup() {
  pinMode(interruptPinne, INPUT_PULLUP);                                             // Konfigurer interruptPin som input
  attachInterrupt(digitalPinToInterrupt(interruptPinne), knapp1Interrupt, FALLING);  // Koble interrupt til interruptPin og kall ISR-funksjonen "handleInterrupt"
  Serial.begin(115200);                                                              // Start seriell kommunikasjon med PC
}

void loop() {
  Serial.print("Interrupt teller: ");  // Skriv antall ganger interruptet har blitt utløst til seriell monitor
  Serial.println(interruptTeller);
  delay(1000);
}

// ISR-funksjonen som blir kalt når interruptet utløses
void knapp1Interrupt() {
  long naa = millis();
  if (naa > sisteTrykk + 200) { //Sjekker om det har gått 200 millisekunder siden siste trykk
  sisteTrykk = naa;  //Lagrer tiden første gang ISR'en kjøres etter knappe-trykk
  interruptTeller++; // Øk interruptCount med en hver gang interruptet utløses } }

Oppgave 2:

Kommer

https://www.youtube.com/watch?v=wIcC8-g9Lnw