wtorek, 12 kwietnia 2011

Jak mikrokontroler "widzi" sygnał cyfrowy?


Autor: Dondu

Jednym z istotnych, choć często nie rozpatrywanych przez początkującego elektronika zjawisk, jest sposób „widzenia” sygnałów cyfrowych przez mikrokontroler.


Są to zjawiska, których:
  • występowania trzeba być świadomym,
  • często można je pominąć,
  • czasami trzeba je wziąć pod uwagę.

Problem należy podzielić na dwa podproblemy:
  1. ustalenie poziomu logicznego
  2. ustalenie momentu skanowania pinu.

Do niniejszego artykułu przyda nam się schemat ideowy części mikrokontrolera odpowiedzialnej za pin wejścia-wyjścia. Posłużę się mikrokontrolerem Atmega8 (AVR firmy Atmel).




Należy być także świadomym jak zbudowany jest układ pinu cyfrowego:







1. Ustalanie poziomu logicznego

Do pinu mikrokontrolera jako wejścia cyfrowego podłączamy z reguły sygnały cyfrowe (zero- jedynkowe). Ale nawet one nie są przecież idealne! Co więcej, mikrokontrolery mogą pracować w szerokich zakresach napięć zasilających. Istotne jest więc ustalenie jasnych zasad dot. rozpoznawania poziomów logicznych (jedynki i zera).

Dlatego też w dokumentacji każdego mikrokontrolera znajdziesz tabelki z parametrami, które powinny spełniać sygnały cyfrowe podłączane do wejść cyfrowych mikrokontrolera.


W tym artykule interesować nas będą przede wszystkim napięcia wejściowe na wejściach cyfrowych, czyli parametry VIL oraz VIH, które dotyczą wszystkich pinów z wyjątkiem RESET i XTAL. Są one opisane w pierwszych dwóch wierszach powyższej tabeli.

Ponieważ mikrokontroler może, być zasilany różnymi napięciami, stąd niektóre parametry określane są względem napięcia zasilania mikrokontrolera oznaczonego przez Vcc.

I tak na przykład dla mikrokontrolera zasilanego Vcc=5V według powyższej tabelki VIL może mieć maksymalnie:

VIL(max) = 0.2VCC = 0.2 • 5V = 1V

Czyli stan niski (zero logiczne) na wejściach musi zawierać się pomiędzy -0.5V a 1V.

Dla stanu wysokiego (jedynki logicznej) będziemy mieli odpowiednio:

VIH(min) = 0.6VCC = 0.6 • 5V = 3V

VIH(max) = VCC + 0.5V = 5V + 0.5V = 5.5V

Czyli stan wysoki (jedynka logiczna) na wejściach musi zawierać się pomiędzy 3V i 5.5V.

Są to oczywiście (zgodnie z notkami pod tabelką) napięcia gwarantowane, czyli takie, które na pewno zostaną prawidłowo zinterpretowane przez mikrokontroler. Jak każdy parametr elektryczny można więc je przekraczać, ale trzeba być świadomym, że nie daje to gwarancji prawidłowej pracy.


Co się stanie gdy podłączę napięcie zbyt wysokie, by było zerem, ale zbyt niskie, by było jedynką?

Do wejścia cyfrowego możemy także podłączyć sygnał, który nie spełni powyższych przykładowo wyliczonych parametrów. Na przykład będzie to sygnał analogowy, który chcemy traktować jak cyfrowy (oczywiście z pewnym przybliżeniem).

Mikrokontroler musi więc zadbać o to, by podczas gdy oczekuje na wejściu (pinie) sygnału cyfrowego, taki sygnał otrzymał. Z tego też powodu producenci wyposażają cyfrowe piny wejściowe w przerzutniki Schmitta.

Przerzutnik Schmitta, odpowiada za wykrycie i ustawianie na jego wyjściu stabilnego zera lub jedynki zależnie od napięcia panującego na pinie. W tym celu wykorzystują komparator oraz histerezę.

Przerzutnik Schmitta wbudowany w pin mikrokontrolera, zgodnie z przyjętą dla niego histerezą wyznacza progi przełączania (ang. threshold) osobno dla napięcia narastającego i opadającego. Sprawdźmy więc jak to jest w naszym przykładowym mikrokontrolerze.

Szybki eksperymencik pokazuje jak w praktyce naocznie zobaczyć pracę przerzutnika Schmitta na wejściu cyfrowym mikrokontrolera ATmega8.



Wykorzystamy pin PB1 jako wejście cyfrowe, na które podajemy powoli zmieniające się napięcie z suwaka potencjometru. Program będzie sterował diodę podłączoną do pinu PC1. Odczytaną wartość stanu wejścia PB1 przepisze więc na PC1 (dla uproszczenia kodu przepisywać będziemy wszystkie bity).

#include <avr/io.h>

int main(void)
{
   DDRC   =  (1<<PC1); //ustawienie pinu jako wyjście   
   DDRB  &= ~(1<<PB1); //ustawienie pinu jako wejście   

   while(1)
   {
      //odczytaj piny portu B i ustaw takie same w PORTC
      PORTC = PINB; 
   }
}

Powyższy program (przy tak tak podłączonej diodzie LED jak na schemacie) zapala diodę, gdy mikrokontroler uznaje, że na wejściu (pinie PB1) jest stan wysoki (jedynka logiczna) i gasi diodę, gdy uznaje, że jest stan niski (zero logiczne).

Wyniki eksperymentu:



Na podstawie powyższego doświadczenia możemy narysować histerezę przedstawiając ją na symbolicznym wykresie wraz z zaznaczonymi (ustalonymi orientacyjnie) progami napięcia wejściowego, przy których mikrokontroler uznał, że na pinie wejściowym jest zero lub jedynka:


Różnica pomiędzy UL i UH to szerokość pętli histerezy, która w naszym eksperymencie (orienatcyjnie) wynosiła około:
1,9V – 1,4V = 0,5V

Porównajmy zmierzone wartości napięć progów przełączania z dokumentacją tego mikrokontrolera, gdzie znajdziemy na wykresach dla jedynki logicznej:


a dla zera logicznego:


Jak widzisz testowe pomiary potwierdzają zgodność z dokumentacją.






2. Ustalenie momentu skanowania pinu.

O co konkretnie chodzi? Prosty przykład:

Gdy prowadzisz samochód, to od czasu do czasu sprawdzasz, co widać w lusterku wstecznym. W czasie, gdy nie patrzysz w lusterko, cały czas widać w nim zmieniającą się za samochodem sytuację. Ty jednak tego nie widzisz, ponieważ „skanujesz” lusterko, tylko co wybrany odcinek czasu.

Przekładając to na elektronikę:
  • Ty, to mikrokontroler,
  • lusterko, to pin,
  • obserwowana przestrzeń za samochodem, to stan pinu.

Istotny jest więc moment „skanowania” pinu, czyli sprawdzania jego stanu.

Skoro już wiesz, że istotny jest „moment „skanowania” pinu, to powinieneś się zastanowić, kiedy ten moment następuje i od czego zależy?


Synchronizatory

Jako, że mikrokontroler jest synchronicznym urządzeniem sekwencyjnym (z nielicznymi jego funkcjami asynchronicznymi), jest więc taktowany przez sygnał zegara. Dlatego też działania na pinach wejściowych muszą być zsynchronizowane z tym sygnałem. Służą do tego synchronizatory.

Synchronizator pośredniczy pomiędzy lusterkiem, a kierowcą w taki sposób, by kierowca rzucając (na ułamek sekundy) okiem na lusterko był pewien, czy za nim jedzie jakiś pojazd, czy też nie. Innymi słowy, zapewniają w filmowym żargonie tzw. "stopklatkę".

Sygnałem synchronizującym jest sygnał taktujący mikrokontroler, który oznaczony jest na poniższych wykresach jako SYSTEM CLK, a na schematach jako clk I/O.


Przypadek 1 – odczyt stanu pinu ustawionego jako wejście

Zerkamy na schemat pinu pokazany na początku tego artykułu, a konkretnie na fragment wejścia:


Na schemacie powyżej możesz zauważyć, że na wejściu pinu są:
  • przerzutnik Schmitta,
  • synchronizator.

W skrócie działanie wygląda tak:
  1. Przerzutnik Schmitta, ustala stan pinu w danej chwili.
  2. Zatrzask, jest otwarty (przezroczysty) i czeka na sygnał do zapamiętania stanu pinu.
  3. Gdy zatrzask otrzyma sygnał do zapamiętania, zamyka się zapamiętując stan pinu.
  4. "Po chwili" zapamiętany w zatrzasku stan pinu przepisywany jest do wejściowego rejestru PINx.
  5. Zatrzask ponownie jest otwierany i wracamy do punktu 1.

Zauważ, że podczas przepisywania (krok 4) zapamiętanego w zatrzasku stanu pinu, nie ma znaczenia co aktualnie dzieje się na pinie (nóżce) wejściowym mikrokontrolera. Jeżeli doprowadzony do pinu sygnał zmienia się w tym czasie (trwania kroku 4), zmiany te nie są widoczne dla mikrokontrolera.

Mikrokontroler będzie "widział" jedynie ten stan pinu, który zostanie zapamiętany w zatrzasku.


Teraz popatrzmy szczegółowo jak cały proces opisany jest w dokumentacji.

Zatrzask jest zamknięty, gdy sygnał zegarowy SYSTEM CLK jest zerem (stan niski), a otwarty (przezroczysty), gdy SYSTEM CLK jest jedynką (stan wysoki):




Przezroczysty, czyli każda zmiana stanu pinu na wyjściu przerzutnika Schmitta, jest widoczna na wyjściu zatrzasku. Na poniższym wykresie jest to pokazane na zakreskowanym fragmencie przebiegu sygnału SYNC LATCH.

Gdy SYSTEM CLK zmienia stan na niski (zero), zatrzask zapamiętuje stan wyjścia przerzutnika Schmitta i pamięta go dopóki SYSTEM CLK ma stan niski. Przy najbliższym narastającym zboczu sygnału  SYSTEM CLK, zapamiętany w zatrzasku stan pinu, przepisywany jest do rejestru PINx:




Chodzi o to, by w momencie zapisywania stanu pinów w rejestrze PINx, stan ten nie ulegał zmianie:




Cały ten proces musi trwać jakiś czas, co wprowadza pewne opóźnienie w stosunku do faktycznego stanu pinu. Rzućmy okiem na wykres przebiegów podczas odczytu stanu pinów:




Na wykresie powyżej, zaznaczone są minimalny i maksymalny czas opóźnienia, który może wynosić od ½ do 1½ cyklu zegara taktującego mikrokontroler.




Podsumowując:

Naszym momentem skanowania pinu jest więc moment zatrzaskiwania informacji o stanie pinu w zatrzaskach (ang. latch), a dzieje się to w momencie opadającego zbocza sygnału zegarowego.

Przez pozostały okres czasu, zmiany na pinie nie są "widoczne" dla mikrokontrolera.

Z punktu widzenia programu opóźnienie to jest jeszcze większe, ponieważ informacja w rejestrze r17 dostępna jest po okresie jeszcze jednego sygnału zegarowego (to już może mieć znaczenie jedynie w bardzo specyficznych projektach).




Przypadek 2 - Odczyt pinu ustawionego jako wyjście.

W przypadku, gdy pin mikrokontrolera ustawiony jest jako wyjście, odczytanie stanu pinu powoduje faktycznie odczytanie wartości zapisanej w rejestrze wyjściowym PORTx, gdyż to on wtedy wymusza stan pinu.



Czytając  przypadek 1 dowiedziałeś się, że zapamiętanie stanu wejścia w rejestrze PINx następuje w momencie narastającego zbocza zegara taktującego.

Z przypadkiem 2 związany jest problem, polegający na tym, że gdy ustawiasz stan pinu wyjściowego w PORTx, a w następnej instrukcji (z jakichś powodów) chcesz odczytać stan pinów, to niestety otrzymasz wartość nieprawidłową ponieważ, stan pinu został zatrzaśnięty w zatrzasku o pół cyklu  zegarowego wcześniej niż ustawiono wartość w rejestrze PORTx.


Dlatego poniższy program zadziała inaczej niż się spodziewasz:
//Test odczytu bez NOP
DDRB    =  (1<<PB0);  //PB0 jako wyjście
PORTB =  (1<<PB0);    //ustaw 1 na PB0
PORTB =  0;           //ustaw 0 na PB0
temp1 = PINB;         //odczytaj rejestr PINB
temp2 = PINB;         //odczytaj rejestr PINB

Oczekiwalibyśmy, by zarówno zmienne temp1 i temp2 miały wartość zero. Jednak tak nie będzie, co możesz zobaczyć na filmie poniżej lub samemu przeprowadzić eksperyment :-)

W takim przypadku powinieneś ten fakt uwzględnić w swoim programie lub pomiędzy instrukcje zapisu do PORTx i odczytu z PINx wprowadzić opóźnienie np. w formie instrukcji: NOP



W tym wypadku opóźnienie jest stałe i wynosi jeden pełny okres zegarowy oznaczony na wykresie jako tpd.

Prawidłowy program powinien więc wyglądać tak:
//Test odczytu bez NOP
DDRB    =  (1<<PB0);  //PB0 jako wyjście
PORTB =  (1<<PB0);    //ustaw 1 na PB0
PORTB =  0;           //ustaw 0 na PB0
asm volatile ("nop"); //niezbędne opóźnienie dzięki instrukcji NOP
temp1 = PINB;         //odczytaj rejestr PINB
temp2 = PINB;         //odczytaj rejestr PINB

Szybki eksperyment:



Pliki projektu w AVR Studio 4, źródłowe oraz hex dla 1MHz:  test-pinx.zip


Kiedy na taki problem mogę się natknąć?

Problem nie dotyczy tylko i wyłącznie odczytu tego samego pinu. Na przykład realizując klawiaturę matrycową, gdzie może wystąpić sytuacja, w której przełączasz np. wiersz klawiatury w PORTC i od razu chcesz odczytać wartość stanów poszczególnych klawiszy w PINB. Bez instrukcji NOP odczyty będą nieprawidłowe lub program musi w inny sposób uwzględnić opisany problem.



Przypadek 3 – Pin wejściowy taktujący timer

Jeszcze inaczej przedstawia się problem momentu skanowania dla pinów, które są wejściami sygnału zegarowego timera (licznika). W przypadku omawianego mikrokontrolera ATmega8,  są dwa timery (Timer0 oraz Timer1), które mają zewnętrzne wejścia zegarowe Tn (odpowiednio T0 i T1).

W przypadku, gdy timery działają w trybach synchronicznych (zsynchronizowanych z zegarem mikrokontrolera) także muszą wykorzystywać synchronizator.



Synchronizator działa w następujący sposób:


Każdy z pinów Tn (T0 lub T1), jest skanowany przez synchronizator tylko raz na każdy cykl zegara taktującego mikrokontroler. Tak zeskanowany stan pinu podawany jest na wykrywacz zbocza, ponieważ te timery mogą (w zależności od ich ustawienia) zliczać zbocza narastające lub opadające.

W tym wypadku także następuje opóźnienie sygnału w stosunku do faktycznego stany pinu. Opóźnienie to może wynosić od 2,5 do 3,5 cykli zegara taktującego mikrokontroler:


Innymi słowy, na przykład, jeżeli na pinie wejściowym timera jest zbocze, które powinno spowodować, zwiększenie timera o 1, to timer może się spóźniać od 2,5 do 3,5 cykli zegara taktującego mikrokontroler.

Opóźnienie to będzie oczywiście stałe dla danych ustawień timera. Dlatego też w większości przypadków nie ma to znaczenia. Ale w specyficznych sytuacjach ta wiedza, może być niezbędna dla prawidłowego działania Twojego projektu.


Ale to nie koniec problemów w przypadku timerów!

Synchronizator timerów dla pinów Tn ma jeszcze jedno ograniczenie:


Każdy półokres sygnału zegara timera musi być dłuższy niż jeden pełny okres zegara taktującego mikrokontroler. To jest bardzo istotne ograniczenie, które znacząco wpływa na możliwości wykorzystania timerów taktowanych zewnętrznym sygnałem zegarowym.


Zgodnie z twierdzeniem Nyquist’a powinniśmy mieć możliwość poprawnego działania timera w przypadku, gdy sygnał zegarowy na pinie Tn będzie, co najmniej dwa razy wolniejszy niż sygnał zegarowy mikrokontrolera.

Jednakże ze względu na niedokładności i nierównomierność wypełnienia sygnału zegarowego mikrokontrolera spowodowane tolerancją źródła taktującego mikrokontroler (kwarcu, rezonatora, kondensatorów, itp.), producent rekomenduje maksymalną częstotliwość sygnału na pinie Tn mniejszą niż częstotliwość taktowania mikrokontrolera podzieloną przez 2,5.

o czym producent wspomina tutaj:


Czyli częstotliwość zewnętrznego sygnału taktującego timer podłączonego pod pin Tn powinna spełniać warunek:

FTn < FCPU / 2.5

gdzie FCPU to częstotliwość zegara taktującego mikrokontroler AVR ATmega8.







Inne możliwe problemy ...

Czasami może się zdarzyć, że sygnały cyfrowe mogą nie nadążać za mikrokontrolerem. Dzieje się to w przypadkach, gdy sygnał obciążony jest dodatkową pojemnością lub indukcyjnością. Chodzi tutaj w szczególności o ścieżki i przewody, ale także nieprawidłowo dobranych elementów dodatkowych jak na przykład tranzystorów czy diod, a nawet rezystorów.

W większości przypadków nie będziesz pewnie miał z tym problemów, ale ważne jest by być tego świadomym i wiedzieć, na co ewentualnie należy zwrócić uwagę przy projektowaniu oraz gdzie szukać rozwiązania problemów podczas testowania urządzenia.


Przydatne materiały dodatkowe:

7 komentarzy:

  1. Dla mnie bomba, pomimo iż niewiele więcej niż wiem, dowiedziałem się z artykułu, ale to z faktu że troche już w tym siedzę, naprawdę szczere dzięki i wyrazy uznania dla autora za chęci do opracowania tego, w dodatku bezpłatnej formie dla następnych masy hobbystów :)
    SUPER

    OdpowiedzUsuń
  2. Rewelacja, prosto, zwieźle i na najwyższym poziomie profesjonalizmu.

    OdpowiedzUsuń
  3. oby takich wiecej!!!! swietna praca!!

    OdpowiedzUsuń
  4. Cze. Może mi ktoś wyjaśnić te dwa zapisy:

    1. Podsumowanie przypadku 1go:
    "Naszym momentem skanowania pinu jest więc moment zatrzaskiwania informacji o stanie pinu w zatrzaskach (ang. latch), a dzieje się to w momencie opadającego zbocza sygnału zegarowego."

    2. Początek przypadku 2go:
    "Czytając przypadek 1 dowiedziałeś się, że zapamiętanie stanu wejścia w rejestrze PINx następuje w momencie narastającego zbocza zegara taktującego."

    Czegoś nie rozumiem?

    OdpowiedzUsuń
    Odpowiedzi
    1. Przegapiłem to pytanie - przepraszam.
      Twoje wątpliwości są wyjaśnione na rysunku Figure 23.

      Usuń
  5. Witam, może mi Pan podać model tego oscyloskopu marki Rigol?

    OdpowiedzUsuń
    Odpowiedzi
    1. Witaj, ten oscylogram został znaleziony gdzieś w sieci.

      Usuń