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:
- Ustaw, które kody PIN chcesz kontrolować w rejestrze PCMSK
- Włącz rejestr PIN-ów do kontroli przerwań zmiany PIN (PCICR)
- Włącz przerwania
- 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)
źródło
Odpowiedzi:
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.
źródło
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.
źródło
Ź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.
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.
źródło