KwikStik z Kinetis K40 oraz akcelerometr 3D LIS35DE – przykład aplikacji
Obsługa SPI
Uruchomienie interfejsu SPI w mikrokontrolerze Kinetis wymaga wykonania kilku czynności. W pierwszej kolejności należy odblokować zegary interfejsu SPI oraz portu A:
SIM->SCGC5 |= (1 << SIM_SCGC5_PORTA_SHIFT); SIM->SCGC6 |= (1 << SIM_SCGC6_DSPI0_SHIFT);
Następnie należy skonfigurować linie portu A jako linie interfejsu SPI:
PORTA->PCR[14] = PORT_PCR_MUX(2) | PORT_PCR_DSE_MASK; PORTA->PCR[15] = PORT_PCR_MUX(2) | PORT_PCR_DSE_MASK; PORTA->PCR[16] = PORT_PCR_MUX(2) | PORT_PCR_DSE_MASK; PORTA->PCR[17] = PORT_PCR_MUX(2);
Teraz trzeba skonfigurować sam interfejs SPI:
SPI0->MCR = SPI_MCR_MSTR_MASK | SPI_MCR_PCSIS(1) | SPI_MCR_DIS_RXF_MASK | SPI_MCR_DIS_TXF_MASK; SPI0->CTAR[0] = SPI_CTAR_FMSZ(7) | SPI_CTAR_CPOL_MASK | SPI_CTAR_CPHA_MASK | SPI_CTAR_PCSSCK(3) | SPI_CTAR_PASC(3) | SPI_CTAR_ASC(3) | SPI_CTAR_PDT(3) | SPI_CTAR_PBR(3) | SPI_CTAR_CSSCK(0x1) | SPI_CTAR_BR(3);
Wszystkie powyższe czynności w przykładowym projekcie wykonuje funkcja SPI_Init. Samo odblokowanie zegarów i konfiguracja linii GPIO nie wymaga raczej komentarza, wyjaśnię za to znaczenie poszczególnych bitów konfiguracji interfejsu SPI (w naszym przypadku SPI0). Najpierw rejestr MCR:
- MSTR – dla wartości 1 mikrokontroler jest urządzeniem Master
- PCSIS – stan linii Chip Select podczas bezczynności, dla wartości 1 stan będzie wysoki
- DIS_RXF, DIS_TXF – jeśli te bity są ustawione to wyłączane są kolejki FIFO dla danych odbieranych i nadawanych
Oprócz tego musimy wpisać odpowiednie ustawienia do jednego z dwóch rejestrów CTAR (CTAR0 lub CTAR1). Podczas nadawania decydujemy z której konfiguracji chcemy skorzystać, w przykładowym programie użyjemy rejestru CTAR0, wpisujemy do niego:
- FMSZ – wielkość ramki danych pomniejszona o jeden (czyli dla 8-bitowej ramki podajemy 7)
- CPOL – polaryzacja sygnału zegarowego, dla wartości 1 zegar podczas bezczynności jest w stanie wysokim
- CPHA – wybór krawędzi zegara na której próbkowana jest linia danych, dla wartości 1 przy CPOL=1 będzie to krawędź rosnąca
Do wysyłania i odbierania danych służy funkcja SPI_Transmit:
void SPI_Transmit(char * data, int len) { int i; SPI0->SR |= SPI_SR_TXRXS_MASK; for (i = 0; i < len; i++) { SPI0->PUSHR = data[i] | SPI_PUSHR_CTAS(0) | SPI_PUSHR_PCS(1) | (i == (len - 1) ? 0 : SPI_PUSHR_CONT_MASK ); while (!(SPI0->SR & SPI_SR_RFDF_MASK)); data[i] = (unsigned char) SPI0->POPR; SPI0->SR |= SPI_SR_RFDF_MASK; } }
Funkcja jako parametry przyjmuje wskaźnik do tablicy z danymi do wysłania (data) oraz długość tej tablicy (len). Funkcja najpierw ustawia bit TXRXS w rejestrze SR, co powoduje uruchomienie komunikacji przez SPI. Samo wysłanie danych polega na zapisie danych w młodszych 16 bitach rejestru PUSHR, starsze bity określają parametry transmisji, między innymi:
- PCS – sześć bitów, ich wartość decyduje o tym, które linie Chip Select zostaną użyte podczas transmisji. Przykładowo dla interfejsu SPI0 dostępne linie CS to: PTA14 (CS0), PTB23 (CS5), PTC0 (CS4) itd. Aby komunikacja odbyła się z użyciem linii CS0 trzeba ustawić najmłodszy bit PCS, aby użyć linii CS1 trzeba ustawić bit drugi itd. Aby sprawdzić, które linie mogą zostać użyte należy zajrzeć do załącznika mk40dn512.pdf (rozdział 8.1. K40 Signal Multiplexing and Pin Assignment)
- CTAS – wartość tych bitów decyduje czy zostaną użyte ustawienia z rejestru CTAR0 (CTAS=000) czy CTAR1 (CTAS=001)
- CONT – od wartości tego bitu zależy zachowanie linii Chip Select po zakończeniu transmisji, jeśli CONT=0 to powróci ona do stanu jaki wybraliśmy dla stanu bezczynności, czyli w naszym przypadku otrzymamy jedynkę, a dla CONT=1 linia zostanie w stanie niskim umożliwiając dalszą komunikację. Stąd zastosowana w funkcji SPI_Transmit przy zapisie do rejestru PUSHR konstrukcja (i == (len – 1) ? 0 : SPI_PUSHR_CONT_MASK ), która dla wszystkich bajtów poza ostatnim dodaje bit CONT, ponieważ dopiero po ostatnim bajcie chcemy aby linia CS wróciła do stanu wysokiego
Po zapisie do rejestru PUSHR musimy w pętli poczekać aż komunikacja się zakończy, czyli aż bit RFDF zostanie ustawiony, wtedy odczytujemy rejestr POPR, który zawiera bajt odczytany przez SPI, następnie zerujemy bit RFDF. Dane odczytane z SPI zapisywane są w tym samym buforze, w którym przekazano funkcji dane do wysłania.