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

Obsługa przerwań - ATmega8

Paweł Frąckowiak 02 Wrz 2010 13:56 3131 12
REKLAMA
  • #1 8467784
    Paweł Frąckowiak
    Poziom 11  
    Witam serdecznie!

    Od niedawna uczę się programowania uC w C i postawiłem sobie za zadanie obsłużenie przerwania INT0. Obsługa ma polegać na zapaleniu diody po naciśnięciu guzika podłączonego do INT0 i jej zgaszeniu po puszczeniu guzika.

    Poczytałem trochę forum i znalazłem posta którym się inspirowałem. Jako, że program w poście pisany był na Attiny to poryłem trochę w nocie katalogowej mojego uC i okazało się że rejestr z posta:
    GIMSK |= 1<<INT0;      // INT0

    muszę zmienić na:
    GICR |= _BV(INT0) 


    Po zmianie rejestrów program coś robi, ale nie to co bym chciał, bo zamiast zapalić diodę gdy kliknę guzik to po prostu ją zapala (bez mojej ingerencji) i gasi z ogromną częstotliwością. Gdy kliknę guzik to zatrzymuje mruganie diody do momentu puszczenia guzika.
    Poniżej umieszczam kod programu, proszę o wytknięcie błędów

    
    #include <avr/io.h>
    #include <avr/delay.h>
    #include <avr/interrupt.h>
    
    void main()
    {
    	DDRD &= ~_BV(INT0);	// ustawienie wejścia dla buttona na INT0
    	PORTD |= _BV(INT0);	// podciągnięcie wejścia do jedynki
    	
    	DDRB |= _BV(0);		// ustawienie portu B jako wyjście
    	PORTB &= ~_BV(0);	// 
    	
    	MCUCR |= _BV(ISC01);	// przerwania na zbocze opadające
    	GICR |= _BV(INT0);	// włącza przerwanie na INT0
    	
    	sei();
    	
    	while(1)
    	{
    	}
    }
    
    ISR(INT0_vect)
    {
    	PORTB ^= _BV(0);
    }


    Z góry dziękuje za pomoc!!!
  • REKLAMA
  • #2 8467846
    gaskoin
    Poziom 38  
    DDRD &= ~_BV(INT0);   // ustawienie wejścia dla buttona na INT0
       PORTD |= _BV(INT0);   // podciągnięcie wejścia do jedynki 


    to jest bez sensu

    powinno być

    
    DDRD &= ~_BV(PD2);   // ustawienie wejścia dla buttona na INT0
       PORTD |= _BV(PD2);   // podciągnięcie wejścia do jedynki 


    przy czym mała uwaga - jeżeli port jest ustawiony na wejście, to nie podciąga się go do jedynki, tylko ustawia w stan wysokiej impedancji. Więc lepszym pomysłem jest ustawienie przycisku jako wyjścia w takim przypadku lub podciągnięcie go rezystorem do Vcc.

    
    DDRD |= _BV(PD2);   // ustawienie wYjścia dla buttona na INT0
       PORTD |= _BV(PD2);   // podciągnięcie wYjścia do jedynki 


    Inspiracją powinien być datasheet, a nie jakieś kody znalezione nie wiadomo gdzie :)

    acha no i czy pozbyłeś się w jakiś sposób debouncingu czy po prostu przycisk sobie samotnie jest podpięty do µC ?
  • REKLAMA
  • #3 8467884
    Paweł Frąckowiak
    Poziom 11  
    Dzięki wielkie działa dokładnie tak jakbym tego chciał:-)
    teraz mogę brnąć dalej

    Dzięki i pozdrawiam!

    Dodano po 10 [minuty]:

    Kod nie był znaleziony byle gdzie bo tutaj na forum:-) , ale oczywiście rozumiem o Ci chodzi.

    Co do debouncingu to no cóż przycisk podpięty jest do uC ot tak.
  • REKLAMA
  • #4 8467933
    tmf
    VIP Zasłużony dla elektroda
    gaskoin napisał:
    [code]
    przy czym mała uwaga - jeżeli port jest ustawiony na wejście, to nie podciąga się go do jedynki, tylko ustawia w stan wysokiej impedancji. Więc lepszym pomysłem jest ustawienie przycisku jako wyjścia w takim przypadku lub podciągnięcie go rezystorem do Vcc.


    A co to za teoria? Jak najbardziej pin ustawiony jako wejście podciąga się za pomocą wewnętrznego pull upa. Coś tu ci się poknociło.
  • #5 8467940
    Paweł Frąckowiak
    Poziom 11  
    wstawiłem kondensator pomiędzy nóżki przycisku i jest jakby lepiej, a jak robi się ten debouncig w sposób zgodny ze sztuką?
  • #6 8467959
    tadzik85
    Poziom 38  
    Zgodnie ze sztuką. hm najprościej to odczekać jakieś 20ms. Inną ciekawszą metodą jest ciągle sprawdzenia stanu pinu. np co 50ms. i reagowanie dopiero gdy w kolejnych 2 sprawdzeniach uzyska się sygnał wymuszający.
  • #7 8467986
    michalko12
    Specjalista - Mikrokontrolery
    Sprawdzasz stan portu np co 2ms. Jeśli reakcja ma nastąpić przy zboczu opadającym to jeśli masz stan niski zwiększasz jakąś zmienną co te 2ms, jeśli stan wysoki to zerujesz. Po tej operacji sprawdzasz czy zmienna osiągnęła np 10 co oznacza że przez 20ms teoretycznie stan był stabilny i nie powinno być już więcej drgań styków więc możesz wykonać operację obsługująca to zdarzenie.
    To jest jeden ze sposobów...
  • #8 8471453
    Paweł Frąckowiak
    Poziom 11  
    Nie chce zakładać nowego posta więc zapytam tutaj, szczególnie, że problem w sumie jest związany z wątkiem główny tego tematu:-)

    Napisałem coś takiego:

    
    volatile int licznik = 0;
    
    void main()
    {
    	DDRB |= _BV(0);	// wyjście
    	DDRB |= _BV(1);	// wyjście
    	
    	PORTB &= ~_BV(0);	// zero
    	PORTB &= ~_BV(1);	// zero
    	
    	DDRD &= ~_BV(PD2);	// wejście przerwania INT0
    	PORTD |= _BV(PD2);	// 
    	
    	GICR |= _BV(INT0);	// włączenie przerwania INT0
    	MCUCR |= _BV(ISC00);	// ustawienie wyzwolenia przerwania na zbocze opadające
    	
    	sei();	// zezwolenie na obsługe przerwań
    	
    	while(1)
    	{
    		for ( int i = 0 ; i < licznik ; i++ )
    		{
    			PORTB |= _BV(0);
    			_delay_ms(100);
    			PORTB &= ~_BV(0);
    			licznik--;
    		}
    	}
    }
    
    ISR(INT0_vect)
    {
    	TCCR1B |= _BV(CS10) | _BV(CS11);	// preskaler 64
    	if ( TCNT1 >= 15625 )
    	{
    		licznik++;
    		TCNT1 = 0;
    	}
    	PORTB |= _BV(1);
    }
    


    program ten w zamyśle miał po naciśnięciu guzika (guzik wciskamy i trzymamy jakiś czas) zacząć zliczać czas z dokładnością do jednej sekundy i co sekundę zwiększać zmienną licznik, by po skończeniu obsługi przerwania (po puszczeniu guzika) już w pętli głównej programu w pętli for mrugnąć diodą tyle razy ile sekund trzymany był guzik.
    Niestety nie do końca tak się dzieje. Po wciśnięciu guzika na około 2-3 sek dioda mająca mrugać zapala się i gaśnie tylko raz.

    Podejrzewam, że problem tkwi tym, że to co się dzieje w obsłudze przerwania nie dzieje się cyklicznie, ale to jest mój domysł i nawet jesli jest poprawny to nie bardzo wiem jak to ominąć.

    Będę wdzięczny za wytknięcie mi błędów w rozumowaniu.
  • #9 8471496
    gaskoin
    Poziom 38  
    przecież masz ustawione przerwanie na zbocze opadające.
  • REKLAMA
  • #10 8471517
    Paweł Frąckowiak
    Poziom 11  
    Zgadza się, ale to stwierdzenie niczego mi nie wyjasniło
  • #11 8471539
    gaskoin
    Poziom 38  
    jak trzymasz przycisk to jakim cudem ma wystąpić jakiekolwiek zbocze ?

    To się wykonuje raz w chwili przyciśnięcia i tyle
  • #12 8471542
    tadzik85
    Poziom 38  
    po 1 ! nie masz przerwy miedzy zapaleniem a zgaszeniem diody(lub odwrotnie).
    po 2. dekrementacja zmiennej licznik w pętli for jest błędna, spowoduje to 2 razy mniej mignięć.

    Po pętli powinna być jeszcze pętla oczekująca na ponowne wyzwolenie. bez tego będziesz bez końca mrugał.

    I jak kolega wspomniał. Obsługa przerwania bez sensu.
  • #13 8471739
    Paweł Frąckowiak
    Poziom 11  
    Dzięki Panowie!!!

    Uświadomiliście mi jakie głupoty popisałem :-p

    Po pierwsze wyzwolenie przerwania w opisie miałem, że jest na zbocze opadające, a tak naprawdę (według noty katalogowej) miałem w programie wyzwolenie ustawione na jakąkolwiek zmianę stanu.

    Druga sprawa to ta nieszczęsna dekrementacja zmiennej licznik - przecież to do niczego nie jest mi w tym przypadku potrzebne, a było tam jako pozostałość po pętli while której używałem przedtem zamiast for.

    Teraz kod poprawiłem i obecnie wygląda tak:

    
    volatile int licznik = 0;
    
    void main()
    {
    	DDRB |= _BV(0);	// wyjście
    	DDRB |= _BV(1);	// wyjście
    	
    	PORTB &= ~_BV(0);	// zero
    	PORTB &= ~_BV(1);	// zero
    	
    	DDRD &= ~_BV(PD2);	// wejście przerwania INT0
    	PORTD |= _BV(PD2);	//
    	
    	GICR |= _BV(INT0);	// włączenie przerwania INT0
    	MCUCR &= ~_BV(ISC00) | ~_BV(ISC01);	// ustawienie wyzwolenia przrwania na stan niski
    	
    	sei();	// zezwolenie na obsługę przerwań
    	
    	while(1)
    	{
    		if( bit_is_set(PIND, PD2) )	// Jeśli wejście INT0 jest w stanie wysokim czyli guzik został puszczony wykonuje pętlę for
    		{
    			for ( int i = 0 ; i < licznik ; i++ )	// pętla wykonująca mruganie tyle razy ile sek trzymany był guzik
    			{
    				PORTB |= _BV(0);
    				_delay_ms(100);
    				PORTB &= ~_BV(0);
    				_delay_ms(100);
    			}
    			licznik = 0;	// zerowanie licznika sekund
    			PORTB &= ~_BV(1);	// wygaszenie diody
    		}
    	}
    }
    
    ISR(INT0_vect)
    {
    	TCCR1B |= _BV(CS10) | _BV(CS11);	// preskaler 64
    	if ( TCNT1 >= 15625 )
    	{
    		licznik++;
    		TCNT1 = 0;
    	}
    	PORTB |= _BV(1);
    }
    


    w tej postaci działa rewelacyjne - dokładnie tak jak chciałem

    Raz jeszcze dziękuje za wytknięcie luk w rozumowaniu :D

    Pozdrawiam!!!
REKLAMA