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] Obsługa zewnętrznego przerwania INT1

demeus 06 Sie 2008 01:52 4066 12
REKLAMA
  • #1 5412536
    demeus
    Poziom 18  
    Witam :D

    Mam problem z obsługą przerwań. Otóż poniżej zamieszczony program w przypadku normalnej pracy wyświetla na wyświetlaczu "1" w przypadku wykrycia stanu niskiego na porcie INT1 wyświetla "0". Niestety program wykonuje 2 - 3 przejścia pomiedzy stanem niskim --> wysokim --> niskim -->... i następnie przestaje reagować na zmianę stanu portu INT1 tzn. objawia się to zniknięciem z wyświetlacza znaków "0" i "1" .

    
    void Inicjalizacja(void)
    {
    	LCD_init();							//Inicjalizacja wyświetlacza
    	sei(); 								//Włączenie obsługi przewań	
    	
    	MCUCR = (0<<ISC11) | (0<ISC10);	//Wyzwolenie przerwania niskim stanem
    	GICR = (1<<INT1);					//Załączenie przerwania na INT1
    }
    
    SIGNAL (SIG_INTERRUPT1) 
    {
    	LCD_xy(0,0);
     	LCD_putint((1),10) ;
    }
    
    int main( void )
    {
    	Inicjalizacja();
    	  
    	while(1)
    	{
    	LCD_xy(0,0);
    	LCD_putint((0),10) ;
    	}
    }
    


    Stan niski jest wyzwalany napięciem ~0,2V natomiast stan wysoki jest wyzwalany napięciem 5,16V. Jeśli trzeba mogę zamieścić schemat układu który steruje niskim i wysokim stanem na wejściu INT1, choć wg. wskazań miernika układ działa prawidłowo.

    Program jest oczywiście uruchamiany na procesorze Atmega8 z wewnętrzym kwarcem 1Mhz.

    Niestety swoją przygodę z procesorami dopiero zaczynam i nie bardzo potrafię znaleźć przyczynę takiego zachowania.

    Z góry dziękuję za wszelkie rady i sugestie.


    --
    pozdrawiam
    demeus
  • REKLAMA
  • #2 5412542
    Balu
    Poziom 38  
    Lepiej zrób zboczem, w przerwaniach nie używaj LCD tylko ustawiaj flagi, w main() na podstawie flag wykonaj działania na lcd.

    sei() dobrym zwyczajem jest wykonać po włączeniu zezwoleń...
    Cytat:

    MCUCR = (0<<ISC11) | (0<ISC10); //Wyzwolenie przerwania niskim stanem
    GICR = (1<<INT1); //Załączenie przerwania na INT1

    Taki zapis jest przeważnie karygodny, bo kasujesz wszystko inne co było w MCUCR i GICR.
    Po Twojej operacji w MCUCR masz 0, a w gicr 00001000 (czy cokolwiek - tlko jedną jedynkę i same zera).
    Powinno się to zrobić tak:

    MCUCR &=~( (1<<ISC11) | (1<ISC10));
    GICR|=1<<INT1;

    Pozdro.
  • #3 5412552
    demeus
    Poziom 18  
    Widzę że ktoś jeszcze żyje o tej porze :)

    Czy w main() mogę sprawdzać flagę INTF1 i na jej podstawie wyświetlać coś na LCD?
    Flaga ta jest ustawiana w przypadku wystąpienia przerwania na INT1,
    procedura obsługi przerwania była by pusta a program by na podstawie tej flagi odpowiednio reagował.

    Czy dobrze myślę? Ew. jakich innych flag można by tutaj użyć?

    --
    pozdrawiam
    demeus
  • REKLAMA
  • REKLAMA
  • #5 5412555
    demeus
    Poziom 18  
    A na podstawie sprawdzania flagi INTF1 nie można tego rozwiązać? :)
  • #7 5415819
    demeus
    Poziom 18  
    Witam ponownie :)

    Wg wskazówek wydziergałem taki kod:
    
    volatile unsigned char flaga = 0;
    
    void Inicjalizacja(void)
    {
    	LCD_init();								//Inicjalizacja wyświetlacza
    	sei(); 									//Włączenie obsługi przewań	
    	
    	MCUCR &= ~((0<<ISC11) | (1<ISC10));	//Wyzwolenie przerwania zboczem opadającym
    	GICR |= 1<<INT1;						//Załączenie przerwania na INT1
    }
    
    SIGNAL (SIG_INTERRUPT1) 
    {
    	flaga = 1;
    }
    
    
    int main( void )
    {
    	Inicjalizacja();
     
    		while(1)
    	{	
    		if(flaga==0) 
    			{
    			LCD_xy(0,0);
    			LCD_putint((1),10);  
    			}
    		else
    			{
    			LCD_xy(0,0);
    			LCD_putint((0),10);
    			}
    		flaga = 0;
    	}
    }
    


    Program działa poprawnie tak jak tego oczekiwałem (stan niski = 0 na LCD, stan wysoki = 1 na LCD), ale mam pytanie...

    Czy jeśli przerwanie wyzwolone zboczem opadającym ustawi flagę na 1 to na wyświetlaczu zostanie wyświetlone "0" to ono powinno tak długo trwać dopóki na INT1 jest stan niski i następnie po pojawieniu się stanu wysokiego pojawia się "1" to trwa tak długo jak jest stan wysoki?

    Mówiąc bardziej po ludzku:
    Czy reakcja na zbocze opadające to jest reakcja na tylko na czas opadania zbocza czy na czas opadania zbocza + czas stanu niskiego?
    I dalej idąc jeśli na INT1 jest stan niski to procesor cały czas jest w procedurze obsługi przerwania?

    --
    pozdrawiam
    demeus
  • #9 5415868
    demeus
    Poziom 18  
    Każda odpowiedź rodzi kolejne pytanie :)

    Więc "0" powinne być wyświetlane w momencie wyzwolenia przerwania, a następnie po osiągnięciu stanu niskiego powinna być wyświetlona "1".
    Natomiast program wyświetla "0" równiez podczas stanu niskiego.

    Czy dobrze rozumuje? Ew. gdzie popełniam błąd w rozumowaniu czy w programie?

    --
    pozdrawiam
    demeus
  • REKLAMA
  • Pomocny post
    #10 5417275
    BoskiDialer
    Poziom 34  
    Śmiem twierdzić, że w
    (0<<ISC11) | (1<ISC10)
    jest błąd, powodujący, że wyrażenie to ma wartość równą 1 (podczas gdy powinno mieć wartość równą 4).

    Jeśli flaga jest ustawiana na zbocze, to w kodzie głównym powinny występować jakieś opóźnienia albo zmiana algorytmu, bo inaczej flaga zostanie sprawdzona raz - będzie ustawiona, zostanie wyświetlone "0", po czym momentalnie flaga zostanie skasowana i w następnym przebiegu zostanie wyświetlone "1".

    Kolejna sprawa, nie ma sensu wykorzystywać przerwań w ten sposób - tylko do ustawienia flagi. Przecież jeśli wystąpi warunek wyzwolenia przerwania, najpierw jest ustawiana flaga w GIFR, można ją testować w pętli głównej (jeśli flaga zezwolenia na przerwanie nie jest ustawiona, flagę trzeba kasować ręcznie poprzez wpisanie w jej miejsce jedynki). Nie ma sensu tworzyć i załączać wektor przerwania tylko po to, żeby tą flagę przepisać do pomocniczej zmiennej.
  • #11 5417765
    demeus
    Poziom 18  
    BoskiDialer napisał:
    Śmiem twierdzić, że w
    (0<<ISC11) | (1<ISC10)
    jest błąd, powodujący, że wyrażenie to ma wartość równą 1 (podczas gdy powinno mieć wartość równą 4).


    Możesz mi wyjaśnić gdzie tu jest błąd? Bo wg. mnie po ustawieniu rejestru MCUCR &= ~((0<<ISC11) | (1<ISC10)) ma on postać 1000 czyli 0x8 czyli przerwanie jest wyzwalane zboczem opadającym.

    BoskiDialer napisał:
    Jeśli flaga jest ustawiana na zbocze, to w kodzie głównym powinny występować jakieś opóźnienia albo zmiana algorytmu, bo inaczej flaga zostanie sprawdzona raz - będzie ustawiona, zostanie wyświetlone "0", po czym momentalnie flaga zostanie skasowana i w następnym przebiegu zostanie wyświetlone "1".


    Patrząc na kod dokładnie tak to powinno działać, natomiast program/procesor regauje na każdą zmianę stanu jak opisałem wyżej.

    BoskiDialer napisał:
    Kolejna sprawa, nie ma sensu wykorzystywać przerwań w ten sposób - tylko do ustawienia flagi. Przecież jeśli wystąpi warunek wyzwolenia przerwania, najpierw jest ustawiana flaga w GIFR, można ją testować w pętli głównej (jeśli flaga zezwolenia na przerwanie nie jest ustawiona, flagę trzeba kasować ręcznie poprzez wpisanie w jej miejsce jedynki). Nie ma sensu tworzyć i załączać wektor przerwania tylko po to, żeby tą flagę przepisać do pomocniczej zmiennej.


    Pytałem o to wcześniej czy można testować flagę INTF1 w rejestrze GIFR, ale powiedziano mi że:

    Balu napisał:
    Można, ale nie jak masz włączone przerwanie sprzętowe które ją skasuje...
    (SEI)


    No cóż dopiero się uczę obsługi przerwań :)
    Zaraz przerobię program by zliczał ilość przerwań to może troche więcej mi się wyjaśni.

    --
    pozdrawiam
    demeus

    Dodano po 42 [minuty]:

    Ok

    Program w takiej postaci działa jak należy:
    
    volatile unsigned int flaga = 0;
    
    void Inicjalizacja(void)
    {
    	LCD_init();								//Inicjalizacja wyświetlacza
    	sei(); 									//Włączenie obsługi przewań	
    	
    	MCUCR = (1<<ISC11);			//Wyzwolenie przerwania zboczem opadającym
    	GICR |= 1<<INT1;						//Załączenie przerwania na INT1
    }
    
    SIGNAL (SIG_INTERRUPT1) 
    {
    	flaga++;
    }
    
    
    int main( void )
    {
    	Inicjalizacja();
     
    		while(1)
    	{	
    		LCD_xy(0,0);
    		LCD_putint((flaga),10);  
    	}
    }
    


    Natomiast nie rozumiem czemu przy takim zapisie:
    MCUCR&=~(0<<ISC11);

    Który z teori wyczytanej przezemnie wydaje się być poprawny i prawidłowy procesor zachowuje się jakby reagował na niski poziom,
    a powinien reagować na zbocze opadające.
    A przy zapisie:
    MCUCR = (1<<ISC11);

    Program reaguje jak należy :)

    --
    pozdrawiam
    demeus
  • Pomocny post
    #12 5418641
    BoskiDialer
    Poziom 34  
    Błąd polegał na tym, że przy ISC10 zamiast przesuwania bitów było porównanie, ISC10=2 a więc relacja była spełniona i przyjmowała wartość 1.

    Co do wszystkich rad dotyczących kasowania i ustawiania bitów MCUCR i innych - mają one sens, ale dopiero po wstępnym wpisaniu tam jakiejś wartości. Mimo, że po resecie są tam wartości domyślne, najlepiej na samym początku wpisać pierwsze wartości (zwykłe przypisanie przez =). Dodatkowo warto, żebyś zrozumiał co oznacza zapis A&=~B - otóż jest to skasowanie w zmiennej/rejestrze A tych bitów, które są ustawione w B, stąd w kodzie mogły dziać się różne dziwne rzeczy - reakcja na niski poziom była spowodowana tym, że nie kasowałeś żadnych bitów, zresztą żadne nie były ustawione. Według mnie najbardziej przejrzysty sposób konfigurowania wygląda mniej więcej tak:
    // INT1 - zbocze opadające, INT0 - brak
    MCUCR = _BV(ISC11);
    // załączenie przerwania od INT1
    GICR = _BV(INT1)

    Oczywiście później ustawiając lub kasując bity w tych rejestrach korzystasz z odpowiednio A|=B oraz A&=~B.
  • #13 5418675
    demeus
    Poziom 18  
    BoskiDialer napisał:
    Błąd polegał na tym, że przy ISC10 zamiast przesuwania bitów było porównanie, ISC10=2 a więc relacja była spełniona i przyjmowała wartość 1.


    Tak zgadza sie nawet nie zauważyłem tego błędu. :)

    --
    pozdrawiam
    demeus
REKLAMA