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