In short
A real-time operating system is a small, deterministic kernel that guarantees a task will run within a known, bounded time — correctness depends not only on the computed result but on *when* it appears. It replaces the single while(1) superloop with multiple prioritised tasks, a scheduler, and safe mechanisms (semaphores, mutexes, queues) for them to share a single CPU. RTOS is a standard unit of the embedded-systems paper in most AICTE/CBCS B.E. ECE curricula; GATE EC carries no operating-systems section, so scheduling, synchronisation and deadlock are examined in GATE CS and in university and placement papers. Indian employers — Bosch, Continental, KPIT, Tata Elxsi, L&T Technology Services, HCL, Wipro, Qualcomm, ISRO/DRDO labs and automotive Tier-1s — test FreeRTOS APIs, scheduling maths and priority inversion in almost every embedded placement interview.
Explained
What makes an operating system "real-time"
- Determinism, not speed. An RTOS is judged by the *worst-case* bound on response, not average throughput. A GPOS (Windows, desktop Linux) optimises average throughput and fairness; an RTOS optimises predictability.
- Hard real time: missing a deadline is a system failure — airbag deployment, ABS braking, flight control, motor commutation, pacemaker.
- Firm real time: a late result is useless but not catastrophic — a dropped frame in machine vision, a missed sensor sample.
- Soft real time: a late result loses value gradually — video playback, GPS display refresh, infotainment UI.
- Key timing quantities: release time, period T, deadline D, worst-case execution time C, response time R, jitter (variation in start/finish), interrupt latency, and context-switch time.
- WCET (worst-case execution time) must be bounded; this is why RTOS code avoids unbounded loops, recursion,
malloc()in steady state, and why caches/pipelines complicate analysis. - Foreground-background (superloop) is the alternative: ISRs (foreground) plus a
while(1)loop (background). Simple and cheap, but response time = worst full loop pass, and the structure collapses as tasks multiply.
Tasks, states and the scheduler
- A task (thread) is an independent function with its own stack, priority, and TCB (Task Control Block: stack pointer, priority, state, saved registers, task name, tick counters).
- Task states: Running (on CPU — only one per core), Ready (runnable, waiting for CPU), Blocked (waiting on a semaphore, queue, event or delay, with a timeout), Suspended (removed from scheduling until resumed), Dormant/Deleted.
- Context switch: save current task's CPU registers onto its stack, update its TCB, pick the next task, restore its registers. On Cortex-M this is done in the PendSV handler, with SysTick giving the periodic tick (typically 1 ms).
- Preemptive scheduling: the highest-priority ready task always runs; it preempts a lower-priority task immediately. This is the default and the only form that gives good hard-real-time behaviour.
- Cooperative (non-preemptive): a switch happens only when the running task yields or blocks. Safer for shared data, but one badly written task freezes the system.
- Round-robin / time slicing: equal-priority ready tasks share the CPU tick by tick; adds fairness but also jitter.
- Idle task: created automatically at the lowest priority; frees deleted tasks' memory and is the hook for putting the MCU into sleep (tickless idle) for battery-powered designs.
- Rate Monotonic (RMS): static priority, shorter period ⇒ higher priority. Optimal among fixed-priority algorithms for periodic, independent tasks with D = T.
- Deadline Monotonic (DMS): static priority by shortest *deadline*; used when D < T.
- Earliest Deadline First (EDF): dynamic priority, the task whose absolute deadline is nearest runs. Optimal for uniprocessors — schedulable whenever total utilisation ≤ 1 — but behaves unpredictably on overload (domino effect) and costs more at run time.
- Least Laxity First (LLF): priority by slack = (deadline − now − remaining execution); optimal but thrashes with frequent switches.
Inter-task communication and synchronisation
- Shared global + critical section: disable interrupts or take the scheduler lock around the access. Simplest, but blocks everything — keep it to a few microseconds.
- Binary semaphore: a signal (0/1) used for synchronisation, typically ISR gives, task takes — the classic "deferred interrupt processing" pattern that keeps ISRs short.
- Counting semaphore: counts resources or events (e.g. 4 DMA buffers available), or counts how many times an ISR fired.
- Mutex: for mutual exclusion only. It has ownership (only the taker may give it) and, unlike a semaphore, implements priority inheritance. Must never be used from an ISR.
- Recursive mutex: the owning task may take it several times; it must give it the same number of times.
- Message queue (mailbox, pipe): copies fixed-size items between tasks or from an ISR, with blocking send/receive and timeouts. The preferred way to pass data — it transfers ownership, so no extra locking.
- Event flags / event groups: a task waits on a bit pattern with AND/OR semantics — useful for "wait until sensor ready AND buffer free".
- Direct-to-task notifications (FreeRTOS): a lightweight 32-bit value stored in the TCB; far faster and smaller than a semaphore for 1-to-1 signalling.
- Stream/message buffers: single-writer, single-reader byte or packet streams, used with UART/DMA.
- Software timers: one-shot or auto-reload callbacks run in a timer service (daemon) task, not in an ISR — so callbacks must never block.
Priority inversion, deadlock and the classic hazards
- Priority inversion: a high-priority task H blocks on a mutex held by a low-priority task L; a medium-priority task M preempts L, so H effectively waits behind M. *Unbounded* inversion is the dangerous form — the Mars Pathfinder 1997 resets are the textbook case.
- Priority inheritance: while L holds a mutex wanted by H, L is temporarily raised to H's priority, so M cannot preempt it. Bounds the inversion; implemented in FreeRTOS mutexes and POSIX
PTHREAD_PRIO_INHERIT. Does not prevent deadlock. - Priority ceiling protocol (PCP / immediate ceiling, ICPP): every resource carries a ceiling = highest priority of any task that may use it; a task taking it is raised to that ceiling at once. Prevents both unbounded inversion *and* deadlock, and limits blocking to one critical section.
- Deadlock: circular wait on two mutexes taken in opposite order. Prevent by a fixed global locking order, by taking with a timeout, or by never nesting locks.
- Starvation / livelock: a low-priority task never runs because higher-priority tasks keep the CPU busy; fix by aging, slicing or re-partitioning work.
- Rate of interrupts: an ISR runs above *all* tasks, so a flood of interrupts starves the scheduler regardless of priorities.
- Reentrancy: functions shared between tasks must use only stack/parameters, not static data (
strtok, oldprintfand non-reentrant library calls are classic faults). - Stack overflow: each task has its own stack; a deep call chain or a big local array corrupts the neighbouring task. Use watermark checks (
uxTaskGetStackHighWaterMark), overflow hooks, and an MPU where available.
Memory, interrupts and kernel internals
- Static allocation (
xTaskCreateStatic) is preferred in safety and certification work — no heap fragmentation, memory sized at build time. - FreeRTOS heap schemes: heap_1 (allocate only, no free), heap_2 (free, but fragments), heap_3 (wraps
malloc/free, not thread-deterministic), heap_4 (coalescing — the usual choice), heap_5 (multiple non-contiguous regions). - Memory protection (MPU): Cortex-M MPU or FreeRTOS-MPU puts unprivileged tasks in sandboxes; needed for IEC 61508 / ISO 26262 partitioning.
- ISR rules: keep ISRs minimal; call only the
...FromISR()APIs; never block or take a mutex in an ISR; always honour the maximum syscall interrupt priority (configMAX_SYSCALL_INTERRUPT_PRIORITYon Cortex-M) — interrupts above it are never masked by the kernel and therefore must not call kernel APIs. - Deferred interrupt handling: the ISR gives a semaphore / sends a notification and requests a context switch via
portYIELD_FROM_ISR(xHigherPriorityTaskWoken), so the real work happens in a prioritised task. - Tick interrupt: the periodic heartbeat for delays and timeouts. Resolution = 1 tick, so
vTaskDelay(1)may sleep anywhere from 0 to 1 tick; usevTaskDelayUntil/xTaskDelayUntilfor true periodic behaviour without drift. - Tickless idle: suppress the tick while idle and sleep the MCU — the standard low-power technique in battery IoT nodes.
- Kernel architectures: monolithic (fast, no isolation), microkernel (QNX, Integrity — only scheduling/IPC in kernel space, drivers as servers; robust and certifiable), exokernel/hypervisor-based (ARINC 653 partitions in avionics, automotive hypervisors).
Common RTOSes, standards and Indian practice
- FreeRTOS (MIT licence, now AWS-stewarded) — the de-facto teaching and industry kernel on Cortex-M/ESP32; SAFERTOS is its certified sibling. Zephyr (Linux Foundation, Apache-2.0) is growing fast in Indian IoT product teams.
- Others: VxWorks and Integrity (avionics, defence), QNX (automotive cockpits), µC/OS-II/III, RTEMS (space), ThreadX/Eclipse ThreadX, Mbed OS, ChibiOS, RIOT.
- Embedded Linux is not hard real time by itself. PREEMPT_RT (mainlined into Linux 6.12, 2024) makes nearly all kernel code preemptible and gives low-tens-of-microseconds latency; Xenomai runs a co-kernel alongside Linux for tighter bounds.
- POSIX 1003.1b/1003.13 real-time profiles standardise
pthread,sched_setscheduler(SCHED_FIFO, SCHED_RR),sem_*,mq_*,timer_create— examinable as the "POSIX RTOS API". - AUTOSAR Classic / OSEK-VDX (ISO 17356) is the automotive task model (BCC1/BCC2/ECC1/ECC2, static configuration via OIL) used by every Indian automotive ECU team; AUTOSAR Adaptive runs on POSIX for high-performance ECUs.
- Safety standards: IEC 61508 (generic, SIL 1-4), ISO 26262 (automotive, ASIL A-D), DO-178C (airborne), IEC 62304 (medical), IEC 61511 (process). These drive static allocation, certified kernels and timing analysis evidence.
- Indian context: ISRO/VSSC use RTEMS and in-house kernels on launch-vehicle avionics; DRDO and HAL work to DO-178C; BEL and automotive Tier-1s in Pune, Bengaluru and Chennai work to AUTOSAR/ISO 26262. BIS adopts IEC standards as IS/IEC equivalents. Hardware commonly used in Indian labs: STM32, ESP32, TI Hercules/C2000, NXP S32K, and Raspberry Pi/BBB running PREEMPT_RT.
Key points
- RTOS - a deterministic kernel giving a *bounded* worst-case response, not necessarily a fast average one.
- Hard vs soft - hard = missed deadline is system failure; soft = value degrades gracefully.
- Task states - Running, Ready, Blocked, Suspended; only one Running task per core.
- Preemptive priority scheduling - the highest-priority *ready* task always owns the CPU.
- Context switch - save registers + TCB, select next task, restore; on Cortex-M done in PendSV.
- Interrupt latency - time from the hardware event to the first instruction of the ISR; worst case is set by the longest interrupt-disabled region.
- Response time - latency + ISR time + scheduler time + preemption by higher-priority tasks.
- RMS - shorter period gets higher priority; schedulable if U ≤ n(21/n − 1); sufficient, not necessary.
- EDF - nearest absolute deadline runs; schedulable iff U ≤ 1 (for D = T); poor overload behaviour.
- Semaphore vs mutex - semaphore signals events (no owner); mutex protects data (has owner + priority inheritance).
- Priority inversion - high-priority task delayed by a medium task via a resource held by a low-priority task.
- Priority inheritance / ceiling - inheritance bounds inversion; ceiling protocol also prevents deadlock.
- Deadlock - circular wait on nested locks; prevented by a fixed lock ordering or timeouts.
- Queue over globals - message queues copy data and remove the need for ad-hoc locking.
- ISR discipline - short ISRs,
FromISRAPIs only, never block, defer the work to a task. - Jitter - variation in task start/finish times; use
vTaskDelayUntilfor drift-free periodic tasks. - Static allocation - avoids heap fragmentation and is mandatory in most certified (IEC 61508 / ISO 26262) builds.
- Tickless idle - suppress the tick and sleep between events for battery-powered nodes.
Formulas and examples
Liu–Layland RMS bound U ≤ n (21/n − 1) — n = number of periodic tasks; the bound falls from 1.000 (n=1) to 0.828 (2), 0.780 (3), 0.757 (4) → ln 2 ≈ 0.693 as n → ∞. Sufficient only.
EDF / dynamic-priority bound U ≤ 1 for deadlines equal to periods (necessary and sufficient, uniprocessor, independent tasks).
Response-time analysis (fixed priority) Rᵢ = Cᵢ + Bᵢ + Σ_(j ∈ hp(i)) ⌈Rᵢ / Tⱼ⌉ Cⱼ — solved by iteration from Rᵢ⁰ = Cᵢ; Bᵢ = worst blocking by a lower-priority task; schedulable if Rᵢ ≤ Dᵢ.
Task response time (practical) R = interrupt latency + ISR execution + scheduler/context-switch time + preemption time
Scheduler (tick) overhead Overhead % = (t_tick_handler / T_tick) × 100
Timer tick resolution T_tick = 1 / f_tick ; worst-case delay error for vTaskDelay(n) ≈ one full tick.
Hyperperiod H = LCM(T₁, T₂, … Tₙ) — the length of the repeating schedule used in timeline drawing.
Context-switch time t_cs = (cycles to save + restore context) / f_CPU
Example 1 — RMS schedulability test. Tasks: T1 (C = 1 ms, T = 4 ms), T2 (C = 2 ms, T = 6 ms), T3 (C = 1 ms, T = 12 ms).
- U = 1/4 + 2/6 + 1/12 = 0.25 + 0.3333 + 0.0833 = 0.6667 (66.7 %)
- RMS bound for n = 3: 3(21/3 − 1) = 3(1.2599 − 1) = 3 × 0.2599 = 0.7798
- 0.6667 ≤ 0.7798 → schedulable under RMS (priorities: T1 highest, T3 lowest). It is also EDF-schedulable since U ≤ 1.
Example 2 — a set that fails the bound but is still schedulable (why the test is only sufficient). Tasks: T1 (C = 2, T = 5), T2 (C = 3, T = 8), T3 (C = 1, T = 10) ms.
- U = 0.4 + 0.375 + 0.1 = 0.875 > 0.7798 → the RMS bound fails, so the simple test gives no answer.
- Response-time analysis for the lowest-priority task T3 (B = 0):
R⁰ = 1 → R¹ = 1 + ⌈1/5⌉×2 + ⌈1/8⌉×3 = 1 + 2 + 3 = 6 R² = 1 + ⌈6/5⌉×2 + ⌈6/8⌉×3 = 1 + 4 + 3 = 8 R³ = 1 + ⌈8/5⌉×2 + ⌈8/8⌉×3 = 1 + 4 + 3 = 8 ms (converged)
- R3 = 8 ms ≤ D3 = 10 ms. Checking T2: R = 3 → 3 + ⌈3/5⌉×2 = 5 → 3 + ⌈5/5⌉×2 = 5 ms ≤ 8 ms. T1: R = 2 ms ≤ 5 ms.
- All deadlines met — the set is schedulable under RMS even though U exceeded the Liu–Layland bound. Since U = 0.875 ≤ 1, EDF also schedules it.
Example 3 — interrupt latency and response time budget. A 72 MHz Cortex-M3 reads an encoder. Longest interrupt-disabled critical section = 8 µs; NVIC + stacking latency = 12 cycles; ISR body = 300 cycles; context switch = 120 cycles; the handling task is the highest priority.
- Cycle time = 1/72 MHz = 13.89 ns.
- Hardware latency = 12 × 13.89 ns ≈ 0.167 µs; worst-case masking adds 8 µs → latency ≈ 8.17 µs.
- ISR body = 300 × 13.89 ns ≈ 4.17 µs; context switch = 120 × 13.89 ns ≈ 1.67 µs.
- Worst-case response R ≈ 8.17 + 4.17 + 1.67 ≈ 14.0 µs. If the encoder pulses at 20 kHz (50 µs apart), the budget holds with ~36 µs margin.
Example 4 — tick overhead and timing resolution. configTICK_RATE_HZ = 1000 on the same 72 MHz MCU; the tick handler (including scheduler run) takes 90 cycles.
- T_tick = 1/1000 = 1 ms; t_tick = 90 × 13.89 ns ≈ 1.25 µs.
- Overhead = 1.25 µs / 1000 µs = 0.125 % of CPU.
- At 10 kHz tick: t_tick unchanged, T_tick = 100 µs → overhead = 1.25 %, resolution improves to 0.1 ms. A finer tick buys resolution and costs CPU — the standard trade-off.
vTaskDelay(10)at 1 kHz delays between 9 and 10 ms (the first tick may arrive immediately);vTaskDelayUntilwith a 10-tick period gives a drift-free 10 ms period.
Example 5 — priority inversion, with and without inheritance. Tasks H (priority 3), M (2), L (1). L takes mutex X and needs 5 ms inside it; H needs X; M needs 40 ms of CPU and uses no mutex. L enters X at t = 0; H becomes ready at t = 1 ms; M becomes ready at t = 2 ms.
- Without inheritance: at t = 1 H blocks on X (L has run 1 ms, 4 ms left). At t = 2 M preempts L and runs 40 ms, finishing at t = 42. L then finishes its remaining 4 ms at t = 46 and releases X; H starts at t = 46 ms → blocking ≈ 45 ms, set by M, not by the critical section. Unbounded in general.
- With priority inheritance: at t = 1 L is raised to priority 3, so M cannot preempt it. L leaves the critical section at t = 5 and drops back to priority 1; H runs at t = 5 ms → blocking = 4 ms, bounded by the critical-section length.
| Mechanism | Owner? | Use for | ISR-safe | Priority inheritance |
|---|---|---|---|---|
| Binary semaphore | No | Event signalling, ISR→task | Yes (...FromISR) | No |
| Counting semaphore | No | Counting resources/events | Yes | No |
| Mutex | Yes | Protecting shared data | No | Yes |
| Recursive mutex | Yes | Nested access by same task | No | Yes |
| Queue | — | Passing data + ownership | Yes | N/A |
| Task notification | — | Fast 1-to-1 signal | Yes | N/A |
| Feature | Superloop (bare metal) | RTOS | GPOS (desktop Linux/Windows) |
|---|---|---|---|
| Worst-case response | Whole loop pass | Bounded by priority analysis | Unbounded |
| Scheduling | Code order | Priority / deadline based | Fairness, throughput |
| Memory overhead | Negligible | ~5-10 kB kernel code + per-task stacks | Megabytes |
| Scalability | Poor beyond ~5 activities | Good | Excellent |
| Typical use | Simple sensor node | Motor control, ABS, drone | Infotainment, servers |
| Criterion | RMS (static) | EDF (dynamic) |
|---|---|---|
| Priority assignment | Fixed, by period | Changes at run time, by deadline |
| Utilisation bound (D = T) | n(21/n − 1), ~69 % for large n | 100 % |
| Run-time cost | Low | Higher (deadline queue) |
| Overload behaviour | Predictable — low-priority tasks miss first | Unpredictable cascade |
| Industry use | Very common (AUTOSAR, FreeRTOS) | Research, some Linux SCHED_DEADLINE |
/* Deferred interrupt handling: short ISR + prioritised task (FreeRTOS) */
void EXTI0_IRQHandler(void) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
clear_exti_flag();
vTaskNotifyGiveFromISR(xSensorTask, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken); /* switch now if needed */
}
void vSensorTask(void *pv) {
TickType_t xLast = xTaskGetTickCount();
for (;;) {
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); /* block, 0 % CPU */
xSemaphoreTake(xBusMutex, pdMS_TO_TICKS(10)); /* inheritance applies */
read_adc_and_filter();
xSemaphoreGive(xBusMutex);
xQueueSend(xDataQueue, &sample, 0);
xTaskDelayUntil(&xLast, pdMS_TO_TICKS(10)); /* drift-free 10 ms period */
}
}
Where it is used
- Automotive ECUs: engine/motor control, ABS, ADAS and BMS built on AUTOSAR Classic + OSEK to ISO 26262 — Bosch, Continental, ZF, Tata Elxsi, KPIT, L&T Technology Services, Mahindra and Tata Motors ECU teams in Pune, Bengaluru and Chennai.
- Aerospace and defence: ISRO launch-vehicle and satellite avionics (RTEMS, in-house kernels), HAL/DRDO flight software to DO-178C, BEL radar and communication systems.
- Industrial and power: PLCs, servo and VFD drives, protection relays and smart-meter firmware (Kirloskar, Siemens India, ABB India, Secure Meters, Genus) where a control loop must close every 100 µs-1 ms.
- Medical and consumer devices: ventilators, infusion pumps and patient monitors to IEC 62304; wearables and smart appliances on FreeRTOS/Zephyr.
- IoT and telecom: ESP32/Zephyr nodes with tickless idle for battery life; 5G small-cell and router firmware; drones (PX4 runs on NuttX, a POSIX-style RTOS).
- Job roles: embedded firmware engineer, RTOS/BSP engineer, AUTOSAR/BSW developer, firmware validation and timing-analysis engineer — typical Indian fresher CTC ₹4-9 lakh, product-company and Tier-1 roles higher.
Interview questions
- Q: What exactly makes an OS "real-time"? A: A guaranteed, bounded worst-case response time — determinism — not raw speed; the system must be provably able to meet its deadlines.
- Q: Difference between a semaphore and a mutex? A: A semaphore is a signalling counter with no owner and may be given from an ISR; a mutex is owned by the task that took it, is used only for mutual exclusion, and supports priority inheritance.
- Q: Explain priority inversion and its fix. A: A high-priority task waits on a resource held by a low-priority task that is itself preempted by a medium-priority task; priority inheritance temporarily raises the holder's priority, and the priority-ceiling protocol raises it immediately to the resource's ceiling and also prevents deadlock.
- Q: Why must ISRs be short in an RTOS? A: An ISR runs above every task, so its duration adds directly to the worst-case latency of all tasks; the work is deferred to a task via a semaphore, notification or queue.
- Q: State the Rate Monotonic schedulability condition. A: For n independent periodic tasks with deadlines equal to periods, U = ΣCᵢ/Tᵢ ≤ n(21/n − 1) is sufficient (≈0.693 for large n); failing it does not prove the set unschedulable — use response-time analysis.
- Q: RMS or EDF — which would you choose for a production ECU, and why? A: RMS, because fixed priorities are cheap, analysable, deterministic under overload and supported by AUTOSAR tooling; EDF gives 100 % utilisation but degrades unpredictably when overloaded.
- Q: What is the difference between
vTaskDelayandvTaskDelayUntil? A:vTaskDelayblocks for a duration measured from the moment of the call, so execution time accumulates as drift;vTaskDelayUntilblocks until an absolute tick, giving a fixed period. - Q: How does a context switch work on a Cortex-M? A: The SysTick (or a yield) triggers PendSV at the lowest exception priority; the handler saves the remaining registers (R4-R11) on the task's stack, stores the stack pointer in the TCB, selects the next TCB, restores its registers, and returns — the hardware stacks and unstacks R0-R3, R12, LR, PC, xPSR automatically.
- Q: Is embedded Linux a real-time OS? A: Not by default; with the PREEMPT_RT patch (mainlined in Linux 6.12) or a co-kernel like Xenomai it gives soft/firm real-time latencies of tens of microseconds, but hard real-time safety work still uses a dedicated RTOS.
- Q: How do you size a task's stack? A: Estimate the deepest call chain plus locals plus the saved context and ISR nesting allowance, then measure in testing with a high-water-mark API and keep a margin; enable the stack-overflow hook or MPU guard.
- Q: A task is "starving" — how do you debug it? A: Check priorities and whether a higher-priority task ever blocks, look for a busy-wait loop instead of a blocking call, check interrupt load, and use a trace tool (Tracealyzer, SystemView) to see actual run and ready times.
- Q: When would you *not* use an RTOS? A: When the application has only two or three simple activities, very tight RAM/ROM (a few kB), or a single tight control loop — a superloop with interrupts is then smaller, cheaper and easier to certify.
Common mistakes
- Treating "real-time" as "fast" — the examiner wants determinism and bounded worst case, not throughput.
- Using a mutex for ISR-to-task signalling (a mutex cannot be used in an ISR) or a binary semaphore where mutual exclusion with inheritance is needed.
- Concluding "not schedulable" when the Liu–Layland bound fails — it is only a sufficient condition; response-time analysis may still pass.
- Forgetting the ceiling function in response-time analysis, or stopping the iteration before Rᵢ converges.
- Busy-waiting (
while(flag);orfordelay loops) inside a task — it burns the CPU at that priority and starves lower-priority tasks. - Calling a non-
FromISRAPI, blocking, or usingprintf/mallocinside an interrupt handler. - Ignoring stack sizing and priority-inversion blocking time (Bᵢ) in timing budgets, then blaming "random" resets that are really stack overflows.
Related topics
ARM and embedded C · Microcontroller peripherals: timers, ADC, PWM and interrupts · Embedded Linux and single-board computers · Operating systems: process scheduling and synchronisation · Internet of Things basics · Embedded interfaces: UART, SPI and I2C