Vizualizácia činnosti registra TCNT0 v CTC režime

Zápisník experimentátora

Hierarchy: Časovač (timer)

V tomto článku nadviažeme na predchádzajúci článok, ktorý popisoval timer2. Pokúsime sa o rovnakú vizualizáciu činnosti registra TCNT0, ktorý sa nachádza v časovači 0. Timer0 v Arduine obsluhuje výpočet času a preto zásahy do neho ovplyvňujú aj funkcie millis a delay. Ukážeme si jednoduché riešenie, ako sa vysporiadať aj s takýmto problémom.

Použité súčiastky

Na vytvorenie animácie môžeme použiť:

  • Arduino Pro Mini (link) - Použil som ho preto, lebo sa zmestí aj na najmenšie skúšobné pole.
  • Mini Breadboard (link) - Najmenšie predávané skúšobné pole. Použil som dve a treba si dať pozor, že sa predávajú dve modifikácie tohoto breadboardu. Tie čo majú drobné výstupky sa dajú takto pekne navzájom pospájať.
  • Prevodník CP2102 USB to Serial (link) - Prevodník slúži na naprogramovanie Arduina.
  • Dosku s rezistormi a LED diódami (link) - Pretože mám vyrobených niekoľko takýchto dosiek, použil som ju, aby som nemusel komplikovane pripojiť 8 LED a 8 rezistorov k pinom Arduina. Ale rovnako môžete použiť aj priamo rezistory a LED, len si dajte pozor, aby ste neprekročili maximálny prúd na pine. Preto používajte rezistory od 330 do 1000 ohmov.

Všetko je zapojené podľa nasledovného obrázka. LED diódy sú pripojené na piny RX (0), TX (1), 2-7 a na obrázku je ešte vidno pripojenie GND na mojej LED doske pomocou dvoch drôtených prepojok k Arduinu. Arduino Pro Mini má vyvedené GND aj na druhej strane, takže som mohol použiť aj jedinú prepojku, ale potom by sa to zle fotografovalo.

RX a TX sú navzájom prehodené, pretože sa bude zobrazovať bitová podoba 8bitového čísla pomocou priameho zápisu do registra PORTD a potrebujeme mať piny zoradené rovnako, ako sú bity v registri. Budeme zobrazovať obsah registra TCNT0, čo je register pre timer0. V ňom sa postupne zvyšuje hodnota až po nastavenú hodnotu registra OCR0A. To sa deje v prípade režimu CTC. Malo by platiť, že maximálna hodnota TCNT0=OCR0A. Na diódach je to ľahko skontrolovateľné, pretože ak do OCR0A zapíšete napríklad hodnotu 63 (binárne 0b00111111), nemali by svietiť posledné dve LED. Ak sa rozsvietia, potom ste niečo naprogramovali zle.

Programovanie

Tento program ovplyvňuje timer0 a preto sa v ňom nedajú používať funkcie millis a delay.

Základ programu som vygeneroval v mojej aplikácii. Pretože hodiny Arduina sú nastavené na 16 MHz a používame 8bitový timer, nemôžeme ho nastaviť na príliš nízku hodnotu. Minimum je približne 70 Hz. Preto som to radšej nastavil na 100 Hz.

Aby sme videli, že program funguje správne, nechal som v programe blikať aj LED diódu na pine 13. Blikanie sa odohráva v obsluhe prerušenia ISR(TIMER0_COMPA_vect) a pretože prerušenie sa vyvolá 100x za sekundu, spomalil som ho pomocou premennej divider na frekvenciu 1 Hz.

Vizualizácia prerušenia sa nachádza vo funkcii loop. Nemôžeme ju mať v prerušení, pretože v okamihu, ako sa prerušenie vyvolá, má register TCNT0 stále rovnakú hodnotu. V predchádzajúcom príklade som na tomto mieste zápis do registra mierne pozdržal pomocou funkcie delay. Tú ale teraz použiť nemôžeme, pretože som preprogramoval časovanie timera 0 a tým som zmenil rátanie času pre Arduino. Napríklad funkcia delay je v jadre Arduina definovaná takto.

void delay(unsigned long ms)
{
    uint32_t start = micros();

    while (ms > 0) {
        yield();
        while ( ms > 0 && (micros() - start) >= 1000) {
            ms--;
            start += 1000;
        }
    }
}

Pretože v tomto prípade potrebujem len nejaké spomalenie, stačí mi v dostatočnom množstve použiť inštrukciu nop. Použil som cyklus, ktorý túto inštrukcie použije v dostatočnom množstve, aby som na pár milisekúnd spomalil vykresľovanie a tým dosiahol želaný efekt. Text v úvodzovkách musí byť zapísaný aj s tým \n na konci, pretože inštrukcie assembleru sa zapisujú každá do osobitného riadku a toto nám to zabezpečí. Keď máme len jednu inštrukciu, tak to nie je tak viditeľné, ako keby sme ich v kóde použili viac naraz.

for(long i=0;i<100000;i++)
    __asm__("nop\n");

Takto vznikol zaujímavý animačný efekt. Trochu to pripomína staré sci-fi filmy, kde kozmické lode, alebo akákoľvek pokročilá technológia bývala stvárnená ako určitým spôsobom blikajúca skupina svetiel.

Keď sa zadívate do blikania, všimnete si, že binárne číslo nejde až do 255. Keby bola v registri OCR0A napríklad hodnota 31, 63 alebo 127, boli by zhasnuté diódy z horných bitov ešte viac viditeľné. To je vlastne aj dôkazom toho, že to v režime CTC naozaj počíta správne.

#define ledPin 13

int divider=0;

void setupTimer() {
  noInterrupts();
  TCCR0A = 0;
  TCCR0B = 0;
  TCNT0  = 0;
  
  OCR0A = 155; // 100.16025641025641 Hz
  TCCR0A |= (1 << WGM01);
  TCCR0B |= 0;
  TCCR0B |= (1 << CS02) | (0 << CS01) | (1 << CS00);
  TIMSK0 |= (1 << OCIE0A);
  interrupts();
}

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

  for (int i = 0; i < 8; i++)
    pinMode(i, OUTPUT);
    
  setupTimer();
}

void loop() {
  PORTD = TCNT0;

  // delay replacement, because we changed timer0
  for(long i=0;i<100000;i++)
    __asm__("nop\n"); 
}

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

Zdrojový kód

Zdrojový kód sa nachádza na serveri GitHub.


29.01.2017


Menu