Skip to content

Audio DSP Pipeline

The LPC2103F controls the D2-81431’s audio processing through a single I2C bus at 480 kHz. Every audio parameter — volume, EQ preset, crossover, phase, sound mode, night mode — translates into one or more I2C write transactions. The firmware never reads audio data or processes samples; it is purely a control plane. All signal processing happens inside the D2-81431’s DSP core.

The I2C0 peripheral is initialized at 480 kHz in fast mode:

ParameterValue
I2C0SCLH0x49 (73)
I2C0SCLL0x49 (73)
Clock ratePCLK / (73 + 73) = 14,745,600 / 146 = ~101 kHz per half-cycle = ~480 kHz effective
Slave address0xB2 (write to D2-81431 at 7-bit address 0x59)
VIC slot5, IRQ 9

The I2C state machine runs entirely in the interrupt handler. The main loop enqueues transactions by copying data to a global transmit buffer, setting the byte count, and asserting the START condition bit. The ISR handles address transmission, data bytes, ACK/NACK detection, and STOP generation. The main loop polls a busy flag and a status register to determine when a transaction has completed.

The firmware uses two I2C packet formats to communicate with the D2-81431:

7-byte init packets — used for DSP coefficient writes during EQ preset loading, sound mode changes, and filter configuration:

Byte 0: 0xB2 (I2C address, write mode)
Byte 1-2: Register address
Byte 3-6: 24-bit fixed-point coefficient data

The coefficient data in bytes 3-6 represents DSP filter parameters — biquad coefficients, gain values, delay taps — in the D2-81431’s native 24-bit fixed-point format. Each 7-byte packet programs a single register in the DSP’s coefficient RAM.

18-byte command packets — used for more complex DSP operations, built by the command builder function at 0x36FC:

Byte 0-1: Constructed from command ID bits [9:8] (register bank encoding)
Byte 2-17: 16 bytes of command data

The command builder takes a 16-bit command ID and a 16-byte data buffer, constructs the 2-byte sub-address from the command ID’s upper bits, appends the 16 data bytes, and transmits the full 18-byte packet via I2C. This format is used for bulk configuration operations and settings persistence.

The firmware maintains an 8-slot circular ring buffer for queueing I2C transactions:

ParameterValue
Buffer size8 slots
Slot size2-byte command + 16-byte data
EnqueueFUN_000035d8 — adds to ring, advances write pointer
DequeueFUN_000036fc — builds 18-byte packet, transmits via I2C

The ring buffer decouples the main loop’s settings changes from the I2C bus timing. When a user adjusts a parameter, the new value is enqueued immediately. The dequeue function runs in the EEPROM auto-save path, draining the buffer at a pace the I2C bus can sustain. This prevents I2C bus contention when multiple parameters change in rapid succession — as happens during a factory reset or when loading saved settings at boot.

Every DSP configuration change follows the same protective sequence:

  1. Mute — set volume to 0 (FUN_000004f8)
  2. Configure — send the I2C commands that change the DSP state
  3. Unmute — restore volume to the user’s current level (FUN_00000510)

This pattern prevents audible artifacts (pops, clicks, transient noise) when DSP coefficients are updated. Loading a new EQ preset involves reprogramming 11 to 17 filter coefficients, and if the DSP is actively processing audio while those coefficients change mid-stream, the intermediate states can produce full-scale transients that would damage speakers. The mute-configure-unmute wrapper makes every transition inaudible.

The master configuration function (FUN_00000dfc) orchestrates the complete audio pipeline. It is called at boot after settings are loaded, and again whenever any audio parameter changes:

  1. Send baseline I2C command
  2. Restore volume from settings
  3. Apply phase and crossover configuration (3-way switch based on crossover mode)
  4. Apply sound mode — dispatches to BYPASS, MUSIC, or MOVIE
  5. Apply night mode — writes 3 coefficient bytes from a lookup table
  6. Select speaker EQ preset — 6-way switch into the preset functions
  7. Send 5 additional output stage I2C commands
  8. Apply output stage configuration (2-way switch)
  9. Apply final mute state — either mute or restore volume based on settings

The ordering matters. EQ presets are loaded after sound mode and night mode because the preset coefficients may overlap with mode-specific parameters, and the last write wins at the register level.

Six speaker EQ presets are hardcoded in the firmware, each loading a different set of DSP filter coefficients optimized for a specific subwoofer enclosure type:

IndexNameFunctionI2C CommandsEnclosure type
0FLATFUN_0000063011No EQ correction (transparent passthrough)
1DUAL8FUN_000006f013Dual 8-inch subwoofers
2BSUB8FUN_000009d017Bass reflex 8-inch
3BSUB10FUN_00000af017Bass reflex 10-inch
4BSUB12FUN_000007d015Bass reflex 12-inch
5HSUB10FUN_000008d015Horn-loaded 10-inch

Each preset function sends a series of 7-byte I2C packets, programming the DSP’s biquad filter bank with coefficients tuned for that enclosure’s transfer function. The FLAT preset uses the fewest commands because it only needs to zero out (or unity-gain) the EQ stages. The bass reflex presets (BSUB8, BSUB10) use the most commands because they apply multi-band correction for the enclosure’s resonant behavior.

Some DSP coefficient packets are shared across presets — the SRAM addresses 0x4000016C, 0x40000237, and 0x4000002A appear in multiple EQ functions, suggesting common filter stages (perhaps a protection limiter or output filter) that are reloaded identically regardless of which enclosure preset is active.

Three sound modes apply bass contour adjustments. Each uses the mute-configure-unmute pattern and sends 2 I2C commands:

ModeFunctionEffect
BYPASSFUN_000005a8No bass contouring — flat response through the DSP
MUSICFUN_00000538Music listening contour
MOVIEFUN_00000570Cinema bass boost contour

The 2 I2C commands per mode likely program a single biquad stage with a shelf or peak filter. BYPASS presumably loads unity-gain coefficients.

Night mode applies dynamic range compression to reduce peak output levels for late-night listening:

StateFunctionI2C CommandsEffect
OFFFUN_000005e01Removes compression — full dynamic range
ONFUN_000006081Adds compression/limiting

A single I2C command toggles the compression on or off, which suggests it enables or disables a compressor/limiter block that is always present in the DSP signal chain but normally bypassed.

Volume is implemented as a lookup table mapping user levels (0-100) to DSP attenuation coefficients:

The volume set function (FUN_00000350) inverts the user’s volume level (vol = 100 - param), indexes into a 3-byte lookup table, and constructs a 7-byte I2C packet that writes the attenuation value to the D2-81431. The lookup table lives in the .data section (copied from flash at boot) and contains 101 entries — one per integer volume step.

The volume up/down handlers use adaptive step sizes to improve the user experience near the extremes of the range:

DirectionConditionStep size
Volume upLevel < 961
Volume upLevel >= 962
Volume downLevel < 52
Volume downLevel >= 51

The larger step size near the extremes compensates for the logarithmic nature of perceived loudness — at very high or very low levels, single-step changes are inaudible, so the firmware doubles the step to maintain perceptible increments.

The firmware monitors the D2-81431’s protection status through P0.13, a GPIO input with 8-sample debounce filtering. The D2-81431 reports current-limit faults (CL FAULT) and thermal protection events (TH FAULT) through this pin. When a fault is detected, the VFD compositor overrides the normal status display with the fault message — CL FAULT or TH FAULT — on line 2 of the display. These fault conditions take the highest display priority, overriding even the LOCK indicator.

The 8-sample debounce means a fault must persist for 8 consecutive 10 ms ticks (80 ms) before the firmware acknowledges it. This filters out transient spikes during heavy bass hits that might momentarily trigger the protection circuitry without indicating a genuine fault condition.