Vizualizace činnosti registru TCNT0 v CTC režimu

Zápisník experimentátora

Hierarchy: Časovač (timer)

V tomto článku navážeme na předchozí článek, který popisoval timer2. Pokusíme se o stejnou vizualizaci činnosti registru TCNT0, který se nachází v časovači 0. Timer0 v Arduinu obsluhuje výpočet času a proto zásahy do něj ovlivňují i funkce millis a delay. Ukážeme si jednoduché řešení, jak se vypořádat i s takovým problémem.

Použité součástky

K vytvoření animace můžeme použít:

  • Arduino Pro Mini (link) - Použil jsem ho proto, že se vejde i na nejmenší breadboard.
  • Mini Breadboard (link) - Nejmenší prodávaný breadboard. Použil jsem dva a třeba si dát pozor, že se prodávají dvě modifikace tohoto breadboardu. Ty co mají drobné výstupky se dají takto pěkně navzájem pospojovat.
  • Převodník CP2102 USB to Serial (link) - Převodník slouží k naprogramování Arduino.
  • Desku s rezistormi a LED diodami (link) - Protože mám vyrobených několik takových desek, použil jsem ji, abych nemusel komplikovaně připojit 8 LED a 8 rezistorů k pinům Arduino. Ale stejně můžete použít i přímo rezistory a LED, jen si dejte pozor, abyste nepřekročili maximální proud na pinu. Proto používejte rezistory od 330 do 1000 ohmů.

Vše je zapojené podle následujícího obrázku. LED diody jsou připojeny na piny RX (0), TX (1), 2-7 a na obrázku je ještě vidět připojení GND na mé LED desce pomocí dvou drátěných propojek k Arduinu. Arduino Pro Mini má vyvedeno GND i na druhé straně, takže jsem mohl použít i jedinou propojku, ale pak by se to špatně fotografovalo.

RX a TX jsou navzájem přehozené, protože se bude zobrazovat bitová podoba 8bitová čísla pomocí přímého zápisu do rejstříku PORTD a potřebujeme mít piny seřazeny stejně, jako jsou bity v registru. Budeme zobrazovat obsah registru TCNT0, což je registr pro timer0. V něm se postupně zvyšuje hodnota až po nastavenou hodnotu registru OCR0A. To se děje v případě režimu CTC. Mělo by platit, že maximální hodnota TCNT0 = OCR0A. Na diodách je to snadno kontrolovatelné, protože pokud do OCR0A zapíšete například hodnotu 63 (binární 0b00111111), neměly by svítit poslední dvě LED. Pokud se rozsvítí, pak jste něco naprogramovali špatně.

Programování

Tento program ovlivňuje timer0 a proto se v něm nelze používat funkce millis a delay.

Základ programu jsem vygeneroval v mé aplikaci. Protože hodiny Arduina jsou nastaveny na 16 MHz a používáme 8bitový timer, nemůžeme ho nastavit na příliš nízkou hodnotu. Minimum je přibližně 70 Hz. Proto jsem to raději nastavil na 100 Hz.

Abychom viděli, že program funguje správně, nechal jsem v programu blikat i LED diodu na pinu 13. Blikání se odehrává v obsluze přerušení ISR (TIMER0_COMPA_vect) a protože přerušení se vyvolá 100x za sekundu, zpomalil jsem ho pomocí proměnné divider na frekvenci 1 Hz.

Vizualizace přerušení se nachází ve funkci loop. Nemůžeme ji mít v přerušení, protože v okamžiku, jak se přerušení vyvolá, má rejstřík TCNT0 stále stejnou hodnotu. V předchozím příkladu jsem na tomto místě zápis mírně pozdržel pomocí funkce delay. Tu ale nyní použít nemůžeme, protože jsem přeprogramoval časování timeru 0 a tím jsem změnil počítání času pro Arduino. Například funkce delay je v jádru Arduina definována takto.

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

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

Protože v tomto případě potřebuji jen nějaké zpomalení, stačí mi v dostatečném množství použít instrukci nop. Použil jsem cyklus, který tuto instrukce použije v dostatečném množství, abych na pár milisekund zpomalil vykreslování a tím dosáhl kýžený efekt. Text v uvozovkách musí být zapsán is tím \n na konci, protože instrukce assembleru se zapisují každá do zvláštního řádku a toto nám to zajistí. Když máme jen jednu instrukci, tak to není tak viditelné, jako kdybychom je v kódu použili více najednou.

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

Takto vznikl zajímavý animační efekt. Trochu to připomíná staré sci-fi filmy, kde kosmické lodě, nebo jakákoliv pokročilá technologie bývala ztvárněná jako určitým způsobem blikající skupina světel.

Když se zadíváte do blikání, všimnete si, že binární číslo nejde až do 255. Kdyby byla v rejstříku OCR0A například hodnota 31, 63 nebo 127, byli by zhasnuté diody z horních bitů ještě více viditelné. To je vlastně i důkazem toho, že to v režimu CTC opravdu počítá správně.

#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 se nachází na serveru GitHub.


31.05.2020


Menu