Back to Technology

CMSIS Part 13: Low Power & Energy Optimization

March 31, 2026 Wasil Zafar 30 min read

How to squeeze years of battery life from a coin cell — the full low-power toolkit from sleep modes and clock gating through tickless RTOS and power-domain management.

Table of Contents

  1. Cortex-M Power States
  2. WFI, WFE & SLEEPDEEP
  3. Peripheral Clock Gating
  4. FreeRTOS Tickless Idle
  5. Power Domain Management
  6. Current Measurement & Profiling
  7. Exercises
  8. Low Power Design Planner
  9. Conclusion & Next Steps
Series Context: This is Part 13 of the 20-part CMSIS Mastery Series. Part 12 covered deterministic memory management; here we address energy — the third pillar of professional embedded firmware alongside correctness and real-time performance.

CMSIS Mastery Series

Your 20-step learning path • Currently on Step 13
1
Overview & ARM Cortex-M Ecosystem
CMSIS layers, Cortex-M families, memory map, toolchains
2
CMSIS-Core: Registers, NVIC & SysTick
core_cmX.h, register access, interrupt controller, SysTick timer
3
Startup Code, Linker Scripts & Vector Table
Reset handler, BSS init, scatter files, boot process
4
CMSIS-RTOS2: Threads, Mutexes & Semaphores
Thread management, synchronization primitives, scheduling
5
CMSIS-RTOS2: Message Queues & Event Flags
Inter-thread comms, ISR-to-thread, real-time design patterns
6
CMSIS-DSP: Filters, FFT & Math Functions
FIR/IIR filters, FFT, SIMD optimizations
7
CMSIS-Driver: UART, SPI & I2C
Driver abstraction layer, callbacks, DMA integration
8
CMSIS-Pack & Software Components
Pack files, device support, dependency management
9
Debugging with CMSIS-DAP & CoreSight
SWD/JTAG, HardFault analysis, ITM tracing
10
Portable Firmware: Multi-Vendor Projects
HAL vs CMSIS, cross-platform BSPs, reusable libraries
11
Interrupts, Concurrency & Real-Time Constraints
Interrupt latency, critical sections, lock-free programming
12
Memory Management in Embedded Systems
Static vs dynamic, heap fragmentation, memory pools
13
Low Power & Energy Optimization
Sleep modes, clock gating, tickless RTOS, power profiling
You Are Here
14
DMA & High-Performance Data Handling
DMA basics, peripheral transfers, zero-copy techniques
15
Security: ARMv8-M & TrustZone
Secure/non-secure worlds, secure boot, firmware protection
16
Bootloaders & Firmware Updates
OTA updates, dual-bank flash, fail-safe strategies
17
Testing & Validation
Unity/Ceedling unit tests, HIL testing, integration testing
18
Performance Optimization
Compiler flags, inline assembly, cache (M7/M33), profiling
19
Embedded Software Architecture
Layered design, event-driven, state machines, component-based
20
Tooling & Workflow (Professional Level)
CI/CD for embedded, MISRA, static analysis, Doxygen

Cortex-M Power States

Every ARM Cortex-M MCU implements a hierarchy of power modes that trade wake latency and peripheral availability for progressively lower current consumption. Understanding this hierarchy — and how your firmware transitions between states — is the difference between a product that runs for days and one that runs for years on the same battery.

At the architecture level, Cortex-M defines three power states: Run (processor executing instructions), Sleep (processor halted, entered via WFI or WFE, woken by any enabled IRQ), and Deep Sleep (processor + most clocks halted, entered by setting SCB->SCR.SLEEPDEEP before WFI). Silicon vendors extend Deep Sleep into vendor-specific states: STOP, Standby, Shutdown — each progressively gate more clocks, disable more voltage domains, and incur progressively longer wake latencies.

Cortex-M Power Mode Comparison

Mode CPU State Wake Latency RAM Retention Peripherals Typical Current (3.3V)
Run Executing N/A Full All available 10–100 mA (depends on MHz)
Sleep (WFI) Halted, clocks run 1–3 cycles Full All available 1–10 mA
Deep Sleep / STOP Halted, main clocks off 2–10 µs (PLL relock) Full (if VCORE on) LPTIM, RTC, EXTI only 5–100 µA
Standby Halted, most power off 50–300 µs (POR) Backup domain only RTC, tamper, wakeup pins 1–5 µA
Shutdown Full power off 300 µs–1 ms (full POR) None Wakeup pins only <1 µA
The Duty Cycle Insight: Average current = I_active × duty_cycle + I_sleep × (1 − duty_cycle). A device active for 1% of the time at 10 mA and sleeping at 10 µA has an average of 109.9 µA. Reducing active time from 1% to 0.1% drops average current to 19.9 µA — a 5x improvement without changing any hardware.

WFI, WFE & SLEEPDEEP

WFI (Wait For Interrupt) suspends the CPU until any pending enabled interrupt is detected. WFE (Wait For Event) suspends until an event — which includes interrupts but also hardware events like DMA completion signals and event triggers from other cores in multi-core devices. On single-core Cortex-M, the practical difference is that WFE can be woken by a SEV (Send Event) instruction from any context, including from the same core's ISR, making it useful for spin-wait elimination.

WFI-Based Sleep Loop with SEVONPEND

/**
 * WFI sleep loop: enter sleep after each SysTick, wake on any IRQ.
 * SEVONPEND=1 ensures a pending (but not yet handled) IRQ wakes WFE too.
 *
 * This is the minimal low-power idle — reduces CPU power by ~80% vs
 * a busy-wait loop at the same clock frequency.
 */
#include "core_cm4.h"

void low_power_idle_init(void) {
    /* SEVONPEND: pending interrupt (even before handler entry) wakes WFE */
    SCB->SCR |= SCB_SCR_SEVONPEND_Msk;

    /* SLEEPONEXIT: re-enter sleep automatically on return from ISR.
       Useful when all work is ISR-driven — avoids thread execution overhead */
    /* SCB->SCR |= SCB_SCR_SLEEPONEXIT_Msk; */  /* Enable if desired */
}

void main_loop_idle(void) {
    /* Drain write buffer before sleeping — prevents late bus errors waking us */
    __DSB();

    /* Enter sleep — CPU halts, clocks continue, all peripherals operational */
    __WFI();

    /* Execution resumes here after any enabled IRQ fires */
}

Entering STOP Mode on STM32 via PWR Registers

STOP mode on STM32 cuts the main voltage regulator to low-power mode and stops all internal clocks except the LSE (32 kHz crystal), LSI, and LPTIM. The SRAM and register contents are preserved. Wakeup sources are RTC alarm, EXTI lines (external pins), LPTIM compare match, or LPUART reception.

/**
 * Enter STM32 STOP2 mode (lowest current with full SRAM retention).
 * Wakeup: RTC alarm (via EXTI20) or EXTI pin.
 *
 * Target: STM32L4 / STM32U5 family.
 * Adjust PWR->CR1 bits for your specific STM32 sub-family.
 */
#include "stm32l4xx.h"   /* or your specific device header */
#include "core_cm4.h"

/* Configure RTC alarm as wakeup source (assumes RTC already initialised) */
static void configure_rtc_wakeup(uint32_t wakeup_seconds) {
    /* Disable RTC write protection */
    RTC->WPR = 0xCAU;
    RTC->WPR = 0x53U;

    /* Set wakeup counter (WUTR) with ~1 Hz clock (RTC_CR WUCKSEL=100) */
    RTC->CR  &= ~RTC_CR_WUTE;                    /* Disable wakeup timer */
    while (!(RTC->ISR & RTC_ISR_WUTWF)) {}        /* Wait for access flag */
    RTC->WUTR = wakeup_seconds - 1U;              /* Counter value        */
    RTC->CR  |= (0x04U << RTC_CR_WUCKSEL_Pos);    /* 1 Hz clock source    */
    RTC->CR  |= RTC_CR_WUTIE | RTC_CR_WUTE;       /* Enable IRQ + timer   */

    /* Re-enable write protection */
    RTC->WPR = 0xFFU;

    /* Route RTC wakeup to EXTI20 so it can wake from STOP */
    EXTI->IMR1  |= EXTI_IMR1_IM20;
    EXTI->RTSR1 |= EXTI_RTSR1_RT20;   /* Rising edge trigger */
}

/**
 * @brief Enter STOP2 mode — returns after wakeup event.
 *        All clocks must be reconfigured after wakeup (PLL relocking).
 */
void enter_stop2_mode(void) {
    /* Select STOP2 mode in PWR CR1 */
    PWR->CR1 = (PWR->CR1 & ~PWR_CR1_LPMS_Msk)
             | (0x02U << PWR_CR1_LPMS_Pos);   /* STOP2 */

    /* Set SLEEPDEEP bit — next WFI enters deep sleep, not sleep */
    SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;

    /* Drain write buffer, flush pipeline */
    __DSB();
    __ISB();

    /* Enter STOP2 — CPU halts until wakeup event */
    __WFI();

    /* Clear SLEEPDEEP — subsequent WFIs return to normal sleep */
    SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;

    /* After STOP2, system clock falls back to MSI (4 MHz default).
       Re-enable PLL and reconfigure clocks for full-speed operation. */
    SystemClock_Config();  /* Vendor-provided or custom clock reconfiguration */
}

/* ---- Application pattern: sample sensors every 5 seconds ---- */
void sensor_application(void) {
    configure_rtc_wakeup(5U);   /* Wake every 5 seconds */

    for (;;) {
        /* Active window: sample sensor, process, transmit BLE */
        float temperature = read_temperature_sensor();
        transmit_ble_advertisement(temperature);

        /* Sleep until next RTC wakeup — ~5 seconds of STOP2 */
        enter_stop2_mode();
    }
}

Peripheral Clock Gating

Even in Run mode, every peripheral clock that is enabled consumes power proportional to its switching activity. On STM32, each peripheral clock is individually gated through the RCC (Reset and Clock Control) peripheral's enable registers. Disabling a peripheral's clock while it is not in use reduces current consumption by eliminating switching activity in the peripheral's clock tree — typically 10–200 µA per peripheral at 3.3V depending on its size and clock frequency.

The pattern is simple: enable the clock before accessing a peripheral, disable it when done. For peripherals used in burst mode (SPI for a sensor read, I2C for an EEPROM write), this can save meaningful average current. The overhead is two register writes and a __DSB() barrier.

APB Clock Gating via RCC Enable Registers

/**
 * Peripheral clock gating: enable/disable APB clocks to save power.
 * Pattern: enable just-in-time, disable after transaction completes.
 *
 * Example: SPI1 (APB2) + I2C1 (APB1) + ADC1 (APB2) clock gating.
 */
#include "stm32f407xx.h"
#include "core_cm4.h"

/* Enable SPI1 clock just before a transaction */
static void spi1_clock_enable(void) {
    RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
    __DSB();   /* Ensure peripheral sees the clock before register access */
}

/* Disable SPI1 clock after transaction is complete */
static void spi1_clock_disable(void) {
    __DSB();               /* Ensure all SPI writes completed first */
    RCC->APB2ENR &= ~RCC_APB2ENR_SPI1EN;
}

/* Disable all unused peripheral clocks at startup */
void clock_gate_unused_peripherals(void) {
    /* APB1: disable peripherals not used in this design */
    RCC->APB1ENR &= ~(RCC_APB1ENR_TIM2EN    |
                      RCC_APB1ENR_TIM3EN    |
                      RCC_APB1ENR_TIM4EN    |
                      RCC_APB1ENR_USART2EN  |
                      RCC_APB1ENR_USART3EN  |
                      RCC_APB1ENR_UART4EN   |
                      RCC_APB1ENR_DACEN);

    /* APB2: disable unused high-speed peripherals */
    RCC->APB2ENR &= ~(RCC_APB2ENR_TIM8EN    |
                      RCC_APB2ENR_USART6EN  |
                      RCC_APB2ENR_SPI4EN);

    /* AHB1: disable unused GPIO port clocks */
    RCC->AHB1ENR &= ~(RCC_AHB1ENR_GPIOBEN  |
                      RCC_AHB1ENR_GPIOCEN  |
                      RCC_AHB1ENR_GPIODEN  |
                      RCC_AHB1ENR_GPIOFEN);
    __DSB();
}

/* Full sensor read burst: enable clocks, read, disable clocks */
float read_temperature_over_spi(void) {
    spi1_clock_enable();

    uint16_t raw = spi_transfer_16bit(0x0000U);

    spi1_clock_disable();

    return (float)raw * 0.0625f;  /* MAX31855 conversion factor */
}

Clock Gating Current Savings by Peripheral

Peripheral Typical Clock Frequency Estimated Saving (µA at 3.3V) Notes
UART (USART) APB1/APB2 (up to 84 MHz) 50–150 µA Disable when no active communication
SPI APB1/APB2 (up to 42 MHz) 30–100 µA Enable per transaction, disable immediately after
I2C APB1 (up to 42 MHz) 20–80 µA Lower overhead than SPI; still worth gating
ADC APB2 (up to 36 MHz) 100–400 µA Highest savings; ADC has large analog front-end
General Timer (TIMx) APB1/APB2 (up to 168 MHz) 10–50 µA Keep only active timers enabled
GPIO Port AHB1 (full speed) 5–20 µA Disable unused GPIO banks entirely

FreeRTOS Tickless Idle

By default, FreeRTOS generates a SysTick interrupt every tick period (typically 1 ms). Even if all tasks are blocked and no work is pending, the SysTick ISR fires 1000 times per second, waking the CPU from sleep, executing the tick increment, and returning to idle. At 10 µA sleep current, these 1000 wakeups per second cost several hundred µA in average active-mode contribution — significant for coin-cell designs.

The FreeRTOS tickless idle feature suppresses SysTick when the idle task runs, programs a low-power timer (LPTIM, RTC) to generate a single wakeup at the next task's deadline, and allows the CPU to remain in sleep for the entire interval. When the low-power timer fires, the RTOS calculates elapsed ticks, updates the kernel time, and resumes the scheduler.

FreeRTOS Tickless Idle with LPTIM Wakeup Timer

/**
 * FreeRTOS tickless idle hook (vPortSuppressTicksAndSleep).
 * Platform: STM32L4 with LPTIM1 as low-power wakeup timer.
 *
 * Enable in FreeRTOSConfig.h:
 *   #define configUSE_TICKLESS_IDLE        1
 *   #define configEXPECTED_IDLE_TIME_BEFORE_SLEEP  2  // minimum ticks
 *
 * FreeRTOS calls this function from the idle task when no higher-priority
 * task is ready and the expected idle time exceeds the minimum threshold.
 */
#include "FreeRTOS.h"
#include "task.h"
#include "stm32l4xx.h"

/* LPTIM1 tick rate: 32768 Hz LSE divided by 32 = 1024 Hz ~ 1 ms/tick */
#define LPTIM_TICK_RATE_HZ  1024U

void vPortSuppressTicksAndSleep(TickType_t xExpectedIdleTime) {
    uint32_t ulLptimCounts;
    TickType_t xModifiableIdleTime;

    /* Calculate LPTIM count for the expected idle period */
    ulLptimCounts = (uint32_t)(xExpectedIdleTime * LPTIM_TICK_RATE_HZ
                                / configTICK_RATE_HZ);

    /* Clamp to LPTIM 16-bit counter maximum */
    if (ulLptimCounts > 0xFFFFU) { ulLptimCounts = 0xFFFFU; }
    if (ulLptimCounts < 2U)      { return; }  /* Too short — skip */

    /* Disable SysTick to prevent it from waking us */
    SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;

    /* Configure LPTIM1 compare value for wakeup */
    LPTIM1->CMP = (uint16_t)(ulLptimCounts - 1U);
    LPTIM1->CR |= LPTIM_CR_CNTSTRT;   /* Start LPTIM in continuous mode */

    /* Allow the RTOS to enter sleep — this is where STOP mode is entered */
    xModifiableIdleTime = xExpectedIdleTime;
    configPRE_SLEEP_PROCESSING(xModifiableIdleTime);

    if (xModifiableIdleTime > 0U) {
        __DSB();
        __WFI();   /* Enter sleep until LPTIM compare match or other IRQ */
    }

    configPOST_SLEEP_PROCESSING(xExpectedIdleTime);

    /* Stop LPTIM, read actual elapsed time */
    uint32_t ulCountAfterSleep = LPTIM1->CNT;
    LPTIM1->CR &= ~LPTIM_CR_CNTSTRT;

    /* Calculate actual ticks slept */
    TickType_t xActualSleepTicks = (TickType_t)(ulCountAfterSleep
                                     * configTICK_RATE_HZ / LPTIM_TICK_RATE_HZ);

    /* Re-enable SysTick and advance RTOS tick count */
    SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;

    /* Inform FreeRTOS how many ticks elapsed during tickless sleep */
    vTaskStepTick(xActualSleepTicks);
}
Tickless Impact: On a system with tasks that run for 1 ms every 100 ms (1% duty cycle), enabling tickless idle typically reduces average current by 40–70% compared to the standard 1 kHz SysTick idle, by eliminating the 99 unnecessary SysTick wakeups per 100 ms interval.

Power Domain Management

Modern Cortex-M SoCs (STM32U5, nRF5340, EFR32) partition the chip into multiple independently controllable power domains. A power domain is a group of logic blocks that share a voltage rail and can be powered down as a unit. Peripheral power domain management lets you cut power to an entire subsystem — radio, USB PHY, analog front-end — independent of the CPU core.

Radio Domain

RF Power Domain (BLE/Zigbee)

RF transceivers consume 5–20 mA when active. For duty-cycled BLE advertisement (advertising every 1 second for 3 ms), gate the radio power domain between events. nRF52/53: use NRF_POWER->TASKS_LOWPWR and the radio peripheral's TASKS_DISABLE. Average radio current drops from 8 mA to ~24 µA at 1 Hz duty cycle.

Analog Domain

ADC / Op-Amp Power Domain

Analog front-ends draw 100–500 µA continuously. Power them down between sample windows. STM32 ADC: write ADC->CR |= ADC_CR_ADDIS to disable, wait for ADC_CR_ADEN to clear. Re-enable with ADC_CR_ADEN. For op-amps with power-down pins, toggle the GPIO just before and after the sample window — the circuit needs 10–100 µs to settle.

Current Measurement & Power Profiling

You cannot optimise what you cannot measure. Power profiling requires a current measurement setup that captures both the high-current active spikes (5–100 mA for 1–10 ms) and the low-current sleep floor (1–100 µA for hundreds of milliseconds). A standard benchtop multimeter averages these together and gives you a meaningless mid-range reading.

The professional approach uses a shunt resistor + oscilloscope (10–100 Ω, captures waveform) or a dedicated power analyser (Otii Arc, Nordic PPK2, Segger J-Trace with energy measurement). For firmware-assisted profiling, toggle a GPIO before and after each power state transition — the oscilloscope shows exactly which firmware activity corresponds to which current peak.

/**
 * Firmware-assisted current measurement anchor points.
 *
 * Connect an oscilloscope probe to PA2 (test pin).
 * Measure current via a shunt resistor in the VDD line.
 * Correlate GPIO state with current trace on a two-channel oscilloscope.
 *
 * GPIO high = active processing / peripheral enabled
 * GPIO low  = sleep / WFI
 */
#include "stm32f407xx.h"
#include "core_cm4.h"

/* Configure PA2 as output for oscilloscope current probe anchor */
static void probe_gpio_init(void) {
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
    __DSB();
    GPIOA->MODER = (GPIOA->MODER & ~GPIO_MODER_MODER2_Msk)
                 | (0x01UL << GPIO_MODER_MODER2_Pos);
    GPIOA->OTYPER &= ~GPIO_OTYPER_OT2;     /* Push-pull */
    GPIOA->BSRR = GPIO_BSRR_BR2;           /* Start low */
}

#define PROBE_HIGH()  (GPIOA->BSRR = GPIO_BSRR_BS2)
#define PROBE_LOW()   (GPIOA->BSRR = GPIO_BSRR_BR2)

void power_profiling_loop(void) {
    probe_gpio_init();

    for (;;) {
        /* Active window: GPIO high = current spike visible on scope */
        PROBE_HIGH();

        /* Do active work: sensor read, computation, transmit */
        float val = read_sensor();
        transmit_result(val);

        /* Mark sleep entry: GPIO falls = sleep current floor on scope */
        PROBE_LOW();
        __DSB();
        __WFI();   /* Sleep until next wakeup — flat line on current trace */

        /* GPIO rises again at next active window start */
    }
}

/**
 * Average current calculation from oscilloscope measurements:
 *
 * I_avg = (I_active * t_active + I_sleep * t_sleep) / (t_active + t_sleep)
 *
 * Example: I_active = 15 mA for 5 ms, I_sleep = 8 µA for 995 ms
 *   I_avg = (15000 µA * 5 + 8 µA * 995) / 1000
 *         = (75000 + 7960) / 1000
 *         = 82.96 µA average
 *
 * Battery life = 1000 mAh / 0.08296 mA = ~12,054 hours = ~502 days
 */

Exercises

Exercise 1 Beginner

Profile Active Current vs Sleep Current for Your MCU

Set up the GPIO probe pattern from this article on your development board. Using an oscilloscope and a shunt resistor (100 Ω in the VDD line), measure: (a) peak active current during a busy computation loop, (b) sleep current floor during __WFI() in the default clock configuration, (c) sleep current with all unused peripheral clocks disabled. Calculate the battery life estimate for a 1000 mAh LiPo with a 1% duty cycle at your measured currents.

Power Measurement WFI Sleep Clock Gating
Exercise 2 Intermediate

Implement Adaptive Tickless with 1-Second Idle Entering STOP

Configure FreeRTOS tickless idle with the vPortSuppressTicksAndSleep() hook. Use LPTIM or the RTC as the low-power wakeup timer. When the idle task detects that all tasks are blocked for at least 1 second, enter STOP mode (not just sleep). After wakeup: (a) reconfigure the system clock (PLL relock), (b) verify the RTOS tick count is correctly advanced, (c) verify that task deadlines are not missed. Measure average current with tickless STOP vs standard SysTick idle.

FreeRTOS Tickless STOP Mode LPTIM
Exercise 3 Advanced

Optimise a BLE Sensor Node from 500 µA Average to <100 µA

Start with a BLE sensor node advertising every 100 ms with GATT notifications at 500 µA average. Apply the following techniques in sequence, measuring average current after each: (a) disable all unused peripheral clocks, (b) increase BLE advertising interval from 100 ms to 1 s, (c) enable FreeRTOS tickless STOP during radio idle, (d) implement aggressive clock gating (ADC enabled only during sensor read burst of 2 ms), (e) reduce CPU frequency from 64 MHz to 16 MHz during BLE packet assembly. Document the current reduction at each step and the final bill of watts.

BLE IoT Energy Budget System-Level Optimisation

Low Power Design Planner

Use this tool to document your project's power management strategy — MCU, power targets, sleep modes, clock gating plan, tickless configuration, and profiling results. Download as Word, Excel, PDF, or PPTX for design-review documentation.

Low Power Design Strategy Generator

Document your power management strategy. Download as Word, Excel, PDF, or PPTX.

Draft auto-saved

All data stays in your browser. Nothing is sent to or stored on any server.

Conclusion & Next Steps

In this article we have assembled the full low-power toolkit for professional ARM Cortex-M firmware:

  • The power mode hierarchy — Run, Sleep, Deep Sleep/STOP, Standby, Shutdown — gives you a spectrum of current/latency trade-offs. Choose the deepest mode whose wake latency your application can absorb.
  • WFI in the idle loop is the minimum viable optimisation — it costs zero lines of real application code and typically reduces CPU power by 60–80% compared to a busy-wait idle.
  • STOP mode with RTC/LPTIM wakeup is the standard pattern for duty-cycled IoT sensors — periods of 1–10 µA sleep interrupted by short active bursts give multi-year coin-cell life.
  • Peripheral clock gating is complementary to sleep modes — it saves power in Run mode by eliminating switching activity in inactive peripherals. Disable clocks for every peripheral you are not using at a given moment.
  • FreeRTOS tickless idle eliminates unnecessary SysTick wakeups and allows the CPU to remain in sleep for the entire inter-task idle interval — typically 40–70% average current reduction over standard SysTick idle.
  • GPIO-anchored current profiling with an oscilloscope gives you accurate active and sleep current figures — the only basis for credible battery-life calculations.
  • Average current follows the duty-cycle equation: reducing active time has a proportionally larger impact than reducing active-mode current. Minimise the active window first; then optimise its current.

Next in the Series

In Part 14: DMA & High-Performance Data Handling, we explore the DMA controller — the mechanism that lets peripherals transfer data directly to SRAM without CPU involvement. We'll cover DMA channel configuration, circular buffers for streaming data, half-transfer and complete-transfer interrupt patterns, and zero-copy techniques that eliminate the CPU entirely from high-bandwidth data paths like ADC streaming and UART DMA reception.

Technology