|

Timery w Arduino

Arduino posiada clock (zegar kwarcowy / oscylator) o częstotliwości 16Mhz. Każdy cykl zegara odbywa się co 1/16000000s czyli co 62.5ns.

Timery 0, 1 i 2

ATmega328P posiada 3 timery[0, 1, 2], które zliczają cykle zegara.

Timer0: 8-bit (0-255), używany do delay, millis, micros, itp.

Timer1: 16-bit (0-65535), używany w bibliotekach (np. servo)

Timer2: 8-bit (0-255), używany w funkcji tone()

Prescalery

Timery działają na rejestrach 8 i 16 bitowych co oznacza, że mogą zliczać maksymalnie do liczby 255 lub 65535. Po przekroczeniu tej liczby counter się resetuje i generuje przerwanie.

Aby uzyskać dłuższe czasy można zmienić multiplicator (prescaler), który będzie zliczał cykle w pewnych odstępach np. x1, x8, x64, x256, x1024. Multiplikator znajduje się pomiędzy clockiem, a timerem. Domyślnie wszystkie timery zliczają cykle z prescalarem x64.

Timer0 z prescalerem x64 generuje przerwanie co ok.1ms, dzięki czemu stosuje się go w metodzie millis().

256 [ilość cykli] x 64[prescaler] x 62.5ns =1024000ns = 1,024ms

Zmiana prescalera

Prescalery mogą być ustawiane za pomocą rejestrów dla każdego z timerów:

Timer0 – TCCR0B

Timer1 – TCCR1B

Timer2 – TCCR2B

Rejestr TCCR1B odpowiada za wybór trybu pracy timera (zliczanie, CTC, PWM), a także za konfigurację preskalera, który określa częstotliwość zliczania.

Zmiana prescalera odbywa się na 3 pierwszych bitach rejestru TCCR1B (CS10, CS11, CS12)

TCCR1B76543210
 ICNC1ICES1WGM13WGM12CS12CS11CS10
CS12CS11CS10USE
000No Clock Timer STOP
0011 (No Prescaling)
0108 (From Prescaler)
01164 (From Prescaler)
100256 (From Prescaler)
1011024 (From Prescaler)
110External clock source on T1 Pin. Clock on falling edge
111External Clock source on T1 pin. Clock on rising edge.
TCCR1B = 0;  // reset rejestru

TCCR1B |= B00000100;  // prescaler 256
TCCR1B |= B00000101;  // prescaler 1024
TCCR1B |= (1 << CS12) | (1 << CS10); //prescaler 1024

Wzór na obliczanie wartości prescalera i czasu dla timera1

wzór na obliczanie prescalera dla timera w Arduino Prescaler, TCNT, OCR
wzór na obliczanie prescalera dla timera w Arduino Prescaler, TCNT, OCR

Rejestr TCNT1 zwraca aktualną ilość cykli (od 0 do 65535). Można go łatwo zresetować manualnie przypisując do niego 0. Prescaler wpływa na szybkość inkrementacji wartości, jakie są zwracane przez TCNT1 (im większy prescaler, tym dłużej trwa inkrementacja cykli timera).

Rejestr OCR1A (Output Compare Register 1A) w Arduino służy do ustawiania wartości porównawczej dla timera 1 w trybach pracy z porównaniem wyjściowym, po spełnieniu równości rejestr TCNT generuje przerwanie.

ISR(TIMER1_COMPA_vect) {}

Rejestr TIMSK1 służy do włączania i wyłączania przerwań związanych z Timer1. Włączenie odpowiednich bitów w tym rejestrze pozwala na wywoływanie przerwań przy określonych zdarzeniach, takich jak osiągnięcie wartości porównawczej lub przepełnienie licznika. Przerwania te są użyteczne do wykonywania działań w precyzyjnych odstępach czasowych lub w odpowiedzi na zmiany wartości licznika.

Przykład programu Blink LED co 1s

#include <avr/io.h>

void setup() {
  TCCR1A = 0; //reset timera1
  TCCR1B = 0; //reset prescalera
  TCCR1B |= B00000100; // prescaler x256
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  if (TCNT1 > 62500) {
    TCNT1 = 0; //reset licznika
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
  }
}
// Identyczny program co powyżej, ale z użyciem prescalera 1024
void setup() {
  TCCR1A = 0; //reset timera1
  TCCR1B = 0; //reset prescalera
  TCCR1B |= B00000101; // prescaler x1024
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  if (TCNT1 > 15625) {
    TCNT1 = 0; //reset licznika
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
  }
}

Przykład Blink LED co 0.5s z użyciem przerwania

int led = LED_BUILTIN;
bool LED_STATE = true;
void setup() {
  pinMode(led, OUTPUT);
  cli();                //stop interrupts for till we make the settings
  TCCR1A = 0;           // Reset entire TCCR1A to 0
  TCCR1B = 0;           // Reset entire TCCR1B to 0
  TCCR1B |= B00000100;  //Set CS12 to 1 so we set prescaler to 256
  TIMSK1 |= B00000010;
  OCR1A = 31250;  //set compare register A to this value
  sei();          //Enable back the interrupts
}
void loop() {}

ISR(TIMER1_COMPA_vect) {
  TCNT1 = 0;
  LED_STATE = !LED_STATE;
  digitalWrite(led, LED_STATE);
}

Obsługa timera1 do zmierzenia czasu wywołania funkcji.

void setup() {
  Serial.begin(115200);
  TCCR1B = bit(CS10); // ustawia mnożnik timera1 na x1
  TCNT1 = 0; // resetuje licznik timera na 0

  // Funkcja do zmierzenia
  DDRB = B00111000; // zamiennik pinMode dla wielu pinów 8-13
  PORTB = B00111000; // zamiennik digitalWrite() dla wielu pinów jednocześnie
  // Koniec funkcji do zmierzenia

  unsigned long value = TCNT1; // zczytuje wartość licznika timera
  Serial.print("Cycles: ");
  Serial.println(value - 1);
  Serial.print("Microseconds: ");
  Serial.println((float)(value - 1) / 16);
}

void loop() {}

Biblioteka TimerOne

Link do biblioteki TimerOne na github

Za pomocą biblioteki TimerOne można stworzyć przerwania wewnętrzne do wykonywania operacji cyklicznych.

#include <TimerOne.h>
int ledPin = 10;

void ledBlink() {
  digitalWrite(ledPin, !digitalRead(ledPin));
}

void setup() {
  pinMode(ledPin, OUTPUT);
  Timer1.initialize(1000000);  //in microseconds
  Timer1.attachInterrupt(ledBlink);
}

void loop() {}

Zobacz: Rejestry portów w Arduino

Podobne wpisy

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *