hal::nvic
Nested Vectored Interrupt Controller - ARM Cortex-M
~380 satır
~18 function
ARM Cortex-M
Overview
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.
Key Features
- 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
Quick Start
import hal::nvic
// Simple interrupt enable
nvic.enable_irq(nvic.USART1_IRQn, do
priority: 5 // 0-15 range
end)
// 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, do
preempt: 2,
sub: 1
end)
// Disable interrupt
nvic.disable_irq(nvic.USART1_IRQn)
// Software trigger interrupt
nvic.set_pending_irq(nvic.EXTI0_IRQn)
Types and Enums'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
}
struct NVICConfig do
irq: IRQn,
priority: int, // Combined or preemption priority
sub_priority: int = 0, // Sub-priority (if grouping allows)
enabled: bool = true
end
Example 1: Priority Grouping & Preemption
Problem: USART ve Timer interrupt'larının öncelik yönetimi
import hal::nvic, hal::usart, hal::timer
function interrupt_sistemi_init() do
// 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, do
priority: 1, // Preemption priority
sub_priority: 0
end)
// Timer2 (medium priority, preempt=3, sub=0)
nvic.enable_irq(nvic.TIM2_IRQn, do
priority: 3,
sub_priority: 0
end)
// ADC (low priority, preempt=5, sub=1)
nvic.enable_irq(nvic.ADC_IRQn, do
priority: 5,
sub_priority: 1
end)
end
// USART handler (can preempt TIM2 and ADC)
function USART1_IRQHandler() do
if usart.get_flag(usart.USART1, usart.FLAG_RXNE) do
let data = usart.receive_data(usart.USART1)
buffer_yaz(data)
usart.clear_flag(usart.USART1, usart.FLAG_RXNE)
end
end
// Timer handler (can preempt ADC, but NOT USART)
function TIM2_IRQHandler() do
if timer.get_flag(timer.TIM2, timer.FLAG_UPDATE) do
// Timer tick processing
systick_emulate()
timer.clear_flag(timer.TIM2, timer.FLAG_UPDATE)
end
end
// ADC handler (lowest priority, cannot preempt anyone)
function ADC_IRQHandler() do
if adc.get_flag(adc.ADC1, adc.FLAG_EOC) do
let 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)
end
end
Çıktı: USART interrupt'ı TIM2'ye girebilir (preempt), ama TIM2 USART'a giremez. ADC her ikisi tarafından preempt edilebilir.
Example 2: Critical Section (Disable All Interrupts)
Problem: Paylaşımlı leti koruma (thread-safe erişim)
import hal::nvic
let paylasilanOlan sayac: int = 0
function kritik_islem() do
// Disable all interrupts (PRIMASK = 1)
nvic.disable_all_interrupts()
// Critical section (atomic operation)
sayac = sayac + 1
let local_sayac = sayac
// Re-enable interrupts
nvic.enable_all_interrupts()
return local_sayac
end
// Alternative: BASEPRI (disable interrupts with priority >= threshold)
function basepri_koruma() do
// 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)
end
Çıktı: sayac leti interrupt'lardan korunur. Race condition önlenir.
Example 3: Vector Table Relocation (Bootloader)
Problem: Bootloader'dan application'a geçiş
import hal::nvic, hal::flash
const BOOTLOADER_BASE: int = 0x0800_0000
const APP_BASE: int = 0x0800_8000 // 32 KB offset
function jump_to_application() do
// Read application's stack pointer
let app_sp = *(APP_BASE cast int*)
// Read application's reset handler
let app_reset = *((APP_BASE + 4) cast int*)
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 do
'MSR MSP, r0' // Set Main Stack Pointer
end with app_sp
// Jump to application reset handler
let app_func = app_reset cast function()
app_func()
end
// Bootloader mode check
function bootloader_mode() -> bool do
// Check if user button pressed during reset
gpio.pin_init(gpio.PORT_A, 0, do mode: gpio.MODE_INPUT end)
let buton = gpio.pin_read(gpio.PORT_A, 0)
if (buton == 1) do
io.yazdir_satır('Bootloader mode (button pressed)')
return true
end
// Check if application is valid (stack pointer in RAM)
let app_sp = *(APP_BASE cast int*)
if (app_sp >= 0x2000_0000 ve app_sp < 0x2002_0000) do
io.yazdir_satır('Valid application found')
return false // Jump to app
end
io.yazdir_satır('No valid application, staying in bootloader')
return true
end
Çıktı: Bootloader 0x0800_0000'den çalışır, uygulama 0x0800_8000'e yüklenir. Reset endrası true vector table kullanılır.
API Reference
Core Functions
// Interrupt enable/disable
function enable_irq(irq: IRQn, config: NVICConfig)
function disable_irq(irq: IRQn)
// Priority configuration
function set_priority_grouping(group: PriorityGroup)
function set_priority(irq: IRQn, preempt: int, sub: int)
function get_priority(irq: IRQn) -> (int, int)
// Pending management
function set_pending_irq(irq: IRQn)
function clear_pending_irq(irq: IRQn)
function is_pending_irq(irq: IRQn) -> bool
// Active status
function is_active_irq(irq: IRQn) -> bool
// Global interrupt control
function disable_all_interrupts() // PRIMASK = 1
function enable_all_interrupts() // PRIMASK = 0
function set_basepri(priority: int) // Disable pri >= threshold
function get_basepri() -> int
// Vector table relocation
function set_vector_table(offset: int)
function get_vector_table() -> int
// System reset
function system_reset()
Important Notes:
- 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 doarken alignment (0x200) gerekli
Platform Support
| 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 endunda pending bit temizlenmeli
- Float kullanma: Cortex-M4F'de FPU lazy stacking maliyetli
- Uzun kritik bölge: disable_all_interrupts() minimum süre
Related Modules
- hal::exti - External interrupts
- hal::systick - SysTick interrupt
- hal::timer - Timer interrupts
- hal::usart - USART interrupts
- hal::dma - DMA interrupts