Skip to content

fix: eliminate false fader events from ADC noise - #556

Merged
chmanie merged 5 commits into
mainfrom
fix/latch-crossover-debounce-adc
Aug 3, 2026
Merged

fix: eliminate false fader events from ADC noise#556
chmanie merged 5 commits into
mainfrom
fix/latch-crossover-debounce-adc

Conversation

@ArthurGibert

@ArthurGibert ArthurGibert commented Jun 9, 2026

Copy link
Copy Markdown
Member

Fixes false FaderChange events (and resulting MIDI CC spam in Jump mode) caused by ADC noise on static faders.

Root cause

Established by live probe diagnostics on hardware:

  • Patch cables plugged into the jacks inject bursty noise into the jack traces, which couples into the fader wiper lines. A single trigger cable is enough; floating cables (far end on an unpowered module) are the worst carriers.
  • Fader position amplifies the effect: at mid-travel the wiper source impedance is maximal (16-19 LSB measured wander) vs 1-2 LSB near the end stops. This is why the noise appeared position- and setup-dependent.
  • The MAX11300 itself was ruled out: DAC write traffic, DAC step size, and ADC sweep range switching all measured clean.

Changes

  • libfp/src/latch.rs: split jitter_tolerance into a tight emission dead zone (25 LSB) and a wider pickup_tolerance (50 LSB) for stay-latched hysteresis, with with_tolerances() constructor and characterization tests. Note this affects latch takeover behavior in Pickup/Scale modes for all apps, not just the fader task.
  • faderpunk/src/tasks/max.rs: intra-dwell burst median — each fader read is the median of 5 back-to-back reads within one mux dwell. Rejects impulsive spikes with no added latency and no scan-rate loss.
  • faderpunk/Cargo.toml: bump max11300 to 0.5.3, which skips redundant DAC data writes via a driver-side shadow register (feat: skip redundant DAC data writes via shadow registers max11300#27). An earlier revision of this branch implemented the dedup firmware-side in process_channel_values; that was reverted in favor of the driver, where &mut self serialization makes it race-free by construction.

An earlier revision also carried gated FADER_DIAG noise diagnostics in read_fader; removed to keep the hot loop lean (the measurement rig lives in this branch's history if needed for future noise sessions).

Validated live on an earlier revision of this branch: zero false emissions across all tested states, including the reproduced worst case (trigger cable patched, faders mid-travel, clock running), with fader feel confirmed unchanged. The checklist below re-validates on the final shape (firmware dedup replaced by the driver-side shadow, diagnostics removed).

Hardware test checklist

  • Load a CV->MIDI layout in Jump takeover mode, patch a trigger/gate cable into an input, set all faders to mid-travel, leave faders untouched: no MIDI CC output / no value changes.
  • Fader feel in all three takeover modes (Pickup/Jump/Scale), including slow fades and fast sweeps to the end stops.
  • App-level latch takeover feel in Pickup and Scale modes (e.g. after a scene change): pickup engages at the expected proximity, no early/late latching (pickup_tolerance is now 50 LSB).
  • CV outputs (e.g. LFO app) still update smoothly (DAC write dedup now in max11300 0.5.3).
  • Layer switch (scene button) latching behaves as before.

@ArthurGibert
ArthurGibert marked this pull request as draft June 10, 2026 20:58
@ArthurGibert
ArthurGibert marked this pull request as ready for review June 19, 2026 14:20
@ArthurGibert
ArthurGibert marked this pull request as draft June 19, 2026 14:26
@ArthurGibert ArthurGibert changed the title fix(latch): split jitter_tolerance into independent tracking and pickup zones fix(latch): invert tolerances, fix call sites, add median filter for spike immunity Jun 22, 2026
@ArthurGibert
ArthurGibert force-pushed the fix/latch-crossover-debounce-adc branch from f70713c to 902f878 Compare July 3, 2026 16:18
@ArthurGibert ArthurGibert changed the title fix(latch): invert tolerances, fix call sites, add median filter for spike immunity fix: eliminate false fader events from ADC noise Jul 3, 2026
@ArthurGibert
ArthurGibert marked this pull request as ready for review July 3, 2026 16:43
@chmanie
chmanie force-pushed the fix/latch-crossover-debounce-adc branch from b9719a2 to 7f8223a Compare July 14, 2026 21:52
@ArthurGibert ArthurGibert added this to the 1.12 milestone Jul 18, 2026
@ArthurGibert
ArthurGibert requested a review from chmanie July 18, 2026 07:37
ArthurGibert and others added 5 commits August 3, 2026 14:33
Introduces asymmetric hysteresis to the AnalogLatch state machine.
Previously a single `jitter_tolerance` served two distinct purposes:
- latched dead zone (how far the fader must move to emit a value)
- pickup proximity zone (how close the fader must be to auto-latch)

These are now independent:
- `jitter_tolerance` (default 50 LSBs): latched dead zone. Absorbs the
  ~25-40 LSB charge-injection transients produced by the MAX11300's
  internal ADC sweep when high-voltage input ports precede P16.
- `pickup_tolerance` (default 25 LSBs): proximity zone for crossover
  detection, layer-switch auto-latch, and external-target-change checks.
  Kept smaller so the fader doesn't accidentally engage by drifting near
  the stored target value.

New constructors:
- `with_tolerances(initial, jitter, pickup, mode)` — full control
- `with_tolerance(initial, tol, mode)` — sets both equal (test compat)
- `new(initial, mode)` — production defaults (50 / 25)

Also includes LATCH_REFACTOR_NOTES.md documenting the next planned
improvements: 3-sample rolling median filter in read_fader() and
asymmetric hysteresis in the external-target-change unlatch path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@ArthurGibert
ArthurGibert force-pushed the fix/latch-crossover-debounce-adc branch from 7f8223a to 0fe8827 Compare August 3, 2026 18:14
@chmanie
chmanie merged commit 3d73b5f into main Aug 3, 2026
3 checks passed
ArthurGibert added a commit that referenced this pull request Aug 5, 2026
This reverts commit 3d73b5f.

#556 merged directly to main after v1.11.0-beta.1 was already cut, so it
never went into the beta currently out for testing, and its own hardware
validation checklist (5 items: fader feel across takeover modes, latch
pickup behavior, CV output smoothness, etc.) was never completed before
merge. Reverting so v1.11.0 ships on exactly what beta.1 validated.

The fix will be resubmitted from fix/latch-crossover-debounce-adc after
v1.11.0 ships stable, this time merged ahead of a beta cut so it actually
rides through a full beta test cycle and the hardware checklist gets
completed.
ArthurGibert added a commit that referenced this pull request Aug 7, 2026
…592 into #590

#556 was reverted (#626) after merging post-beta-cut with its hardware
checklist incomplete — dropping it so the published v1.11.0 release notes
don't claim a fix that was actually backed out.

#594 and #619 were missing because knope's package-scope matching only
picks up commits scoped exactly `fix(faderpunk):` — these merged as
`fix(clock):` and `fix(midi):` respectively, so they were silently
excluded despite being real faderpunk fixes.

#592 (naming the USB-MIDI virtual ports) only exists because of the new
MIDI-class transport introduced by #590 in this same release — stable
v1.11.0 users never see an unnamed-ports intermediate state, so it's
folded into the #590 feature line rather than listed as a separate fix.
ArthurGibert added a commit that referenced this pull request Aug 7, 2026
* chore: prepare release

* chore: fix 1.11.0 changelog — drop reverted #556, add #594/#619, fold #592 into #590

#556 was reverted (#626) after merging post-beta-cut with its hardware
checklist incomplete — dropping it so the published v1.11.0 release notes
don't claim a fix that was actually backed out.

#594 and #619 were missing because knope's package-scope matching only
picks up commits scoped exactly `fix(faderpunk):` — these merged as
`fix(clock):` and `fix(midi):` respectively, so they were silently
excluded despite being real faderpunk fixes.

#592 (naming the USB-MIDI virtual ports) only exists because of the new
MIDI-class transport introduced by #590 in this same release — stable
v1.11.0 users never see an unnamed-ports intermediate state, so it's
folded into the #590 feature line rather than listed as a separate fix.

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: ArthurGibert <artgibert@gmail.com>
ArthurGibert added a commit that referenced this pull request Aug 7, 2026
This reverts commit de31e32, reapplying
the original #556 fix (asymmetric jitter-tolerance latch state machine +
burst-median ADC read dedup).

#556 was reverted (#626) because it originally merged directly to main
after v1.11.0-beta.1 was already cut, skipping beta testing, with its
hardware validation checklist left incomplete. Reapplying here so it can
go through the process properly this time: merged well ahead of the next
beta cut, with the hardware checklist actually completed before merge.

Do not merge until:
- v1.11.0 has fully shipped stable (not just beta)
- The 5-item hardware checklist below has been completed on real hardware
ArthurGibert added a commit that referenced this pull request Aug 7, 2026
)

This reverts commit de31e32, reapplying
the original #556 fix (asymmetric jitter-tolerance latch state machine +
burst-median ADC read dedup).

#556 was reverted (#626) because it originally merged directly to main
after v1.11.0-beta.1 was already cut, skipping beta testing, with its
hardware validation checklist left incomplete. Reapplying here so it can
go through the process properly this time: merged well ahead of the next
beta cut, with the hardware checklist actually completed before merge.

Do not merge until:
- v1.11.0 has fully shipped stable (not just beta)
- The 5-item hardware checklist below has been completed on real hardware
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants