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

[atmega8][c] Brak zmiany zmiennej w pętli.

y0yster 21 Sie 2008 15:18 3160 30
  • #1 5459922
    y0yster
    Poziom 19  
    Witam.

    Zacząłem pisać w c dla avr i mam następujący problem.
    Powiem jeszcze, że używam avrstudio 4.

    Otóż:
    
    	while(1)
    	{
    		cos = 2;
    	}
    


    lub

    
    	for(;;)
    	{
    		cos = 2;
    	}
    


    Już długo programuję w c++ dla PC i wiem, że to nie ma znaczenia inna pętla, ale nie o to tutaj chodzi.

    Dlaczego wartość zmiennej cos nie zostaje zmieniona na 2 ????

    Dodam, że sprawdzałem w debugerze to nic się nie zmienia, ale w rejestrach Rxx zmienia się. Pod tym względem AS jest niedorobione.

    W programie wykorzystuję Timer0/Counter0, który ma zmienić swoje działanie dla cos == 2 a nie zmienia ????

    Dlaczego tak się dzieje?

    Pozdrawiam.
  • Pomocny post
    #2 5459962
    szelus
    Poziom 34  
    Na przyszłość proponuję podawać bardziej znaczace fragmenty programu.
    A wtym przypadku, domyślam się, że brakuje deklaracji jako volatile zmiennej współużywanej w przerwaniu.
    Jaki masz ustawiony poziom optymalizacji w kompilatorze?
  • Pomocny post
    #3 5459980
    BoskiDialer
    Poziom 34  
    Jeśli zmienna nie jest oznaczona jako "volatile", to kompilator jeśli zdecyduje, że zmienna na nic nie wpływa, może ją zoptymalizować (czytaj: usunąć). Kompilator nie sprawdza, czy zmienna jest używana w przerwaniach, gdyż dla niego są to osobne funkcje, które muszą być wywoływane (a te nie są w twojej funkcji wywoływane). Masz dwa rozwiązania: zmienną do komunikacji pomiędzy programem a przerwaniami oznaczyć jako volatile - każdy dostęp musi wiązać się z dostępem do pamięci ram. Drugie to wstawka wymuszająca na kompilatorze zapisanie wartości wszystkich zmiennych do pamięci. Powinno Tobie wystarczyć oznaczenie zmiennej jako volatile.
  • #4 5460071
    y0yster
    Poziom 19  
    Rzeczywiście dodanie volatile pomogło.
    Ale tutaj mam pytanie odnośnie tego kodu.

    #define F_CPU 1000000
    
    #include <avr/io.h>
    #include <util/delay.h>
    #include <avr/interrupt.h>
    
    #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
    #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
    #define tbi(sfr, bit) (_SFR_BYTE(sfr) ^= _BV(bit))
    
    volatile char prawo = 1;
    char wezyk = 0b00010001;
    
    void porusz( void)
    {
    	PORTD = wezyk;
    
    	if( prawo == 1)
    	{
    		if( wezyk == 0x88) //wezyk == 0b10001000
    			wezyk = 0b00010001;
    		else
    			wezyk = wezyk << 1;
    	}
    	else
    	{
    		if( wezyk == 0x11) //wezyk == 0b00010001
    			wezyk = 0b10001000;
    		else
    			wezyk = wezyk >> 1;
    	}
    }
    
    SIGNAL( SIG_OVERFLOW0)
    {
    	porusz();
    }
    
    int main()
    {
    	cbi( DDRC, PC5);
    	sbi( PORTC, PC5);
    
    	//ustawienie portu D ma wyjscie
    	DDRD = 0xFF;
    	//wylaczenie calego portu
    	PORTD = 0x00;
    
    	//wlaczamy globalne przerwania
    	sei();
    
    	//wyzerowanie licznika
    	TCNT0 = 0x00;
    	//wlaczenie "odbioru" przepełnienia od licznika
    	sbi( TIMSK, TOIE0);
    	//wlaczenie countera, ustawiajac prescaler na 1024
    	TCCR0 = 5; //0b00000101
    
    	while(1)
    	{
    		if( bit_is_clear( PINC, PC5))
    		{
    			_delay_ms( 100);
    			tbi( prawo, 0);
    		}
    	}
    
    	return 0;
    }


    Skoro zmienna prawo nie działała to dlaczego wezyk działał.

    Pisaliście, że odnosi się to do funkcji przerwania. A to nie o tyle w przerwaniu, co w tej nieskończonej pętli nie mogłem się odnosić do niej.
  • #5 5460125
    szelus
    Poziom 34  
    Dlatego, że kompilator analizuje każdy ciąg sterowania i wykorzystanie w nim zmiennych oddzielnie.
    Kompilator wiedział, że funkcja porusz na wejściu czyta zmienną wężyk, a potem ją modyfikuje, zatem jest powód, aby zapisać do niej zmiany.
    Z kolei w main jest pętla nieskończona, która używa zmiennej prawo. Ponieważ sterowanie nie opuszcza tej pętli (z punktu widzenia kompilatora), to wewnątrz pętli zmienną można przenieść do rejestu i nie ma potrzeby uaktualniania komórki pamięci, ponieważ (znowu, z punktu widzenia kompilatora), nikt jej nie czyta (pętla czyta sobie z rejestru).
    Dodanie volatile informuje kompilator, że zmienna jest wykorzystywana poza kontrolą, jaką kompilator ma nad przepływem sterowania w programie i wymusza na kompilatorze zapis/odczyt komórki pamięci przy każdym odwołaniu.
    ---
    Poprawilem nazwę funkcji na 'porusz', bo ją miałem na myśli.
  • #7 5460333
    szelus
    Poziom 34  
    y0yster napisał:
    Więc porównywanie jej w przerwaniu także na to nie wpływa. Dopiero modyfikacja by coś zmieniła?

    ???
    Spróbuj zadać pytanie jeszcze raz, bo chyba "nadajemy na innej fali" ;)
    Ja wiem, że czasem nie potrafię wytłumaczyć prosto... :(

    P.S. Kompilator nic nie wie o przerwaniach. To jest funkcja sprzętowa. Z tego punktu widzenia, funkcja porusz() odwołuje się do innej zmiennej prawo, niż funkcja main(), bo sterowanie z main nigdy nie trafia do porusz(), więc to co main() ustawia w zmiennej prawo nie może mieć wpływu na wykonanie funckji porusz(). Taki jest przebieg "rozumowania" kompilatora (optymalizatora).
  • #8 5460354
    y0yster
    Poziom 19  
    To może inaczej.
    Wystarczy, że zmienna jest użyta w jakim kolwiek celu podczas przerwania i wtedy jest możliwe bezproblemowe odwoływanie się do niej?
    Oj, zakręciłem. Przecież to oczywiste, że tak.
    Ale nurtuje mnie jeszcze ta nieskończona pętla. Czy w kompilatorze można wywalić jakąś opcję, albo dodać, aby wykonywał wszystko co zostało napisane w kodzie?
    A cy jak bym umieścił w pętli coś tego typu:
    -przesuwanie bitowe zmiennej
    -wysyłanie jej wartości na port uC
    Czy takie coś by spowodowało, że kompilator już posłusznie by skompilował kod? Wiem, że w kompilatorze wystarczyło by sprawdzić i przetestować, ale :P.
    Sorry, że drugi raz odpowiem sobie sam, ale to testowałem i wszystko działa.

    Pozdrawiam i dzięki za tak szybkie odpowiedzi.
  • #9 5460372
    szelus
    Poziom 34  
    1. Można wyłączyć optymalizację (opcja -O0). To się przydaje (czasem) przy debugowaniu (dla AVR może po JTAG-u, nie wiem, bo nigdy nie korzystałem). Ale program jest wtedy większy i wolniejszy.

    2. Porty są zadeklarowane jako volatile, dlatego kompilator zawsze wykona do nich zapis/odczyt.

    3. Przeczytaj jeszcze raz to, co napisałem powyżej. Według kompilatora to co robisz w main() nie ma wpływu na to co sie dzieje w przerwaniach i odwrotnie. Chyba, że poinformujesz go o tym właśnie przy użyciu volatile.
  • #10 5460378
    Freddie Chopin
    Specjalista - Mikrokontrolery
    OMG czlowieku, wystarczy przed zmienna dopisac volatile i TAK WLASNIE nalezy zrobic. przyjmij ze nie da sie tego zrobic inaczej i koniec. cokolwiek tam nakombinujesz moze PRZYPADKIEM bedzie dzialac w pewnych warunkach, ale po to tworcy jezyka C wymyslili slowo kluczowe volatile zeby dzialalo ZAWSZE.

    4\/3!!
  • #11 5460393
    BoskiDialer
    Poziom 34  
    Zmienna nadal może nie być zapisywana - dopóki w toku wykonywania nie będzie wywoływanej funkcji, która mogła by korzystać z tej zmiennej, nie zabraknie rejestrów pod zmienne, dopóty wartość będzie trzymana w rejestrze. Oznaczenie zmiennej jako volatile wyłącza ten mechanizm "cacheowania" wartości zmiennych w rejestrach. Dla przykładu rejestry IO są definiowane jako zmienne volatile i mają podany tylko adres - co odczyt/zapis, to fizyczny dostęp do rejestru IO/pamięci. Tak więc jeśli w pętli zrobił byś wpisywanie wartości do rejestru IO (który jest ulotny=volatile) dostęp do tego rejestru nie zostanie zoptymalizowany. Zapis i odczyt z rejestru IO nigdy nie zostanie zoptymalizowany (nawet odczyt rejestru IO bez podania zmiennej, w której ma się znaleźć wartość - po prostu rejestr musi zostać odczytany). Przesuwanie bitów może zostać zoptymalizowane, ale w poprawnym kodzie nigdy nie jest to zauważalne - błędem jest nie używanie atrybutu volatile kiedy jest potrzebny, ponieważ to jest informacja dla kompilatora, że tutaj nie może wtargnąć z optymalizacją, bo mogło by to zaszkodzić poprawności wykonywania kodu.
  • #12 5460398
    szelus
    Poziom 34  
    Oj, Freddie! :)
    Człowiek jest dociekliwy - i dobrze. Gorzej, jak ktoś poznawszy nowe słowo kluczowe, jak volatile (czy np. virtual w C++), które rozwiązało mu jakiś problem zaczyna je dodawać wszędzie, czy potrzeba, czy nie. ;) Spotkałem już takie przypadki :(
  • #14 5461743
    Pijopic
    Poziom 17  
    I jeszcze jedna dosc istotna sprawa z innej beczki - w przerwaniu wywolujesz procedure, ale w niej nie wylaczasz przerwania, w wiekszosci przypadkow taka obsluga przerwania tworzy problemy i dlatego na poczatku wywolanej procedury wylacz przerwania a na koncu znow je wlacz.
  • #15 5462216
    szelus
    Poziom 34  
    Pijopic napisał:
    [...] na poczatku wywolanej procedury wylacz przerwania a na koncu znow je wlacz.


    A broń Panie Boże! (o ile się nie wie, co się robi). W AVR przerwania blokują się automatycznie przy wejściu do procedury obsługi (funkcja sprzętowa) a instrukcja RETI odblokowuje je ponownie. Zresztą, funkcjonalnie podobnie zachowuja się wszystkie procesory, jakie znam.
    Ręczne odblkokowywanie może (choć niekoniecznie, np w tym konkretnym przypadku nie) dać nieprzyjemne efekty w postaci przepełnienia stosu. Chociaż w praktyce to mało prawdopodobne - przerwania musiałyby przychodzić z predkościa bliską predkości ich obsługi, a wtedy całość i tak by w zasadzie nie działała.
    Ale to zły styl pisania i niepotrzebne zamieszanie...
  • #16 5462552
    Pijopic
    Poziom 17  
    Szelus to zalezy w czym piszesz program, w zestawie AVRStudio + WinAVR nie zdazylo mi sie jeszcze zeby pominiecie wylaczenia przerwania nie spowodowalo problemow. Z tego co pamietam to niektore kompilatory zalatwiaja to automatycznie, nowym (20071226) WinAVR napewno nie. Nie jestem pewien tego (musze to sprawdzic) ale nie wydaje mi sie takze by bylo to realizowane sprzetowo... Oczywiscie bywaja sytuacje ze niepotrzebne jest wylaczanie i wlaczanie przerwan w kolko, ale na tym polega optymalizacja kodu by pomijac to co jest zbedne...
  • #17 5462665
    BoskiDialer
    Poziom 34  
    Pijopic: Mam spore doświadczenie w pisaniu na avr'y w C i asmie, więc wiem jak one działają. To co piszesz, jest bzdurą. Niezależnie od środowiska po wejściu do funkcji przerwania, flaga I jest kasowana sprzętowo, zawsze, co za tym idzie przerwania są blokowane. Funkcja przerwania kończy się zawsze za pomocą reti, które spowrotem ustawia flagę I. W WinAVR możesz mieć problem tego typu, że zamiast SIGNAL() używasz INTERRUPT() - tutaj kompilator jawnie na początku przerwania załączy przerwania ponownie a funkcja będzie się kończyć za pomocą ret, podczas wykonywania mogą zostać obsłużone inne przerwania.
  • #18 5462700
    Pijopic
    Poziom 17  
    Nie bzdury gdyz to co ty piszesz odnosi sie wlasnie do SIGNAL(), a stosujac obecnie zalecana forme ISR(), co za tym idzie biblioteke interrupt.h zamiast signal.h, moja wersja jest prawidlowa. Autor postu wlasnie z tego rozwiazania korzysta (mimo korzystania ze starej nomenklatury w samej procedurze obslugi przerwania), wiec proponowanie starszych wychodzacych z obiegu rozwiazan jest tu chyba bzdura....
    A i jeszcze jedno flaga "I" jest kasowana sprzetowo, ale ona tylko informuje o wystapieniu przerwania, a nie wplywa na to czy jest ono wlaczone czy nie, o tym decyduje ustawienie flagi "GIE" oraz flagi konkretnego przerwania, w tym przypadku TOIE0 !!
  • #19 5462803
    BoskiDialer
    Poziom 34  
    SIGNAL(vector)
    {
    	asm volatile(""::);
    }
    jak i twoja wersja wykorzystująca ISR(vector)
    ISR(vector)
    {
    	asm volatile(""::);
    }

    generuje dokładnie ten sam kod:
    __vector_X:
    	/* tu prolog */
    	/* kod */
    	/* tu epilog */
    	reti

    Tak utworzona funkcja przerwania blokuje pozostałe przerwania. Natomiast:
    ISR(vector, ISR_NOBLOCK)
    {
    	asm volatile(""::);
    }
    generuje taki kod:
    __vector_x:
    	sei
    	/* tu prolog */
    	/* kod */
    	/* tu epilog */
    	reti

    Widać istnieje potrzeba na załączenie przerwań w kodzie z atrybutem NOBLOCK - domyślnie po wejściu do funkcji przerwania, flaga I jest kasowana.
    Zacytuję tutaj dokumentację do atmega8 (chociaż wygląda to tak samo dla wszystkich avr'ów)
    atmega8.pdf napisał:
    • Bit 7 – I: Global Interrupt Enable
    The Global Interrupt Enable bit must be set for the interrupts to be enabled. The individual
    interrupt enable control is then performed in separate control registers. If the Global
    Interrupt Enable Register is cleared, none of the interrupts are enabled independent of
    the individual interrupt enable settings. The I-bit is cleared by hardware after an interrupt
    has occurred, and is set by the RETI instruction to enable subsequent interrupts
    . The Ibit
    can also be set and cleared by the application with the SEI and CLI instructions, as
    described in the Instruction Set Reference.
    .

    Zmieniłeś post podczas mojego pisania, więc dopisze: Nie wiem, czy wiesz, ale flaga I to jest właśnie GIE (Global Interrupt Enable).
    żeby konkretna funkcja obsługi przerwania została wywołana, musi być również załączone odpowiadające przerwanie - tutaj TOIE0 (Timer Overflow Interrupt Enable), ono nie jest ruszane, po prostu informuje, że tego przerwania oczekujemy. Po wystąpieniu warunku przerwania ustawiana jest flaga TOV0 w TIFR - ta dopiero informuje, że należy wywołać konkretne przerwanie. Ta flaga również jest kasowana podczas wchodzenia do funkcji przerwania:
    atmega8.pdf napisał:
    • Bit 0 – TOV0: Timer/Counter0 Overflow Flag
    The bit TOV0 is set (one) when an overflow occurs in Timer/Counter0. TOV0 is cleared
    by hardware when executing the corresponding interrupt Handling Vector
    . Alternatively,
    TOV0 is cleared by writing a logic one to the flag. When the SREG I-bit, TOIE0
    (Timer/Counter0 Overflow Interrupt Enable), and TOV0 are set (one), the
    Timer/Counter0 Overflow interrupt is executed.

    W tym samym cytacie na końcu masz napisane, kiedy dopiero zostanie wywołany kod obsługi przerwania.
  • #20 5462810
    szelus
    Poziom 34  
    :arrow:Pijopic
    Proponuję się dokształcić i nie wprowadzać zamieszania: http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html
    Poza tym, z flagami mylisz kilka rzeczy. Po pierwsze flaga I zezwala na przyjmowanie przerwań, a nie informuje o wystąpieniu. O wystąpieniu zgłoszenia informują odpowiednie flagi w odpowiednich rejestrach typu xIFR związanych z danym przerwaniem.
    Po drugie mega8 nie ma żadnej flagi GIE, coś pokręciłeś.
    Po trzecie flaga zezwolenia konkretnego przerwania np. TOIE0 oczywiście musi być ustawiona aby takie przerwanie doszło do skutku, ale bit I również musi być ustawiony.
  • #21 5463320
    Pijopic
    Poziom 17  
    Troche namieszalem skrutami myslowymi, ale zadko interesuje mnie teoria, a zawsze praktyka, w praktyce wylaczenie przerwan ma wplyw na dzialanie programu, a dzieje sie tak dlatego, ze po wyjsciu z przerwania i ustawieniu flgi I moze zostac wywolane przerwanie ktore czekalo w kolejce, a to moze zmienic dzialanie calego programu, gdyz pewne fragmenty kodu moga byc wtedy pomijane i nigdy nie wykonane. Programowe wylaczenie przerwan zapobiega takiej sytuacji. A ze nigdy nie mialem sytuacji by ta zasada nie byla prawdziwa to myslalem ze to norma :) ale przy prostych aplikacjach gdzie przerwania wystepuja zadko, to fakt sprzetowa obsluga flag napewno sie sprawdza...

    Post raportowany,
    proszę poprawić liczne błędy,
    przycisk pisownia
    And!
  • #22 5463376
    BoskiDialer
    Poziom 34  
    W przerwaniach nie kasuje się flagi I, ponieważ i tak jest ona skasowana. Przerwania mają priorytet nad głównym kodem, więc jeśli przychodzą zbyt często i kod główny się nie wykonuje, to jest to błąd w programie. Jakkolwiek pomiędzy dowolnymi dwoma przerwaniami musi zostać wykonana co najmniej jedna instrukcja programu głównego - wynika to z tego, że ustawienie flagi I wpływa na przerwania dopiero po jednym cyklu. Nie rozumiem co masz na myśli pisząc "pewne fragmenty kodu mogą być wtedy pomijane i nigdy nie wykonane".
  • #23 5463500
    Pijopic
    Poziom 17  
    Trudno nazwac bledem ciagle naplywajace dane np poprzez USART... W praktyce sprzetowe skasowanie flagi I rozni sie od programowego, sprzetowe nie ma wplywu na kolejke przerwan, programowe uniemozliwia wywolanie przerwania ktore wystapilo w trakcie obslugi poprzedniego przerwania. Bynajmniej takie wnioski moge wyciagnac z mojej praktyki. Co do twojego pytania, mam na mysli to, ze jesli jakis fragment kodu bedzie umieszczony miedzy dwoma nastepujacymi po sobie przerwaniami, to jesli sie mozliwosci ich wywolania nie zablokuje na pewien czas, to ten kod nigdy nie zostanie wykonany i jesli ma on za zadanie przetrawic dane, ktore pojawily sie w wyniku przerwania, to nigdy nie zostana one przetworzone.
  • #24 5463529
    BoskiDialer
    Poziom 34  
    Ciężko to nazwać błędem, co innego zbyt długie przetwarzanie danych - jeśli nie można szybciej, trzeba wybrać inny procesor. Sprzętowe skasowanie flagi I od programowego niczym się nie różni. Jeśli zajdzie warunek jakiegoś przerwania, ustawiana jest odpowiadająca flaga (wyjątkiem są przerwania zewnętrzne reagujące na poziom), procesor wskoczy do podprogramu gdy tylko wszystkie trzy flagi (I, IE oraz IF) będą ustawione. Jeśli chcesz anulować wykonanie któryś przerwań, kasujesz odpowiadające flagi (wpisując jedynki do odpowiadających rejestrów flag) zanim wyjdziesz z przerwania, nic więcej nie zrobisz - nie ważne w jaki sposób flaga I została skasowana, flagi przerwań i tak będą ustawiane.

    ps wyraz "bynajmniej" nie zastępuje wyrazu "przynajmniej" - sprawdź w słowniku.
  • #25 5463690
    Pijopic
    Poziom 17  
    Moze nie jest to tak do konca trafne uzycie tego slowa, ale wedlug nowego slownika pwn dopuszczalne... A co do tematu - poczytalem troche dokumentacje i uzycie instrukcji cli i sbi ma wplyw na sposob obslugi przerwan bedacych w kolejce. A jesli sie z tym nie zgadzasz to wyjasnij mi dlaczego w praktycznych rozwiazaniach sie to potwierdza, inaczej dziala kod w ktorym dodatkowo sie nie ustawi programowo flagi "I" na poczatku i koncu obslugi przerwania niz taki w ktorym sie to zrobi??
  • #26 5463733
    BoskiDialer
    Poziom 34  
    Teraz mówimy o kasowaniu flagi I na początku, a nie o ustawianiu. Podawanie przykładu to jak szukanie różnicy pomiędzy zerem zapisanym dziesiętnie a zerem zapisanym binarnie. Inaczej jest z ustawianiem, bo wtedy kod przerwania będzie mógł być przerwany innym przerwaniem. Kasowanie na początku nic nie daje, ustawianie na końcu, jeśli tego nie kontrolujesz, tylko dajesz możliwość przepełnienia stosu, gdyż wtedy kolejne przerwania będą obsługiwane zanim wyjdziesz z jednej funkcji przerwania. Może skoro masz doświadczenie, masz też kod, w którym uwidacznia się owa różnica - tobie jest łatwiej podać argument co do swojej tezy, ja opieram się o doświadczenie oraz dokumentację, której ciężko zaprzeczyć. Najlepszym argumentem w tym przypadku jest cytat, wskaź ten, który przeczytałeś, który mówi o wpływie cli i sei (sbi tyczy się czegoś innego) na przerwania w sposób inny, niż tylko blokowanie przerwań.
  • #27 5463778
    Pijopic
    Poziom 17  
    Czy wszystko trzeba pisac doslownie?? Piszac "ustawi" mialem na mysli wpierw wylaczy, a pozniej wlaczy globalna flage, wynikalo to jasno z kontekstu poprzednich wypowiedzi.... A jako ze dzis juz nie mam czasu na pogaduszki, to napisze tylko tyle, wazne ze to dziala i potrafi pomoc, o czym pamietaj, moze Ci to kiedys ulatwic rozwiazanie nie jednego problemu...
  • #28 5463795
    BoskiDialer
    Poziom 34  
    Tylko, że ja nigdy żadnego problemu nie miałem, bo wiem dokładnie jak wszystko działa. Twoje "ważne że działa" jest wątpliwe, prawdopodobnie sam nie wiesz jak działa Twój kod. Nie spotkałem się, żeby ktoś, kto nie kasował i ustawiał flagi I w przerwaniu miał z tego powodu problemy z programem.

    ps. Ei incumbit probatio qui dicit, non qui negat.
  • #29 5464012
    Pijopic
    Poziom 17  
    Poszukalem przykladu w kodzie kogos innego, by nie bylo ze to tylko moje widzimisie.

    
    interrupt [EXT_INT1] void ext_int1_isr(void)  //külsömegszakitás ORA INT1
    {
      #asm("cli")
      if (hour<vege) 
        { lm75_init(0,ee_be1,ee_ki1,1);
          rtc_set_alarm_time(0,vege,0,0,0);
          este=1;}
         else {lm75_init(0,ee_be,ee_ki,1);
              este=0;}
      lcd_clear();        
      #asm("sei")        
      return;
    }
    /****************************************************************************************************************/
    interrupt [EXT_INT2] void ext_int2_isr(void)  //mozgásérzékelés
    {
        #asm("cli")
        t=10;
        #asm("sei")        
      return;
    } 
    


    I tak na marginesie - skonczmy ten temat, kazdy ma swoje racje i niech przy nich pozostanie :)
REKLAMA