STM32'de IRQ Gecikme Ölçümü: GPIO + Osiloskop
Neden Yazılım Timestamp Yanlıştır?
Firmware geliştiricilerin IRQ gecikme ölçümünde yaptığı en yaygın hata DWT->CYCCNT veya TIM->CNT okumaktır. Bu yöntem ISR giriş noktasından itibaren zamanı ölçer — interrupt sinyalinin donanımda üretilmesi ile ISR çalışması arasındaki süreyi değil.
Standart STM32H7 NVIC latency (Cortex-M7 dokümantasyonu): 12 cycle Gerçek observed latency (pipeline, cache miss, interrupt masking): 48–180 cycle
Bu fark, yüksek frekanslı kontrol döngülerinde (10kHz+) ciddi jitter kaynağıdır.
Doğru Yöntem: GPIO Toggle
Prensip: Interrupt kaynağı aktif olduğunda bir GPIO set, ISR'ın ilk satırında reset. Osiloskop bu iki kenar arasındaki süreyi ölçer.
// Interrupt kaynağı konfigürasyonu (TIM2 örneği)
void TIM2_IRQHandler(void) {
// ISR'ın ilk ifadesi — cache miss öncesi
GPIOA->BSRR = GPIO_BSRR_BR15; // PA15 → LOW
// ... gerçek ISR işlemleri ...
HAL_TIM_IRQHandler(&htim2);
}
// Timer callback'te GPIO set
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM2) {
GPIOA->BSRR = GPIO_BSRR_BS15; // PA15 → HIGH (sonraki periyot için hazırla)
}
}
Kritik:
GPIOA->BSRR = GPIO_BSRR_BR15doğrudan register yazımı kullanın.HAL_GPIO_WritePin()fonksiyonu ek döngüler ekler ve ölçümü bozar.
Osiloskop Konfigürasyonu
- Kanal A: Interrupt kaynağı (timer output veya external trigger)
- Kanal B: GPIO (PA15)
- Trigger: Kanal A yükselen kenar
- Zaman tabanı: 1µs/div (STM32H7 @ 480MHz için 50ns/div önerilir)
- Ölçüm: Kanal A yükselen kenar → Kanal B düşen kenar arası
STM32H7 Örnek Sonuçlar
| Durum | Ölçülen IRQ Latency |
|---|---|
| Sıfır yük, I-cache açık | 125ns (60 cycle @ 480MHz) |
| Aktif FPU işlemi (float context save) | 312ns |
| DCache miss (ilk çağrı) | 480ns |
| FreeRTOS task context'i içinden | 720ns |
| RTOS + DCache miss | ~1.1µs |
NVIC Priority Masking Tuzağı
FreeRTOS kullanan projelerde configMAX_SYSCALL_INTERRUPT_PRIORITY yanlış ayarlanmışsa taskENTER_CRITICAL() bölümleri tüm interrupt'ları maskeleyebilir:
// stm32h7xx_hal_conf.h
// Bu değer NVIC_EncodePriority ile encode edilmiş değerdir
#define configMAX_SYSCALL_INTERRUPT_PRIORITY (5 << 4) /* Priority 5 */
// Priority 5 ve üzeri interrupt'lar critical section içinde maskelenir
// EtherCAT veya motor kontrol IRQ'ları bu eşiğin altında olmalı
NVIC_SetPriority(TIM2_IRQn, 4); // Critical section'dan etkilenmez
cyclictest Eşdeğeri: latency_test Pattern
Linux cyclictest aracına benzer şekilde, STM32'de minimum/maksimum/ortalama jitter ölçebilirsiniz:
#define SAMPLE_COUNT 100000
uint32_t samples[SAMPLE_COUNT];
uint32_t min_lat = UINT32_MAX, max_lat = 0, idx = 0;
// DWT cycle counter aktif et
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CYCCNT = 0;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
void TIM2_IRQHandler(void) {
uint32_t lat = DWT->CYCCNT; // ISR giriş timestamp
GPIOA->BSRR = GPIO_BSRR_BR15;
// Beklenen periyot cycle sayısı çıkar (480MHz * 100µs = 48000)
uint32_t jitter = lat > 48000 ? lat - 48000 : 48000 - lat;
if (idx < SAMPLE_COUNT) samples[idx++] = jitter;
if (jitter < min_lat) min_lat = jitter;
if (jitter > max_lat) max_lat = jitter;
DWT->CYCCNT = 0; // Reset for next cycle
HAL_TIM_IRQHandler(&htim2);
}
