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

[atmega8][C] odbiornik podczerwieni i pilot bez nazwy

omen_s 31 Gru 2008 00:40 3740 26
REKLAMA
  • #1 5926992
    omen_s
    Poziom 19  
    Witam

    Czy może ktoś już rozpracowywał taki pilot ?

    [atmega8][C] odbiornik podczerwieni i pilot bez nazwy

    Nie ma na nim żadnej nazwy, jest od transmitera ukf do samochodu także o bliżej nie określonej nazwie firmy.

    Posiadam odbiornik TSOP34836 podłączony do portu INT0 wg standardowej aplikacji (kondensator i 2 rezystory przy odbiorniku) czy on będzie dobry ? Mam jeszcze wersję na 38KHz TSOP34838. Macie jakieś wskazówki jak to ugryźć ? nie bardzo uśmiecha mi się próbowanie wszystkich możliwych standardów nie mając w ogóle pewności że układ odbiera cokolwiek przez ten odbiornik.

    Pozdrawiam

    ps. Do wykorzystania został mi timer0 i timer2 (8bitowe)
  • REKLAMA
  • Pomocny post
    #2 5927022
    dawid512
    Poziom 32  
    Na pewno nie jest to RC5 ale kiedyś próbowałem podobnego pilota na układzie odbiornika w standardzie RC5. W prawdzie odbierał sygnał ale nie potrafił go poprawnie zdekodować.
  • #3 5927573
    BoskiDialer
    Poziom 34  
    omen_s: Nie wiem w czym problem. Kiedyś znalazłem jakiegoś pilota o jakimś dziwacznym formacie transmisji, napisałem prosty analizator, który próbkował wyjście odbiornika 10000 razy na sekundę i przesyłał dane do komputera. Prosty analizator i zacząłem się zastanawiać co które zbocze może oznaczać. Bez żadnej dokumentacji rozpracowałem kod, całość teraz chodzi bezbłędnie. Jeśli Ci się chce, to zawsze znajdzie się metoda do rozkodowania sygnału.

    Timery zawsze są przydatne, u mnie np odbiornik jest podłączony pod zwykły pin i próbkowany 10000 razy na sekundę, zamiast komputera sam uC analizuje dane i przekazuje je do głównego programu.

    Zacznij od odebrania surowych danych i wyświetlenia ich, sam wygląd sygnału dużo mówi o możliwym standardzie.
  • #4 5929818
    Limonit
    Poziom 13  
    Zintegrowane odbiorniki, których używasz filtrują nośną sygnału o określonej częstotliwości. I jeśli częstotliwość działania tego pilota jest inna, to nie bedą one dobre do odbioru.
    Nie jest natomiast powiedziane, że nie odbiorą nic. Ale może być trudno taki sygnał interpretować.

    Sprawdzić czy cokolwiek się odbiera możesz. Zobacz, czy przerwanie jest w ogóle zgłaszane, wyprowadź to sobie na diodę przykładowo.

    Mi przykładowo odbiornik kodu RC5 łapie wszystkie piloty, oczywiście nie próbuję tego dalej dekodować.
  • #5 5930491
    omen_s
    Poziom 19  
    Po przeczytanie manuala od atmegi8 mam już ustawione przerwanie od int0 aby wykrywało logiczną zmianę (nie jestem pewny czy powinno być tak czy na zbocze opadające lub rosnące). Do układu mam podłączony LCD ale mam także komunikację po rs232 z komputerem więc wysyłam sobie przez uart. w tym momencie mam w przerwaniu od int0 ustawione żeby wysyłało mi znak przez uart i to działa bo widze w terminalu że odbiera znaki jak naduszę przycisk na tym pilocie.
    I teraz jak mam zrobić to sprawdzanie 10000 razy ? nie mogę zrobić tablicy z 10000 elementami :|

    teraz mam tak :

    zmienne:
    //definicje i zmienne dla pilota
    volatile unsigned int liczba_odebranych = 0;
    char odb[200];
    volatile unsigned char flaga_odbior = 0;
    volatile unsigned char flaga_przerwanie_timer0 = 0;
    //koniec definicji i zmiennych dla pilota


    przerwania:
    ISR(INT0_vect)
    {
    	flaga_odbior = 1;
    }
    
    ISR(TIMER0_OVF_vect)
    {
    	flaga_przerwanie_timer0++;
    }


    ustawienie timera0, przerwań int0 :
    // konfiguracja PD2 jako wejście
    DDRD  &= ~(1<<PD2);           
    // enable internal pull-up resistors
    PORTD |= (1<<PD2);   
    
    // Ustawia timer z preskalerem Fcpu/64
    TCCR0 |= ((1 << CS00) | (1 << CS01));
    //zezwolenie na przerwania
    TIMSK |= (1 << TOIE0);
    
    //zgłasazanie przerwania od int0 gdy zmieni stan
    MCUCR |= (1 << ISC00);
    //zezwolenie na przerwania od int0
    GICR |= (1 << INT0);
    


    no i główna pętla :
    // odblokowanie przerwań
    sei();
     	
    	while(1)
    	{
    	//UART_putstring("0 ");
    	
    	if (flaga_odbior == 1)
         {
    		//UART_putstring("flaga_odbior=1 ");
    		
    		flaga_przerwanie_timer0 = 0;		
    
    		do
    	 	 {
    			if (flaga_przerwanie_timer0 >= 25)
    			 {	
    				flaga_przerwanie_timer0 = 0;
    				if (flaga_odbior >= 1)
    				 {
    				 	//liczba_odebranych++;
    					flaga_odbior = 0;
    				 	//UART_putchar('1');
    					
    					odb[liczba_odebranych] = '1';
    				 }
    				else
    				 {
    				 	//liczba_odebranych++;
    					//UART_putchar('0');
    
    					odb[liczba_odebranych] = '0';
    				 }
    				liczba_odebranych++;
    			 }
    	 	 }
    		while (liczba_odebranych < 200);
    		
    		
    		flaga_odbior = 0;
    		UART_putstring(odb);
         }
         }
     return 0;
    }
    


    To są moje początki w C dlatego nie wszystko może być tak jak powinno się robić, wszelkie uwagi mile widziane ;)

    przy prescalerze ustawionym na 64 timer powinien odliczyć 10000Hz przy wartości 25 (kwarc 16MHz). Teraz te 200 znaków to odbiera mi to :

    np dla 1 :
    11101000000000000000000000000000000000000000000000000000000000000000000000000000
    00000111010000000000000111101000111101000111111000111000011110000111101000111010
    00111101000000000000000011111100000000001110100000000000000000000000000000000000
    00000000000000000000000000000000000000000000011101000000000000011110100011110100
    0111111000111000011110000111101000111010001111010000000000000000111111000000000


    I jakoś nie widzę żeby to się różniło w przypadku innych klawiszy więc przydało by się sprawdzić więcej niż200 znaków
  • #6 5937828
    Limonit
    Poziom 13  
    Teraz masz coś w rodzaju przebiegu czasowego, kiedy cokolwiek jest odbierane. A spróbuj może sprawdzać stan pinu wejściowego (w procedurze przerwania INT) i ten przebieg zapisywać. Może zobaczysz coś ciekawszego.
    I spróbuj ustawić przerwania wyzwalane każdym zboczem, bo nie znając kodowania można coś przeoczyć.
  • REKLAMA
  • #7 5938567
    BoskiDialer
    Poziom 34  
    Jeśli t0 idzie na F_CPU/64, to licznik t0 przepełnia się 976,6 razy na sekundę (co 256 cykli zegara timera). Jeśli teraz jeszcze dajesz programowo dzielenie /25, to otrzymujesz próbkowanie 40 razy na sekundę - zbyt mało.
    Zamiast wykorzystywać przerwanie int0 wysyłaj to, co jest w danym momencie na pinie.
    Można zastosować kompresję, np 4 próbki na jedną cyfrę hex, taki zapis jest dość czytelny, zważywszy że przy 10kHz zmiany nie będą aż tak szybkie, więc będą przeważać 0 i F.
    Bloki jedynek na wyjściu świadczą o tym, że zbocza pojawiają się zbyt szybko względem prędkości próbkowania.
  • REKLAMA
  • #8 5939698
    omen_s
    Poziom 19  
    zrobiłem teraz tak :
    ISR(INT0_vect)
    {
    	
    	//wyłączamy przerwanie od int0
    	GICR &= ~(1 << INT0);
    		
    		do
    	 	 {
    				
    				znakh++;
    
    				if (bit_is_set(PIND,2))
    				 {
    
    					znak_hex[znakh] = 1;
    
    				 }
    				
    				if (bit_is_clear(PIND,2))
    				 {
    
    					znak_hex[znakh] = 0;
    
    				 }
    				
    				if (znakh == 4)
    				 {				
    					
    					znakh = 0;
    					znakh = (znak_hex[0] * 8) + (znak_hex[1] * 4) + (znak_hex[2] * 2) + (znak_hex[3] * 1);
    					
    					if (znakh == 0)
    					{
    						odb[liczba_odebranych] = '0';
    					}
    					if (znakh == 1)
    					{
    						odb[liczba_odebranych] = '1';
    					}
    					if (znakh == 2)
    					{
    						odb[liczba_odebranych] = '2';
    					}
    					if (znakh == 3)
    					{
    						odb[liczba_odebranych] = '3';
    					}
    					if (znakh == 4)
    					{
    						odb[liczba_odebranych] = '4';
    					}
    					if (znakh == 5)
    					{
    						odb[liczba_odebranych] = '5';
    					}
    					if (znakh == 6)
    					{
    						odb[liczba_odebranych] = '6';
    					}
    					if (znakh == 7)
    					{
    						odb[liczba_odebranych] = '7';
    					}
    					if (znakh == 8)
    					{
    						odb[liczba_odebranych] = '8';
    					}
    					if (znakh == 9)
    					{
    						odb[liczba_odebranych] = '9';
    					}
    					if (znakh == 10)
    					{
    						odb[liczba_odebranych] = 'A';
    					}
    					if (znakh == 11)
    					{
    						odb[liczba_odebranych] = 'B';
    					}
    					if (znakh == 12)
    					{
    						odb[liczba_odebranych] = 'C';
    					}
    					if (znakh == 13)
    					{
    						odb[liczba_odebranych] = 'D';
    					}
    					if (znakh == 14)
    					{
    						odb[liczba_odebranych] = 'E';
    					}
    					if (znakh == 15)
    					{
    						odb[liczba_odebranych] = 'F';
    					}
    					
    					liczba_odebranych++;
    				 }
    
    	 	 }
    		while (liczba_odebranych < 100);
    		
    		liczba_odebranych = 0;
    		UART_putstring(odb);
    
    
    	//włączamy przerwanie od int0
    	GICR |= (1 << INT0);
    }


    I teraz program jak by się zacinał i działał co 3-6 naciśnięć guzika na pilocie i wyświetla mi same 0 i ze dwa razy 7 i znowu same zera;

    Ja nie potrzebuje odebrać dokładnie całego kodu pilota tylko kawałek charakterystyczny dla danego przycisku i to wysłać przez usarta. Teraz mam podpięty czujnik 38KHz. przerwanie od int0 jest ustawione na zbocze opadające. Jak ustawić przerwanie na oba zbocza ? Można tak ?
  • REKLAMA
  • #9 5939774
    mirekk36
    Poziom 42  
    omen_s napisał:
    Jak ustawić przerwanie na oba zbocza ? Można tak ?


    - na dwa zbocza nie można ustawić, ale za to można przełączać "w locie", tak aby raz była reakcja na rosnące a później na opadające. Potem już tylko mierzyć czasy pomiędzy zboczami.
  • #10 5939837
    BoskiDialer
    Poziom 34  
    omen_s: Dziwaczny ten program - brak oczekiwania ani odwołań do timera, zmienna znakh jest wykorzystywana raz jako licznik próbek i raz jako aktualna wartość (pomimo, że nie jest potem zerowana). Spróbuj może coś tego pokroju:
    #include <avr/io.h>
    
    #define TIMER_WAIT() \
    { \
    	while(!( TIFR & _BV(TOV0) )) \
    		; \
    	TIFR = _BV(TOV0); \
    }
    
    #define IR_GET() (PIND & _BV(2))
    
    const unsigned char hextab[16] = "0123456789ABCDEF";
    
    int main_(void)
    {
    
    	uart_init();
    	
    	// PORTD=wejścia/pu
    	DDRD = 0x00;
    	PORTD = 0xFF;
    
    	// wszystkie przerwania od timerów wyłączone
    	TIMSK = 0;
    	// Załączenie timera 0, F_CPU/8, przepełnienie co 2048 cykli
    	TCCR0 = _BV(CS01);
    
    	while(1)
    	{
    		// oczekiwanie na pojawienie się pierwszego zbocza
    		while(IR_GET())
    			TIMER_WAIT();
    		// zbieranie danych
    		char odb[201];
    		unsigned char kodb;
    		for(kodb = 0; kodb<200; kodb++)
    		{
    			unsigned char buff = 0;
    			unsigned char kbit;
    			for(kbit=0; kbit<4; kbit++)
    			{
    				buff <<= 1;
    				TIMER_WAIT();
    				if(IR_GET())
    					buff |= 1;
    			}
    			odb[kodb] = hextab[buff];
    		}
    
    		odb[200] = 0;
    		UART_putstring(odb);
    	}
    	return 0;
    }



    mirekk36: Rozumiem, że "ISC01=0, ISC00=1: Any logical change on INT0 generates an interrupt request." nie oznacza przerwania na oba zbocza.
  • #11 5940091
    omen_s
    Poziom 19  
    :arrow: Boski Dialer

    Twój program jest bardzo pomocny, działa i odczytuje kod pilota który jednak wciąż się momentami różni.

    000000000000000003FFFFFFFF83C3E1E0F0783C1E1E0FFF83FFE1FFF07FFC1FFF07FF83FFE0FFF83FFC1E0FFF87C3C1E0FF
    F83C3E1FFF0783FFE0FFF07FFC1E1FFF07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
    
    000000000000000003FFFFFFFF87C3C1E0F0787C3C1E0FFF83FFE1FFF07FFC1FFF0FFF83FFE0FFF87FFC1E0FFF8783C1E0FF
    F87C3C1FFF0783FFC1FFF07FFC3C1FFF07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
    
    000000000000000003FFFFFFFF0783C1E1F0F0783C1E0FFF07FFC1FFF07FF83FFE0FFF83FFC1FFF07FFC1E1FFF0783C3E1FF
    F0783C1FFF0F07FFC1FFF0FFF83C1FFF0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
    
    000000000000000003FFFFFFFF83C1E0F078783C1E0F0FFFC3FFE0FFF87FFC1FFF07FFC3FFE0FFF83FFE1E0FFF83C1E0F0FF
    F83C1E0FFF0783FFE0FFF87FFC1E0FFF83FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
    
    000000000000000007FFFFFFFF0783C1E0F0F8783C1E0FFF87FFC1FFF07FFC3FFE0FFF83FFE1FFF07FFC1E1FFF0783C1E0FF
    F0783C1FFF0F07FFC1FFF07FF83C1FFF07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
    
    000000000000000003FFFFFFFF8783C1E0F0787C3C1E0FFF83FFC0FFF07FFC1FFE0FFF83FFE1FFF07FFC1E1FFF8783C1E0FF
    F8783C1FFF0F87FFC1FFF07FFC3C1FFF07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
    
    000000000000000007FFFFFFFF8783C1E0F0F8783C1E0FFF83FFC1FFF07FFC3FFE0FFF83FFE1FFF07FFC1E1FFF0783C1E0FF
    F8783C1FFF0787FFC1FFF0FFFC3C1FFF07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
    
    000000000000000007FFFFFFFF0783C1E1F0F8783C1E0FFF87FFC1FFF07FF83FFE0FFF83FFC1FFF07FFC1E1FFF0783C1E1FF
    F0783C1FFF0707FFC1FFF07FF83C1FFF07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
    
    000000000000000007FFFFFFFF0783C1E0F078783C1E0FFF83FFC1FFF07FFC3FFE0FFF87FFE1FFF07FFC1E0FFF0783C1E1FF
    F0783C1FFF0F07FFC1FFF07FF83C1FFF07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
    
  • #12 5940185
    mirekk36
    Poziom 42  
    BoskiDialer napisał:

    Rozumiem, że "ISC01=0, ISC00=1: Any logical change on INT0 generates an interrupt request." nie oznacza przerwania na oba zbocza.


    oczywiście, że oznacza. Masz rację - coś mi się poplątało z wejściem ICP Timera - bo na nim ostatnio robiłem sobie dekodowanie IR
  • #13 5940193
    BoskiDialer
    Poziom 34  
    Heh, dokładnie ten sam standard jak w pilocie, który ja rozpracowywałem: długi stan niski (70ck) to synchronizacja/początek, krótki stan niski (4ck) to krawędź. Zaraz po synchronizacji jest dość długi stan wysoki oznaczający kod przycisku (trochę krótszy jest w moim pilocie do sygnalizacji trzymania przycisku), pomiędzy niskimi stanami jest stan wysoki o zmiennej długości: krótki (tutaj około 4ck) oznacza 0, dłuższy (około 13ck) to 1. Po rozkodowaniu będą z tego 4 bajty, z czego drugi jest negacją pierwszego, a czwarty negacją trzeciego. Teraz musisz tylko napisać odpowiedni kod, który to będzie interpretował w locie i zwracał pierwszy i trzeci (względnie drugi i czwarty) bajt.
  • Pomocny post
    #14 5946877
    rezi_ar
    Poziom 17  
    Witam
    Wygląda na kod NEC
    Link

    pozdrawiam
    rezi
  • #15 5949252
    omen_s
    Poziom 19  
    BoskiDialer poratuj kawałkiem kodu jeśli możesz. Sam nie umiem sobie z tym poradzić.

    #include <avr/io.h>
    
    #define TIMER_WAIT() \
    { \
       while(!( TIFR & _BV(TOV0) )) \
          ; \
       TIFR = _BV(TOV0); \
    }
    
    #define IR_GET() (PIND & _BV(2))
    
    const unsigned char hextab[16] = "0123456789ABCDEF";
    
    
    void timer_czekaj (unsigned int czas)
    {
    	int ile_juz;
    	for (ile_juz=0; ile_juz < czas; ile_juz++)
    	 {
    		TIMER_WAIT();
    	 }
    }
    
    // inicjalizacja portu szeregowego
    void UART_Init()
    {
       UBRRH = 0;
       UBRRL = 51;
       UCSRA = 0;
       UCSRC = _BV(URSEL)|_BV(UCSZ0)|_BV(UCSZ1);
       UCSRB = _BV(RXEN)|_BV(TXEN)|_BV(RXCIE); 
    }
    
    // wysyła znak podany jako parametr na port szeregowy
    void UART_putchar (unsigned char c)
    {
       while ( !( UCSRA & _BV(UDRE)) )
       ;
       UDR = c;
    }
    
    //wysyła string podany jako parametr na port szeregowy
    void UART_putstring(const char *tekst)
    {
      while(*tekst)
        UART_putchar(*tekst++);
    }
    
    int main(void)
    {
    
       UART_Init();
       
       // PORTD=wejścia/pu
       DDRD = 0x00;
       PORTD = 0xFF;
    
       // wszystkie przerwania od timerów wyłączone
       TIMSK = 0;
       // Załączenie timera 0, F_CPU/8, przepełnienie co 2048 cykli
       TCCR0 = _BV(CS01);
    
       while(1)
       {
          // oczekiwanie na pojawienie się pierwszego zbocza
          while(IR_GET())
             timer_czekaj(132);
          // zbieranie danych
          char odb[137];
          unsigned char kodb;
          for(kodb = 0; kodb<136; kodb++)
          {
             unsigned char buff = 0;
             unsigned char kbit;
             for(kbit=0; kbit<4; kbit++)
             {
                buff <<= 1;
                TIMER_WAIT();
                if(IR_GET())
                   buff |= 1;
             }
             odb[kodb] = hextab[buff];
          }
    
          odb[136] = 0;
          UART_putstring(odb);
       }
       return 0;
    }


    Chciałem przeczekać cały początek i odbierać tylko kod przycisku bo tam się powtarza potem często np 83C1E ale dostaje takie coś :
    
    0007FFFFFFFF0787C3C1E0F0787C3E1FFF07FFC3FFF0FFF83FFE1FFF07FFC1FFF0FFF83C1FFF0F8
    783C1FFF0F8783FFE0F07FFC3FFE0FFF83C3FFE0FFFFFFFFFFFFFFFFF
    
    0003FFFFFFFF8783C1E0F0787C3C1E0FFF83FFE1FFF07FFC3FFF0FFF83FFE1FFF07FFC1E1FFF878
    3C1E0FFF8783C1FFF0787FFC1FFF07FFC3C1FFF07FFFFFFFFFFFFFFFF
    
    0000000FFFFFFFFE0F0F0783C1E0F078783FFE0FFF87FFC1FFF07FF83FFE0FFF87FFC1FFF0783FF
    E1E0F0783FFE1F0F07FFC1E1FFF87FFC1FFF0F87FFC1FFFFFFFFFFFFF

    Początek mi się mocno zmienia.
  • #16 5949886
    BoskiDialer
    Poziom 34  
    Jak już wiadomo jaki to jest standard, to trzeba by zmodyfikować kod, aby mierzył długość każdego stanu. To powinno dalej ułatwić napisanie właściwego kodu. Moim kodem poratuję w ostateczności. Co do posługiwania się bezpośrednio surowymi danymi - nie tędy droga. Dane muszą zostać zinterpretowane, gdyż zawsze mogą wkraść się drobne zakłócenia typu krótszy lub dłuższy stan, podczas gdy np mieści się on w pewnych granicach i jest dopuszczalny. Nie można posługiwać się danymi w takiej postaci w jakiej są teraz.
    Pokaż może jak wygląda w surowej postaci (pierwotnym kodem) sekwencja odpowiadająca każdemu przyciskowi, na przyszłość się przyda.
  • #17 5950285
    omen_s
    Poziom 19  
    :arrow: BoskiDialer tutaj masz kody tych klawiszy (po 5 razy dla każdego) odczytane twoim kodem. A ja zaraz zacznę rozpracowywać ten standard NEC.

    1 CH-
    000000000000000003FFFFFFFF0783C1E0F078783C1E0FFF83FFC1FFF07FFC3FFE0FF
    F83FFE1FFF07FFC3E1FFF0783C1E1FFF0783C1FFF0F07FFC1FFF07FF83C1FFF0FFF
    
    000000000000000003FFFFFFFF83C1E0F0F0783C3E1F0FFF83FFE0FFF87FFC1FFF07F
    F83FFE0FFF87FFC1E0FFF83C3C1E0FFF83C1E1FFF0783FFE1FFF07FFC3E1FFF07FF
    
    000000000000000003FFFFFFFF83C1E0F078783C1E0F07FF83FFE0FFF87FFC1FFF07F
    FC3FFE0FFF83FFE1E0FFF83C3E1F0FFF83C1E0FFF0783FFE0FFF83FFC1E0FFF87FF
    
    000000000000000007FFFFFFFF0783C1E1F0F8783C1E0FFF87FFC1FFF0FFF83FFE0FF
    F87FFC1FFF07FFC1E1FFF0783C1E1FFF0783C1FFF0F07FFC1FFF0FFF83C1FFF0FFF
    
    000000000000000003FFFFFFFF0783C1E0F0F0783C1E1FFF87FFC1FFF07FFC3FFE0FF
    F83FFC1FFF07FFC3C1FFF0783C1E1FFF0783C1FFE0F07FFC1FFF0FFF83C1FFF0FFF
    
    
    2 CH
    000000000000000007FFFFFFFF0783C1E1F0F0783C1E1FFF07FFC1FFF0FFF83FFE0FF
    F87FFC1FFF0787FFE1FFF0787C1E1FFF0783FFE1E0707FFC1FFF0FFF83C1FFF0FFF
    
    000000000000000003FFFFFFFF83C3C1E0F0783C3E1E0FFF83FFE1FFF07FFC3FFE0FF
    F83FFE1FFF0783FFE0FFF8383C1E0FFF83C3FFE0F0783FFE1FFF07FFC3E1FFF07FF
    
    000000000000000003FFFFFFFF83C3C1E0F0783C1E1E0FFF83FFE0FFF07FFC1FFF07F
    F83FFE0FFF8783FFE0FFF83C3C1E0FFF83C1FFE0F0783FFC1FFF07FFC3E1FFF07FF
    
    000000000000000003FFFFFFFF83C1E0F0F0783C1E0F0FFF83FFE1FFF07FFC1FFF07F
    F83FFE0FFF8783FFE0FFF87C3C1E0FFF87C3FFE0F0787FFE1FFF07FFC3E1FFF07FF
    
    000000000000000003FFFFFFFF83C1E1F0F8783C1E0F07FF83FFE0FFF83FFC1FFF07FF
    C3FFE0FFF83C3FFE0FFF83C3E1E0FFF83C1FFF0F0783FFE0FFF87FFC1E0FFF87FF
    
    
    3 CH+
    000000000000000003FFFFFFFF8783C1E0F0787C3C1E0FFF87FFE1FFF07FFC3FFF0FF
    F83FFE1FFF07FFC1FFF0FFF83C1E0F0FFF83C1E0F0787FFC1FFF07FFC3C1FFF07FF
    
    000000000000000003FFFFFFFF83C1E0F0F87C3C1E0F0FFFC3FFE0FFF87FFE1FFF07F
    FC1FFE0FFF83FFE1FFF07FFC1E0F0F07FFC1E0F078383FFE0FFF87FFC1E0FFF87FF
    
    000000000000000003FFFFFFFF83C1E0F0F0783C1E0F0FFF83FFE0FFF87FFC1FFF07F
    FC3FFE0FFF83FFE1FFF07FFC1E1E0F07FFC1E1E0F0783FFE1FFF07FFC1E0FFF87FF
    
    000000000000000003FFFFFFFF83C3E1F070783C1E1F0FFF83FFE0FFF87FFC1FFF07F
    F83FFE0FFF87FFC1FFF07FFC3C1E0F07FFC3E1E0F0783FFE0FFF07FFC3E0FFF87FF
    
    000000000000000007FFFFFFFF0783C1E0F0F8783C1E0FFF83FFC1FFF07FFC3FFE0FF
    F83FFE1FFF07FFC1FFF0FFF83C1E1F0FFF83C1E0F0F07FFC1FFF07FF83C1FFF07FF
    
    
    4 |<<
    000000000000000007FFFFFFFF8783C1E0F078783C1E0FFF87FFC1FFF07FFC3FFE0FF
    F83FFE1FFF0783C3FFF0F0783C1FFF0F87FFC1FFF0F87FFC1FFF07FF83C1FFF07FF
    
    000000000000000003FFFFFFFF87C3C1E0F0783C3C1E0FFF83FFC1FFF07FFC3FFE0FF
    F83FFE1FFF0783C1FFF078783C1FFF0F87FFC1FFF0F83FFC1FFF07FFC3C1FFF07FF
    
    000000000000000003FFFFFFFF83C1E1E0F0783C1E0F0FFF83FFE0FFF87FFC1FFF07F
    FC3FFE0FFF87C3E1FFF0787C1E1FFF0783FFE1FFF0783FFE1FFF07FFC1E0FFF07FF
    
    000000000000000007FFFFFFFF8783C1E1F0787C3C1E0FFF87FFC1FFF07FFC3FFE0FF
    F83FFE0FFF0783C1FFE0F0783C1FFF0F07FFC1FFF0F87FFC1FFF07FFC3C1FFF07FF
    
    000000000000000003FFFFFFFF83C1E0F078783C1E0F0FFFC3FFE0FFF87FFE1FFF07F
    FC3FFE0FFF83C3E0FFF0783C1E1FFF0783FFE0FFF0783FFE0FFF87FFC1E0FFF83FF
    
    
    5 >>|
    000000000000000003FFFFFFFF83C3C1E0F0783C3C1E0FFF83FFE1FFF07FFC1FFF0FF
    F83FFE0FFF8783C1E0F0787C3FFE0F0FFFC3FFE0FFF87FFE1FFF07FFC1E1FFF07FF
    
    000000000000000003FFFFFFFF83C1E1F0F0783C1E0F07FF83FFE1FFF87FFC1FFF07F
    FC3FFE0FFF83C3C1E0F0783C3FFE0F07FFC3FFE07FF83FFE0FFF07FFC1E0FFF87FF
    
    000000000000000003FFFFFFFF83C3E1E0F0787C1E1E0FFF83FFE0FFF07FFC1FFF0FF
    F83FFE0FFF0783C1E0F078783FFE0F07FFC3FFE0FFF87FFE1FFF07FFC3E1FFF07FF
    
    000000000000000003FFFFFFFF83C1E0F078783C1E0F07FF83FFE0FFF87FFC1FFF07F
    FC1FFE0FFF83C3E1F0F0783C1FFF0F07FFC1FFF0FFF83FFE0FFF87FFC1E0FFF87FF
    
    000000000000000003FFFFFFFF83C3E1E0F0783C1E0F0FFF83FFE0FFF83FFC1FFF07F
    FC3FFE0FFF87C3C1E0F0783C1FFE0F07FFC3FFE0FFF83FFE1FFF07FFC1E1FFF07FF
    
    
    6 >||
    000000000000000007FFFFFFFF0783C1E1F0F0783C1E0FFF87FFC1FFF07FF83FFE0FF
    F87FFC1FFF07FFC3FFE0F0783C3E1FFF0783C3E1FFF07FFC1FFF07FF83C1FFF07FF
    
    000000000000000003FFFFFFFF87C1E1E0F0783C1E0F0FFF83FFE0FFF07FFC1FFF0FF
    F83FFE0FFF87FFC1FFF0787C3C1E0FFF87C1E1E0FFF83FFE0FFF07FFC3E1FFF07FF
    
    000000000000000003FFFFFFFF83C1E1F0F0783C1E1F07FF83FFE0FFF87FFC1FFF07F
    F81FFE0FFF83FFC1FFF0783C3E1E0FFF83C3E0F0FFF83FFE0FFF87FFC1E1FFF83FF
    
    000000000000000003FFFFFFFF87C3C1E0F0783C3C1E0FFF83FFE1FFF07FFC1FFF0FF
    F83FFE1FFF87FFC1FFF078783C1E0FFF83C3C1E0FFF87FFC1FFF07FFC3E1FFF07FF
    
    000000000000000007FFFFFFFF0783C1E1F0F8783C1E0FFF87FFC1FFF0FFFC3FFE0FF
    F83FFE1FFF07FFC3FFE0F0783C1E1FFF0783C1E1FFF07FFC1FFF0FFF83C1FFF07FF
    
    
    7 –
    000000000000000003FFFFFFFF83C3E1F0F0783C1E1F0FFF83FFE0FFF07FFC1FFF0FF
    F83FFE0FFF87FFC1FFF07FFC1E1E0F0783C1E1F0F07FFC1FFF0FFF83FFE0FFF87FF
    
    000000000000000003FFFFFFFF87C1E1E0F0783C3E1E0FFF83FFE0FFF07FFC1FFF0FF
    F83FFE0FFF87FFC1FFF07FFC3C1E0F0783C1E1E0F07FFC3FFE0FFF83FFE0FFF07FF
    
    000000000000000003FFFFFFFF83C1E0F0F8783C1E0F07FFC3FFE0FFF83FFC1FFF07F
    FC3FFE0FFF83FFE1FFF07FFC1E1F0F0783C1E0F0F87FFC1FFF0FFFC3FFE0FFF87FF
    
    000000000000000003FFFFFFFF83C1E1F0F0783C1E1F0FFF83FFE1FFF87FFC1FFF07F
    FC3FFE0FFF83FFC1FFF07FFC3C1E0F0783C3E1E0F07FFC1FFE0FFF83FFE1FFF07FF
    
    000000000000000003FFFFFFFF83C1E1F0F8783C1E0F0FFFC3FFE0FFF87FFC1FFF07F
    FC1FFE0FFF83FFE1FFF07FFC1E0F0F0783C1E0F0F87FFC1FFF0FFFC3FFE0FFF87FF
    
    
    8 +
    000000000000000003FFFFFFFF83C1E0F0787C3C1E0F07FFC3FFE0FFF83FFC1FFF07F
    FC1FFE0FFF83FFE1E0FFF83C1FFF0F0783C1E0FFF0783FFE0F07FF83FFE0FFF83FF
    
    000000000000000007FFFFFFFF0783C1E0F0F8783C1E0FFF83FFC1FFF07FFC3FFE0FF
    F83FFE1FFF07FFC3E0FFF0783FFE1F0F8783C1FFF0787FFC1E0FFF87FFC1FFF07FF
    
    000000000000000007FFFFFFFF8783C1E0F078783C1E0FFF83FFC1FFF07FFC3FFE0FF
    F83FFE0FFF07FFC3E1FFF0783FFE1F0F8383C1FFF0787FFC1E0FFF87FFC1FFF07FF
    
    000000000000000007FFFFFFFF8783C1E0F0787C3C1E0FFF83FFC1FFF07FFC3FFE0FF
    F83FFE1FFF07FFC1E1FFF0783FFE0F0F0783C1FFF0F87FFC1E0FFF87FFC1FFF0FFF
    
    000000000000000007FFFFFFFF0783C1E1F0F0783C1E0FFF87FFC1FFF0FFF83FFE0FF
    F87FFC1FFF07FFC3E1FFF0783FFE1F0F0783C3FFF0F07FFC1E1FFF83FFC1FFF07FF
    
    
    9 EQ
    000000000000000003FFFFFFFF8783C1E0F0787C3C1E0FFF87FFE1FFF07FFC1FFF0FF
    F83FFE0FFF07FFC1E1F0FFF83C1E0F0F8783FFE0FFF8783FFE0FFF87FFC1FFF07FF
    
    000000000000000003FFFFFFFF83C1E1F0F8783C1E0F07FFC3FFE0FFF87FFC1FFF07F
    FC1FFE0FFF83FFE1E0F07FFC1E0F0F0783C1FFF07FF83C1FFF0FFFC3FFE0FFF83FF
    
    000000000000000003FFFFFFFF87C3E1E0F0783C3E1E0FFF83FFE1FFF07FFC1FFF0FF
    F83FFE0FFF87FFC1E0F0FFFC3C1E0F0783C1FFE0FFF87C3FFE0FFF83FFE1FFF07FF
    
    000000000000000003FFFFFFFF83C1E0F0F8783C1E0F07FF83FFE0FFF83FFC1FFF07F
    FC3FFE0FFF83FFE1F0F07FFC1E1F0F8783C1FFF0FFF83C1FFF0FFF83FFE0FFF87FF
    
    000000000000000007FFFFFFFF0783C1E0F0F0783C1E0FFF87FFC1FFF0FFFC3FFE0FF
    F83FFE1FFF07FFC3E0E0FFF83C3E1F0F0783FFE0FFF0783FFE0FFF07FFC1FFF07FF
    
    
    10 0
    000000000000000003FFFFFFFF83C3E1E0F0787C1E1E0FFF83FFE1FFF07FFC1FFF0FF
    F83FFE0FFF8783FFE0FFF8783FFE0F0787C3FFE0F0787FFE1E0FFF83FFE1FFF07FF
    
    000000000000000003FFFFFFFF83C3E1F0F0783C1E1F0FFF83FFE0FFF87FFC1FFF0FF
    F83FFE0FFF83C3FFE0FFF83C3FFE0F0783C1FFF0F0783FFE1F0FFF83FFE0FFF83FF
    
    000000000000000003FFFFFFFF83C3E1E0F0783C3E1E0FFF83FFE1FFF07FFC1FFF0FF
    F83FFE0FFF8783FFE0FFF8783FFE0F0787C3FFE0F0783FFE1E0FFF83FFE1FFF07FF
    
    000000000000000007FFFFFFFF0783C1E0F070783C1E0FFF87FFC1FFF0FFF83FFE0FF
    F87FFC1FFF0783FFE1FFF0783FFE1F0F0783FFE1F0F07FFC1E1FFF87FFC1FFF0FFF
    
    000000000000000003FFFFFFFF83C1E1F0F0783C1E0F0FFF83FFE0FFF87FFC1FFF0FF
    F83FFE0FFF83C1FFE0FFF83C1FFE0F0783C1FFF0F0783FFE1F07FF83FFE0FFF87FF
    
    
    11 100+
    000000000000000003FFFFFFFF8783C1E0F078783C1E0FFF87FFC1FFF07FFC3FFE0FF
    F83FFE0FFF07FFC1E1F0FFF83FFE1F078783C1FFF07FFC3C1E0FFF83FFC1FFF07FF
    
    000000000000000003FFFFFFFF83C1E0F0F87C3C1E0F07FFC3FFE0FFF87FFE1FFF07F
    FC1FFE0FFF83FFE1F0F07FFC1FFF0F0783C1E0FFF87FFC1E0F0FFFC3FFE0FFF87FF
    
    000000000000000003FFFFFFFF83C3C1E0F0783C3C1E0FFF83FFE1FFF07FFC1FFE0FF
    F83FFE1FFF07FFC1E0F0FFF83FFE0F0F8783C1FFF07FFC3C1E0FFF83FFE1FFF07FF
    
    000000000000000003FFFFFFFF83C3C1E0F0783C3E1E0FFF83FFE1FFF07FFC1FFF0FF
    F83FFE1FFF87FFC0E0F0FFFC3FFE0F0783C1C1FFF07FFC3E1E0FFF83FFE1FFF07FF
    
    000000000000000003FFFFFFFF83C1E1E0F0783C1E1F0FFF83FFE0FFF87FFC1FFF07F
    FC3FFE0FFF83FFE1E0F07FFC1FFE0F0783C3E1FFF07FFC1E1F07FF83FFE1FFF07FF
    
    
    12 200+
    000000000000000003FFFFFFFF87C3E1E0F0787C3E1E0FFF83FFE1FFF07FFC1FFF0FF
    F83FFE0FFF07FFC1E0FFF87FFC1E0F0787C3C1FFF0787C1FFE0FFF83FFE1FFF07FF
    
    000000000000000003FFFFFFFF87C3E1E0F0787C1E1E0FFF83FFE1FFF07FFC1FFF0FF
    F83FFE0FFF07FFC1E0FFF87FFC1E0F0783C3C1FFF0783C3FFE07FF83FFE1FFF07FF
    
    000000000000000003FFFFFFFF83C1E0F0F0783C1E0F0FFF83FFE1FFF87FFC1FFF07F
    FC3FFE0FFF87FFC1E0FFF83FFC1E0F0783C3E1FFF0783C1FFF0FFF83FFE0FFF87FF
    
    000000000000000003FFFFFFFF83C3C1E0F0783C3C1E0FFF83FFE1FFF07FFC1FFF0FF
    F83FFE0FFF87FFC1E0FFF87FFC1E0F0F83C3C1FFF0787C1FFE0FFF83FFE1FFF07FF
    
    000000000000000003FFFFFFFF83C1E1F0F0783C1E0F0FFF83FFE0FFF87FFC1FFF0FF
    FC3FFE0FFF87FFC1E0FFF83FFE0E0F0783C1E0FFF0783C1FFF0FFF83FFE0FFF87FF
    
    
    13 1
    000000000000000003FFFFFFFF83C1E1F0F0783C1E0F0FFF83FFE0FFF87FFC1FFF0FF
    FC3FFE0FFF87C3E1FFF07FFC1E1F0F0783FFE1FFF0783C1FFF0FFF83FFE0FFF87FF
    
    000000000000000003FFFFFFFF8783C1E0F0F87C3C1E0FFF87FFE1FFF07FFC3FFE0FF
    F83FFE1FFF0783C1FFF0FFF83C1E0F0F87FFC1FFF0F8783FFE0FFF83FFC1FFF07FF
    
    000000000000000003FFFFFFFF8783C1E0F0783C3C1E0FFF87FFE1FFF07FFC3FFE0FF
    F83FFE1FFF0783C1FFF0FFF83C1E0F0F07FFC1FFF0F8383FFE0FFF83FFC1FFF07FF
    
    000000000000000007FFFFFFFF8783C1E0F0F87C3C1E0FFF87FFC1FFF07FFC3FFE0FF
    F83FFE1FFF0783C1FFE0FFF83C1E1F0F07FFC1FFF0F8783FFE0FFF83FFC1FFF07FF
    
    000000000000000003FFFFFFFF87C3C1E0F0787C3C1E0FFF83FFE1FFF07FFC3FFE0FF
    F83FFE1FFF0783C1FFF0FFF83C1E0F0F87FFC1FFF0787C3FFE0FFF83FFE1FFF07FF
    
    
    14 2
    000000000000000007FFFFFFFF0783C1E0F0F8383C1E0FFF87FFC1FFF07FFC3FFE0FF
    F83FFE1FFF0787C3E1FFF07FFC1E1F0F07FFC1FFF0FFF83C1E0FFF87FFC1FFF07FF
    
    000000000000000003FFFFFFFF8783C1E0F0787C3C1E0FFF83FFC1FFF07FFC3FFE0FF
    F83FFE1FFF0783C1E0FFF87FFC1E0F0F87FFC1FFF07FFC3C1E0FFF87FFC1FFF07FF
    
    000000000000000007FFFFFFFF0783C1E0F078783C1E0FFF87FFC1FFF07FFC3FFE0FF
    F87FFE1FFF0783C1E1FFF07FFC1E0F0F07FFC1FFF0FFF83C1E0FFF87FFC1FFF07FF
    
    000000000000000003FFFFFFFF83C1E1F0F8783C1E0F0FFF83FFE0FFF87FFC1FFF07F
    FC3FFE0FFF83C1E1E07FF83FFE1F0F0783FFE0FFF87FFC1E1F07FF83FFE0FFF83FF
    
    000000000000000007FFFFFFFF0783C1E0F0F0783C1E0FFF87FFC1FFF07FF81FFE0FF
    F87FFC1FFF0783C3E1FFF07FFC1E0F0F07FFC1FFF0FFF83C1E0FFF87FFC1FFF0FFF
    
    
    15 3
    000000000000000003FFFFFFFF87C3C1E0F0787C3C1E0FFF87FFE1FFF07FFC3FFE0FF
    F83FFE1FFF0783FFE1FFF07FFC1FFF0F87FFC1E0FFF87C3C1E0F07FFC3E1FFF07FF
    
    000000000000000003FFFFFFFF83C1E1F0F0783C1E1F0FFF83FFE0FFF87FFC1FFF07F
    FC3FFE0FFF87C3FFE0FFF83FFE1FFF0783FFE1F0FFF83C1E0F0F07FFC1E0FFF87FF
    
    000000000000000007FFFFFFFF8783C1E0F0787C3C1E0FFF83FFE1FFF07FFC3FFE0FF
    F83FFE0FFF0783FFE1FFF07FFC1FFF0F87FFC1E0FFF8783C1E0F07FFC3C1FFF07FF
    
    000000000000000003FFFFFFFF83C1E0F0F8783C1E0F07FF83FFE0FFF87FFC1FFF07F
    FC3FFE0FFF83C3FFF0FFF83FFE1FFF0783FFE0F0FFF83C1E0F0F87FFC1E0FFF87FF
    
    000000000000000003FFFFFFFF8783C1E0F0787C3C1E0FFF83FFC1FFF07FFC3FFE0FF
    F83FFE1FFF0783FFE1FFF87FFC1FFF0787FFC1E0FFF87C3C1E0F07FFC3C1FFF07FF
    
    
    16 4
    000000000000000003FFFFFFFF87C3C1E0F0783C3C1E0FFF83FFE1FFF07FFC1FFF0FF
    F83FFE1FFF8783C1E0FFF87C3C1E0F0FFFC3FFE0FFF83C3FFE0FFF83FFE1FFF07FF
    
    000000000000000007FFFFFFFF8383C1E0F0F83C3C1E0FFF87FFE1FFF07FFC3FFE0FF
    F83FFE1FFF0783C1E1FFF0783C1E0F0FFF83FFE0FFF8783FFE0FFF87FFC1FFF07FF
    
    000000000000000007FFFFFFFF8783C1E0F0F8783C1E0FFF87FFC1FFF07FFC3FFE0FF
    F83FFE1FFF0783C1E1FFF0783C1E1F0FFF83FFE0FFF8783FFE0FFF87FFC1FFF07FF
    
    000000000000000003FFFFFFFF83C3E1E0F0783C3E1F0FFF83FFE1FFF07FFC1FFF0FF
    F83FFE0FFF8383C1E0FFF8383C1E0F07FFC1FFE0FFF83C3FFE0FFF83FFE1FFF07FF
    
    000000000000000003FFFFFFFF87C3E1E0F0783C1E1E0FFF83FFE1FFF07FFC1FFF0FF
    F83FFE0FFF0783C1E0FFF8783C1E0F07FFC3FFE0FFF87C3FFE0FFF83FFE0FFF07FF
    
    
    17 5
    000000000000000003FFFFFFFF83C3C1E0F0787C3C1E0FFF83FFE1FFF07FFC3FFE0FF
    F83FFE1FFF0783C1FFF07FF83FFE0F0787C3FFE0FFF87C3C1E0FFF87FFE1FFF07FF
    
    000000000000000003FFFFFFFF8783C1E1F078783C1E0FFF87FFC1FFF07FFC3FFE0FF
    F83FFC1FFF0783C3FFE0FFF83FFE1F0F0783FFE1FFF8783C1E1FFF87FFC1FFF07FF
    
    000000000000000003FFFFFFFF83C1E1F0F0783C1E0F0FFF83FFE0FFF87FFC1FFF07F
    FC3FFE0FFF83C3E1FFF07FFC1FFF0F0783C1FFF0FFF83C1E0F0FFF83FFE0FFF87FF
    
    000000000000000003FFFFFFFF83C1E1E0F0783C1E1F0FFF83FFE1FFF07FFC1FFF0FF
    F83FFE0FFF8783C1FFF07FFC3FFE0F0787C3FFE0FFF87C3E1F0FFF83FFE0FFF07FF
    
    000000000000000003FFFFFFFF87C3C1E0F0787C1E1E0FFF83FFE1FFF07FFC1FFF0FF
    F83FFE0FFF0383C1FFF0FFF83FFE0F0783C3FFE0FFF83C3E1E0FFF83FFE1FFF07FF
    
    
    18 6
    000000000000000007FFFFFFFF8783C1E1F0F87C3C1E0FFF87FFC1FFF07FFC3FFE0FF
    F83FFE1FFF0783FFE1E07FF83FFE1F0FFF83C1FFF0F87FFC1E0F0FFFC3C1FFF07FF
    
    000000000000000003FFFFFFFF8783C1E0F078783C1E0FFF87FFC1FFF07FFC3FFE0FF
    F83FFE1FFF0783FFE1F0FFF83FFE0F0FFF83C1FFF0783FFC1E0F07FF83C1FFF07FF
    
    000000000000000003FFFFFFFF8783C1E0F0F8783C1E0FFF87FFC1FFF07FFC3FFE0FF
    F83FFC1FFF0783FFE1E0FFF83FFE1F0FFF83C1FFF0F87FFC1E0F0FFFC3C1FFF07FF
    
    000000000000000003FFFFFFFF87C3C1E1F0787C3C1E0FFF87FFC1FFF07FFC3FFE0FF
    F83FFE1FFF0783FFE1E0FFF83FFE1F0FFF83C1FFF0787FFC1E0F0FFFC3C1FFF07FF
    
    000000000000000003FFFFFFFF83C1E1E0F0783C3E1E0FFF83FFE0FFF07FFC1FFF0FF
    F83FFE0FFF8783FFE0F07FFC3FFE0F07FFC3E1FFF0783FFE1F0F07FFC3E0FFF07FF
    
    
    19 7
    000000000000000003FFFFFFFF83C1E1F0F0783C1E1F0FFF83FFE0FFF87FFC1FFF07F
    FC3FFE0FFF87C3FFE0F0783C1E1FFF0783FFE0F0FFF83FFE1FFF07FFC1E0FFF87FF
    
    000000000000000003FFFFFFFF0783C1E0F0F0783C1E0FFF87FFC1FFF07FFC3FFE0FF
    F87FFE1FFF0783FFE1F0F0783C1FFF0F07FFC1E1FFF03FFC1FFF07FF83C1FFF07FF
    
    000000000000000003FFFFFFFF8783C1E0F0787C3C1E0FFF83FFC1FFF07FFC3FFE0FF
    F83FFE1FFF0783FFE1F0F0783C1FFF0787FFC1E0FFF83FFC1FFF0FFFC3C1FFF07FF
    
    000000000000000003FFFFFFFF87C3E1E0F0783C1E1F0FFF83FFE1FFF07FFC1FFF0FF
    F83FFE0FFF8783FFE0F078783C1FFF0787FFC1E0FFF83FFE1FFF07FFC1E1FFF07FF
    
    000000000000000003FFFFFFFF8783C1E0F0783C3C1E0FFF87FFE1FFF07FFC1FFF0FF
    F83FFE0FFF0783FFE0F0F0783C1FFF0787FFC1E0FFF87FFC1FFF07FFC3C1FFF07FF
    
    
    20 8
    000000000000000007FFFFFFFF0783C1E1F0F8383C1E0FFF87FFC1FFF07FF83FFE0FF
    F87FFC1FFF0783FFE1E0F07FFC3E1FFF0783FFE0F0FFF83FFE1F07FF83C1FFF07FF
    
    000000000000000003FFFFFFFF83C3C1E0F0783C3E1E0FFF83FFE1FFF07FFC1FFF0FF
    F83FFE0FFF8783FFE0F0F87FFC1E0FFF87C3FFE0F07FFC3FFE0F07FFC3E1FFF07FF
    
    000000000000000003FFFFFFFF8783C1E0F0787C3E1E0FFF83FFE1FFF07FFC1FFF0FF
    F83FFE1FFF8783FFE0F0F87FFC1E0FFF8783FFE0F07FFC3FFE0F07FFC3C1FFF07FF
    
    000000000000000003FFFFFFFF8783C1E0F0787C3C1E0FFF83FFE1FFF07FFC3FFF0FF
    F83FFE0FFF0783FFE1F0F87FFC1E0FFF8783FFE0F07FF83FFE0F07FFC3E1FFF07FF
    
    000000000000000003FFFFFFFF83C3E1E0F0783C1E1F0FFF83FFE0FFF07FFC1FFF07F
    F83FFE0FFF83C3FFE0F0787FFC1E0FFF83C3FFE0F07FFC3FFE0F07FFC1E1FFF07FF
    
    
    21 9
    000000000000000003FFFFFFFF83C1E0F0F87C3C1E0F0FFFC3FFE0FFF87FFE1FFF07F
    FC1FFE0FFF83C1FFE0F07FFC1E1F0FFF83C1FFF0F87FFC1E0FFF87FFC1E0FFF87FF
    
    000000000000000003FFFFFFFF83C3E1F0F0783C1E1F0FFF83FFE0FFF87FFC1FFF07F
    FC3FFE0FFF87C1FFE0F07FFC3E1E0FFF83C3FFF0F07FFC1E1FFF87FFC1E1FFF87FF
    
    000000000000000003FFFFFFFF83C1E0F0F0783C1E0F07FF83FFE0FFF83FFC1FFF07F
    F83FFE0FFF87C3FFE0F07FFC3E1E0FFF83C1FFF0F07FFC1E0FFF87FFC1E1FFF87FF
    
    000000000000000003FFFFFFFF0783C1E0F0F0783C1E0FFF87FFC1FFF07FFC3FFE0FF
    F83FFE1FFF0787FFE1F0FFF83C3E1FFF0783FFE1F0FFF83C1FFF0FFF83C1FFF07FF
    
    000000000000000003FFFFFFFF83C1E1F078783C1E1F07FFC3FFE0FFF83FFC1FFF07F
    FC3FFE0FFF83C3FFE0F07FFC1E1E07FF83C1FFF0F07FFC1E0FFF87FFC1E0FFF87FF
    
  • #18 5950624
    BoskiDialer
    Poziom 34  
    Pierwsza wiadomość, dobra, to da się to zdekodować. Nie wystąpiły żadne zakłócenia, kody poszczególnych przycisków:
    CH-		45 00 (wszystkie sekwencje z poprzednich postów są od tego przycisku)
    CH 		46 00
    CH+ 	47 00
    |<< 	44 00
    >>| 	40 00
    >|| 	43 00
    – 		07 00
    + 		15 00
    EQ 		09 00
    0 		16 00
    100+	19 00
    200+	0D 00
    1		0C 00
    2	 	18 00
    3	 	5E 00
    4	 	08 00
    5	 	1C 00
    6	 	5A 00
    7	 	42 00
    8	 	52 00
    9	 	4A 00

    Druga wiadomość, też dobra, to analiza jest stosunkowo łatwa - pomierzyć każdy stan (można na bieżąco), odpowiadające fragmenty muszą mieścić się w pewnych ramach, prosta walidacja i można zebrać dane. Można też napisać kod działający całkowicie stanowo na przerwaniu od timera, wtedy nie trzeba będzie czynnie odbierać danych.
  • #19 5950833
    omen_s
    Poziom 19  
    Czyli jest to standard Nec tak ?
  • #20 5950905
    BoskiDialer
    Poziom 34  
    Tak, jest to ten standard.
  • #21 5951867
    omen_s
    Poziom 19  
    Teraz zwraca mi same 0, nie umiem skonfigurować prawidłowo timera 2. Przy założeniu że timer2 będzie pracować z taką prędkością jak timer 0 to wystarczy że będę sprawdzać czy długość jest <=1 lub >=2 czy tak ? Według strony którą podał rezi_ar za co dziękuję wynika że dla 0=1120us a dla 1=2240us więc to sprawdzanie powinno wystarczyć.

    #include <avr/io.h>
    #include <avr/interrupt.h>
    
    #define TIMER_WAIT() \
    { \
       while(!( TIFR & _BV(TOV0) )) \
          ; \
       TIFR = _BV(TOV0); \
    }
    
    #define IR_GET() (PIND & _BV(2))
    
    const unsigned char hextab[16] = "0123456789ABCDEF";
    unsigned char czas_timer2 = 0;
    
    // inicjalizacja portu szeregowego
    void UART_Init()
    {
       UBRRH = 0;
       UBRRL = 51;
       UCSRA = 0;
       UCSRC = _BV(URSEL)|_BV(UCSZ0)|_BV(UCSZ1);
       UCSRB = _BV(RXEN)|_BV(TXEN)|_BV(RXCIE); 
    }
    
    // wysyła znak podany jako parametr na port szeregowy
    void UART_putchar (unsigned char c)
    {
       while ( !( UCSRA & _BV(UDRE)) )
       ;
       UDR = c;
    }
    
    //wysyła string podany jako parametr na port szeregowy
    void UART_putstring(const char *tekst)
    {
      while(*tekst)
        UART_putchar(*tekst++);
    }
    
    
    ISR(TIMER2_OVF_vect)
    {
    	czas_timer2++;
    }
    
    
    int main(void)
    {
       UART_Init();
       
       // PORTD=wejścia/pu
       DDRD = 0x00;
       PORTD = 0xFF;
    
       // wszystkie przerwania od timerów wyłączone
       TIMSK = 0;
       // Załączenie timera 0, F_CPU/8, przepełnienie co 2048 cykli
       TCCR0 = _BV(CS01);
    
       // Załączenie timera 2, F_CPU/8
       TCCR2 |= _BV(CS21);
    
       while(1)
       {
          // oczekiwanie na pojawienie się pierwszego zbocza
          while(IR_GET())
    			TIMER_WAIT();
          // zbieranie danych
          char odb[137];
          unsigned char kodb;
          for(kodb = 0; kodb<136; kodb++)
          {
             unsigned char buff = 0;
             unsigned char kbit;
             for(kbit=0; kbit<4; kbit++)
             {
                buff <<= 1;
                
    			//włączenie timer2
    			TIMSK |= _BV(TOIE2);
    			
    			TIMER_WAIT();
    
                if(IR_GET())
    			{
                	if ( czas_timer2 <= 1)
    				{
    					czas_timer2 = 0;
    					//wyłączenie timer2
    					TIMSK |= (0<<TOIE2);
    				}
    				
    				if ( czas_timer2 >= 2)
    				{
    					buff |= 1;
    					czas_timer2 = 0;
    					//wyłączenie timer2
    					TIMSK |= (0<<TOIE2);
    				}
    			}
             }
             odb[kodb] = hextab[buff];
          }
    
          odb[136] = 0;
          UART_putstring(odb);
       }
       return 0;
    }
    


    Pozdrawiam
  • #22 5953263
    BoskiDialer
    Poziom 34  
    Sprawdzanie <= 1 a >=2 jest błędne. Na podanej stronie zakłada się, że T=560us, timer pracuje akurat o wiele szybciej, więc trzeba te warunki skorygować. Musisz mierzyć jak długo panuje jeden stan, doświadczalnie na zebranych danych ustaliłem dla swojego prostego dekodera (którym uzyskałem kody), że start jest, kiedy stan niski jest przez od 50 do 90 próbek, sync od 25 dla 50, krótki impuls od 2 do 6, krótka przerwa (kodująca 0) od 2 do 6 a długa przerwa (kodująca 1) od 7 do 20. Najpierw zmierz długość każdego stanu, dalej jest tylko z górki.

    ps. Timera nie załącza się zmieniając flagę w TIMSK - to jest tylko zezwolenie na przerwanie, które w moim kodzie nie jest potrzebne. Dodatkowo kasowanie bitu nie zapisuje się jako
    a |= (0<<b); // ŹLE
    które siłą rzeczy nigdy nie będzie działać. Poprawne jest:
    a &=~(1<<b); // dobrze
  • #23 5954671
    omen_s
    Poziom 19  
    Chodzi o coś w tym kierunku ? Wciąż zwraca mi zera.

    #include <avr/io.h>
    #include <avr/interrupt.h>
    
    #define TIMER_WAIT() \
    { \
       while(!( TIFR & _BV(TOV0) )) \
          ; \
       TIFR = _BV(TOV0); \
    }
    
    #define IR_GET() (PIND & _BV(2))
    
    const unsigned char hextab[16] = "0123456789ABCDEF";
    unsigned char czas_timer2 = 0;
    
    char czas_start = 0;
    char czas_sync = 0;
    char czas_impuls = 0;
    
    // inicjalizacja portu szeregowego
    void UART_Init()
    {
       UBRRH = 0;
       UBRRL = 51;
       UCSRA = 0;
       UCSRC = _BV(URSEL)|_BV(UCSZ0)|_BV(UCSZ1);
       UCSRB = _BV(RXEN)|_BV(TXEN)|_BV(RXCIE); 
    }
    
    // wysyła znak podany jako parametr na port szeregowy
    void UART_putchar (unsigned char c)
    {
       while ( !( UCSRA & _BV(UDRE)) )
       ;
       UDR = c;
    }
    
    //wysyła string podany jako parametr na port szeregowy
    void UART_putstring(const char *tekst)
    {
      while(*tekst)
        UART_putchar(*tekst++);
    }
    
    
    ISR(TIMER2_OVF_vect)
    {
    	czas_timer2++;
    }
    
    
    int main(void)
    {
       UART_Init();
       
       // PORTD=wejścia/pu
       DDRD = 0x00;
       PORTD = 0xFF;
    
       // wszystkie przerwania od timerów wyłączone
       TIMSK = 0;
       // Załączenie timera 0, F_CPU/8, przepełnienie co 2048 cykli
       TCCR0 = _BV(CS01);
    
       // Załączenie timera 2, F_CPU/8
       TCCR2 |= _BV(CS21);
    
       while(1)
       {
          // oczekiwanie na pojawienie się pierwszego zbocza
          while(IR_GET())
    			TIMER_WAIT();
          // zbieranie danych
          char odb[137];
          unsigned char kodb;
          for(kodb = 0; kodb<136; kodb++)
          {
             unsigned char buff = 0;
             unsigned char kbit;
    
    			czas_timer2 = 0;
    			
    			      while(IR_GET())
    					    TIMER_WAIT();
    	
    				if ( ((czas_timer2 >= 50) && (czas_timer2 <=90)) || czas_start )
    				{
    					czas_start = 1;
    					czas_timer2 = 0;
    
    					     while(IR_GET())
    							   TIMER_WAIT();
    
    					if ( ((czas_timer2 >= 25) && (czas_timer2 <=50)) || czas_sync )
    					{
    						czas_sync = 1;
    						czas_timer2 = 0;
    						
    						      while(IR_GET())
    								    TIMER_WAIT();
    
    						if ( ((czas_timer2 >= 2) && (czas_timer2 <=6)) || czas_impuls )
    						{
    							czas_impuls = 1;
    							czas_timer2 = 0;
    
             					for(kbit=0; kbit<4; kbit++)
             					{
               					
    								buff <<= 1;
                
    								czas_timer2 = 0;
    
    
    								TIMER_WAIT();
    
    								if ((czas_timer2 >= 2) && (czas_timer2 <=6))
    								{
    									czas_timer2 = 0;
    
    								}
    				
    								if ((czas_timer2 >= 7) && (czas_timer2 <=20))
    								{
    									buff |= 1;
    									czas_timer2 = 0;
    								}
    
    							}
    				
    						}
    					}
    				}
    			
            
             odb[kodb] = hextab[buff];
          }
    
          odb[136] = 0;
          UART_putstring(odb);
       }
       return 0;
    }
    
  • Pomocny post
    #24 5954780
    BoskiDialer
    Poziom 34  
    Momentami odnoszę wrażenie, że nie wiesz co piszesz. Do pomiaru długości nie potrzebujesz dodatkowego timera oprócz tego t0. Wystarczy, że będziesz sprawdzał co określony czas, jeśli na wejściu będzie ten sam stan co poprzednio, to będziesz zwiększał pomocniczą zmienną. Jeśli stan się zmieni, to dopiero zapiszesz wartość do np.tablicy, żeby później interpretować sygnał.

    Chociaż dobrze kombinujesz, brakuje kilku negacji, nie wiem czy t2 rusza, nie chce mi się sprawdzać.

    Dodano po 24 [minuty]:

    Nie wnikając w ład, funkcjonalność, czytelność, to można to zrobić mniej więcej tak:
    #include <avr/io.h> 
    #include <avr/interrupt.h>
    #include <stdint.h>
    
    #define TIMER_WAIT() \
    { \
    	while(!( TIFR & _BV(TOV0) )) \
    		; \
    	TIFR = _BV(TOV0); \
    } 
    
    #define IR_GET() (PIND & _BV(2))
    
    // inicjalizacja portu szeregowego 
    void UART_Init() 
    { 
    	UBRRH = 0; 
    	UBRRL = 51; 
    	UCSRA = 0; 
    	UCSRC = _BV(URSEL)|_BV(UCSZ0)|_BV(UCSZ1); 
    	UCSRB = _BV(RXEN)|_BV(TXEN)|_BV(RXCIE); 
    } 
    
    // wysyła znak podany jako parametr na port szeregowy 
    void UART_putchar(unsigned char c) 
    { 
    	while ( !( UCSRA & _BV(UDRE)) ) 
    		; 
    	UDR = c; 
    } 
    
    //wysyła string podany jako parametr na port szeregowy 
    void UART_putstring(const char *tekst) 
    { 
    	while(*tekst) 
    		UART_putchar(*tekst++); 
    }
    
    const unsigned char hextab[16] = "0123456789ABCDEF";
    
    /** Funkcja sprawdza jak długo trwa stan niski. */
    unsigned char ir_getlo(unsigned char maxlen)
    {
    	unsigned char len = 0;
    	while(1)
    	{
    		TIMER_WAIT();
    		// jeśli pojawił się stan wysoki - przerwij funkcję
    		if(IR_GET())
    			return len;
    		// aktualizacja licznika - jak długo trwa aktualny stan
    		len++;
    		// jeśli stan niski trwa zbyt długo - przerwij funkcję
    		if(len > maxlen)
    			return 0;
    	}
    }
    /** Funkcja sprawdza jak długo trwa stan wysoki */
    unsigned char ir_gethi(unsigned char maxlen)
    {
    	unsigned char len = 0;
    	while(1)
    	{
    		TIMER_WAIT();
    		if(!IR_GET())
    			return len;
    		len++;
    		if(len >= maxlen)
    			return 0;
    	}
    }
    
    int main(void) 
    { 
    	UART_Init(); 
    
    	// PORTD=wejścia/pu 
    	DDRD = 0x00; 
    	PORTD = 0xFF; 
    
    	// wszystkie przerwania od timerów wyłączone 
    	TIMSK = 0;
    	// Załączenie timera 0, F_CPU/8, przepełnienie co 2048 cykli 
    	TCCR0 = _BV(CS01); 
    
    	while(1) 
    	{ 
    		// oczekiwanie na pojawienie się stanu niskiego
    		while(IR_GET()) 
    			TIMER_WAIT(); 
    
    		// odebranie startu - jeśli błąd, to powrót do początku
    		if(ir_getlo(90) < 50)
    			continue;
    		// odebranie sync
    		if(ir_gethi(50) < 25)
    			continue;
    		// odbieranie poszczególnych bitów
    		// - zmienna, w której będzie cały odebrany kod
    		uint32_t code = 0;
    		unsigned char i;
    		for(i=0; i<32; i++)
    		{
    			// sync bitu
    			if(ir_getlo(6) < 2)
    				break;
    			// właściwy bit
    			unsigned char len = ir_gethi(20);
    			if(len < 2)
    				break;
    			// zrobienie miejsca na bit
    			code >>= 1;
    			// wstawienie bitu
    			if(len >= 7)
    				code |= (1UL << 31);
    		}
    		// jeśli pętla została przerwana w środku (któryś stan był zbyt długi lub zbyt krótki) - przerwij (wróć do początku pętli)
    		if(i < 32)
    			continue;
    		
    		// sprawdzenie poprawności kodu - jeśli błąd, wróć do początku
    		if(((code >> 8) ^ ~code) & 0x00FF00FF)
    			continue;
    		// wyłuskanie właściwego kodu przycisku
    		uint16_t code_unp = (code & 0xFF) | ((code >> 8) & 0xFF00);
    		
    		// wysłanie kodu przycisku
    		char odb[5];
    		odb[0] = hextab[(code_unp >> 12) & 0xF];
    		odb[1] = hextab[(code_unp >> 8) & 0xF];
    		odb[2] = hextab[(code_unp >> 4) & 0xF];
    		odb[3] = hextab[code_unp & 0xF];
    		odb[4] = 0;
    		
    		UART_putstring(odb); 
       } 
       return 0; 
    }

    Jednak sam kod ma wiele wad, jak np blokujące działanie. Można kod przepisać do działania na przerwaniach. Przeanalizuj może najpierw co ten kod robi, czy kompiluje się chociaż (pisany od ręki, może nie działać).
  • #25 5966225
    rezi_ar
    Poziom 17  
    Witam
    Nie pisałem nigdy pod atmega8, więc nie analizuję Twojego kodu.
    Pisałem rozpoznawanie NEC'a w ASM na PIC.
    Stosowałem prostą metodę detekcji 0 i 1.
    Tak naprawdę (pewnie już to wiesz) sygnały z odbiornika TSOPxxx są odwrotne do tych na rysunku na stronie opisującej standard. Wynika to z tego, że przy odbieraniu sygnału z pilota na wyjściu układu jest stan niski a nie wysoki.
    Co robiłem:
    Czekałem na zbocze narastające, odmierzałem około 800ms i sprawdzałem stan. Jeżeli stan był niski, to odebrane było zero, jeżeli wysoki to jedynka.
    Jeżeli odebrane było zero, to wracałem do początku (oczekiwanie na zbocze narastające). Jeżeli odebrałem 1, to czekałem na zbocze opadające i wtedy wracałem do początku procedury (oczekiwanie na zbocze narastające).
    Na zielono na wspomnianej wcześniej stronie jest napisane dokładnie co trzeba zrobić aby odebrać 4 bajty danych.

    pozdrawiam
    rezi
  • #26 5966878
    omen_s
    Poziom 19  
    BoskiDialer
    Dziękuję bardzo za kod. Oczywiście skompilował się bez problemów. Przerobiłem kod aby działał na przerwaniu od int0 i usunąłem jego "blokujące działanie". Dziękuję za pomoc :D

    rezi_ar
    Tak jak piszesz z tym TSOPxxx o jego zmienionym wyjściu doczytałem w nocie katalogowej jeszcze zanim go jeszcze kupiłem jednak to są moje początki w C więc muszę jeszcze dużo się nauczyć odnośnie programowania jak i samych mikrokontrolerów. Twój opis pewnie będzie pomocny innym.

    Pozdrawiam
  • #27 6374005
    wielki-szu
    Poziom 14  
    Witam czy istnieje taka możliwość zrobić takie urządzenie które rozpoznawało by po włączeniu na jakim kodzie dany pilot pracuje. Handluje pilotami i czsami ludzie przynoszą mi różne wynalazki na wzór i pasowało by wiedzieć co im można w zastępstwie dać za nie. Czy któryś z sznownych kolegów jest mi w stanie pomóc?
REKLAMA