hal::nvic
Nested Vectored Interrupt Controller - ARM Cortex-M
~380 satır
~18 fonksiyon
ARM Cortex-M
Genel Bakış
NVIC modülü, ARM Cortex-M serisi işlemcilerde interrupt yönetimini sağlar. 240+ interrupt kaynağını 16 öncelik seviyesi ile yönetir, nested interrupts destekler ve kritik bölgeleri koruma mekanizmaları sunar. Gerçek zamanlı embedded sistemlerin omurgasıdır.
Temel Özellikler
- 240+ Interrupt: STM32F4'te 82, F7'de 97, H7'de 150+ IRQ
- 16 Priority Level: 4-bit öncelik (0=en yüksek)
- Preemption: Yüksek öncelikli interrupt düşük öncelikliye girebilir
- Sub-Priority: Aynı öncelikte kuyruk sıralaması
- Masking: PRIMASK, FAULTMASK, BASEPRI ile kesme kapatma
- Tail-Chaining: Interrupt geçişlerinde state save/restore yok
- Late Arrival: Stack sürecinde daha yüksek öncelikli interrupt gelirse hemen çalışır
- Vector Table: Flash/RAM'den relocatable interrupt vektörleri
Hızlı Başlangıç
içe_aktar hal::nvic
// Simple interrupt enable
nvic.enable_irq(nvic.USART1_IRQn, yap
priority: 5 // 0-15 range
son)
// Priority grouping (preemption vs sub-priority)
nvic.set_priority_grouping(nvic.PRIGROUP_4_4) // 4 bit preempt, 4 bit sub
// Preemption priority 2, sub-priority 1
nvic.set_priority(nvic.TIM2_IRQn, yap
preempt: 2,
sub: 1
son)
// Disable interrupt
nvic.disable_irq(nvic.USART1_IRQn)
// Software trigger interrupt
nvic.set_pending_irq(nvic.EXTI0_IRQn)
Tipler ve Enum'lar
// Priority Grouping
enum PriorityGroup {
PRIGROUP_0_4, // 0 bit preempt, 4 bit sub
PRIGROUP_1_3, // 1 bit preempt, 3 bit sub
PRIGROUP_2_2, // 2 bit preempt, 2 bit sub
PRIGROUP_3_1, // 3 bit preempt, 1 bit sub
PRIGROUP_4_0 // 4 bit preempt, 0 bit sub (default)
}
// IRQ Numbers (platform-specific)
enum IRQn {
// Cortex-M core exceptions
NMI_IRQn = -14,
HardFault_IRQn = -13,
MemManage_IRQn = -12,
BusFault_IRQn = -11,
UsageFault_IRQn = -10,
SVCall_IRQn = -5,
PendSV_IRQn = -2,
SysTick_IRQn = -1,
// STM32-specific interrupts (0+)
WWDG_IRQn = 0,
PVD_IRQn = 1,
TAMP_STAMP_IRQn = 2,
RTC_WKUP_IRQn = 3,
FLASH_IRQn = 4,
RCC_IRQn = 5,
EXTI0_IRQn = 6,
EXTI1_IRQn = 7,
// ... 80+ more
USART1_IRQn = 37,
TIM2_IRQn = 28,
DMA1_Stream0_IRQn = 11
}
yapı NVICConfig yap
irq: IRQn,
priority: sayı, // Combined or preemption priority
sub_priority: sayı = 0, // Sub-priority (if grouping allows)
enabled: mantık = doğru
son
Örnek 1: Priority Grouping & Preemption
Problem: USART ve Timer interrupt'larının öncelik yönetimi
içe_aktar hal::nvic, hal::usart, hal::timer
fonksiyon interrupt_sistemi_init() yap
// Set priority grouping: 3-bit preempt, 1-bit sub
nvic.set_priority_grouping(nvic.PRIGROUP_3_1)
// USART1 (high priority, preempt=1, sub=0)
nvic.enable_irq(nvic.USART1_IRQn, yap
priority: 1, // Preemption priority
sub_priority: 0
son)
// Timer2 (medium priority, preempt=3, sub=0)
nvic.enable_irq(nvic.TIM2_IRQn, yap
priority: 3,
sub_priority: 0
son)
// ADC (low priority, preempt=5, sub=1)
nvic.enable_irq(nvic.ADC_IRQn, yap
priority: 5,
sub_priority: 1
son)
son
// USART handler (can preempt TIM2 and ADC)
fonksiyon USART1_IRQHandler() yap
eğer usart.get_flag(usart.USART1, usart.FLAG_RXNE) yap
değişken data = usart.receive_data(usart.USART1)
buffer_yaz(data)
usart.clear_flag(usart.USART1, usart.FLAG_RXNE)
son
son
// Timer handler (can preempt ADC, but NOT USART)
fonksiyon TIM2_IRQHandler() yap
eğer timer.get_flag(timer.TIM2, timer.FLAG_UPDATE) yap
// Timer tick processing
systick_emulate()
timer.clear_flag(timer.TIM2, timer.FLAG_UPDATE)
son
son
// ADC handler (lowest priority, cannot preempt anyone)
fonksiyon ADC_IRQHandler() yap
eğer adc.get_flag(adc.ADC1, adc.FLAG_EOC) yap
değişken value = adc.read_value(adc.ADC1)
sensor_buffer[buffer_idx] = value
buffer_idx = (buffer_idx + 1) % 32
adc.clear_flag(adc.ADC1, adc.FLAG_EOC)
son
son
Çıktı: USART interrupt'ı TIM2'ye girebilir (preempt), ama TIM2 USART'a giremez. ADC her ikisi tarafından preempt edilebilir.
Örnek 2: Critical Section (Disable All Interrupts)
Problem: Paylaşımlı değişkeni koruma (thread-safe erişim)
içe_aktar hal::nvic
değişken paylasilanOlan sayac: sayı = 0
fonksiyon kritik_islem() yap
// Disable all interrupts (PRIMASK = 1)
nvic.disable_all_interrupts()
// Critical section (atomic operation)
sayac = sayac + 1
değişken local_sayac = sayac
// Re-enable interrupts
nvic.enable_all_interrupts()
dön local_sayac
son
// Alternative: BASEPRI (disable interrupts with priority >= threshold)
fonksiyon basepri_koruma() yap
// Disable interrupts with priority >= 3 (only allow 0-2)
nvic.set_basepri(3)
// Critical section
paylasilanOlan veri = veri_oku()
veri_isle(veri)
// Restore (allow all)
nvic.set_basepri(0)
son
Çıktı: sayac değişkeni interrupt'lardan korunur. Race condition önlenir.
Örnek 3: Vector Table Relocation (Bootloader)
Problem: Bootloader'dan application'a geçiş
içe_aktar hal::nvic, hal::flash
sabit BOOTLOADER_BASE: sayı = 0x0800_0000
sabit APP_BASE: sayı = 0x0800_8000 // 32 KB offset
fonksiyon jump_to_application() yap
// Read application's stack pointer
değişken app_sp = *(APP_BASE kayıt sayı*)
// Read application's reset handler
değişken app_reset = *((APP_BASE + 4) kayıt sayı*)
io.yazdir_satır('Jumping to application at 0x' + APP_BASE.onaltılıya())
// Disable all interrupts
nvic.disable_all_interrupts()
// Relocate vector table to application address
nvic.set_vector_table(APP_BASE)
// Set stack pointer
asm yap
'MSR MSP, r0' // Set Main Stack Pointer
son with app_sp
// Jump to application reset handler
değişken app_func = app_reset kayıt fonksiyon()
app_func()
son
// Bootloader mode check
fonksiyon bootloader_mode() -> mantık yap
// Check if user button pressed during reset
gpio.pin_init(gpio.PORT_A, 0, yap mode: gpio.MODE_INPUT son)
değişken buton = gpio.pin_read(gpio.PORT_A, 0)
eğer (buton == 1) yap
io.yazdir_satır('Bootloader mode (button pressed)')
dön doğru
son
// Check if application is valid (stack pointer in RAM)
değişken app_sp = *(APP_BASE kayıt sayı*)
eğer (app_sp >= 0x2000_0000 ve app_sp < 0x2002_0000) yap
io.yazdir_satır('Valid application found')
dön yanlış // Jump to app
son
io.yazdir_satır('No valid application, staying in bootloader')
dön doğru
son
Çıktı: Bootloader 0x0800_0000'den çalışır, uygulama 0x0800_8000'e yüklenir. Reset sonrası doğru vector table kullanılır.
API Referansı
Temel Fonksiyonlar
// Interrupt enable/disable
fonksiyon enable_irq(irq: IRQn, config: NVICConfig)
fonksiyon disable_irq(irq: IRQn)
// Priority configuration
fonksiyon set_priority_grouping(group: PriorityGroup)
fonksiyon set_priority(irq: IRQn, preempt: sayı, sub: sayı)
fonksiyon get_priority(irq: IRQn) -> (sayı, sayı)
// Pending management
fonksiyon set_pending_irq(irq: IRQn)
fonksiyon clear_pending_irq(irq: IRQn)
fonksiyon is_pending_irq(irq: IRQn) -> mantık
// Active status
fonksiyon is_active_irq(irq: IRQn) -> mantık
// Global interrupt control
fonksiyon disable_all_interrupts() // PRIMASK = 1
fonksiyon enable_all_interrupts() // PRIMASK = 0
fonksiyon set_basepri(priority: sayı) // Disable pri >= threshold
fonksiyon get_basepri() -> sayı
// Vector table relocation
fonksiyon set_vector_table(offset: sayı)
fonksiyon get_vector_table() -> sayı
// System reset
fonksiyon system_reset()
Önemli Notlar:
- Priority Inversion: Düşük öncelikli interrupt uzun sürerse, yüksek öncelikli bekler
- Shared Peripherals: Aynı periferali kullanan interrupt'ların önceliği dikkatli ayarlanmalı
- Stack Overflow: Nested interrupt çok derinleşirse stack overflow olabilir
- Vector Table: RAM'e relocation yaparken alignment (0x200) gerekli
Platform Desteği
| Platform | Core | IRQ Count | Priority Bits |
|---|---|---|---|
| STM32F1 | Cortex-M3 | 60 | 4 bit (16 levels) |
| STM32F4 | Cortex-M4F | 82 | 4 bit |
| STM32F7 | Cortex-M7 | 97 | 4 bit |
| STM32H7 | Cortex-M7 | 150 | 4 bit |
| GD32VF103 | RISC-V | 87 (ECLIC) | 4 bit |
Best Practices
- Priority tasarımı: Kritik işlemler 0-3, normal 4-10, arka plan 11-15
- Kısa handler: ISR'da minimum işlem, flag set + main'de işle
- Sub-priority kullan: Aynı öncelikteki interrupt'ları sırala
- Pending temizle: Handler sonunda pending bit temizlenmeli
- Float kullanma: Cortex-M4F'de FPU lazy stacking maliyetli
- Uzun kritik bölge: disable_all_interrupts() minimum süre
İlgili Modüller
- hal::exti - External interrupts
- hal::systick - SysTick interrupt
- hal::timer - Timer interrupts
- hal::usart - USART interrupts
- hal::dma - DMA interrupts