Seri Protokoller
TEKNİK REHBER SERİ PROTOKOLLER GPIO 2026

GPIO ve libgpiod
modern karakter cihaz arayüzü.

Sysfs deprecated. Doğru yol: /dev/gpiochipN character device ve libgpiod. gpiodetect'ten gpiomon'a, C API'den Python'a — tam rehber.

00 GPIO temelleri

GPIO (General Purpose Input/Output) — dijital giriş veya çıkış olarak programlanabilen donanım pin'leri.

Temel özellikler

Giriş (Input)Pin dışarıdan uygulanan voltajı okur. Yüksek (1) veya düşük (0) seviye. Buton, sensör durum girişi.
Çıkış (Output)Pin, yazılım tarafından belirlenen voltaj seviyesini üretir. LED, röle, başka MCU'ların enable pini.
Pull-upPin dahili olarak VCC'ye bağlanır. Bağlantı yokken pin yüksek (1) okur. Buton bağlantısında kullanışlı.
Pull-downPin dahili olarak GND'ye bağlanır. Bağlantı yokken pin düşük (0) okur.
Open-drainPin yalnızca GND'ye çekebilir (active low). Yüksek için harici pull-up gerekir. I²C ve kablo üzerinden sinyal paylaşımında kullanılır.
InterruptPin değişiminde (yükselen kenar, düşen kenar veya her ikisi) kernel interrupt üretir. Polling yerine daha verimli.
NOT

GPIO pinleri genellikle çoğul işlevlidir (multiplexed): aynı fiziksel pin SPI CS, UART TX veya GPIO olarak kullanılabilir. Hangi işlevin aktif olduğu pinmux (pin multiplexer) konfigürasyonuyla belirlenir ve device tree veya bootloader tarafından yapılandırılır.

Elektriksel sınırlar

SoC GPIO pinleri genellikle 3.3V toleranslıdır. 5V sinyal doğrudan bağlanırsa pin kalıcı olarak hasar görebilir. 5V cihazlarla bağlantı için seviye kaydırıcı (level shifter) kullan.

01 Sysfs GPIO — eski yöntem

/sys/class/gpio/ arayüzü Linux 3.x döneminde tanıtıldı. Kernel 5.x ile birlikte resmi olarak deprecated (kullanım dışı) ilan edildi.

bash — sysfs GPIO (artık önerilmez)
# Pin 17'yi export et (userspace'e aç):
echo 17 | sudo tee /sys/class/gpio/export

# Yön ayarla (out = çıkış):
echo "out" | sudo tee /sys/class/gpio/gpio17/direction

# Değer yaz (LED yak):
echo 1 | sudo tee /sys/class/gpio/gpio17/value

# Pin 23'ü giriş yap ve oku:
echo 23 | sudo tee /sys/class/gpio/export
echo "in"  | sudo tee /sys/class/gpio/gpio23/direction
cat /sys/class/gpio/gpio23/value

# İşiniz bitince unexport et:
echo 17 | sudo tee /sys/class/gpio/unexport

Neden deprecated?

  • Race condition: export/unexport işlemleri atomik değil; birden fazla süreç çakışabilir.
  • Pin sahipliği yok: Hangi uygulamanın hangi pini kullandığını takip etmek imkansız.
  • Interrupt erişimi zor: Edge detection için "edge" dosyasına yazmak ve poll() kullanmak gerekir.
  • Kaynaklar serbest bırakılmaz: Uygulama crash olursa pin export durumda kalır.
  • Performans: Her okuma/yazma open/read/write/close sistem çağrısı gerektirir.
UYARI

Sysfs GPIO arayüzü gelecekteki kernel sürümlerinde kaldırılabilir. Yeni kod yazan herkes libgpiod kullanmalıdır. Kernel 5.10+ ile /sys/class/gpio/ altına yazma işlemleri dmesg'de deprecation uyarısı üretir.

02 libgpiod — character device ABI

libgpiod, Linux GPIO character device (/dev/gpiochipN) üzerindeki yeni ABI'yi saran kütüphanedir.

  Userspace    gpiodetect / gpioget / gpiomon / C API / Python
                              │
                        libgpiod.so
                              │
               /dev/gpiochip0   /dev/gpiochip1
                              │
              GPIO character device driver (gpio-cdev)
                              │
                 Hardware GPIO controller (BCM2711, imx-gpio ...)
    

ABI v1 vs v2

ÖzellikABI v1 (libgpiod < 2.0)ABI v2 (libgpiod ≥ 2.0)
Bulk line işlemstruct gpiod_line_bulkstruct gpiod_line_config + request
Event (interrupt)gpiod_line_event_wait()gpiod_line_request_get_fd() + poll()
API tarzıLine-centricRequest-centric (daha güvenli)
Python bindinggpiod 1.x (Chip/Line nesneleri)gpiod 2.x (Chip/LineSettings)
bash — kurulum
# Araçlar ve kütüphane:
sudo apt install gpiod libgpiod-dev python3-gpiod

# Versiyon kontrol:
gpiodetect --version
# gpiodetect (libgpiod) v1.6.3

# Karakter cihaz izinleri:
ls -la /dev/gpiochip*
# crw-rw---- 1 root gpio 254, 0 Jan  1 00:00 /dev/gpiochip0

# Kullanıcıyı gpio grubuna ekle:
sudo usermod -aG gpio $USER

03 gpiodetect ve gpioinfo

Sistemdeki GPIO chip'lerini ve hat (line) bilgilerini listeleyen araçlar.

bash — gpiodetect
# Tüm GPIO chip'leri listele:
gpiodetect
# gpiochip0 [pinctrl-bcm2711] (58 lines)
# gpiochip1 [raspberrypi-exp-gpio] (8 lines)
bash — gpioinfo
# gpiochip0'ın tüm hatlarını listele:
gpioinfo gpiochip0
# gpiochip0 - 58 lines:
#         line   0:      unnamed       unused   input  active-high
#         line   1:      unnamed       unused   input  active-high
#         ...
#         line  14:         "TXD"       unused   input  active-high
#         line  15:         "RXD"       unused   input  active-high
#         ...
#         line  17:      unnamed       unused   input  active-high
#         line  18:      unnamed       unused   input  active-high

# Tüm chip'lerdeki tüm hatlar:
gpioinfo

# Belirli bir hat bilgisi (libgpiod 2.x):
gpioinfo --chip gpiochip0 --line 17

gpioinfo çıktısında önemli alanlar:

line NHat numarası. libgpiod fonksiyonlarında ve gpioget/gpioset'te kullanılır.
nameHat adı. Device tree'deki gpio-line-names özelliğinden gelir. "unnamed" ise isim verilmemiş.
consumerHattı şu an kullanan süreç/driver. "unused" ise serbest.
input/outputMevcut yön.
active-high/lowPolarite: active-high → 1 = yüksek voltaj.

04 gpioget ve gpioset

Anlık GPIO okuma ve yazma için komut satırı araçları. Script ve shell one-liner için uygundur.

bash — gpioget
# gpiochip0 hat 17'yi oku:
gpioget gpiochip0 17
# 0  (düşük seviye)

# Birden fazla hat aynı anda:
gpioget gpiochip0 17 18 27
# 0 1 0

# Active-low (değer tersine çevrilir):
gpioget --active-low gpiochip0 17

# libgpiod 2.x sözdizimi (--chip ve --line):
gpioget --chip gpiochip0 --line 17

# Consumer adı ile oku (lsof'ta görünür):
gpioget --consumer my-app gpiochip0 17
bash — gpioset
# gpiochip0 hat 17'yi yüksek yap (LED yak):
gpioset gpiochip0 17=1

# Hat 17'yi düşük yap (LED söndür):
gpioset gpiochip0 17=0

# Birden fazla hat:
gpioset gpiochip0 17=1 18=0 27=1

# Belirli süre sonra varsayılan değere dön (-z, --daemonize):
gpioset --mode=time --sec=2 gpiochip0 17=1
# 2 saniye yüksek, sonra düşük

# Interrupt/sinyal bekleyerek çıkma:
gpioset --mode=signal gpiochip0 17=1 &
sleep 1
kill $!   # gpioset çıkar, pin serbest kalır
NOT

gpioset komut satırı aracı, komutu tamamlayıp çıktığında GPIO hattı serbest kalır ve önceki durumuna döner. Çıkış sinyali bekleme (--mode=signal) ile arka planda çalıştırarak pin değerini koruyabilirsin. Kalıcı GPIO kontrolü için C veya Python API kullan.

05 gpiomon — edge detection

gpiomon, GPIO hatlarında kenar algılama (edge detection) yapar ve olayları raporlar. Polling olmadan interrupt tabanlı çalışır.

bash — gpiomon
# gpiochip0 hat 25'te yükselen kenarlara (rising edge) bak:
gpiomon --rising-edge gpiochip0 25
# event:  RISING EDGE offset: 25  timestamp: [1735689600.000123456]
# event:  RISING EDGE offset: 25  timestamp: [1735689601.234567890]
# (sonsuz döngü — ctrl-c ile durdur)

# Düşen kenar (falling edge):
gpiomon --falling-edge gpiochip0 25

# Her iki kenar:
gpiomon gpiochip0 25   # her iki kenar varsayılan

# Belirli sayıda olay sonra çık:
gpiomon --num-events=5 gpiochip0 25
# 5 olay sonra otomatik çıkar

# Birden fazla hat izle:
gpiomon gpiochip0 17 18 25 27

# Sessiz mod (sadece kenar sayısını logla):
gpiomon --silent --num-events=10 gpiochip0 17 | wc -l

# Format string ile timestamp:
gpiomon --format "%e %o %S" gpiochip0 25
# %e: event type (R/F), %o: offset, %S: second timestamp

gpiomon, dahili olarak poll() veya epoll() kullanır; yükselen/düşen kenar olaylarını kernel interrupt'ından alır. CPU yükü minimumdur.

06 C ile libgpiod API

libgpiod C kütüphanesi ile GPIO'yu programatik olarak kontrol etmek. ABI v1 (libgpiod < 2.0) gösterilecektir.

gpio_blink.c
#include <stdio.h>
#include <gpiod.h>
#include <unistd.h>

#define CHIP_NAME  "gpiochip0"
#define LED_LINE   17
#define BTN_LINE   27

int main(void)
{
    struct gpiod_chip *chip;
    struct gpiod_line *led, *btn;

    /* chip aç */
    chip = gpiod_chip_open_by_name(CHIP_NAME);
    if (!chip) { perror("gpiod_chip_open"); return 1; }

    /* hat handle'larını al */
    led = gpiod_chip_get_line(chip, LED_LINE);
    btn = gpiod_chip_get_line(chip, BTN_LINE);
    if (!led || !btn) {
        perror("gpiod_chip_get_line");
        gpiod_chip_close(chip);
        return 1;
    }

    /* LED → output, başlangıç değeri 0 */
    if (gpiod_line_request_output(led, "blink-app", 0) < 0) {
        perror("request output"); goto cleanup;
    }

    /* BTN → input, pull-up flag ile */
    struct gpiod_line_request_config bcfg = {
        .consumer     = "blink-app",
        .request_type = GPIOD_LINE_REQUEST_DIRECTION_INPUT,
        .flags        = GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP,
    };
    if (gpiod_line_request(btn, &bcfg, 0) < 0) {
        perror("request input"); goto cleanup;
    }

    /* Blink döngüsü */
    for (int i = 0; i < 10; i++) {
        int btn_val = gpiod_line_get_value(btn);
        printf("Buton: %d\n", btn_val);

        gpiod_line_set_value(led, 1);
        usleep(500000);   /* 500 ms */
        gpiod_line_set_value(led, 0);
        usleep(500000);
    }

cleanup:
    gpiod_line_release(led);
    gpiod_line_release(btn);
    gpiod_chip_close(chip);
    return 0;
}
bash — derleme
gcc -Wall -o gpio_blink gpio_blink.c -lgpiod
sudo ./gpio_blink

Interrupt (event) ile GPIO okuma — C

gpio_interrupt.c
#include <stdio.h>
#include <gpiod.h>

int main(void)
{
    struct gpiod_chip *chip = gpiod_chip_open_by_name("gpiochip0");
    struct gpiod_line *line = gpiod_chip_get_line(chip, 25);

    /* Düşen kenar event request */
    if (gpiod_line_request_falling_edge_events(line, "irq-app") < 0) {
        perror("request events");
        gpiod_chip_close(chip);
        return 1;
    }

    struct gpiod_line_event event;
    struct timespec timeout = { .tv_sec = 5, .tv_nsec = 0 };

    for (;;) {
        int ret = gpiod_line_event_wait(line, &timeout);
        if (ret < 0) { perror("event_wait"); break; }
        if (ret == 0) { printf("Timeout — 5s içinde olay olmadı\n"); continue; }

        gpiod_line_event_read(line, &event);
        printf("Olay: tip=%d zaman=%lld.%09ld\n",
               event.event_type,
               (long long)event.ts.tv_sec,
               event.ts.tv_nsec);
    }

    gpiod_line_release(line);
    gpiod_chip_close(chip);
    return 0;
}
gpiod_chip_open_by_name()İsme göre chip aç. "gpiochip0", "gpiochip1" vb.
gpiod_chip_open_by_number()Numaraya göre chip aç.
gpiod_chip_get_line(chip, n)Hat numarasına göre handle al.
gpiod_chip_find_line(chip, name)Hat adına göre handle al (device tree name).
gpiod_line_request_output(line, consumer, val)Hattı çıkış olarak talep et, başlangıç değeri ver.
gpiod_line_request_input(line, consumer)Hattı giriş olarak talep et.
gpiod_line_get_value(line)Anlık değeri oku (0 veya 1).
gpiod_line_set_value(line, val)Çıkış değerini yaz.
gpiod_line_release(line)Hattı serbest bırak.
gpiod_line_event_wait(line, timeout)Event bekle. 1=olay geldi, 0=timeout, -1=hata.

07 Python ile gpiod

Python gpiod modülü libgpiod C kütüphanesini Python'a bağlar. API'si C kütüphanesiyle paralel yapıdadır.

gpio_python.py
import gpiod
import time

CHIP_NAME = "gpiochip0"
LED_LINE  = 17
BTN_LINE  = 27

chip = gpiod.Chip(CHIP_NAME)

# LED hattını çıkış olarak al:
led  = chip.get_line(LED_LINE)
led.request(consumer="py-blink", type=gpiod.LINE_REQ_DIR_OUT, default_vals=[0])

# Buton hattını pull-up girişi olarak al:
btn  = chip.get_line(BTN_LINE)
btn.request(
    consumer="py-blink",
    type=gpiod.LINE_REQ_DIR_IN,
    flags=gpiod.LINE_REQ_FLAG_BIAS_PULL_UP
)

try:
    for i in range(10):
        val = btn.get_value()
        print(f"Buton: {val}")
        led.set_value(1)
        time.sleep(0.5)
        led.set_value(0)
        time.sleep(0.5)
finally:
    led.release()
    btn.release()
    chip.close()

Python ile interrupt bekleme

gpio_interrupt.py
import gpiod
import select

chip = gpiod.Chip("gpiochip0")
line = chip.get_line(25)

# Düşen kenar event request:
line.request(
    consumer="irq-python",
    type=gpiod.LINE_REQ_EV_FALLING_EDGE
)

print("Buton basılmasını bekliyorum (GPIO 25, düşen kenar)...")

while True:
    # 5 saniye timeout ile event bekle:
    ev_avail = line.event_wait(sec=5)
    if not ev_avail:
        print("Timeout — olay yok")
        continue

    event = line.event_read()
    print(f"Olay: {event.type} @ {event.sec}.{event.nsec:09d}")

    # GPIOD_LINE_EVENT_RISING_EDGE = 1
    # GPIOD_LINE_EVENT_FALLING_EDGE = 2
    if event.type == gpiod.LineEvent.FALLING_EDGE:
        print("Buton basıldı!")

08 Pratik: button debounce ve LED blink

Mekanik butonlar press/release sırasında yüzlerce kez boyunca hızlı geçişler üretir (contact bounce). Yazılımsal debounce bu sorunu çözer.

NOT

Tipik bir mekanik butonda ilk bası sırasında 5-50ms boyunca 10-100 adet sahte geçiş olabilir. Debounce yapmadan interrupt sayıcı her bası için 1 yerine 50+ artış gösterir.

debounce.c
#include <stdio.h>
#include <stdlib.h>
#include <gpiod.h>
#include <time.h>
#include <signal.h>

#define BTN_LINE   17
#define LED_LINE   27
#define DEBOUNCE_MS 50   /* 50ms debounce penceresi */

static volatile int running = 1;

static void sighandler(int sig) { (void)sig; running = 0; }

static long long ms_now(void)
{
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return ts.tv_sec * 1000LL + ts.tv_nsec / 1000000LL;
}

int main(void)
{
    signal(SIGINT,  sighandler);
    signal(SIGTERM, sighandler);

    struct gpiod_chip *chip = gpiod_chip_open_by_name("gpiochip0");
    struct gpiod_line *btn  = gpiod_chip_get_line(chip, BTN_LINE);
    struct gpiod_line *led  = gpiod_chip_get_line(chip, LED_LINE);

    /* LED çıkış */
    gpiod_line_request_output(led, "debounce", 0);

    /* Buton: düşen kenar interrupt, pull-up */
    struct gpiod_line_request_config cfg = {
        .consumer     = "debounce",
        .request_type = GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE,
        .flags        = GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP,
    };
    gpiod_line_request(btn, &cfg, 0);

    int       led_state    = 0;
    long long last_press_ms = 0;
    int       press_count  = 0;

    printf("Butona bas (GPIO %d), LED (GPIO %d) toggle olacak.\n",
           BTN_LINE, LED_LINE);

    while (running) {
        struct timespec timeout = { .tv_sec = 1, .tv_nsec = 0 };
        int ret = gpiod_line_event_wait(btn, &timeout);
        if (ret <= 0) continue;

        struct gpiod_line_event ev;
        gpiod_line_event_read(btn, &ev);

        long long now_ms = ev.ts.tv_sec * 1000LL + ev.ts.tv_nsec / 1000000LL;

        /* Debounce: son olaydan bu yana DEBOUNCE_MS geçmediyse yoksay */
        if (now_ms - last_press_ms < DEBOUNCE_MS) {
            printf("  [bounce yoksayıldı: %lld ms]\n", now_ms - last_press_ms);
            continue;
        }

        last_press_ms = now_ms;
        press_count++;
        led_state ^= 1;
        gpiod_line_set_value(led, led_state);
        printf("Bası #%d → LED %s\n", press_count, led_state ? "ON" : "OFF");
    }

    gpiod_line_set_value(led, 0);
    gpiod_line_release(btn);
    gpiod_line_release(led);
    gpiod_chip_close(chip);
    printf("Toplam bası: %d\n", press_count);
    return 0;
}
bash — derleme ve çalıştırma
gcc -Wall -o debounce debounce.c -lgpiod
sudo ./debounce
# Butona bas (GPIO 17), LED (GPIO 27) toggle olacak.
# Bası #1 → LED ON
#   [bounce yoksayıldı: 3 ms]
#   [bounce yoksayıldı: 7 ms]
# Bası #2 → LED OFF