1) Cel lekcji
W poprzednich lekcjach mikrokontroler tylko wystawiał stany na wyjściach (LED). Teraz robimy krok dalej: uczysz się, jak poprawnie skonfigurować pin jako wejście i jak bezpiecznie odczytywać stan przycisku.
- różnica: DDRx, PORTx, PINx,
- pull-up (wewnętrzny rezystor podciągający),
- stan spoczynkowy i logika aktywnego zera,
- pierwszy kontakt z drganiami styków (debouncing).
2) Najważniejsza teoria: DDR, PORT, PIN
Każdy port ma 3 podstawowe rejestry:
- DDRx — kierunek pinu (0 = wejście, 1 = wyjście),
- PORTx — zapis stanu na wyjściu, a dla wejścia: włącza pull-up,
- PINx — odczyt aktualnego stanu pinu (tu czytasz przycisk).
Przycisk czyta się z PINx.
3) Podłączenie przycisku — najprostszy, poprawny wariant
Użyjemy wewnętrznego pull-up (nie potrzebujesz zewnętrznego rezystora).
- Przycisk podłącz pomiędzy pin PD2 a GND
- LED zostaje na PB0 (jak w lekcjach 8–9)
Gdy pull-up jest włączony:
- przycisk NIE wciśnięty → pin ma stan 1 (HIGH)
- przycisk wciśnięty → pin jest zwarty do masy → stan 0 (LOW)
4) Konfiguracja: LED jako wyjście, przycisk jako wejście
#include <avr/io.h>
#include <util/delay.h>
#define LED_DDR DDRB
#define LED_PORT PORTB
#define LED_PIN PB0
#define BTN_DDR DDRD
#define BTN_PORT PORTD
#define BTN_PINREG PIND
#define BTN_PIN PD2
int main(void)
{
// LED: wyjście
LED_DDR |= (1 << LED_PIN);
// Przycisk: wejście
BTN_DDR &= ~(1 << BTN_PIN);
// Włącz wewnętrzny pull-up dla PD2
BTN_PORT |= (1 << BTN_PIN);
while (1)
{
// tu za chwilę logika
}
}
5) Wariant 1: LED świeci tylko, gdy trzymasz przycisk
Najprostsze sterowanie typu „przycisk = reakcja natychmiastowa”.
while (1)
{
// aktywne zero: wciśnięty = 0
if ( (BTN_PINREG & (1 << BTN_PIN)) == 0 )
{
LED_PORT |= (1 << LED_PIN); // ON
}
else
{
LED_PORT &= ~(1 << LED_PIN); // OFF
}
}
- Maskujesz interesujący bit: BTN_PINREG & (1<<BTN_PIN)
- Jeśli wynik = 0 → przycisk wciśnięty (bo zwarcie do GND)
- Nie ma opóźnień: reaguje natychmiast
6) Dlaczego przycisk „wariuje”? Drgania styków (debouncing)
Mechaniczny przycisk NIE przełącza idealnie 0/1. Podczas wciskania i puszczania pojawiają się krótkie, wielokrotne przełączenia (kilka–kilkanaście ms). To powoduje:
- „podwójne kliknięcia”
- losowe przełączanie stanu
- niestabilne odczyty
7) Wariant 2: przełączanie LED po kliknięciu (toggle) + prosty debouncing
To jest najczęściej oczekiwane zachowanie: klik → zmiana stanu. Warunek: musisz wykryć zbocze (moment wciśnięcia), a nie stan ciągły.
uint8_t led_state = 0;
while (1)
{
// 1) wykrycie wciśnięcia (aktywnie 0)
if ( (BTN_PINREG & (1 << BTN_PIN)) == 0 )
{
_delay_ms(20); // debounce
// 2) sprawdzenie, czy nadal wciśnięty po 20 ms
if ( (BTN_PINREG & (1 << BTN_PIN)) == 0 )
{
led_state ^= 1; // toggle stanu
if (led_state)
LED_PORT |= (1 << LED_PIN);
else
LED_PORT &= ~(1 << LED_PIN);
// 3) czekamy aż puścisz (blokada wielokrotnego zliczania)
while ( (BTN_PINREG & (1 << BTN_PIN)) == 0 ) { }
_delay_ms(20); // debounce puszczenia
}
}
}
Masz debounce i blokadę „trzymania”.
8) Wariant 3: „krótkie naciśnięcie” vs „długie przytrzymanie” (mini-projekt)
Ten wzorzec przyda Ci się później w projektach (menu, reset, tryby pracy).
- krótkie naciśnięcie: toggle LED
- długie przytrzymanie (np. > 700 ms): LED OFF i reset stanu
uint8_t led_state = 0;
while (1)
{
if ( (BTN_PINREG & (1 << BTN_PIN)) == 0 )
{
_delay_ms(20);
if ( (BTN_PINREG & (1 << BTN_PIN)) == 0 )
{
// pomiar czasu trzymania (prosto, na delayach)
uint16_t t = 0;
while ( (BTN_PINREG & (1 << BTN_PIN)) == 0 )
{
_delay_ms(10);
t += 10;
if (t >= 700) break;
}
if (t >= 700)
{
// długie przytrzymanie: reset
led_state = 0;
LED_PORT &= ~(1 << LED_PIN);
}
else
{
// krótkie kliknięcie: toggle
led_state ^= 1;
if (led_state) LED_PORT |= (1 << LED_PIN);
else LED_PORT &= ~(1 << LED_PIN);
}
// debounce puszczenia
while ( (BTN_PINREG & (1 << BTN_PIN)) == 0 ) { }
_delay_ms(20);
}
}
}
W przyszłości zrobimy to poprawnie timerem (bez blokowania programu).
9) Zadania obowiązkowe (musisz zrobić)
- Podłącz przycisk na PD2 i uruchom wariant 1 (LED świeci tylko podczas trzymania).
- Zrób wariant 2: klik przełącza LED (toggle).
- Dodaj debounce 20 ms i blokadę „czekaj aż puścisz”.
10) Zadania dodatkowe (dla lepszych)
- Dodaj drugi przycisk (np. PD3) i steruj dwoma LED.
- Zrób: przycisk1 = ON, przycisk2 = OFF.
- Zrób tryby: każde kliknięcie zmienia tryb świecenia LED (np. 3 tryby).
11) Zadania PRO (dla ambitnych / konkursowych)
- Zrób wykrywanie „zbocza” bez pętli while(przycisk) (pamiętaj poprzedni stan).
- Zrób filtrację debouncing bez _delay_ms (tylko koncepcja – kod uproszczony).
- Połącz z lekcją 9: przycisk zmienia wzór świecenia 4 LED.