Thursday, August 27, 2026

ESP32 DMA — General Purpose Guide

 

ESP32 DMA — General Purpose Guide

 

What DMA Is

Direct Memory Access lets a peripheral read or write memory directly, without the CPU copying each byte. The CPU sets up a transfer once, then goes off and does other work while hardware moves the data. It only gets interrupted when the transfer finishes, or hits a defined checkpoint.

 

Two Different DMA Architectures

DMA capability and implementation differ significantly across ESP32 generations. This guide separates classic ESP32 (original Xtensa LX6 chip) from newer variants (S2/S3/C3/C6/H2), which use a different, more unified DMA architecture called GDMA. Mixing the two casually is a common source of inaccurate assumptions, so they're treated separately below.

 

Classic ESP32 (Xtensa LX6) — Peripheral DMA Support

Peripheral

DMA Support

SPI (HSPI/VSPI)

Yes, hardware DMA

I2S

Yes, hardware DMA

UART

Yes, hardware DMA support; exact implementation depends on driver/IDF version

SD/SDMMC

Yes, hardware DMA

I2C

Does not use the general-purpose DMA engine the way SPI/I2S/UART do

CAN/TWAI

No

RMT

Has its own internal buffer memory, not the same DMA engine

 

A note on crypto (AES/SHA): these accelerators have their own internal hardware data-movement mechanisms, which are distinct from — and shouldn't be equated with — the general peripheral DMA engine used by SPI/I2S/UART. They're left out of the table above deliberately, since presenting them alongside ordinary DMA peripherals overstates the similarity.

 

Newer ESP32 Variants (S2/S3/C3/C6/H2) — GDMA

Many newer ESP32 variants use a GDMA-based architecture, extending DMA support to additional peripherals such as I2C on supported chips, and generally offering more flexible memory access than the classic chip. Exact capability still depends on the specific chip and the ESP-IDF driver version in use, so specifics should be checked against the datasheet/IDF docs for the exact target rather than assumed to carry over from the classic chip or between newer variants themselves.

 

Descriptor-Based Model

DMA on ESP32 doesn't work with one flat buffer — it works with a linked list of descriptors (lldesc_t on classic ESP32, dma_descriptor_t on newer IDF versions/GDMA). Each descriptor holds:

          Pointer to a data buffer

          length — actual bytes to transfer

          size — allocated buffer size

          owner bit — flags whether CPU or DMA hardware currently controls it

          eof flag — marks end-of-frame (triggers an interrupt if enabled)

          Pointer to the next descriptor

 

This chaining is what allows:

          Transfers larger than a single descriptor's max buffer size (on classic ESP32, a commonly cited rule of thumb is about 4095 bytes per descriptor — this is hardware/driver-specific and not a universal constant across ESP32 generations)

          Scatter-gather transfers (non-contiguous buffers treated as one logical stream)

          Circular/ring buffers for continuous streaming (e.g. continuous audio out, continuous ADC sampling) without CPU re-arming after every chunk

 

Memory Rules

          DMA buffers should be allocated as DMA-capable memory to guarantee they're in DMA-accessible internal SRAM

          On classic ESP32, DMA generally cannot access external PSRAM directly — data has to be staged through internal RAM first. Newer chips substantially improve DMA/PSRAM support, but still have alignment and cache-coherency rules that need to be checked per chip

          Buffers typically need 4-byte alignment, and some peripherals also want the length aligned to a fixed boundary (varies by peripheral)

          Never let a buffer go out of scope or get freed while DMA still owns it — check the owner bit or wait for completion first

 

Interrupts and Synchronization

          Instead of interrupting per-byte, DMA interrupts fire on events like IN_DONE, OUT_DONE, or EOF — i.e. once a whole descriptor or whole descriptor chain completes

          The owner bit on each descriptor prevents the CPU and DMA engine from touching the same buffer simultaneously — always confirm ownership before reading/writing a buffer manually

          For continuous/streaming use cases, a common pattern is double buffering: while DMA fills/drains buffer A, the CPU processes buffer B, then they swap

 

RX/TX Channels

Peripherals like SPI and I2S commonly have separate DMA channels for transmit and receive, which is what allows full-duplex transfers (simultaneous send and receive) without extra CPU juggling. This isn't a universal rule for every peripheral, though — the exact DMA channel architecture differs between ESP32 generations and peripherals, so it should be confirmed for the specific peripheral and chip in use.

 

Throughput Characteristics

          Transfer speed on the wire is largely determined by the peripheral's own clock (SPI clock, I2S bit clock, UART baud rate) rather than CPU instruction speed, since the CPU isn't in the data path for each byte

          That said, DMA doesn't guarantee a perfectly fixed or fully predictable system-level transfer rate — CPU load, other interrupts, bus contention, cache activity, and memory availability can still affect latency and overall throughput in practice

 

Typical Workflow (Conceptual)

1. Allocate DMA-capable buffer(s).

2. Fill descriptor(s) with buffer pointer and length, chain them if needed.

3. Hand the descriptor chain to the peripheral's DMA controller.

4. Peripheral starts moving data using its own clock; CPU is free for other work.

5. On completion (or EOF), an interrupt fires; CPU reads results or refills buffers for the next round.

6. For continuous operation, re-arm descriptors before the DMA engine reaches the end of the chain (or use a circular descriptor list).

 

Note

This is a conceptual reference, not a substitute for the ESP-IDF Technical Reference Manual for your specific chip. Peripheral DMA capability, descriptor formats, and buffer constraints should be verified against the datasheet and IDF driver documentation for the exact ESP32 variant in use before being relied on for a design.


Taming Conducted EMC on a Budget Buck Converter

 

Taming Conducted EMC on a Budget Buck Converter

A CISPR 25 Filter Case Study

Cheap LM2596-based buck converter modules are everywhere in DIY 12V-to-5V conversions — camper van builds, car electronics, small embedded projects. They're inexpensive, easy to wire up, but generally not designed with automotive EMC compliance in mind. This article walks through a hands-on CISPR 25 conducted-emissions pre-compliance investigation into exactly how bad that problem is, and what it actually takes to fix it.

The Problem: An Off-the-Shelf Converter That Was Never Designed for Vehicles

The test subject is a common low-cost buck converter built around the LM2596S switching regulator, configured to step 12V vehicle power down to 5V for powering small electronics — the kind of module widely used in camper van conversions and similar builds. In this setup it was powered from a maintenance-free lead-acid battery, with the output set to roughly 5V and loaded with a small LED floodlight drawing about 400 mA — well under the module's rated 2A output.

While these converters work fine electrically, they are not designed to meet automotive EMC requirements, and using them in a vehicle can be non-compliant at best and capable of disrupting other vehicle electronics at worst.

Two Kinds of Emissions: Conducted vs. Radiated

Electromagnetic interference from a device generally falls into two categories:

        Radiated emissions — interference radiated outward via electric or magnetic fields.

        Conducted emissions — interference that travels directly along power or signal wiring.

This investigation focuses entirely on conducted emissions, measured according to the CISPR 25 standard used for automotive components.

Measurement Setup

Measuring conducted emissions properly requires a LISN (Line Impedance Stabilization Network). A LISN does two jobs: it presents a standardized, known impedance to the device under test, and it lets interference signals be tapped off cleanly for analysis by a spectrum analyzer or measurement receiver.

The test bench used a symmetrical, largely standards-compliant configuration with two LISNs mounted on a grounded copper plate, conductively bonded to the plate and fitted with the required input capacitors. The available copper plate was somewhat small, so the full minimum spacing and cable-routing requirements from the standard couldn't be strictly met — a compromise judged acceptable given how large the eventual limit violations turned out to be, and given that the goal was to compare relative filter performance rather than produce a certified compliance report. Because of this, the results throughout should be read as a pre-compliance investigation rather than a certified CISPR 25 compliance test.

Instrumentation:

        A Siglent SSA3021X Plus spectrum analyzer fitted with the EMI option, providing the standardized 9 kHz resolution bandwidth and quasi-peak detection needed for CISPR-style measurements.

        Techbox EMC View software to automate the process — configuring CISPR 25 limits, setting correct resolution bandwidths, and compensating for frequency-dependent insertion losses of the LISNs and measurement cables, rather than configuring all of this manually on the analyzer. EMC View is free to use for measurements up to 10 MHz and works with a range of common spectrum analyzers; since the LISNs used were also from Techbox, their correction factors were already built into the software.

        The conducted-emissions measurement range for this setup spanned 150 kHz to 108 MHz.

On the resulting plots, the blue line shows the CISPR 25 peak limit, while the red line shows the applicable quasi-peak/average limit — the exact detector and limit depend on the frequency band and CISPR 25 test category, and CISPR measurement systems distinguish peak, quasi-peak, and CISPR-average detectors as separate things, not interchangeable labels for the same reading. Both limits must be satisfied where applicable — exceeding either one at any frequency point constitutes a failure.

Baseline Result: A Massive Failure

Tested with no filtering, the unmodified buck converter failed immediately and badly. Violations exceeded 30 dB above the limit at some frequencies — and because the display is logarithmic, that number understates how severe it looks visually. To put it in perspective: a 20 dB excess means the interference voltage is 10 times higher than allowed; a 30 dB excess means it's roughly 32 times over the limit. In total, the converter exceeded the allowed limit at 238 separate measurement points across the swept range.

Attempt 1: Decoupling Capacitors

One of the most common fixes suggested by viewers of an earlier short video on this topic was to simply add decoupling capacitors to the supply line. Four capacitors — 100 µF, 10 µF, 100 nF, and 10 nF — were soldered in parallel on a small perfboard and inserted between the LISNs and the converter.

Result: Some improvement, but nowhere near enough to pass. This was expected: shunt capacitors alone provide limited attenuation when there's little series impedance between the noise source and the supply — which is the case here, with both the battery and the converter's input presenting low impedance. What's actually needed is a series element, such as an inductor or ferrite component, to create the impedance discontinuity that isolates the noisy converter from the source. Capacitors alone can't provide that.

Attempt 2: A PI Filter

The next step up was a PI filter in the supply line, made from a 47 µH inductor and two 150 nF capacitors (mounted on the underside of the board). The video cites a resulting cutoff frequency around 120 kHz for this filter; for reference, the ideal single-L/single-C corner frequency for these values (fc = 1/(2π√LC)) works out closer to 60 kHz, so the 120 kHz figure likely reflects the actual PI topology or an equivalent-capacitance calculation rather than a simple single-pole estimate. This filter replaced the decoupling-capacitor board in the same test position between the LISNs and the converter.

Result: Noticeably better attenuation at lower frequencies compared to the plain capacitors, but still short of full compliance. Notably, a persistent noise bump just above 10 MHz remained essentially unchanged. To check whether near-field coupling across that region was responsible, an additional measurement was taken with a grounded conductive shield placed between the filter and the converter. The shield produced essentially no change, suggesting the peak was not primarily caused by direct near-field coupling across that particular region — though this doesn't rule out every possible near-field mechanism, just that one.

Understanding Why: Differential Mode vs. Common Mode Noise

To get past this plateau, it's necessary to distinguish between two propagation modes on the supply lines:

        Differential mode (DM) noise — interference current flows in opposite directions on the positive and negative lines.

        Common mode (CM) noise — interference current flows in the same direction on both lines and returns through parasitic capacitances, chassis/ground structures, shielding, or other unintended paths — in this case, capacitive coupling into the ground plane. (Radiated emission is more a consequence of this coupling than the return path itself.)

Each mode requires a different filtering strategy, and a filter tuned for one may do little for the other.

Because the test setup already used two LISNs, it was straightforward to adapt the measurement to isolate each mode separately: a TBLM1 LISN mate (also from Techbox) was connected to the two LISNs, with either the differential-mode or common-mode output routed to the spectrum analyzer while the unused output was terminated in 50 ohms.

Result: A clear pattern emerged — differential-mode noise decreases with increasing frequency, while common-mode noise increases with frequency. This is consistent with the fact that the coupling and radiation mechanisms behind common-mode noise become more effective at higher frequencies. The conclusion: effectively suppressing common-mode noise needs substantially more deliberate filter design than differential-mode noise does.

Attempt 3: A Proper CM/DM Filter Topology

To address common-mode noise directly, the investigation moved to components from Würth Elektronik's "Design Your EMC Filter" kit — described as a practical toolkit of proven filter topologies plus the components to build and test them.

The key component here is the common-mode choke: two coils wound symmetrically on a shared magnetic core. For common-mode signals (identical, in-phase on both lines), the choke presents high impedance and blocks the noise. For differential-mode currents, the magnetic flux from the two windings largely cancels, so the choke presents relatively low impedance to that current — though not zero, as the next point explains.

"Sample circuit 1" from the kit is a classic filter structure intended to suppress both CM and DM noise simultaneously: Y-capacitors provide common-mode noise a direct path to ground, working alongside the common-mode choke's high impedance; from the differential-mode perspective, the same circuit behaves as a simple LC filter. In practice, common-mode chokes always have some leakage inductance (non-ideal behavior for differential-mode signals) — normally undesirable, but here it's actually a small bonus, since it adds some differential-mode suppression too. Because the test setup, unlike a mains-powered appliance, has no protective earth, the Y-capacitors were instead tied to the negative supply return conductor rather than to a separate chassis or earth reference — a distinction that turns out to matter a great deal, as the next section shows.

This filter was inserted into the converter's supply line and measured.

Result: Disappointing. There was a modest reduction in differential-mode noise, but the common-mode interference — the thing this filter was specifically meant to address — remained almost unchanged.

Diagnosing the Failure, and Scaling Up

The first instinct was to scale up the design rather than change its topology: much larger 1000 µF X-capacitors were added along with a bigger common-mode choke, on the reasoning that bigger impedance jumps should help more.

Result: Noise at lower frequencies dropped significantly this time — but the stubborn peak around 10 MHz still refused to move.

The eventual explanation traced back to how the Y-capacitors were grounded. A common-mode capacitor only helps if it provides a low-impedance high-frequency return path without simultaneously providing a path that bypasses the common-mode choke. The reference itself can vary with the system architecture, but in this setup the critical issue was that tying the Y-capacitors to the same supply return the choke was meant to isolate created a path that bypassed the choke rather than routing common-mode current usefully — a genuinely common real-world design mistake, included here deliberately as a lesson.

To investigate further, a metal housing was placed over the buck converter and bonded to the Y-capacitors.

Result: Common-mode noise was suppressed far more effectively. However, leaving the Y-capacitors completely disconnected produced a nearly identical measurement result — which strongly suggests it was the enclosure/shielding that changed the common-mode current path, not the Y-capacitors themselves that were responsible for the improvement. This particular shielding approach wasn't pursued further here, partly because radiated emissions and shielding are planned as a separate topic, and partly because the result above shows this specific fix isn't fully understood or robust yet.

The Working Solution: A Multi-Stage Filter

The best result up to this point had come from the combination of two X-capacitors and one common-mode choke. Building on that, a second common-mode choke stage was added to create a multi-stage filter, while skipping an additional X-capacitor and relying instead on the capacitance already present at the converter's input.

Result: Success — the converter measured below the selected CISPR 25 limits across the measured range, apart from a few narrowband peaks in the FM broadcast band. A close-up look at those FM-band peaks pointed to local FM radio station signals coupling into the unshielded test setup rather than something generated by the converter itself. A DUT-off ambient scan should be considered part of the diagnostic process whenever unexplained narrowband peaks like this remain — comparing DUT-on and DUT-off scans is the standard way to confirm ambient signals aren't being mistaken for (or masking) device emissions.

It's worth restating the caveat from earlier: this demonstrates that the modified converter can meet the selected CISPR 25 limit levels under this particular pre-compliance setup — it does not, on its own, establish formal CISPR 25 compliance.

Key Takeaways

1.       A common, low-cost buck converter can be wildly out of spec. Limit violations exceeding 30 dB (roughly 32× over the limit) at over 200 measurement points show this isn't a marginal issue — it's a fundamental design gap for automotive use.

2.       Differential-mode and common-mode noise are different problems. Filters aimed at one may do almost nothing for the other; measuring them separately (via a LISN mate) reveals which one actually dominates and where.

3.       Simple decoupling capacitors rarely solve conducted EMC issues when source and load impedances are already low — you need a genuine impedance discontinuity, not more capacitance.

4.       Common-mode chokes are the right tool for common-mode noise, but topology and grounding matter enormously. A textbook filter circuit can fail completely if a Y-capacitor's return path ends up bypassing the common-mode choke instead of routing noise usefully to a proper reference — a subtle but very common mistake.

5.       Bigger components help, but only once the topology is correct. Scaling up capacitor and choke values gave real gains, but only after the underlying grounding error was identified and understood.

6.       Multi-stage filtering (cascaded common-mode chokes) can close the final gap once single-stage designs plateau.

7.       Not every remaining peak is the device's fault. Ambient signals (like local FM broadcasts) can couple into an unshielded test setup and should be identified before being blamed on the device under test.