⚡ 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
- 16 EXTI Hattı: EXTI0-EXTI15 (GPIO pinleri for)
- Trigger Modları: Rising edge, falling edge veya her ikisi
- Maskeleme: Interrupt enable/disable desteği
- Software Interrupt: Yazılımdan interrupt tetikleme
- Event Mode: Interrupt olmadan DMA tetikleme
- Wake-up Source: Stop/Standby modundan çıkış
- Debounce Desteği: Yazılım bazlı gürültü filtreleme
- Priority Control: NVIC ile öncelik yönetimi
🚀 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
- ✅ Debounce kullan: Mekanik butonlarda 20-50ms debounce uygula
- ✅ Callback kısa olsun: Interrupt handler'da minimum işlem do, flag set et
- ✅ Priority ayarla: Kritik olaylar for düşük priority numarası kullan
- ✅ Pending temizle: Interrupt handler endunda pending bit'i temizle
- ❌ Float işlem doma: Interrupt'ta floating-point hesaplamalardan kaçın
- ❌ Uzun delay kullanma: Blocking delay callback forde kullanılmamalı
🔗 Related Modules
- hal::gpio - GPIO pin configuration
- hal::nvic - Interrupt priority management
- hal::power - Low power modes with wakeup
- hal::systick - Timing for debounce
- hal::timer - Hardware encoder mode