Licznik


Tematy pokrewne   Podrozdziały
(w budowie)
  Rejestr licznika
APP004 – kostka do gry
Podsumowanie

 

 

Rejestr licznika

 
   

Oprócz samego mikroprocesora kontroler wyposażony jest w dodatkowe układy, które ułatwiają sterowanie oraz tworzenie różnych aplikacji. Licznik jest układem zliczającym impulsy podawane na jego wejście zegarowe. Z licznikiem w postaci układu scalonego spotkaliśmy się już w rozdziale o generatorach (jeśli go pominąłeś, to wróć i przeczytaj dokładnie podane tam informacje). Wbrew pozorom licznik jest bardzo pożytecznym urządzeniem i ma mnóstwo zastosowań. Mikrokontroler ATTINY13 posiada w swoim wnętrzu tylko jeden licznik 8-mio bitowy (jego starsi bracia mają tych liczników więcej i mogą one być 16-to bitowe). Licznik czasem nazywamy timerem, co po angielsku oznacza układ odmierzający czas.

W najprostszym przypadku dostęp do licznika uzyskujemy za pomocą rejestru sterującego TCCR0B (ang. Timer Counter Control Register 0 B) oraz rejestru licznika TCNT0 (ang. Timer CouNTer 0). Istnieją jeszcze dodatkowe rejestry. Omówimy je później.

Stan licznika odczytujemy lub zapisujemy do rejestru TCNT0. Rejestr sterujący TCCR0B określa sposób pracy licznika:

bit 7 6 5 4 3 2 1 0
nazwa FOC0A FOC0B WGM02 CS02 CS01 CS00
R/W W W R R R/W R/W R/W R/W
stan 0 0 0 0 0 0 0 0

Rejestr sterowania licznikiem TCCR0B

Na początek omówimy znaczenie trzech ostatnich bitów rejestru sterującego. Bity CS02, CS01 i CS00 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 z wewnętrznego generatora). 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 CS02, CS01 i 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.

Wykonajmy kilka prostych ćwiczeń. Podłącz do płytki bazowej ATTINY13 płytkę aplikacyjną APP001. Ustaw zworki w położeniu górnym, aby do portów mikrokontrolera były podłączone wszystkie diody LED (jeśli nie masz płytki APP001, to zbuduj ją na płytce stykowej i połącz z płytka bazową za pomocą przewodów). Uruchom Eclipse i utwórz nowy projekt AVR dla ATTINY13.

Licznik TCNT0

/*
 * main.c
 *
 *  Created on: 31 paź 2015
 *      Author: Jerzy
 *  Opis:
 *  Program wyświetla na diodach D0...D4 płytki APP001
 *  stan najstarszych bitów rejestru licznika
 *  Konfiguracja jumperów:
 *  J0: góra
 *  J1: góra
 */

#include <avr/io.h>

int main(void)
{

    DDRB = 0b11111; // Wszystkie linie jako wyjścia
    TCCR0B = 0b101; // 1MHz : 1024 -> do licznika

    while(1)
    {
        PORTB = TCNT0 >> 3; // Pięć najstarszych bitów TCNT0 na wyjście
    }
}

 

Powyższy program uruchamia licznik w trybie zliczania impulsów zegarowych z podziałem przez 1024. Następnie w pętli głównej mikrokontroler pobiera zawartość rejestru licznika TCNT0, przesuwa ją o 3 bity w prawo i wpisuje do rejestru PORTB. Przesunięcie o trzy bity powoduje, że na diody LED płytki APP001 zostają podane stany pięciu najstarszych bitów licznika. Gdy uruchomisz ten program, to zobaczysz, że tylko trzy najstarsze diody mrugają wystarczająco wolno, aby oko mogło to zobaczyć. Policzmy:

 

1000000 Hz : 1024 = 976,5625 Hz

 

Tyle wynosi częstotliwość impulsów zegarowych, które są podawane na wejście licznika i następnie zliczane przez niego. Z jaką częstotliwością mrugają poszczególne diody LED? Policzmy:

 

bit TCNT0 Częstotliwość Uwagi
0 488,28125 Hz  
1 244,140625 Hz  
2 122,0703125 Hz  
3 61,03515625 Hz D0
4 30,517578125 Hz D1
5 15,2587890625 Hz D2
6 7,62939453125 Hz D3
7 3,814697265625 Hz D4

 

Jak widzisz, ostatnia dioda D4 mruga z częstotliwością ok 4Hz, czyli cztery razy na sekundę. Każda niższa dioda mruga z częstotliwością dwa razy większą od swojej poprzedniczki. Dla D1 jest to częstotliwość ponad 30 Hz, której oko ludzkie już nie rozpoznaje – na tej "wadzie" wzroku człowieka bazuje kino oraz telewizja: jeśli obrazy będą się zmieniały około 30 razy na sekundę, to oko zarejestruje to jako obraz ciągły i ruchomy.

Kolejny program wykorzystuje licznik do odmierzania czasu. Licznik potrafi pracować w wielu różnych trybach. Najprostszym z nich jest tzw. tryb normalny (ang. normal mode). Tryb ten jest automatycznie ustawiany w czasie startu mikrokontrolera lub po resecie. W trybie normalnym licznik zlicza impulsy aż do osiągnięcia maksymalnej wartości 255 (binarnie 0b11111111). Następny impuls zegarowy powoduje wyzerowanie licznika (binarnie 0b00000000) oraz ustawienie znacznika TOV0 (ang. Timer/Counter Overflow Flag – znacznik przepełnienia licznika 0) w rejestrze TIFR0 (ang. Timer/Counter 0 Interrupt Flag Register – rejestr znaczników przerwań, o przerwaniach piszemy w kolejnym rozdziale).

bit 7 6 5 4 3 2 1 0
nazwa OCF0B OCF0A TOV0
R/W R R R R R/W R/W R/W R
stan 0 0 0 0 0 0 0 0

Rejestr TIFR0

Uwaga: znacznik TOV0 zerujemy zapisując do bitu nr 1 wartość 1, nie 0! Podłącz do płytki bazowej płytkę APP001 i ustaw zworki w położenie górne. Skompiluj w Eclipse poniższy program i prześlij go do kontrolera.

Odmierzanie czasu – tryb normalny

/*
 * main.c
 *
 *  Created on: 31 paź 2015
 *      Author: Jerzy
 *  Opis:
 *  Program wykorzystuje licznik w trybie normalnym do odmierzania czasu.
 *  Mruga diodą D0 z częstotliwością 1Hz, czyli co sekundę.
 *  Konfiguracja jumperów:
 *  J0: góra
 *  J1: góra
 */

#include <avr/io.h>
#define VCNT 11

// Czeka na przewinięcie licznika
//-------------------------------
void czekaj()
{
    while(!(TIFR0 & (1<<TOV0))); // Czekamy na znacznik przepełnienia TOV0
    TIFR0 |= (1<<TOV0);          // Zerujemy znacznik TOV0
    TCNT0 = VCNT;                // Inicjujemy licznik
}

int main(void)
{
    DDRB  = 0b1;    // PB0 jako wyjście
    PORTB = 0;      // Zerujemy PB0
    TCNT0 = VCNT;   // Wartość początkowa dla licznika
    TCCR0B = 0b101; // Licznik sterowany sygnałem 976,5625 Hz

    while(1)
    {
        czekaj();   // czekamy na przewinięcie licznika
        czekaj();
        PINB = 1;   // zmieniamy stan diody LED na przeciwny
    }
}

Jak to działa? Źródłem impulsów zegarowych dla licznika jest tutaj preskaler ustawiony na dzielenie częstotliwości 1MHz przez 1024. Daje nam to częstotliwość około 976 Hz. Czyli w ciągu każdej sekundy do licznika trafia nieco ponad 976 impulsów zegara. Pojemność licznika jest zbyt mała, aby zliczyć tyle impulsów, ponieważ jest to licznik 8-mio bitowy. Jednakże załóżmy, że nie chcemy zliczać wszystkich impulsów w ciągu jednej sekundy, lecz jedynie 1/4 z nich. Daje nam to 976 / 4 = 244 impulsy. Tyle licznik może zliczyć. Aby się wyzerował po zliczeniu 244 impulsów (co odpowiada ok. 1/4 sekundy), musi mieć wartość początkową 11 (11 + 244 = 255 i następny impuls zeruje licznik). To dlatego wpisujemy tę wartość do rejestru licznika na początku programu. Następnie wywołujemy funkcję czekaj(), która w pętli sprawdza stan bitu TOV0 rejestru TIFR0. Gdy licznik się wyzeruje, bit TOV0 zostanie ustawiony na 1 i pętla while zostanie przerwana. Wtedy zerujemy bit TOV0 i do licznika ponownie wpisujemy 11. W programie musimy wywołać funkcję czekaj() dwukrotnie, aby uzyskać opóźnienie 1/2 sekundy.

Operując odpowiednio ustawieniami preskalera oraz wartościami początkowymi licznika, możemy otrzymać różne czasy opóźnień. Lepsze sposoby odmierzania czasu poznamy w następnym rozdziale, gdy będziemy omawiać przerwania.

W programie wykorzystujemy nowy element języka C – funkcję. Funkcja jest fragmentem programu, który możemy wielokrotnie wywoływać z różnych miejsc. Przed pierwszym użyciem funkcja musi zostać zdefiniowana. Definicja wygląda następująco:

 

typ nazwa(parametry)
{
    treść
}

typ – określa rodzaj informacji zwracanej przez funkcję. W naszym przypadku użyliśmy typu void (ang. próżny, pusty), ponieważ funkcja czekaj nic nie zwraca. Jej przeznaczeniem jest po prostu czekanie aż licznik się przepełni, wyzerowanie znacznika TOV0 oraz zainicjowanie licznika wartością początkową. Funkcje zwracające dane opiszemy później.

nazwa – pozwala odwołać się do funkcji z programu. Tworzymy ją wg zasad nazw w C (w skład mogą wchodzić tylko litery łacińskie małe i duże, cyfry oraz znak _. Litery małe i duże są rozróżniane. Nazwa nie może być identyczna ze słowem kluczowym i nie może rozpoczynać się od cyfry).

parametry – pozwalają przekazać do funkcji dane. Zajmiemy się tym później. Nasza funkcja nie wymaga przekazywania danych

 

Inną metodą odmierzania czasu jest wykorzystanie trybu CTC (ang. Clear Timer on Compare match – Zerowanie Licznika przy zgodności porównania). Tryb pracy licznika (inny niż normalny) ustawiamy w rejestrze TCCR0A (ang. Timer/Counter Control Register A).

bit 7 6 5 4 3 2 1 0
nazwa COM0A1 COM0A0 COM0B1 COM0B0 WGM01 WGM00
R/W R/W R/W R/W R/W R R R/W R/W
stan 0 0 0 0 0 0 0 0

Rejestr TCCR0A

Pierwsze cztery bity określają sposób pracy linii PB0 (zwanej tutaj OC0A) i PB1 (zwanej tutaj OC0B). Omówimy je później.

Ostatnie dwa bity (wraz z bitem WGM02 z rejestru TCCR0B) określają tryb pracy modułu licznika. Aby włączyć tryb CTC, należy bit WGM02 wyzerować (w czasie startu lub po resecie bit ten jest automatycznie zerowany), a bity WGM01 i WGM00 ustawić odpowiednio na: 1 i 0. Tryb CTC wykorzystuje rejestr OCR0A (ang. Output Compare Register – rejestr porównawczy wyjścia). Wartość umieszczona w rejestrze OCR0A jest ciągle porównywana ze stanem licznika. Gdy licznik osiągnie tę wartość, to zostaje wyzerowany, a w rejestrze TIFR0 będzie ustawiony na 1 znacznik OCF0A. W ten sposób program wykrywa fakt wyzerowania licznika.

Poniższy program wykorzystuje tryb CTC do odmierzania czasu. Działa identycznie jak poprzedni (prześledź komentarze).

Odmierzanie czasu – tryb CTC

/*
 * main.c
 *
 *  Created on: 31 paź 2015
 *      Author: Jerzy
 *  Opis:
 *  Program wykorzystuje licznik w trybie CTC do odmierzania czasu.
 *  Mruga diodą D0 z częstotliwością 1Hz, czyli co sekundę.
 *  Konfiguracja jumperów:
 *  J0: góra
 *  J1: góra
 */

#include <avr/io.h>

// Czeka na przewinięcie licznika
//-------------------------------
void czekaj()
{
    while(!(TIFR0 & (1<<OCF0A))); // Czekamy na znacznik zerowania OCF0A
    TIFR0 |= (1<<OCF0A);          // Zerujemy znacznik OCF0A, zapisując do niego 1
}

int main(void)
{
    DDRB  = 0b1;    // PB0 jako wyjście
    PORTB = 0;      // Zerujemy PB0
    OCR0A = 244;    // Wartość, do której zlicza licznik
    TCCR0A = 0b010; // Włączamy tryb CTC
    TCCR0B = 0b101; // Licznik sterowany sygnałem 976,5625 Hz

    while(1)
    {
        czekaj();   // czekamy na przewinięcie licznika
        czekaj();
        PINB = 1;   // zmieniamy stan diody LED na przeciwny
    }
}

 

Następny program wykorzystuje licznik do losowania liczby losowej. Liczba losowa (ang. random number) jest liczbą z pewnego przedziału, lecz której wartości nie potrafimy przewidzieć, dopóki się nie pojawi. To tak jak w rzucie kostką. Dopóki nie rzucimy (zakładamy, że kostka nie jest oszukana), nie wiemy, ile wypadnie oczek.

W jaki sposób losować liczbę losową? Bardzo prosto: uruchamiamy licznik w trybie zliczania zegara FCPU/8 (normalnie 125000Hz) i czekamy na naciśnięcie klawisza. Licznik działa tak szybko, że nikt nie jest w stanie nacisnąć klawisz przy określonym stanie licznika. Gdy klawisz zostanie zwolniony, odczytujemy stan licznika. Stan ten jest naszą liczbą losową.

 

Losowanie1

/*
 * main.c
 *
 *  Created on: 31 paź 2015
 *      Author: Jerzy
 *  Opis:
 *  Program losuje za pomocą licznika TCNT0 liczbę 4 bitową,
 *  którą wyświetla na diodach D1...D4.
 *  Konfiguracja jumperów:
 *  J0: dół
 *  J1: góra
 */

#include <avr/io.h>

int main(void)
{

    DDRB   = 0b11110; // PB1...PB4 jako wyjścia, PB0 jako wejście
    PORTB  = 0b00001; // Opornik podciągający do PB0
    TCCR0B = 0b010;   // 1MHz/8 -> do licznika

    while(1)
    {
        if(!(PINB & 1)) PORTB = (TCNT0 << 1) | 1; // Losujemy
    }
}

 

Losowanie2

/*
 * main.c
 *
 *  Created on: 31 paź 2015
 *      Author: Jerzy
 *  Opis:
 *  Program losuje za pomocą licznika TCNT0 liczbę 5 bitową,
 *  którą wyświetla na diodach D0...D4.
 *  Konfiguracja jumperów:
 *  J0: dół
 *  J1: góra
 */

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

int main(void)
{

    DDRB   = 0b11111; // wszystkie linie PB jako wyjścia
    TCCR0B = 0b010;   // 1MHz -> do licznika

    while(1)
    {
    	PORTB = TCNT0;  // Stan licznika na wyjście
    	_delay_ms(178); // Czekamy pewien czas
    }
}

 

W drugim przypadku generowany ciąg liczb nie jest przypadkowy, ponieważ mikroprocesor mikrokontrolera pracuje synchronicznie z licznikiem: oba są sterowane z tego samego źródła sygnałów zegarowych. Aby się o tym przekonać, wystarczy kilka razy nacisnąć klawisz RST i obserwować początek serii.

 

Źródłem sygnału zegarowego dla licznika może być zmieniający się stan linii PB2, która w tym przypadku nosi nazwę T0. Podłącz do płytki bazowej płytkę aplikacyjną APP001 i prześlij do mikrokontrolera poniższy program:

/*
 * main.c
 *
 *  Created on: 8 paź 2015
 *      Author: Jerzy
 *  Opis:
 *  Program odczytuje stan przycisku, likwiduje ewentualne drgania,
 *  i przesyła go do bitu T0 (PB2). Na liniach PB3 i PB4 wyświetla
 *  dwa najmłodsze bity licznika.
 *  Konfiguracja jumperów:
 *  J0: dół
 *  J1: góra
 */

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

// Przesyła dwa najmłodsze bity rejestru licznika
// do bitów PB3 i PB4 portu B
//-----------------------------------------------
void stan_licznika(void)
{
    PORTB = (PORTB & 0b00111) | ((TCNT0 << 3) & 0b11000);
}

int main(void)
{

    DDRB   = 0b11110; // PB1...PB4 jako wyjścia, PB0 jako wejście
    PORTB  = 0b00101; // Opornik podciągający do PB0
    TCCR0B = 0b111;   // T0 --> sygnał zegarowy dla licznika

    while(1)
    {
        while(PINB & 1);    // czekamy na wciśnięcie przycisku
        _delay_ms(10);      // czekamy na wygaśnięcie drgań styków
        PORTB &=0b11011;    // zerujemy bit PB2
        stan_licznika();    // pokazujemy stan licznika na PB3 i PB4
        while(!(PINB & 1)); // czekamy na zwolnienie przycisku
        _delay_ms(10);      // czekamy na wygaśnięcie drgań styków
        PORTB |=0b00100;    // ustawiamy na 1 bit PB2
        stan_licznika();    // pokazujemy stan licznika na PB3 i PB4
    }
}

Program w pętli odczytuje stan linii PB0 i kopiuje go na linię PB2. Do linii PB0 podłączony jest przycisk na płytce APP001. Licznik jest tak skonfigurowany, że źródłem jego impulsów zegarowych jest linia BP2. Gdy stan tej linii zmieni się z niskiego na wysoki (co odpowiada zwolnieniu przycisku), licznik zliczy impuls i zwiększy swój stan o 1. Zobaczymy to na dwóch ostatnich diodach LED podłączonych do linii PB3 i PB4. Pojawią się tam dwa najmłodsze bity rejestru licznika TCNT0.

Zmień w programie wartość wpisywaną do rejestru TCCR0B z 0b111 na 0b110. W tej konfiguracji licznik zlicza impuls zegarowy przy przejściu z 1 na 0.

Tutaj programowo zmieniamy stan linii T0 (PB2). Jednakże linia ta może pracować jako wejście, do którego przesyłamy ciąg impulsów zegarowych z zewnętrznego źródła. W takim przypadku licznik będzie zliczał impulsy pojawiające się na linii T0.

 

 

 

APP004 – Kostka do gry

 
   
Wykorzystując licznik, zaprojektujemy kolejną płytkę aplikacyjną, która będzie symulowała kostkę do gry. Kostka będzie zawierała 7 diod LED oraz jeden przycisk. Po naciśnięciu przycisku program wylosuje liczbę od 1 do 6 i przedstawi ją w postaci oczek na kostce. Diody LED połączymy w 4 grupy sterowane oddzielnymi liniami portów:
obrazek Linia PB1
obrazek Linia PB2
obrazek Linia PB3
obrazek Linia PB4

Linię PB0 zarezerwujemy dla przycisku. Schemat płytki aplikacyjnej APP004 z kostką jest następujący:

Spis elementów

Element Ilość Opis
Złącze kątowe męskie 2 x 4 Goldpin 1 Do połączenia z APP000
Opornik 270Ω/0,125W 3 –(                )–
Opornik 470Ω/0,125W 1 –(                )–
Czerwona dioda LED 7 Oczka kostki
przycisk Omron 1 Do losowania
 
obrazek

Utwórz nowy projekt Eagle. Na schemacie wykorzystaj elementy:

frames  A5L-LOC  (ramka)

supply1   GND x 3 (masa)

pinhead  PINHD-2X4  PINHD-2X4/90 x 1 (goldpiny kątowe męskie do połączenia z płytką bazową, element obróć o 180º i odbij lustrzanie)

switch-omron  10-XX x 1 (przycisk W)

rlc   R-EU   R-EU_0204/7 x 4 (oporniki)

led   LED   LED3MM x 7 (diody LED)

Elementy rozmieść odpowiednio na schemacie, połącz przewodami, nazwij i nadaj im wartości.

obrazek

Na podstawie schematu utwórz płytkę PCB:

obrazek

Test płytki APP004

/*
 * main.c
 *
 *  Created on: 2 mar 2015
 *      Author: Jerzy
 *  Opis:
 *  Program steruje generacją oczek na kostce do gry
 */

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

int main(void)
{

    uint8_t cube[] = {0b00011,     // 1 oczko
                      0b00101,     // 2 oczka
                      0b00111,     // 3 oczka
                      0b01101,     // 4 oczka
                      0b01111,     // 5 oczek
                      0b11101,     // 6 oczek
                      0b00101,     // Obrót 1
                      0b10001,     // Obrót 2
                      0b01001,     // Obrót 3
                      0b00011};    // obrót 4
    uint8_t i,j;

    DDRB  = 0b11110;   // Ustawiamy linie wyjścia i wejścia
    PORTB = 0b00001;   // Do PB0 opornik podciągający

    while (1)
    {
        while(PINB & 0b00001); // Czekamy na przycisk
        for(i = 0; i < 6; i++)
        {
            PORTB = cube[i];
            _delay_ms(1000);
        }
        for(j = 0; j < 10;j++)
            for(i = 6; i < 10; i++)
            {
                PORTB = cube[i];
                _delay_ms(100);
            }
        PORTB = 0b00001; // Gasimy diody
    }
}

W tablicy cube zdefiniowane są stany portu B dla poszczególnych układów oczek kostki. Program najpierw czeka na naciśnięcie przycisku, po czym przesyła kolejno zawartość tablicy cube na port B. W efekcie na kostce pojawiają się kolejne układy oczek. Program pozwala przetestować poprawność montażu płytki APP004.

Program kostki do gry

/*
 * main.c
 *
 *  Created on: 24 lut 2015
 *      Author: Jerzy
 *  Opis:
 *  Program steruje generacją oczek na kostce do gry
 */

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

int main(void)
{

    uint8_t cube[] = {0b00011,     // 1 oczko
                      0b00101,     // 2 oczka
                      0b00111,     // 3 oczka
                      0b01101,     // 4 oczka
                      0b01111,     // 5 oczek
                      0b11101,     // 6 oczek
                      0b00101,     // Obrót 1
                      0b10001,     // Obrót 2
                      0b01001,     // Obrót 3
                      0b00011};    // obrót 4
    uint8_t 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 PB0 podpinamy opornik podciągający
    TCCR0B = 0b101;                               // 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 = 0b00001;                 // 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 = 0b00001;             // 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 = 0b00001;            // Gasimy wszystkie diody
            _delay_ms(100);
        }
    }
}

Po naciśnięciu przycisku na płytce APP004 zostaje wykonany "rzut" kostką, a następnie wyświetlany jest wynik. Jeśli w trakcie wyświetlania wyniku zostanie ponownie naciśniety przycisk, to wykonywany jest nowy rzut. Po upływie 3 sekund wynik na kostce zaczyna mrugać, po czym diody LED gasną (w celu oszczędzania energii).

 

 

Podsumowanie

 
   
 
bit 7 6 5 4 3 2 1 0
nazwa                
R/W R/W R/W R/W R/W R/W R/W R/W R/W
stan 0 0 0 0 0 0 0 0

Rejestr licznika TCNT0

Rejestr TCNT0 jest rejestrem licznika, który zlicza impulsy doprowadzone do jego wejścia. Rejestr posiada 8 bitów danych i może zliczać od 0 do 255.

 
bit 7 6 5 4 3 2 1 0
nazwa                
R/W R/W R/W R/W R/W R/W R/W R/W R/W
stan 0 0 0 0 0 0 0 0

Rejestr porównawczy OCR0A

bit 7 6 5 4 3 2 1 0
nazwa                
R/W R/W R/W R/W R/W R/W R/W R/W R/W
stan 0 0 0 0 0 0 0 0

Rejestr porównawczy OCR0B

Rejestry porównawcze OCR0A i OCR0B zawierają 8-mio bitowe wartości, które są ciągle porównywane ze stanem licznika TCNT0. Rejestry te są wykorzystywane do generacji różnych przebiegów.

 

bit 7 6 5 4 3 2 1 0
nazwa OCF0B OCF0A TOV0
R/W R R R R R/W R/W R/W R
stan 0 0 0 0 0 0 0 0

Rejestr znaczników przerwań TIFR0

Rejestr TIFR0 zawiera tzw. znaczniki przerwań (ang. Interrupt Flags). Są one ustawiane na 1, gdy zajdzie określone zdarzenie. Znacznik TOV0 jest ustawiany, gdy licznik przekracza wartość krańcową. Pozostałe znaczniki są ustawiane, gdy wartość w liczniku jest równa zawartości jednego z rejestrów porównawczych OCR0A lub OCR0B.

Uwaga: znaczniki zerujemy przez zapis bitu o wartości 1, nie zero!.

 

bit 7 6 5 4 3 2 1 0
nazwa COM0A1 COM0A0 COM0B1 COM0B0 WGM01 WGM00
R/W R/W R/W R/W R/W R R R/W R/W
stan 0 0 0 0 0 0 0 0

Rejestr sterowania licznikiem TCCR0A

bit 7 6 5 4 3 2 1 0
nazwa FOC0A FOC0B WGM02 CS02 CS01 CS00
R/W W W R R R/W R/W R/W R/W
stan 0 0 0 0 0 0 0 0

Rejestr sterowania licznikiem TCCR0B

Rejestry TCCR0A i TCCR0B są rejestrami sterującym trybami pracy licznika. Bity WGM02, WGM01 i WGM02 pozwalają wybrać odpowiedni tryb pracy. W rozdziale opisujemy dwa tryby (jest ich w sumie 8):
WGM02 WGM01 WGM00 Tryb
0 0 0 Tryb normalny, licznik liczy od 0 do 255. Po osiągnięciu wartości 255 licznik zeruje się przy następnym impulsie zegarowym. Ustawiany wtedy jest znacznik TOV w rejestrze TIFR0.
0 1 0 Tryb CTC (ang. Clear Timer on Compare match). W trybie tym licznik jest zerowany po osiągnięciu wartości w rejestrze porównawczym OCR0A. Zerowanie jest wykonywane przy następnym impulsie zegarowym. Dlatego faktycznie licznik w jednym cyklu zlicza OCR0A + 1 impulsów. Przy wyzerowaniu licznika zostaje ustawiony na 1 znacznik OCF0A w rejestrze TIFR0

Bity CS02, CS01 i CS00 wybierają źródło sygnałów zegarowych, które będzie zliczać licznik.
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.

 

 


   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