Technika cyfrowa, robotyka – mikrokontrolery AVR – liczniki/timery

Na dzisiejszych zajęciach poznamy kolejny element mikrokontrolera – licznik/timer. Ponieważ jest to temat dosyć rozległy i dla początkujących raczej trudny, ograniczymy się do podstawowych informacji o licznikach, poszerzając wiadomości na kolejnych zajęciach.

Licznik lub timer (czytaj: tajmer) jest rejestrem, który potrafi zliczać impulsy zegarowe. Z licznikami spotkaliśmy się już wcześniej. Mikrokontroler ATTINY13 zawiera jeden taki licznik, który ma 8 bitów pojemności. Dostęp do zawartości licznika odbywa się poprzez rejestr o nazwie TCNT0 (ang. Timer CouNTer 0). Zapis do rejestru TCNT0 powoduje odpowiednią zmianę zawartości licznika. Odczyt rejestru TCNT0 udostępnia aktualną zawartość licznika (czyli wartość, do której licznik doliczył).

Ponieważ licznik ten jest ośmiobitowy, to zlicza w zakresie od 0 do 255. W normalnym trybie pracy licznik przewija się na 0 po osiągnięciu wartości 255.

Oprócz rejestru TCNT0 mikrokontroler posiada kilka dodatkowych rejestrów, które sterują sposobem pracy tego licznika. Jednym z nich jest rejestr o nazwie TCCR0B (ang. Timer Counter Control Register 0 B). Poszczególne bity tego rejestru są następujące:

 

bit: 7 6 5 4 3 2 1 0
nazwa: FOC0A FOC0B WGM02 CS02 CS01 CS00

 

Dzisiaj będziemy wykorzystywać tylko trzy ostatnie bity oznaczone jako CS00, CS01 i CS02 (funkcje pozostałych bitów opiszemy w odpowiednim czasie w dalszej części kursu). Bity te określają źródło impulsów, które będą zliczane przez licznik – CS = Clock Select, czyli wybór zegara. Trzy bity dają 8 różnych możliwości. W poniższej tabelce termin FCPU oznacza częstotliwość impulsów zegarowych, które taktują mikrokontroler. Zwykle jest to 1MHz, lecz częstotliwość ta może się zmienić przez odpowiednie skonfigurowanie mikrokontrolera (teraz nie zaprzątaj sobie tym głowy i przyjmij po prostu, że twój ATTINY13 jest taktowany zegarem o częstotliwości około 1MHz). Sygnał zegarowy o częstotliwości FCPU trafia do tzw. prescalera, czyli dzielnika częstotliwości, który dzieli ją przez 8, 64, 256 i 1024. Wyjście prescalera może stać się źródłem impulsów zegarowych dla licznika.

 

CS02 CS01 CS00 Opis
0 0 0 Brak impulsów zegarowych, licznik jest zatrzymany.
0 0 1 Licznik zlicza impulsy o częstotliwości FCPU (1000000 Hz).
0 1 0 Licznik zlicza impulsy o częstotliwości FCPU/8 (125000 Hz)
0 1 1 Licznik zlicza impulsy o częstotliwości FCPU/64 (15625 Hz)
1 0 0 Licznik zlicza impulsy o częstotliwości FCPU/256 (3906,25 Hz)
1 0 1 Licznik zlicza impulsy o częstotliwości FCPU/1024 (976,5625 Hz)
1 1 0 Licznik zlicza impulsy z linii PB2 pracującej jako wejście T0. Zliczenie następuje przy opadającym zboczu impulsu.
1 1 1 Licznik zlicza impulsy z linii PB2 pracującej jako wejście T0. Zliczenie następuje przy narastającym zboczu impulsu.

 

Ustawiając odpowiednio bity CS00...CS02 określamy szybkość pracy licznika. Ponieważ zlicza on niezależnie od procesora w mikrokontrolerze, wykorzystuje się go często do precyzyjnego odmierzania czasu (stąd pochodzi angielska nazwa timer, czyli licznik czasu, czasomierz). Możemy również zliczać impulsy zewnętrzne. W tym wypadku sygnał należy doprowadzić do linii PB2 i ustawić ją jako wejście danych.

 

Ćwiczenie nr 1

Utworzymy prosty układ, który będzie nam pokazywał stan najstarszego bitu licznika. Licznik ustawimy, tak aby zliczał impulsy zegarowe z częstotliwością FCPU/1024. Najstarszy bit będzie się zmieniał z częstotliwością FCPU/1024/256 = 3,814697265625Hz, czyli około 4 razy na sekundę. Licznik będzie zliczał w kółko od 0 do 255.

 

obrazek

  obrazek   
Sygnały programujące
na mikrokontrolerze

 

obrazek
Sygnały programujące
na wtyczce
programatora

 

Spis elementów:

 

Element Ilość Opis
programator ISP 1 do zasilania i programowania mikrokontrolera
płytka stykowa + kable 1 do montażu układu
ATTINY13 1 mikrokontroler
opornik 220Ω/0,125W 1 –(                )–  do ograniczania napięcia i prądu diody LED
kondensator 100nF 1 do eliminacji zakłóceń (nie musisz go koniecznie stosować, lecz jest to zalecane)
czerwona dioda LED 1 do sygnalizacji stanu wysokiego na wyjściu PB0

 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/*
 * ATTINY13_0012.c
 *
 * Created: 2014-06-09 19:35:00
 *  Author: Jerzy Wałaszek
 */ 

#include <avr/io.h>

int main(void)
{
    DDRB = (1<<PB0);              // Ustawiamy linię PB0 jako wyjście
    TCCR0B = (1<<CS02)|(1<<CS00); // Uruchamiamy licznik z częstotliwością FCPU/1024    
    while(1) PORTB = (1<<PB5) | (TCNT0 >> 7);
}

 

Ćwiczenie nr 2

W tym projekcie pokażemy, jak zliczać impulsy zewnętrzne. Utworzymy prosty generator impulsów, którego wyjście będzie połączone z linią PB2 mikrokontrolera ATTINY13. Linia ta może służyć jako źródło impulsów zegarowych, które będzie zliczał licznik. W tej roli PB2 nosi nazwę T0. Na wyjście wyprowadzimy cztery najmłodsze bity licznika poprzez linie PB0, PB1, PB3 i PB4.

 

obrazek

 

SN7404LS

obrazek

  obrazek   
Sygnały programujące
na mikrokontrolerze

 

obrazek
Sygnały programujące
na wtyczce
programatora

 

Spis elementów:

 

Element Ilość Opis
programator ISP 1 do zasilania i programowania mikrokontrolera
płytka stykowa + kable 1 do montażu układu
ATTINY13 1 mikrokontroler
SN7404LS 1 6 inwerterów NOT do konstrukcji generatora
opornik 470Ω/0,125W 1 –(                )–  do ograniczania napięcia i prądu diody LED na wyjściu generatora
opornik 220Ω/0,125W 4 –(                )–  do ograniczania napięcia i prądu diody LED
opornik 1kΩ/0,125W 1 –(                )–  do generatora impulsów
kondensator 100nF 1 do eliminacji zakłóceń (nie musisz go koniecznie stosować, lecz jest to zalecane)
kondensator 100µF/6,3V 1 do generatora impulsów
czerwona dioda LED 5 do sygnalizacji stanu wysokiego na wyjściu generatora oraz wyjściach mikrokontrolera
mikroprzełącznik 1 do przekazywania impulsów z generatora na wejście T0

 

obrazek

 
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
/*
 * ATTINY13_0013.c
 *
 * Created: 2014-06-10 07:36:00
 *  Author: Jerzy Wałaszek
 */ 

#include <avr/io.h>

int main(void)
{
    DDRB = (1<<PB4)|(1<<PB3)|(1<<PB1)|(1<<PB0);  // Ustawiamy linie wyjściowe
    PORTB = (1<<PB5)|(1<<PB2);  // Podłączamy oporniki podciągające i zerujemy wyjścia
    TCNT0 = 0;                  // Zerujemy licznik
    TCCR0B = (1<<CS02)|(1<<CS01)|(1<<CS00); // Licznik zlicza impulsy z PB2 przy narastającym zboczu    

    while(1) PORTB = (1<<PB5)|(1<<PB2)|(TCNT0 & 0b0011)|((TCNT0 & 0b1100)<<1); // Licznik na wyjście  
}

 

W generatorze zmień kondensator 100µF na 1000µF, co spowoduje zmniejszenie częstotliwości generowanych impulsów. Następnie zwróć uwagę, że stan licznika zmienia się wtedy, gdy dioda na wyjściu generatora zaczyna świecić, czyli gdy pojawia się na tym wyjściu stan logiczny 1. Licznik zlicza impulsy zewnętrzne przy narastającym zboczu (zmianie z 0 na 1) sygnału na wejściu T0 (PB2). Zmień teraz wiersz 15 na:

 

TCCR0B = (1<<CS02)|(1<<CS01);

 

Stan licznika zmienia się, gdy dioda LED gaśnie, czyli stan na wyjściu generatora zmienia się z 0 na 1. Licznik zlicza impulsy generatora przy opadającym zboczu sygnału.

 

Ćwiczenie nr 3

Mikrokontroler ATTINY13 da się również wykorzystać do zabawy. Stworzymy prostą kostkę do gry, która po naciśnięciu przycisku będzie nam wyświetlała oczka od 1 do 6.

 

obrazek

 

Na pierwszy rzut oka może się wydawać, że projektu tego nie da się zrealizować, ponieważ do wyświetlania wyniku potrzebujemy 7 diod oraz 1 przycisku, co angażuje 8 linii portów, a my mamy do dyspozycji jedynie 5 linii PB0...PB4 (linię PB5/RESET zostawiamy w spokoju). Jeśli jednak dokładnie przyjrzysz się układom oczek, to zauważysz, że poszczególne oczka nie są niezależne, lecz tworzą cztery grupy:

 

obrazek Oczka

1
2
3
4
5
6

Grupy

G1
G2
G1 G2
G2 G3
G1 G2 G3
G2 G3 G4

 

Jeśli przyjmiemy prąd diody na poziomie 5 mA, to do jednego portu możemy podłączyć równolegle dwie diody LED, które będą zapalane stanem wysokim. Rozważania te prowadzą do następującego schematu:

 

obrazek

  obrazek   
Sygnały programujące
na mikrokontrolerze

 

obrazek
Sygnały programujące
na wtyczce
programatora

 

Ponieważ kostka z diod LED jest dosyć trudna do odtworzenia na płytce stykowej, zaprojektowałem odpowiednią płytkę drukowaną:

 

obrazek

 

Na powyższym rysunku jest widok płytki od strony ścieżek. Płytkę taką można utworzyć w dowolnym edytorze PCB, których mnóstwo znajdziesz w Internecie. Ja korzystałem z bardzo prostego edytora PCB Artist, ale może być dowolny inny. Kolorem zielonym zaznaczony jest obrys płytki. Kolor czerwony oznacza ścieżki. Kolor niebieski to połączenia przewodowe po drugiej stronie płytki. Kolor biały to punkty lutownicze elementów. Kolor żółty oznacza otwory przelotowe, w których lutujemy przewody (oznaczone na niebiesko) łączące ścieżki po drugiej stronie płytki.

Tworzenie płytek to temat na osobny kurs elektroniki. Poradniki możesz znaleźć w sieci. Ja stosuję następującą prostą metodę (wiem, że są lepsze, ale zwykle wymagają one wprawy lub sprzętu), która jeszcze mnie nie zawiodła:

 

Najpierw przygotowuję projekt płytki w edytorze PCB. Następnie drukuję na zwykłej kartce papieru same ścieżki oraz zarys płytki (wydruk musi być od strony ścieżek). Da się to zrobić bezpośrednio z edytora PCB.

 

obrazek

 

Przygotowuję odpowiednią płytkę jednostronnie miedziowaną. Płytkę laminatową da się łatwo przyciąć nawet zwykłym nożem do tapet. Wystarczy kilkakrotnie przejechać nim wzdłuż linii cięcia po jednej i po drugiej stronie płytki, a następnie płytkę przełamujemy.

 

obrazek

 

Rysunek płytki wycinam  z zapasem około 2 cm i zawijam wokół płytki. Warstwa miedzi ma być od strony wydruku:

 

obrazek obrazek obrazek

 

Za pomocą punktaka (coś z ostrym końcem) delikatnie nakłuwam każdy otwór wydrukowany na papierze. Powoduje to przeniesienie punktów otworów z papieru na warstwę miedzi. Operację tę należy przeprowadzać ostrożnie i z wyczuciem. Zbyt silne nakłucie spowoduje deformację warstwy miedzi wokół przyszłego otworu, a w konsekwencji odpadnięcie ścieżki, czego raczej należy unikać. Wytłoczone w miedzi punkty ułatwią wiercenie.

Otwory wiercimy małą wiertarką elektryczną (za około 30 zł do kupienia w każdym sklepie dla elektroników) i wiertłem 0,8mm. Należy zwrócić uwagę, aby wiertło było zawsze prowadzone prostopadle do powierzchni płytki. I nie rób tego na stole gościnnym, załatw sobie jakąś podkładkę, najlepiej deseczkę o grubości 2cm.

 

obrazek obrazek

 

Po wywierceniu otworków papier usuwam. Płytkę myję bardzo dokładnie środkami do mycia naczyń (np. płyn Ludwik) i po wyschnięciu szlifuję bardzo drobnym papierem ściernym. Operacja ta ma na celu usunięcie z powierzchni płytki wszelkich zanieczyszczeń, tlenków i zadziorów po wierceniu.

Teraz najtrudniejsza część procesu. Za pomocą pisaka edding 141 F (taki zwykły, czarny, niezmywalny, 0,6mm) najpierw obrysowuję delikatnie wszystkie otwory kółeczkami, tak aby otwór znalazł się w środku. Kółeczka posłużą za punkty lutownicze.

 

obrazek obrazek

 

Następnie kółeczka łączę liniami wg projektu płytki. Linie rysuję zwykle grubsze niż wynika to z projektu. Po prostu tak jest pewniej. Nie żałuję pisaka i każdą linię poprawiam po wyschnięciu pierwszej kreski. Nie rób tego, gdy tusz jest jeszcze mokry, ponieważ końcówka pisaka może go zdzierać i zostaną nieładne przerwy wewnątrz ścieżek. Najlepiej zrób rysunek, poczekaj 10-15 minut i popraw go jeszcze raz. W trakcie rysowania unikam dotykania płytki palcami. Płytkę najlepiej jest sobie jakoś umocować, co na pewno wpłynie na precyzję rysunku ścieżek. Jeśli na tym etapie wystąpi jakaś pomyłka, to pisak da się łatwo zmyć rozpuszczalnikiem nitro. Oczywiście rysowanie należy wtedy rozpocząć od początku.

Kolejnym etapem jest wytrawienie płytki. W sklepie elektronicznym kupujemy małą paczuszkę nadsiarczanu sodowego B327 (niektórzy trawią płytki chlorkiem żelaza – też można, lecz chlorek bardzo wszystko brudzi, a jeśli kapnie ci na spodnie, to będziesz miał dodatkowy wywietrznik). Wystarczy ci 100g tego środka na kilka płytek. Na opakowaniu jest dokładna instrukcja użytkowania. Proszek rozpuszczamy w gorącej wodzie o temperaturze około 50 stopni. Należy to zrobić w plastikowej kuwetce, aby rodzic żeński nie zrobił nam później awantury. Taką małą kuwetkę kupisz za grosze w supermarkecie. Trawienie rób w miejscu, gdzie nic nie uszkodzisz (kuchnia, balkon, piwnica, itp. zapewnij sobie dopływ powietrza).

 

obrazek

 

Po rozpuszczeniu nadsiarczanu sodowego w wodzie wkładamy do kuwety płytkę i czekamy, aż zostanie wytrawiona na niej warstwa miedzi. Trwa to około 10...20min (w zależności od stężenia środka). Od czasu do czasu poruszaj delikatnie kuwetą. Płyn powinien zmieniać barwę na lekko niebieską, co jest dobrym objawem. Na powierzchni miedzi możesz zaobserwować małe bąbelki. Jeśli cię to ciekawi, to zapytaj swojego chemika o reakcję, jaka się odbywa w kuwecie.

 

obrazek obrazek obrazek

 

Gdy cała miedź zostanie wytrawiona, wyjmij płytkę z kuwety (jeśli będziesz ją zbyt długo trawił, to mogą ci poznikać niektóre ścieżki), przepłukaj ją dokładnie w letniej wodzie i wysusz. Płyn trawiący wylej do ubikacji i spuść wodę. Kuwetę również umyj w letniej wodzie. Jeszcze ci się przyda.

Teraz należy dokładnie przyjrzeć się ścieżkom, czy nie mają przerw lub zwarć (w razie wątpliwości posłuż się miernikiem nastawionym na mierzenie oporności). Zwarte ścieżki łatwo rozłączysz małym skalpelem. Ubytki ścieżek trzeba będzie naprawić w czasie lutowania elementów za pomocą małego drucika. Pisak zmywasz łatwo rozpuszczalnikiem nitro.

 

obrazek

 

Ja dodatkowo pokrywam ścieżki roztworem kalafonii w denaturacie. Tworzy to małą warstewkę ochronną i ułatwia później lutowanie. Roztwór taki bardzo łatwo przygotujesz: odlej do małego słoiczka trochę denaturatu i wrzuć do niego kawałek kalafonii. Po kilku minutach kalafonia się rozpuści.

 

obrazek

 

Pozostaje nam jedynie wlutować w płytkę elementy. Przewody połączeniowe do płytki stykowej powinny być zakończone odpowiednimi wtykami. Gotowe przewody możesz kupić w sklepie elektronicznym lub na aukcji w serwisie Allegro.pl. Pamiętaj jednak, aby nie zostawiać ich "wiszących" na wlutowanym w płytkę drucie, ponieważ po kilku wygięciach drucik się po prostu złamie. Ja zawsze pokrywam miejsce styku przewodu z płytką klejem butapren, który doskonale się do tego nadaje – nie żałuj kleju, daj kilka warstw w odstępach co godzinę. Przemyśl zastosowanie obudowy na kostkę – wystarczy małe pudełeczko (najlepiej czarne) z otworkami na diody LED i przewody. Takie pudełko zabezpieczy płytkę przed przypadkowym kontaktem z metalowymi przedmiotami (zwarcie) oraz ukryje niedoróbki.

Płytkę zastosujemy w projekcie kostki elektronicznej. Kolejność połączeń jest następująca (od strony elementów):

 

GND PB1 PB2 PB3 PB4

 

obrazek

 

 
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/*
 * ATTINY13_0014.c
 *
 * Created: 2014-06-15 10:14:00
 *  Author: Jerzy Wałaszek
 */

#include <avr/io.h>
#include <util/delay.h>

int main(void)
{

    unsigned char cube[] = {0b100011,     // 1 oczko
                            0b100101,     // 2 oczka
                            0b100111,     // 3 oczka
                            0b101101,     // 4 oczka
                            0b101111,     // 5 oczek
                            0b111101,     // 6 oczek
                            0b100101,     // Obrót 1
                            0b110001,     // Obrót 2
                            0b101001,     // Obrót 3
                            0b100011};    // obrót 4
    unsigned char i,v,c;

    DDRB = (1<<PB4)|(1<<PB3)|(1<<PB2)|(1<<PB1);   // Ustawiamy linie wyjścia i wejścia
    PORTB = (1<<PB5)|(1<<PB0);                    // Do linii PB5 (RESET) i PB0 podpinamy oporniki podciągające
    TCCR0B = 5;                                   // Licznik będzie zliczał z częstotliwością około 1kHz

    while (1)
    {
        while(PINB & (1<<PB0)) ;      // Czekamy na naciśnięcie klawisza
        v = TCNT0 % 6;                // Zapamiętujemy stan licznika, będzie to wartość przypadkowa
        i = 6;                        // Dopóki przycisk naciśnięty, obracamy kostką
        do
        {
            PORTB = cube[i++];
            if(i == 10) i = 6;
            _delay_ms(100);
        } while(!(PINB & (1<<PB0)));

        // wykonujemy jeszcze 10 dodatkowych obrotów kostką

        for(c = 0; c < 10; c++)
        {
            PORTB = cube[i++];
            if(i == 10) i = 6;
            _delay_ms(100);
        }

        PORTB = 0b100001;             // Gasimy wszystkie diody

        // Przez 3 sekundy wyświetlamy oczka - istnieje możliwość przerwania przyciskiem

        for(c= 0; (c < 30) && (PINB & (1<<PB0)); c++)
        {
            PORTB = cube[v];              // Wyświetlamy oczka kostki
            _delay_ms(100);
            PORTB = 0b100001;             // Gasimy wszystkie diody
        }

        // Wykonujemy 10 mrugnięć - jeśli przycisk wciśnięty, operacja przerwana

        for(c= 0; (c < 10) && (PINB & (1<<PB0)); c++)
        {
            PORTB = cube[v];
            _delay_ms(200);
            PORTB = 0b100001;         // Gasimy wszystkie diody
            _delay_ms(100);
        }
    }
}

 

Program działa następująco:

 

Po ustawieniu portu B włączony zostaje licznik, który zlicza impulsy zegara podzielone przez 1024. Daje to częstotliwość nieco mniej niż 1kHz. Licznik służy nam do generowania wyniku rzutu kostką.

Wchodzimy w pętlę główną. Na początku tej pętli czekamy na naciśnięcie przycisku, testując stan linii PB0. Jeśli linia ta przyjmie 0, to pętla testująca zostaje przerwana. W tym momencie odczytujemy stan licznika, dzielimy go przez 6 i w zmiennej v zapamiętujemy resztę z tego dzielenia. Reszta ma wartość od 0 do 5 i określa liczbę oczek kostki - 1. Wartość ta posłuży do znalezienia w tabeli cube odpowiedniego wpisu do PORTB. Najpierw jednak wchodzimy w drugą pętlę, która symuluje obroty kostki. Pętla ta jest wykonywana dotąd, aż zostanie zwolniony przycisk. Wtedy wchodzimy w następną pętlę, która wykonuje jeszcze dodatkowe 10 obrotów kostki. Po zakończeniu tej pętli gasimy zapobiegawczo wszystkie diody, po czym wchodzimy w kolejną pętlę, która tym razem wyświetla wylosowane oczka. Petla ta wykonuje się przez 3 sekundy lub krócej, jeśli w międzyczasie zostanie wciśnięty przycisk. Gdy ta pętla się zakończy, mikrokontroler wykonuje ostatnią pętlę, która mruga zapalonymi diodami LED, po czym gasi je (dla oszczędności, ekologia rulez!). Ostatnia pętla również jest przerywana przy naciśniętym przycisku – te przerwania zwiększają dynamikę działania całego urządzenia. Inaczej użytkownik musiałby czekać z naciśnięciem klawisza na koniec całego cyklu, a tak może rozpocząć nowy cykl praktycznie w dowolnym momencie. Gdy ostatnia pętla zakończy działanie, następuje powrót na początek pętli głównej i ponowne oczekiwanie na naciśniecie przycisku – zwróć jednak uwagę, że jeśli przycisk jest naciśnięty, to nowy cykl rozpoczyna się od razu.

 

Ponieważ zbliża się koniec roku szkolnego, kończymy zajęcia na kole informatycznym. Od przyszłego roku będziemy kontynuować kurs programowania mikrokontrolerów ATMEL. Zaczniemy do nich podłączać różne czujniki oraz układy wykonawcze i docelowo zbudujemy małego, ruchomego robota, który będzie się inteligentnie poruszał w środowisku.

 


   I Liceum Ogólnokształcące   
im. Kazimierza Brodzińskiego
w Tarnowie

©2024 mgr Jerzy Wałaszek

Dokument ten rozpowszechniany jest zgodnie z zasadami licencji
GNU Free Documentation License.

Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl

W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe