Mikrokontrolery AVR część 4 – Porty we/wy

Porty wejścia – wyjścia służą kontrolerowi do komunikacji się z otoczeniem. Normalnym trybem pracy portów jest wysyłanie lub czytanie ich stanu, porty te mają też swoje alternatywne funkcje, dzięki czemu kontroler może podawać lub odbierać sygnały od układów takich jak liczniki, przetworniki oraz moduły komunikacji, takie jak i2C, SPI czy RS-232.

W tym artykule zajmę się jedynie podstawową funkcją portów, czyli wystawianiem sygnału i czytaniem ich stanów.

Kontroler atmega8 ma trzy porty, PORTB, PORTC i PORTD. Do ich konfiguracji używamy trzech rejestrów: DDRx, PINx, PORTx, x to literka, zależna od tego którym portem chcemy sterować, czyli do rejestru, którego portu chcemy się odwoływać.

DDRx – to jest 8b rejestr, poprzez który możemy ustawiać, czy dany pin portu ma być wejściem czy wyjściem. Każdy bit tego rejestru odpowiada kolejno poszczególnym pinom portu. Aby pin był wejściowy na bit odpowiadający jemu musimy podać 0 logiczne, a jeśli ma być wyjściowy to 1 logiczną. Standardowa wartość to 0, czyli jeżeli nic nie zmienimy, to pin jest wejściem.

PINx – to także jest rejestr 8b, w nim także każdy bit jest przyporządkowany do pinów portu. Ten rejestr służy do czytania stanu danego pinu, kiedy ten pin jest ustawiony w rejestrze DDRx jako pin wejściowy.

PORTx – ten rejestr pełni dwie funkcje, zależnie od tego czy pin jest ustawiony jako wejście czy wyjście. W sytuacji kiedy jest to wyjście, to do tego rejestru wpisujemy wartość logiczną, która ma być na wyjściu pinu. Kiedy pin jest wejściem to wpisanie do tego rejestru jedynkę pod poszczególne bity spowoduje podciągnięcie, rezystorem podciągającym do napięcia zasilania.

Jak widać jest to dosyć łatwe, teraz przejdźmy do praktyki. Poniżej przedstawiam pseudo kody, do wpisywania danych na port i czytania portu w języku C.

Wpisanie do DDRB:

DDRB |= (1<<1)|(1<<3)|(1<<7);

taka operacja spowoduje ustawienie pinu 1, 3 i 7 jako wyjścia, a reszta zastanie jako wejścia.

Resetowanie bitów DDRB:

DDRB &= ~((1<<1)|(1<<7));

po takiej operacji ustawiona jedynka pozostanie tylko na pinie 3, a reszta będzie jako wejścia.

W przypadku wpisywania do rejestru PORTx zasada jest taka sama.

Podałem tutaj jakiś kod, ale co on w zasadzie robi? Są to typowe operacje logiczne. Ustawianie bitu działa w ten sposób, że wpisujemy 1 czyli ustawiony jest tylko pierwszy bit, a następnie przesuwamy go o określoną pozycje. Potem dokonujemy dodawania logicznego i tam gdzie mamy jedynkę w wyniku przesunięcia, to tam ustawi nam się 1. Postaram się to rozrysować.

Wpisujemy 1:

0 0 0 0 0 0 0 1

przesuwamy, załóżmy o 3:

0 0 0 0 0 0 1 0

0 0 0 0 0 1 0 0

0 0 0 0 1 0 0 0

teraz dodajemy logicznie to do zawartości rejestru:

0 0 0 0 1 0 0 0

1 0 0 1 0 0 0 1 – losowa wartość

—————–

1 0 0 1 1 0 0 1

W przypadku gdy czyścimy piny, to korzystamy z operacji iloczynu (mnożenia) logicznego i z negacji.

Wpisujemy 1:

0 0 0 0 0 0 0 1

Przesuwamy, załóżmy o 1:

0 0 0 0 0 0 1 0

Negujemy:

1 1 1 1 1 1 0 1

Wykonujemy operacje mnożenia czyli operacje AND:

1 1 1 1 1 1 0 1

1 0 1 1 0 0 1 0

—————–

1 0 1 1 0 0 0 0

W efekcie tej operacji resetujemy (zerujemy) tylko ten bit, który chcemy, a reszta zostaje bez zmian.

Z czytaniem stanu bitu jest trochę inna zabawa, wykonuje się to poprzez tak zwaną maskę. Przykładowy pseudo kod w C wygląda tak:

(PINB & 0x01)

Taki kod można zastosować jako warunek w instrukcji warunkowej, ale jak on działa? Ma on za zadanie odfiltrować wartość zerowego bitu rejestru PINB i wystawić zależnie od jego wartości 0 lub wartość różną od zera, w tym przypadku 1. Rozrysujmy to:

Bierzemy wartość rejestru PINB:

0 0 0 0 1 0 1 1 – wartość losowa

Teraz bierzemy maskę:

0 0 0 0 0 0 0 1

Dokonujemy iloczynu logicznego(funkcja AND, &)

0 0 0 0 1 0 1 1

0 0 0 0 0 0 0 1

—————–

0 0 0 0 0 0 0 1

Oddzielamy w ten sposób nie interesujące nas bity, a przepuszczamy tylko te, które nas interesują. Możemy także przepuścić przez maskę większą ilość bitów.

Ale po co te wszystkie kombinacje? Nie łatwiej byłoby bezpośrednio operować na bitach?

Niestety nie można, bo język C na to nie pozwala, ale znając metody pokazane powyżej można wykonać wszystko to samo, co prawda w trochę trudniejszy sposób, ale jak załapie się metodę, to nie stanowi to żadnego problemu.

W tej części to wszystko. W następnej części będę mówił o przerwaniach i o tym jak korzystać z nich w języku C.

Facebook Comments

Komentarze do „Mikrokontrolery AVR część 4 – Porty we/wy

  1. Kiedy pin jest wyjściem to wpisanie do tego rejestru jedynkę pod poszczególne bity spowoduje podciągnięcie, rezystorem podciągającym do napięcia zasilania.

    Powinno być:
    Kiedy pin jest WEJŚCIEM to wpisanie do tego rejestru jedynkę pod poszczególne bity spowoduje podciągnięcie, rezystorem podciągającym do napięcia zasilania.

    Pozdrawiam serdecznie,

  2. Trochę lipa.
    Wytłumaczono tutaj tyle co w większości artykułach, czyli nic.
    np: Kiedy musi być „&=” albo „|=”, co oznacza „<<" w poleceniu "1<<7".
    Ten artykuł nic nie pomaga.
    Powinno być to wyjaśnione od podstaw, a nie tylko część.
    Pozdrawiam

    • Nie tłumaczyłem dokładnie co oznaczają operacje bitowe, bo to można przeczytać w każdym kursie do c/c++. Ale uwaga słuszna i postaram się w najbliższym czasie rozwinąć tą część artykułu.

  3. „było by”(!). Materiał fajny, niby nic odkrywczego od innych, a jednak jako stary assemblerowiec 6502 (i ciut elektronik amator) mnie wyjaśnił więcej niż inne www. Mnie najłatwiej spojrzeć na bajt/bity i już wszystko jasne. Choć ostatnio programowałem w ASM chyba z 15 lat temu. Dzięki

Dodaj komentarz

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

Time limit is exhausted. Please reload CAPTCHA.

This site uses Akismet to reduce spam. Learn how your comment data is processed.