Praktyka programowania w C Laboratorium 5. 2013 by Krzysztof J. Urbański Łączność bezprzewodowa: RFM73 Użycie pasma mikrofalowego 2,4 GHz ma swoje wady i zalety zaletą jest kompaktowa konstrukcja (nie potrzeba zewnętrznej anteny), zaś wadą mniejszy zasięg niż w przypadku pasma 868 MHz lub 433 MHz. Ograniczony zasięg jest spowodowany silnym tłumieniem mikrofal przez prawie wszystkie napotkane przeszkody, podobnie jak w przypadku WiFi. W warunkach dobrej propagacji (otwarta przestrzeń) możliwe jest uzyskanie zasięgu 60 m, w rzeczywistości będzie to kilkanaście metrów, czyli w granicach jednego pomieszczenia lub mieszkania. Transmisja jest realizowana paczkami danych (pakietami) o zawierającymi do 32 bajtów informacji. Przesłane pakiety mogą być sprzętowo potwierdzane przez odbiorcę i w razie potrzeby retransmitowane. Każdy z układów RFM73 dysponuje własnym adresem, ale na dzisiejszych zajęciach użyjemy identycznego adresu dla wszystkich układów. Aby wymiana danych doszła do skutku, trzeba będzie ustawić identyczny kanał radiowy dla wszystkich urządzeń oraz identyczną prędkość transmisji. Inne parametry (długość sum kontrolnych, transmisja potwierdzeń, adresy potoków) powinny pozostać niezmienione (domyślne). Zadania na dzisiaj: 1. Pobranie wzorcowego projektu z uruchomioną komunikacją UART (w tym rozpoznawanie poleceń z terminala). 2. W projekcie powinny być uruchomione komponenty do regulacji jasności diody RGB funkcja RGB(r, g, b) z poprzednich zajęć sprawdź to! 3. Uruchomienie komponentu SPI w mikrokontrolerze według wskazówek poniżej. 4. Zdefiniowanie 2 sygnałów BitIO (rf_ce, rf_nss ) oraz ExtInt (rf_irq) według wskazówek w dalszej części instrukcji. 5. Dodaj do projektu pliki rfm7x.c oraz rfm7x.h. Dołącz plik nagłówkowy w Projekt_2.c. 6. Weryfikacja działania układu RFM73 kiedy układ zostanie wykryty, zapal na 1 sekundę zieloną diodę. Kiedy wystąpi problem czerwoną. 7. Ustaw tryb RX funkcją rfm7x_mode_receive(); oraz kanał 45: rfm7x_channel(45); 8. Dodaj nieskończoną pętlę wewnątrz funkcji main. Wciśnięcie przycisku powinno wysyłać wiadomość: rfm7x_transmit_message_once((const unsigned char *)"Hi there!", 9+1); co sekundę. 9. Kiedy przycisk nie jest wciśnięty, sprawdzaj FIFO odbiornika i wyświetl odebrany komunikat, jeśli FIFO nie jest puste. 10. Kiedy odebrany zostanie pakiet zaczynający się od *, przekaż go jako argument funkcji dekodującej polecenia tekstowe. 2013 by Krzysztof J. Urbański 1
Moduł wlutowany w NE64BASE jest podłączony do linii SPI mikrokontrolera oraz kilku dodatkowych GPIO. SPI to skrót od Serial Peripheral Interface, przy czym w odróżnieniu od UART (SCI) ten interfejs jest synchroniczny, czyli transmisja bitów danych jest synchronizowana dedykowaną linią zegarową. Jedno z urządzeń jest układem nadrzędnym (Master), pozostałe układy są podrzędne (Slave). Układem Master, odpowiedzialnym za sterowanie przebiegiem transmisji i generowanie sygnału zegarowego SCK jest tutaj mikrokontroler, zatem moduł RFM73 działa jako Slave. Pozostałe linie interfejsu SPI to MOSI (Master Out Slave In), MISO (Master In Slave Out) oraz nss (aktywna stanem niskim linia Slave Select). Tą linią będziemy sterować programowo (jak zwykłym GPIO), dlatego jest odłączona od układu peryferyjnego SPI (SS pin allocation: Disabled). Mikrokontroler MC9S12NE64 przy zegarze 25 MHz dysponuje maksymalną szybkością transmisji SPI wynoszącą 12,5 MHz, podczas gdy RMF73 akceptuje co najwyżej 8 MHz. W przykładzie obok użyto zegara ok. 1 MHz, ale możesz później poeksperymentować z wyższymi jego prędkościami. Dodatkową komplikacją jest to, że w SPI są stosowane 4 tryby komunikacji, różniące się polaryzacją zegara (SCK aktywny stanem niskim lub wysokim) oraz momentem, kiedy następuje zatrzaskiwanie danych (faza zegara). RFM73 wymaga konfiguracji dokładnie takiej, jaka została użyta w przykładzie obok. Więcej informacji znajdziesz w nocie katalogowej: http://www.hoperf.com/upload/rf/rfm73_datasheet.pdf. 2013 by Krzysztof J. Urbański 2
Dodaj dodatkowe komponenty, które będą sterować pozostałymi liniami RFM73: Komponent rf_irq nie jest zwykłym BitIO, lecz ExtInt (External Interrupt). Pojawienie się stanu niskiego na linii XIRQ (PE0) wyzwala sprzętowo przerwanie, którego obsługa będzie na razie wyglądać następująco: void rfm7x_clear_irq(void); //prototyp (nagłówek) funkcji z pliku rfm7x.c void rf_irq_oninterrupt(void) { //reakcja na zgłoszenie przerwania od RFM73 rfm7x_clear_irq(); //wyczyść flagę przerwania RFM73 Pomimo niskiej ceny i niepozornego wyglądu RFM73 jest dość skomplikowanym urządzeniem, i nie wystarczy tylko włączyć jego zasilanie, aby od razu można było wysyłać lub odbierać dane. Konieczna jest wcześniejsza inicjalizacja rejestrów, adresów potoków, konfiguracja trybu pracy. Najwygodniej jest posłużyć się gotowymi bibliotekami (funkcjami), które realizują to zadanie. W przykładowym projekcie dostępnym na stronie http://www.mikrocpp.info/zestawy/ne64base/ne64base-multitest-with-ethernet są dołączone biblioteki do obsługi radia: rfm7x.c oraz rfm7x.h, których autorem jest Wouter van Ooijen. Oryginalne biblioteki wspierały tylko moduły RFM70. Zmiany, które wprowadziłem, polegają na dodaniu obsługi także RFM73 oraz dostosowaniu tych bibliotek do mikrokontrolerów rodziny HCS12. Obowiązuje tutaj licencja zlib: //! The files in this library are licensed under the zlib license, //! which rougly says: when you redistribute the *source* you must //! keep the copyright notice intact. Otherwise you can use these files //! any way you like, including getting rich from selling applications. 2013 by Krzysztof J. Urbański 3
Minimalny kod wymagany do wysyłania danych wygląda tak: rfm7x_init(rfm73); //inicjalizacja modułu rfm7x_mode_transmit(); //ustaw tryb nadawania rfm7x_channel(45); //kanał 45 for(;;) { rfm7x_transmit_message_once((const unsigned char *)"Hi there!", 9+1); //wysyłanie pakietu danych bez potwierdzenia while(rfm7x_transmit_fifo_full()){ //czekaj na wysłanie pakietu CpuDelay100US(10000); //pauza Odbieranie danych jest niewiele bardziej skomplikowane: //to powinny być zmienne globalne uint8_t rfmsg_len, rfpipe; //długość odebranego pakietu i numer potoku uint8_t rfmsg[32+1]; //maksymalnie 32 znaki + miejsce na dodatkowy znak końca napisu w C //to wystarczy wykonać raz (w trakcie uruchamiania zestawu) rfm7x_init(rfm73); //inicjalizacja modułu rfm7x_channel(45); //kanał 45 //ten fragment można umieścić w funkcji main rfm7x_mode_receive(); //ustaw tryb odbierania for(;;) { if(!rfm7x_receive_fifo_empty()) { //odebrano jakieś dane if(0!= rfm7x_receive(&rfpipe, rfmsg, &rfmsg_len) && rfmsg_len > 0) { //danych jest więcej niż 1 bajt rfmsg[rfmsg_len] = '\0'; //dodaj znak końca napisu w C do bufora (void)printf("rfmsg: %s\r\n", rfmsg); //wyświetl odebrany komunikat w terminalu W trakcie działania programu można swobodnie przełączać się między nadawaniem a odbieraniem, co pozwala na zrealizowanie komunikacji dwukierunkowej. 2013 by Krzysztof J. Urbański 4
Moduły RFM7x maja ponadto wiele przydatnych cech, np. możliwość sprzętowego potwierdzania i retransmisji danych w razie potrzeby (bez konieczności ingerencji ze strony programisty), każdy z modułów może mieć swój unikatowy adres, co pozwala na bezkolizyjną współpracę wielu układów na tym samym kanale radiowym. Bardzo przydatne jest też sprzętowe wsparcie dla maksymalnie 6 potoków, co pozwala na budowanie prostych sieci lokalnych w topologii gwiazdy 1:6. Typowy scenariusz użycia to stacja bazowa (AP) + maksymalnie 6 stacji klienckich, będących np. czujnikami bezprzewodowymi. Wykrywanie obecności modułu RFM7x i jednocześnie test komponentu SPI oraz pomocniczych GPIO. if(rfm7x_is_present()) (void)printf("rfm7x is present.\r\n"); else (void)printf("rfm7x is NOT present or initialization failed.\r\n"); //na początku pliku Events.c znajdują się poniższe definicje kolorów RGB sprawdź, czy pasują do twojego zestawu! #define RGB_R 2 #define RGB_G 1 #define RGB_B 4 2013 by Krzysztof J. Urbański 5