Porty mikrokontrolera - Przegląd rozwiązań

Pozostałe układy mikrokontrolerów, układy peryferyjne i inne, nie mieszczące się w powyższych kategoriach.
Awatar użytkownika
ZbeeGin
User
User
Posty: 246
Rejestracja: sobota 08 lip 2017, 17:16
Lokalizacja: GOP
Kontaktowanie:

Porty mikrokontrolera - Przegląd rozwiązań

Postautor: ZbeeGin » niedziela 05 sie 2018, 19:48

W tym artykule, chciałbym nieco przybliżyć wszystkim podstawowe zagadnienie związane z rozpoczynaniem pracy z każdym mikrokontrolerem: porty zewenętrzne i ich konfiguracja. Ma to być przegląd rozwiązań, zatem nie będziemy się skupiać na jednym tylko typie mikrokontrolera, i przejdziemy płynnie od najmniej zaawansowanego do najbardziej zaawansowanego.

Wstęp

Jak wiadomo mikrokontroler komunikuje się ze światem znajdującym się poza własną strukturą za pomocą swoich wyprowadzeń. Z reguły większość z nich to uniwersalne porty wejścia-wyjścia, zwane GPIO (General Pheripherial Input-Output). Zwykle ich konstrukcja opiera się na wykorzystaniu układu tranzystorowego typu push-pull. Uproszczony schemat takiego wyjścia portu mamy poniżej:

rys1.png

Jak widać na rysunku, mamy tu dwa komplementarne tranzystory, które będą się otwierać w zależności od stanu wewnętrznej linii SYGNAŁ. Jeśli na końcówce portu ma zostać wystawiona jedynka logiczna to będzie przewodzić górny tranzystor i wymusi on napięcie mniej więcej równe VCC. Jeśli zaś na port miałby zostać wystawione zero logiczne to dolny tranzystor będzie nam ściągał pin wyjściowy do poziomu GND.
Diody jakie widzicie bezpośrednio na wyjściu to diody zabezpieczające przed pojawianiem się na wyjściu napięć wyższych niż VCC lub niższych od GND. Zwane one są diodami "clamp" i są już stosowane jako standard. O istnieniu tych diod można się przekonać czytając noty katalogowe, gdzie np. określa się maksymalne napięcie wejściowe w stanie wysokim jako VCC+0,5V. Te pół wolta to właśnie spadek na diodach clamp.

Pewnie zauważyliście, że w takim układzie mamy tylko wyjście, a przecież mówimy tu o portach wejścia-wyjścia. Oczywiście o tym też pomyślano. Do końcówki wyjściowej podłączono jeszcze bufor wejściowy, którzy może przekazać aktualny stan końcówki.

rys2.png

W takim prostym układzie bieżący stan wyjścia jaki wystawiają tranzystory będzie przez bufor powtarzany. Odpowiedni układ na bramkach tranzystorów może na przykład ustawić stan równowagi poza zakresem poziomów logicznych i nie zakłócający wejścia bufora. Wtedy niepodłączona końcówka będzie określana jako pływająca, a bufor nie będzie jednoznacznie mógł zinterpretować stanu jaki wystawiają tranzystory. Dopiero doprowadzając sygnał z zewnątrz będziemy wymuszać określony stan.

To tyle wstępnej teorii. Teraz czas na rozwiązania praktyczne.

INFO: W tekście będę operować na pełnej szerokości portów w przykładach. Wiedzcie jednak, że współczesne kompilatory pozwalają na wyłuskiwanie poszczególnych bitów. Zatem konfiguracja portu nie musi się odbywać dla wszystkich jego linii. Można na przykład operować tylko na bicie 1. W poszczególnych mikrokontrolerach jest to różnie definiowane, stąd takie uproszczenie.

Mikrokontrolery MCS-51 - czyli staruszek i8051.

Zacznę może od nieco historycznego już układu. i8051 to w pełni 8-bitowy mikrokontroler, który posiada w podstawowej wersji dość proste cztery porty GPIO. Każdy z nich obsługuje jeden rejestr portu znajdujący się w przestrzeni SFR (Special Function Registers). Skupimy się na razie tylko na porcie P1 pomijając pozostałe, które posiadają odrobinę inną konstrukcję.

rys3.png

Widać tu duże podobieństwo do naszego uproszczonego układu. Zamiast górnego tranzystora mamy rezystor pull-up, który wymusza nam stan wysoki jeśli dolny tranzystor nie jest otwarty. W zasadzie to nie jest rezystor, a odpowiednio wysterowany tranzystor. Do tego mamy dwa bufory odczytu: jeden czyta bezpośrednio z końcówki, drugi czyta z zatrzasku portu.

INFO: To dlatego zaleca się by np. diody sterowane bezpośrednio z końcówki mikrokontrolera sterować katodą od strony masy. Wtedy możemy uzyskać większy prąd wpływający. Praca diody przy sterowaniu anodą była by tu sporym obciążeniem dla tego rezystora pull-up.

Konfiguracja takiego porty w układach '51 jest uproszczona do minimum. Widzimy tylko jeden uniwersalny rejestr zwany tak samo jak port. Jeśli do tego rejestru wpiszemy zera to nasz przerzutnik uruchomi dolne tranzystory ściągając końcówki do zera. Jeśli zaś wpiszemy jedynki to rolę wymuszacza stanu wysokiego będzie pełnił rezystor pull-up.

A co z wejściem? Tutaj konstruktorzy wymyślili dość prosty mechanizm. Jeśli wpiszemy 1 do rejestru to dolny tranzystor pozwoli na działanie pull-up-a i mamy po prostu wejście z podciąganiem do plusa, które to bezpiecznie możemy ściągnąć do poziomu masy w przypadku doprowadzenia stanu niskiego. M.in. dlatego wydajność prądowa wyjścia w stanie wysokim jest dość niska.

Zapis stanu portu jak i odczyt odbywają się przez ten sam rejestr. Oczywiście odpowiednie układy będą dbać o to by czytać przez właściwy bufor. Przy operacjach wejścia będzie czytany dolny bufor bezpośredni, a przy operacjach typu: odczytaj-zmień-zapisz (Read-Modify-Write) górny bufor, który czyta stan przerzutnika.

Dlatego też jeśli chcemy zapisać stan portu to piszemy po prostu:

Kod: Zaznacz cały

P1 = 0b00001111; // połowa portu będzie wystawiać zera, druga połowa jedynki


Zaś jeśli chcemy odczytywać to najpierw musimy wpisać jedynki, a potem możemy już odczytywać stan aktualny:

Kod: Zaznacz cały

P1 = 0x0FF;
(...)
if (P1 == 0x01) { };


Nieco odmiennie zbudowany jest zaś port P0, który może pracować jako GPIO lub jako przedłużenie szyny adresowej/danych podczas współpracy z zewnętrzną pamięcią programu.

rys3_1.png

Widzimy na rysunku oba tranzystory układu push-pull. Niestety górny tranzystor jest wyłączany gdy port ten pracuje jako zwykłe GPIO. Dlatego w przypadku takiej pracy tego portu musimy dodać zewnętrzne rezystory pull-up. Chyba, że świadomie chcemy mieć wyjście typu Open-Drain.

Podobna sytuacja, ale z nieco innych powodów zachodzi w małych układach rodziny '51 produkcji Atmel-a: AT89C2051. Tam piny P1.0 i P1.1 pełnią rolę wejść wewnętrznego komparatora, stąd też nie posiadają wewnętrznego podciągania.

Mikrokontrolery Atmel AVR.

Firma Atmel rozpoczęła swoją przygodę od produkcji układów zgodnych z 8051, ale z pamięcią wewnętrzną typu Flash. Był to dość poważny skok jakościowy i szybko te modele stały się popularne. Jednak firma nie osiadła na laurach i przy projektowaniu swojej własnej rodziny mikrokontrolerów: AVR zauważono pewne niedogodności związane z posiadaniem tylko jednego rejestru dla portu we-wy. Dlatego w tych procesorach mamy już trzy rejestry związane z portami:

1. DDRx - Rejestr ustalający w którym kierunku działa port.
2. PORTx - Rejestr wyjściowy portu, albo rejestr sterujący podciąganiem wejścia.
3. PINx - Rejestr wejściowy do bezpośredniego czytania stanu końcówki lub zmiany jej stanu na przeciwny.

Popatrzmy teraz na strukturę potu.

rys4.png

Od razu widać, że jest o wiele bardziej skomplikowany, choć struktura dalej przypomina tą jaką poznaliśmy na początku.

Praca jako wyjście.

W przypadku pracy jako wyjście musimy uaktywnić część znajdującą się w środkowej części tego rysunku. Mamy tam dwa przerzutniki i bufor pracujący trójstanowo. Górny przerzutnik odpowiada za załączenie bufora wyjściowego i jest połączony z rejestrem portu DDRx. Dlatego aby praca jako wyjście była możliwa trzeba wpisać jedynki do DDRx. Wtedy stan drugiego przerzutnika połączonego z rejestrem PORTx będzie przekazywany na końcówkę portu.
Tu można zauważyć też pewien sprytny układ ze sprzężeniem zwrotnym za pomocą multipleksera. Układ ten jest połączony z rejestrem PINx. W niektórych procesorach AVR - zwłaszcza w tych nowszych - rejestr PINx może też pełnić rolę szybkiego przełącznika stanu portu. Przy pracy jako wyjście, zapisanie jedynki do rejestru PINx spowoduje szybkie przełączenie stanu przerzutnika. Stąd nie potrzebujemy trzech operacji Read-Modify-Write. Wystarczy tylko jeden szybki zapis!

Podsumowując, praca jako wyjście będzie realizowana tak:

Kod: Zaznacz cały

DDRA = 0xFF;  // aktywacja wyjścia
PORTA = 0xFF;  // wymuszamy stany wysokie na wyjściach

PINA = 0xFF;  // tak możemy szybko przełączyć stan wyjścia na przeciwny.


Ponieważ układ odczytu jest ciągle aktywny przy aktywnym stanie procesora, to odczyt z rejestru PINx będzie odczytywał rzeczywisty stan portu. Aby nie doszło jednak do pewnych konfliktów i gry na zboczach dodano prosty układ synchronizacji, który opóźnia o dwa takty ten sygnał. Przy pisaniu programów w językach wysokiego poziomu ten mechanizm jest ukrywany i kod jest generowany tak by nie doszło do pewnych perturbacji przy natychmiastowym odczycie wystawionego stanu. Przy programowaniu w kodzie maszynowym trzeba o tym pamiętać i odpowiednio napisać kod dodając np. pustą instrukcję.

INFO: W przeciwieństwie do 8051 układy AVR mogą sterować - w ograniczonym jednak stopniu - diody LED bezpośrednio z końcówek. Dzieje się tak, ponieważ deklarowana wydajność prądowa portu w stanie wysokim to umożliwia.

Praca jako wejście, z podciąganiem lub bez.

W przypadku pracy jako wejście mamy stale uaktywnioną część znajdującą się na dole rysunku i ewentualnie część górną z tranzystorem. Nasz górny przerzutnik jeśli nie będzie aktywny i w DDRx będzie 0 zablokuje nam bufor wyjściowy, a dolny przerzutnik będzie kontrolował wtedy tylko tranzystor podciągający przez stan rejestru PORTx. Wpisując jedynkę do przerzutnika górna bramka załączy tranzystor.

Podsumowując, praca jako wejście będzie realizowana tak:

Kod: Zaznacz cały

DDRA = 0x00;  // aktywacja wejścia
PORTA = 0xFF;  // możemy wymusić podciągnięcie wpisując jedynki lub nie wpisując zera

if(PINA == 0xFF) { }


Mikrokontrolery Texas Intruments MSP430.

Mikrokontrolery MSP430 - mało u Nas znane - to 16-bitowe jednostki. Do niedawna były one liderem w energooszczędności i są dalej wykorzystywane w takich aplikacjach. Pomimo, iż rdzeń pracuje w domenie 16-bitów to dostęp do urządzeń peryferyjnych zwykle odbywa się już magistralą 8-bitową. Stąd dla każdego portu GPIO przeznaczono cztery 8-bitowe rejestry związane z pracą portu:

1. PxDIR - Rejestr ustalający w którym kierunku działa port.
2. PxOUT - Rejestr wyjściowy portu.
3. PxIN - Rejestr wejściowy do czytania stanu końcówki.
4. PxSEL - Rejestr wyboru pracy portu jako GPIO bądź układu peryferyjnego.

Popatrzmy teraz na strukturę potu.

rys5.png

Doszedł nowy rejestr i układ portu musi zostać dodatkowo obudowany logiką kombinacyjną w postaci dwóch multiplekserów. Tutaj od razu powinno się Wam rzucić w oczy to, że dany pin może pracować tylko w jednym kierunku. Odpowiednie bufory w prawej części są blokowane lub aktywowane i nie ma możliwości wystawiania stanu i jednoczesnego jego czytania wprost z końcówki.

Praca jako wyjście zwykłego portu.

W przypadku pracy jako zwykłe wyjście musimy uaktywnić drugi od góry MUX podłączony do rejestru PxOUT oraz bufory z prawej przełączyć w tryb wyjściowy. Odbywa się to przez odpowiednie ustawienie rejestru PxDIR. Podobnie jak w przypadku AVR musimy wpisać do PxDIR jedynki by taki stan uzyskać. Wtedy stan rejestru PxOUT będzie przekazywany na końcówkę portu.

Kod: Zaznacz cały

P1SEL = 0x00;  // praca jako zwykły GPIO
P1DIR = 0xFF;  // praca jako wyjście
P1OUT = 0xFF;  // wymuszenie stanów wysokich na wyjściach
(...)
P1OUT = 0x00; // wymuszenie stanu niskiego na wyjściach


Ponieważ układ buforów jest przełączony na wyjście to odczyt z rejestru PxIN będzie odczytywał tylko stan wyjściowy MUX-a a nie rzeczywisty stan końcówki, którą np. urządzenie peryferyjne może ściągnąć do masy.

Praca jako wejście zwykłego portu.

W przypadku pracy jako zwykłe wejście musimy przełączyć bufory z prawej w tryb wejściowy. Odbywa się to przez wyzerowanie rejestru PxDIR. Wtedy stan końcówki będzie mógł być przekazany do rejestru PxIN. Nie mamy tutaj możliwości włączenia wewnętrznego podciągania. Trzeba je dodać na zewnątrz.

Kod: Zaznacz cały

P1SEL = 0x00;  // praca jako zwykły GPIO
P1DIR = 0x00;  // praca jako wejście
if (P1IN == 0xFF) { }


W nowszych mikrokontrolerach MSP430 konstruktorzy rozwiązali problem braku podciągania dodając kolejny rejestr PxREN. Zatem nie każdy mikrokontroler z rodziny MSP430 tą funkcję posiada i trzeba uważnie przeczytać Reference Manual dla danej rodziny. Za jego pomocą można włączyć podciąganie pull-up, lub pull-down jeśli zapiszemy do niego jedynki a stan rejestru PxDIR wskazuje na pracę jako wejście. Kontrolą nad tym, czy będzie to pull-up czy pull-down zajmuje się wtedy rejestr PxOUT.

Praca jako wyjście lub wejście układu peryferyjnego.

Kierunkowość pracy portu obowiązuje nas także jeśli zamiast zwykłego GPIO na danej końcówce ma się pojawić sygnał z lub do urządzenia peryferyjnego, np. USART. Dlatego ta część odpowiedzialna za kierunek jest taka sama jak w powyższych przypadkach. Jedyną zmianą jaką należy wykonać to przestawić rejestr PxSEL by przełączyć oba MUX-y odłączając rejestry PxIN/PxOUT. Zmiany rejestru PxOUT nie będą odzwierciedlane na wyjściach, a PxIN będzie powtarzał stan rejestru wyjściowego lub stan wejścia układ peryferyjnego.

Kod: Zaznacz cały

P1SEL = 0xFF;  // praca jako peryferia
P1DIR = 0xFF;  // praca jako wyjście

P1DIR = 0x00;  // praca jako wejście


Rejestrami sterującymi komutacją pinów dla układu przerwań - część dolna struktury portu - nie będziemy się na razie tu zajmować.

Mikrokontrolery Microchip PIC.

Mikrokontrolery Microchip PIC w przypadku portów we-wy mają sporą gamę rozwiązań układowych do sterowania końcówkami portów. Trzeba zatem pamiętać, że prawie każdy port jest tu skonstruowany inaczej. Co więcej, niektóre linie jednego portu wykazują tu również pewne różnice.

ProTip: Dlatego zawsze należy zajrzeć do noty katalogowej danego procesora, jakie porty są dostępne i czy nie ma tam wyjątków.

PORTA lub GPIOx
Generalnie PORTA/porty GPIOx mają konstrukcję typu push-pull. Wyjątek stanowi pin 4 PORTA gdzie pracuje on tylko jako open-drain.

rys6.png
rys6_1.png

Mamy tu trzy przerzutniki. Przerzutnikiem TRIS ustala się, czy dany port będzie pracował jako port wejścia czy jako port wyjścia. W przypadku wpisania do niego stanu 1 stan jego wyjść będzie blokował tranzystory wyjściowe, pozwalając na niezakłóconą pracę bufora wejściowego. Jeśli zaś wpiszemy do niego stan 0 to tranzystory zostaną odblokowane i ich stanem będzie zarządzać przerzutnik Data. Tutaj podobnie jak w przypadku AVR rzeczywisty stan końcówki można zawsze odczytać i stan ten jest powielany w dolnym przerzutniku.

INFO: Bramka OR dla logiki "ujemnej" - aktywne zera - zachowuje się jakby realizowała funkcję AND. I tu to wykorzystano.

PORTB
PORTB ma nieco inną konstrukcję. Pojawił się dodatkowy sygnał ~RBPU, który może wymusić na pinach tego portu tzw. słabe podciągnięcie do Vdd. Ten sygnał pochodzi z bitów konfiguracyjnych, więc pracę podciągnięcia ustala się sprzętowo w momencie startu mikrokontrolera.

rys7.png
rys7_1.png

Aby to podciągnięcie działało port musi pracować jako wejście, stąd sygnał ~RBPU jest bramkowany wyjściem przerzutnika TRIS.
Jak widać na drugim obrazku piny 4..7 mają dodatkowe obwody logiczne, ponieważ mogą pracować też jako źródła przerwań. Ogólna zasada pracy portu jest taka sama.

PORTF, PORTG
Ciekawym przypadkiem są porty PORTF/PORTG, które mogą pracować wyłącznie jako wejścia lub jako wyjścia do sterowania wyświetlaczy LCD (bez sterownika). Nie ma tu żadnych przerzutników konfiguracyjnych, stąd też nie ma rejestru konfiguracji kierunku pracy portu, ale jest sygnał LCDSE pochodzący z układu sterownika LCD blokujący bufor wejściowy.

rys8.png


Inne specyficzne rozwiązania
W niektórych nowszych procesorach PIC występuje możliwość odczytywania samego stanu przerzutnika wyjściowego, bez względu na to jaki aktualnie stan aktualnie występuje na samej końcówce - jest on aktywowany sygnałem Read LAT. Taka procedura jest potrzebna do prawidłowej pracy przy operacjach Read-Modify-Write na portach. Dlatego też przy samym odczycie rejestru portu nie czyta się sygnału zza bufora tylko przed buforem, co gwarantuje uzyskanie informacji o wartości wpisanej do portu, a nie zastanej. Przy zapisie nic się nie zmienia - w dalszym ciągu zapisujemy stan przerzutnika sterującego stanem.

rys9.png


Od strony programowej
Od strony programowej są dostępne dwa lub trzy główne rejestry związane z pracą portów:

1. TRISx - służący do określenia w jakim kierunku dany port ma działać, o ile jest on dwukierunkowy,
2. PORTx - służący do odczytywania stanu końcówek, a w przypadku braku rejestru LATx również do wystawiania stanu na port,
3. LATx - służący do wystawiania stanu na port lub, w celu jego modyfikacji, w operacjach Read-Modify-Write.

Proste operacje ustawiania stanu portu

Kod: Zaznacz cały

TRISB = 0x00;  // praca jako wyjście

PORTB = 0xFF;  // wymuszenie stanów wysokich na wyjściach
(...)
PORTB = 0x00; // wymuszenie stanu niskiego na wyjściach


Proste operacje ustawiania stanu portu w przypadku obecności rejestru LATx

Kod: Zaznacz cały

TRISB = 0x00;  // praca jako wyjście

LATB = 0xFF;  // wymuszenie stanów wysokich na wyjściach
(...)
LATB = 0x00; // wymuszenie stanu niskiego na wyjściach


Sprawdzenie stanu wejść

Kod: Zaznacz cały

TRISB = 0xFF;  // praca jako wejście

if (PORTB == 0xF0) { }  // akcja przy stanach niskich na RB3...RB0


Operacja zmiany stanu pinów na przeciwny z wykorzystaniem rejestru LATx poddanego operacji XOR.

Kod: Zaznacz cały

TRISB = 0x00;  // praca jako wyjścia
(...)
LATB ^= 0b00001111;  // zmiana stanu na przeciwny najmłodszych bitów


Mikrokontrolery Atmel XMEGA.

Atmel po sukcesie rodziny AVR zaprojektował i wprowadził na rynek ich nowe wcielenie w postaci procesorów XMEGA. Zostały one znacznie rozbudowane w stosunku do swoich starszych braci pod względem układów peryferyjnych. Niestety wprowadzono je nieco zbyt późno, stąd nie zyskały już takiej popularności.

Układ portów GPIO jest już jak najnardziej "wpółczesny" i stąd jego konstrukcja jest jeszcze bardziej rozbudowana. Za to dostajemy sporo funkcji dodatkowych, a to jeszcze nie koniec niespodzianek.

rys10.png

Można tu wydzielić trzy zasadnicze bloki:
- Część górna to układ konfiguracji opcji dodatkowych portu,
- Część środkowa to klasyczny układ bufora wyjściowego znanego już z rodziny AVR,
- Część dolna to również klasyczny układ bufora wejściowego również znanego z rodziny AVR.

Bufor wejściowy i towarzyszące mu układy to niemal 100% kopia układu z procesorów AVR. Również mamy tu bufor dołączony bezpośrednio do pinu - choć można zauważyć dodatkowy układ łącznikowy, o którym później - oraz synchronizator w postaci krótkiego rejestru przesuwającego, którego ostatni człon stanowi dostępny dla programu przerzutnik IN.

Bufor wyjściowy to klasyczny układ push-pull połączony z dwoma przerzutnikami. Przerzutnik DIR określa kierunek pracy portu i blokuje układ wyjściowy jeśli port ma pracować jako wejście, pozwalając na swobodną pracę układu bufora wejściowego. Przerzutnik OUT zaś to rejestr wyjściowy portu, którym możemy wstawić odpowiedni stan. Tutaj odmiennie niż w układach AVR nie służy on do sterowania podciąganiem. Tym zajmuje się już układ konfiguracji, który wymaga osobnego podrozdziału.

Układ konfiguracji pracy portu
Układ konfiguracji to dość pokaźna struktura logiczna pozwalająca na aktywowanie kilka niespotykanych jak dotąd funkcjonalności portu. Pierwsze co powinno się nam rzucić w oczy to sygnały Pull Enable razem z Pull Direction, pozwalające na naprzemienne włączanie dwóch tranzystorów łączących "rezystor" podciągający do zasilania - realizując funkcję pull-up, albo do masy - realizując funkcję pull-down:

rys11.png

Tranzystory te mogą też pełnić rolę układu podtrzymywania stanu - bus keeper. Wtedy sygnał Pull Keep pozwala na przełączenie multiplekserem obwodu sprzężenia zwrotnego. Układ taki pozwala na podtrzymywanie ostatniego stanu pinu, nawet gdy nie jest on już wymuszany z zewnątrz. Przy takiej konfiguracji stan pinu nigdy nie będzie pływający - wysoka impedancja, zawsze będzie podciągnięty do 1 lub 0.

rys12.png

Następną ciekawą funkcją związaną już z konfiguracją tranzystorów bufora wyjściowego jest sumowanie lub mnożenie "na drucie" - Wired-OR, Wired-AND. Odpowiada za to dodatkowy sygnał Wired OR/AND, który może wyłączać jeden z tranzystorów układu push-pull. Gdy wyłączony zostanie dolny tranzystor to końcówka może pracować w konfiguracji Wired-OR. Jeśli zaś wyłączony zostanie górny tranzystor to wyjście będzie pracować w konfiguracji Wired-AND.
Dodatkowo sterując sygnałami Pull-Enable, Pull-Direction można tym dwóm konfiguracjom dodawać również wewnętrzne rezystory podciągające.

rys13.png

Także unikalną funkcją jest przełączanie logiki pracy wyjścia na logikę "ujemną" - gdzie stanem aktywnym jest stan niski. Funkcją tą zarządza sygnał Inverted I/O, który aktywuję bramki EX-OR zarówno w obwodzie bufora wyjściowego jak i wejściowego. Dzięki temu można normalnie operować bitami w rejestrze portów na standardowych poziomach i dopiero w module GPIO sygnały zostaną odwrócone.

INFO: Odwrócona logika obowiązuje zarówno na wyjściu jak i na wejściu. Dlatego trzeba uważać by nie popełnić błędu w programie, np. oczekując nie tego stanu logicznego co potrzeba.

Strona programowa - Podstawowa
Jak już pewnie się domyślacie, takie skomplikowanie portów odbije się na większym skomplikowaniu rejestrów portu. I tak, i nie. Gdybyśmy porty w układach XMEGA traktowali tylko jak zwykłe GPIO to tak jak w przypadku AVR mamy trzy główne rejestry:
- DIR - służący do określania kierunku pracy portu,
- OUT - służący do określania stanu wyjściowego portu,
- IN - służący do określania bieżącego stanu końcówki portu.

Dlaczego nie napisałem DIRx albo OUTx - gdzie "x" oznaczałby nazwę portu: A, B, C, itd.? Otóż w przypadku urządzeń peryferyjnych w układach XMEGA obowiązują nieco inne zasady dostępu do rejestrów. Każde urządzenie lub grupa ustawień ma tzw. adres bazowy i poszczególne rejestry są po prostu przesunięte o określony offset względem tego adresu. Daje to pewne możliwości dla projektanta kodu by w razie zmian układowych - np. przeniesienie wyświetlacza z portu PORTB na PORTD bez zmiany kolejności połączeń - mógł szybko zmieniać kod dokonując wyłącznie zmiany adresu bazowego.
Takie podejście do rejestrów pozwala też na utworzenie specjalnych struktur, rzutowanych pod konkretne adresy. Dzięki temu kod źródłowy staje się również bardziej przejrzysty. Dlatego też chcąc się odwołać do rejestru OUT portu PORTA moglibyśmy napisać PORTA.OUT. Właśnie taki sposób jest to rozwiązane w przypadku stosowania gotowych plików z definicjami przygotowanymi przez Atmel-a.

Dlatego proste operacje na portach będą wyglądać następująco:

Kod: Zaznacz cały

PORTA.DIR = 0xFF;  // aktywacja wyjścia
PORTA.OUT = 0xFF;  // wymuszamy stany wysokie na wyjściach
(...)
PORTA.OUT = 0x00;  // wymuszamy stany niskie na wyjściach

Kod: Zaznacz cały

PORTA.DIR = 0x00;  // aktywacja wejścia

if(PORTA.IN == 0x00) { }


Strona programowa - Zaawansowana
By dopełnić opisu musimy powinniśmy poznać jeszcze kilka rejestrów należących do grupy rejestrów danego portu. Spójrzmy na pełną mapę rejestrów. W tej chwili będą nas interesować tylko te zaznaczone w czerwonych ramkach:

rys14.png

Część rejestrów z górnej ramki już poznaliśmy. Pozostałe 6 rejestrów służy do szybkich operacji bitowych na odpowiadających im rejestrach.
- DIRSET/OUTSET - służą do szybkiego - bez operacji Read-Modify-Write - nadawania stanu wysokiego na danych bitach rejestru DIR/OUT.
- DIRCLR/OUTCLR - służą do szybkiego - bez operacji Read-Modify-Write - nadawania stanu niskiego na danych bitach rejestru DIR/OUT.
- DIRTGL/OUTTGL - służą do szybkiego - bez operacji Read-Modify-Write - nadawania stanu przeciwnego na danych bitach rejestru DIR/OUT.
Aby zatem dokonać szybkiej zmiany stanu zgodnej z przeznaczeniem danego rejestru, wystarczy tylko wpisać 1 na danym bicie.

Na przykład kod:

Kod: Zaznacz cały

PORTA.DIRSET = PIN1_bm;  // aktywacja wyjścia na pinie PA1
PORTA.OUTSET = PIN1_bm;  // i wymuszamy stan wysoki
(...)
PORTA.OUTTGL = PIN1_bm;  // tak możemy szybko przełączyć stan PA1 na przeciwny.


Rejestry z dolnej ramki służą do konfiguracji opcji dodatkowych dla poszczególnych pinów, dlatego też każdy pin ma osobny rejestr PINxCTRL - gdzie "x" to tym razem numer pinu od 0 do 7.

rys15.png

Przy obsłudze portów jako zwykłe wejścia-wyjścia będą nas interesować bity:
- INVEN - kontrolujący sygnał Inverted I/O i pozwalający na włączenie logiki ujemnej dla danego pinu. Gdybyśmy chcieli by PB2 pracował w tym trybie to możemy napisać:

Kod: Zaznacz cały

PORTB.PIN2CTRL |= (1 << PORT_INVEN_bm);


- OPC[2..0] - to trzy bity kontrolujące dodatkowe funkcje danego portu. Poszczególne kombinacje tworzą tabelkę z możliwymi ustawieniami:

Kod: Zaznacz cały

OPC[2:0] | Nazwa zdefiniowana       | Funkcja    | Podciąganie
---------------------------------------------------------------
   0b000 | PORT_OPC_TOTEM_gc        | Push-pull  | Brak
   0b001 | PORT_OPC_BUSKEEPER_gc    | Push-pull  | Bus-keeper
   0b010 | PORT_OPC_PULLDOWN_gc     | Push-pull  | Pull-down (Wejście)
   0b011 | PORT_OPC_PULLUP_gc       | Push-pull  | Pull-up (Wejście)
   0b100 | PORT_OPC_WIREDOR_gc      | Wired-OR   | Brak
   0b101 | PORT_OPC_WIREDAND_gc     | Wired-AND  | Brak
   0b110 | PORT_OPC_WIREDORPULL_gc  | Wired-OR   | Pull-down
   0b111 | PORT_OPC_WIREDANDPULL_gc | Wired-AND  | Pull-up


Na przykład, gdybyśmy szybko chcieli skonfigurować pin PE4 do pracy jako wejście z podciągnięciem do plusa, możemy napisać:

Kod: Zaznacz cały

PORTE.DIRCLR = PIN4_bm; 
PORTE.PIN4CTRL = PORT_OPC_PULLUP_gc;


Porty wirtualne
Konstruktorzy rodziny XMEGA dodali jeszcze jedną niespodziankę: tzw. porty wirtualne. Porty te to cztery grupy rejestrów VPORTx, które można skonfigurować tak, by ich poszczególne rejestry IN, OUT, DIR zostały zmapowane do rzeczywistych portów GPIO. Struktura rejestrów wirtualnych portów:
rys16.png

Zapis do rejestru portu wirtualnego będzie się równał zapisowi rejestru w połączonym z nim portem GPIO.

Oczywiście wcześniej należy skonfigurować ich przynależność do realnych portów korzystając z rejestrów VPCTRLA i VPCTRLB z grupy PORTCFG.
rys17.png


Kod: Zaznacz cały

PORTCFG.VPCTRLA = PORTCFG_VP0MAP_PORTC_gc; // mapujemy PORTC do VPORT0
VPORT0.DIR = 0xFF;  // aktywacja wyjścia
VPORT0.OUT = 0x00;  // wymuszamy stany niskie na wyjściach



Ciąg dalszy nastąpi...
Do czasu powstania pełnej treści nie piszcie odpowiedzi. Jeśli mam błąd lub macie uwagi to przez prywatne wiadomości.

W kolejce stoi jeszcze kilka rodzin mikrokontrolerów: STM32 M0 i M3. Więc nieco cierpliwości zanim całość dopiszę.


p.s. Fragment o mikrokontrolerach PIC powstał we współpracy z kol. Antystatyczny.
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.

Wróć do „Inne mikroklocki, również peryferyjne”

Kto jest online

Użytkownicy przeglądający to forum: Obecnie na forum nie ma żadnego zarejestrowanego użytkownika i 0 gości