00 GPIO temelleri
GPIO (General Purpose Input/Output) — dijital giriş veya çıkış olarak programlanabilen donanım pin'leri.
Temel özellikler
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.
# 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.
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
| Özellik | ABI v1 (libgpiod < 2.0) | ABI v2 (libgpiod ≥ 2.0) |
|---|---|---|
| Bulk line işlem | struct gpiod_line_bulk | struct gpiod_line_config + request |
| Event (interrupt) | gpiod_line_event_wait() | gpiod_line_request_get_fd() + poll() |
| API tarzı | Line-centric | Request-centric (daha güvenli) |
| Python binding | gpiod 1.x (Chip/Line nesneleri) | gpiod 2.x (Chip/LineSettings) |
# 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.
# Tüm GPIO chip'leri listele:
gpiodetect
# gpiochip0 [pinctrl-bcm2711] (58 lines)
# gpiochip1 [raspberrypi-exp-gpio] (8 lines)
# 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:
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.
# 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
# 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
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.
# 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.
#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;
}
gcc -Wall -o gpio_blink gpio_blink.c -lgpiod
sudo ./gpio_blink
Interrupt (event) ile GPIO okuma — 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;
}
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.
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
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.
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.
#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;
}
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