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:
- minimalnymi podłączeniam pinów i filtracji zasilania mikrokontrolera,
- filtracji zasilania i sposobu podłączenia odbiornika podczerwieni. (w trakcie opracowania)
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:
- przygotowaniu mikrokontrolera do dekodowania sygnału RC-5,
- oczekiwanie w uśpieniu do trybu IDLE, na rozpoczęcie transmisji,
- 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 :-) } }
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ńCześć,
Usuń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ć.
Dzięki za zainteresowanie, temat założyłem tutaj: Elektroda
UsuńWitam!
OdpowiedzUsuń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.
Witaj!
UsuńJakiego mikrokontrolera używasz?
ATmegi8 oraz 32. W obydwu przypadkach się pojawił ten problem. Rozwiązałem go częściowo poprzez zastosowanie czegoś takiego:
UsuńSFIOR |= 0x01;
choć nie do końca pomogło.
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ć.
OdpowiedzUsuńpozdrawiam robert
Cześć.
Usuń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
Cześć. Dzięki za informacje, wszystko działa tak jak tego chciałem.
UsuńJeszcze mam pytanie , jaki mam wybrać profil żeby moja wiadomość była widoczna z moim loginem a nie jako anonimowy.
pozdrawiam robert
Cześć.
Usuń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
Na atmega32 kombinuje na 1000 sposobow i nic nie dziala
OdpowiedzUsuń