Mikrokontrolery - Jak zacząć?

... czyli zbiór praktycznej wiedzy dot. mikrokontrolerów.

czwartek, 31 marca 2011

Podczerwień - Transmisja danych


Autor: Dondu

Transmisja danych z wykorzystaniem podczerwieni jest jedną z najtańszych, bezprzewodowych rodzajów transmisji i jest powszechnie używana w urządzeniach domowych, zabawkach, robotach, itp.

W świecie mikrokontrolerów wykorzystanie podczerwieni do transmisji danych, jest bardzo łatwe w realizacji w oparciu o timery i system przerwań. Pozwala to na komunikację "w tle" prawie nie zauważalną dla naszego urządzenia.




Mała podpowiedź

Aby prosto sprawdzić, czy pilot cokolwiek nadaje wystarczy popatrzeć na niego przez kamerę aparatu cyfrowego lub telefonu:


Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

Podczerwień: RC-5 - Opis standardu


Autor: Dondu

Artykuł jest częścią cyklu: Podczerwień - Transmisja danych


Standard RC-5 powstał w okolicach 1980 roku. Opracowany został przez firmę Philips jako standard używany do sterowania produkowanych urządzeń domowych w szczególności sprzętu audio-wideo. Szybko został adoptowany przez innych producentów.

Ze względu na proste zasady tworzenia i dekodowania sygnału w standardzie RC-5, jest on bardzo popularny wśród hobbystów i wytwórców urządzeń o niskim nakładzie. Wpływa na to także obecność na rynku bardzo tanich pilotów RC-5 o różnych ilościach klawiszy, kształtach i wielkościach.


Standard RC-5

Standard RC-5 jest standardem transmisji szeregowej o następujących podstawowych parametrach:
  • kodowanie bifazowe typu Manchester,
  • częstotliwość sygnału nośnego 36kHz (ang. carrier frequency),
  • okres jednego bitu przesyłanej informacji równy 64 okresom częstotliwości nośnej.

Tylko tyle? Tak, ponieważ reszta wynika z powyższych założeń, co po kolei pokażę w niniejszym artykule. Na początek musisz jednak poznać kodowanie typu Manchester.





Kodowanie typu Manchester

Ten system kodowania opiera się o następujące zasady:

Zasada nr 1: 
Na początku sygnał przyjmuje stan odpowiadający jego wartości binarnej.

Należy to rozumieć następująco:
  • jeżeli wysyłany bit jest zerem, to na początku bitu w sygnale wyjściowym Manchester także mamy mieć zero.
  • jeżeli wysyłany bit jest jedynką, to na początku bitu w sygnale wyjściowym Manchester także mamy mieć jedynkę.




Zasada nr 2:
W połowie czasu transmisji bitu następuje zmiana sygnału na przeciwny.

Zmiana ta następuje zawsze, niezależnie, czy przesyłany bit jest zerem, czy jedynką.




Bardzo istotne jest byś zwrócił uwagę na to że:

W sygnale wyjściowym Manchester czas trwania zera lub jedynki:
  • nigdy nie jest dłuższy niż czas trwania transmisji jednego bitu,
  • nigdy nie jest krótszy niż połowa czasu trwania transmisji jednego bitu.

To bardzo ważne wnioski, które będą nam potrzebne do dekodowania sygnału w odbiorniku.


Te proste założenia kodowania bifazowego typu Manchester mają bardzo pożądane przez nas właściwości:
  1. ciągła synchronizacja transmisji pomiędzy nadajnikiem i odbiornikiem bez dodatkowej linii,
  2. duża odporność na zmiany szybkości transmisji.





Fala nośna 36kHz

Aby zwiększyć odporność na zakłócenia oraz umożliwić komunikację jednocześnie wielu pilotów nadających w tym samym obszarze (np. pomieszczeniu) stosuje się modulację fali nośnej.

W standardzie RC-5 jako fala nośna wykorzystywany jest przebieg prostokątny o stałej częstotliwości 36kHz.

RC-5 - Fala nośna o częstotliwości 36kHz.
RC-5 - Fala nośna o częstotliwości 36kHz.

Okres takiej fali wynosi:




W praktyce oznacza to, że dioda LED mruga z częstotliwością 36kHz, czyli co około 27,78μs.

Wypełnienie fali nośnej (ang. duty cycle)  wynosi od 25% do 33%. Jest to istotne w przypadku budowy pilota we własnym zakresie. Jeżeli jednak chodzi o odbiornik nie ma to dla nas prawie żadnego znaczenia, ponieważ pracę związaną z falą nośną wykona za nas czujnik IR.






Przesył danych, czyli modulacja fali nośnej.

Aby za pomocą fali nośnej przesłać dane należy modulować jej przebieg za pomocą danych do przesłania.
Do przesyłania danych za pomocą podczerwieni stosuje się modulację polegającym na naprzemiennym nadawaniu oraz przerwach w nadawaniu:


Podczerwień - Przykład sygnału modulowanego.
Przykład sygnału modulowanego.

W standardzie RC-5 przyjęto, że okres jednego bitu przesyłanej informacji jest równy 64 okresom fali nośnej.

RC5 - Fala nośna - okres dla przesłania jednego bitu danych.
RC5 - Fala nośna - okres dla
przesłania jednego bitu danych.


W związku z tym czas przesyłania jednego bitu wynosi:


RC-5 Okres przesyłania jednego bitu danych.
RC-5 Okres przesyłania jednego bitu danych.

Ponieważ kodowanie Manchester zmienia stan generowanego przebiegi w połowie każdego bitu stąd powinniśmy wiedzieć, że tzw: półbit trwa:


RC-5 Okres przesyłania połowy bitu danych.
RC-5 Okres przesyłania połowy bitu danych.


czyli sumarycznie możemy przedstawić falę nośną potrzebną do przesłania jednego bitu danych następująco:


RC5 - Charakterystyka czasowa przesyłania  bitu za pomocą fali nośnej.
RC5 - Charakterystyka czasowa przesyłania 
bitu za pomocą fali nośnej.


Skoro mamy już ustalone zasady dot. czasów przesyłania jednego bitu oraz wiemy, że kodowanie Manchester wymusza zmianę stanu sygnału wyjściowego na przeciwny w połowie nadawania bitu, stąd wnioskujemy, że możliwe są tylko dwa przypadki modulowania sygnału, poprzez przerwanie nadawania fali nośnej:
  1. nadawanie fali nośnej w pierwszej połówce przesyłanego bitu danych oraz przerwa w nadawaniu trwająca całą drugą połówkę,
  2. lub odwrotnie.
Przypadek nr 1 został w standardzie RC-5 przypisany nadawaniu logicznego zera:



Modulacja sygnału standardu RC-5: Logiczne zero
Modulacja sygnału nadawania standardu RC-5
Logiczne zero

Źródło: www.sbprojects.com


a przypadek nr 2 nadawaniu logicznej jedynki:


Modulacja sygnału nadawania standardu RC-5
Logiczna jedynka
Źródło: www.sbprojects.com

Zaraz, zaraz! Coś tu się nie zgadza!

Przecież pierwsze założenie kodu Manchaster brzmi: "Na początku sygnał przyjmuje stan odpowiadający jego wartości binarnej".

Dlaczego więc logiczne zero w pierwszej połówce nadaje falę nośną, a logiczne jeden nadaje ją w drugiej połówce? Powinno być przecież odwrotnie!

Jeżeli to zauważyłeś, to gratuluję! :-)

Tak, sygnały RC-5 w trakcie nadawania jest zanegowany ponieważ odbiornik ponownie go zaneguje w czasie odbioru. W ten sposób na wyjściu odbiornika, otrzymamy prawidłowy sygnał zgodny z kodowaniem Manchester. Przeczytasz o tym szczegółowo w artykule dot. dekodowania sygnału.


W praktyce wysyłanie danych wygląda więc tak:


Modulacja sygnału podczerwieni - układ nadajnika - animacja.
Modulacja sygnału podczerwieni.
Układ nadajnika.
Źródło: www.sbprojects.com

Nie sugeruj się ilością impulsów na powyższej animacji ponieważ nie jest ona zgodna ze standardem RC-5. Niemniej jednak pokazuje jak generowana jest zmodulowana fala nośna (wysyłane światło), przesyłająca zakodowane dane za pomocą podczerwieni.






Ramka danych RC-5

Standard RC-5 jest standardem transmisji szeregowej, która wykorzystuje tzw. ramki.

Ramka, to określony standardem zbiór pojedynczych bitów danych składający się na pakiet danych określony standardem. W przypadku standardu RC-5 pakiet ten składa się z 14 bitów podzielonych następująco:


Ramka nadawania danych standardu RC-5.
Ramka nadawania danych standardu RC-5.
źródło: www.sbprojects.com

Na powyższym rysunku przedstawiona jest ramka nadawania wraz z przykładem przesyłanych danych oraz opisem poszczególnych bitów. Czarne pola to fragmenty fali nośnej.

Ramka odbiorcza widziana przez mikrokontroler jest negacją powyższej ramki, co pokażę w artykule o dekodowaniu sygnału pilota.

Transmisja rozpoczyna się od bitu nr 1, a kolejne bity oznaczają:

Numer bitu Opis
1-2 Dwa bity startu zawsze będące jedynką.
3 Bit toggle, zmieniający wartość na przeciwną wraz z każdym naciśnięciem przycisku pilota. 
4-8 Numer sterowanego urządzenia (adres). W przypadku standardowych pilotów podczerwieni, to rodzaj urządzenia do którego pilot jest przeznaczony np. TV, DVD, itd.
9-14 Dane właściwe. W przypadku pilotów numer komendy do realizacji przez urządzenie.


Na powyższym rysunku widzimy więc:
  • dwa bity startu będące jedynkami,
  • bit toggle o wartości zero,
  • numer sterowanego urządzenia o wartości 5 (binarnie 00101),
  • komendę o wartości 53 (binarnie 110101).
Gdy w tabelkach poniżej odczytasz numer urządzenia (5) oraz komendy (53) stwierdzisz, że powyższy przykład ramki adresowany jest do urządzenia VCR1 (magnetowid) i żąda od niego włączenia funkcji odtwarzania (Play).


Przycisk trzymany dłużej - kolejne ramki

W przypadku pilotów standardu RC-5, gdy przycisk jest trzymany, ramki wysyłane są jedna za drugą w odstępach 114ms. Jeżeli więc świadomie i celowo trzymasz przyciśnięty przycisk, to pilot wysyła:

RC5 - Częstotliwość wysyłania ramek, gdy przycisk jest przyciśnięty.
RC5 - Częstotliwość wysyłania ramek, gdy przycisk jest przyciśnięty.

prawie 9 ramek na sekundę.


Bit toggle

Ponieważ z reguły naciśnięcie przycisku trwa dłużej niż 114ms, stąd w takim przypadku do odbiornika wysyłane jest co najmniej kilka ramek.

Aby sterowane pilotem urządzenie mogło rozróżnić, czy kolejna odebrana ramka oznacza przytrzymanie przycisku, czy też jego puszczenie i ponowne naciśnięcie, w standardzie RC-5 zastosowano dodatkowy bit zwany toggle bit (bit przełączający).

Pilot zmienia wartość tego bitu w przypadku wykrycia momentu naciśnięcia przycisku.

Jeżeli więc w dwóch lub więcej kolejnych ramkach bit toggle ma tę samą wartość oznacza to, że nie nastąpiło puszczenie przyciski i ponowne wciśnięcie.

Jeżeli w dwóch lub więcej kolejnych ramkach bit toggle zmienia wartość na przeciwną w stosunku do stanu bitu w poprzedniej odebranej ramce oznacza to, że nastąpiło puszczenie przyciski i ponowne jego wciśnięcie.

W ten prosty sposób, urządzenie odbiorcze może rozróżnić szybkie przyciskanie i puszczanie przycisku od przytrzymania go przez jakiś czas.


Standardowe numery urządzeń i komendy RC-5

Ponieważ standard RC-5 został opracowany przez firmę Philips, stąd istnieje zbiór standardowych numerów urządzeń sterowanych oraz komend. Poniższą listę należy traktować jako niepełną.

Więcej znajdziesz na angielskojęzycznej Wikipedii: tutaj

Numer
urządzenia
dec (hex)
Rodzaj
urządzenia
0 (0x00) TV1
1 (0x01) TV2
2 (0x02) Teletext
3 (0x03) Video
4 (0x04) LV1
5 (0x05) VCR1
6 (0x06) VCR2
7 (0x07) Experimental
8 (0x08) Sat1
9 (0x09) Camera
10 (0x0A) Sat2
11 (0x0B)
12 (0x0C) CDV
13 (0x0D) Camcorder
14 (0x0E)
15 (0x0F)
16 (0x10) Pre-amp
17 (0x11) Tuner
18 (0x12) Recorder1
19 (0x13) Pre-amp
20 (0x14) CD Player
21 (0x15) Phono
22 (0x16) SatA
23 (0x17) Recorder2
24 (0x18)
25 (0x19)
26 (0x1A) CDR
27 (0x1B)
28 (0x1C)
29 (0x1D) Lighting
30 (0x1E) Lighting
31 (0x1F) Phone
Komenda
dec (hex)
Komenda TV Komenda VCR
(0x00) 0 0
1 (0x01) 1 1
2 (0x02) 2 2
3 (0x03) 3 3
4 (0x04) 4 4
5 (0x05) 5 5
6 (0x06) 6 6
7 (0x07) 7 7
8 (0x08) 8 8
9 (0x09) 9 9
10 (0x0A) -/-- -/--
12 (0x0C) Standby Standby
13 (0x0D) Mute
16 (0x10) Volume +
17 (0x11) Volume -
18 (0x12) Brightness +
19 (0x13) Brightness -
32 (0x20) Program + Program +
33 (0x21) Program - Program -
50 (0x32) Fast Rewind
52 (0x34) Fast Forward
53 (0x35) Play
54 (0x36) Stop
55 (0x37) Recording

Na podstawie: www.sbprojects.com

W przypadku, gdy dysponujesz fabrycznym pilotem w standardzie RC-5 wystarczy odebrać jedną ramkę by dowiedzieć się, jaki numer urządzenia ma zakodowany pilot. Niektóre piloty mają możliwość sterowania kilkoma urządzeniami, będą więc generować różne numery urządzeń.

W przypadku, gdy budujesz własny pilot podczerwieni powinieneś wybrać numer urządzenia, którym pilot ma sterować.

W przypadku, gdy budujesz własnego pilota i własne urządzenie odbiorcze, masz pełną dowolność w wyborze zarówno numeru urządzenia jak i komend oraz ich znaczenia. Aby jednak nie narazić się na problem, że w kontakcie z innym pilotem Twoje rządzenie zaczyna "wariować" sugeruję wybranie wolnych numerów urządzeń. Warto także w miarę możliwości i potrzeb utrzymać numerów komend zgodnych ze standardem.


Nie rozumiesz czegoś? Masz wątpliwości?
Nie wahaj się pytać za pomocą systemu komentarzy do artykułu.

W następnym artykule z tego cyklu poznasz praktyczny przykład dekodowania kodu RC-5 przez mikrokontroler.

Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

Podczerwień: RC-5 - Dekodowanie sygnału


Autor: Dondu

Artykuł jest częścią cyklu: Podczerwień - Transmisja danych


Poznałeś już zasady dot. nadawania w podczerwieni w standardzie RC-5, czas więc poznać zasady dot. dekodowania sygnału odebranego z odbiornika podczerwieni.

Aby zrozumieć niniejszy artykuł powinieneś znać i rozumieć treść artykułu: RC-5 - Opis standardu


Jak dekodować sygnał standardu RC-5?

Na początku przypomnijmy sobie wygląd ramki nadawczej standardu RC-5:

RC-5: Podczerwień - Ramka widziana na wyjściu nadajnika.
Wyk. 1 - Przykład ramki nadawczej.

oraz informację podaną w art. wymienionym powyżej:

Ramka odbiorcza widziana przez mikrokontroler jest negacją powyższej ramki.

Skoro jest zanegowana, to wygląda tak:


RC-5: Podczerwień - Ramka widziana na wyjściu odbiornika (wejściu mikrokontrolera).
Wyk. 2 - Ramka z wyk. 1 widziana na wyjściu odbiornika (wejściu mikrokontrolera). 

W ramce nadawczej (wykres nr 1) zaznaczone są  czarne pola , o których pisałem w poprzednim artykule. Jest to fala nośna o częstotliwości 36kHz. Gdyby wykres był narysowany w powiększeniu zobaczyłbyś tę falę.

Dlaczego w ramce odbiorczej  (wykres nr 2) nie widać fali nośnej, tylko ładny prostokątny sygnał?

Odbiornik, który używamy do odbioru sygnału podczerwieni, wykonuje za nas kolosalną pracę związaną z filtrowaniem sygnału z większości zakłóceń przy okazji zamieniając falę nośną na przebieg prostokątny.

W praktyce wygląda to tak:

Obróbka sygnału przez odbiornik podczerwieni.
Obróbka sygnału przez odbiornik podczerwieni.
Źródło: www.sbprojects.com

Na powyższej animacji ładnie widać negację sygnału oraz jego filtrowanie z fali nośnej. Nie sugeruj się ilością impulsów na powyższej animacji ponieważ nie jest ona zgodna ze standardem RC-5.

Gdybyśmy nie wykorzystywali scalonego odbiornika podczerwieni, a jedynie sam fototranzystor itp., to układ nasz musiałby wykonać właśnie takie przekształcenie pozbywając się przy okazji zakłóceń z różnych źródeł światła w tym dziennego i fluorescencyjnego. To nie jest proste i dlatego odbiorniki nieco kosztują.





Dekodowanie

Na początku przypomnimy sobie kilka istotnych informacji z poprzedniego artykułu:

W sygnale wyjściowym Manchester czas trwania zera lub jedynki:
  • nigdy nie jest dłuższy niż czas trwania transmisji jednego bitu,
  • nigdy nie jest krótszy niż połowa czasu trwania transmisji jednego bitu.

oraz dwie zasady:

Podczerwień RC-5: Kodowanie Manchester - zasada nr 1
Kodowanie Manchester - Zasada nr 1



Podczerwień RC-5: Kodowanie Manchester - zasada nr 2
Kodowanie Manchester - Zasada nr 2


Powyższe wykresy pokazują sygnał nadawany. Pamiętaj, że po stronie wyjścia odbiornika (czyli wejścia mikrokontrolera) otrzymujemy sygnał zanegowany.





Synchronizacja

Zanim pojawi się pierwsze zbocze sygnału, może trwać przerwa w nadawaniu dowolną ilość czasu:

RC5: W sygnale odbieranym cisza przed pierwszym zboczem pierwszego bitu startu.


Nasuwa się więc pytanie: Skąd mamy wiedzieć, że właśnie rozpoczął się pierwszy bit startu, skoro pierwsze zbocze opadające jest już w jego połowie?

Musimy więc przyjąć pewne zasady, które pozwolą nam zsynchronizować się z odbieranym sygnałem.

Standard RC-5 zawiera w sobie sygnał zegarowy synchronizujący transmisję, o czym pisałem w poprzednim artykule. Musimy jakoś ten sygnał odzyskać z odebranego sygnału z odbiornika. Jak to zrobić?

Popatrzmy na zasadę nr 2 pokazaną wyżej. Zasada ta, pozwoli nam wykorzystać do zsynchronizowania się z sygnałem.

Jak to odczytać?
Zauważ, że niektóre zmiany stanu sygnału (zbocza) występują na początku (końcu) bitu,

RC5: Zbocza sygnału na początku i końcach bitów.


 a niektóre w jego połowie

RC5: Zbocza sygnału w połowach bitów.


Na szczęście wiemy, że z podstawowych założeń standardu RC-5 da się obliczyć (patrz poprzedni artykuł) czas trwania bitu oraz połowy bitu i wynoszą odpowiednio:

RC-5: Czas trwania całego bitu.

RC-5: Czas trwania połowy bitu.

Stąd już prosta droga do synchronizacji pod warunkiem, że dokładnie w momencie wystąpienia każdego zbocza będziemy o tym wiedzieli. Potrzebujemy więc funkcji mikrokontrolera, która pozwoli nam na natychmiastową reakcję w momencie wystąpienia zbocza.

Wniosek 1:
Potrzebne nam są przerwania.


No dobrze, wiemy już że potrzebne są nam przerwania, ale odstępy między zboczami trwają 1778µs lub 889µs i w dodatku czasami zaczynają się w połowie bitu, a czasami na jego początku:


RC-5: Przykład ramki z naniesionymi czasami bitów i półbitów.


Co gorsza w pobliżu może nadawać inny pilot RC-5 i przerwania mogą się nałożyć skracając czasy ich wystąpienia. Może także nadawać inny pilot w innym niż RC-5 standardzie i możemy otrzymać z niego jeszcze inne czasy pomiędzy zboczami.

Jak to ogarnąć? Trzeba jakoś mierzyć te czasy.

Wniosek 2:
Potrzebny nam sprzętowy licznik czasu.

Podsumowując, jeżeli w momencie wystąpienia zbocza otrzymamy przerwanie, to istotnym dla nas będzie fakt, ile czasu upłynęło od poprzedniego przerwania, i że mogą wystąpić następujące przypadki:
  1. czas bliski 1778µs,
  2. czas bliski 889µs
  3. czasy znacznie krótsze lub znacznie dłuższe od 2 i 3.

Popatrzmy jak przebiegałoby to na przykładzie naszej ramki. Moglibyśmy liczyć czas łączny od pierwszego zbocza opadającego:

RC-5: Przykład ramki z naniesionymi sumarycznymi czasami bitów i półbitów.

Niestety, ale:

W algorytmie sumującym czasy od pierwszego zbocza opadającego bardzo szybko doprowadzimy do rozsynchronizowania się odbioru z nadawanym sygnałem.

Dlaczego tak się stanie?

Dlatego, że błędy w odmierzaniu czasów przez nadajnik (nie są przecież idealne) oraz odbiornik (np. nasze przerwanie wystąpiło w momencie obsługi innego i musi chwilkę zaczekać, niedokładność zegara, zmiana temperatury, itp.) są sumowane, przez co pod koniec odbioru ramki może znacznie zwiększyć się błąd sumaryczny, co w konsekwencji może prowadzić do utraty synchronizacji.

Innymi słowy patrząc na przykładowy wykres czasów sumarycznych (powyżej) zbocze, które według czasu nadajnika powinno wystąpić w okolicach 21.336µs, otrzymasz według odbiornika np. w 24.000µs. Nadajnik wysłał je więc jako połowa bitu 14, a odbiornik odbierze je jako koniec bitu 14 i w konsekwencji mamy błąd transmisji.

Co więc należy zrobić?

Wniosek 3:
Wykorzystać właściwości kodu Manchester i synchronizować się z każdym wykrytym zboczem sygnału.

Jak to zrobić?

Skoro z powodu możliwości rozsynchronizowania się odbioru nie możemy sumować czasu od pierwszego przerwania, to musimy zastąpić je pomiarem czasu od ostatniego przerwania:


RC-5: Przykład ramki z zasadą pomiaru czasów bitów i półbitów.

oraz zliczać odebrane połowy bitów i całe bity. W ten sposób będziemy mogli dokładnie określić, w którym miejscu ramki odbiorczej w danym momencie (obsługiwanym przerwaniu od zbocza) jesteśmy.

Wniosek 4:
Potrzebne nam są programowe liczniki (zmienne) bitów i półbitów.

Podsumowując uchwycimy już każde zbocze sygnału i będziemy znali czas od poprzedniego zbocza, a mając w zmiennych licznik bitów i półbitów, nie rozsynchronizujemy się z nadawanym sygnałem.





Wartość bitu

Jak z sygnału wyciągnąć informację o tym, czy przesyłany bit jest jedynką, czy zerem?

Zasada nr 1 mówi, iż na początku bitu jego stan odpowiada jego wartości, wystarczyłoby więc odczytać stan pinu w momencie początku bitu. Nie zawsze jednak mamy przerwanie w momencie, gdy bit się rozpoczyna i w tym momencie przychodzi nam z pomocą zasada nr 2, czyli zmiana stanu bitu (wystąpienie zbocza) zawsze w połowie bitu:

RC-5: Przykład ramki i zasady kodowania Manchester - zmiana stanu zawsze w połowie bitu.


W tym momencie powinniśmy więc określać wartość bitu.

Jak to zrobić? Przypomnijmy sobie, jak reprezentowane są bity od strony nadawania:

Sygnał wychodzący z pilota RC-5 dla logicznego zera oraz jedynki
Źródło: www.sbprojects.com


a pamiętając o zanegowaniu i filtracji fali nośnej przez odbiornik otrzymujemy po stronie wyjścia z odbiornika (np. podłączonego do pinu wejściowego mikrokontrolera):


Sygnał wychodzący z odbiornika podczerwieni
dla logicznego zera oraz jedynki.


Podsumowując:

Wniosek 5:
Gdy w sygnale wychodzącym z odbiornika w połowie bitu:
  • występuje zbocze opadające, to bit ma wartość jeden,
  • występuje zbocze narastające, to bit ma wartość zero.





Początek odbioru

Szczególnym przypadkiem jest początek dekodowania sygnału (pierwszy bit startu), gdy mamy bliżej nieokreślony czas w przerwie nadawania. Może on być strasznie długi, ale może być także niewiele dłuższy niż czas przesyłania jednego bitu, czyli 1778µs.

Niestety standard RC-5 nie określa parametru minimalnego czasu trwania ciszy przed pierwszym bitem startu.

Jak z tego wybrnąć?

Jednym z założeń standardu RC-5 jest fakt, że pilot dla przytrzymanego dłużej przycisku wysyła kolejne ramki co 114ms. To już daje nam pojęcie o tym, że nasz algorytm powinien czas przerwy nadawania przed pierwszym bitem startowym rozpoznawać jako nie dłuższy niż 114ms.

Czas przerwy oczywiście może być dłuższy i będzie takowy wtedy, gdy nie naciskamy przycisku. Istotne jest dla nas jednak to, że nasz algorytm nie może przyjmować, że przerwa musi mieć np. 200ms, bo nie będziemy w stanie odebrać kolejnych ramek wysyłanych przez pilota, gdy przez dłuższy czas jest naciśnięty przycisk.

No dobrze - mamy więc przedział czasu pomiędzy: 1778µs, a 114ms.

Przedział jest duży - jaki czas mam wybrać?

Super byłoby, gdybyśmy przyjmowali minimalny czas ciszy przed pierwszym bitem startu np. około 100ms. Jednakże tutaj trzeba brać pod uwagę, że piloty nie są super dokładnymi urządzeniami i w większości przypadków nie posiadają kwarców, które zapewniły, by im dużą dokładność utrzymania czasów wysyłanego sygnału. Podobnie z odbiornikiem - nie zawsze mikrokontroler korzysta z kwarcu.

Dlatego przyjęcie bezpiecznego progu np. 50ms, jest bardzo korzystne z punktu niezawodności pilota. Masz wtedy większą pewność, że pilot innego standardu nie zakłóci Twojej transmisji.

Ale jeżeli chcesz zaryzykować, możesz oczywiście przyjąć, że czas ciszy przed pierwszym bitem startu (zbocze opadające) jest niewiele dłuższy niż czas jednego bitu.

Pamiętaj jednak, że czas ten powinien być:

RC-5: Zależność czasowa ciszy przed pierwszym bitem startu.

czyli:

Wniosek 6:
Algorytm odbiorczy powinien sprawdzać okres przerwy w sygnale przed pierwszym bitem startu przyjmując, że czas ten powinien być znacznie dłuższy od 1778µs, ale krótszy niż 114ms.





Odbiór poszczególnych bitów

W zasadzie wszystko w tej materii opisałem powyżej, więc tutaj wypunktuję, że:
  • pilnujemy każdego zbocza poprzez przerwanie,
  • odmierzamy czas od poprzedniego przerwania,
  • ustalamy kiedy jest połowa bitu,
  • w połowie bitu ustalamy jego wartość na podstawie kierunku zbocza sygnału,
  • zapisujemy wartość bitu w zmiennej.

W tym miejscu należy zauważyć pewien wyjątek, który będzie miał wpływ na algorytm dekodowania sygnału w standardzie RC-5. Przyglądnij się tym fragmentom wykresu, gdzie czas pomiędzy zboczami równy jest 1778µs:

RC-5: Przykład ramki pokazujący zależność końca trwania całego bitu.

Czy widzisz pewną zależność?

Wniosek 7:
Jeżeli czas pomiędzy aktualnym i poprzednim przerwaniem wynosi 1778µs, to jesteśmy aktualnie w połowie bitu.

Takiej zależności nie ma w przypadku półbitów.





Koniec odbioru

Tutaj nie ma większych problemów.

Aby zakończyć odbiór korzystamy z jednego z założeń standardu RC-5, które mówi, że długość ramki ma 14 bitów:
Wniosek 8:
Aby zakończyć odbiór ramki danych, powinniśmy zliczać bity i zakończyć odbiór niezależnie od tego, czy sygnał nadal występuje, czy też nie.

Czy w takim układzie nie powstaje problem, że odczytamy fragment sygnału, który faktycznie pochodzi z pilota o innym standardzie?

Teoretycznie taka możliwość istnieje. Jednakże zabezpiecza nas przed tym:
  • po części częstotliwość fali nośnej (różna dla różnych standardów),
  • różne czasy pomiędzy zboczami
Tutaj możesz zobaczyć porównanie różnych standardów: 

Zestawienie standardów transmisji podczerwieni.
Zestawienie parametrów różnych standardów
transmisji w podczerwieni.

Jest teoretycznie możliwy przypadek, w którym problem odczytania fragmentu sygnału pochodzącego z innego pilota wystąpi. Tym przypadkiem jest wykorzystanie standardu RC-5 do transmisji ramek dłuższych niż 14 bitów.

Nic nie stoi bowiem na przeszkodzie, by ktoś działający w zasięgu Twojego odbiornika opracował własny standard transmisji oparty o RC-5, ale zawierający np. 20 bitów. Niestety standard RC-5 nie zawiera żadnych zabezpieczeń w postaci np. bitu parzystości.

Aby zabezpieczyć się przed takim przypadkiem powinno się sprawdzać ciszę w sygnale po zakończeniu ostatniego 14-go bitu. Cisza ta powinna być znacznie dłuższa niż cały bit, czyli 1778µs. Niemniej jednak taki problem możesz spotkać co najwyżej na zawodach robotów :-)





Tolerancja

W niniejszym artykule zwracałem uwagę na fakt, iż zarówno nadajnik (pilot) jak i odbiornik nie są urządzeniami idealnymi. W szczególności dotyczy to pilotów, które często z powodu redukcji kosztów nie wykorzystują kwarców, co bezpośrednio wpływa na niedokładność odmierzania czasów w trakcie nadawania. Zmieniająca się temperatura, także ma istotny wpływ na końcowy efekt w postaci sygnału pilota. Podobnie w przypadku urządzeń odbiorczych, które także nie są idealne i podlegają tym samym problemom co pilot.

Co więcej pilot może na przykład skracać nieco czasy, a urządzenie odbiorcze oczekiwać dłuższych niż zgodne ze standardem. W ten sposób sumaryczny błąd się powiększa przez co łatwiej o błędy transmisji.

Jak temu zapobiec?

Należy wprowadzić parametr tolerancji, który będzie określał margines (+ i -) dla każdego bitu lub półbitu. W tak prosty sposób można uniknąć problemów z niepoprawnymi transmisjami.

Innymi słowy jeżeli przyjmiemy tolerancję np 250µs dla całego bitu, to nasz program dekodujący powinien rozpoznawać czas bitu (który standardowo trwa 1778µs) jako należący do przedziału:




a dla półbitu (standardowo 889µs):




Jeżeli tolerancja będzie zbyt duża, to czasy bitu i półbitu będą nachodzić na siebie, co uniemożliwi dekodowanie sygnału.

Dlatego program powinien wyłapać taką sytuację już na etapie kompilacji programu i zasygnalizować ją w postaci błędu lub co najmniej ostrzeżenia (warning).



Nie rozumiesz czegoś? Masz wątpliwości?

Nie wahaj się pytać za pomocą systemu komentarzy do artykułu.


Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

Podczerwień: RC-5 - Algorytm odbioru danych


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.

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ć.

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:
  1. pierwszy bit startu,
  2. drugi bit startu,
  3. pozostałe bity,
  4. wykrywanie ostatniego bitu i zapis danych do zmiennych globalnych,
  5. 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;
  }

}




Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

Podczerwień: RC-5 - Przykład odbioru danych na AVR ATmega8


Autor: Dondu

Artykuł jest częścią cyklu: Podczerwień - Transmisja danych


W poprzednich artykułach z tego cyklu poznałeś podstawy dot. transmisji w podczerwieni w standardzie RC-5 wraz z algorytmem odbioru za pomocą przerwań zewnętrznych i timera. Nadszedł czas na opracowanie przykładu implementacji urządzenia odbierającego transmisję danych w RC-5.

W niniejszym artykule przedstawiony jest prosty przykład odbierający komendy wydawane z pilota. Zadaniem przykładu jest pokazanie jedynie konkretnego rozwiązania umożliwiającego jego dalsze przerabianie i wykorzystanie w dowolny sposób.

Jako mikrokontroler wybrałem ATmega8 stosowany w większości artykułów dla początkujących


Schemat

Schemat jest prosty do granic możliwości zachowując najistotniejsze elementy zgodnie z:

Jako odbiornik podczerwieni użyłem odbiornika TFMS-5360 dedykowany dla 36kHz. Możesz oczywiście użyć inny dlatego na schemacie nie jest podany konkretny typ. Sprawdź dokładnie datasheet swojego odbiornika i prawidłowo podłącz zasilanie oraz pin wyjściowy.

Schemat układu testowego dla mikrokontrolera ATmega8
w obudowie DIP.


Diody LED posłużą nam do prostej sygnalizacji:

Dioda Pin Opis znaczenia
LED1 PB0 Sygnalizuje numer odebranej komendy (ilość błysków równa się numerowi komendy).
LED2 PD7 Stan bitu toggle. Dioda świeci, gdy bit toggle jest ustawiony, w przeciwnym wypadku nie świeci.
LED3 PD6 Włączana na początku funkcji przerwania i wyłączana na jego końcu. W ten sposób możemy obserwować słabiutkie błyskanie diody w trakcie, gdy mikrokontroler wykonuje kolejne przerwania dekodując sygnał z odbiornika podczerwieni.

Jeżeli użyjesz pilota niezgodnego ze standardem RC-5 będziesz mógł obserwować, że nasz program działa, ale odrzuca ramki przesyłane przez takiego pilota.


Wersję wykorzystującą interfejs RS-232 i terminal komputerowy do pokazywania odebranych danych znajdziesz w artykule: RC-5 - Testowanie pilota z terminelem RS-232





Program

Program oparty jest o algorytm opisany w artykule: RC-5 - Algorytm odbioru danych

Składa się z on trzech plików, gdzie dwa z nich stanowią bibliotekę (dd_rc5.h i dd_rc5.c), a trzeci (RC5_main.c) przykład pozwalający testować dekodowanie sygnału pilota według powyższego schematu odbiornika.

Programy jak zwykle są bardzo dokładnie komentowane, przez co nie powinieneś mieć problemów z ich zrozumieniem , przerabianiem i wykorzystaniem.


Do pobrania

Pobierz: RC5-Dekodowanie-przyklad-1.zip (kopia)

W pliku ZIP znajdują się:
  • kompletny projekt w AVR Studio 4.18
  • wszystkie opisane niżej pliki dd_rc5.c, dd_rc5.h oraz RC5_main.c,
  • a także plik RC5-ver4.hex skompilowany dla zegara 1MHz (domyślny w ATmega8), który można wgrać do mikrokontrolera.


Plik nagłówkowy dd_rc5.h

W pliku dd_rc5.h znajdziesz definicje, które należy dostosować do posiadanego mikrokontrolera, wybranego przerwania oraz ustawionego zegara F_CPU (preskaler i tolerancja).

Jeżeli wykorzystujesz LED testowy do sygnalizacji przerwań (LED3), to powinieneś ustawić niniejsze parametry w zależności, gdzie LED3 podłączasz:

/*=== LED PRZERWANIA ================================
LED testowy do pokazywania momentu rozpoczęcia i zakończenia przerwania,
co pozwala śledzić pracę programu, gdy odbiera dane w szczególności
z pilotów innych standardów niż RC-5. Szczegóły w linku podanym wyżej */
#define DD_RC5_LED_PRZ_DDR    DDRD
#define DD_RC5_LED_PRZ_PORT   PORTD
#define DD_RC5_LED_PRZ_PIN    PD6
//UWAGA!!!
//Aby wyłączyć diodę LED na stałe zakomentuj poniższą definicję */
#define _DD_RC5_WLACZ_DIODE_PRZERWAN_ 1

Jeżeli nie używasz LED3, to zakomentuj lub usuń definicję _DD_RC5_WLACZ_DIODE_PRZERWAN_.


Preskaler wraz z tolerancją decydują o poprawności dekodowania sygnału RC-5. O tolerancji pisałem na końcu artykułu: RC-5 - Dekodowanie sygnału

W zależności od wybranej częstotliwości zegara taktującego mikrokontroler (F_CPU), wybranego preskalera timera oraz żądanej tolerancji czasów, biblioteka dokonuje obliczenia wartości timera dla poszczególnych czasów, sygnału RC-5. W tym celu wykorzystujemy dwa parametry:

DD_RC5_TIMER_PRESKALER
Tutaj podaj wartość preskalera wybierając 8, 64, 256 lub 1024 */
#define DD_RC5_TIMER_PRESKALER    256

DD_RC5_TOLERANCJA_US
/*=== TOLERANCJA SYGNAŁU =================================
Parametry tolerancji (mikrosekundy) sprawdzania czasów odbieranego sygnału.
Jeżeli masz kłopoty z odbiorem w szczególności, gdy nie każda komenda jest
odczytywana możesz doświadczalnie regulować parametrem tolerancji
ustawiając własną wartość DD_RC5_TOLERANCJA_US. Im ten parametr jest
mniejszy tym mniejsza tolerancja na błędy.
*/
#define DD_RC5_TOLERANCJA_US 250  //Sugeruję próbować od 150 do 350
                                  //ale można także inne wartości
                                  //... tylko nie przesadzaj :-)


W zależności, do którego pinu INT podłączysz odbiornik RC-5, powinieneś odpowiednio zmodyfikować poniższe definicje:

/*=== PRZERWANIE =======================================
Wybierz przerwanie, które używasz oraz ustaw odpowiednie
DDR i symbol pinu */
#define DD_RC5_PRZERWANIE_INT       INT0
#define DD_RC5_PRZERWANIE_INT_PIN   PD2
#define DD_RC5_PRZERWANIE_INT_DDR   DDRD
#define DD_RC5_PRZERWANIE_INT_PORT  PORTD



Zabezpieczenia (warningi)

Ponieważ obliczone parametry timera mogą dla różnych wartości F_CPU, preskalera i tolerancji wykraczać poza możliwości timera, biblioteka ma wprowadzone zabezpieczenia w postaci warningów, które w procesie kompilacji informują o problemach i ich przyczynach.

Warningi zawierają także podpowiedzi, co należy w danym przypadku zrobić, by ustawić poprawne parametry.

Pełna wersja pliku dd_rc5.h:
/*

Biblioteka dekodowania sygnału RC-5 np. z pilota podczerwieni.
Wersja: 1.0

Biblioteka powstała do celów nauki sposobu dekodowania sygnału RC-5
i nie jest optymalizowana pod kątem objętości kodu.

Mikrokontrolery AVR.
Testowana na ATmega8 z kompilatorem AVR-GCC 4.3.3

Plik: dd_rc5.h

Data:   2013.07.30
Autor:  Dondu
www:  http://mikrokontrolery.blogspot.com/2011/03/RC5-IR-podczerwien-odbior-danych-przyklad-AVR-ATmega8.html
*/


#ifndef _DD_RC5_H_
#define _DD_RC5_H_ 1


/*=== STROJENIE ODBIORNIKA =============================
Jeżeli pilot nie odbiera prawidłowo danych z pilota RC-5 powinieneś
dobrać PRESKALER oraz TOLERANCJĘ - oba parametry znajdziesz poniżej.
Aby mieć pewność, że Twój układ działa i wykrywa sygnał RC-5 możesz
dodać diodę LED. Szczegóły w linku podanym wyżej lub opisach poniżej */


/*=== LED PRZERWANIA ================================
LED testowy do pokazywania momentu rozpoczęcia i zakończenia przerwania,
co pozwala śledzić pracę programu, gdy odbiera dane w szczególności
z pilotów innych standardów niż RC-5. Szczegóły w linku podanym wyżej */
#define DD_RC5_LED_PRZ_DDR    DDRD
#define DD_RC5_LED_PRZ_PORT   PORTD
#define DD_RC5_LED_PRZ_PIN    PD6
//UWAGA!!!
//Aby wyłączyć diodę LED na stałe zakomentuj poniższą definicję */
#define _DD_RC5_WLACZ_DIODE_PRZERWAN_ 1


/*=== PRESKALER timera ==================================

UWAGA!!!
Zmieniając preskaler jest prawdopodobne, że zmieniasz także częstotliwość
zegara mikrokontrolera za pomocą fusebitów. Jeżeli tak, to nie zapomnij
zmienić F_CPU w opcjach projektu!!!

Tutaj podaj wartość preskalera wybierając 8, 64, 256 lub 1024 */
#define DD_RC5_TIMER_PRESKALER    256


/*=== TOLERANCJA SYGNAŁU =================================
Parametry tolerancji (mikrosekundy) sprawdzania czasów odbieranego sygnału.
Jeżeli masz kłopoty z odbiorem w szczególności, gdy nie każda komenda jest
odczytywana możesz doświadczalnie regulować parametrem tolerancji
ustawiając własną wartość DD_RC5_TOLERANCJA_US. Im ten parametr jest
mniejszy tym mniejsza tolerancja na błędy.
*/
#define DD_RC5_TOLERANCJA_US 250  //Sugeruję próbować od 150 do 350
                                  //ale można także inne wartości
                                  //... tylko nie przesadzaj :-)


/*=== PRZERWANIE =======================================
Wybierz przerwanie, które używasz oraz ustaw odpowiednie
DDR i symbol pinu */
#define DD_PRZERWANIE_INT       INT0
#define DD_PRZERWANIE_INT_PIN   PD2
#define DD_PRZERWANIE_INT_DDR   DDRD
#define DD_PRZERWANIE_INT_PORT  PORTD






/*** Poniżej nic nie zmieniaj!!! ******************/


//--- DIODA LED SYGNALIZACJI PRZERWAŃ ------------------------------
#if _DD_RC5_WLACZ_DIODE_PRZERWAN_
 #define DD_RC5_LED_PRZ_ON DD_RC5_LED_PRZ_PORT |=  (1<<DD_RC5_LED_PRZ_PIN)
 #define DD_RC5_LED_PRZ_OFF  DD_RC5_LED_PRZ_PORT &= ~(1<<DD_RC5_LED_PRZ_PIN)
#endif



//--- Przerwanie ----------------------------------------

#define DD_RC5_WLACZ_DEKODOWANIE  GICR  |= (1<<DD_PRZERWANIE_INT)
#define DD_RC5_WYLACZ_DEKODOWANIE   GICR  &= ~(1<<DD_PRZERWANIE_INT)


//--- PRESKALER -----------------------------------------

//automatyczne dobranie parametrów do ustawienia w rejestrze TCCR0
#if   DD_RC5_TIMER_PRESKALER == 1
 #define DD_RC5_TIMER_PRESKALER_BITY ((1<<CS00))
#elif DD_RC5_TIMER_PRESKALER == 8
 #define DD_RC5_TIMER_PRESKALER_BITY ((1<<CS01))
#elif DD_RC5_TIMER_PRESKALER == 64
 #define DD_RC5_TIMER_PRESKALER_BITY ((1<<CS01)|(1<<CS00))
#elif DD_RC5_TIMER_PRESKALER == 256
 #define DD_RC5_TIMER_PRESKALER_BITY ((1<<CS02))
#elif DD_RC5_TIMER_PRESKALER == 1024
 #define DD_RC5_TIMER_PRESKALER_BITY ((1<<CS02)|(1<<CS00))
#endif



/* CZASY BITÓW I PÓŁBITÓW */


 //Czasy bitów i półbitów obliczone zgodnie z wybraną tolerancją
 
 #define DD_RC5_OKRES_BITU_MIN \
             ((F_CPU/1000UL) * (1778UL-DD_RC5_TOLERANCJA_US) / \
             (DD_RC5_TIMER_PRESKALER * 1000UL))
 
 #define DD_RC5_OKRES_BITU_MAX \
             ((F_CPU/1000UL) * (1778UL+DD_RC5_TOLERANCJA_US) / \
             (DD_RC5_TIMER_PRESKALER * 1000UL))

 #define DD_RC5_OKRES_POLOWY_BITU_MIN \
             (((F_CPU/1000UL) * ((1778UL-DD_RC5_TOLERANCJA_US)/2)/ \
             (DD_RC5_TIMER_PRESKALER * 1000UL)))

 #define DD_RC5_OKRES_POLOWY_BITU_MAX \
             (((F_CPU/1000UL) * ((1778UL+DD_RC5_TOLERANCJA_US)/2)  / \
             (DD_RC5_TIMER_PRESKALER * 1000UL)))


//Sprawdzamy, czy obliczony stan timera dla maksymalnego trwania bitu
//nie przekroczy możliwości timera (8-bit) ponieważ nie obsługujemy
//jego przepełnienia
#if (DD_RC5_OKRES_BITU_MAX > 255)
 # warning "DD_RC5: Preskaler timer0 jest zbyt maly w stosunku do czestotliwosci F_CPU lub ustawiles zbyt duza tolerancje"
 # warning "DD_RC5: Dekodowanie RC_5 nie bedzie dzialac prawidlowo."
 # warning "DD_RC5: Zwieksz preskaler timera i/lub parametr tolerancji."
 # warning "DD_RC5: Szczegoly opiasne sa w tutaj: http://mikrokontrolery.blogspot.com/2011/03/RC5-IR-podczerwien-odbior-danych-przyklad-AVR-ATmega8.html"
#endif

//sprawdzamy, czy obliczony stan timera dla połowy bitu będzie równy
//co najmniej 2. Lepiej nie działać na granicy możliwośći timera.
#if (DD_RC5_OKRES_POLOWY_BITU_MIN < 2)
 # warning "DD_RC5: Preskaler timer0 jest zbyt duzy w stosunku do czestotliwosci F_CPU"
 # warning "DD_RC5: Dekodowanie RC_5 nie bedzie dzialac prawidlowo."
 # warning "DD_RC5: Zmniejsz preskaler timera i/lub zwieksz czestotliwosc zegara mikrokontrolera."
 # warning "DD_RC5: Szczegoly opiasne sa w tutaj: http://mikrokontrolery.blogspot.com/2011/03/RC5-IR-podczerwien-odbior-danych-przyklad-AVR-ATmega8.html"
#endif


//Sprawdzamy, czy obliczone wartości timera dla minimalnego czasu trwania
//całego bitu nie zazębiają się z czasem maksymalnego trwania półbitu
#if (DD_RC5_OKRES_BITU_MIN <= DD_RC5_OKRES_POLOWY_BITU_MAX)
 # warning "DD_RC5: Czas minimalnego trwania calego bitu, zachodzi na maksymalny czas trwania polowy bitu."
 # warning "DD_RC5: Zmniejsz wartosc parametru DD_RC5_TOLERANCJA_US_PROC."
 # warning "DD_RC5: Szczegoly opiasne sa w tutaj: http://mikrokontrolery.blogspot.com/2011/03/RC5-IR-podczerwien-odbior-danych-przyklad-AVR-ATmega8.html"
#endif


//Sprawdzamy, czy wybrany poziom tolerancji jest wystarczający
//do poprawnego dekodowania sygnału
#if (DD_RC5_TOLERANCJA_US < 150)
 # warning "DD_RC5: Wybrany poziom tolerancji jest maly przez co jest spore prawdopodobienstwo, ze transmisja nie bedzie dzialac."
 # warning "DD_RC5: Zwieksz wartosc parametru DD_RC5_TOLERANCJA_US."
 # warning "DD_RC5: Mozesz zingnorowac ten warning bedac swiadomym skutkow."
 # warning "DD_RC5: Szczegoly opiasne sa w tutaj: http://mikrokontrolery.blogspot.com/2011/03/RC5-IR-podczerwien-odbior-danych-przyklad-AVR-ATmega8.html"
#endif



/*--- DEKLARACJE FUNKCJI ---------------------------------------------*/
void dd_rc5_ini(void);



/*--- DEKLARACJE ZMIENNYCH GLOBALNYCH --------------------------------*/

//Zmienna: dd_rc5_dane_odebrane
extern volatile unsigned char dd_rc5_dane_odebrane; //dane odebrane z pilota

//Zmienna: dd_rc5_status
//zawiera w sobie:
// bity 0-4 - adres urządzenia
// bit  5 - stan bitu toggle
// bit  6 - nie wykorzystany
// bit  7 - flaga poprawnego odebrania komendy z pilota i gotowości
//        do odczytu ze zmiennej dd_rc5_komenda.
//        UWAGA!
//        Po dokonaniu odczytu odebranej danej i ewentualnie statusu,
//        należy wyzerować ten bit (bit 7), aby funkcja odbierająca
//        rozpoczęła nasłuchiwanie nowych danych z pilota.
//        Jeżeli nie wyzerujesz bitu 7, nowe dane nie będą odbierane.
//        Zamiast zerować tylko bit 7, możesz wyzerować całą
//        zmienną dd_rc5_status
extern volatile unsigned char dd_rc5_status;
//maska bitu 7 rejestru statusu dd_rc5_status
#define DD_RC5_STATUS_DANE_GOTOWE_DO_ODCZYTU 0x80 //bit 7


#endif /* _DD_RC5_H_ */



Plik główny dd_rc5.c

Główny plik biblioteki zawiera funkcję inicjującą dd_rc5_ini() oraz funkcję przerwania ISR(wektor).

W pliku znajdują się także funkcje inline, które mają zwiększyć czytelność kodu oraz ułatwić jego ewentualne modyfikacje.


Pełna wersja pliku dd_rc5.c:
/*
Biblioteka dekodowania sygnału RC-5 np. z pilota podczerwieni.
Wersja: 1.0

Biblioteka powstała do celów nauki sposobu dekodowania sygnału RC-5
i nie jest optymalizowana pod kątem objętości kodu.

Mikrokontrolery AVR.
Testowana na ATmega8 z kompilatorem AVR-GCC 4.3.3

Plik: dd_rc5.c

Data:   2013.07.30
Autor:  Dondu
www:  http://mikrokontrolery.blogspot.com/2011/03/RC5-IR-podczerwien-odbior-danych-przyklad-AVR-ATmega8.html
*/

#include <avr/io.h>
#include <avr/interrupt.h>

#include "dd_rc5.h"

volatile unsigned char dd_rc5_dane_odebrane;
volatile unsigned char dd_rc5_status;


//-----------------------------------------------------------------------------


void dd_rc5_ini(void)
{

  //Funkcja inicjująca Timer oraz przerwania  oraz LED testowy

  //ustaw pin diody LED sygnalizującej przerwania
  #ifdef _DD_RC5_WLACZ_DIODE_PRZERWAN_
    DD_RC5_LED_PRZ_DDR   |= (1<<DD_RC5_LED_PRZ_PIN);
    DD_RC5_LED_PRZ_PORT  &=  ~(1<<DD_RC5_LED_PRZ_PIN);
  #endif


  //Ustaw pin przerwania jako wejście bez włączonego rezystora pull-up
  DD_RC5_PRZERWANIE_INT_DDR  &= ~(1<<DD_RC5_PRZERWANIE_INT_PIN);
  DD_RC5_PRZERWANIE_INT_PORT &= ~(1<<DD_RC5_PRZERWANIE_INT_PIN);


  //ustaw wykrywanie zbocza opadającego na pinie INT0 (PD2)
  //zgodnie ze standardem RC5 zbocze opadające będzie pierwszym zboczem
  //pierwszego bitu startu
  MCUCR |= (1<<ISC01);

  //Włącz przerwania z pinu INT0
  DD_RC5_WLACZ_DEKODOWANIE;

  //Ustawienia timera
  TCCR0 = DD_RC5_TIMER_PRESKALER_BITY;

  //Jeżeli wcześniej używałeś tego timer0, to odkomentuj poniższe linie
  //SFIOR |= (1<<PSR10);  //resetuj preskaler timer0
  //TCNT0 = 0x00;     //zeruj timer0
  //TIFR |= (1<<TOV0);  //zgaś flagę przerwania timer0
}


//-----------------------------------------------------------------------------

//funkcja zerująca timer oraz flagę jego przepełnienia
inline void dd_rc5_zeruj_timer_i_flage_przepelnienia(void)
{
  SFIOR |= (1<<PSR10);  //resetuj preskaler timera
  TCNT0 = 0x00;       //zeruj timer
  TIFR |= (1<<TOV0);    //zgaś flagę przepełnienia timera
}

//-----------------------------------------------------------------------------

inline void dd_rc5_ustaw_zbocze_opadajace(void)
{
  MCUCR &= ~(1<<ISC00); //ustaw wykrywanie zbocza opadającego
}

//-----------------------------------------------------------------------------

inline void dd_rc5_ustaw_zbocze_narastajace(void)
{
  MCUCR |= (1<<ISC00);  //ustaw wykrywanie zbocza narastającego
}

//-----------------------------------------------------------------------------

inline void dd_rc5_zmien_zbocze_na_przeciwne(void)
{
  //ustaw wykrywanie zbocza przeciwnego do aktualnie ustawionego
  MCUCR ^= (1<<ISC00);
}


//-----------------------------------------------------------------------------

ISR(DD_RC5_PRZERWANIE_WEKTOR)
{
  //Obsługa przerwania wykrytego zbocza sygnału z czujnika podczerwieni

  //W zależności od ustawień w danym momencie rejestru MCUCR
  //przerwanie wykonywane jest przy wykryciu narastającego lub opadającego
  //zbocza sygnału z czujnika podczerwieni (bity ISC00 i ISC01)
  //Do wykrycia kierunku zbocza wystarczy bit ISC00.

  //UWAGA dot. flagi TOV0
  //W poniższych porównaniach czasu trwania impulsu sprawdzamy,
  //także flagę przepełnienia timera (flaga TOV0), ponieważ samo
  //porównanie wartości timera jest niewystarczające,
  //gdyż mógł on zostać przepełniony.

  //zmienne pomocnicze statyczne dostępne tylko w tej funkcji
  static unsigned char  dd_rc5_numer_bitu = 1;
  static unsigned char  dd_rc5_polbit_licznik = 0;
  static unsigned int   dd_rc5_dane_temp = 0;

  //włącz diodę sygnalizującą przerwanie
  #ifdef _DD_RC5_WLACZ_DIODE_PRZERWAN_
    DD_RC5_LED_PRZ_ON;
  #endif



  //Jeżeli program główny lub inny fragment programu, nie odczytał
  //odebranch wcześniej danych, to wychodzimy z przerwania
  //i nie dekodujemy właśnie nadawanej przez pilota ramki.
  //Jest to zabezpieczenie, przed nadpisaniem poprzednio odebranych,
  //a jeszcze nie użytych danych z pilota.
  if(!(dd_rc5_status & DD_RC5_STATUS_DANE_GOTOWE_DO_ODCZYTU)) {


    //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.
    //Utrata wiedzy o wystąpieniu przerwania mogłaby spowodować błędne
    //dekodowanie sygnału.
    //--- UWAGA!!! ---
    //Ponieważ w dalszej części programu musimy znać kierunek zbocza, które
    //wywołało przerwanie, stąd możesz odnieść mylne wrażenie, że warunki
    //if() oraz komentarze ich dotyczące, nie są spójne. Gdy będziesz je
    //analizował pamiętaj, że już tutaj  zmieniamy zbocze na przeciwne!!!
    dd_rc5_zmien_zbocze_na_przeciwne();


    //W zależności, który bit jest aktualnie dekodowany
    switch(dd_rc5_numer_bitu) {

      //--- bit startowy nr 1 ------------------------------------

    case 1:

      //Wykryto pierwsze zbocze opadające.
      //Sprawdzamy, czy przerwa w sygnale była wystarczająco długa,
      //by stwierdzić, że to pierwsze zbocze opadające pierwszego
      //bitu startu. Tylko w takim przypadku zaczniemy dekodować sygnał.
      //Jeżeli przerwa była zbyt krótka, należy zaczekać
      //na następną ramkę danych.
      //
      //UWAGA!! Tutaj stosujemy prostą zasadę dla warunku czasu cziszy
      //przed pierwszym bitem startu polegającą na przyjęciu, że czas
      //ciszy musi być dłuższy niż czas przepełnienia timera. Jest to
      //świadome uproszczenie algorytmu - szczegóły w linku.
      if(
        (TIFR & (1<<TOV0))  //jeżeli wystąpiło przepełnienie
        //można także inaczej:
        //lub licznik odliczył czas wymaganego minimum
        //przyjmujemy czas równy 1.5 bitu
        //|| (TCNT0 > (DD_RC5_OKRES_BITU_MAX + DD_RC5_OKRES_POLOWY_BITU_MAX))
        //|| (TCNT0 > 250)
        //albo napisz własny inny warunek
      ) {


        //Przerwa między kolejnymi ramkami była wystarczająca,
        //by mieć pewność, że rozpoczynamy odbiór nowej ramki.

        dd_rc5_zeruj_timer_i_flage_przepelnienia();

        //następny bit będzie bitem nr 2
        dd_rc5_numer_bitu=2;

        //wyzeruj pomocniczą zmienną odebranych danych
        dd_rc5_dane_temp = 0;
        dd_rc5_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ż bit ma numer 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
      //oraz flagę przepełnienia zgodnie z uwagą na początku funkcji
      if(
        (TIFR & (1<<TOV0)) //jeżeli wystąpiło przepełnienie timera
        || (TCNT0 < DD_RC5_OKRES_POLOWY_BITU_MIN) //lub półbit za krótki
        || (TCNT0 > DD_RC5_OKRES_POLOWY_BITU_MAX) //lub półbit za długi
      ) {

        //Wykryto błąd w sygnale ponieważ czas jest inny niż
        //dopuszczalny. Przerywamy dekodowanie tej ramki
        //sygnału i rozpoczynamy od nowej ramki
        dd_rc5_numer_bitu = 1;

      } else {

        //Czas jest z zakresu półbitów

        //Które zbocze wywołało przerwanie?
        if(MCUCR & (1<<ISC00)) {

          //Przerwanie wywołało zbocze opadające bitu startowego nr 2
          //co oznacza, że drugi bit startu odebrany prawidłowo

          dd_rc5_zeruj_timer_i_flage_przepelnienia();

          //zwiększ licznik bitów
          dd_rc5_numer_bitu++;


        } else {

          //wykryto pierwsze zbocze narastające bitu startowego nr 2

          dd_rc5_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(
        (TIFR & (1<<TOV0)) //przepełnienie timera to błąd
        || (TCNT0 < DD_RC5_OKRES_POLOWY_BITU_MIN)
        || (TCNT0 > DD_RC5_OKRES_BITU_MAX)

      ) {

        //Wykryto błąd w sygnale ponieważ czas nie mieści się
        //w założonych progach bitu ani półbitu. Przerywamy więc
        //dekodowanie tej ramki sygnału i rozpoczynamy
        //oczekiwanie na początek kolejnej ramki

        //przygotuj się do odbioru nowej ramki
        dd_rc5_numer_bitu = 1;
        break;  //break, by nie sprawdzał poniższego warunku





        //Czy minął czas półbitu?
      } else if(
        (TCNT0 <= DD_RC5_OKRES_POLOWY_BITU_MAX)
        //warunku MIN nie musimy sprawdzać, ponieważ
        //został już sprawdzony na początku niniejszego if()
        //&& (TCNT0 >= DD_RC5_OKRES_POLOWY_BITU_MIN)

      ) {

        //Upłynął czas równy połowie bitu

        //sprawdzamy, czy to druga połowa bitu
        if(dd_rc5_polbit_licznik) {

          //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
          dd_rc5_dane_temp <<= 1;

          //Jeżeli aktualnie ustawione jest zbocze narastające
          //to znaczy, że przerwanie zostało wywołane przez zbocze
          //opadające, a to oznacza, że odebraliśmy jedynkę logiczną,
          //którą należy dodać na najmniej znaczącej pozycji rejestru
          //odbiorczego. Czytaj uwagę na początku tej funkcji.
          if(MCUCR & (1<<ISC00)) {
            dd_rc5_dane_temp |= 1;
          }

          //wyzeruj licznik półbitów
          dd_rc5_polbit_licznik = 0;

          //zwiększ licznik bitów
          dd_rc5_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
          dd_rc5_polbit_licznik = 1;

        }

        dd_rc5_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
        dd_rc5_dane_temp <<= 1;

        //Jeżeli aktualnie ustawione jest zbocze narastające
        //to znaczy, że przerwanie zostało wywołane przez zbocze
        //opadające, a to oznacza, że odebraliśmy jedynkę logiczną,
        //którą należy dodać na najmniej znaczącej pozycji rejestru
        //odbiorczego. Czytaj uwagę na początku tej funkcji.
        if(MCUCR & (1<<ISC00)) {
          dd_rc5_dane_temp |= 1;
        }

        dd_rc5_zeruj_timer_i_flage_przepelnienia();

        //zwiększ licznik bitów
        dd_rc5_numer_bitu++;

      }


      //czy to już ostatni bit?
      if(dd_rc5_numer_bitu > 14) {

        //Tak, odebrano ostatni bit

        //Zapisz dane do zmiennych globalnych

        //1-Dane są na sześciu najmłodszych bitach
        dd_rc5_dane_odebrane = dd_rc5_dane_temp & 0b111111;

        //2-Adres urządzenia i bit toggle
        //przesuń w prawo o 6 pozycji pozbywając się bitów danych
        dd_rc5_dane_temp >>= 6;
        //przepisz bity do rejestru statusu i dodaj bit gotowości
        //danych do odczytu
        dd_rc5_status = (dd_rc5_dane_temp & 0b111111)
                        | DD_RC5_STATUS_DANE_GOTOWE_DO_ODCZYTU;

        //przygotuj się do odbioru nowej ramki
        dd_rc5_numer_bitu = 1;

        //Wyłączamy dekodowanie (blokada przerwania INT), do momentu
        //gdy program główny ponownie go włączy
        DD_RC5_WYLACZ_DEKODOWANIE;

        //koniec odbioru ramki ... uff nareszcie :-)
      }
      break;
    }
  }


  //Jeżeli na końcu funkcji przerwania numer bitu ma wartość 1
  //to oznacza, że zarządano przerwania dekodowania z powodu błędu
  if(dd_rc5_numer_bitu==1) {
    //ustawiamy stan początkowy
    dd_rc5_zeruj_timer_i_flage_przepelnienia();
    dd_rc5_ustaw_zbocze_opadajace();
    dd_rc5_polbit_licznik = 0;
  }


  //wyłącz diodę sygnalizującą przerwanie
  #ifdef _DD_RC5_WLACZ_DIODE_PRZERWAN_
    DD_RC5_LED_PRZ_OFF;
  #endif
}


Plik testowy RC5_main.c

Plik zawiera program testujący bibliotekę. Działanie programu polega na:

  1. przygotowaniu mikrokontrolera do dekodowania sygnału RC-5,
  2. oczekiwanie w uśpieniu do trybu IDLE, na rozpoczęcie transmisji,
  3. po poprawnym odebraniu jednej ramki danych wyświetlenie stanu bitu toggle za pomocą diody LED2 oraz błyśnięcie diodą LED1 tyle razy ile wynosi kod odebranej komendy.


Plik ten możesz dowolnie modyfikować, ustawiając na przykład dodatkowy warunek sprawdzania numeru urządzenia, który znajduje się w zmiennej dd_rc5_status. Szczegóły w artykule: RC-5 - Opis standardu

Pełna wersja pliku RC5_main.c:
/*
Test biblioteki RC-5 na ATmega8

Schemat testowy:
 - trzy diody LED anodami do pinów PB0, PD6 i PD7, a katodami przez rezystory
   330Ohm do GND (masa).
 - wyjście czujnika podczerwieni do pinu INT0.

Plik:   RC5_main.c
Kompilator: AVR-GCC 4.3.3

Data:   2013.07.30
Autor:  Dondu
www:  http://mikrokontrolery.blogspot.com/2011/03/RC5-IR-podczerwien-odbior-danych-przyklad-AVR-ATmega8.html

Schemat testowy dostępny w linku powyżej.
*/


#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/sleep.h>

/* Załącz bibliotekę RC-5*/
#include "dd_rc5.h"


/* LED testowy do pokazywania odebranych danych */
#define LED_PORT  PORTB
#define LED_DDR   DDRB
#define LED_PIN   PB0
#define LED_INI   LED_DDR  |=  (1<<LED_PIN)
#define LED_ON    LED_PORT |=  (1<<LED_PIN)
#define LED_OFF   LED_PORT &= ~(1<<LED_PIN)


/* LED testowy do pokazywania stanu bitu toggle */
#define LED_TOGGLE_BIT_PORT PORTD
#define LED_TOGGLE_BIT_DDR  DDRD
#define LED_TOGGLE_BIT_PIN  PD7
#define LED_TOGGLE_BIT_INI  LED_TOGGLE_BIT_DDR  |=  (1<<LED_TOGGLE_BIT_PIN)
#define LED_TOGGLE_BIT_ON LED_TOGGLE_BIT_PORT |=  (1<<LED_TOGGLE_BIT_PIN)
#define LED_TOGGLE_BIT_OFF  LED_TOGGLE_BIT_PORT &= ~(1<<LED_TOGGLE_BIT_PIN)


/* Zmienne globalne z odebranymi danymi */
volatile unsigned char dd_rc5_dane_odebrane = 0;
volatile unsigned char dd_rc5_status = 0;


//-----------------------------------------------------------------------------

void test(void)
{

  //Funkcja testowa pokazująca na diodach LED odbierane dane z pilota RC5.
  //Funkcję tę możesz dowolnie skomplikować lub usunąć.

  unsigned char f;  //zmienna pomocnicza

  //sprawdzamy bit toggle zapalając przypisaną mu diodę
  //gdy bit ten jest jedynką i gasząc, gdy jest zerem
  if(dd_rc5_status & 0b100000) {
    LED_TOGGLE_BIT_ON;
  } else {
    LED_TOGGLE_BIT_OFF;
  }

  //opóźnienie w celu lepszej widoczności zmian diod LED
  _delay_ms(300);

  //dd_rc5_dane_odebrane = dd_rc5_status & 0b11111;

  //mrugnij diodą danych tyle razy jaki kod klawisza odebrano
  for(f=0; f<dd_rc5_dane_odebrane; f++) {
    LED_ON;
    _delay_ms(50);
    LED_OFF;
    _delay_ms(300);
  }

  //Ponieważ odczytaliśmy odebrane dane zgaś flagę gotowości
  //danych do odczytu, by umożliwić odebranie następnych danych
  dd_rc5_status &= ~DD_RC5_STATUS_DANE_GOTOWE_DO_ODCZYTU;
  DD_RC5_WLACZ_DEKODOWANIE;

}


//-----------------------------------------------------------------------------

int main(void)
{

  //inicjuj LED-y testowe
  LED_INI;
  LED_TOGGLE_BIT_INI;


  //inicjuj timer oraz pin przerwania zewnętrznego INTx
  dd_rc5_ini();

  //włącz możliwość usypiania do trybu IDLE
  sleep_enable();
  set_sleep_mode(SLEEP_MODE_IDLE);

  //włącz przerwania globalne
  sei();

  //pętla główna
  while(1) {

    //tutaj możesz dodać swój kod

    //sprawdź, czy dane są gotowe do odczytu
    if(dd_rc5_status & DD_RC5_STATUS_DANE_GOTOWE_DO_ODCZYTU) {
      //dane czekają na odczyt
      test();
    }

    sleep_cpu(); //śpij koteczku :-)
  }
}

Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

Działy
Działy dodatkowe
Inne
O blogu




Dzisiaj
--> za darmo!!! <--
1. USBasp
2. microBOARD M8


Napisz artykuł
--> i wygraj nagrodę. <--


Co nowego na blogu?
Śledź naszego Facebook-a



Co nowego na blogu?
Śledź nas na Google+

/* 20140911 Wyłączona prawa kolumna */
  • 00

    dni

  • 00

    godzin

  • :
  • 00

    minut

  • :
  • 00

    sekund

Nie czekaj do ostatniego dnia!
Jakość opisu projektu także jest istotna (pkt 9.2 regulaminu).

Sponsorzy:

Zapamiętaj ten artykuł w moim prywatnym spisie treści.