Łącze radiowe - moduły TR-52D
Każda stacja meteorologiczna powinna być zbudowana w części czujników umieszczonych na zewnątrz, najlepiej w specjalnej klatce, oraz z części prezentującej pomiary umieszczonej w pomieszczeniu w miejscu najbardziej dogodnym dla użytkownika. Oba te elementy muszą być ze sobą połączone kanałem transmisyjnym pozwalającym na przesyłanie danych pomiarowych. Z technicznego, ale też praktycznego punktu widzenia, najłatwiej jest to zrobić wykorzystując łącze radiowe w pasmach nie wymagających pozwolenia. Mała moc nadajników spełniających wymóg braku konieczności uzyskiwania pozwolenia na pracę w eterze ogranicza zasięg do kilkudziesięciu metrów zależnie od ilość przeszkód (ścian) ale też od ich budowy. Ściana żelbetowa będzie mocniej tłumić sygnał niż ściana z bloczków cementowo wapiennych. W praktyce taki zasięg jest zupełnie wystarczający. Opisywane urządzenie pracuje bez żadnych problemów z łączem radiowym w odległości ok 20 metrów miedzy urządzeniem umieszczonym na zewnątrz i wewnątrz. Przeszkodę stanowią 2 ściany budynku z bloczków.
Łącze można realizować używając szerokiej gamy modułów radiowych. Ja zgodnie z początkowym założeniem zastosowałem to co miałem, czyli moduły IQRF TR52-D produkowane przez czeską firmę MICRORISC. Z punktu widzenia projektanta to bardzo dobry wybór. TR-52D to nie tylko moduł radiowy. Producent wpadł na pomysł, żeby połączyć prosty (punktu widzenia programisty) moduł radiowy z małym mikrokontrolerem PIC16F886. Taki pomysł nie wydaje się czymś odkrywczym i rzeczywiście tak by było, gdyby na tym poprzestano. Produkt MICRORISC to znacznie więcej niż układ moduł radiowy i mikrokontroler. Został zaprojektowany cały ekosystem zbudowany z małych łatwych w użyciu i programowaniu modułów radiowych, zestawów rozszerzających DCC Kits i firmowych narzędzi sprzętowych i programowych stanowiących kompleksowe wsparcie dla projektanta.
W protokole wymiany danych zaimplementowano tylko te funkcje, które są niezbędne do przesyłania danych pomiędzy modułami. Nie oznacza to jednak rezygnacji z maksymalnej dostępnej funkcjonalności ograniczonej zasobami małego mikrokontrolerem. Oprócz możliwości wymiany danych pomiędzy dwoma modułami (peer-to-peer) zaimplementowano własny protokół sieciowy o nazwie IQMESH. Niestety takie możliwości muszą kosztować. Moduły nie są tanie. TR-52D nie jest już produkowany, a jego następca kosztuje ponad 100 PLN/sztukę. A to nie wszystko, bo żeby ich używać trzeba kupić przynajmniej jeden moduł programatora USB. Taki programator CK-USB-04A kosztuje ok. 300 PLN. Gdybym nie miał modułów i CK-USB-04 to owe rozwiązanie nie byłoby brane pod uwagę właśnie ze względu na koszty. Są inne tańsze opcje.
Poza ceną system ma same zalety. Jeżeli potrzebne jest kodowanie transmisji, to może ją wykonać Host współpracujący z modułem. Ponieważ moduł radiowy ma wbudowany programowany przez użytkownika mikrokontroler, to w prostych zastosowaniach można go użyć bez dodatkowych elementów. Potrzebne jest tylko 8-pinowe złącze takie, jak dla dużych katy SIM w telefonach i układ zasilania +5 V. Przez kilka lat używałem samodzielnego modułu TR-52 z dołączonym czujnikiem DS18B20 jako zewnętrznego czujnika temperatury i pracował znakomicie. Zasoby wbudowanego mikrokontrolera nie są duże, a musi on też wykonywać funkcje pracującego w tle miniaturowego systemu operacyjnego IQRF OS obsługującego transmisję SPI i funkcje sieci MESH. Dlatego kiedy układ musi wykonywać bardziej zaawansowane funkcje, to moduł współpracuje z zewnętrznym mikrokontrolerem hostem tak, jak to jest w naszym przypadku.
Schemat blokowy modułu został pokazany na rysunku 25. Część radiowa (transceiver RF) bazuje na układzie MRF49A. Jak już wspomniałem mikrokontroler to 8 bitowy PIC16F886. Układ zasilania zawiera stabilizator MCP1700. W moduł wbudowano też sensor temperatury MCP9700A. Przy okazji opisu DS18B20 wyjaśniałem dlaczego nie używam modułów pomiaru temperatury umieszczanych wewnątrz urządzenia. Dla systemu IQRF OS przeznaczona jest pamięć 24AA16MC. Wszystkie wymienione elementy modułu są produkowane przez firmę Microchip.
Użytkownik uruchamiana swoją aplikację pod systemem IQRF OS i ma do dyspozycji wiele funkcji systemowych. Program użytkownika jest pisany w języku C i kompilowany kompilatorem CC5X. Producent zadbał o to, aby nie trzeba było się martwić o niezbędne narzędzia. Darmowa wersja kompilatora jest zupełnie wystarczająca do napisania sporego programu. Trzeba pamiętać, że taki moduł jest inteligentnym układem peryferyjnym i nie są na nim uruchamiane duże zadania. Tworzenie aplikacji umożliwia firmowy pakiet IRQF IDE. Z jego pomocą możemy wykonać wszystkie czynności projektowe: edytować (za pomocą zewnętrznego edytora) plik źródłowy, skompilować go, zaprogramować mikrokontroler modułu radiowego i debugować działający program.
Wspomniany programator modułów CK-USB-04A umożliwia z poziomu IRQF IDE zapisanie pamięci Flash mikrokontrolera kodem programu użytkownika. Za pomocą CK-USB-04A można również testować poprawność transmisji. Po wgraniu do modułu odpowiedniego programu CK-USB-04A odczytuje odebrane dane przez SPI, przesyła do IQRF IDE, gdzie można je wyświetlić praktycznie on-line. Z poziomu IQRF IDE można również wysyłać dane (przez CK-USB-04A z włożonym modułem TR 52D) do drugiego modułu TR-52D.
Te właściwości znacznie ułatwiają testowanie poprawności przesyłania danych przez łącze radiowe.
Już wiemy, że w naszym przypadku moduł radiowy współpracuje z mikrokontrolerem hostem. Jest nim PIC16F18446 z modułu MPLAB Xpress. Moduł TR52B jest w magistrali SPI układem slave, a Host układem master. Konfiguracja interfejsu SPI Master Hosta został pokazana na rysunku 8 (pierwsza część artykułu - EP12/22). W takiej konfiguracji układ master generuje sygnał zegarowy oraz sygnał aktywacji interfejsu w układzie slave SS (Slave Select). Rozdzielone linie danych wyjściowych MOSI i danych wejściowych MISO (z punktu widzenia mastera) pozwalają na transmisję w trybie duplex. Kiedy master chce wysłać na przykład 8-bitową daną, to generuje 8 taktów zegara. Dane są zapisywane do układu slave w czasie narastającego lub opadającego zbocza sygnału zegarowego.
W przypadku modułów TR52B dane są zapisywane i odczytywane narastającym zboczem zegara (rysunek 26).
Obsługa SPI przez IQRF OS nakłada pewne ograniczenia czasowe w trakcie przesyłania danych. Na rysunku 27 pokazano te ograniczenia. Pomiędzy przesłaniem bajtów musi być zachowany odstęp minimum 100 μs dla zegara SCK 250 kHz.
Typowy czas T2 jest równy 500 μs. Jest to dość istotne ograniczenie i musi być w praktyce dokładnie przestrzegane. Obsługa magistrali SPI w IQRF OS jest zaimplementowana jako maszyna stanu. Moduł TR52B (jego mikrokontroler) może zapytać o stan swojej magistrali używając funkcji getStatusSPI(). Host musi sekwencyjnie pytać moduł radiowy o status SPI wysyłając sekwencyjnie komendę SPI_CHECK (bajt 0x00). Na to zapytanie slave odpowiada bajtem statusu (tabela 9).
//wysłanie i odczytanie danych z IQRF przez SPI
uint8_t GetIQRFData(uint8_t data){
uint8_t d;
IQRF_CS = 0;
__delay_us(12);
d = SPI2_ExchangeByte(data);
__delay_us(12);
IQRF_CS = 1;
__delay_us(120);
return(d);
}
Na listingu 26 pokazano procedurę wymiany danych hosta z modułem, a na listingu 27 odczytywanie statusu.
//odczytanie statusu
uint8_t GetIQRFStatus (void){
return(GetIQRFData(0));
}
Żeby dokładniej zrozumieć jak działa wymiana danych z modułem trzeba widzieć jak działa wymiana danych przez interfejs SPI (rysunek 28). Master (mikrokontroler) inicjuje wymianę danych generując 8 cykli zegarowych. W czasie trwania tych cykli 8-bitowa dana mastera jest wysyłana przez linię SDO do układu slave (moduł radiowy). Jednocześnie w czasie trwania tych 8 cykli zegarowych slave przesyła 8-bitwą daną do układu master. Wynika z tego ważny wniosek: układ slave nie może wysyłać swoich danych w reakcji na przesyłaną daną przez układ master, bo w momencie ich przesłania slave nie zna wartości tej danej. Dane slave mogą być reakcją na wcześniejsze dane od mastera. Dlatego protokół wymiany danych w IQRF OS ma kilka stanów określanych statusem. Jeżeli moduł (slave) z pracującym IQRF OS nie ma zlecenia przesłania jakichś danych to na dowolną daną do niego przesłaną odpowiada statusem. Jeżeli chcemy żeby moduł odpowiedział statusem i się nie przestawił w inny stan, to wysyłamy do niego wartość zerową. Dlatego na listingu 27 procedura odczytująca status wysyła do modułu zero. Moduł odpowiada jednobajtowym statusem o wartościach pokazanych w tabeli 9. W tym przypadku nie jest konieczne przesyłanie sum kontrolnych i sprawdzanie statusu wykonywane jest szybko. Program zależnie od wartości statusu może podjąć konkretne działanie. Na przykład zainicjować wymianę danych przez wysłanie odpowiedniej komendy.
W module radiowym domyślnie komunikacja przez SPI jest wyłączona (ale sam interfejs działa) i program użytkownika musi ją włączyć komendą systemową enableSPI(). Jeżeli się tego nie zrobi, to na zapytania hosta o status moduł odpowie wartością 0x00 (SPI nie aktywne). Kiedy SPI jest włączony i moduł nie ma żadnych danych do przesłania to odpowiada statusem 0x80 - SPI gotowe.
Jeżeli host się spodziewa danych z modułu, to oczekuje najpierw statusu 0x80. Jak już wiemy jest to informacja, że transmisja SPI jest włączona i moduł jest gotowy do wymiany informacji z Hostem. Moment, w którym moduł ma rzeczywiste dane do przesłania sygnalizowany jest statusem o wartości 0x40+ld, gdzie ld jest ilością danych. Jeżeli Host odbierze taki status, to może zainicjować odbieranie dane wysyłając do niego komendę SPI_CMD.
Komenda SPI_CMD wysyłana przez Hosta wykorzystuje właściwości interfejsu SPI (rysunek 28). Zależnie od potrzeb może tylko odczytać dane z modułu, lub w trakcie odczytywania jednocześnie przesłać swoje ważne dane. Moduł wie, że zaczyna się przesyłanie komendy SPI_CMD, kiedy odbierze bajt kodu komendy równy 0xF0. Oczywiście w trakcie przesyłania kodu komendy moduł odpowiada statusem. Po kodzie komendy jest wysyłany kolejny bajt nazwany PTYPE. Bajt PTYPE PTYPE spełnia dwie funkcje: zawiera informację o liczbie danych przesyłanych do modułu i informację, czy przesyłane po nim dane mają być przez moduł radiowy przyjęte jako ważne, czy odrzucone. Format PTYPE jest pokazany na rysunku 29. Pole SPIDLEN jest zapisywane na podstawie wartości statusu określającego ilość danych gotowych do przesłania przez moduł (0x40+ld).
Na przesłany bajt PTYPE moduł ponownie odpowiada statusem. Po bajcie PTYPE Host wysyła tyle bajtów ile jest zapisanych w polu SPIDLEN. Każdy przesłany bajt do modułu powoduje odesłanie przez moduł ważnych danych. Jeżeli CTYPE jest równy zero to Host może wysyłać do modułu cokolwiek na przykład zera. Jeżeli jednak Host ma ważne dane to musi ustawić w b7=2 w CTYPE i wysyłać bajt po bajcie ważne dane. Komenda SPI_CMD została pokazana na rysunku 30. Host może również wysłać dane do modułu w dowolnym momencie pod warunkiem, że wcześniej odczyta status 0x80.
Transmisja danych powinna być zawsze zabezpieczona sumą kontrolną CRC. Testowanie prawidłowości CRC znacznie zwiększa pewność, że przesyłane dane są prawidłowe. W systemie IRQF OS przesyłane dane są zabezpieczone CRC liczoną według zależności:
Dla danych wysłanych przez hosta
gdzie:
- DM - 8-bitowe dane wysyłane przez mastera - hosta,
- spidlen - ilość danych, a
- ^ - oznacza operację XOR.
Dla danych odbieranych przez hosta
gdzie:
- DS - 8-bitowe dane przesyłane przez slave.
Warto zauważyć, że do obliczania CRC dla danych odbieranych potrzebujemy wartości PTYPE wysyłanej w komendzie SPI_CMD do odebrania tych danych. Brak, lub błędnie wyliczona CRC może skutkować odrzuceniem danych przesyłanych z Hosta do modułu. Kontrola CRC danych odbieranych z modułu jest kontrolowana przez oprogramowanie Hosta i zależnie od woli programisty może być wykonywana lub nie. Na listingu 28 pokazano procedurę wysyłającą komendę SPI_CMD. Zależnie od argumentu dir dane przesyłane do modułu TR52D są ważne (DATAS), lub nie ważne (DATANS). Argument ld określa ilość danych, argument BufSend jest wskaźnikiem na bufor z danymi do przesłania z Hosta do modułu, a argument BufRec jest wskaźnikiem na bufor do którego są zapisywane dane odczytane z modułu. Procedura oblicza i wysyła CRC dla danych wysyłanych.
/*******************************************************
wymiana danych z modulem Iqrf po magistrali SPI.
dir - kierunek wymiany danych
DATAS - dane Hosta ważne
DATANS S- dane Hosta nie ważne
ld liczba danych taka sama do wysłania i odebrania
BufSend - bufor z danymi do wysłania
BufRec - bufor z danymi do odebrania
*******************************************************/
uint8_t CmdIQRFSpiTransfer(
uint8_t dir, uint8_t ld, uint8_t *BufSend, uint8_t *BufRec){
uint8_t ldtemp, crcm, crcs, i, status ;
ldtemp = ld;
if( dir == DATAS){
//PTYPE -> CTYPE = 1 dane ważne transfer M->S
ld |= 0x80;
}
//inicjalizacja crc z kodem komendy
crcm = 0xf0 ;
//wysłanie kodu komendy i odebranie SPISTAT
//zwraca status = SPISTAT
status = GetIQRFData(0xf0);
//crc od PTYPE
crcm ^= ld;
//wysłanie PTYPE - zwraca status = SPISTAT
status = GetIQRFData(ld);
for (i = 0; i < ldtemp; i++){
crcm ^= BufSend[i] ;
BufRec[i] = GetIQRFData (BufSend[i]);
}
crcm ^= 0x5f;
BufRec[i] = GetIQRFData(crcm);
for (i = 0; i< ldtemp; i++){
crcs ^= BufRec[i];
}
crcs ^= 0x5f;
if(crcs == BufRec[i]){
return NOERROR;
}else{
return ERROR;
}
}
Procedura ReadDevice (listing 29) odczytuje status modułu TR-52D i jeżeli ma on wartość z zakresu 0x04….0x80 wywołuje procedurę CmdIQRFSPiTransfer odczytującą dane z modułu i zapisującą je do bufora buffer_rec (argument ReadDevice). Po tej operacji zwracana jest wartość SPIDATAOK sygnalizująca, że dane z modułu zostały odebrane.
//odczytanie danych z modu?u IQRF po SPI
uint8_t ReadDevice( uint8_t *buffer_rec){
uint8_t buffer[30];
uint8_t i, status;
for (i = 1; i<20; i++){
buffer[i] = 0;
}
status = GetIQRFData(0);
if(status == 0)
return (SPINOACTIV);
if(status == 07)
return (SPISTOP);
if(status == 0x3F || status == 0x3E)
return(SPINOREADY);
if(status == 0x80)
return (SPIREADY);
if(status >= 0x40 && status <0x80){
//wysłanie komendy odczytania
//danych z modułu TR-52D
CmdIQRFSpiTransfer(
DATANS, status&0x3f, buffer, buffer_rec);
return (SPIDATAOK);
}
}
W module peryferyjnym naszej stacji pogodowej procedura ReadDevice może odbierać polecenia sterownicze, czy konfiguracyjne. Można sobie wyobrazić, że układ wyświetlacza pyta tylko o temperaturę na przykład co minutę, a o pozostałe parametry co 10 minut czy godzinę. Można wysyłać komendy zlecające różne pomiary. W prototypie moduł peryferyjny jest pytany o wszystkie pomiary jednocześnie. Jeżeli zostanie odebrana komenda W, to moduł peryferyjny odsyła bufor z zapisanymi wszystkimi pomiarami. Wysyłanie zawartości bufora realizuje funkcja DeviceSend (listing 30).
uint8_t DeviceSend (uint8_t ld, uint8_t *BufSend){
uint8_t buffer[30];
uint8_t i, status;
for (i = 1; i<20; i++){
buffer[i] = 0;
}
status = CmdIQRFSpiTransfer(
DATAS, ld, BufSend, buffer);
return status;
}
Na listingu 31 pokazano uproszczoną pętlę, w której cyklicznie są wykonywane wszystkie pomiary, sprawdzane jest odebrania komendy i jeżeli jest poprawna to wynik pomiaru jest wysyłany przez moduł TR-52D. Testowe polecenie składa się z bajtu numeru modułu (NSTER) i kodu komendy W. Nadanie modułowi numeru umożliwia odpytywanie innych modułów na przykład monitorujących zanieczyszczenie powietrza.
while (1){
//odczytanie czujnika cisnienia *str+0
LPS331PressRead (str);
//odczytanie czujnika wilgotnosci *str+8
HTS221ReadHum(str);
//odczytanie czujnika temperatury str+14 delay 1 sek
ConvTemperature(str);
status = ReadDevice(IQRFbuf);
if(status == SPIDATAOK){
if(CheckIQRFCmd(IQRFbuf) == NOERROR){
while(GetIQRFStatus () != 0x80);
__delay_ms(1);
status = DeviceSend (20,str);
}
}
}
Testowy układ czujnika został zmontowany na kawału płytki uniwersalnej (fotografia 1).
Wszystkie połączenia wykonano drutem w izolacji (kynar). Całość jest zasilana napięciem +5 V przez złącze USB modułu mikrokontrolera, a wewnętrzny stabilizator modułu dostarcza napięcia +3,3 V zasilającego wszystkie układy układu czujnika zewnętrznego. Czujnik DS18B20 jest przylutowany na czas testów bezpośrednio do wyprowadzeń na płytce. Docelowo będzie podłączony kablem o długości ok. 1 m i umieszczony na zewnątrz obudowy.
Tomasz Jabłoński, EP