Jak zaprogramować przycisk w AVR ATTINY

Zaprogramowaliśmy ostatnio najprostszą możliwość wykorzystania wyjścia z kontrolera. Dzisiaj wykorzystamy nasz projekt z poprzedniej lekcji do zaprogramowania wejścia.

Spróbujmy zaprogramować micro switch czyli jeden z najprostszych przycisków, dzięki któremu będziemy w stanie kontrolować szybkość migania diodą. Spróbujemy określić trzy prędkości migania i wciśnięcie przycisku spowoduje zmianę.

Microswitch

To taki mały przycisk, dzięki któremu w momencie naciśnięcia możemy zamknąć obwód. Dzięki temu jeśli zaprogramujemy jeden z pinów naszego mikrokontrolera na wejściowy to wówczas zmieni się stan na tym pinie. Jest tylko jeden haczyk według mnie.

Tym haczykiem jest to że przy wciśnięciu przez chwilę napięcie zachowuje się niestabilnie, w tym czasie stan się zmienia wielokrotnie, można to sobie wyobrazić jako przeskakujące impulsy kiedy zbliżymy do siebie dwa kable. Niestety tych zmian nie jesteśmy w stanie zaobserwować gołym okiem. Dla człowieka wciśnięcie przycisku trwa bardzo krótko z punktu widzenia mikrokontrolera, który potrafi coś zrobić milion razy w ciągu sekundy takie wciśnięcie trwa wieki.

W związku z powyższym w naszym programie będziemy musieli uwzględnić wahania na początku i na końcu wciśnięcia przycisku. Ale po kolei

Podłączenie

Bazując na poprzedniej części o diodzie wszystko podłączamy tak samo (diodę i programator) i teraz jedno wyjście od naszego microswitcha podłączamy do minusa (GND lub masa) a drugie podłączamy do pinu ATTINY84A-PU z drugiej strony jak diodę czyli do portu PA1.

Wykrycie naciśnięcia czyli zapal diodę

Na tym etapie powinniśmy mieć podłączony programator, diodę (przez rezystor) oraz przycisk microswitch na porcie PA1.

Teraz napiszemy prosty program, który wykryje czy przycisk jest aktualnie wciśnięty i w zależności od tego czy jest zapali lub zgasi diodę. Możemy bazować na poprzednim programie lub napisać od nowa w każdym razie:

Najpierw dołączamy bibliotekę obsługującą mikrokontrolery AVR (najprawdopodobniej program już to zrobił). Następnie tak jak poprzedni definiujemy taktowanie na milion (czyli jeden MHz). Możemy także dodać/zostawić bibliotekę util/delay.h .

Dalej zanim zaczniemy funkcję główną programu tym razem zdefiniujmy sobie piny PB1 i PA1. Dzięki temu dalsze programowanie będzie o wiele łatwiejsze i będzie można łatwiej rozróżnić co jest diodą a co przyciskiem.

#define LED (1<<PB1)
#define KEY (1<<PA1)

Następnie w funkcji głównej tak jak wcześniej pin LED ustawiamy na wyjście zaś dalej ustawiamy na przycisku plus (jeśli zamkniemy obwód mikrokontroler wykryje to).

DDRB |= LED;
PORTA |= KEY;

Dalej w naszej pętli nieskończonej tworzymy prosty warunek sprawdzający czy przycisk jest wciśnięty i jeśli tak ustawiamy LED na świecenie a jeśli nie to wyłączamy diodę.

if (!(PINA & KEY)){
PORTB |= LED;
} else {
PORTB &= ~LED;
}

Jak określić pojedyncze naciśnięcie klawisza

No dobra z powyższego wiemy już jak sprawdzić czy guzik jest wciśnięty. Niestety w bardziej praktycznych projektach samo wykrycie czy guzik jest wciśnięty nie za bardzo nas urządza. Zazwyczaj za ich pomocą coś konfigurujemy i potrzebujemy wykryć „kliknięcie”. Całkiem nieźle odzwierciedli to chyba zmiana stanu diody po pojedynczym wciśnięciu i puszczeniu switcha.

Będziemy tutaj potrzebować obsłużyć pojedyncze wciśnięcie przycisku. Jest na to prosty ale zarazem chyba niezbyt efektywny sposób. W każdym razie potrzebujemy nieco zmienić zawartość naszej pętli while.

Na początek tak jak do tej pory sprawdzamy czy przycisk jest wciśnięty. Gdybyśmy bezpośrednio w tym warunku ustawili zmianę stanu diody na przeciwny to program zachowałby się całkowicie losowo i zostawił stan diody oczekiwany lub nie w zależności od ilości przebiegu pętli (czy byłby parzysty lub nie).

W celu wyeliminowania tego zjawiska dobrze jest wykryć wciśnięcie przycisku i poczekać na puszczenie. Zazwyczaj takie kliknięcie nie trwa więcej niż pół sekundy, więc dodanie funkcji _delay_ms(500) na razie powinno rozwiązać problem.

if (!(PINA & KEY)){
//Zmieniam stan diody na przeciwny
PORTB ^= LED;
_delay_ms(500);
}

Ma to rozwiązanie kilka wad, z jednej strony program stanie na pół sekundy z drugiej strony jeśli przytrzymasz przycisk dłużej niż pół sekundy to dioda będzie migać co pół sekundy zmieniając stan z drugiej strony jak „klikniesz” dwa razy szybciej niż co pół sekundy to przy drugim kliknięciu nic się nie stanie.

Za pomocą powyższego rozwiązania nie uda nam się zrealizować trzech prędkości migania diodą. Dlatego też napiszemy teraz nieco lepsze rozwiązanie. Będzie to funkcja, która bez zatrzymania programu będzie w stanie określić czy przycisk został wciśnięty.

Zaraz po definicji LED i KEY nad funkcją główną programu dodajemy jeszcze definicję dwóch zmiennych. Jedna będzie odpowiedzialna za liczenie ilości wciśnięć przycisku. Na tej podstawie program będzie w stanie stwierdzić czy migać diodą z pierwszą drugą czy też trzecią prędkością. Druga zmienna będzie przechowywać czas wciśnięcia przycisku co będzie potrzebne do obsłużenia drgań styków.

int pressCounter = 0;
int pressDuring = 0;

Teraz wydzielimy sobie część kodu odpowiedzialną za obsługę przycisku do oddzielnej funkcji o nazwie checkKey() i na początku tej funkcji umieszczamy warunek sprawdzający czy przycisk jest wciśnięty. Następnie zwiększamy nasz licznik przebiegów o jeden. Tutaj dodałem warunek żeby nie dodawało powyżej pewnej liczby przebiegów bo nie ma to sensu w tym momencie. (ten sposób można by wykorzystać też do wykrycia dłuższego przytrzymania przycisku i zaprogramować dwie akcjie).

Dalej sprawdzamy czy przycisk jest już wciśnięty od 50 przebiegów programu (w tym czasie powinien minąć czas drgań na styku) oraz czy przycisk nie został już oznaczony jako wciśnięty. Dzięki takiemu rozwiązaniu możemy wcisnąć guzik i trzymać w nieskończoność a zostanie odczytany jako wciśnięty jeden raz.

Pod powyższym warunkiem oznaczamy przycisk jako wciśnięty i zwiększamy o jeden ilość wciśnięć. W naszym programie założyliśmy że będziemy mieli tylko trzy prędkości migania, więc zerujemy licznik jeśli przekroczy 2, dzięki temu mamy trzy możliwości. (0,1 lub 2). Na tej podstawie później stwierdzimy, którą prędkość migania uruchomić.

Na koniec obsługujemy jeszcze jeśli przycisk nie ma zmienionego stanu to zerujemy czas trwania wciśnięcia i oznaczenie jako wciśnięty. A tak mogłaby wyglądać ta funkcja.

void checkKey(){
if (!(PINA & KEY)){
if(pressDuring < 1000){ pressDuring++; } if(pressDuring > 50 && isPressed == 0){
isPressed = 1;
pressCounter++;
if(pressCounter > 2){
pressCounter = 0;
}
}
} else {
isPressed = 0;
pressDuring = 0;
}
}

Teraz w naszej głównej pętli programu while wystarczy użyć funkcji poprzez wpisanie

while(1){
checkKey();
}

Jak zaprogramować diodę na trzy prędkości migania?

Na ten moment guzik mamy zaprogramowany. Teraz należałoby zaprogramować nasze trzy prędkości migania diody. Jako że jeszcze nie używamy żadnego zegara nie możemy zatrzymywać programu jak to robiliśmy wcześniej, więc wykorzystamy podobną metodę jak do przycisku licząc przebiegi pętli głównej.

Będę tutaj tak jak wcześniej potrzebował jakichś liczników przebiegów oraz długość jednego przebiegu, czyli ile czasu dioda ma się świecić. Ustawię sobie na starcie czas świecenia na 3000 przebiegów pętli.

int checkTime = 1000;
long duringCounter = 0;

Dalej tworzymy funkcję do obsługi diody, będzie to bardzo prosta funkcja w której najpierw zwiększamy licznik przebiegów. Później ustawiamy „Timer” na ilość przebiegów w zależności od opcji wybranej przyciskiem. I na koniec jeśli ilość obiegów przekroczy Timer to zmieniamy stan diody i liczymy od nowa.

void checkLed(){
duringCounter++;
if(pressCounter == 0){
checkTime = 3000;
} else if(pressCounter == 1) {
checkTime = 5000;
} else {
checkTime = 10000;
}

  if(duringCounter > checkTime){
    PORTB ^= LED;
    duringCounter = 0;
  }
}

No i mamy prawie cały program teraz wystarczy tylko dodać naszą funkcję w pętli głównej programu, wrzucić na mikrokontroler i powinno działać jak byśmy tego oczekiwali. Poniżej jak mógłby wyglądać kod programu w pełni.

Oczywiście nie jest to w pełni optymalne rozwiązanie i dałoby się lepiej. Chociażby poprzez wykorzystanie zegara dzięki któremu można by precyzyjniej określić czas świecenia diody. Ponadto można by nasze funkcje napisać tak aby były bardziej uniwersalne i obsługiwać więcej przycisków i diod. Jeśli się uczysz to polecam w ramach ćwiczenia podjąć próbę napisania programu i złożenia dwóch albo trzech diod sterowanych kilkoma switchami.

No i na koniec zdjęcie działającego tego jakże górnolotnego projektu.