Do jednego z planowanych projektów potrzebuję zrealizować jednokierunkową bezprzewodową wymianę danych między dwoma urządzeniami. Zaopatrzyłem się w najtańsze chyba na ten czas dostępne moduły do komunikacji radiowej mianowicie nadajnik FS1000A i odbiornik MX-RM-5V.
Jedyne dane jakie dostałem od sprzedawcy zamieszczam poniżej:
Jedyne dane jakie dostałem od sprzedawcy zamieszczam poniżej:
Parametry techniczne nadajnika: (FS1000A)
Zasięg ok. 200m
Napięcie zasilania 3,5V - 12V
Modulacja AM ASK/OOK
Moc 10mW
Transfer ~ 10kB/s
Pobór prądu: 40mA(12V) oraz 9mA(3,5V)
Wymiary: 30mm x 14mm x 9mm
Parametry techniczne odbiornika: (MX-RM-5V)
Napięcie zasilania 5V
Czułość 100dB
Pobór prądu ok. 5.5mA
Wymiary: 19mm x 19mm x 7mm
Rozpocząłem przeglądać internet w celu zdobycia informacji jak zacząć pracę z tymi modułami. Materiałów jest sporo, niestety niemalże wszystkie opisują pracę na Arduino i to z gotowymi bibliotekami (np. VirtualWire). Udało mi się jednak znaleźć przydatne informacje i uruchomić moduły.
Na początku warto wspomnieć, że to najtańsze (zapewne Chińskie) moduły komunikacyjne. Ja mam akurat na częstotliwość 433 MHz. Nie posiadają one zaimplementowanego żadnego protokołu komunikacji (USART, SPI itp.). Zatem taki prosty protokół, który chociaż trochę uodporni naszą transmisję na zakłócenia, musimy napisać sami. Do pierwszego uruchomienia zastosuję kodowanie Manchester (jak radzi wiele źródeł). Zasada działania samego kodowania nie jest skomplikowana i bez problemu można ją znaleźć w literaturze i internecie. Najprościej mówiąc: każdy nadawany bit jest dzielony na połowę. W czasie trwania pierwszej połowy nadawany jest faktyczny stan bitu, w czasie drugiej połowy nadawany jest stan przeciwny. Przykładowo, chcemy nadać 3 bity: 0b011 z częstotliwością 1Hz. W normalnej transmisji (bez kodowania Manchester) nadalibyśmy stan HIGH przez 1s, następnie znowu stan HIGH i na końcu stan LOW przez 1s. Jeśli zastosujemy kodowanie Manchester transmisja będzie wyglądała następująco: nadajemy stan HIGH przez 0,5s następnie zmieniamy stan na przeciwny na 0,5s - czyli nadajemy stan LOW. W tym miejscu kończy się przesył najmniej znaczącego bitu. Następnie znów nadajemy stan HIGH przez 0,5s i przez kolejne 0,5s zmieniamy stan na przeciwny - LOW. Tutaj kończy się nadanie drugiego bitu. Ostatni bit do przesłania to zero, zatem przez 0,5s ustawiamy stan LOW i zmieniamy go na przeciwny ustawiając stan HIGH. Tyle o transmisji kodowanej Manchesterem. Poniżej znajduje się kod odbiornika i nadajnika.
Kod nadajnika:
#include <avr\io.h> #include <stdio.h> #include <util/delay.h> #define F_CPU 12000000UL //do testów LED #define LED1PIN (1<<PD5) #define LED1PORT PORTD #define LED1DDR DDRD #define LED1setHIGH LED1PORT |= LED1PIN #define LED1setLOW LED1PORT &= ~LED1PIN //pin nadawania #define TXPIN (1<<PB4) #define TXPORT PORTB #define TXDDR DDRB #define TXsetHIGH TXPORT |= TXPIN #define TXsetLOW TXPORT &= ~TXPIN #define TXtoggle TXPORT ^= TXPIN //wysylanie 8bitow (wystawienie stanu wysokiego jesli byl stan wysoki void TxSendByte(uint8_t sendingValue) { //zapalamy led LED1setHIGH; //wybudzenie odbiornika (wyslanie 20 razy naprzemiennie 1 i 0 for(int i = 0;i<20;i++) { TXsetHIGH; _delay_us(500); TXsetLOW; _delay_us(500); } //synchronizacka (3ms stan wysoki, 500us niski) TXsetHIGH; _delay_us(3000); TXsetLOW; _delay_us(500); //nadawanie 8 bitów - wartosc przekazana do funkcji for(int i = 0;i<8;i++) { if( (sendingValue>>i) & 0b00000001) TXsetHIGH; else TXsetLOW; _delay_us(500); TXtoggle; _delay_us(500); } //po zakonczeniu nadawania ustawiamy 0 TXsetLOW; //gasimy led LED1setLOW; } int main(void) { //init ledów LED1DDR |= LED1PIN; //pin LED1 jako wyjscie LED1setHIGH; //init nadawania TXDDR|= TXPIN; TXsetLOW; //pętla główna while(1) { //wysyłanie na przemian (co 1s) cyfry 123 i 13 TxSendByte(123); _delay_ms(1000); TxSendByte(13); _delay_ms(1000); } }
Kod odbiornika:
#include <avr\io.h> #include <stdio.h> #include <avr/interrupt.h> #include <util/delay.h> #define F_CPU 16000000UL //do testów LED #define LED1 (1<<PC0) #define LED1port PORTC #define LED1ddr DDRC #define LED1setHIGH LED1port |= LED1 #define LED1setLOW LED1port &= ~LED1 //pin odbierania #define RX (1<<PD2) #define RXport PORTD #define RXddr DDRD #define RXpin PIND uint8_t isThatNoise = 1; volatile uint8_t recievedByte=0; ISR(INT0_vect) { //sprawdzenie czy jest 1 for(int i=0;i<100;i++) { _delay_us(20); isThatNoise = 0; if(!(RXpin & RX)) { isThatNoise = 1; break; } } //jesli to nie zakłócenia to wchodzi do ifa if(!isThatNoise) { EIMSK &= ~(1<<INT0); //wyłączenie przerwania od INT0 while(1) { //czekanie jak minie 3ms stanu wysokiego, jak pojawi się stan niski to przeczekuje stan niski + zapas i zaczyna odczytywac if(!(RXpin & RX)) { _delay_us(750); //petla odczytująca po kolei każdy bit for(int i=0;i<8;i++) { //sprawdzenie co zostało przesłane if(RXpin & RX) //jesli przesłana jest 1 recievedByte |= (1<<i); else //jesli przeslane jest 0 recievedByte &= ~(1<<i); _delay_us(1000); //przed przejsciem do kolejnej iteracji odczekanie az nadajnik nada kolejny bit } break; //wyjscie z nieskonczonej petli while(1) } } } //zezwolenie na przerwania EIMSK |= (1<<INT0); //zezwolenie na przerwania od INT0 } void usartInitialize(void) { #define BAUD 9600 #include <util/setbaud.h> UBRR0H = UBRRH_VALUE; UBRR0L = UBRRL_VALUE; #if USE_2X //jesli nie da sie uzykac BAUD bez trybu zwiekszenia predkosci to go zwieksza UCSR0A |= (1<<U2X0); #else UCSR0A &= ~(1<<U2X0); #endif UCSR0B = (1<<TXEN0); //włącza nadajnik UCSR0C = (1<<UCSZ00) | (1<<UCSZ01); //8bitow danych, 1bit stopu, brak bitu parzystosci } void usartSendByte(uint8_t sendingByte) { while( !( UCSR0A & (1<<UDRE0) ) ); //czekanie jak bufor nadawania bedzie pusty UDR0 = sendingByte; //przypisanie danych do bufora nadawania (wysłanie) } int main(void) { //wlacz przerwania globalne sei(); //init ledow LED1ddr |= LED1; //wyjscie LED1setHIGH; //init pinu RX RXddr &= ~RX; //wejscie RXport &= ~RX; //bez pull-up (stosuje zewnętrzny rezystor pulldown 270 omów) //init przerwań od INT0 (PD2) EICRA |= (1<<ISC01) | (1<<ISC00); //przerwanie od zbocza narastającego EIMSK |= (1<<INT0); //zezwolenie na przerwania od INT0 //init uart usartInitialize(); //petla glowna while(1) { if(recievedByte != 0) { usartSendByte(recievedByte); if(recievedByte == 13) { recievedByte = 0; LED1setLOW; _delay_ms(250); LED1setHIGH; } else recievedByte = 0; } } }
Przeprowadziłem testy. Warto wspomnieć, że jest tutaj zastosowane jedynie kodowanie Manchester z synchronizacją na początku. Nie ma żadnej kontroli błędów w postaci sumy kontrolnej (bit parzystości/nieparzystości lub CRC). Przesyłałem co 1s na przemian liczbę 8-bitową. Najpierw 123, następnie 13. Sprawdzałem, czy odbiornik poprawnie je odbierze. Początkowo, uruchomiłem moduły bez wlutowania dodatkowej anteny. Niestety zasięg to 1 cm, czyli żaden. Zgodnie z zaleceniami znalezionymi w internecie, do poprawy zasięgu wystarczy antena w postaci kawałka drutu. Długość kawałka jest określana jako 1/4 długości fali elektromagnetycznej, co dla 433MHz wynosi ok 17,3 cm. Po zastosowaniu takiej anteny moduły mogłem już odsunąć od siebie na większą odległość. Jednak - nadal nie była to odległość mnie zadowalająca.
Do testów złożyłem dwa proste układy. Układem nadajnika steruje mikrokontroler AVR ATTiny2313. Odbiornik podłączony jest z mikrokontrolerem ATMega328P. Do testów, podłączyłem go również przez konwerter USART-USB do komputera. Dzięki temu wszystko co przyjdzie do odbiornika wysyłam przez USART i odczytuję w terminalu. Poniżej znajdują się screeny z terminala dla czterech odległości: 20 cm, 100 cm, 200 cm oraz 300 cm. Na czerwono zaznaczyłem błędnie odebrane dane. Przy większych odległościach możemy zaobserwować, że nie wszystkie transmitowane liczby są w ogóle odbierane przez odbiornik. Dzięki temu, że przesyłam naprzemiennie dwie różne liczby, nawet w przypadku kiedy przesłana będzie błędna liczba wiemy jaka powinna być przesłana kolejna.
Dane o liczbie błędnie odebranych danych zebrałem poniżej. Dla większych odległości wyniki są mniej miarodajne, ponieważ niestety nie mam wiedzy o liczbie wysłanych danych. Zatem mogę jedynie określić ile z otrzymanych danych było poprawne, a nie ile z nadanych danych było poprawne. No cóż, uwzględnię to przy kolejnych testach.
odległość | % poprawnych danych |
---|---|
20 cm | 100% |
100 cm | 92% |
200 cm | 59% |
300 cm | 43% |
Tak jak wspomniałem, gdyby np. do odległości 300 cm doliczyć dane, które w ogóle nie zostały odebrane, to % poprawnych danych zapewne zmniejszyłby się ok. dwukrotnie.
Brak komentarzy:
Prześlij komentarz