Home Wiki Electricity & Electrons Interrupts and RTOS: Instant Response to Real-World Events
Electricity & Electrons

Interrupts and RTOS: Instant Response to Real-World Events

What Is an Interrupt and Why You Need One

An interrupt pauses the currently executing code, runs a short handler function, and returns to the original code exactly where it left off. Instead of constantly polling, the MCU reacts instantly when an event occurs.

In a factory, a motor stall sensor must trigger emergency stop within microseconds. The NVIC on Cortex-M manages up to 240 interrupt sources with configurable priorities. Higher-priority interrupts preempt lower-priority ones, so safety-critical events always take precedence.

External Interrupts: Instant Response to Events

External interrupts trigger on GPIO pin edges — rising, falling, or both. This pattern counts pulses from flow meters, RPM sensors, and encoders:

volatile uint32_t pulse_count = 0;

void exti_init(void) {
    GPIO_InitTypeDef gpio = {0};
    gpio.Pin = GPIO_PIN_0; gpio.Mode = GPIO_MODE_IT_RISING;
    gpio.Pull = GPIO_PULLDOWN;
    HAL_GPIO_Init(GPIOA, &gpio);
    HAL_NVIC_SetPriority(EXTI0_IRQn, 1, 0);
    HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}

void HAL_GPIO_EXTI_Callback(uint16_t pin) {
    if (pin == GPIO_PIN_0) pulse_count++;
}

The main loop periodically reads and resets the counter to calculate flow rate or RPM.

Timer Interrupts: Precise Periodic Tasks

Timer interrupts fire at exact intervals for deterministic sensor sampling and control loops. The handler sets a flag rather than performing heavy work — keeping interrupts short is critical.

volatile bool sensor_flag = false;

void TIM7_IRQHandler(void) {
    if (__HAL_TIM_GET_FLAG(&htim7, TIM_FLAG_UPDATE)) {
        __HAL_TIM_CLEAR_FLAG(&htim7, TIM_FLAG_UPDATE);
        sensor_flag = true;  // Signal main loop to read sensors
    }
}

Interrupt Pitfalls: Race Conditions and Volatile

Interrupts introduce concurrency. The volatile keyword forces the compiler to read actual memory each time, preventing optimization bugs with shared variables.

// WRONG: compiler may cache the value
uint32_t counter = 0;

// CORRECT: volatile forces re-read from memory
volatile uint32_t counter = 0;

Critical sections protect multi-byte reads from corruption when an interrupt fires mid-access:

uint32_t safe_read_counter(void) {
    __disable_irq();
    uint32_t value = pulse_count;
    __enable_irq();
    return value;
}

Rules for interrupt handlers: keep them short, never call blocking functions, never allocate memory, always use volatile for shared variables, and use critical sections for multi-byte data.

FreeRTOS: A Minimal Real-Time Operating System

When bare-metal firmware grows too complex, FreeRTOS provides structured multitasking with priorities. It runs on Cortex-M with as little as 4 KB of RAM.

#include "FreeRTOS.h"
#include "task.h"

void sensor_task(void *params) {
    while (1) {
        float temp = read_temperature(&hadc1);
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

int main(void) {
    HAL_Init();
    SystemClock_Config();
    xTaskCreate(sensor_task, "Sensors", 256, NULL, 2, NULL);
    vTaskStartScheduler();
    while (1) {}
}

Tasks, Priorities, and Queues in FreeRTOS

FreeRTOS uses preemptive scheduling. Industrial priority hierarchy: safety interlocks (4), motor control (3), sensor reading (2), logging (1). Queues safely pass data between tasks:

typedef struct { float temperature, current; } SensorData;
QueueHandle_t sensor_queue;

void sensor_task(void *p) {
    while (1) {
        SensorData d = { read_temperature(&hadc1), read_current(&hadc2) };
        xQueueSend(sensor_queue, &d, portMAX_DELAY);
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

Practical Example: Multi-Task System Reading Sensors and Controlling Motors

This example uses an external interrupt for emergency stop (highest priority) and a FreeRTOS task for PID motor control.

SemaphoreHandle_t emergency_sem;

void HAL_GPIO_EXTI_Callback(uint16_t pin) {
    if (pin == GPIO_PIN_1) {
        BaseType_t w = pdFALSE;
        xSemaphoreGiveFromISR(emergency_sem, &w);
        portYIELD_FROM_ISR(w);
    }
}

void safety_task(void *p) {
    while (1) {
        xSemaphoreTake(emergency_sem, portMAX_DELAY);
        motor_set_speed(&motor, 0);
    }
}

void control_task(void *p) {
    PidController pid;
    pid_init(&pid, 2.0f, 0.5f, 0.1f);
    while (1) {
        float duty = pid_compute(&pid, 45.0f, read_temperature(&hadc1), 0.01f);
        motor_set_speed(&motor, (int16_t)duty);
        vTaskDelay(pdMS_TO_TICKS(10));
    }
}

int main(void) {
    HAL_Init();
    SystemClock_Config();
    emergency_sem = xSemaphoreCreateBinary();
    xTaskCreate(safety_task,  "Safety",  256, NULL, 4, NULL);
    xTaskCreate(control_task, "Control", 512, NULL, 3, NULL);
    vTaskStartScheduler();
    while (1) {}
}

Summary

Interrupts let the MCU respond instantly to hardware events without wasting CPU cycles on polling. External interrupts react to GPIO edges, timer interrupts create precise periodic schedules. Shared data requires volatile and critical sections to prevent race conditions. FreeRTOS provides structured multitasking with prioritized tasks, queues for safe data passing, and semaphores for event signaling. Industrial systems assign priorities by criticality — safety first, then control, then communication. The next lesson introduces ESP32 and wireless IIoT with WiFi and MQTT.

interrupts RTOS FreeRTOS real-time ISR multitasking المقاطعات نظام التشغيل الحقيقي الاستجابة الفورية المهام المتعددة الأولويات التزامن