Redakcja: dondu
Tematem zadania jest eliminacja drgań styków klawiatury złożonej z przycisków chwilowych zwiernych określone tutaj .
Układ testowy zrealizowałem tak:
- mikrokontroler wystawia na pin logiczną jedynkę (przez załączenie wewnętrznego rezystora podciągającego do Vcc, zaś wciśnięty przycisk zwiera go do masy (GND).
- gdy przycisk jest zwolniony mikrokontroler odczytuje wartość logiczną wejścia jako 1, gdy przycisk zostanie wciśnięty mikrokontroler odczytuje wartość logiczną wejścia jako 0.
Aby na drodze programowej móc obsługiwać tak prostą klawiaturę należy uwzględnić opóźnienie, w czasie którego przycisk powinien pewnie złapać lub stracić kontakt. Nie uwzględnienie zjawiska drgań styków prowadzi do błędnych odczytów i działania programów korzystających z klawiatury (np. zmiana w menu o kilka pozycji podczas jednego przyciśnięcia przycisku). Więcej na ten temat dowiesz się z tego artykułu: Przycisk - drgania styków - bouncing
Zaproponowane przeze mnie rozwiązanie programowej eliminacji drgań styków opiera się o następujący, prosty, algorytm:
Wydzielony obszar pamięci (w zadaniu: stan_przyciskow) zawiera informację o wartościach logicznych dla poszczególnych przycisków klawiatury. Obszar ten jest typu globalnego (zmienna globalna volatile), dzięki temu możliwe jest przekazywanie informacji wypracowanej przez program obsługi klawiatury do programu głównego lub innego dowolnego fragmentu programu.
Idea eliminacji drgania styków przycisku polega na:
W przeciwnym wypadku uznaje się, że nastąpiło drganie zatem nie ma potrzeby aktualizowania informacji o stanie klawiatury.
- wykryciu faktu jego wciśnięcia lub puszczenia (zmiana ostatniego stabilnego stanu logicznego, występującego na odpowiadającym pinie mikrokontrolera, na przeciwny),
- odczekaniu zdefiniowanego czasu (czasu drgania styków)
- ponownym sprawdzeniu, czy nowy wymuszony stan logiczny nadal się utrzymuje.
W przeciwnym wypadku uznaje się, że nastąpiło drganie zatem nie ma potrzeby aktualizowania informacji o stanie klawiatury.
Sytuacja stanów przejściowych polegających na drganiu styków podczas przełączania zobrazowana jest na rysunku poniżej (ze względów programistycznych wartość dla stanu stabilnego w przypadku wciśnięcia przycisku = 1).
Rys. 2 - Drgania styków w czasie naciśnięcia i puszczenia przycisku. |
Aby pewnie wykrywać zmianę stanu przycisków klawiatury należy zapewnić odpowiedni czas zwłoki (rys: 3):
W praktyce zwłoka od 10ms do 20ms jest wystarczająca, ale spotyka się przyciski, które wymagają większych czasów.
czas zwłoki >= MAX(t1, t2).
W praktyce zwłoka od 10ms do 20ms jest wystarczająca, ale spotyka się przyciski, które wymagają większych czasów.
Zatem należy cyklicznie badać stan wejścia, do którego podłączony jest przycisk i po wykryciu jego zmiany odczekać ustalony czas po czym ponownie zbadać stan wejścia. Jeśli nowy stan jest inny niż ostatnio wykryty stan stabilny oznacza to, że nastąpiło przełączenie.
Algorytm działa jednakowo tak w przypadku wciśnięcia jak i puszczenia przycisku. Zobrazowane to jest na poniższym rysunku.
Rys. 3 - Działanie algorytmu deboucingu. |
W związku z powyższym należy cyklicznie badać stan pinów klawiatury i zapewnić możliwość odczekania zadanego czasu po wykryciu zmian w położeniach przycisków.
Esencją zadania jest opracowanie sposobu na obsługę klawiatury bez użycia funkcji opóźniających _delay(), które blokując wykonywanie się programu głównego wpłynęłyby na jego wydajność.
Zdecydowałem się na wykonanie obsługi klawiatury w programie obsługi przerwania timer0. Dodatkowo położyłem nacisk na dowolną konfigurację pinów przypisanych do klawiatury, co umożliwi swobodne projektowanie płytki drukowanej.
Program jest w miarę dobrze wyposażony w komentarze więc nie ma potrzeby jego omawiania. Zwrócę jednak uwagę na jego istotę. Otóż realizacja opóźnienia polega na zliczaniu ilości przepełnień timer0, które występuje, co około 2ms.
Zatem jeśli stan logiczny na wejściu mikrokontrolera po upływie zadanego czasu jest inny niż poprzednio zapisany w stan_przyciskow następuje aktualizacja informacji na odpowiedniej pozycji bitowej tej zmiennej. Dodatkowe tablice służą automatyzacji kodu obsługującego jednocześnie wszystkie przyciski klawiatury.
Zatem jeśli stan logiczny na wejściu mikrokontrolera po upływie zadanego czasu jest inny niż poprzednio zapisany w stan_przyciskow następuje aktualizacja informacji na odpowiedniej pozycji bitowej tej zmiennej. Dodatkowe tablice służą automatyzacji kodu obsługującego jednocześnie wszystkie przyciski klawiatury.
// EPP - Drgania stykow - zmora poczatkujacych // Algorytm programowej obslugi drgajacych przyciskow // // ky3orr, 2012.02.26 // http://www.serwis-elektroniki.prv.pl // ky3orr<mawpa>gmail.com // // Atmega8, zegar 1[Mhz] #define F_CPU 1000000UL //wartosc sygnalu zegarowego w [Hz] #define KEYBOARD_DEBOUNCE_MS 20 //czas zwloki debouncera w [ms] #define KEY1_PIN PB0 //definicja klawiatury #define KEY1_PORT PORTB #define KEY1_PIN_REG PINB #define KEY1_DIR DDRB #define KEY2_PIN PB1 #define KEY2_PORT PORTB #define KEY2_PIN_REG PINB #define KEY2_DIR DDRB #define KEY3_PIN PB2 #define KEY3_PORT PORTB #define KEY3_PIN_REG PINB #define KEY3_DIR DDRB #define KEY4_PIN PB3 #define KEY4_PORT PORTB #define KEY4_PIN_REG PINB #define KEY4_DIR DDRB #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> volatile uint8_t stan_przyciskow; //4LSB zawieraja informacje o stanie //przyciskow 1-wcisniety, 0-wolny volatile uint8_t stable_state[4]; //tablica do szybkich operacji w petli volatile uint8_t debounce_timer[4]; //tablica do szybkich operacji w petli void SetupKeyboard (void) { //konfiguracja klawiatury KEY1_DIR &= ~(1<<KEY1_PIN); //pin przycisku jako wejscie KEY1_PORT |= (1<<KEY1_PIN); //podciagniecie pinu do VCC KEY2_DIR &= ~(1<<KEY2_PIN); KEY2_PORT |= (1<<KEY2_PIN); KEY3_DIR &= ~(1<<KEY3_PIN); KEY3_PORT |= (1<<KEY3_PIN); KEY4_DIR &= ~(1<<KEY4_PIN); KEY4_PORT |= (1<<KEY4_PIN); } void InitDebounceTimer (void) //konfiguracja timera0 { TCNT0 = 0x1; //zmiana ustawien powinna byc dla wartosci !0xFF i !0x0 TCCR0 |= (1<<CS01); //preskaler = 8 (dla 1[MHz] przepelnienia co 2.048[ms]) TCCR0 &= ~((1<<CS00) | (1<<CS02)); TIMSK |= (1<<TOIE0); //obsluga przerwania od przepelnienia } void SetupHardware (void) { ACSR &= ~(1<<ACD); //wylaczenie zbedznie pracujacego ACD SetupKeyboard(); InitDebounceTimer(); } ISR(TIMER0_OVF_vect) { uint8_t key_value[4]; //tablica wartosci dla klawiatury //odczyt klawiatury (przycisk: 1->wcisniety, 0-wolny) if(KEY1_PIN_REG & (1<<KEY1_PIN)) {key_value[0] = 0;} else {key_value[0] = 1;} if(KEY2_PIN_REG & (1<<KEY2_PIN)) {key_value[1] = 0;} else {key_value[1] = 1;} if(KEY3_PIN_REG & (1<<KEY3_PIN)) {key_value[2] = 0;} else {key_value[2] = 1;} if(KEY4_PIN_REG & (1<<KEY4_PIN)) {key_value[3] = 0;} else {key_value[3] = 1;} for(uint8_t i = 0; i < 4; i++) { //obsluga kazdego przycisku z osobna if(debounce_timer[i] > 0) { //gdy odmierzanie czasu zwloki if((--debounce_timer[i] == 0) && (stable_state[i] ^ key_value[i])) { stable_state[i] ^= 0x1; //aktualizacja stanu klawiatury gdy koniec zwloki stan_przyciskow ^= (1<<i); //i zmienil sie stan logiczny na wejsciu } } else { //skanowanie klawiatury if(stable_state[i] ^ key_value[i]) { //wykrycie zmiany stanu debounce_timer[i] = KEYBOARD_DEBOUNCE_MS / 2; //ustawienie czasu zwloki } } } } int main(void) { sei(); //wlaczenie obslugi przerwan SetupHardware(); //konfiguracja sprzetu while(1) { //petla glowna programu //kod programu korzystajacy ze zmiennej -> stan_przyciskow } }
To bardzo ciekawy algorytm radzenia sobie z problemem drgań styków. W szczególności w tym rozwiązaniu podoba mi się jego elastyczność, ułatwiająca szybką zmianę pinów, do których podpinamy przyciski.
Może to być bardzo przydatne, szczególnie na etapie budowania prototypów, ponieważ w czasie projektowania PCB często zdarza się, że warto przycisk podłączyć pod inny pin niż jest to na pierwotnym schemacie, by na PCB łatwiej rozmieścić elementy lub ścieżki. W takiej sytuacji wystarczy zmienić definicje przycisku na początku programu. :-)
Zobacz inne rozwiązania problemu drgań styków:
Co się dzieje jak są 2 przyciski wciśnięte ?
OdpowiedzUsuńMirek