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

AVR/ATtiny2313 - Ciekawa "zwiecha" systemu przy blokadzie INT0 i Power

robiw 06 Paź 2012 11:39 1893 12
REKLAMA
  • #1 11382423
    robiw
    Poziom 26  
    Witam Kolegów,
    Idąc śladem Kolegi mirekk36 napisałem sobie prosty program do pilota (podobny do tego z jego I książki) a w zasadzie jego wzór z tą jednak różnicą…z resztą od początku. Powiem szczerze, że problem, który opiszę za chwilę rozwiązałem w inny sposób (bez blokowania INT0), ale ciekaw jestem gdzie tkwił błąd, czego sam nie byłem w stanie ustalić! Program i projekt jest bardzo prosty: do jednego z portów podpięto 8 klawiszy, ich drugie wyprowadzenia połączono razem i podpięto do wyprowadzenia przerwania zewnętrznego INT0. Program na początku ustawia te piny obsługujące przyciski jako wyjścia, INT0 jako wejście z podciąganiem, zezwala na INT0, uruchamia obsługę przerwań i usypia procesor.

    Wybudzenie powoduje wciśnięcie dowolnego przycisku, wtedy procek wchodzi do obsługi ISR INT0, odczytuje numer wciśniętego klawisza (odpowiednio zmieniając kierunki portów), ustawia flagę (jednocześnie będącą kodem odczytanego klawisza gdy !=0xFF) dla pętli głównej i blokuje przerwanie INT0. W pętli głównej program instruowany ta flagą wykorzystuje wartość odczytanego klawisza i usypia procek ponownie. Pewnie się zastanawiacie czemu kodu odczytanego klawisza nie wykorzystuje w samej procedurze INT0 bez jej blokowania – otóż muszę tak robić bo to tzw. wykorzystanie numeru klawisza polega na jego wysłaniu i odebraniu potwierdzenia wysłania w procedurze ISR ICP1 a jak wiemy wejście w przerwanie INT0 blokuje inne przerwania – nie chciałem komplikować sprawy i obsługiwać przerwań w czasie innych przerwań. Problem w tym, że ten nawet prosty szkielet programu (bez wysyłania czegokolwiek - jak niżej) powoduje w pewnych warunkach zawieszenie się programu a dokładnie, jak przypuszczam, wejście w stan uśpienia prawdopodobnie z zablokowanym przerwaniem INT0 co skutecznie wiesza system. Jest to prawdopodobnie spowodowane jakimiś stanami przejściowymi spowodowanymi drganiem styków – nie mogę jednak znaleźć miejsca gdzie takie coś może powodować zawieszenie się systemu. Może Wy Panowie zauważycie to co oczywiste i co może przydać się innym…robiw

    Kod całości (uproszczony bo prób pokonania problemu było wiele ;-) ):

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
  • REKLAMA
  • #2 11382920
    Tomasz Gumny
    Poziom 28  
    Jeśli wybudzasz procesor przerwaniem INT0, to musi być aktywne na poziom. W takim przypadku przerwania są generowane przez cały czas wciśnięcia przycisku i procesor niewiele więcej zrobi. Z kolei po zwolnieniu przycisku, żaden nie jest zwarty i na wejściu jest 0xff.
  • REKLAMA
  • #3 11383353
    robiw
    Poziom 26  
    To wiem. Przecież to robi ten program...robiw

    Dodano po 27 [minuty]:

    Właśnie po naciśnięciu przycisku procesor jest wzbudzany, następnie w procedurze INT0 odczytywany jest kod przycisku i jeśli !=0xFF to przekazywany jest do pętli głównej a przerwanie jest blokowane. Następnie pętla główną "widząc", że jest kod do wykorzystania robi z niego użytek a następnie odblokowuje przerwanie co skutkuje także tym iż za chwilę procesor jest ponownie usypiany. Wszystko ok tylko po którymś razie usypia się na amen...robiw
  • REKLAMA
  • #4 11383851
    Andrzej__S
    Poziom 28  
    robiw napisał:

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Jeżeli z jakiegoś powodu w momencie wykonywania pierwszej instrukcji jakiś klawisz będzie wciśnięty to po niej mikrokontroler wejdzie w procedurę obsługi przerwania (przed wejściem w tryb uśpienia), wróci z niej z wyłączonym zezwoleniem na INT0 i wejdzie w tryb uśpienia.

    Spróbuj może tak, jak proponuje avr-libc-user-manual:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
  • REKLAMA
  • #5 11383909
    robiw
    Poziom 26  
    Zauważ, że blokada INT0 wystąpi w przerwaniu tylko wtedy, gdy odczytany zostanie kod inny niż 0xFF a tylko taki kod dopuści w pętli głównej do uśpienia procesora. Z reszta kod inny od wspomnianego powoduje jego wykorzystanie w pętli głównej, następnie odblokowanie przerwania, ustawienie tego kodu jako 0xFF i wyjście z warunku a następnie w kolejnym przebiegu pętli uśpienie procesora. Z reszta to uśpienie można dać na końcu w pierwszej części warunku po ponownym uruchomieniu przerwania- wtedy niepotrzebna będzie część warunku z else. Tak czy inaczej i tak wiesza to cały system...robiw
  • Pomocny post
    #6 11384173
    Andrzej__S
    Poziom 28  
    A w ogóle spróbowałeś tak zrobić, jak napisałem? Czy z góry założyłeś, że nie mam racji?
    robiw napisał:
    zauważ, że blokada INT0 wystąpi w przerwaniu tylko wtedy, gdy odczytany zostanie kod inny niż 0xFF

    No właśnie o tym piszę. Jeśli przycisk był wciśnięty w momencie wykonywania instrukcji GIMSK |= (1<<INT0); i przytrzymany dłużej niż opóźnienie 50ms w procedurze obsługi przerwania, to mikrokontroler wejdzie w tryb uśpienia z wyłączonym zezwoleniem na przerwanie INT0.
    robiw napisał:
    Z reszta to uśpienie można dać na końcu w pierwszej części warunku po ponownym uruchomieniu przerwania

    To niczego nie zmienia. Chodzi właśnie o stan klawisza w momencie pomiędzy "ponownym uruchomieniu przerwania" a uśpieniem i po 50ms. Jeśli w tych dwóch momentach klawisz zostanie odczytany jako wciśnięty, będzie "zwiecha".
    Podejrzewam, że po każdym przyciśnięciu klawisza na czas dłuższy niż te 50ms (debouncing) + 0,5s w procedurze obsługi przerwania + czas wykonania instrukcji wewnątrz bloku warunkowego if(pressedKey != 0xFF) + kolejne 50ms (debouncing) będzie skutkowało zawieszeniem mikrokontrolera. Spróbuj może wcisnąć jakiś klawisz, przytrzymać kilka sekund i zaobserwować jakie będą efekty.
  • #7 11384280
    Tomasz Gumny
    Poziom 28  
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
    Przy INT0 wyzwalanym poziomem masz niewielki wpływ na INTF0...
    Cytat:
    Note that if a level triggered interrupt is used for wake-up from Power-down, the required level
    must be held long enough for the MCU to complete the wake-up to trigger the level interrupt. If
    the level disappears before the end of the Start-up Time, the MCU will still wake up, but no interrupt
    will be generated. The start-up time is defined by the SUT and CKSEL Fuses as described
    in “System Clock and Clock Options” on page 22.
    Czy tego się doczytałeś?
  • #8 11384756
    wojekkk
    Poziom 18  
    Ja robilem tak ze dawalem takie 2 funkcje
    void INT0_ACT(void)
    {
    MCUCR |= _BV(ISC00) | _BV(ISC01);
    GIMSK |= _BV(INT0);
    return;
    }
    
    void INT0_DEACT(void)
    {
    GIMSK &= ~_BV(INT0);
    MCUCR &= ~_BV(ISC00) & ~_BV(ISC01);
    return;
    }
    
    


    i deaktywowalem zaraz po wejsciu w przerwanie, a aktywowalem zaraz przed uspieniem i smigalo lux.
  • #9 11385056
    robiw
    Poziom 26  
    Tomasz Gumny: czytałem datasheet i wiem dokładnie jak to działa. Nie mogłem jednak znaleźć miejsca powodującego problem...

    Andrzej_S: genialne! Właśnie o to chodziło. Że też nie mogłem tego zauważyć! Starzeje się! Właśnie, gdy program w pętli głównej wykona instrukcje GIMSK |= (1<<INT0); a w tym czasie wystąpi przerwanie z poprawnie odczytanym klawiszem to w tym przerwaniu nastąpi blokada INT0, następnie program wróci do pętli głównej i wejdzie w power down z zablokowanym INT0 i problem gotowy! Nie sprawdzalem Twojego rozwiązania, bo nie mam pod ręką układu, ale na spokojnie zastanowie się co to zmienia. W sumie to nie zauważyłem takiej sugestii w dokumentacji. Miałeś również rację, że program się wieszał przy każdym, dłuższym przytrzymaniu. Dzięki!

    wojekkk: hmm, zastanawiam się co to zmienia?

    robiw

    Dodano po 6 [minuty]:

    Zastanawiałem się też, czy wejście w PowerDown uzależnić w pętli głównej od warunku:

    i f ( pressedKey == 0 x F F ) power_mode;

    ale nawet po sprawdzeniu warunku a przed wykonaniem power_mode może nadejść przerwanie blokujące INT0 więc to nie byłoby dobre rozwiązanie...
  • #10 11385322
    Andrzej__S
    Poziom 28  
    robiw napisał:
    Nie sprawdzalem Twojego rozwiązania, bo nie mam pod ręką układu, ale na spokojnie zastanowie się co to zmienia.


    To, co zaproponowałem kompiluje się tak (fragment pliku *.lss):
    
    			cli();
      8e:	f8 94       	cli
    			GIMSK |= (1<<INT0);
      90:	8b b7       	in	r24, 0x3b	; 59
      92:	80 64       	ori	r24, 0x40	; 64
      94:	8b bf       	out	0x3b, r24	; 59
    			sleep_enable();
      96:	85 b7       	in	r24, 0x35	; 53
      98:	80 62       	ori	r24, 0x20	; 32
      9a:	85 bf       	out	0x35, r24	; 53
    			sei();
      9c:	78 94       	sei
    			sleep_cpu();
      9e:	88 95       	sleep
    			sleep_disable();
      a0:	85 b7       	in	r24, 0x35	; 53
      a2:	8f 7d       	andi	r24, 0xDF	; 223
      a4:	85 bf       	out	0x35, r24	; 53
      a6:	e9 cf       	rjmp	.-46     	; 0x7a <main+0x20>
    

    Chodzi o to, że zawsze gwarantowane jest wykonanie co najmniej jednej instrukcji po instrukcji sei, przed wyzwoleniem przerwania, a w tym przypadku jest to instrukcja sleep. Pewne jest więc, że procesor wejdzie w tryb uśpienia przed ewentualnym przerwaniem. Przerwanie wybudzi więc procesor ponownie, wykona się procedura obsługi przerwania i program wróci do instrukcji następującej po sleep.
    Myślę, że funkcje sleep_enable() i sleep_disable() są alternatywne. Zapobiegają one tylko niezamierzonemu wejściu w tryb uśpienia w innej części programu, włączając i wyłączając zezwolenie na uśpienie (bit SE w rejestrze MCUCR). Najbardziej istotne jest, aby użyć połączenia:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    robiw napisał:
    W sumie to nie zauważyłem takiej sugestii w dokumentacji.

    avr-libc-user-manual napisał:

    <avr/sleep.h>: Power Management and Sleep Modes
    .....
    This sequence ensures an atomic test of some_condition with interrupts being disabled.
    If the condition is met, sleep mode will be prepared, and the SLEEP instruction
    will be scheduled immediately after an SEI instruction. As the intruction right after the
    SEI is guaranteed to be executed before an interrupt could trigger, it is sure the device
    will really be put to sleep.
    ....
  • #11 11385363
    robiw
    Poziom 26  
    Chodzi Ci o to, że po sei(); jest gwarancja wykonania co najmniej 1 instrukcji i nie może dojść do przerwania przed power_mode? Na pewno? robiw
  • #12 11386525
    Andrzej__S
    Poziom 28  
    ATtiny2313 datasheet strona 13:
    Atmel napisał:

    When using the SEI instruction to enable interrupts, the instruction following SEI will be executed
    before any pending interrupts, as shown in this example.
    Assembly Code Example
    Kod: text
    Zaloguj się, aby zobaczyć kod

  • #13 11386750
    wojekkk
    Poziom 18  
    Wiem tyle, ze mialem identyczny przypadek tylko na attiny44 po wybudzeniu zawieszal sie, pomagal tylko reset i dzialalo do momentu uspienia. Uklad mial dzialac tak ,ze ta sama noga jak pojawialo sie 0v to usypiala uklad i byl uspiony do momentu az pojawilo sie 5v i poki stan byl wysoki uklad mial dzialac. Po tych funkcjach nie musisz uzywac juz cli(); bo po zastosowaniu funkcji "INT0_DEACT();" przerwanie nie ruszy w tym przypadku. Wytestuj i zobaczysz ze naprawde pomoze :)

    PS:
    To sa rejestry z ATTINY44 Nie pamietam czy w 2313 sa te same nazwy.
REKLAMA