logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

[C] Dekodowanie RC5 - jaką taktykę przyjąć

nelik1987 12 Sty 2011 19:26 5428 15
REKLAMA
  • #1 8996545
    nelik1987
    Poziom 31  
    Witam, od dawna chce napisać bibliotekę do dekodowania sygnały z pilotów RC5.
    Znam zasadę działania i rozumiem jak kodowany jest sygnał.
    Problem w tym, że nie wiem jak rozwiązać sposób dekodowania sygnału, miałem kilka pomysłów ale nie wiem który będzie najlepszy.

    Chce to zrobić tak, procesor będzie oczekiwał na przerwanie wywołane opadającym zboczem (opadającym bo z TSOP4836 otrzymujemy sygnał zanegowany) następnie należało by sprawdzać jaki jest stan przez połowę okresu tj 889 µs, a następnie sprawdzać stan przez czas jednego okresu czyli 1,778ms. sprawdzanie w czasie okresu było by powtarzane do czasu odczytania ostatniego bitu dopiero wtedy następowało by wyjście z przerwania. Niestety przerwanie trwało by wtedy stosunkowo długo bo około 25ms. znacie jakieś inne rozwiązanie?
  • REKLAMA
  • #2 8996726
    mario06
    Poziom 15  
    Atmel na swojej stronie ma przykład aplikacji dekodowania kodu RC5 (nie chodzi mi o sam program a o sposób) DOC2817. Najlepiej wzorować się na rysunku numer 5. Tak jak piszesz nie najlepszym rozwiązaniem jest puszczenie wszystkiego w jednym przerwaniu. Należy to rozdzielić na przerwania które są wywoływane w odpowiednich momentach i ustawiają tylko pojedyncze dane i się kończą czekając na następne wywołanie z odpowiednim zabezpieczeniem czasowym. Mimo takiego skomplikowania da się to zrobić z jednym timerem. A co ważne nie będzie przerwanie zatrzymywało mikrokontrolera na 25ms, co leprze kod jest wysyłany co 114 ms więc procek siedziałby i nic nie robił przez 18% czasu.
  • #3 8996941
    nelik1987
    Poziom 31  
    Właśnie teraz myślałem o innym sposobie bo zatrzymanie procesora na 25 ms było niedopuszczalne i właśnie sam wykominowałem ze wystarczy mierzyć poziom raz na okres bo z tej informacji można wywnioskować czy było zbocze narastające czy opadające. Dokładnie tak jak piszą w PDF jest to czas 3/4 okresu od początku okresu. Czy konieczne jest ustalanie czasu okresy tak jak to pokazano w PDF? rozumiem ze jest to potrzebne w przypadku gdy nie mamy idealnego 36kHz, ale czy ta częstotliwość mozę się znacząco różnić i robić problemy podczas odczytu? czy w zupełności wystarczy ustawienie na sztywno czasów podanych w specyfikacji czyli te 1,778ms?
  • #4 8997126
    szelus
    Poziom 34  
    Czasy niestety mogą sie różnić, w zależności od pilota.

    Ja wykorzystałem alternatywną, prawie "sprzętową" metodę. ;)
    Sygnał z odbiornika wchodzi na wejście ICP zatrzaskując Timer1 (który chodzi w kółko na 16 bitach) i generując przerwanie. Procedura obsługi przerwania przełącza na zmianę aktywne zbocze. Odejmując wyniki kolejnych pomiarów mam odstęp pomiędzy kolejnymi zboczami. Zbocza sobię liczę w zmiennej globalnej - w ten sposób wiem, na którym etapie odbioru jestem. Poprawnie odebrane bity też wrzucam do zmiennej globalnej i jak zborę wszystkie, to przekazuję do programu głównego. W ten sposób obsługa RC5 praktycznie nie zajmuje procesora i jest niezależna od opóźnienia obsługi przerwań.
  • REKLAMA
  • #5 8998250
    mario06
    Poziom 15  
    Dobrze jest sprawdzać czas trwania okresu stanu niskiego/wysokiego (szybko można wykryć błędną transmisję), ale tak jak to zostało napisane czas trwania nie musi być dokładnie zgodny ze specyfikacją ja stosowałem tolerancje 1/4 trwania bitu. Tak samo odczytanie stanu bitu nie musi być dokładnie w 3/4 bitu może być chwile po synchronizacji, byle stan był stabilny (ja dawałem niewielkie opóźnienie w przerwaniu od synchronizacji i wtedy odczytywałem i większego problemu nie było). Fajnie byłoby też wyłączać odbiornik po błędnym lub prawidłowym odczycie kodu, po prostu nie reagować na przerwania przez paręnaście ms.
  • #6 9009693
    asembler
    Poziom 32  
    Fajnym i szybkim sposobem jest podpięcie czujnika do przerwania zewnętrznego. Wykryte w ten sposób zbocza opadające i odpowiedni licznik programowy (to są wolne przebiegi) pozwala jednoznacznie odczytać kod.
  • #7 9011698
    nelik1987
    Poziom 31  
    asembler napisał:
    Fajnym i szybkim sposobem jest podpięcie czujnika do przerwania zewnętrznego.


    no właśnie o tym pisałem...

    asembler napisał:
    Wykryte w ten sposób zbocza opadające i odpowiedni licznik programowy (to są wolne przebiegi) pozwala jednoznacznie odczytać kod.


    mierząc tylko i wyłącznie zbocza opadające będzie trzeba domniemac że jezeli w czasie trwania bitu nie zostanie wywołane przerwanie to mamy zbocze narastające a co ze zboczami opadającymi na granicy 2 bitów, trzeba by je odrzucać bo nie jest to informacja o wartości bitu, taki sposób komplikuje konstrukcję. Co do wykorzystania licznika programowego zastanowię się nad tym, ponieważ warto by było zostawić timery w spokoju jeżeli miała by to być użyteczna i uniwersalna biblioteka
  • #8 9015522
    Konto nie istnieje
    Konto nie istnieje  
  • #9 9016787
    janbernat
    Poziom 38  
    Ja bym zrobił polling- czyli odpytywanie stanu wejścia.
    Co jakiś czas- dużo mniejszy niż minimalny czas trwania impulsu.
    W petli głównej.
    A czas odpytywania wyznaczany przez któryś timer.
  • REKLAMA
  • #10 9016923
    asembler
    Poziom 32  
    janbernat napisał:
    Ja bym zrobił polling- czyli odpytywanie stanu wejścia.
    Co jakiś czas- dużo mniejszy niż minimalny czas trwania impulsu.
    W petli głównej.
    A czas odpytywania wyznaczany przez któryś timer.

    Skoro czas odpytywnia wyznaczany przez timer to co szkodzi od razu skoczyc do obslugi tego timera i odpytać w przerwaniu?
    Angazowanie programu głównego do tego celu rodzi wiecej kłopotów niż jest to warte. W granicznym przypadku może sie okazav że program główny nic innego nie będzie mógł robic.
  • REKLAMA
  • #11 9020602
    janbernat
    Poziom 38  
    Nie upieram się- można całość umieścić w przerwaniu.
    Bo jak program krótki to odkładanie/ zdejmowanie ze stosu może być dłuższe niż wykonanie obsługi.
    Całość nie jest krytyczna czasowo jeśli ma robić tylko dekodowanie.
  • #13 9040882
    hotdog
    Poziom 26  
    Mechanizm z ICP wydaje być się lepszy, ponieważ przerwania zewnętrzne mają dosyć niski priorytet (a w AVR'ach przestawić go nie da rady). W rejestrach mamy już wtedy potrzebną wartości.

    W większości przypadków nie robi to większej różnicy (wystarczy przerwanie od GPIO), bo mamy aż około 300us na samplowanie, co w większości jest mocno wystarczające.

    Przy czym w wersji z GPIO dogi są dalej 2:
    1. Po wystąpieniu przerwania od GPIO włączyć przerwanie od timera i samplować w nim (pierwsze o 889+889/2 i następne co 889).
    2. Wykorzystać tylko przerwanie od GPIO. W nim sprawdzić jaki czas upłynął od ostatniego przerwania. W zależności ile to było rozpoczynamy dekodowanie nowego sygnału lub dopisujemy odpowiedni bit do aktualnie dekodowanego.

    Ogólnie moim zdaniem 1 metoda jest fajniejsza, mniej skomplikowana, ale za to jest odrobinę mniej optymalna.

    https://www.elektroda.pl/rtvforum/topic1497626.html
  • #14 9042514
    -Radar-
    Poziom 12  
    Dobrym pomysłem może też być 3 krotne samplowanie poziomu aby uodpornić się na zaklócenia - wówczas jeśli jeden wynik samplowania jest różny uznajemy go za błąd i odrzucamy. Z tego co pamiętam taki tryb pracy realizował jakiś układ do rs-232..
  • #15 9056511
    nelik1987
    Poziom 31  
    Napisałem program i mam pewne problemy z jego działaniem, w skrócie napiszę jak działa program:
    Mikroprocesor oczekuje na zbocze opadające po czym wywołuje przerwanie, w przerwaniu tym za pierwszym razem zerowany jest timer 1, po czym wychodzi z przerwania po kolejnym zboczu opadającym odczytywana jest wartość timera1 i zapisywana jako ref_time czyli czas trwania bitu. Jednocześnie włączane jest przerwanie od osiągnięcia przez timer 1 wartości ref_time i wyłączone zostaje przerwanie od INT0. Po wywołaniu przerwania od Timera 1 następuje sprawdzenie jaki jest stan PD2 jeżeli wysoki zapisywane jest 0 do tablicy a jeżeli niski zapisywana jest 1. Przerwania od timera wykonywane są do czasu aż licznik osiągnie wartość 12 (odczytanie 12 bitów) pierwsze 2 bity S1 i S2 są zawsze równe 1 (tak przeczytałem w książce o RC5). po odczytaniu wszystkich bitów wyłączane jest przerwanie od timera 1 i włączona od INT0 i proces powinien zacząć się od początku W pętli While wysyłane są potrzebne informacje o czasie trwania bitu i ich wartościach.



    Problem w tym, że po uruchomieniu wciskam klawisz w pilocie odczytywany czas wynosi około 1,778 ms (czyli OK) niestety wartości bitów równają się samym zerom albo samym jedynkom, czasami pojawią się jakieś bitu ale nie jest to powtarzalne. Jak wcisnę jakiś guzik drugi raz to czas ref_time jest dziwny bo czasami wynosi 0,889 ms (czyli pół bitu) a czasami wartości są w ogóle dziwne. Gdzie może być błąd jaki popełniłem, co możecie mi doradzić na temat samego programu

    #include <avr/io.h>
    #include <util/delay.h>
    #include "biblioteki/usart.c"  	// Biblioteka odpowiadająca za transmisję USART / USART library
    #include <avr/interrupt.h> 		// Biblioteka przerwań
    #include <rc5.h>
    
    //////////////////////////////////////////////////
    //												//
    //      BIBLIOTEKA DEKODOWANIA SYGNAŁU RC5		//
    //			   RC5 DECODING LIBRARY				//
    //                KORNEL BOROWSKI				//
    //            WWW.MIKROKONTROLERY.ORG			//
    //												//
    //////////////////////////////////////////////////
    
    volatile int ref_time=50;	// zmienna przechowująca czas trwania bitu
    volatile char t=0;
    volatile char RC5_error=0;		// zmienna błędów (1 - błąd; 0 - brak błędu)
    volatile char RC5_code[] = {1,1,0,0,0,0,0,0,0,0,0,0,0,0}; // tablica bitów kodu RC5 (pierwsze 2 są zawsze równe 1)
    volatile uint8_t RC5_code_count = 2;
    volatile int RC5_address = 0;
    volatile int RC5_command = 0;
    
    
    int licznik = 0;  // POMOCNICZY LICZNIK do UART
    int licznik1 = 0; // POMOCNICZY LICZNIK do UART
    int licz = 0;
    
    
    
    #define FOSC 16000000			// Czestotliwosc zegara w Hz / System Clock in Hz
    #define BAUD 9600    			// Predkosc transmisji USART / USART speed
    #define MYUBRR FOSC/16/BAUD	
    
    void led_toggle(void);			// do testów !!!!!!!!!!!!!!!!!!!!
    
    
    ISR (TIMER1_COMPA_vect) 		// Przerwanie od zrównania z wartością w rej. OCR1A  a licznikiem 1
    {
    led_toggle();
    if(RC5_code_count<=12)
    {
    	cli();
    	//_delay_us(200);				// Poczekaj 200 us na ustalenie się stanu
    	if(bit_is_clear(RC5_PIN,RC5_BIT)) // jeżeli wykryto stan niski
    	{
    		RC5_code[RC5_code_count] = 1;
    	}
    	else
    	{
    		RC5_code[RC5_code_count] = 0;
    	}
    	RC5_code_count++;
    	GICR = _BV(INT0);									// Ustawienie wektora przerwania dla INT0
    	MCUCR = _BV(ISC01);
    	sei();
    	
    } 
    else
    {
    	t=0;					// czekaj na początek nowej ramki
    	TIMSK &= ~_BV(OCIE1A); 	// wyłączenie przerwania od osiągnięcia zadanej wartości przez timer 1
    	RC5_code_count=2;
    }
    
    
    }
    
    ISR (INT0_vect) 				// Przerwanie od zbocza opadającego INT0
    {
    led_toggle();
    if(t==0)
    	{	
    	TCNT1=0;					// wyzeruj licznik 1
    	t=1;
    	}
    else if (t==1)
    	{
    	ref_time = TCNT1/2.0;		// Przepisz do ref_time wartość z licznika 1 i i podziel przez 2 aby uzyskać czas bitu w us
    	
    	if((ref_time < MIN_FULL_BIT) || (ref_time > MAX_FULL_BIT)) // jeżeli zmierzony czas bitu jest wiekszy niż maksymalny dopuszczalny LUB mniejszy niż minimalny dopuszczalny ustaw błąd
    		{
    		RC5_error = 1; 			// Ustaw błąd odczytu danych RC5
    		t=0;
    		//_delay_ms(50);			// po błędzie poczekaj 50 ms
    		//volatile char RC5_code[] = {1,1,0,0,0,0,0,0,0,0,0,0,0,0}; // wyzeruj tablicę
    		}
    	else
    		{
    		TIMSK = _BV(OCIE1A); 	// Ustawienie wektora przerwania od osiągnięcia zadanej wartości przez timer 1
    		OCR1A = ref_time*2.0;	// ustaw warość porównania dla licznika1 na czas trwania bitu
    		t=2;
    		RC5_code_count=2;		// ustaw zmienna tablicową na 2 element
    		RC5_error = 0; 			// Kasuj błąd odczytu danych RC5
    		GICR = 0;
    		MCUCR = 0;
    		}
    	}	
    }
    
    
    
    int main (void)
    {
    //////////// ZMIENNE ////////////
    
    /////////////////////////////////
    USART_Init(MYUBRR);			// Inicjalizacja transmisjii USART
    
    DDRA = 255;						    				// TEST dla TOGGLE LED jako wyjście
    GICR = _BV(INT0);									// Ustawienie wektora przerwania dla INT0
    MCUCR = _BV(ISC01); 								// Ustawienie przerwania od zbocza opadającego na INT0 (PD2)
    TCCR1B = _BV(CS11) | _BV(WGM12);					// Ustawienie timera 1 z preskalerem 8 (zlicza co 0,5 us) oraz kasowanie wartości licznika po każdym zrównaniu z wartością w OCR1A
    RC5_PORT = _BV(RC5_BIT);							// Polaryzacja dodatnia wejścia przerwania zewnętrznego INT0 (PD2)
    
    PORTA=0xFF;		
    DDRD = 0;				
    
    sei();												// Włącz obsługę przerwań
    while(1)  											// Program glowny, petla nieskonczona
    {
    
    RC5_address=((1)*RC5_code[7])  + ((2)*RC5_code[6])  + ((4)*RC5_code[5])  + ((8)*RC5_code[4])  + ((16)*RC5_code[3]) ;							   // 5 bitów
    RC5_command=((1)*RC5_code[13]) + ((2)*RC5_code[12]) + ((4)*RC5_code[11]) + ((8)*RC5_code[10]) + ((16)*RC5_code[9]) + ((32)*RC5_code[8]) ;   // 6 bitów
    
    //if(TCNT1==0)
    
    
    
    if(licz>=15)
    {
    	if(licznik>=10000)
    	{
    		for(licznik1=0;licznik1<=12;licznik1++)
    		{
    			USART_Putint(licznik1);
    			USART_Transmit(45); // myślnik (-)
    			USART_Putint(RC5_code[licznik1]);
    			USART_Transmit(13); //ENTER
    		}
    		USART_Putint(RC5_address);
    		USART_Transmit(13); //ENTER
    		USART_Putint(RC5_command);
    		USART_Transmit(13); //ENTER
    		USART_Transmit(13); //ENTER
    		USART_Transmit(84); //t
    		USART_Transmit(61); //=
    		USART_Putint(t);
    		USART_Transmit(13); //ENTER
    		USART_Putint(RC5_error);
    		USART_Transmit(13); //ENTER
    		USART_Transmit(13); //ENTER
    		USART_Putint(ref_time);
    		USART_Transmit(13); //ENTER
    		USART_Transmit(13); //ENTER
    
    		licznik=0;
    	}
    	licznik++;
    	licz=0;
    }
    licz++;
    }} 	//zamyka while i main
    	
    void led_toggle(void) {
    if(PORTA) {PORTA=0x00;} else
    PORTA=0xFF;
    }
REKLAMA