Prawidłowe użycie przerwania zmiany pinów


10

Próbuję użyć przerwań zmiany pinów do wykrycia naciśniętych przycisków. Do tej pory nigdy nie pracowałem z tego rodzaju przerwaniami i są pewne problemy, więc chcę się upewnić, czy jest to prawidłowe użycie.

Jeśli poprawnie dostałem arkusz danych, należy wykonać następujące czynności, aby użyć przerwania zmiany PIN:

  1. Ustaw, które kody PIN chcesz kontrolować w rejestrze PCMSK
  2. Włącz rejestr PIN-ów do kontroli przerwań zmiany PIN (PCICR)
  3. Włącz przerwania
  4. Użyj odpowiedniego wektora przerwań

Projekt: Simple Moodlamp, kolory kontrolowane za pomocą 4 przycisków.

Ustawiać:

  • Atmega168A-PU
  • 4 mini przełączniki przyciskowe
  • MOSFETY do sterowania moją 3-watową diodą LED RGB

Oto kod, którego używam, który nie działa zgodnie z oczekiwaniami:

#include <avr/io.h>
#include <stdint.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#define BUTTON1 (1<<PC5) 
#define BUTTON2 (1<<PC4) 
#define BUTTON3 (1<<PC3) 
#define BUTTON4 (1<<PC2) 

#define GREEN   (1<<PB1) 
#define BLUE    (1<<PB2) 
#define RED     (1<<PB3) 

void init() {

        // enable LED
        DDRB |= GREEN;
        DDRB |= BLUE;
        DDRB |= RED;

        // button pullups
        PORTC |= BUTTON1;
        PORTC |= BUTTON2;
        PORTC |= BUTTON3;
        PORTC |= BUTTON4;

        // pin change interrupts for buttons
        PCMSK1 |= PCINT13;
        PCMSK1 |= PCINT12;
        PCMSK1 |= PCINT11;
        PCMSK1 |= PCINT10;

        // enable pin change for buttons
        PCICR |= PCIE2;

        sei();

}

ISR(PCINT2_vect) {

                PORTB = BLUE;
}


void ledTest() {

                PORTB ^= RED;
                _delay_ms(250);
                PORTB ^= RED;
                _delay_ms(250);
                PORTB ^= RED;
                _delay_ms(250);
                PORTB ^= RED;


                PORTB ^= BLUE;
                _delay_ms(250);
                PORTB ^= BLUE;
                _delay_ms(250);
                PORTB ^= BLUE;
                _delay_ms(250);
                PORTB ^= BLUE;

                PORTB ^= GREEN;
                _delay_ms(250);
                PORTB ^= GREEN;
                _delay_ms(250);
                PORTB ^= GREEN;
                _delay_ms(250);
                PORTB ^= GREEN;
}

int main() {

        init();
        ledTest();

        _delay_ms(500);
        PORTB |= GREEN;

        while(1) {
                _delay_ms(100);
        }
}

Uwaga: Przyciski należy usunąć. Ponieważ próbuję wykonać ten krok po kroku i nie powinno to mieć znaczenia dla włączenia diody LED, zignorowałem to tutaj.

Pytanie: Czy sposób, w jaki próbuję użyć przerwań, jest prawidłowy?

Problemy z moją konfiguracją:

  • Przyciski 1-3 są całkowicie ignorowane.
  • Button4 uruchamia reset atmega

Rzeczy, które sprawdziłem:

  • Przyciski nie są w żaden sposób połączone z resetowanym kodem PIN
  • Przyciski są prawidłowo podłączone do GND, jeśli zostaną naciśnięte
  • Przyciski nie są podłączone do GND, jeśli nie zostaną naciśnięte
  • Przyciski działają ładnie, jeśli korzystam z nich bez przerwy, np .:

    if (! (PINC & BUTTON4)) {PORTB ^ = NIEBIESKI; }

  • Zewnętrzny kryształ 16MHZ / wewnętrzny kryształ
  • Wszelkie błędy w routingu
  • Używam kondensatora 100nF między PWR i GND na atmega
  • VCC (7), GND (8), GND (22), AVCC (20) są połączone (ponieważ nie potrzebuję AREF, nie jest podłączony)

Musisz flagę PCIE1 (nie PCIE2) i PCINT1_vect (nie PCINT2)
microtherion

Dlaczego PCIE1? Korzystam z rejestru C, więc jeśli policzę, będzie to A (PCIE0), B (PCIE1), C (PCIE2)? W każdym razie wypróbowałem to z PCIE1 i PCINT1_vect i po naciśnięciu przycisków nie ma reakcji.
echox

1
Zakładanie ortogonalności w takich zadaniach może być nieco ryzykowne. W tym konkretnym przypadku byłbyś prawie poprawny, z tym wyjątkiem, że ATmega168 nie ma portu A. W każdym razie poszedłem do arkusza danych i pinoutu. Inna wskazówka była taka, że ​​używasz PCIE2, ale ustawiasz bity w PCMSK1; to nie może być prawdą (niestety nie wiem, dlaczego twój poprawiony szkic nadal nie działa).
microtherion

Dzięki, rozumiem również, że połączenie oprogramowania do debugowania, które zależy od sprzętu do samodzielnej budowy, nie jest takie łatwe ;-)
echox

Odpowiedzi:


14

Przerwania zmiany pinów zwykle nie są dobrym sposobem na wykrycie akcji przycisków. Wynika to z tego, że mechaniczne przyciski odbijają się, a otrzymasz wiele bezsensownych przerwań, a mimo to nadal musisz ogłaszać.

Lepszym sposobem jest okresowe przerywanie, jak co 1 ms (częstotliwość 1 kHz). To długi czas na większości procesorów, więc ułamek czasu spędzonego w przerwie będzie niewielki. Po prostu próbkuj stan przycisku przy każdym przerwie. Zadeklaruj nowy stan przycisku, jeśli widziałeś nowy stan 50 ms z rzędu. 50 ms jest dłuższe niż większość przycisków, które odbijają się, ale wciąż jest wystarczająco krótkie, aby ludzie nie zauważyli opóźnienia ani nie przejmowali się nim.

Zauważ, że w ten sposób możesz również obsługiwać wiele przycisków w tym samym okresowym przerwaniu 1 ms. Wszystko czego potrzebujesz to jeden licznik dla każdego przycisku.

Więcej na temat czasu odbicia:

Czasami, jak w tym przypadku, ktoś mówi, że 50 ms to zbyt długi czas odbicia. Nie dotyczy to zwykłych przycisków naciskanych przez ludzi. Może to być problem w aplikacjach o krytycznym czasie, takich jak stoper, ale jak dotąd nie natknąłem się na jeden. Testowałem to na początku lat 80. XX wieku, a także wielu innych ludzi.

Prawdą jest, że typowy czas odskoku przycisku wynosi około 10 ms, przy czym prawie wszystko ustala się o 25 ms. Czynnikiem ograniczającym czas usuwania jest postrzeganie przez ludzi. 50 ms jest nieco krótsze niż w przypadku, gdy ludzie zaczynają zauważać opóźnienie, gdy go nie szukają. Nawet wtedy zajmuje dużo więcej czasu, aby było denerwujące. W niektórych przypadkach może istnieć możliwość wykrycia przez człowieka różnicy między opóźnieniem 50 ms a 0 ms, jeśli konkretnie tego szuka , ale jest to zupełnie inna sytuacja niż naciśnięcie przycisku i zobaczenie, że coś się dzieje i nie myślenie o opóźnieniu.

50 ms jest zatem dobrym czasem odbicia, ponieważ opóźnienie jest poniżej limitu postrzegania w zwykłych aplikacjach, znacznie poniżej limitu irytacji i znacznie powyżej czasu odrzutu większości przełączników. Znalazłem przełączniki, które odbijały się prawie tak długo, więc równie dobrze możesz przesunąć się do limitu postrzegania, ponieważ nie ma nic do stracenia.

Zrobiłem wiele produktów z przyciskami zadeklarowanymi w oprogramowaniu, używając czasu usuwania 50 ms. Ani razu klient nie wspomniał nawet o opóźnieniu. Wszyscy zaakceptowali przyciski jako działające bez problemu.


1
W niektórych przypadkach 50 ms może być za długie (zwykle 10-20 ms jest granicą ludzkiej percepcji i powinno to wystarczyć do ogłaszania), ale opisana tutaj metoda jest odpowiednia.
Laszlo Valko

1
@Laszlo: Nie, 50 ms nie jest zbyt długie w zwykłym przypadku. Zobacz dodatek do mojej odpowiedzi.
Olin Lathrop,

Próbowałem 50ms, co działa dla mnie dobrze :-) Nadal jestem ciekawy, dlaczego przerwanie zmiany pinów nie działa (oprócz podskakujących rzeczy), ale to działa :-) Dzięki.
echox

1

Przerwania zmiany pinów są lepszym sposobem na usuwanie błędów niż odpytywanie. Przerwanie zwykle przechodzi przez pewną logikę, taką jak D-Flip Flop lub D-Latch. Chociaż jest to prawda, trudniej jest zaimplementować tę procedurę usuwania z kompilatorów wyższego poziomu. Po wystąpieniu przerwania flaga przerwania nie jest usuwana, a zezwolenie na przerwanie jest usuwane do momentu wystąpienia opóźnienia. Po wystąpieniu opóźnienia sprawdzany jest stan styku i jeśli nadal znajduje się on w danym stanie, który wywołał przerwanie, stan przycisku jest zmieniany, flaga przerwania jest usuwana i włączane jest włączanie przerwania. Jeśli nie jest w stanie, który spowodował zainicjowanie, włączane jest włączanie przerwań, a stan pozostaje taki sam. To zwalnia procesor do innych zadań. Okresowe przerwy w marnowaniu czasu w programie.


-1

„Przerwania zmiany pinów zwykle nie są dobrym sposobem na wykrycie działań przycisków”.

Źle. PC INT to najlepsza opcja. Jeśli użyjesz odpytywania do sprawdzenia stanu przycisku, przez większość czasu nic nie zostanie wykonane. Marnujesz cenny czas procesora. PC INT umożliwia wykonywanie akcji tylko na żądanie.

„Dzieje się tak, ponieważ mechaniczne przyciski odbijają się, a otrzymasz wiele bezsensownych przerwań, a mimo to nadal musisz ogłosić”.

Poprawnie podskakuj. Jednak NIGDY nie powinieneś ogłaszać przycisku / przełącznika wewnątrz procedury przerwania (ten sam powód: strata czasu procesora). ISR mają być naprawdę krótkie i wydajne, pod względem kodu. Po prostu użyj sprzętowego ogłaszania. Utrzymuj oprogramowanie w czystości!

Debugowanie sprzętowe jest wygodniejsze, patrz tutaj / RC debouncing + Schmitt trigger for reference. Używałem go niezliczoną ilość razy z PC INT, nigdy nie zawiodło.

Tak, możesz (i powinieneś) użyć PC INT, aby uzyskać stan przycisku. Ale musisz także użyć odpowiedniego zapowiedzi sprzętowej.


2
Debugowanie oprogramowania jest prawidłowym podejściem i przez większość czasu niewielkie dodatkowe obciążenie procesora jest nieistotne. Mówienie, że zwykle powinieneś debiutować w sprzęcie, jest co najmniej wątpliwe. Mówienie, że we wszystkich przypadkach trzeba używać sprzętowego ogłaszania, jest po prostu błędne.
Olin Lathrop

W większości aplikacji kontroler i tak działa bezczynnie przez większość czasu, uruchamiając główną pętlę. Ponadto czas procesora wymagany do sprawdzenia stanu operacji we / wy i potencjalnego zwiększenia zmiennej jest minimalny. Wdrożenie prostego debugowania w oprogramowaniu jest prawie „darmowe”, sprzęt kosztuje. I nie śmiej się z kilku centów, montaż również kosztuje, a jeśli używasz średnich do dużych ilości produktu, nie jest to bez znaczenia. To prawda, że ​​czas ISR powinien być krótki, ale w tym przypadku nie jest to argumentem. Prawdopodobnie jest to bardziej krytyczne, jeśli PC INT ISR strzela 50 razy z rzędu z powodu odbicia.
Rev1.0

@Nelson, „strata czasu procesora” ma znaczenie w niektórych aplikacjach, a nie w wielu innych. Powinieneś zakwalifikować swoją odpowiedź do sytuacji, w której czas procesora ma krytyczne znaczenie.
user1139880
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.