wtorek, 1 marca 2016

[1] ATXmega - 128A3U - Porty GPIO

W tym poście chciałbym przedstawić podstawowy program na mikrokontrolery ATmega, którego zadaniem będzie, jak łatwo można się domyślić, mruganie diodami.

Wgranie programu


W tego typu Atmegach z wgranym bootloaderem FLIP, kod wgrywa się na układ za pomocą programu o takiej samej nazwie dodatkowo należy się zaopatrzyć w kabel USB. Jest on dostępny na stronie producenta. Do wgrywania wykorzystuje się plik HEX który powstaje w procesie kompilacji kodu na innym programie.

Sposób obsługi programatora oraz tworzenie nowego pliku projektu opisano na stronie Leon Instruments, Link zamieściłem w sekcji głównej ATmega dla tego bloga. 

Opis portów we/wy


W tym poście przedstawię sposób konfiguracji portów wejścia wyjścia.

Każdy z portów składa się z 8 pinów. Pin może być ustawiany z 8 bitów. Wykorzystywany przeze mnie układu posiada porty A, B, C, D, E, F, każdy z nich ma 8 pinów o numerach od 0 do 7. Sposoby odwołania się polegają na podaniu numer czy to w formacie binarnym, szesnastkowym czy dziesiętnym, poprzez podanie maski, która jest kolejną potęgą liczby 2 dla następnych wyprowadzeń, lub z wykorzystaniem zdefiniowanych wcześniej nazw w pliku avr/io.h. 

Przykładowe ustawiania pinu przedstawiłem poniżej:

PORTA.DIR = PIN2_bm;      //bit mask
PORTB.DIR = (1<<PORT4);   //po nazwie, operacja przesiniuęcia biowego
PORTC.DIR = 0b00000011;   //za pomocą wartosci binarnej
PORTD.DIR = 0x03          //wartosc szesnastkowa
PORTE.DIR = 1             //wartosc dziesietna

Należy pamiętać, że po resecie układu wszystkie piny są ustawione jako wejściowe bez podłączonego podciągania zasilania z VCC lub uziemienia z GND. Jest on wtedy w stanie wysokiej impedancji, którego opór wynosi setki MOhm.

Każdy z pinów jest odpowiednio zabezpieczony przez diody wejściowe. W celu dodatkowej ochrony należałoby wykorzystać diody Schotkiego. 

Do pinów XMEGA można bezpiecznie podłączać napięcia, które mieszczą się przedziale wartości bezpiecznej czy takiej z zakresu napięć zasilania (od GND do VCC).

Kolejnym elementem jest wydajność prądowa pinów wyjściowych. Limit prądu jaki może popłynąć przez ten pin wynosi około 20-25 mA. Przy czym maksymalny prąd jaki można pobrać z całego układu wynosi 200 mA. Czyli ta wartość musi być rozłożona na ilość wykorzystywanych pinów. 

Programowanie


Programowanie opiera się na wprowadzeniu odpowiedniej wartości do rejestrów mikrokontrolera. Może to być wykonane tak jak w innych rodzajach AVR-ów, czyli poprzez wprowadzenie następującej komendy:

  • nazwa zmienianego rejestru = (1<<BIT1) | (1<<BIT2)

Drugi sposób jest nowszy. Do ich zaprogramowania wykorzystuje się struktury:

  • nazwa układu peryferyjnego.nazwa rejestru = ....; np. PORTA.DIR, PORTB.OUT, PORTA.DIRCLR.
Poniżej przedstawiam krótki opis niektórych ważniejszych rejestrów.

Jednym z podstawowych rejestrów który daje możliwość ustawiania pewnych opcji na pinie jest PINxCTRL. Do każdego pinu przyporządkowany jest inny rejestr kontrolny.


Rys. 1. Rejestr PINxCTRL

Pozwala on na ustawienie rezystorów podciągających, szybkość narastania zbocza, przerwania czy np. odwrócenie wartości podawanych na wyprowadzenie.

DIR (Data Direction Register) służy do ustawienia stanu wejścia bądź wyjścia dla konkretnego wyprowadzenia pinu. Wprowadzenie wartości 1 oznacza wyjście a zero to wejście. Składa się on z 8 bitów. 

Rys. 2. Rejestr DIR

Kolejnym rejestrem jest DIRSET (Data Direction Set). Pozwala on na ustawienie pinów jako wyjścia. Wpisanie 1 pozwala na ustawienie wartości. Odczytanie zwróci wartość znajdującą się w rejestrze. Jest on również rejestrem 8 bitowym numerowanym od 0 do 7.


Rys. 3. Rejestr DIRSET

Następnym rejestrem jest DIRCLR (Data Direction Clear). Pozwala on na ustawienie poszczególnych pinów jako wejścia. Wpisanie do niego wartości 1, czyści odpowiadający bit w rejestrze DIR. 


Rys. 4. Rejestr DIRCLR

Jest jeszcze rejestr DIRCR (Data Direction Toggle). Wpisanie jedynki spowoduje przełączenie stanu na wybranym pinie.

Teraz pora na rejestr OUT (Data Output Value) pozwala on na ustawienie odpowiedniego stanu na pinie. Wprowadzenie jedynki powoduje pojawienie się stanu wysokiego, 0 natomiast stan niski.


Rys. 5. Rejestr OUT

Kolejny rejestr z tego zestawu to OUTCLR (Data Output Value Clear). Pozwala on na ustawienie stanu niskiego na wybranym pinie poprzez wpisanie wartości 1. Przykład użycia dla pinu 5 wygląda następująco: 
PORTC.OUTCLR = PIN5_bm;

Następnie OUTSET (Data Putput Value Set). W tym przypadku wpisanie 1 ustawia stan wysoki. Przykład użycia dla pinu 5 wygląda następująco:
PORTC.OUTSET = PIN5_bm;

OUTTGL ( Data Output Value Toggle ) przełącza stan na wybranym wyprowadzeniu. 

Kolejna grupa rejestrów IN ( Data Input ). Jest to rejestr wejściowy który służy do odczytania stanu jaki w danym momencie znajduje się na pinie. Dodatkowo wybrane wejście nie jest próbkowane, aby odczytać stan bufor wejściowy musi być włączony. 

Rys. 6. Rejestr IN

INTCTRL (Interrupt Control). Jest on podobnie jak poprzednie siedmiobitowy, z tą różnicą, że nie do wszystkich bitów można uzyskać dostęp. Wartości 7 do 4 są zarezerwowane dla wykorzystania w przyszłości. Dla uzyskania odpowiedniej kompatybilności z innymi mikrokontrolerami, te bity, przy wprowadzaniu danych do rejestru, należy ustawić na 0. Pozostałe wartości pozwalają na włączenie odpowiedniego przerwania dla portu.


Rys. 7. Rejestr INTCRTL

INT0MASK (Interrupt 0 Mask). Pozwala na ustawienie maskowania dla pinów wykorzystywanych do przerwania 0. Wprowadzenie 1 ustawia pin na przerwaniu 0.

INT1MASK (Interrupt 1 Mask). Działa tak samo jak poprzedni opisywany, z tą różnicą, że ustawiane jest przerwanie pierwsze.

INTFLAGS (Interrupt Flag). Bity od 7 do 2 są zarezerwowane. Flagi są ustawiane gdy zostanie wykryta odpowiednia zmiana stanu. Ich zerowanie wykonuje się poprzez wprowadzenie wartości 1 w odpowiednie miejsce w rejestrze.


Rys. 8. Rejestr INTFLAGS

I ostatni jaki będzie omawiany w tym poście czyli REMAP. Jego zadaniem jest przemapowienie funkcji pinów jak np. SPI na inne dostępne wyprowadzenia.


Rys. 9. Rejestr REMAP

Bit 7 oraz 6 są zarezerwowane. W przypadku wprowadzania danych do rejestru, aby zachować kompatybilność, należy do nich wprowadzić 0.
Bit 5 SPI. Pozwala na zmianę pinów interfejsu SPI poprzez wpisanie 1.
Bit 4 zmienia piny USART0 z Px[3:0] na Px[7:4].
Bit 3 TC0D Timer 0, Przesuwa lokację z Px3 na Px7.
Bit 2 TC0C z Px2 na Px6.
Bit 1 TC0B z Px1 na Px5.
Bit 0 TC0A z Px0 na Px4.

Przykładowy program


Program będzie dosyć prosty będą ustawione pewne wyprowadzenia, na które po kolei będzie podawany stan wysoki, przerwa czasowa, stan niski. Co pozwoli na cykliczne mruganie podłączonymi diodami. Wykorzystałem trzy diody zostały one podłączone z jednej strony do GND, z drugiej natomiast do wyprowadzeń A1, B3 oraz C6.

#define F_CPU 2000000UL
#include <avr/io.h>
#include <util/delay.h>
 
void Mrugaj(void);
 
int main(void)
{
 //Ustawienie pinu jako wyjściowy
 //Za pomocą bitu maski
 PORTA.DIR = PIN1_bm;
 //Operacja przesunięcia bitowego
 PORTB.DIR = (1<<PORT3);
 //Za pomocą bitu maski
 PORTC.DIR = PIN6_bm;
 
    while(1)
    {
 Mrugaj();
    }
}
 
//Zmiana stanu pinu 0 na przeciwny
void Mrugaj(void)
{
 _delay_ms(500);
 //Zapalenie diody
 PORTA_OUT |= (1<<PIN1_bp);
 _delay_ms(500);
 //Zgaszenie diody
 PORTA_OUT &= ~(1<<PIN1_bp);
 _delay_ms(500);
 //Zapalenie diody
 PORTB.OUTSET = PIN3_bm;
 _delay_ms(500);
 //Zgaszenie diody
 PORTB.OUTCLR = PIN3_bm;
 _delay_ms(500);
 PORTC.OUTSET = PIN6_bm;
 _delay_ms(500);
 PORTC.OUTCLR = PIN6_bm;
}

Poniżej przedstawiam drugi program, który dodatkowo obsługuje przycisk podłączony do pinu E5.

#define F_CPU 2000000UL
#include <avr/io.h>
#include <util/delay.h>
 
void Mrugaj(void);
 
int main(void)
{
 //Ustawienie pinu jako wyjściowy
 //Za pomocą bitu maski
 PORTA.DIR = PIN1_bm;
 //Operacja przesunięcia bitowego
 PORTB.DIR = (1<<PORT3);
 //Za pomocą bitu maski
 PORTC.DIR = PIN6_bm;
 
 //Ustawienie na pinie E3 wejście
 //z podciągnięciem do zasilania
 PORTE.DIRCLR = PIN5_bm;
 PORTE.PIN5CTRL = PORT_OPC_PULLUP_gc;
 
    while(1)
    {
 Mrugaj();
 
 //Sprawdza czy na pinie jest E5 jest stan wysoki
 if(!(PORTE.IN & PIN5_bm))
 {
  PORTB.OUTTGL = PIN3_bm;
 }
    }  } 

Na końcu słów kilka co do sposobu programowania. Jest on na pewno w pewny sensie nowatorski, Zdecydowanie oszczędza ilość elementów na płytce, przez co jest ona tańsza oraz oczywiście mniejsza. 

Jeśli chodzi o samą wygodę to jest ona średnia. Zdecydowanie byłoby łatwiej gdy by były one dostępne w jednym programie jak np. w STM32, gdzie wgrywanie następuje bezpośrednio z programu głównego w którym kod został napisany. Więc wniosek jest prosty jeśli nie ma dużej ilości zmian w kodzie, i zależy nam na jak najtańszym zakupie wtedy płytki z wgranym Flipem są jak najbardziej w porządku.

Bibliografia


[1] 8-bit Atmel XMEGA AU Microcontroller
[2] Leon Instruments Kurs ATXMega
[3] Francuz T. AVR Praktyczne projekty