czwartek, 31 marca 2011

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 :-)
  }
}

11 komentarzy:

  1. Cześć, dobra robota, wszystko działa. Jednak chciałem przeportować Twój kod na ATTiny13, i dokonałem drobnych modyfikacji, bo na Tiny były inne nazwy niektórych rejestrów. No i mam problem, bo coś nie działa. Przerwanie jest generowane, ale diody danych i toggle bita wcale się nie zaświecają. Strona sprzętowa na pewno poprawna, jakby co mogę wrzucić kod (zmienione tylko te nazwy rejestrów). Może jakiś pomysł co może być nie tak?

    OdpowiedzUsuń
    Odpowiedzi
    1. Cześć,
      najlepiej by było, gdybyś opisał problem na forum Mikrokontrolery ogólnie
      wklejając cały kod ze zmianami. Wtedy będzie mi łatwiej odpowiedzieć.

      Usuń
    2. Dzięki za zainteresowanie, temat założyłem tutaj: Elektroda

      Usuń
  2. Witam!
    Mam dość nietypowy problem z kompilacją. Mianowicie wyskakuje błąd "'PSR10' undeclared".
    Korzystam z AtmelStudio 6.1.
    Jest jakiś inny sposób zerowania licznika, czy mam instalować inne oprogramowanie?
    Może jest jakieś proste wyjaśnienie tego błędu.

    OdpowiedzUsuń
    Odpowiedzi
    1. Witaj!
      Jakiego mikrokontrolera używasz?

      Usuń
    2. ATmegi8 oraz 32. W obydwu przypadkach się pojawił ten problem. Rozwiązałem go częściowo poprzez zastosowanie czegoś takiego:

      SFIOR |= 0x01;

      choć nie do końca pomogło.

      Usuń
  3. Cześć. Jestem początkujący w dziedzinie mikrokontrolerów i staram się jak najwięcej ogarnąć z tego tematu. Problem jest w tym , że nie wiem jak zrobić warunek żeby podać konkretny adres i komendę RC5 w wyniku czego np.zapali się led. Nie chodzi mi o gotowe rozwiązanie tylko o wskazówkę, podpowiedź jak to wykonać.
    pozdrawiam robert

    OdpowiedzUsuń
    Odpowiedzi
    1. Cześć.

      W powyższym programie odebrana dana (np. kod wciśniętego przycisku pilota) jest dostępna w zmiennej globalnej dd_rc5_dane_odebrane, a jej wykorzystaniem zajmuje się funkcja test(). Przyglądnij się opisowi i działaniu tej funkcji. Tam właśnie możesz wykonać co tylko zechcesz z odebraną daną lub stanem bitu toggle.

      Jeżeli będziesz miał jeszcze jakieś pytania to śmiało pisz :-)

      Pozdrawiam,
      Jacek

      Usuń
    2. Cześć. Dzięki za informacje, wszystko działa tak jak tego chciałem.
      Jeszcze mam pytanie , jaki mam wybrać profil żeby moja wiadomość była widoczna z moim loginem a nie jako anonimowy.
      pozdrawiam robert

      Usuń
    3. Cześć.
      No to się cieszę :-)
      Co do nicka, to albo masz konto na Google i wtedy (gdy jesteś zalogowany) komentujesz jako użytkownik, albo możesz wybrać opcję Nazwa i wpisywać ją za każdym razem.
      Pozdrawiam,
      Jacek

      Usuń
  4. Na atmega32 kombinuje na 1000 sposobow i nic nie dziala

    OdpowiedzUsuń