Oto jedna z najpopularniejszych z dostępnych w internecie wersji wraz z numeracją pinów. Swoisty plug & play, gdzie moduł wlutowany jest w układ na płytce PCB.
Opis linii:
1. GND & VCC
Standardowo: VCC - napięcie, GND - masa. Moduł zasilany jest
napięciem 3,3V( do 3,7V), na piny natomiast przyjmuje maksymalnie około 5,5V,
dzięki czemu napięcie obniżyć musimy jedynie na linii zasilającej. Zdecydowanie
nie zalecam zasilania go napięciem rzędu 5V na dłuższą metę(Zdarzało mi się
puścić mu tyle przez programator i jeszcze żyje, w projektach natomiast zasilam
go bez wyjątku napięciem 3,3V).
2. CE(Chip Enable)
Linia sterująca przy pomocy której, dzięki zmianom stanów z wysokiego na niski przełączamy nRF'a ze stanu spoczynku, w stan odbioru lub transmisji danych.. Łączymy ją z dowolnym wolnym pinem mikroprocesora (Oczywiście
ustawionym jako wyjście).
3. CSN(Chip Select Not)
Także łączymy ją z dowolnym, niewykorzystywanym pinem
mikroprocesora ustawionego jako wyjście. Służy do transmisji komend do nRF’a i ich odczytu z
wykorzystaniem interfejsu komunikacyjnego SPI.
4. SCK(Sercial Clock)
Łączymy z pinem SCK mikroprocesora. Służy do taktowania transmisji SPI.
5. MOSI(Master output, Slave input)
Łączymy z pinem MOSI mikroprocesora. Linia danych przesyłanych z układu Master do Slave.
6.MISO(Master input, Slave output)
Łączymy z pinem MISO mikroprocesora. Linia danych przesyłanych z układu Slave do Master.
7.IRQ(Interrupt Request)
Jej podłączenie nie jest niezbędne do działania nRF'a,
ale znacznie ułatwia nam pracę. Możemy
ustawić aby wystawiany był stan wysoki w przypadku otrzymania danych, lub też
ich transmisji, a stąd już prosta droga do przerwań zewnętrznych znacznie
optymalizujących program. W przypadku mojej biblioteki podłączyłem go do pinu PD2, na którym wywoływane jest przerwanie INT0.
2. Trochę teorii i test interfejsu SPI
Aby ułatwić sobie pisanie, w pliku
.h biblioteki do obsługi nRF'ów zawarłem kilka prostych makrodefinicji, które pokrótce objaśnię, aby wyeliminować ewentualne niejasności.
Wartość PAYLOAD to ilość bajtów
przesyłana jednorazowo przez nRF-a. Wynosić ona może od 1 aż do 32, co
daje nam sporą przepustowość połączenia. Obecnie ustawiony jest jeden, nic nie stoi jednak na przeszkodzie aby zwiększyć tę ilość. Biblioteka została napisana tak, aby po zmianie wartości PAYLOAD w pliku .h, można było po prostu wysłać tak długą daną bez przeszukiwania i poprawiania pozostałych
linijek kodu.
UWAGA! Wartość PAYLOAD w
odbiorniku jak i nadajniku musi być taka sama, w innym przypadku odbiornik
odbierze tylko część danych, lub też niepoprawnie je zinterpretuje, zwracając błąd transmisji.
R_ADDR to adres naszego
odbiornika(lub nadajnika w przypadku, kiedy oczekuje on potwierdzenia o udanej transmisji, ale o tym później). T_ADDR to adres
nadajnika(Dokładniej rzecz biorąc adres, który nadajnik nadaje).
Adres odbiornika i nadajnika musi
być identyczny, aby transmisja przebiegła pomyślnie. W przypadku większej ilości
odbiorników można zmieniać adresy „w locie”.
Przykład:
Nadajnik:
R_ADDR 0x01
T_ADDR 0x0f
Odbiornik:
R_ADDR 0x0f
T_ADDR 0x01
Adresy mogą wyglądać również tak, jak w przykładzie powyżej. W tym przypadku nadajnik nadaje na adres 0x0f,
podczas gdy odbiornik takowego nasłuchuje, zaś w przypadku transmisji zwrotnej(na
przykład w celu potwierdzenia) odbiornik nada na adres 0x01, a nasz nadajnik na
takowym odbierze.
Adresy muszą mieć długość w
zakresie 2-5 bajtów, tutaj ułatwiłem sobie sprawę pisząc jedynie jeden bajt,
jednak jak później zobaczycie w pliku .c powtarzam go pięciokrotnie, w efekcie otrzymując adres: 0x01 0x01 0x01 0x01 0x01.
TR_MODE i
RC_MODE to ustawienie trybu odbioru lub transmisji nRF'a, poprzez zmianę stanu bitu PRIM_RX. Funkcja nRF_Config zostanie
szerzej opisana w późniejszym czasie, służy jednak ona do zapisu danych do
rejestrów urządzenia. Poszczególne bity które ustawiłem opisane zostały w
komentarzach na zdjęciu.
sbi (set bit) i
cbi (clear bit) to makrodefinicje ustawiania
lub zerowania wybranego pinu na wybranym porcie. Poprawia czytelność kodu i ułatwia proces pisania.
CE i
CSN to definicje pinów portu, do którego podpięte są linie sygnałowe CE i CSN. W razie nieoczekiwanych zmian mogę zmienić ich ułożenie bez żmudnego poprawiania całej biblioteki - po prostu wpisuję ich nowe umiejscowienie tutaj.
CE_lo ; CE_hi ; CSN_lo ; CSN_hi to dodatkowe uproszczenie. Tymi definicjami bezpośrednio ustawiam CE lub CSN w stan wysoki lub niski.
Skoro podstawowe elementy części teoretycznej mamy już za sobą, możemy przejść do etapu komunikacji z nRF'em, czyli odczytu i zapisu rejestrów. Jak już nadmieniłem, do komunikacji wykorzystujemy sprzętowy interfejs komunikacyjny SPI(lub USI w przypadku chociazby ATTINY), którego opis pominę przez wzgląd na dużą ilość dobrych materiałów dostępnych na ten temat w sieci, zarówno w języku polskim jak i angielskim. Załączę jedynie swoje funkcje które wykorzystuję, przez wzgląd na pewną ich niestandardowość. Jeśli jednak byłyby jakiekolwiek komplikacje lub problemy, mogę opisać także obsługę SPI oraz odpowiedzieć na pytania w komentarzach czy mailowo :). Zachęcam więc do nawązania konwersacji w przypadku jakichkolwiek niejasności.
Wracając do tematu, funkcja zapisująca określoną wartość do dowolnego rejestru nRF'a wygląda tak:
Jako pierwszy argument "reg" wpisujemy nazwę rejestru(ich definicje znajdują się w pliku .h, dostępnym na końcu poradnika), pod "value" zaś wartość, którą chcemy do niego wpisać.
Cały proces zapisu zaczyna się od ustawienia linii CSN w stan niski, co sprawia, iż nRF zaczyna "słuchać" naszych poleceń. Wysyłamy więc do niego najpierw nazwę rejestru wraz z doklejoną "czynnością", a następnie wartość, jaką chcemy zapisać. W tym przypadku jest to bit "1"(W_REGISTER) oznaczający, iż chcemy zapisać coś do danego rejestru i adres rejestru(jego nazwa). Niezbyt skomplikowane, prawda? Później wystarczy tylko, aby ustawić linię CSN na powrót w stan wysoki, a nRF powraca w stan czuwania.
Tutaj funkcja SPI_Shift. Jak widzimy, jest to standardowa funkcja służąca to wysłania i odczytania 1 bajtu poprzez SPI.
Proces odczytu rejestru przebiega bardzo podobnie do zapisu, z tą różnicą, że adres sklejany jest teraz z czynnością odczytu(R_REGISTER) czyli zerem zamiast jedynką wysyłaną na początku wraz z adresem. Następnie wysyłamy pusty bajt "NOP", aby pobrać z nRF'a interesującą nas wartość, którą zapisujemy do zmiennej lokalnej "data". Potem wystarczy dopisać, aby funkcja zwracała tę zmienną jako jej wynik. Na tym etapie możemy już sprawdzić zarówno poprawność połączeń jak i działanie samego nRF'a, odczytując z niego jakiś rejestr. Nasz testowy program mógłby więc wyglądać tak:
Odczytaną daną można przesłać także poprzez UART do komputera i wyświetlić w terminalu, lub na dowolnym LCD(na przykład tym zgodnym z hd44780 jak robiłem to ja w trakcie testów). W efekcie, jeśli wszystko powyżej zostało wykonane poprawnie, powinna nam się zaświecić podłączona do pinu 0 portu A dioda, lub wyświetlić wartość 0x0E na którymś z wyświetlaczy. 0x0E to bazowa wartość rejestru "STATUS", co odczytać można w nocie katalogowej, którą także załączam poniżej. Niniejszym na tym zakończę pierwszą część, a na dniach dodam kolejną, opisującą zagadnienia związane z poprawną inicjalizacją nRF'a oraz komunikacją pomiędzy dwoma układami. W przypadku dostrzeżenia jakiegokolwiek błędu czy to w zapisie, rozumowaniu, czy też wiedzy bardzo proszę o informację, jestem hobbystą, więc nie wykluczam błędów, jednakże starałem się takowych nie popełniać. Miłego programowania!
Link do pełnej wersji biblioteki nRF24L01.h:
https://github.com/Jeiiy/nRF24L01-
Link do datasheeta nRF24L01+(Opis rejestrów strona 54):
https://www.sparkfun.com/datasheets/Components/SMD/nRF24L01Pluss_Preliminary_Product_Specification_v1_0.pdf