LinkedIn YouTube Facebook
Szukaj

Newsletter

Proszę czekać.

Dziękujemy za zgłoszenie!

Wstecz
Artykuły

Mikrokontrolery AVR XMEGA w praktyce, część 18. SPI w XMEGA

SPI w XMEGA

Mikrokontroler ATxmega128A3U posiada trzy interfejsy SPI, dostępne w portach C, D, E, a nazwy tych interfejsów to odpowiednio: SPIC, SPID, SPIE. Są one funkcjonalnie identyczne, a my w naszych przykładach wykorzystamy moduł SPIC. Wszystkie przykłady zostały przedstawione w jednym zbiorczym listingu 1.

Pierwsza rzecz, od której najlepiej zacząć, to konfiguracja pinów IO. W starych AVR-ach, włączenie interfejsu powodowało automatyczne skonfigurowanie pinów wejść i wyjść. W XMEGA musimy to zrobić samodzielnie. Konfiguracja pinów IO została opisana w odcinku 4. Spójrzmy zatem do ATxmega128A3U datasheet i w tabeli przedstawionej na rysunku 1, sprawdźmy, które piny jaką funkcję realizują. Zamieszczam tą tabelkę, by podkreślić dwie bardzo istotne sprawy:

  • Pin SS (slave select) można wykorzystać jako CS do sterowania slavem, kiedy ustawimy go jako wyjście. Jednak jeśli będzie on ustawiony jako wejście, (tak jest domyślnie po włączeniu procesora!), to pojawienie się stanu niskiego na pinie SS spowoduje przełączenie modułu SPI z trybu master do trybu slave. Jeśli Twój procesor zawsze ma pracować w trybie master, ustaw pin SS jako wyjście, nawet jeśli jako CS wykorzystujesz inne piny.
  • Przypis 4 pod tabelą mówi, że piny MOSI i SCK można zamienić miejscami. Po co? Zwróć uwagę na interfejs USART C1. Piny MOSI i SCK są funkcjonalnie identyczne jak TXD i XCK. Jeśli projektując płytkę, zamienimy te dwa piny, później pisząc program będziemy mogli wybrać, czy chcemy korzystać z SPI oraz USART. SPI jest prostsze, a USART jest trochę bardziej skomplikowany lecz daje większe możliwości. Zamiany dokonuje się, wpisując wartość PORT_SPI_bm do rejestru PORTx.REMAP. Pamiętaj, że w płytce eXtrino XL, którą wykorzystamy w tym kursie, piny MOSI i SCK są zamienione.

 

 

Rys. 1. Piny IO w XMEGA

 

Po skonfigurowaniu pinów IO, przechodzimy do konfiguracji właściwego interfejsu. Wystarczy wpisać odpowiednie wartości do zaledwie jednego rejestru o nazwie CTRL. Ustawiamy w nim kilka prostych parametrów:

  • SPI_ENABLE_bm – ustawienie tego bitu powoduje uaktywnienie interfejsu,
  • SPI_MASTER_bm – włączenie trybu master,
  • SPI_MODE_x_gc – grupa konfiguracyjna, decydująca m.in. o próbkowaniu i polaryzacji sygnału zegarowego (patrz rysunek 4 z poprzedniej części)
  • SPI_DORF – przesyłanie danych od najmłodszego bitu,
  • SPI_PRESCALER oraz SPI_CLK2X – ustawianie częstotliwości zegara.

Jeśli chcemy wykorzystywać przerwania, musimy jeszcze ustawić priorytet przerwań w rejestrze INTCTRL oraz uruchomić kontroler przerwać PMIC (opisane w odcinku 6). SPI może generować tylko jeden rodzaj przerwań o nazwie SPIx_INT_vect.

Do wysyłania i odbierania danych służy rejestr DATA. Pamiętaj, że SPI jest interfejsem full-duplex i nawet jeśli chcesz tylko odebrać jakieś dane, musisz coś wysłać, za przykład 0. Transmisja rozpoczyna się po wpisaniu bajtu danych do rejestru DATA. Następnie należy poczekać, aż dane zostaną przesłane, sprawdzając w pętli rejestr STATUS. Pojawienie się jedynki na pozycji SPI_IF_bm oznacza, zakończenie transmisji (co wywoła przerwanie, jeśli jest odblokowane). Dane wysłane ze slave’a do mastera możesz odczytać również z rejestru DATA.

Przed rozpoczęciem transmisji musisz ustawić pin CS wybranego slave’a w stan niski, a następnie poczekać na ustabilizowanie się napięcie na tym pinie. Do tego celu można użyć instrukcji czekania _delay_us(1) albo kilkukrotnie wkleić asm volatile(„nop”);. Po zakończeniu transmisji, pin CS musisz ustawić w stan wysoki.

Przykłady inicjalizacji, funkcji przesyłającej dane oraz procedury przerwania znajdziesz na listingu 1.

Listing 1. Kod programu demonstrującego działanie SPI w XMEGA



#include 
#include 
 
struct PORTX_t {                                      // struktura danych
    volatile uint8_t IN;                              // rejestr wejściowy
    volatile uint8_t OUT;                             // rejestr wyjściowy
} PORTX;

uint8_t SpiTransmit(uint8_t data) {                   // transmisja SPI
    SPIC.DATA       =    data;                        // wysyłanie danych
    while(SPIC.STATUS == 0);                          // czekanie na zakończenie transmisji
    return SPIC.DATA;                                 // odczytanie danych 
}

int main(void) {
    
    // sygnały CS dla peryferiów eXtrino XL
    PORTE.OUTSET    =    PIN3_bm | PIN6_bm;           // SD, PORTX, DIGPOT
    PORTE.DIRSET    =    PIN3_bm | PIN6_bm;           // SD, PORTX, DIGPOT
    
    // konfiguracja SPI
    PORTC.DIRSET    =    PIN4_bm | PIN5_bm | PIN7_bm; // wyjścia SPI
    PORTC.DIRCLR    =    PIN6_bm;                     // wejście SPI
    PORTC.OUTCLR    =    PIN7_bm | PIN6_bm | PIN5_bm | PIN4_bm;
    PORTC.REMAP     =    PORT_SPI_bm;                 // zamiana miejscami SCK i MOSI
    SPIC.CTRL       =    SPI_ENABLE_bm|               // włączenie SPI
                         SPI_MASTER_bm|               // tryb master
                         SPI_MODE_3_gc|               // tryb 3
                         SPI_PRESCALER_DIV64_gc;      // preskaler
    SPIC.INTCTRL    =    SPI_INTLVL_LO_gc;            // niski priorytet przerwań
    
    // przerwania
    PMIC.CTRL       =    PMIC_LOLVLEN_bm;             // włączenie przerwań o priorytecie LO
    sei();
    
    // pierwsza transmisja
    SPIC.DATA = 0;
    
    // pętla główna
    while(1) {
        if(PORTX.OUT == PORTX.IN) {                   // jeśli wciśnięto przycisk przy świecącej diodzie
            PORTX.OUT = PORTX.OUT << 1;               // przesuń diodę na następną pozycję
            if(PORTX.OUT == 0) PORTX.OUT = 1;         // jeśli ostatnia, zacznij od nowa
        }
    }
}

ISR(SPIC_INT_vect) {
    PORTE.OUTSET    =    PIN6_bm;                     // chip deselect
    asm volatile("nop");                              // czekanie na ustabilizowanie się pinu E6
    asm volatile("nop");
    asm volatile("nop");
    asm volatile("nop");
    asm volatile("nop");
    PORTE.OUTCLR    =    PIN6_bm;                     // chip select
    PORTX.IN        = SPIC.DATA;                      // odczytanie danych
    SPIC.DATA       =    PORTX.OUT;                   // rozpoczęcie nowej transmisji
                                                      // i wyjście z przerwania
}

 

Dystrybutorem zestawu X3-DIL64 oraz eXtrino XL jest KAMAMI.pl.

 

 

Dominik Leon Bieczyński

www.leon-instruments.pl