CTC timer v mikrokontroléru ATtiny85

Zápisník experimentátora

Hierarchy: Časovač (timer)

ATtiny85 obsahuje dva 8bitové timery. Nemají sice takový rozsah jako ty v ATmega328P, ale na většinu úkolů to postačuje. V tomto článku se podíváme na časovače z hlediska CTC režimu. Na generování programů budeme využívat online kalkulátor.

Použité součástky

Pokud si chcete experimenty vyzkoušet, budete potřebovat.

  • Vývojovou desku pro ATtiny85, nebo jen zkušební pole se zastrčeným mikrokontrolérem.
  • USBasp programátor.

Zdrojové texty jsem psal pro frekvence 1000000, 8000000 a 16000000. Používal jsem jádro https://github.com/SpenceKonde/ATTinyCore. Pro každý timer jsou tři příklady. Všechny blikají LED diodou, která je připojena na pin 0. Pin 0 je na mikrokontroléru vpravo dole.

Timer0

Timer0 je v podstatě shodný se stejným timerem v ATmega328P. Proto i nastavení registrů vypadá skoro stejně. To co se může lišit je umístění jednotlivých bitů, ale protože používáme můj kalkulátor, nemusí nás to trápit a základ programu nám kalkulátor vygeneruje. Protože pracujeme s nízkou frekvencí 1 Hz, nemůžeme ji nastavit na timeru přímo. Používám drobný trik a nastavil jsem frekvenci na 100 Hz a v obsluze přerušení si pomocí modula dělím tuto frekvenci hodnotou 100 a tím se dostanu na požadovaný 1 Hz. V kalkulačce je na to už připravená šablona, která generuje tento kód.

#define ledPin 0
volatile int divider=0;

void setupTimer0() {
  noInterrupts();
  // Clear registers
  TCCR0A = 0;
  TCCR0B = 0;
  TCNT0 = 0;

  // 100.16025641025641 Hz (1000000/((155+1)*64))
  OCR0A = 155;
  // CTC
  TCCR0A |= (1 << WGM01);
  // Prescaler 64
  TCCR0B |= (1 << CS01) | (1 << CS00);
  // Output Compare Match A Interrupt Enable
  TIMSK |= (1 << OCIE0A);
  interrupts();
}

void setup() {
  pinMode(ledPin, OUTPUT);
  setupTimer0();
}

void loop() {
}

ISR(TIMER0_COMPA_vect) {
  if(divider==0)
    digitalWrite(ledPin, digitalRead(ledPin) ^ 1);
  divider++;
  divider%=100;
}

Timer1

Timer1 je v CTC režimu složitější. Nastavuje se odlišně a musíme nastavit více registrů. Na druhé straně ale máme k dispozici mnohem více dělicích poměrů, což dává tomuto timeru lepší možnosti.

Všimněte si, že k nastavení požadované frekvence musíme nastavit dva registry. Tento timer totiž pomocí jednoho registru nastavuje frekvenci a pomocí jiného vyvolává přerušení. V mém případě mají oba registry stejnou hodnotu. Hodnota registru OCR1A ale může mít jakoukoliv hodnotu, jen nesmí být větší než hodnota v OCR1C. V tomto případě nám je jedno, kdy přesně přerušení nastane, stačí nám, že nastane a vyvolá náš kód na zpracování přerušení. Ale takto se mi to jevilo být přehlednější a tak jsem to i nastavil v kalkulačce.

#define ledPin 0
volatile int divider=0;

void setupTimer1() {
  noInterrupts();
  // Clear registers
  TCNT1 = 0;
  TCCR1 = 0;

  // 100.16025641025641 Hz (1000000/((155+1)*64))
  OCR1C = 155;
  // interrupt COMPA
  OCR1A = OCR1C;
  // CTC
  TCCR1 |= (1 << CTC1);
  // Prescaler 64
  TCCR1 |= (1 << CS12) | (1 << CS11) | (1 << CS10);
  // Output Compare Match A Interrupt Enable
  TIMSK |= (1 << OCIE1A);
  interrupts();
}

void setup() {
  pinMode(ledPin, OUTPUT);
  setupTimer1();
}

void loop() {
}

ISR(TIMER1_COMPA_vect) {
  if(divider==0)
    digitalWrite(ledPin, digitalRead(ledPin) ^ 1);
  divider++;
  divider%=100;
}

Zdrojové texty

Zdrojové texty se nacházejí na serveru GitHub.


31.10.2017


Menu