Autor: Dondu
Artykuł jest częścią cyklu: Podczerwień - Transmisja danych
Aby poprawnie zdekodować sygnał odebrany w standardzie RC-5, niezbędne jest określenie poprawnego algorytmu.
Często jest tak, że ze względu na to, iż transmisja powinna przebiegać "w tle" programu głównego nie przeszkadzając mu zbytnio, należy tak opracować algorytm, by jego realizacja trwała jak najkrócej nawet kosztem zwiększonej objętości kodu wynikowego. Taki też algorytm znajdziesz poniżej.
Aby zrozumieć poprawnie działanie poniżej opisanego algorytmu, powinieneś znać i rozumieć treść dwóch artykułów:
Wstęp
Jak już ustaliliśmy w poprzednim artykule potrzebujemy licznik czasu (timer) oraz przerwanie.
Można oczywiście do rozwiązania podejść na wiele sposobów. W zależności od mikrokontrolera i posiadanych przez niego timerów można wykorzystać jeden z tych bardziej rozbudowanych posiadających na przykład tryb Input Capture lub podobny. Mamy wtedy zarówno przerwanie jak i timer w jednym, i tylko jeden pin zajęty, co jest oczywiście zaletą takiego rozwiązania.
Jednakże z reguły taki timer ma bezcenne dla projektanta funkcjonalności i przeznaczenie takiego "wypasionego" timera na bardzo prostą czynność dekodowania sygnału RC-5, może być niemożliwe lub utrudniać wykonanie pozostałych funkcjonalności projektowanego urządzenia.
Dlatego dobrym rozwiązaniem jest zastosowanie przerwań zewnętrznych z reguły oznaczanych jako INT oraz najprostszego timera.
Zastosowanie przerwania INT ma jeszcze jedną kolosalną zaletę. Z reguły taki pin wybudza mikrokontroler z najgłębszego trybu snu.
Pozwala to opracować urządzenie, które w trakcie czuwania zużywa najmniejszą możliwą ilość energii, a jednocześnie może zostać wybudzone za pomocą pilota.
Pozwala to opracować urządzenie, które w trakcie czuwania zużywa najmniejszą możliwą ilość energii, a jednocześnie może zostać wybudzone za pomocą pilota.
Z drugiej strony przerwań INT jest z reguły mało i możesz mieć je wykorzystane. W takiej sytuacji w niektórych mikrokontrolerach, zamiast przerwania INT można wykorzystać przerwanie od zmiany stanu pinu portu (tzw. Pin Change Interrupt). W takim przypadku mamy jeszcze większe oszczędności i większą elastyczność implementacji naszej komunikacji w podczerwieni. Jednakże w tym przypadku poniższy algorytm może wymagać niewielkich modyfikacji, ponieważ przerwania od zmiany stanu pinu mogą nie mieć możliwości sprzętowego rozróżniania zbocza narastającego od opadającego - wszystko zależy od zastosowanego mikrokontrolera.
Co zrobić jeżeli nie mam wolnych przerwań INT, ani Pin Change?
Jeżeli nie mamy żadnej z powyższych możliwości, to możemy się posiłkować nawet wykorzystaniem komparatora i przerwań z niego pochodzących. Z reguły komparator ma możliwość wyboru wykrywanego zbocza co ułatwia implementację. Przykład takiego rozwiązania zaprezentujemy na stronie.
Algorytm
Poniżej przedstawiam jeden z możliwych wariantów algorytmu poprawnie dekodujący sygnał w standardzie RC-5.
Algorytm jest napisany w taki sposób, by łatwo było zrozumieć zasadę jego działania, a przede wszystkim metody dekodowania. Algorytm ten można śmiało zoptymalizować do postaci znacznie krótszej.
Analizując algorytm krok po kroku zobaczysz jak odbieramy pierwszy bit startu, drugi i kolejne.
Sygnał podawany jest na pin przerwania zewnętrznego, którego reakcja na początku jest ustawiona na zbocze opadające zgodnie z wykresem ramki na wyjściu odbiornika:
Dekodując sygnał na podstawie kolejnych przerwań:
Za każdym przerwaniem zmieniamy reakcję przerwania na przeciwną (zbocze opadające, na zbocze narastające lub odwrotnie), tak aby łapać wszystkie następujące po sobie zbocza.
Zmianę zbocza na przeciwne powinniśmy wykonać na samym początku funkcji przerwania, by żadnego nie przegapić.
Zmianę zbocza na przeciwne powinniśmy wykonać na samym początku funkcji przerwania, by żadnego nie przegapić.
Timer jest ustawiony na ciągłe zliczanie z określoną częstotliwością zegara, pozwalającą na poprawne odmierzanie istotnych dla nas czasów. Za każdym przerwaniem timer służy do odczytania czasu jaki upłynął od poprzedniego przerwania, a następnie jest zerowany, by liczył czas od aktualnego przerwania.
Śledząc po kolei zbocza sprawdzamy czasy.
Jeżeli czas, który upłynął od poprzedniego zbocza nie jest zgodny ze standardem, przerywamy odbiór ramki, rozpoczynając procedurę od nowa.
Po to, by łatwiej było zrozumieć algorytm podzielony jest on na osobne fragmenty dekodujące:
- pierwszy bit startu,
- drugi bit startu,
- pozostałe bity,
- wykrywanie ostatniego bitu i zapis danych do zmiennych globalnych,
- ustawianie odbioru od początku, gdy wystąpił błąd lub zakończono odbiór ramki.
Gdy wszystkie bity ramki zostaną prawidłowo odebrane zapisujemy dane do zmiennych (nr urządzenia, komendę oraz stan bitu toggle) i czekamy na nową ramkę. Tutaj w zależności od projektu należy albo zastosować bufor odebranych danych, albo nie pozwolić na odbiór następnej ramki dopóki poprzednie dane nie zostaną odebrane przez program główny.
Algorytm przedstawiony jest w postaci pseudokodu i jest dokładnie opisany więc nie powinieneś mieć kłopotów z jego zrozumieniem.
Na bazie tego algorytmu w następnym artykule przećwiczysz działający przykład. Istotnym jest, byś rozumiał algorytm, zanim przejdziesz dalej.
//funkcja zerująca timer oraz flagę jego przepełnienia zeruj_timer_i_flage_przepelnienia() { resetuj_preskaler_timera zeruj_timer zgaś_flagę_przepełnienia_timera } //--------------------------------------------------------------------------- FUNKCJA_OBSLUGI_PRZERWANIA() { //Czy poprzednio odebrane dane zostały wykorzystane? if(poprzednie dane niewykorzystane) wyjdz_z_tej_funkcji //Na samym początku zmieniamy wykrywanie zbocza przeciwnego do //poprzedniego. Robimy to po to, by nie umknęło nam żadne przerwanie, //które może wystąpić w trakcie wykonywania niniejszej funkcji przerwania. zmien_zbocze_na_przeciwne(); //W zależności, który bit jest aktualnie dekodowany switch(numer_bitu) { //--- bit startowy nr 1 ------------------------------------ case 1: //Wykryto pierwsze zbocze opadające. //Sprawdzamy, czy przerwa w sygnale była wystarczająco długa if(przerwa wystaczajaco dluga) { //Przerwa między kolejnymi ramkami była wystarczająca, //by mieć pewność, że rozpoczynamy odbiór nowej ramki. zeruj_timer_i_flage_przepelnienia(); numer_bitu=2; dane_temp = 0; polbit_licznik = 0; //kończymy czekając na zbocze narastające drugiego bitu startu } else { //Zbyt krótka przerwa w sygnale pomiędzy ramkami //dlatego tę ramkę danych musimy przeczekać. //Tutaj nie trzeba nic wykonywać, ponieważ aktualnie numer_bitu //jest równy 1 co oznacza, że warunek na kończu funkcji //przerwania ustawi stan początkowy } break; //--- bit startowy nr 2 ------------------------------------ case 2: //sprawdzamy, czy czas połowy bitu jest zgodny z parametrami if( wystąpiło przepełnienie timera lub półbit za krótki lub półbit za długi ) { //Wykryto błąd w sygnale. //Przerywamy dekodowanie tej ramki rozpoczynamy od nowa numer_bitu = 1; break; //break, by nie sprawdzał końcowego if(numer_bitu > 14) } else { //Czas jest z zakresu półbitów //Które zbocze wywołało przerwanie? if(zbocze opadające) { //Przerwanie wywołało zbocze opadające bitu startowego nr 2 //co oznacza, że drugi bit startu odebrany prawidłowo zeruj_timer_i_flage_przepelnienia(); numer_bitu++; } else { //wykryto pierwsze zbocze narastające bitu startowego nr 2 zeruj_timer_i_flage_przepelnienia(); } } break; //--- pozostałe bity -------------------------------------- default: //Tutaj odbieramy pozostałe bity ramki //W zależności jaki czas upłynął od ostatniego zbocza (przerwania) //Czy czas wykracza poza brzegowe parametry (min i max) if( wystąpiło przepełnienie timera lub czas krótszy niż minimalny dla półbitu lub czas dłuższy niż maksymalny dla całego bitu ) { //Wykryto błąd w sygnale. //Przerywamy dekodowanie tej ramki rozpoczynamy od nowa numer_bitu = 1; break; //break, by nie sprawdzał końcowego if(numer_bitu > 14) //Czy minął czas półbitu? } else if( czas równy lub krótszy niż maksymalny dla półbitu ) { //Upłynął czas równy połowie bitu //sprawdzamy, czy to druga połowa bitu if(polbit_licznik == 1) { //Tak to druga połówka aktualnie dekodowanego bitu //i jesteśmy aktualnie w połowie czasu odbieranego //bitu. Jest to moment, w którym ustalamy wartość //odebranego bitu na podstawie kierunku zbocza. //Przesuń dane o jeden bit w lewo, by zrobić miejsce //na odebrany bit dane_temp << 1 //Jeżeli przerwanie wywołało zbocze opadające //oznacza to, że odebraliśmy jedynkę więc ją dodajemy if(było zbocze opadające) { dane_temp |= 1; } //zeruj licznik półbitów polbit_licznik = 0; //zwiększ licznik odebranych bitów numer_bitu++; } else { //To pierwsza połowa dekodowanego bitu, czyli jesteśmy //aktualnie na początku czasu przesyłanego bitu. //Ustawiamy licznik półbitów polbit_licznik = 1; } //w obu przypadkach półbitów zeruj_timer_i_flage_przepelnienia(); } else { //Upłynął czas całego bitu i jesteśmy aktualnie w połowie //odbieranego bitu. Jest to moment, w którym ustalamy wartość //odebranego bitu na podstawie kierunku zbocza. //Przesuń rejestr odbiorczy o jeden bit w lewo by zrobić //miejsce na odebrany bit dane_temp << 1 //Jeżeli przerwanie wywołało zbocze opadające //oznacza to, że odebraliśmy jedynkę więc ją dodajemy if(było zbocze opadające) { dane_temp |= 1; } zeruj_timer_i_flage_przepelnienia(); //zwiększ licznik bitów numer_bitu++; } //--- czy to już ostatni bit? -------------------------------- if(numer_bitu > 14) { //Tak, odebrano ostatni bit zapisz dane do zmiennych globalnych //1-Dane są na sześciu najmłodszych bitach dane_odebrane = dane_temp[komenda]; //2-Nr urządzenia (adres) i bit toggle status = dane_temp[toggle bit oraz nr urządzenia]; //przygotuj się do odbioru nowej ramki numer_bitu = 1; //koniec odbioru ramki ... uff nareszcie :-) } break; } //--- Błąd lub koniec transmisji ------------------------------- //Jeżeli na końcu funkcji przerwania numer bitu ma wartość 1 //to oznacza, że zarządano przerwania dekodowania z powodu błędu //lub poprawnego zakończenia dekodowania if(numer_bitu == 1) { //ustawiamy stan początkowy zeruj_timer_i_flage_przepelnienia(); ustaw_zbocze_opadajace(); polbit_licznik = 0; } }
Witam. Chciałbym się zapytać odnośnie fizyki, tego jak gdyby czasu trwania impulsu. Na rysunku jest pokazane, że jedynka binarna jest zboczem opadającym, a zero jest zboczem narastającym. Skąd mam wiedzieć ile trwa zero, a ile trwa jedynka. Jest to jakaś przypisana wartość czasowa-energia dla 1 i 0. Pozdro
OdpowiedzUsuńWitam.
UsuńPrzeczytaj proszę cały cykl artykułów o standardzie RC-5, a wszystko się wyjaśni :-)