Skip to content

Low-noise quiet capture for voltmeter and slowest ADC mode#38

Merged
laurb9 merged 1 commit into
masterfrom
adc-quiet-capture
Jul 12, 2026
Merged

Low-noise quiet capture for voltmeter and slowest ADC mode#38
laurb9 merged 1 commit into
masterfrom
adc-quiet-capture

Conversation

@laurb9

@laurb9 laurb9 commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Rescoped implementation of #13 (see the rescope comment there for full rationale).

What

  • New ADCInput::readMultiQuiet() on AVR: instead of free-running conversions, take single conversions with the CPU sleeping in SLEEP_MODE_IDLE during each one, engaging the ADC noise canceler to keep core switching noise out of the samples. This sidesteps the 2015 blocker — the noise canceler fundamentally can't work with free-running mode (ADATE), because the next conversion starts before the CPU can sleep.
  • IDLE, not SLEEP_MODE_ADC, deliberately: full ADC noise-reduction mode halts clkIO, which would stop Timer0 (micros()/elapsedus accounting) and the PWM test signals on pins 9/5. IDLE keeps those running while still stopping the CPU core clock — the dominant on-chip noise source. SLEEP_MODE_ADC remains an escalation path if IDLE isn't enough on hardware.
  • Activation: quiet path is used when the previous frame was flat (voltmeter display, reusing the rolling isFlatLine state) or when the slowest ADC mode is selected. It drops back to free-running the moment the signal moves again.
  • Portable by construction: ADCBase::readMultiQuiet() defaults to readMulti(), so Teensy/SAMD/ESP32 are unaffected — no #ifdef in shared code.

Drive-by fixes surfaced during the work

  • adc_avr.h redeclared curMode, shadowing the ADCBase member — base-class accessors would have read a stale copy. Removed the shadow.
  • loop() called isFlatLine() twice per frame in SIMAVR_TEST builds, double-sampling its rolling Vpp average. Now called once and reused.

Cost

Zero new RAM (Uno globals unchanged at 490 bytes); flash 21972 bytes (68%) on Uno. The read()/readFast()/readMulti() hot paths are byte-for-byte untouched.

Verification

  • arduino:avr:uno: builds clean, --warnings all, zero warnings
  • adafruit:samd:adafruit_feather_m0: builds clean, zero warnings
  • make sim-test: PASS, 8/8 trace lines match the golden baseline (run in the devcontainer)

Runtime-affecting — needs hardware flash-test

This changes ADC timing in voltmeter/slow modes. The interesting hardware check: whether the voltmeter's Vpp/% jitter readout visibly drops once the display goes flat, and that scope mode still recovers promptly when a signal reappears.

Agent: claude-fable-5 (Claude Code)

🤖 Generated with Claude Code

Rescoped from issue #13: instead of free-running conversions, the new
ADCInput::readMultiQuiet() takes single conversions with the CPU sleeping
in SLEEP_MODE_IDLE during each one, engaging the AVR ADC noise canceler
to keep core switching noise out of the sample. IDLE rather than
SLEEP_MODE_ADC so clkIO (Timer0/micros, PWM test signals) keeps running.

The quiet path activates when the previous frame was flat (voltmeter
display, reusing the rolling isFlatLine state) or when the slowest ADC
mode is selected. The free-running readMulti() hot path is untouched,
and non-AVR platforms fall back to it via the ADCBase default.

Also fixes a latent curMode shadow in adc_avr.h (ADCInput redeclared the
ADCBase member, so base accessors would have read a stale copy) and a
double isFlatLine() call in loop() that skewed the rolling Vpp average.

Verified: arduino:avr:uno and adafruit:samd:adafruit_feather_m0 build
clean with --warnings all (globals unchanged at 490 bytes); make
sim-test passes 8/8 against the golden baseline in the devcontainer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@laurb9 laurb9 merged commit ee08d38 into master Jul 12, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant