Skocz do zawartości

RC5 - raz jeszcze


marek1707

Pomocna odpowiedź

W mojej nowej konstrukcji postanowiłem użyć pilota zdalnego sterowania RC5. Nigdy jakoś nie miałem okazji korzystać z tego standartu IR (choć były inne) no i musiałem się temu przyjrzeć. Podczas przydzielania zadań do poszczególnych zasobów sprzętowych procesora ręczne sterowanie stało gdzieś na końcu kolejki i gdy już zamykano sklepik, został tylko mały kawałeczek timera 1. Główny licznik i jego dwa wyjścia OC1A i OC1B wzięły już silniki krokowe, które jako napęd całości miały znajomą kierowniczkę mięsnego. Przepraszam, ale właśnie zagrywam się "Kolejką" i te porównania jakoś tak same wchodzą (z tyłu sklepu) 🙂

Patrząc na sposób kodowania sygnału, naturalną wydaje się metoda sprawdzania kierunku zbocza w środku bitu. Znalazłem też w sieci kilka innych pomysłów. Od maszyny stanów chodzącej na przerwaniach, poprzez wsparcie całego timera zerowanego i ruszanego po każdym zdarzeniu aż przez aktywne oczekiwanie i stratę 100% czasu procesora. Jakoś tak żaden z tych sposobów do mojego ochłapu timera 1 nie pasował. W ciągu wieczora wymyśliłem i napisałem działający kod mojego nowego pomysłu.

Po przeczytaniu zapewne zauważycie, że jest on skrojony dokładnie do tego, co miałem w procku wolnego ale działa niezawodnie i zużywa poniżej 0.4% mocy obliczeniowej ATmegi 16MHz. Gdyby ktoś kiedyś miał odbierać RC5 a był w podobnej sytuacji, może skorzysta.

Pomysł opiera się na wykorzystaniu wejścia IC1 (to nic nowego) w celu zatrzaskiwania w rejestrze ICR1 aktualnego stanu timera 1. Ten z kolei pracuje ciągle w trybie normal, licząc w kółko 0 -> 0xFFFF z zegarem 2MHz (preskaler 1/8). Nowością wg. mnie jest praca tylko na jednym kierunku zbocza: narastającym. Rysując przebieg sygnału z odbiornika IR (któryś dedykowany TSOP na przykład) i nakładając na niego moje przerwania dostaniemy taki obraz:

Te szpilki na dole to właśnie chwile wykonywania funkcji obsługi przerwania od narastającego zbocza na wejściu IC1. Żadna nie trwa dłużej niż 5.5us. Doliczając czasy prologu i epilogu (zachowania i odtwarzania stanu procesora) będzie trochę ponad 6us.

Algorytm bazuje na pomiarze czasu, jaki upłynął od poprzedniego, takiego samego zbocza. Ten czas - jak wynika z rysunku, może być równy 1*CNB (Czas Nadawania Bitu), 1.5*CNB lub 2*CNB. Teraz wystarczyło tylko dopisać obsługę każdego z tych przypadków. Acha, rozróżniam jeszcze sytuację, gdy od poprzedniego zbocza upłynęło "bardzo dużo czasu", czyli, że jest to z pewnością pierwsze zbocze zaraz po bicie startu. Ono obsługiwane jest specjalnie, bo inicjuje stan systemu odbioru. Zmienna shift_reg jest 16-bitowym rejestrem przesuwającym, do którego wstawiam odebrane bity.

if (BARDZO_DLUGO)
{
shift_reg = 0x0001;
poprzedni_bit = 1;
licznik_bitow = 1;
}
else
{
if (licznik_bitow > 0)
{
	if (CNB1)
	{
		shift_reg = (shift_reg << 1) | poprzedni_bit;		// T = 1*CNB -> zawsze ten sam bit
		licznik_bitow++;
	}
	else
	{
		if (CNB15)
		{
			if (poprzedni_bit == 1)
			{
				shift_reg = (shift_reg << 2) | 2;	// T = 1.5*CNB -> jeśli poprzedni = 1 to teraz 10
				poprzedni_bit = 0;
				licznik_bitow += 2;
			}
			else
			{
				shift_reg = (shift_reg << 1) | 1;	// T = 1.5*CNB -> jeśli poprzedni = 0 to teraz 1
				poprzedni_bit = 1;
				licznik_bitow++;
			}
		}
		else
		{
			if (CNB2)
			{
				shift_reg = (shift_reg << 2) | 2;	// T = 2*CNB -> zawsze 10
				poprzedni_bit = 0;
				licznik_bitow += 2;
			}
			else
			{
				licznik_bitow = 0;			// Inny czas: błąd timingu!
			}
		}
	}
}

if (licznik_bitow >= RC5_FRAME_LENGTH)
{
	licznik_bitow = 0;
	if (rc5_flag == 0)
	{
		rc5_data = shift_reg;
		rc5_flag = 1;
	}
}
}

Nie jest to czysty kod w C, wprowadziłem uproszczenia np. w warunkach "if-ów" ale one tylko zaciemniały obraz a każdy chętny na pewno poradzi sobie ze sprawdzaniam, czy zmierzony czas mieści się w zadanych granicach.

Ciekawostką jest to, że czasem jedno zbocze "odbiera" 2 bity a liczba zboczy w różnych znakach jest różna 🙂

Interfejsem do funkcji pracującej w przerwaniu są dwie zmienne statyczne (volatile static):

uint8_t rc5_flag - ustawiana na 1 gdy odebrano cały znak

uint16_t rc5_data - w której kod tego znaku można odczytać

Po odczycie znaku należy rc5_flag wyzerować potwierdzając tym samym gotowość funkcji "łykającej" znaki do przyjęcia następnego.

Mając już odebrany kod znaku można zmodyfikować część przekazującą go dalej tak, by np. rozróżniała stan bitu "toggle" itp

Wyzerowanie w którymś momencie zmiennej licznik_bitów wprowadza automat w stan poszukiwania długiej przerwy, czyli oczekiwania na bit startu nowej ramki. Następuje to po prawidłowym odebraniu 14 bitów (RC5_FRAME_LENGTH) lub po wykryciu błędu formatu.

Do wykrywania upływu "bardzo długiego czasu" wykorzystałem ostatni wolny zasób timera 1: OCR1C. Po każdym zboczu jest on ustawiany na zgłoszenie przerwania za 30ms od danej chwili. To prawie maksymalny czas, bo dla szybkości zliczania 2MHz wymaga to 60000 cykli timera:

OCR1C = ICR1 + 60000;
rc5_space_counter = 0;
TIFR1 = _BV(OCF1C);		// Zeruję poprzednie zgłoszenia

Funkcja obsługująca przerwanie od OCR3C jest już prosta. Inkrementuje prosty, statyczny licznik 8-bitowy, jednocześnie nie pozwalając mu "przekręcić się" przez 255:

ISR(TIMER1_COMPC_vect)
{
if (rc5_space_counter < 255)
	rc5_space_counter++;
}

Ten licznik, a ściślej jego wartość > 0 jest właśnie detekowana jako "BARDZO_DLUGO" w funkcji obsługi przerwania od IC1. Każde obsłużone zbocze "odwleka" moment przyjścia przerwania COMPC na 30ms w przyszłość i dopóki przychodzą często, nic się nie dzieje. Brak zbocza przez dłuższy czas spowoduje, że wciąż liczący timer 1 "dogoni" OCR3C, obsłuży się przerwanie i fakt ten zostanie zapamiętany w rc5_space_counter. Upływający czas, jeśli odstęp jest bardzo długi, będzie co prawda inkrementował co 32ms licznik (przy każdym przejściu timera 1 przez wartość wpisaną do OCR3C) ale go nie przepełni - co jest warunkiem poprawnego wykrycia pierwszego impulsu kolejnego znaku. Zamiast licznika mógłby to być zwykły znacznik 1-bitowy (albo wręcz sprzętowy bit zgłoszenia przerwania od komparacji w kanale C), ale zliczanie jest mi potrzebne do realizacji jeszcze innej funkcji.

Link do komentarza
Share on other sites

Ciekawy temat, tylko niestety standard RC5 nie należy do jakiś super powszechnych. Nawet posiadany przez mnie pilot PHILIPSa, który ten standard opracował, w nim nie pracuje. Ogólnie mi w domu nie udało się znaleźć pilota w standardzie RC5, co skłoniło mnie do budowy własnego pilota.

Kariera RC5 w konstrukcjach amatorskich zaczęła się w momencie kiedy to jakimś czasopiśmie opisano go dokładnie, ale niestety w sprzęcie RTV nie jest to jakoś specjalnie popularny kod.

Link do komentarza
Share on other sites

No to miałem szczęście początkującego. Pierwszy który wziąłem do ręki (i jedyny pilot bez którego rodzina będzie mogła przez jakiś czas się obejść) pochodzi z telewizora LG i pracuje w RC5 🙂

Mi akurat zależało na małym obciążeniu procesora i pomyślałem, że może ktoś jeszcze będzie miał podobne priorytety.

Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.