Obsługa sprzętowego mudułu SPI w mikrokontrolerach AVR
Interfejs SPI jest szybkim, dupleksowym, synchronicznym interfejsem szeregowym. Jest on stosowany do łączenia układu nadrzędnego (Master), sterującego transmisją z urządzeniami podrzędnymi (Slave). W rozpatrywanym przypadku funkcję nadrzędną pełni mikrokontroler. Urządzeniem podrzędnym może być np. pamięć EEPROM, zegar RTC, wyświetlacz LCD i wiele innych peryferiów wyposażonych w ten interfejs.
Przewagą SPI nad innymi interfejsami szeregowymi stosowanymi w mikrokontrolerach jest jego szybkość oraz prostota. Wadą może się okazać ilość sygnałów, które musimy zastosować do transmisji (co najmniej 3 dla transmisji dupleksowej). Nie jest ich co prawda dużo, ale czasami zbyt wiele.
Opisywana implementacja interfejsu dotyczy wykorzystania sprzętowego modułu SPI dostępnego w mikrokontrolerach AVR (nie we wszystkich). Istnieje możliwość wykorzystania modułu USI (Universal Serial Interface), ale o tym innym razem.
1. Dedykowane rejestry
Rejestr SPCR

SPCR
Znaczenie poszczególnych bitów jest następujące:
SPIE– aktywacja obsługi przerwań dla modułu SPI,SPE– włączenie modułu SPI,DORD– ustawienie tego bitu oznacza, że jako pierwszy, transmitowany jest najmniej znaczący bit (LSb), natomiast skasowanie tego bitu oznacza transmisję od najbardziej znaczącego bitu (MSb),MSTR– wybór pomiędzy trybem Master (1), a Slave (0). Jeśli pin SS skonfigurowany jest jako wejście i pojawi się na nim stan niski, gdyMSTRustawiony jest na 1, toMSTRzostanie wyzerowany i ustawiony zostanie znacznikSPIFw rejestrzeSPSR. Aby ponownie włączyć tryb master, należy wpisać 1 doMSTR.CPOL– polaryzacja zegara, dokładne wyjaśnienie znajduje się na poniższym rysunku i w tabeli 1,CPHA– faza zegara, uwaga j.w.SPR1,SPR0– częstotliwość taktowania transmisji. Opis w tabeli 2.
| Tryb SPI | CPOL | CPHA | SCK w czasie oczekiwania | SCK w momencie próbkowania |
|---|---|---|---|---|
| 0 | 0 | 0 | niski | zbocze narastające |
| 1 | 0 | 1 | niski | zbocze opadające |
| 2 | 1 | 0 | wysoki | zbocze opadające |
| 3 | 1 | 1 | wysoki | zbocze narastające |
| SPI2X | SPR1 | SPR0 | Częstotliwość SCK |
|---|---|---|---|
| 0 | 0 | 0 | fosc/4 |
| 0 | 0 | 1 | fosc/16 |
| 0 | 1 | 0 | fosc/64 |
| 0 | 1 | 1 | fosc/128 |
| 1 | 0 | 0 | fosc/2 |
| 1 | 0 | 1 | fosc/8 |
| 1 | 1 | 0 | fosc/32 |
| 1 | 1 | 1 | fosc/64 |
Rejestr SPSR

SPSR
SPIF– flaga przerwania od modułu SPI. Ustawiana, gdy transmisja zostanie ukończona.WCOL– flaga kolizji zapisu. Ustawiana, gdySPDRjest zapisywana podczas transmisji danych,SPI2X– gdy flaga ta jest ustawiona (1), prędkość transmisji SPI w trybie master jest podwojona.
Rejestr SPDR
Jest to rejestr pośredniczący w zapisie i odczycie danych z rejestru przesuwnego modułu SPI. Zapis do tego rejestru rozpoczyna transmisję, natomiast jego odczyt powoduje przepisanie informacji z rejestru przesuwnego.
2. Piny
Poszczególne mikrokontrolery różnią się od siebie rozmieszczeniem wyprowadzeń modułu SPI. Warto więc wprowadzić niezależne nazwy za pomocą dyrektywy define.
#define SPI_HW_PORT PORTB #define SPI_HW_DIR DDRB #define SPI_HW_PIN PINB #define SPI_HW_SCK PB5 #define SPI_HW_MISO PB4 #define SPI_HW_MOSI PB3 #define SPI_HW_CS PB2B
3. Tryb nadrzędny (Master)
Włączenie modułu SPI nie powoduje ustawienia pinów MOSI, MISO i SCK w odpowiednim trybie – należy zrobić to ręcznie. MOSI, SCK należy ustawić jako wyjścia, natomiast MISO jako wejście. Pin CS nie jest obsługiwany przez moduł SPI, więc wymaga sterowania ręcznego.
static inline void spi_hw_init(uint8_t conf) { SPI_HW_DIR |= (_BV(SPI_HW_CS) | _BV(SPI_HW_SCK) | _BV(SPI_HW_MOSI)); SPCR = conf; }
Następnie włączamy moduł SPI działający w trybie nadrzędnym z częstotliwością SCK zależną od parametru przekazanego funkcji spi_hw_init().
Można jeszcze wyczyścić rejestry poprzez ich odczytanie.
Sterowanie CS
- ustawienie CS w stanie wysokim
SPI_HW_PORT |= _BV(SPI_HW_CS);
- ustawienie CS w stanie niskim
SPI_HW_PORT &= ~(_BV(SPI_HW_CS));
Pozostaje tylko funkcja, która realizuje zapis i jednoczesny odczyt danych
static inline uint8_t spi_hw_data_send(uint8_t data) { SPDR = data; while((SPSR & _BV(SPIF)) == 0); SPSR |= _BV(SPIF); return SPDR; }
4. Przykład
Żeby lepiej zrozumieć sposób obsługi sprzętowego modułu SPI przygotowałem program, który zapisuje i odczytuje przykładowe dane z zewnętrznej pamięci EEPROM. Pamięć zastosowana w przykładzie to 25C040 komunikująca się z otoczeniem za pomocą interfejsu SPI.
#include <spi\spi_hw.h> #include <uart\uart.h> #include <util\delay.h> #ifndef F_CPU #define F_CPU 16000000UL // zegar w Hz #endif #define RS_BAUD 9600 #define RS_UBRR F_CPU / 16 / RS_BAUD - 1 int main() { uart_init(RS_UBRR); uart_puts("ATMega168 SPI test\r\n"); uart_puts("mikrokontroler.info\r\n"); spi_hw_init(_BV(SPE) | _BV(MSTR) | _BV(CPOL) | _BV(CPHA) | _BV(SPR1) | _BV(SPR0)); uart_puts("Zapis... "); spi_hw_cs_low(); spi_hw_data_send(0x06); spi_hw_cs_high(); _delay_us(1); spi_hw_cs_low(); spi_hw_data_send(0x02); spi_hw_data_send(0x00); spi_hw_data_send('m'); spi_hw_data_send('i'); spi_hw_data_send('k'); spi_hw_data_send('r'); spi_hw_data_send('o'); spi_hw_data_send('k'); spi_hw_data_send('o'); spi_hw_data_send('n'); spi_hw_data_send('t'); spi_hw_data_send('r'); spi_hw_data_send('o'); spi_hw_data_send('l'); spi_hw_data_send('e'); spi_hw_data_send('r'); spi_hw_cs_high(); _delay_ms(5); uart_puts("OK.\r\nOdczyt: "); spi_hw_cs_low(); spi_hw_data_send(0x03); spi_hw_data_send(0x00); for(int i = 0; i < 14; i++) uart_putc(spi_hw_data_send(0x00)); spi_hw_cs_high(); }
W przykładzie wykorzystane zostały funkcje obsługujące sprzętowy moduł portu szeregowego RS232.
Moduł SPI uruchamiany jest w trybie Master, wariant 3 (CPOL = 1, CPHA = 1) z częstotliwością SCK równą fosc / 128 czyli 125 kHz. Następnie wysyłana jest instrukcja zezwalająca na zapis WREN (szczegóły w dokumentacji układu 25C040). W kolejnym kroku zapisywane są dane (znaki ASCII tworzące słowo: mikrokontroler). Zapis dokonywany jest po zmianie poziomu CS z niskiego na wyskoki. Według dokumentacji czas zapisu wynosi maksymalnie 5ms i tyle należy odczekać przed rozpoczęciem kolejnych operacji.
Gdy dane są już zapisane, następuje ich odczyt i wysłanie portem szeregowym.
Wynik działania programu przedstawia się następująco:
5. Podsumowanie
Przedstawiony opis pokazuje jak w prosty sposób uruchomić interfejs SPI w mikrokontrolerach AVR. Poniżej dostępne są pliki zawierające wszystkie powyższe funkcje. Wystarczy dołączyć je do projektu i stosować SPI już od zaraz.



