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

[atmega8][c] obsługa dwóch przerwań + odmierzanie czasu

herszt 15 Wrz 2008 23:53 2567 14
  • #1 5539375
    herszt
    Poziom 18  
    Witam!

    Od jakiegoś czasu buduję bardzo prostą centralkę alarmową i wszystko ładnie śmiga, ale na mojej drodze pojawiła się kolejna przeszkoda - nie mam pojęcia jak sprawić aby w momencie wykrycia zdarzenia alarm zachowywał się w sposób jaki bym sobie życzył. Sprawa wygląda następująco: w momencie wykrycia zdarzenia od razu włączany jest sygnał dźwiękowy, a ja chciałbym aby użytkownik miał chwilę czasu na podejście do urządzenia i ewentualne rozbrojenie za pomocą klawiatury. Sprawa wyglądała prosto w odwrotnej sytuacji - danie użytkownikowi czasu an opuszczenie strefy. W urządzeniu wykorzystuję dwa przerwania - do jednego pinu poprzez bramkę NAND podłączone są czujki ruchu, do drugiego podłączone przez bramkę NAND są guziki klawiatury. Poniżej zamieszczam kod obsługi obu przerwań:

    Naruszenie strefy:

    
    SIGNAL(SIG_INTERRUPT0)
    {
    
    	if(stan==1) {
    		write_command(0x1) ;
    		write_command(0x80) ;
    		write_text("   NARUSZENIE") ;
    		write_command(0x0C0);
    		write_text("     STREFY") ;
    		LCD_LED_ON ;
    		PORTB |= _BV(3) ;
    	}
    } 
    


    Naciśnięcie guzika na klawiaturze:
    
    
    SIGNAL(SIG_INTERRUPT1)
    {
    	
        PORTB = PORTB|0b00000000 ;
    	waitms(3) ;
    		if(bit_is_clear(PINB,2))
    		{
    		sound(50) ;
    			if(stan==0)
    			{
    			cli() ;
    				write_command(0x1) ;
    				write_command(0x80) ;
    				write_text("   UZBRAJANIE") ;
    				write_command(0x0C0);
    				write_text("     SYSTEMU") ;
    				LCD_LED_ON ;
    				waitms(500) ; waitms(500) ; waitms(500) ; sound(50) ; 
    				waitms(500) ; waitms(500) ; waitms(500) ; sound(50) ;
    				waitms(500) ; waitms(500) ; waitms(500) ; sound(50) ;
    				waitms(500) ; waitms(500) ; waitms(500) ; sound(50) ;
    				waitms(500) ; waitms(500) ; waitms(500) ; sound(50) ;
    				waitms(500) ; waitms(500) ; waitms(500) ; sound(50) ;
    				waitms(500) ; waitms(500) ; waitms(500) ; sound(50) ;
    				waitms(500) ; waitms(500) ; waitms(500) ; sound(50) ;
    				waitms(500) ; waitms(500) ; waitms(500) ; sound(50) ;
    				waitms(500) ; waitms(500) ; waitms(500) ; sound(200) ;
    				stan = 1 ;
    			sei() ;
    			}
    		}
    	PORTB = PORTB&0b11111100 ;
    	PORTB = PORTB|0b00000001 ;
    	waitms(3) ;
    		if(bit_is_clear(PINB,2))
    		{
    		sound(50) ;
    			if(stan==1)
    			{
    				stan = 0 ;
    				write_command(0x1) ;
    				write_command(0x80) ;
    				write_text("   ROZBRAJANIE") ;
    				write_command(0x0C0);
    				write_text("     SYSTEMU") ;
    				LCD_LED_ON ;
    				PORTB &= ~_BV(3) ;
    				waitms(500) ;
    			}
    		}
    	PORTB = PORTB&0b11111100 ;
    	PORTB = PORTB|0b00000010 ;
    	waitms(3) ;
    		if(bit_is_clear(PINB,2))
    		{
    			sound(50) ;
    		}
    	PORTB = PORTB&0b11111100 ;
        czuwanie() ;
    
    } 
    


    W tym wypadku obsługiwane są tylko 3 guziki - ich stan sprawdzany jest po kolei za pomocą multipleksera. Pierwszy guzik uzbraja system (i tu bardzo topornie rozwiązałem sprawę oczekiwania aż użytkownik opuści strefę - wyłączam przerwania, czekam trochę, a następnie włączam przerwania). Guzik drugi rozbraja system/wyłącza alarm. Trzeci guzik w sumie nic nie robi poza wydaniem dźwięku ;) I tu mam problem jak zmusić urządzenie aby po wykryciu przerwania z czujki poczekał jakiś czas aż naciśnie się guzik 2 - jeśli się nie naciśnie dopiero włączy alarm. Mógłbym to rozwiązać równie topornie, ale to nie po prostu nie działa - mikrokontroler bawi się w robienie pętli z procedury waitms() i nic sobie nie robi z naciskania guzika, a chciałbym żeby reagował podczas całego czasu oczekiwania jeśli ktoś wciśnie ten guzik.
    Mam nadzieję, że wyraziłem się dość jasno o co mi chodzi ;)

    z góry dzięki za pomoc
    pozdrawiam
    herszt
  • Pomocny post
    #2 5539453
    korrus666
    Poziom 40  
    Nic sobie nie robi z naciskania guzika bo właśnie obsługuje przerwanie. Ja bym to zrobił w ten sposób.
    Przerwanie od czujki uruchamia jakiś timer i kończy się obsługa przerwania. Timer odmierza czas i po jego upłynięciu sprawdza jakąś zmienną i w zależności od niej uruchamia alarm bądź nie. I tak jeśli przed przyjściem przerwania z tego timera naciśnięty był przycisk 2 to ustawia tą zmienną tak aby timer nie uruchomił alarmu.
  • #3 5539459
    herszt
    Poziom 18  
    A można prosić jakiś pomocny fragmencik kodu? Bo chyba, o ile się nie mylę, timerem 'za jednym razem' nie odmierzę np. 30 sekund.

    z góry dzięki
    pozdrawiam
    herszt
  • Pomocny post
    #4 5539469
    Dr.Vee
    VIP Zasłużony dla elektroda
    Najprostsze rozwiązanie - w przerwaniu od czujki uruchom sprzętowy timer. Przerwanie od timera powoduje włączenie alarmu. Jeśli użytkownik wyłączy system przyciskiem, to w procedurze przerwania przycisku wyłączasz timer.

    Żeby wszystko działało jak trzeba, musisz zezwolić na zagnieżdżone przerwania (czyli żeby ISR od timera mógł się wykonać w momencie, gdy procesor wykonuje ISR przerwania od przycisków). Nie miałbyś tego problemu, gdyby ISR przycisków był krótki i wykonywał się szybko, ale w Twoim kodzie występują opóźnienia i to dość długie.

    W przyszłości lepiej stosować się do prostej reguły - im krótszy czas wykonania się ISR, tym lepiej.

    Pozdrawiam,
    Dr.Vee
  • #5 5540087
    herszt
    Poziom 18  
    Można spytać jak obsługuje się zagnieżdżone przerwania? Bo jak mniemam to nie służą to tego cli() lub sei()? :)

    z góry dzięki za pomoc
    pozdrawiam
    herszt
  • Pomocny post
    #6 5540171
    Dr.Vee
    VIP Zasłużony dla elektroda
    Akurat tutaj nie masz racji, bo tak się to robi. Problem z zezwoleniem na przerwania w ISR jest taki, że to samo przerwanie może zostać zgłoszone jeszcze raz, a tego byś nie chciał. Najprościej w SIG_INTERRUPT1 wyłączyć zezwolenie na INT1 i włączyć przerwania. Przed końcem ISR robisz na odwrót - cli() i ustawiasz flagę od INT1.

    Wchodzisz na grząski grunt, więc powodzenia w debugowaniu ;)

    Pozdrawiam,
    Dr.Vee
  • #7 5540209
    herszt
    Poziom 18  
    Chyba się trochę pogubiłem :P Oj czuję, że to nie będzie takie proste jak mogłoby się wydawać. Czy poza cli() i sei() powinienem jeszcze czegoś używać? Są jeszcze jakieś inne procedury do obsługi przerwań? Bo widzę, że mam tu bawić się jakimiś flagami - czego nigdy do tej pory nie miałem okazji robić :)

    z góry dzięki
    pozdrawiam
    herszt
  • Pomocny post
    #8 5540298
    korrus666
    Poziom 40  
    Możesz włączać i wyłączać każde źródło przerwania z osobna i o tym pisze Dr.Vee. Więc w czasie kiedy obsługujesz przerwania to wyłączasz je tak aby drugi raz nie przyszło z tego samego źródła ale pozwalasz na przyjście przerwań z innych urządzeń więc nie wyłączasz globalnej obsługi przerwań. A przed powrotem z tego przerwania z powrotem je włączasz.
  • Pomocny post
    #9 5540385
    Dr.Vee
    VIP Zasłużony dla elektroda
    W skrócie:
    
    /* zakładając, że bit INT1 w GICR jest normalnie ustawiony */
    ISR(INT1_vect) {
        GICR &= ~_BV(INT1); /* wyzeruj flagę INT1 */
        sei();
        /* kod obsługi */
        cli();
        GICR |= _BV(INT1); /* ustaw flagę INT1 */
    }


    Pozdrawiam,
    Dr.Vee
  • #10 5545766
    herszt
    Poziom 18  
    Widzę, że muszę nieco bardziej zgłębić sprawę przerwań. Poczytam trochę (może ktoś zna jakieś dobre stronki, które mogłyby mi pomóc - bo w sumie na większości stron o avr-ach są ogólniki) i dam znać czy coś udało mi się zdziałać w tej kwestii.

    pozdrawiam
    herszt
  • Pomocny post
    #11 5545796
    Dr.Vee
    VIP Zasłużony dla elektroda
    Lepiej zainwestujesz swój czas przepisując program tak, żeby 90% pracy wykonywał w main, później "szedł spać", a budziły go tylko odpowiednie przerwania. W różnych ISR ustawiasz odpowiednie zmienne globalne, a w main() je odczytujesz i reagujesz odpowiednio.

    I nagle się okaże, że nie potrzeba zagnieżdżania przerwań, system jest prostszy i da się ładnie opisać za pomocą automatu skończonego (od którego zresztą powinieneś był wyjść, zwłaszcza w systemie alarmowym).

    Pozdrawiam,
    Dr.Vee
  • #12 5546383
    herszt
    Poziom 18  
    Przyznam, o tym nie pomyślałem. Proste, ale skuteczne rozwiązanie :) Rozumiem, że w tym wypadku mam zaprzęgnąć do pracy układ watchdoga?

    z góry dzięki
    pozdrawiam
    herszt
  • Pomocny post
    #13 5546490
    kred
    Poziom 20  
    Chyba nie mówisz, że nie masz zaimplementowanej obsługi watchdoga w systemie? Wyobraź sobie sytuację że program gdzieś pójdzie w maliny - jak zachowa się wtedy Twój alarm?
    Kopanie pieska musisz dobrze przemyśleć przy fazie projektowania programu - dodanie tego na samym końcu projektu może się już nie udać.

    Pozdrawiam,
    Krzysztof
  • Pomocny post
    #14 5546575
    Dr.Vee
    VIP Zasłużony dla elektroda
    Watchdog to jedno, odmierzanie czasu to drugie. Na razie skup się na przejściach między stanami systemu, co gdzie włączać i wyłączać, na jakie zdarzenie czekać w każdym stanie.

    Zresetować watchdoga jest prosto (np. w main po wyjściu ze stanu uśpienia), trudniej jest zapewnić, że system zachowa się odpowiednio po zresetowaniu w każdym możliwym stanie.

    Pozdrawiam,
    Dr.Vee
  • #15 5548977
    herszt
    Poziom 18  
    kred napisał:
    Chyba nie mówisz, że nie masz zaimplementowanej obsługi watchdoga w systemie?


    Szczerze mówiąc to nie mam ;) Jakoś nigdy nie miałem okazji bliżej poznać tego układu. A z tego co teraz czytam to wbrew pozorom (głównie moim :) ) to dość przydatna sprawa. Ale najpierw jak pisze Kolega @dr_.Vee zajmę się tym aby program zachowywał się należycie i wprowadzę parę poprawek.

    dzięki za pomoc
    pozdrawiam
    herszt
REKLAMA