Fix Si4684 pitch distortion: uncalibrated crystal (CTUN/XTAL_FREQ)
Root cause of FM/DAB "stonata" audio: xtalCtun=31/nominal XTAL_FREQ defaults were never measured against this board's actual crystal (Abracon ABM8-19.200MHZ-10-1-U-T, CL=10pF, two external 15pF load caps). Fixed via CTUN trim by ear (31->0) plus a new live calibration loop that reads the Si4684's own FM_RSQ FREQOFF field (broadcast carriers are GPS-locked, so it's a free precision frequency reference) to trim XTAL_FREQ with no lab equipment. Converged to CTUN=0, XTAL_FREQ=19199750 Hz, residual -3/-4 ppm cross-checked on two stations. New tools/infrastructure (kept, not one-off): - Si4684Driver::recalibrateXtal() + POST /api/tuner/xtal-calibrate: live crystal re-trim without an ESP32 reflash. - FM_RSQ FREQOFF exposed as "freqoff_ppm" in GET /api/tuner/status. - tools/si4684_xtal_calibration.py: automates the trim loop. - tools/si4684_antenna_calibration.py: AN851 Appendix A ANTCAP/VARM/VARB sweep tool (same session, separate calibration). - CONFIG_ESP32_I2S_TEST_TONE (off by default): isolates Si4684-specific audio issues from shared-downstream ones by writing a tone directly over the I2S bus the Si4684 also uses. - SIGMA_WRITE_REGISTER_BLOCK/sigma_safeload_block now retry on NACK and verify via read-back instead of firing I2C writes blind. - FM de-emphasis set to European 50us (was left at the US 75us default). - DAB_ACF_ENABLE restored to its previous 0x0000 with a citation explaining why (tested the datasheet default of 0x0003, made things audibly worse). See docs/si4684-rf-investigation-report.md's 2026-08-23 entry for the full elimination chain and docs/TODO.md for follow-up work (persisting calibration results to EEPROM instead of requiring a firmware edit). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -204,6 +204,37 @@ Done in fw 0.8.5 unless noted:
|
||||
|
||||
---
|
||||
|
||||
## TODO — calibration functions need to become permanent, in-firmware, on-demand tools (2026-08-23)
|
||||
|
||||
Both ANTCAP calibration (`tools/si4684_antenna_calibration.py`) and Si4684
|
||||
crystal calibration (`tools/si4684_xtal_calibration.py`,
|
||||
`POST /api/tuner/xtal-calibrate`) currently exist as **host-side Python
|
||||
scripts driving live-but-unpersisted HTTP endpoints** — they compute a
|
||||
result but the operator has to hand-edit firmware source (constants in
|
||||
`Si4684Driver.cpp` / the `gSi4684.boot(...)` call in
|
||||
`hardware_bootstrap.cpp`) and reflash to make a result permanent.
|
||||
|
||||
**Wanted instead**: both calibration procedures should be triggerable
|
||||
on-demand *from the device itself* (an HTTP endpoint is enough — no UI
|
||||
required yet) and, once a result converges, **write the result to the
|
||||
24AA025E48 EEPROM** (same chip/pattern already used for ANTCAP
|
||||
persistence, see `Eeprom24aa::writeFmAntCap`/`writeDabAntCap`) so it
|
||||
survives a reboot without a firmware reflash. `recalibrateXtal()`
|
||||
(`Si4684Driver.cpp`) already does the live re-boot-with-new-params part;
|
||||
what's missing is EEPROM persistence for CTUN/XTAL_FREQ (ANTCAP already
|
||||
persists this way — the xtal calibration should follow the same shape,
|
||||
likely a new EEPROM word address alongside the existing FM/DAB ANTCAP
|
||||
ones) and doing the FREQOFF-averaging/damping/convergence-loop logic
|
||||
in firmware (or keeping it host-side and just adding the EEPROM-persist
|
||||
step at the end — decide when picked up).
|
||||
|
||||
Not started — explicitly deferred to a future session, noted here only so
|
||||
it isn't lost. See `docs/si4684-rf-investigation-report.md`'s 2026-08-23
|
||||
entry for full context on why this calibration was needed and how it
|
||||
currently works.
|
||||
|
||||
---
|
||||
|
||||
## Quality gates (run from `Software/` before merge)
|
||||
|
||||
```bash
|
||||
|
||||
Reference in New Issue
Block a user