📚 Lekcja 6: Biblioteki AVR, GPIO, maski bitowe i #define

Najważniejsza lekcja „narzędziowa” – po niej rozumiesz, jak naprawdę steruje się mikrokontrolerem.

1) Biblioteki AVR – co naprawdę robią?

Biblioteki AVR nie są frameworkiem jak Arduino. One głównie:

  • definiują adresy rejestrów sprzętowych,
  • udostępniają nazwy bitów (PB0, PD2, itp.),
  • dodają kilka funkcji pomocniczych (np. delay).
#include <avr/io.h>
Kluczowe avr/io.h to mapa sprzętu – bez niej nie ma GPIO, timerów ani ADC.

Najczęściej używane nagłówki

#include <avr/io.h>      // rejestry i piny
#include <util/delay.h> // opóźnienia
#include <stdint.h>     // uint8_t, uint16_t
#include <avr/interrupt.h> // przerwania (później)

2) GPIO od strony sprzętu: DDRx, PORTx, PINx

Każdy pin mikrokontrolera to 1 bit w trzech rejestrach.

  • DDRx – kierunek (0 = wejście, 1 = wyjście)
  • PORTx – stan wyjścia / pull-up
  • PINx – odczyt stanu pinu
// PB0 jako wyjście
DDRB |= (1 << PB0);

// LED ON
PORTB |= (1 << PB0);

// LED OFF
PORTB &= ~(1 << PB0);

3) Maski bitowe – absolutny fundament AVR

Maska bitowa to liczba, w której ustawiony jest tylko jeden (lub kilka) bitów. Dzięki niej możesz manipulować konkretnymi pinami bez ruszania reszty rejestru.

Tworzenie maski

(1 << PB0)   // 00000001
(1 << PB3)   // 00001000

Cztery podstawowe operacje (MUSISZ ZNAĆ)

// SET – ustaw bit
X |= maska;

// CLEAR – skasuj bit
X &= ~maska;

// TOGGLE – przełącz bit
X ^= maska;

// READ – sprawdź bit
if (X & maska) { ... }
Zapamiętaj 90% programowania AVR to operacje na maskach bitowych.

4) #define – dlaczego i jak go używać?

#define to makro preprocesora – działa zanim kod zostanie skompilowany. W embedded używa się go bardzo często do:

  • numerów pinów,
  • masek bitowych,
  • konfiguracji sprzętu.

Przykład – czytelny kod GPIO

#define LED_PORT PORTB
#define LED_DDR  DDRB
#define LED_PIN  PB0

LED_DDR |= (1 << LED_PIN);
LED_PORT |= (1 << LED_PIN);
Dlaczego tak? Zmiana pinu LED wymaga zmiany jednej linijki, a nie całego programu.

#define vs const

  • #define – do pinów i masek
  • const – do wartości logicznych (czas, progi)
#define BUTTON_PIN PD2
const uint16_t debounce_ms = 20;

5) Pull-up i logika odwrócona

// wejście
DDRD &= ~(1 << PD2);

// pull-up ON
PORTD |= (1 << PD2);

Przy pull-up:

  • przycisk puszczony → stan wysoki (1)
  • przycisk wciśnięty → stan niski (0)
Błąd uczniów Zapominają o ! w warunku:
if (!(PIND & (1 << PD2)))

6) Pełny wzorzec programu GPIO

#include <avr/io.h>
#include <util/delay.h>

#define LED_PORT PORTB
#define LED_DDR  DDRB
#define LED_PIN  PB0

#define BTN_PORT PORTD
#define BTN_DDR  DDRD
#define BTN_PIN  PD2

int main(void)
{
    LED_DDR |= (1 << LED_PIN);

    BTN_DDR &= ~(1 << BTN_PIN);
    BTN_PORT |= (1 << BTN_PIN);

    while (1)
    {
        if (!(PIND & (1 << BTN_PIN)))
            LED_PORT |= (1 << LED_PIN);
        else
            LED_PORT &= ~(1 << LED_PIN);

        _delay_ms(20);
    }
}
To jest wzorzec Na tym schemacie będziemy budować wszystkie kolejne projekty.

7) Co MUSISZ umieć po tej lekcji

  • rozumieć rolę avr/io.h
  • używać DDRx / PORTx / PINx
  • stosować maski bitowe
  • czytelnie używać #define
  • napisać własny kod GPIO bez Arduino