Mikrokontrolery AVR część 6 – timer0

Ta część kursu będzie poświęcona najprostszemu z układów licznikowych w mikrokontrolerach atmega8. Będę mówiło liczniki/czasomierzu 0. Takie licznik/czasomierz to po prostu układ, którego celem jest zliczać przychodzące do niego impulsy.

Tutaj już pojawia się problem kiedy mówimy że coś jest licznikiem, a kiedy czasomierzem. Generalnie to jest to dokładnie ten sam układ, ale zależnie od swojej roli, działa impulsach o różnym pochodzeniu. Czyli licznik, to układ, który tylko zlicza impulsy, a te impulsy pochodzą z zewnątrz układu, a czasomierz, mierzy czas, czyli musi być dokładny, więc zlicza impulsy pochodzące z zegara taktującego kontroler.Poniżej zamieszczam schemat licznika/czasomierza 0, schemat zaczerpnięty z noty katalogowej.

Licznik ten składa się z:

  • Bloku wejściowego, w którym dokonuje się wyboru źródła impulsów, na schemacie oznaczony jest jako Clock Select
  • Układu sterującego, który zarządza działaniem licznika
  • rejestru TCNTn, w przypadku licznika 0 rejestr nazywa się po prostu TCNT0, jest to najważniejszy rejestr licznika, bo właśnie to w nim są zliczane impulsy
  • rejestru TCCR0, który umożliwia konfigurację licznika

Dodatkowym układem, którego nie ma na schemacie jest preskaler. W układzie wyboru źródła impulsów widać sygnał określony jako From Prescaler. Co takiego te preskaler robi, dzieli on sygnał z zegara na sygnał o mniszej częstotliwości. Możliwy jest podział przez:1, 8, 64, 256, 1024.

Po co to dzielimy? Czasem(nawet często) zachodzi potrzeba, że częstotliwość taktowania jest zbyt duża, i było by ją ciężko zliczyć, więc gdy ją podzielimy, wtedy automatycznie mamy mniej impulsów do zliczania.

Jednym z najważniejszych parametrów licznika jest jego pojemność wyrażana w bitach. Gdzie ta pojemność jest? Jest to rejestr TCNTn. W który się zlicza impulsy. Im większa pojemność, tym więcej impulsów można zmieścić (zliczyć). W przypadku licznika 0 jego pojemność wynosi 8b, czyli można zliczyć w mim 256 impulsów. A co się dzieje, gdy licznik zostanie zapełniony (wartość FFh), wtedy licznik się ?przewija? i zlicza od początku, ale także wysyła sygnał, że skończył zliczać. I to w tym wszystkim jest najważniejsze. Za każdym razem gdy się przepełni daje nam znać, że to zrobił. I wtedy wiemy, że miął określony czas, bądź dotarła do kontrolera określona ilość impulsów. W tedy też może być generowane przerwanie.

Teraz kiedy już wiemy jak mniej więcej działa licznik, przejdźmy do jego konfiguracji. Zacznijmy od tego jak wybrać sygnał impulsów. Służą do tego trzy bity w rejestrze TCCR0. Poniżej przedstawiam ten rejestr wraz z tabelą możliwych konfiguracji.

 

Jest to chyba na tyle jasne, że nie muszę tłumaczyć, przedstawię to później w przykładzie. A teraz przejdę do następnego rejestru.

Gdy licznik się przepełnia, jak pisałem wcześniej zostaje wysłany sygnał. Ślad po tym sygnale zostaje jako ustawiony bit TOV0 w rejestrze TIFR. Możemy w głównym programie sprawdzać ciągle stan tego bitu i zależnie od tego czy jest ustawiony, czy nie podjąć pewną akcję. Ale po co się męczyć i zawracać ciągle głowę kontrolerowi tym sprawdzaniem, mamy przecież przerwania. Żeby skonfigurować przerwania od przepełnienia licznika/czasomierza0 musimy tylko włączyć te przerwanie. Odpowiedzialny jest za to bit TOIE0 w rejestrze TIMSK.

Kiedy ten bit będzie ustawiony(1), to za każdym razem gdy się przepełni licznik zostanie wywołane przerwanie. Przypominam, że jeżeli chcemy aby nasze przerwanie działało musimy włączyć globalny system przerwań.

Już wiemy jak ustawiać preskaler, ale jak skąd mamy wiedzieć jakie ustawienie jest najlepsze. Powiem teraz o tym jaki dzielnik najlepiej wybrać. Jest to koszmar porządkujących, żeby dobrać takie nastawy, aby czas mierzony był prawidłowy i dokładny, sprawa nie jest taka trudna jak się wydaje, tylko trzeba poznać sposób działania. Wytłumaczę to na przykładzie.

 

Przykład

Załóżmy, że mamy kontroler taktowany częstotliwością 1MHz i chcemy odmierzać dokładnie 1 sekundę.

Na początek trzeba przyjąć zasadę, że impuls musi być zawsze cały nigdy jego część. Czyli na żadnym etapie nie może nam wyjść wartość inna niż całkowita. Cała ta operacja opiera się na dzieleniu liczby 1M, bądź wyników z poprzedniego dzielenia. Więc mamy:

1000 000/64/125 = 125

Teraz wytłumaczę dzielimy przez 64, wartość z preskalera i po tej operacji mamy wartość 15625. Następnie mamy rejestr TCNT0 i w nim dzielimy przez 125, czyli co 125 impulsów będziemy mieli przepełnienie. No ale przecież mamy rejestr 8b, czyli 256 impulsów? I tutaj trzeba zastosować metodę załadowania wartości początkowej. Jak to robimy? Po prostu wpisujemy tam wartość 256-125, czyli 131. Na koniec zostało nam jakieś takie 125, a powinno być 1. Niestety bardziej się nie da podzielić. Trzeba stworzyć jeszcze jeden własny dzielnik w funkcji obsługi przepełnienia timera. Taki licznik to po prostu zmienna, którą zwiększamy o 1 za każdym przepełnieniem licznika 0. A kiedy osiągnie wartość 125 w tedy wiemy, że mamy odmierzoną idealnie 1 s. Na początek może się to wydawać bardzo zawiłe, ale gdy przeliczymy to kilka razy sami dla różnych wartości staje się to łatwe.

 

Przykład

Teraz przedstawię program, który będzie co 1s zmieniał stad diod. W tym celu skorzystam z obliczeń z poprzedniego przykładu. Schemat będzie taki sam jak ten w przykładzie z poprzedniej części kursu.

 

#define F_CPU 1000000 //ustawienie oscylatora na 1MHz

#include <avr/io.h> //dołączenie podstawowej biblioteki

#include <avr/interrupt.h> //dołączenie biblioteki z przerwaniami

 

#define LED PORTC //zdefiniowanie stałych

#define PORTD2 2

 

char licznik = 0; //zmienna dla licznika programowego

 

int main()

{

DDRC = ffh; //konfiguracja portu jako wyjścia

TCCR0 |= (1<<CS00) | (1<<CS01); //ustawienie preskalera na 64

TIMSK |= 1<<TOIE0; //włączenie przerwania od przepełnienia licznika

TCNT0 = 131; //ustawienie wartości początkowej

sei(); //globalne włączenie przerwań

while() //pusta pętla

{

 

}

}

 

SIGNAL(SIG_OVERFLOW0) //początek funkcji obsługi przerwania

{

licznik++; //zwiększenie o 1

if(licznik > 125) //sprawdzamy, czy nie minęło już 125 przepełnień

{

PORTC = ~PORTC; //zmiana stanu na porcie c

licznik = 0; //wyzerowanie licznika

}

TCNT0 = 131; //ustawienie wartości początkowej

}

 

Program ten nie był sprawdzany, więc nie gwarantuje działania w pełni. Komentarze chyba mówią wystarczająco dokładnie o tym co będzie robił kontroler.

Wydaje mi się, że to już wszystkie podstawowe zagadnienia związane z licznikiem 0 w atmega8. W następnej części omówię licznik/czasomierz 1, którego możliwości już są o wiele większe, za czym też idzie stopień skomplikowania układu.

Leave a comment

7 Comments

  1. zamiast rejestru: TMISK -> TIMSK

    Odpowiedz
  2. PeeR

     /  4 listopada 2011

    „Następnie mamy rejestr TCNT0 i w nim dzielimy przez 125, czyli co 125 impulsów będziemy mieli przepełnienie.”

    A po co się dzieli przez 125? I dlaczego właśnie 125?

    Odpowiedz
    • Te 125 to wartość liczbowa, która określa przez ile ma być podzielona częstotliwość idąca z preskalera. Jest to wartość wyliczona na potrzeby przykładu, tak aby po wszystkich etapach dzielenia częstotliwości uzyskać częstotliwość o okresie 1s.

      Odpowiedz
  3. janek

     /  6 stycznia 2012

    fajnie że sie starasz ale może napisz jakis działający program

    Odpowiedz
  4. TCNT0 = 131; /ustawienie wartości początkowej zmień na TCNT0 = 131; //ustawienie wartości początkowej

    będzie lepiej ;)

    Odpowiedz
  5. ogólnie dobra robota należy sie pochwała!! oby tak dalej i więcej przykładów

    Odpowiedz

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

*


*

Możesz użyć następujących tagów oraz atrybutów HTML-a: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>