⚡ hal::exti

External Interrupts - GPIO Pin Event Detection

~360 satır ~15 function STM32/GD32/ESP32

📖 Overview

EXTI modülü, GPIO pinlerinde meydana gelen değişiklikleri (rising edge, falling edge) yakalayarak interrupt üretir. Buton basımları, harici sensör sinyalleri ve asenkron olaylar for kritik öneme sahiptir. Polling gerektirmeden düşük güç tüketimi ve yüksek tepki süresi sağlar.

🔑 Key Features

🚀 Quick Start

import hal::exti, hal::gpio, hal::nvic

// Button pin setup (PA0 - User Button)
gpio.pin_init(gpio.PORT_A, 0, do
    mode: gpio.MODE_INPUT,
    pull: gpio.PULL_DOWN,
    speed: gpio.SPEED_LOW
end)

// EXTI configuration (falling edge for button press)
let exti_cfg = exti.init(0, do  // EXTI line 0 -> PA0
    trigger: exti.TRIGGER_FALLING,
    mode: exti.MODE_INTERRUPT,
    callback: buton_callback
end)

// Enable interrupt in NVIC
nvic.enable_irq(nvic.EXTI0_IRQn, do
    priority: 5
end)

// Callback function
function buton_callback() do
    // Button pressed!
    led_toggle()
end

📦 Types and Enums'lar

// EXTI Lines (0-15 for GPIO pins)
enum Line {
    LINE_0, LINE_1, LINE_2, LINE_3, LINE_4,
    LINE_5, LINE_6, LINE_7, LINE_8, LINE_9,
    LINE_10, LINE_11, LINE_12, LINE_13, LINE_14, LINE_15,
    LINE_PVD = 16,    // PVD output
    LINE_RTC = 17,    // RTC alarm
    LINE_USB = 18,    // USB wakeup
    LINE_ETH = 19     // Ethernet wakeup
}

// Trigger Mode
enum Trigger {
    TRIGGER_RISING,   // 0->1 transition
    TRIGGER_FALLING,  // 1->0 transition
    TRIGGER_BOTH      // Any edge
}

// EXTI Mode
enum Mode {
    MODE_INTERRUPT,   // Generate interrupt
    MODE_EVENT        // Generate event (for DMA)
}

struct EXTIConfig do
    trigger: Trigger,
    mode: Mode,
    callback: function(),
    debounce_ms: int = 0  // Software debounce
end

💡 Example 1: Button with Debounce

Problem: Mekanik butonlarda bouncing problemi

import hal::exti, hal::gpio, hal::systick

let end_basin_zamani: int64 = 0
const DEBOUNCE_SURESI: int = 50  // 50 ms

function buton_init() do
    // PA0 -> User Button (Active Low)
    gpio.pin_init(gpio.PORT_A, 0, do
        mode: gpio.MODE_INPUT,
        pull: gpio.PULL_UP
    end)
    
    // EXTI0 -> Falling edge (button press)
    exti.init(0, do
        trigger: exti.TRIGGER_FALLING,
        mode: exti.MODE_INTERRUPT,
        callback: buton_handler
    end)
    
    nvic.enable_irq(nvic.EXTI0_IRQn, 5)
end

function buton_handler() do
    let simdiki_zaman = systick.get_ms()
    
    // Debounce check
    if (simdiki_zaman - end_basin_zamani < DEBOUNCE_SURESI) do
        return  // Ignore bouncing
    end
    
    end_basin_zamani = simdiki_zaman
    
    // Real button press detected
    buton_basildi()
end

function buton_basildi() do
    io.yazdir_satır("Button pressed!")
    led_toggle()
end

Çıktı: Bouncing problemi olmadan temiz buton algılama. 50ms forde tekrar tetiklemeleri filtreler.

💡 Example 2: Rotary Encoder (Quadrature Decoder)

Problem: Mekanik encoder ile konum okuma

import hal::exti, hal::gpio

let encoder_konum: int = 0
let end_A: bool = false
let end_B: bool = false

function encoder_init() do
    // PA1 -> Encoder A channel
    gpio.pin_init(gpio.PORT_A, 1, do
        mode: gpio.MODE_INPUT,
        pull: gpio.PULL_UP
    end)
    
    // PA2 -> Encoder B channel
    gpio.pin_init(gpio.PORT_A, 2, do
        mode: gpio.MODE_INPUT,
        pull: gpio.PULL_UP
    end)
    
    // EXTI on both channels (both edges)
    exti.init(1, do
        trigger: exti.TRIGGER_BOTH,
        callback: encoder_A_handler
    end)
    
    exti.init(2, do
        trigger: exti.TRIGGER_BOTH,
        callback: encoder_B_handler
    end)
    
    nvic.enable_irq(nvic.EXTI1_IRQn, 5)
    nvic.enable_irq(nvic.EXTI2_IRQn, 5)
    
    // Read initial state
    end_A = gpio.pin_read(gpio.PORT_A, 1)
    end_B = gpio.pin_read(gpio.PORT_A, 2)
end

function encoder_A_handler() do
    let A = gpio.pin_read(gpio.PORT_A, 1)
    let B = end_B
    
    // Quadrature decoding logic
    if (A != end_A) do
        if (A == B) do
            encoder_konum = encoder_konum + 1  // CW
        else do
            encoder_konum = encoder_konum - 1  // CCW
        end
    end
    
    end_A = A
end

function encoder_B_handler() do
    let A = end_A
    let B = gpio.pin_read(gpio.PORT_A, 2)
    
    if (B != end_B) do
        if (A != B) do
            encoder_konum = encoder_konum + 1  // CW
        else do
            encoder_konum = encoder_konum - 1  // CCW
        end
    end
    
    end_B = B
end

function encoder_oku() -> int do
    return encoder_konum
end

Çıktı: Rotary encoder konumunu gerçek zamanlı takip eder. CW/CCW yönü otomatik algılanır.

💡 Example 3: Multi-Source Priority Handling

Problem: Birden fazla interrupt kaynağını öncelik sırasına göre yönetme

import hal::exti, hal::gpio, hal::nvic

const KRITIK_OLAY_PIN: int = 0   // PA0 -> Emergency stop
const NORMAL_OLAY_PIN: int = 1   // PA1 -> Regular sensor
const DUSUK_OLAY_PIN: int = 2    // PA2 -> Low priority event

function coklu_interrupt_init() do
    // Critical event (highest priority)
    gpio.pin_init(gpio.PORT_A, KRITIK_OLAY_PIN, do
        mode: gpio.MODE_INPUT, pull: gpio.PULL_UP
    end)
    exti.init(KRITIK_OLAY_PIN, do
        trigger: exti.TRIGGER_FALLING,
        callback: kritik_handler
    end)
    nvic.enable_irq(nvic.EXTI0_IRQn, 0)  // Highest priority
    
    // Normal event (medium priority)
    gpio.pin_init(gpio.PORT_A, NORMAL_OLAY_PIN, do
        mode: gpio.MODE_INPUT, pull: gpio.PULL_UP
    end)
    exti.init(NORMAL_OLAY_PIN, do
        trigger: exti.TRIGGER_FALLING,
        callback: normal_handler
    end)
    nvic.enable_irq(nvic.EXTI1_IRQn, 5)  // Medium priority
    
    // Low priority event
    gpio.pin_init(gpio.PORT_A, DUSUK_OLAY_PIN, do
        mode: gpio.MODE_INPUT, pull: gpio.PULL_UP
    end)
    exti.init(DUSUK_OLAY_PIN, do
        trigger: exti.TRIGGER_FALLING,
        callback: dusuk_handler
    end)
    nvic.enable_irq(nvic.EXTI2_IRQn, 10)  // Low priority
end

function kritik_handler() do
    // Emergency stop - preempt everything!
    motor_durdur()
    alarm_calar()
    io.yazdir_satır("CRITICAL: Emergency stop!")
end

function normal_handler() do
    // Regular sensor reading
    let deger = sensor_oku()
    io.yazdir_satır("Sensor value: " + deger.metne())
end

function dusuk_handler() do
    // Low priority background task
    log_olay("Low priority event")
end

Çıktı: Kritik olaylar düşük öncelikli interrupt'ları preempt eder. Sistem güvenliği sağlanır.

📚 API Reference

Core Functions

// EXTI hattını structlandırır
function init(line: int, config: EXTIConfig) -> Result

// EXTI hattını etkinleştirir
function enable(line: int)

// EXTI hattını devre dışı bırabreak
function disable(line: int)

// Pending flag'i temizler
function clear_pending(line: int)

// Pending flag'i kontrol eder
function is_pending(line: int) -> bool

// Software interrupt tetikler
function trigger_software(line: int)

// GPIO pin ile EXTI hattını matchir
function connect_gpio(line: int, port: gpio.Port, pin: int)
⚠️ Important Notes:
  • Shared IRQs: EXTI5-9 ve EXTI10-15 paylaşımlı IRQ kullanır
  • Pin Mapping: Aynı pin numarası farklı portlarda aynı EXTI hattını kullanır (PA0, PB0, PC0 -> EXTI0)
  • Callback Safety: Callback functionları kısa olmalı (ağır işlemler main loop'a ertelenmeli)
  • Debounce: Donanım debounce yok, yazılım filtreleme gerekli

🖥️ Platform Support

Platform EXTI Hatları Interrupt Groups Event Mode
STM32F1 20 (0-15 + 4 özel) 7 IRQ Evet
STM32F4 23 (0-15 + 8 özel) 7 IRQ Evet
STM32F7 23 7 IRQ Evet
GD32VF103 20 ECLIC (47 IRQ) Evet
ESP32 Tüm GPIO 2 CPU core Hayır

✅ Best Practices

🔗 Related Modules

🔙 HAL Modules | 🌐 Turkish