Uwaga! Ta strona wysyła Ci "ciasteczko".

Artykuł pochodzi ze strony XYZ HOBBY ROBOT (abxyz.bplaced.net)

Kurs AVR-GCC Dekoder protokołu RC5

3.06.11 ABXYZ

Pilot na podczerwień to prosty i tani sposób na sterowanie bezprzewodowe urządzeniami z bliskiej odległości. W tym artykule opisałem jak wykonać odbiornik współpracujący ze standardowym pilotem RC5. Funkcję dekodera protokołu RC5 będzie oczywiście pełnił mikrokontroler atmega - napiszemy taki program w języku C. Odbiornik można wykorzystać do budowy różnych urządzeń sterowanych pilotem.

Scalony odbiornik podczerwieni SFH5110-36

Obok pilota RC5 i mikrokontrolera AVR ATMEGA będzie potrzebny scalony odbiornik podczerwieni SFH5110-36, układ ten jest łatwo dostępny i kosztuje ok 3zł.

obrazek
Fot. 1. SFH5110-36 Scalony odbiornik/demodulator podczerwieni
obrazek
Rys.1. Podłączenie układu SFH5110-36

Rysunek poniżej wyjaśnia sposób działania układu SFH5110-36. Odbiornik SFH5110-36 posiada trzy wyprowadzenia: 1-wyjście cyfrowe, 2-masa, 3-zasilanie(5V) . Normalnie na wyjściu odbiornika SFH5110-36 jest wysoki stan napięcia, gdy odbiornik zostanie oświetlony pilotem, na wyjściu pojawi się stan niski. Pilot świeci diodami IR impulsowo, z częstotliwością ok 36kHz, gdyż odbiornik SFH5110-36 reaguje tylko na oświetlenie impulsowe o częstotliwości 36kHz, eliminuje to wpływ zakłóceń z innych źródeł światła.

obrazek
Rys. 2. Zasada działania i sposób użycia odbiornika podczerwieni SFH5110-36.

Scalony odbiornik podczerwieni podobny do SFH5110-36 można wyciągnąć ze zużytego TV lub innego sprzętu. Przed wylutowaniem odbiornika należy koniecznie rozpoznać na płycie do których wyprowadzeń układu przyłączone jest zasilanie, a które jest wyjściem danych.

Pilot RC5

Chociaż w każdym porządnym domu znajdzie się kilka pilotów na podczerwień, to jest całkiem prawdopodobne, że żaden nie działa zgodnie z protokołem Philips RC5. Ale to żaden problem. Nowiutkiego pilota RC5 kupisz na allegro w cenie ok 5..7zł. Ja posłużę tu się pilotem od starego telewizora, fotka poniżej, pilot ten ma w środku układ scalony SAA3010P.

obrazek
Fot. 2. Klasyczny pilot, pozostał mi po wymianie starego TV, w środku ma układ scalony SAA3010P

Protokół zdalnego sterowania RC5

Wciśnięcie przycisku w pilocie RC5 skutkuje wysłaniem ramki danych, która składa się z czternastu bitów danych. Najpierw idą dwa bity startowe, oba zawsze mają wartości 1. Następny jest bit kontrolny toggle, bit ten zmienia swoją wartość na przeciwną, za każdym razem, kiedy naciśnięty jest któryś przycisk w pilocie. W przypadku, gdy któryś przycisk zostanie przytrzymany dłużej, pilot będzie wysyłał identyczne ramki danych(bez zmiany bitu toggle) - aż do chili zwolnienia przycisku. Kolejne pięć bitów sys5..sys0 to adres systemu (urządzenia), dla TV adres ma wartość 0. Pozostałe sześć bitów cmd4..cmd0, zawierają numer przycisku.

obrazek
Rys.3. Ramka danych protokołu sterowania RC5 składa się z 14 bitów.

W protokole RC5 wykorzystuje się kodowanie bitów Manchaster. Bit o wartości 1 kodowany jest jako przejście poziomu sygnału z niskiego na wysoki, a bit o wartości 0, jako przejście z poziomu wysokiego na niski. W obu przypadkach zmiana poziomu sygnału następuje w połowie czasu trwania bitu. Czas trwania każdego bitu wynosi ok 1.8 milisekundy.

obrazek
Rys.4. Kodowanie bitów Manchester

Pilot RC5 przesyła kolejne ramki danych w odstępie czasu nie mniejszym niż okres trwania 50 bitów.

obrazek
Rys.5. Odstęp pomiędzy kolejnymi ramkami danych równa się czasowi trwania 50 bitów.

Przykładowy program dekodera RC5

Program będzie dekodował komendy pilota RC5 i prezentował wynik na siedmiu diodach LED. Sześć diod pokaże numer przycisku, a siódma - toggle bit. Toggle bit zmienia swój stan na przeciwny przy każdym wciśnięciu przycisku. Siedem diod LED przyłączyłem do portu D AVRa, a odbiornik podczerwieni SFH5110-36 do PB0.

W programie wykorzystano przerwanie wywoływane przepełnieniem timera0 - "Timer0 Overflow". Timer0 skonfigurowany jest tak, że przerwania występują co 32 mikrosekundy. Z każdym wystąpieniem przerwania inkrementowana jest globalna zmienna "timerL", a co 256 wystąpień przerwania, inkrementowana jest globalna zmienna "timerH". Zmienne "timerL" i "timerH" tworzą zegary, które użyjemy w programie do mierzenia czasu trwania impulsów i do wymierzania odcinków czasu.

Wykrywaniem i dekodowaniem komend rc5 zajmuje się funkcja "detect". Funkcja "detect" uruchamiana jest w głównej pętli programu, zwraca 12-bitowy kod komendy RC5, albo wartość -1, jeśli w okresie 131ms nie wykryje komendy lub wystąpi błąd.

Na początku funkcji "detect" program odczytuje w pętli stań wejścia uC, do którego podłączono odbiornik podczerwieni SFH5110-36, oczekując okresu ciszy trwającego co najmniej 3.5ms - cisza to stan wysoki napięcia na wyjściu SFH5110-36. Przypominam, że normalnie na wyjściu odbiornika SFH5110-36 jest wysoki stan napięcia, a gdy odbiornik oświetlany jest pilotem, to na wyjściu pojawia się stan niski. Jeśli w ciągu 131ms nie wystąpił okres ciszy trwający 3.5ms, to funkcja kończy działanie zwracając kod -1, oznaczający brak komendy. Następnie program oczekuje opadającego zbocza sygnału w połowie pierwszego bitu startowego

Po wykryciu bitu startowego mierzony jest czas trwania niskiego poziomu sygnału. Jeśli nie wykryto pierwszego bitu startowego w okresie 131ms, albo mierzony czas trwania poziomu niskie sygnału w pierwszym bicie startowym okazał się dłuższy niż 1.1ms, to funkcja kończy działanie zwracając kod oznaczający brak komendy. Dalej program oczekuje opadającego zbocza w środku drugiego bitu startowego. Funkcja wykorzystuje opadające lub rosnące zbocze sygnału w połowie każdego bitu do synchronizacji. Odczyt wartości kolejnego bitu ramki dokonuje się po okresie 3/4 czasu trwania bitu od momentu wykrycia zbocza w środku poprzedniego bitu. Odczytana wartość bitu zachowywana jest w zmiennej. Dalej, jeśli odczytano wartość bitu 1, to program oczekuje wystąpienia zbocza opadającego w środku bitu, a jeśli odczytano wartość bitu 0, zbocza narastającego. W momencie wykrycia krawędzi w środku bitu następuje wyzerowanie timera i cała akcja jest powtarzana dla kolejnego bitu. Jeśli program nie wykryje odpowiedniego zbocza w okresie 5/4 czasu trwania bitu od momentu poprzedniej synchronizacji, to funkcja kończy działanie z błędem.

obrazek
Rys. 6. Próbkujemy po 3/4 czasu trwania bitu od momentu wykrycia zbocza sygnału w połowie poprzedniego bitu.

A oto nasz programik, całość w jednym pliku.

//---------------------------------------------------------------
//   Plik "main.c"
//
//   KURS AVR-GCC (abxyz.bplaced.net)
// 
//   Dekoder  RC5
// 
//   (schemat i opis działania w artykule)
//   testowanie na atmega8 (8MHz)
//---------------------------------------------------------------

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

// Odbiornik podczerwieni SFH5110  przyłączona do portu  PB0 
#define RC5_IN   (PINB & (1<<0))

//
typedef unsigned char u8;
typedef unsigned int  uint;

// Zmienne globalne pełnią rolę  programowych zegarów
// napędzanych przerwaniem TIMER0_OVF
volatile u8 timerL; 
volatile u8 timerH; 

//---------------------------------------------------------------
// Funkcja konfiguruje i uruchamia Timer0 
// oraz włącza przerwanie od przepełnienia timera,
// przerwanie powinno występować co 32us.  
//---------------------------------------------------------------
void init_rc5()
{
  //atmega8
  TCCR0 = (1<<CS00);  // włącza Timer0  
  TIMSK = (1<<TOIE0); // włącza przerwanie "Timer0 Overflow"

/*
  //atmega88
  TCCR0B = (1<<CS00);
  TIMSK0 = (1<<TOIE0);
*/
  // Zezwala na przerwania 
  sei();
}

//---------------------------------------------------------------
// Procedura obsługi przerwania  Timer0 Overflow"
//---------------------------------------------------------------
ISR(TIMER0_OVF_vect)
{
   volatile  static u8 inttemp;

   // zmienna timerL zwiększa się co 32us
   timerL++;

   // zmienna timerH  zwiększa się co 8.192ms (32us*256) 
   inttemp++;
   if(!inttemp ) timerH++;
}

//---------------------------------------------------------------
// Funkcja wykrywa i dekoduje  komendę pilota RC5                                             
//---------------------------------------------------------------
 uint detect()
 {
    u8 temp;
    u8 ref1;
    u8 ref2;
    u8 bitcnt;
    uint command;

    timerH  = 0;
    timerL  = 0;

    // Czeka na okres ciszy na linii wejścia uC trwający  3.5ms
    // Jeśli nie wykryje takiego okresu ciszy w ciągu 131ms,
    // to kończy działanie funkcji z błędem
    while( timerL<110)
    {
       if(timerH>=16)  return  command = -1;

       if(!RC5_IN) timerL = 0;
    }

    // Czeka na  pierwszy bit startowy. 
    // Jeśli nie wykryje bitu startowego w ciągu 131ms,
    // to kończy działanie funkcji z błędem
    while(RC5_IN)  
         if(timerH>=16)  return command = -1 ;


    // Pomiar czasu trwani niskiego poziom syganłu 
    // w pierwszym bicie startowym.
    // Jeśli nie wykryje rosnącego zbocza sygnału w ciągu  
    // 1ms, to kończy działanie funkcji z błędem 
    timerL = 0;
    while(!RC5_IN)
         if(timerL>34) return command = -1;

    //
    temp = timerL;
    timerL = 0;

    // ref1 - oblicza  3/4 czasu trwania bitu
    ref1 =temp+(temp>>1);

    // ref2 - oblicza 5/4 czasu trwania bitu
    ref2 =(temp<<1)+(temp>>1);

 
    // Oczekuje na zbocze opadające sygnału w środku drugiego
    // bitu startowego.
    // Jeśli nie wykryje zbocza w ciągu 3/4 czasu trwania 
    // bitu, to kończy działanie funkcji z błędem 
    while(RC5_IN)
         if(timerL > ref1) return command = -1;

    // W momencie wykrycia zbocza sygnału, synchronizuje
    // zmieną timerL dla próbkowania  bitu toggle
    timerL = 0;

    // Odczytuje dekoduje pozostałe 12 bitów polecenia rc5
    for(bitcnt=0, command = 0; bitcnt <12; bitcnt++)
    {
       // Czeka 3/4 czasu trwania bitu od momentu wykrycia
       // zbocza sygnału w połowie poprzedniego bitu 
       while(timerL < ref1) {};
 
       // Próbkuje - odczytuje port we  uC
       if(!RC5_IN)
       {
          // Jeśli odczytano 0, zapamiętuje w zmiennej 
          // "command" bit o wartości 0         
          command <<= 1 ;

          // Oczekuje na zbocze rosnące sygnału w środku bitu.
          // Jeśli nie wykryje zbocza w ciągu 5/4 czasu trwania 
          // bitu, to kończy działanie funkcji z błędem    
          while(!RC5_IN)
             if(timerL > ref2) return command = -1;
       }
       else
       {
          // Jeśli odczytano 1, zapamiętuje w zmiennej 
          // "command" bit o wartości 1  
          command = (command <<1 ) | 0x01;

          // Oczekuje na zbocze opadające sygnału w środku bitu.
          // Jeśli nie wykryje zbocza w ciągu 5/4 czasu trwania 
          // bitu, to kończy działanie funkcji z błędem 
          while(RC5_IN)
             if(timerL > ref2) return command = -1;
       }

       // W momencie wykrycia zbocza sygnału, synchronizuje
       // zmieną timerL dla próbkowania kolejnego bitu
       timerL = 0;
   }

   // Zwraca kod polecenia rc5
   // bity 0..5 numer przycisku
   // bity 6..10  kod systemu(urządzenia)
   // bit 11 toggle bit
   return command;
 }

//---------------------------------------------------------------
// GLÓWNA FUNKCJA PROGRAMU                                                   
//---------------------------------------------------------------
int main(void)
{
  //
  uint cmd;
  u8 out;

  // Porty PD0..PD6  wyjściami - diody LED
  DDRD  = 0x7f;
  PORTD = 0x00;

  // uruchamia Timer0 i przerwanie
  init_rc5();

  while (1)
  {
     // Wykrywa i dekoduje polecenie pilota RC5 
     cmd = detect();

     // Jeśli odebrano komendę 
     if(cmd != -1)
     {  
        // Na sześciu diodach LED pokaże numer polecenia rc5,
        // a na siódmej LED - toggle bit
        out = (cmd & (1<<11)) >> 5;
        out |= cmd & 0x3f;
        PORTD = (out);
     }
  }

  return 0;
}
Listing 1 Program dekodera RC5

Ważne! Program przeznaczony jest dla uC atmega8 taktowanego zegarem 8MHz. Jeśli mikrokontroler ma pracować z częstotliwością inną niż 8MHz, to program będzie wymagał drobnego dopasowania. Jeśli masz z tym problem, pytaj na forum.

Katalog z plikami projektu można pobrać klikając w link dekoder_rc5.zip. Kod wynikowy programu ma wielkość 400 bajtów.

Przykładowy program pilota RC5

Jeśliby ktoś chciał samodzielnie zbudować pilota RC5, to dołączam przykładowy programik do uruchomienia na atmega8. Jak napisano wcześniej, pilot świeci diodami IR impulsowo, z częstotliwością ok 36kHz, gdyż odbiornik SFH5110-36 reaguje tylko na oświetlenie impulsowe o częstotliwości 36kHz, eliminuje to wpływ zakłóceń z innych źródeł światła. W programie pilota użyto układ czasowy Timmer2 uC atmega8 do generowania przebiegu prostokątnego o częstotliwości 36kHz. Ponieważ dotąd jeszcze w naszym kursie nie przerabialiśmy układów czasowych AVRów, więc postaram się krótko wyjaśnić co i jak. Tajmer2 jest ośmiobitowym rejestrem działającym jak licznik, w naszym przykładzie tajmer2 zlicza cykle zegara taktującego mikrokontroler - liczy od zera w górę. Timer2 został skonfigurowany do pracy w trybie CTC(Clear Timer on Compare Match). Czyli w momencie, gdy zawartość tajmera zrówna się z zawartością rejestru OCR2, timer jest zerowany, a stan wyjścia na wyprowadzeniu OC2(w atemega8 - PB3) zmienia się na przeciwny. W wyniku otrzymujemy na wyjściu OC2 sygnał prostokątny, którego częstotliwość zależy od liczby zapisanej w rejestrze OCR2 i od częstotliwości zegara taktującego mikrokontroler. Diodę nadawczą IR należy przyłączyć do wyprowadzenia OC2 AVRa, najlepiej poprzez jakiś tranzystor wzmacniający prąd, aby wystarczając mocno świeciła.

Działanie programu pilota jest zupełnie proste: Funkcja "send_cmd" w pętli wysyła ramkę danych protokołu rc5 o długości 14 bitów. Jeśli wysyłany bit danych ma wartość 1, to przez pierwszą połowę czasu trwania bitu tajmer2 jest wyłączony i dioda IR nie świeci; a w drugiej połowie czasu trwania bitu tajmer2 pracuje - dioda IR świeci. Jeśli wysyłany bit ma wartość 0, to w pierwszej połowie bitu tajmer2 pracuje - dioda IR świeci, w drugiej połowie bitu tajmer2 stoi- dioda IR nie świeci.

Jeszcze taka uwaga. Przykłady w naszym kursie są maksymalnie uproszczone. Ale ponieważ pilot zasilany jest z baterii, więc należałoby zoptymalizować program i całą konstrukcję pilota w kierunku minimalizacji zużycia prądu.

/***************************************************************
   Plik "main.c"

   KURS AVR-GCC (abxyz.bplaced.net)
 
   nadajnik  RC5
 
   (schemat i opis działania w artykule)
   testowanie na atmega8 (8MHz)
*****************************************************************/

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


typedef unsigned char  u8;
typedef  unsigned int  uint;

//---------------------------------------------------------------
// Funkcja wysyła komendę  RC5                                             
//---------------------------------------------------------------
 void send_cmd(uint cmd)
 {
    uint m;

    // dołącza 2 bity startowe 
    cmd |= (3<<12); 

    for(m=(1<<13); m>0; m>>=1)
    {
       if(cmd & m)// jeśli bit o wartości 1
       {   
           // Dioda nadawcza  nie świeci 
           TCCR2= (1<<FOC2)|(1<<COM21)|(1<<CS20); 
           _delay_ms(0.89); // czas trwania połowy bitu 
           // świeci 
           TCCR2= (1<<FOC2)|(1<<WGM21)|(1<<COM20)|(1<<CS20); 
           _delay_ms(0.89);
       }
       else // jeśli bit o wartości 0 
       {   
           // Dioda nadawcza świeci  
           TCCR2= (1<<FOC2)|(1<<WGM21)|(1<<COM20)|(1<<CS20); 
           _delay_ms(0.89);
           // Nie świeci 
           TCCR2= (1<<FOC2)|(1<<COM21)|(1<<CS20); 
           _delay_ms(0.89);
       } 
    }
    TCCR2= (1<<FOC2)|(1<<COM21)|(1<<CS20); // wyłączamy LED i timer2
    TCCR2= 0;  
   
 }

//---------------------------------------------------------------
// GLÓWNA FUNKCJA PROGRAMU                                                   
//---------------------------------------------------------------
int main(void)
{
   uint  i,cmd;
   u8 toggle = 0 ;  

   // PB3(OC2) wyjście - dioda nadawcza podczerwieni 
   DDRB = (1<<PB3);

   // Timer2, CTC mode, no prescaling ,Toggle OC2 on compare match 
   // TCCR2= (1<<WGM21)|(1<<COM20)|(1<<CS20);   
   // OC2 - 36 KHz
   OCR2 = 110; 
 
   while(1)
     // Wysyła po kolei wszystkie komendy 
     for(i=0; i<64; i++)
     {
        // do kolejnej komendy dostawia toggle bit
        cmd =  i|((toggle & 0x01)<<11);
        toggle++;
  
        // Wysyła  komendę 
        send_cmd(cmd); 
        _delay_ms(89); // 50 bitów "ciszy"

        // Komendy wysyłamy sobie co sekundę 
        _delay_ms(1000);  
     }

   return 0;
}
Listing 2 Program pilota RC5

Podobnie jak program dekodera program pilota przeznaczony jest dla uC atmega8 taktowanego zegarem 8MHz. Jeśli mikrokontroler ma pracować z częstotliwością inną niż 8MHz, to program będzie wymagał małego dopasowania.

Katalog z plikami projektu można pobrać klikając w link pilot_rc5.zip.

Pisząc program dekodera RC5 opierałem się na atmelowskiej nocie aplikacyjnej AVR410 doc1473.pdf.

3.06.11 ABXYZ

Dekoder protokołu RC5 - ciąg dalszy

7.11.11 ABXYZ

Ostatnio ktoś na forum wytknął, że program dekodera RC5 marnuje czas procesora testując w pętlach stan linii wejścia mikrokontrolera. W odpowiedzi zamieszczam tu drugą wersję programu dekodera RC5, w której wykorzystano przerwanie zewnętrzne mikrokontrolera. W tej wersji całość kodu zajmująca się odczytem komend RC5 umieszczona została w procedurze obsługi przerwania INT0, zaś główna pętla programu jest pusta, może wykonywać inne zadanie.

Program dekodera przeznaczony jest dla uC ATMEGA taktowanego zegarem 8MHz. Wyjście odbiornika podczerwieni należy przyłączyć do portu PD2(INT0), a osiem diod LED do portu B. Na sześciu diodach LED wyświetla się numer komendy RC5, a ósma dioda LED pokazuje stan bitu toggle; bit ten zmienia swoją wartość na przeciwną, za każdym razem, kiedy naciśnięty jest któryś przycisk w pilocie. Oprócz przerwania zewnętrznego INT0, program korzysta z Timera0, do pomiaru szerokości impulsów. Dlatego, jeśli mikrokontroler ma pracować z częstotliwością inną niż 8MHz, to program będzie wymagał małego dopasowania.

Katalog z plikami źródłowymi projektu można pobrać klikając w link dekoder_rc5_v2.zip.

Katalog zawiera także plik Makefile, który należy dostosować do posiadanego "sprzętu", tzn. wybrać typ AVRa, częstotliwość pracy mikrokontrolera, programator. Najlepiej skompilować program otwierając projekt w edytorzy "Programers's Notepad" dostarczonym wraz z pakietem "WinAVR"; jeśli nie wiesz jak, to polecam artykuł "Szybki start z WinAVR"



Copyright © 2009-2017 XYZ Hobby Robot - Wszelkie prawa zastrzeżone