Your MCU's Timer Peripheral Is Silently Killing Your BLE Battery Budget — Here's When to Offload to Hardware

Offloading MCU timers to hardware peripherals to cut BLE power drain

Your datasheet promised 3 years on a CR2032. Your bench says 14 months. Somewhere between the marketing PDF and your actual product, more than half your battery budget vanished, and you’re pretty sure the radio ate it.

It probably didn’t.

The radio transmits for a few milliseconds and goes quiet. The thing quietly draining your coin cell is your CPU waking up hundreds of times a second to babysit a timer, poke an ADC, or toggle a GPIO between advertising events. Each wake is tiny. There are just a lot of them.

Here’s the case for BLE timer offload: moving timing-critical work off the CPU and onto hardware peripherals that run while the core sleeps. I’ll give you a decision framework first, so you know whether it’s even worth your time, then a worked example with numbers you should verify on your own hardware. (If you haven’t already, our BLE power optimization guide covers the connection-interval side, which is where most blog posts stop.)

Why CPU-Driven Timing Costs More Than You Think

A CPU wake isn’t free, and it isn’t cheap in proportion to the work you do. Here’s the anatomy of one wake:

  1. Wake latency: the core comes out of sleep, clocks ramp up, the interrupt vector fetches.
  2. Active current while your handler runs.
  3. Return to sleep, which also takes clock settling time.

The fixed overhead (steps 1 and 3) dominates when the actual work is small. If your timer callback reads one register and sets a flag, you might spend 40% of that wake’s energy just getting into and out of the handler. The work was 1% of the code and 40% of the energy.

That’s why frequent tiny tasks are the worst offenders. A callback firing 200 times a second, each doing almost nothing, burns far more average current than one heavy computation that runs once a second. You pay the wake tax every single time.

The scale of the gap is the whole reason this matters:

Current draw (relative, log-ish)
Deep sleep   | █                    ~1 µA
CPU active   | ████████████████████  ~1000x+
             +-------------------------------

Roughly three orders of magnitude between a sleeping core and an active one. Every unnecessary wake drags your average current toward that right-hand bar.

The Decision Framework

Before you touch any peripheral wiring, figure out whether offloading pays off. Three variables decide it:

  • Event frequency: how many times per second does this task fire?
  • CPU-active time per event: how long does the core actually stay awake to service it?
  • Wake overhead: the fixed cost per wake, independent of the work.

Multiply them out. High-frequency events with low CPU work and tight timing are your best candidates. You’re paying the wake tax constantly for almost no real computation, and the timing sensitivity means you can’t just batch them lazily.

Infrequent tasks aren’t worth the effort no matter how you slice them. Logic-heavy tasks (parsing, crypto, state machines) need the CPU anyway; offloading the trigger saves you almost nothing. One-shot setup code stays put.

Here’s the matrix I use:

                 LOW CPU work        HIGH CPU work
              +------------------+-------------------+
 HIGH freq    |  OFFLOAD         |  Consider DMA /   |
 (many/sec)   |  (best ROI)      |  restructure      |
              +------------------+-------------------+
 LOW freq     |  Leave on CPU    |  Leave on CPU     |
 (rare)       |  (not worth it)  |  (logic-heavy)    |
              +------------------+-------------------+

The top-left box is where the money is. High frequency, low work: a periodic ADC sample, a regular GPIO strobe, a timestamp capture. These are exactly the tasks that look trivial in code and cost you dearly in average current.

Top-right is more subtle. If the work is genuinely heavy but frequent, you can’t make it disappear, but you can often restructure it. DMA can move data without the CPU, and you can batch results so the core wakes once per buffer instead of once per sample. You’re not eliminating the wake, you’re amortizing it.

The bottom row is a trap for the eager optimizer. Rare events, even expensive ones, contribute almost nothing to your daily average. Offloading them adds complexity and buys you microamps you’ll never measure.

This framework isn’t Nordic-specific. Every modern low-power MCU (STM32, ESP32, TI, Silicon Labs) has some version of a hardware fabric that lets peripherals talk to each other without the core. The names change; the math doesn’t.

How Core-Independent Peripherals Solve It

The idea behind core-independent peripherals is simple: peripherals trigger other peripherals directly, so the CPU never has to wake to shuttle events between them.

Generically, this is an event-and-task interconnect. One peripheral emits an “event” (a timer expired, a pin toggled). Another peripheral has a “task” it can perform (start a conversion, capture a value). The fabric wires events to tasks in hardware. On Nordic silicon, that fabric is PPI on older parts and DPPI (Distributed Programmable Peripheral Interconnect) on newer ones. Treat it as one implementation of a general pattern.

A concrete chain looks like this:

[Timer/RTC] --event--> [ADC sample] --DMA--> [RAM buffer]
     ^                                            |
     |                CPU ASLEEP  <---------------+
                (wakes only when buffer full)

The timer fires, the ADC samples, DMA writes the result to RAM. No interrupt, no handler, no wake. The CPU only comes up when the buffer is full and there’s actual work: processing a batch of samples, deciding what to advertise. You’ve turned 200 tiny wakes per second into a handful of meaningful ones.

The nRF54L15 Power Domains Worked Example

The nRF54L15 splits its silicon into separate power domains so peripherals and their interconnect can stay active in a low-power state while the main Cortex-M33 core is fully asleep. That separation is what makes offload actually save energy. The fabric and a handful of peripherals draw a small fixed current, and the core, the expensive part, stays off.

Take a common scenario: you’re advertising over BLE and sampling a sensor on a fixed interval between advertising events.

The CPU-serviced version sets a timer interrupt, wakes the core on every tick, reads the ADC in the handler, stores the value, goes back to sleep. Every tick is a full wake with all its overhead.

The DPPI-chained version wires the timer event straight to the ADC’s sample task and lets DMA store results. The core sleeps through every individual sample and wakes only to process a batch.

Scenario (illustrative: VERIFY ON YOUR HW)
+-------------------------+-------------+------------+
| Approach                | Avg current | CPU wakes  |
+-------------------------+-------------+------------+
| CPU-serviced timer      |   higher    | every tick |
| DPPI-chained peripheral |   lower     | rare       |
+-------------------------+-------------+------------+

I’m deliberately not printing hard microamp figures here. Your savings depend on sample rate, handler length, clock configuration, and compiler output, and quoting a number I can’t reproduce on your bench would be dishonest. Pull the active, sleep, and wake-latency figures from the current published nRF54L15 datasheet (check the revision and date on Nordic’s site, they update these) and treat them as reference points, not promises.

[NOTE FOR HUBBLE TEAM: insert verified nRF54L15 active/sleep/wake-latency figures from the latest datasheet revision here, with the revision number and publication date cited inline. I couldn’t confirm exact current values against a specific datasheet revision at time of writing, so I’ve left this as a labeled placeholder rather than risk quoting a stale or invented number.]

To measure your own before/after, use a Nordic Power Profiler Kit II or an equivalent current profiler. Run the CPU-serviced version, capture average current over a full advertising cycle, then swap in the peripheral-chained version and capture again under identical conditions. The delta is your real answer.

When NOT to Offload

Offloading has real costs. Interconnect channels are a finite resource; DPPI gives you a fixed number, and complex products run out. Debugging a hardware-triggered chain is harder than stepping through an interrupt handler, because there’s no code to break on when the wiring is wrong.

Some tasks genuinely need the CPU. Anything with branching logic, state that depends on prior samples, or decisions the hardware can’t make on its own belongs in software. Don’t contort a peripheral chain to avoid a wake that was going to happen anyway.

And don’t optimize blind. Profile first. If your current trace shows the core is already sleeping 99% of the time, your problem is elsewhere (probably the radio or a leaky GPIO), and peripheral offload won’t move the needle.

Verify It Yourself: A Short Checklist

Before you decide offloading is worth it, and again after you’ve done it:

  1. Profile the baseline. Capture average current over a full BLE cycle with a PPK2 or similar.
  2. Count your wakes. Which handlers fire most often? Frequency times active-time tells you where the energy goes.
  3. Run each candidate through the matrix. High frequency, low work, timing-sensitive tasks first.
  4. Offload one chain, measure again under identical conditions, and keep the delta.
  5. Confirm timing didn’t drift. Faster average current is worthless if your samples now land in the wrong window.

The framework travels across silicon. The numbers don’t. Trust your own bench over any datasheet, ours included.


Notes flagged for review: the nRF54L15 current figures in the worked example are left as a labeled placeholder because I couldn’t verify specific values against a datasheet revision. Internal link in the intro points to a rollout guide as the closest available match to a “BLE power optimization” pillar; swap for a dedicated power-optimization page if one exists. No verified DPPI tutorial or profiling-tools doc was available in the provided list, so those planned links were omitted rather than guessed.


Hubble Network connects BLE devices straight to satellites, so your power budget answers to physics on the bench rather than gateway coverage on a map. See how it works →