SPI.doc

(1536 KB) Pobierz



SPI – Interfejs komunikacji szeregowej
Interfejs SPI używany jest do komunikacji między mikrokontrolerami AVR lubi innymi urządzeniami jak zewnętrzne pamięci EEPROM, zewnętrzne przetworniki analogowo - cyfrowe, czujniki itp.

Urządzenia komunikujące się za pomocą interfejsu SPI dzieli się na dwie podstawowe grupy. Pierwsze to urządzenia Master ( Wyższy ), które inicjują i kontrolują wysyłanie danych oraz urządzenia Slave ( Niewolnik ), które odbierają dane wysyłane przez Master'a ale mogą je do niego także w tym samym czasie wysyłać. Zazwyczaj w prostych rozwiązaniach mamy jeden układ typu Master, który wysyła i odbiera dane od jednego lub kilku układów typu Slave.

Najważniejszą część interfejsu SPI stanowi 8-bitowy rejestr przesuwny, który występuje zarówno w urządzeniu skonfigurowanym jako Master jak i Slave oraz sygnał zegarowy generowany przez urządzenie Master.

A oto jak przebiega transmisja. Powiedzmy, że Master chce wysłać do układu Slave jakiś bajt nazwijmy go M oraz odebrać od niego również pewien bajt danych nazwijmy go S. Zanim rozpocznie się komunikacja oba urządzenia umieszczają swoje bajty w swoich rejestrach przesuwnych. Zostało to przedstawione poglądowo na rysunku poniżej M0..M7 oznaczają kolejne bity bajtu M podobnie ma się sprawa z bajtem S.

Następnie Master generuje 8 pulsów zegara na linii SCK i zawartość rejestru przesuwnego Master'a ( bajt M ) jest wysyłany w takt pulsów zegarowych do Slave oraz w tym samym czasie nadchodzi do Master'a bajt wysyłany przez Slave ( bajt S ). Po pierwszym impulsie zegar sytuacja wygląda tak jak na obrazku niżej.

Po następnym impulsie zegarowym mamy taką sytuację.

Po 8 pulsach zegara Master ma bajt S a Slave bajt M.



To, że transmisja zachodzi w tym samym czasie w obu kierunkach nazywa się w pełni dwukierunkowym transferem danych ( full duplex data transfer ).

W mikrokontrolerach AVR cztery piny są używane przy komunikacji SPI :
- MISO ( Master in Slave out ) - Tą linią Slave przesyła dane do Master
- MOSI ( Master out Slave in ) - Tą drogą docierają dane od Master do Slave
- SCK ( Serial Colock ) - To jest linia na, której generowany jest sygnał zegarowy przez urządzenie Master i odbierany przez Slave, zegar jest niezbędny do poprawnego działania SPI gdyż synchronizuje komunikacje to właśnie w takt zegara przesuwane są poszczególne bity w rejestrach przesuwnych.
- SS' ( Slave Select ) - W zależności od tego jak skonfigurujemy nasze urządzenie jako Master lub Slave ta linia ma różne znaczenia w obu przypadkach :
• SS' dla Slave - Mając w układzie więcej urządzeń Slave niż jedno Master musi mieć możliwość wybrania do którego w danym momencie mają trafić dane a do których nie. Po to właśnie jest linia SS'. Jeżeli SS' jest w stanie wysokim to dany Slave jest niejako nieaktywny i nie odbiera nadchodzących danych natomiast gdy SS' jest w stanie niskim to jest aktywny i nadchodzące dane będą odbierane o ile SPI został poprawnie skonfigurowany. Program Master'a musi kontrolować SS' każdego Slave tak by wysyłać dane tylko do wybranych urządzeń w danym momencie.
• SS' dla Master – W tym przypadku mamy jeszcze dwie możliwości w zależności od tego czy pin SS' skonfigurowano jako wejście czy jako wyjście. Jeżeli SS' jest skonfigurowane jako wyjście to czy jest w stanie niskim czy w stanie wysokim nie wpływa na interfejs SPI jest on w obu przypadkach aktywny. Natomiast gdy SS' jest wejściem to musi być trzymane w stanie wysokim by urządzenie pracowało jako Master. Gdy natomiast wystąpi sytuacja, że na SS' będącym wejściem pojawi się stan niski zostanie to zinterpretowane tak jakby inny Master wybrał naszego Master'a jako Slave i rozpoczął z nim komunikacje. Taka sytuacja ( więcej niż jeden Master w układzie ) jest niebyt ciekawa i o ile to nie potrzebne lepiej jej unikać. Jeżeli jest nam to niepotrzebne lepiej konfigurować SS' Master'a jako wyjście wtedy będziemy mieli pewność, że sytuacje tego typu nie wystąpią.

W przypadku gdy mamy w układzie tylko jedno urządzenie Slave nie musimy się martwić implementowaniem w programie dla Master'a możliwości przestawiania stanu SS' Slave gdyż i tak nie mamy, żadnego wyboru bo Slave jest tylko jeden. Można wówczas na stałe dołączyć linie SS' Slave do masy wówczas będzie on stale gotów do komunikacji z Master'em. Natomiast pin SS' urządzenia Master można pozostawić niepołączony należy jednak zadbać by był wyjściem gdyż wtedy to w jakim jest stanie ( wysokim czy niskim ) nie ma znaczenia i w obu przypadkach SPI jest aktywne o czym już wyżej wspomniałem. Gdyby był wejściem to nieprzewidziane zmiany stanu mogą skutecznie zablokować komunikacje wystarczy by w jakiś nieprzewidziany sposób stan zmienił się na SS' Master'a na niski i SPI nie będzie działać poprawnie. Pozostałe linie łączymy tak jak na rysunku niżej.



Gdy natomiast mamy 2 lub więcej Slave'ów musimy mieć możliwość wybrania jednego konkretnego Slave'a gdy będziemy chcieli wysłać dane tylko do niego. Jak już wiadomo linia SS' w stanie niskim powoduje, że Slave jest aktywny i może komunikować się z Master'em natomiast SS' w stanie wysokim powoduje, że Slave jest nieaktywny i nie odbiera ani nie wysyła, danych. Najprościej więc do wyboru Slave posłużyć się dodatkowymi pinami układu Master podłączonymi do SS' Slave'ów i zmieniając ich stan na wysoki blokować dane Slave'y a na niski uaktywniać je. Pomocny może okazać się poniższy rysunek.

Należy tutaj zauważyć, że nazwy MISO, MOSI, SCK, SS' są używane w mikrokontrolerach AVR ale inne urządzenia np. zewnętrzne EEPEROM'y mogą mieć te nazwy inne należy więc sprawdzić w dokumentacji danego urządzenia jak nazywają się odpowiedniki powyższych i z nimi je połączyć.

To czy piny używane podczas komunikacji SPI są wejściami czy wyjściami zależy od tego czy urządzenie skonfigurowano jako Master czy jako Slave. Są tutaj dwie możliwości albo mamy narzucone, ustawienie danego pinu jako wejście albo kierunek pinu musi być ustalony przez programistę zgodnie z tym jaką funkcje pełni. Pomocna może być poniższa tabela.



Przed przystąpieniem do analizy kodu przykładowych programów należy jeszcze podać minimum informacji na temat rejestrów sterujących SPI tak aby kody były dla wszystkich zrozumiałe.

Do sterowania interfejsem SPI w mikrokontrolerach AVR ATmega8 służą 3 rejestry SPCR, SPSR oraz SPDR. Nie będę na razie podawał znaczenia wszystkich bitów danych rejestrów podam tylko te niezbędne do zrozumienia kodów reszta zostanie uzupełniona później.

 

Na pierwszy ogień weźmy rejestr SPCR ( SPI Control Register ).  Bę

dą nas tu na razie interesować bity :
- SPE ( SPI Enable ) - Bit ten musi być ustawiony kiedy chcemy używać interfejsu SPI gdyż ustawienie tego bitu powoduje po prostu włączenie interfejsu SPI.
- SPIE ( SPI Interrupt Enable ) – Bit ten ustawiamy kiedy chcemy aby przerywania pochodzące od interfejsu SPI były włączone jeżeli nie chcemy korzystać z przerywań bit powinien mieć
wartość 0. Należy pamiętać, że aby korzystać z przerywań należy w rejestrze SREG ustawić wcześniej bit I co czyni
w asemblerze instrukcja sei a w C funkcja sei().
- MSTR ( Master / Slave Select ) - Nadanie bitowi wartości 1 powoduje, że urządzenie pracuje jako Master natomiast nadanie mu wartości 0 powoduje pracą urządzenia jako Slave.
- SPR1 i SPR0 – Bity te służą do konfiguracji częstotliwości sygnału zegara SPI generowanego przez Master'a na linii SCK. Jeżeli urządzenie skonfigurowano jako Master to można zmieniając wartości tych bitów ustawiać dzielnik przez, który zostanie podzielona częstotliwość oscylatora mikrokontrolera i tak otrzymaną częstotliwość będzie miał sygnał zegarowy generowany przez Master. Z racji, że dane są przesyłane w takt zegara zmieniając wartości bitów a co za tym idzie częstotliwość zegara SPI można zmieniać szybkość przesyłanych danych. Należy zauważyć, że jeżeli urządzenie skonfigurowano jako Slave to ustawienie tych bitów nie ma znaczenia gdyż Slave tylko odbiera sygnał, który generuje Master. Nie można jednak zapomnieć o ograniczeniach narzucanych przez sam sprzęt, które powodują, że częstotliwość zegara SPI nie może być większa niż fosc / 4 gdyż wtedy dane nie będą poprawnie odbierane. Poniższa tabelka przedstawia możliwości ustawienia dzielnika za pomocą bitów SPR1 i SPR0.

 

 

Pora na rejestr SPSR ( SPI Status Register ):

 

na razie będzie nas interesował tylko bit SPIF tego rejestru.

- SPIF ( SPI Interrupt Flag ) - Jest to bit, który można tylko odczytywać nie można więc go ustawiać lub kasować programowo gdyż to sprzęt nim steruje. Jest on ustawiany przez sprzęt kiedy transfer SPI dobiegnie końca . Jest on kasowany przez sprzęt kiedy zostanie zgłoszone przerywanie od SPI ( SPI_STC ) i zaczyna się procedura jego obsługi lub jest kasowany wtedy gdy nastąpi odczytanie bitu SPIF i zawartości rejestru SPDR.

Na koniec rejestr SPDR
- Do tego rejestru zapisujemy dane do wysłania przez Master lub Slave oraz z tego rejestru odczytujemy nadchodzące dane po stwierdzeniu końca komunikacji, do czego może posłużyć wyżej opisana flaga SPIF lub przerywanie SPI_STC. Po poprawnym zainicjowaniuinterfejsu SPI wystarczy tylko wpisać do tego rejestru wartość do wysłania aby transmisja została rozpoczęta.

Teraz przejdziemy do części praktycznej zostaną przedstawione dwa proste przykłady użycia interfejsu SPI. Pierwszy z nich prezentuje użycie interfejsu SPI w bardzo prosty sposób. Drugi na przykładzie urządzenia Slave pokazuje wykorzystanie przerywania SPI_STC ( Spi Transfer Complete ) do wykrycia końca transmisji danych. Żeby móc przetestować pokazane przykłady wystarczy złożyć następujący prosty układ :

Gdybym się pomylił lub o czymś zapomniał przy rysowaniu schematu to napisze jeszcze słowami, że w sumie nie chodzi o nic więcej jak tylko połączenie ze sobą pinów MISO, MOSI, SCK obu układów pin SS' Slave łączymy z masą a do jego PortuD dołączamy diody ledukłady oczywiście podłączamy do napięcia 5V i dodajemy kondensatory odsprzęgające o wartościach C4 i C3 100nF ceramiczne oraz C5 i C6 10uF elektrolity natomiast reset łączymy z vcc przez rezystor 10k i z minusem przez kondensator 100nF.

Układ jest dość prosty a dane, które będzie otrzymywał Slave od Master'a są wyświetlane na 8 diodach połączonych do pinów portu D. Możemy dzięki temu stwierdzić czy interfejs działa poprawnie i czy wszystko zostało dobrze złożone. Do przykładów wykorzystano 2 AVR'y Atmega8 i na takich właśnie mikrokontrolerach kody będą działać poprawnie przy stosowaniu innych należy zgodnie z dokumentacją zmienić część kodu. Układ testowy został złożony na płytce stykowej ale oczywiście można go także złożyć na płytce uniwersalnej lub na płytce drukowanej. Programy zostały napisane w 2 językach programowania w AVR Asemblerze i AVR – GCC.

Pierwszy program ( animacja ) działa tak, że na diodach led wyświetlane są kolejne bajty wysyłane przez Master te bajty to nic innego jak coraz większe ( o jeden ) liczby od 0 do 255 z racji, że master po wysłaniu bajtu odczekuje ok. 1 sekundy efektem jest prosta animacja. Najpierw kody dla urządzenia Master.

Wersja ASM:

.include "m8def.inc"
.cseg
.org 0x000 rjmp Start

Start:
ldi r16, low( RAMEND )            
out SPL, r16
ldi r16, high( RAMEND )
out SPH, r16

Inicjajcja_spi:
ldi r16, ( 1 << PB3 )|( 1 << PB5 ) | ( 1 << PB2 )              //MOSI, SCK, SS' jako wyjścia
out DDRB, r16                           
ldi r16, ( 1<<SPE )|( 1<<MSTR )|( 1<<SPR0 ) | ( 1 << SPR1 )    //Włączamy SPI, układ Master
out SPCR, r16                             //Najmniejsza częstotliwość SCK fosc / 128
ldi r19, 0

main:                              //Pętla nieskończona
out SPDR, r19                             //Wysyłamy zawartość r19    
inc r19                                   //Zwiększamy wartość w r19 o 1   
Czekaj_na_wyslanie:                       //Oczekujemy na zakończenie transmisji ( do ustawienia SPIF )
sbis SPSR, SPIF                           //Przez sprzęt
rjmp Czekaj_na_wyslanie
ldi r16, 250                              //Czekamy ok. 1 sek
rcall Czekaj
ldi r16, 250
rcall Czekaj
ldi r16, 250
rcall Czekaj
ldi r16, 250
rcall Czekaj
rjmp main       

Czekaj:                                    //Procedura opóźniająca o 1 * r16 [ ms ]
P2:
ldi r17, 10
P1:
ldi r18, 25
P0:
nop
dec r18
brne P0
dec r17
brne P1
dec r16
brne P2
ret


Wersja C:
#define F_CPU 1000000UL
#include<avr/io.h>
#include<util/delay.h>

void Inicjacja_spi()
{
    DDRB = ( 1 << PB5 ) | ( 1 << PB3 ) | ( 1 << PB2 );   //MOSI, SCK, SS' jako wyjścia
    SPCR = ( 1 << SPE ) | ( 1 << MSTR ) | ( 1 << SPR1 ) | ( 1 << SPR0 );   //Włączamy SPI,    
}                    //układ Master, najmniejsza częstotliwość SCK fosc / 128

void Wyslij_spi(char bajt)
{
    SPDR = bajt;                    //Wysyłamy zawartość zmiennej bajt   
    while( ! bit_is_set( SPSR, SPIF ) );  //Oczekujemy na zakończenie transmisji ( do ustawienia SPIF ) przez sprzęt
}        ...

Zgłoś jeśli naruszono regulamin