Redakcja: dondu
Tematem dot. zadania eliminacji drgań styków klawiatury.
Mój program eliminuje drgania styków w przerwaniu. W każdym wywołaniu przerwania sprawdzany jest jeden z przycisków. Tylko jeden. Dzięki temu przerwanie trwa krócej i mniej ingeruje w wykonywany poza nim program.
Do zadania użyłem układu testowego zaprojektowanego tak:
Schemat Eagle 6: schemat.zip
Do realizacji przerwania użyłem Timer0. Wybrałem go, ponieważ jest najmniej użytecznym timerem do innych celów. Nie pozwala na dokładną regulację częstotliwości i nie obsługuje sprzętowego PWM. Na szczęście w tym zastosowaniu precyzyjna regulacja częstotliwości nie jest konieczna.
//EPP: Drgania styków - zmora początkujących //17 lutego 2012r. //Kod napisany dla ATmega8 //Autor: xxx-xxx001 //dla: http://mikrokontrolery.blogspot.com //Rozmiar: // Program: 386 bytes (4.7% Full) // (.text + .data + .bootloader) // Data: 6 bytes (0.6% Full) // (.data + .bss + .noinit) //---------------------------------------------------------------------------- //Definicje stałych #define F_CPU 8000000 //Częstotliwość taktowania 8MHz #define Liczba_T 10 //Liczba testów przycisku //---------------------------------------------------------------------------- //Biblioteki #include <avr/io.h> //Potrzebne biblioteki #include <avr/interrupt.h> #include <util/delay.h> //---------------------------------------------------------------------------- //Definicje funkcji void Init(); //---------------------------------------------------------------------------- //Potrzebne zmienne volatile char stan_przyciskow; //Zmienna zawierająca stabilny stan //przycisków volatile char licznik[]={0,0,0,0}; //Tablica liczników, każdy licznik ma //swoją komórkę w tebeli volatile char flag =0; //Flaga wskazująca na przycisk który //jest aktualnie testowany //---------------------------------------------------------------------------- //Główna funkcja programu int main() { Init(); //Wywołanie funkcji konfigurującej //Timer0 i porty IO char port = 0; while(1) //Pętla główna programu { //tutaj Twój kod ... //przykład wykorzystania zmiennej globalnej: stan_przyciskow if(stan_przyciskow!=0) //Jeśli któryś z przycisków { //jest naciśnięty port = port^stan_przyciskow; //Zamień stan diody która mu PORTC = port; //odpowiada na przeciwny _delay_ms(250); //Odczekaj 1/4 sekundy } } } //---------------------------------------------------------------------------- //Funkcja włączająca potrzebne układy i zajmująca się innymi nie ciekawymi //rzeczami :-) void Init() { //------------------- //Konfiguracja Timer0 TCCR0 |= _BV(CS00) | _BV(CS01); //Uruchamiamy Timer0, preskaler 64 TIMSK |= _BV(TOIE0); //Zezwalanie na przerwania //------------------- //Włącz przerwania globalne sei(); //------------------- //Konfiguracja portów DDRC = 0b00001111; //Pierwsze 4 piny portu C wyjściowe DDRB = 0; //Port B wejściowy (przyciski) PORTC = 0b00001111; //Zaświeć diody i PORTB = 0b00001111; //włącz PULL-UP dla przycisków } //---------------------------------------------------------------------------- //Obsługa przerwania od przepełnienia Timer0 ISR(TIMER0_OVF_vect) { if(flag>=4) //Jeśli zostały przetestowane wszystkie flag=0; //przyciski to ustaw flagę na pierwszy z nich //----------------------- //Testowanie przycisków if(bit_is_clear(PINB, flag)) if(licznik[flag] < Liczba_T) //Jeśli przycisk jest wciśnięty, licznik[flag] += 1; //a licznik mniejszy od ustalonej //watrości dodaj 1 do licznika if(bit_is_set(PINB, flag)) //Jeśli przycisk nie jest aktywny licznik[flag] = 0; //wyzeruj cały licznik if(licznik[flag] == Liczba_T) //Jeśli licznik osiągnął wymaganą stan_przyciskow |= _BV(flag); //ilość zaliczonych testów ustaw bit //odpowiadający właściwemu //przyciskowi if(licznik[flag] == 0) //Jeśli wykryty został stan wysoki stan_przyciskow &= ~_BV(flag); //(przycisknie jest wciśnięty) wyzeruj //bit odpowiadający odpowiedniemu //przyciskowi flag++; //Inkrementuj flagę przycisku }Projekt AVR Studio z plikiem C: deboucing.zip
Kiedy Timer0 przepełni się, program sprawdza, czy przycisk jest wciśnięty. Jeśli tak, do odpowiedniej komórki tablicy licznik[] dodajemy jeden. Jeśli jednak przycisk nie jest wciśnięty lub jest niestabilny (styki drgają) zerujemy licznik.
Odpowiedni bit w zmiennej stan_przyciskow jest ustawiany dopiero wtedy, gdy licznik doliczy do wartości ustalonej wcześniej jako stała Liczba_T. Następnie inkrementujemy flagę flag, czyli przechodzimy do następnego przycisku.
Podsumowanie
Wadą tego rozwiązania jest duże zapotrzebowanie na czas procesora (w końcu wszystko jest realizowane przez CPU), choć nie tak jak w pokazanym przykładzie. Procesor nie jest blokowany w oczekiwaniu. Większa jest też pewność wskazań, test jest realizowany dowolną ilość razy (zależną od potrzeb), a nie tylko dwukrotnie.
Niestety, kiedy stosujemy takie rozwiązanie musimy zdecydować się na pewien kompromis. Trzeba znaleźć złoty środek pomiędzy obciążeniem procesora, czasem reakcji i pewnością wskazań. Stała Liczba_T decyduje o ilości testów jaka jest wymagana do uznania przycisku za wciśnięty. Kiedy pomnożymy tą liczbę przez ilość przycisków (w tym przypadku 4), a następnie przez czas pomiędzy przerwaniami, otrzymamy czas opóźnienia reakcji na naciśnięcie przycisku.
Zwiększenie tej stałej gwarantuje lepszą skuteczność, ale zwiększa czas reakcji. Aby uniknąć tego efektu należy zmniejszyć wartość preskalera. Niestety, przekłada się to na większe obciążenie CPU.
Na filmiku widać przykład gdzie specjalnie dobrałem parametry tak, aby było widać, że mikrokontroler sprawdza przycisk przez dłuższy czas, nie reagując na krótkie jego przyciśnięcia. Ale Ty możesz dobrać parametry do swoich potrzeb.
Komentarz Dondu:
W wielu projektach mikrokontroler (jego CPU) się nudzi, kręcąc się bezczynnie w pętli głównej przez bardzo duży procent czasu. W takich przypadkach powyższy sposób eliminacji drgań styków, w niczym nie przeszkadza i jest w pełni akceptowalny.
Co więcej daje on większą pewność eliminacji drgań przycisku, sprawdzając kilka razy (jeden po drugim), czy stan przycisku jest już stabilny. Jeżeli nie, to zaczyna procedurę sprawdzania przycisku od nowa.
Ten algorytm warto stosować, tam gdzie nie może dojść do pomyłki, nawet kosztem zwiększenia długości i częstotliwości skanowania przycisku.
Kompromis - to dobre słowo :-)
Zobacz inne rozwiązania problemu drgań styków:
Co więcej daje on większą pewność eliminacji drgań przycisku, sprawdzając kilka razy (jeden po drugim), czy stan przycisku jest już stabilny. Jeżeli nie, to zaczyna procedurę sprawdzania przycisku od nowa.
Ten algorytm warto stosować, tam gdzie nie może dojść do pomyłki, nawet kosztem zwiększenia długości i częstotliwości skanowania przycisku.
Kompromis - to dobre słowo :-)
Zobacz inne rozwiązania problemu drgań styków:
witam,
OdpowiedzUsuńsuper programik ... ale uwaga - kompiluje się z warning'ami (zmienna flag winna być uint8_t)
po przeróbce na mega88 wszystko hula
drobne zmiany kosmetyczne i można przystosować do własnych potrzeb
gratulacje
pozdrawiam