Brak produktów
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.
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 poniżej i sprawdźmy, które piny jaką funkcję realizują. Zamieszczam tą tabelkę, by podkreślić dwie bardzo istotne sprawy:
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:
Jeśli chcemy wykorzystywać przerwania, musimy jeszcze ustawić priorytet przerwań w rejestrze INTCTRL oraz uruchomić kontroler przerwań PMIC w XMEGA. 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
#include <avr/io.h> #include <avr/interrupt.h> 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 }