Persist Si4684 crystal calibration (ibias/ctun/xtalFreqHz) to EEPROM

Extends the existing FM/DAB ANTCAP EEPROM persistence pattern
(Eeprom24aa::writeFmAntCap/writeDabAntCap) to the crystal trim found by
POST /api/tuner/xtal-calibrate, which previously only applied live and
was lost on every reboot.

- Eeprom24aa gains readXtalCalibration()/writeXtalCalibration() at word
  addresses 0x02 (ibias), 0x03 (ctun), 0x04-0x07 (xtalFreqHz,
  big-endian), right after the existing FM/DAB ANTCAP bytes.
- HardwareBootstrap::boot() now boots ADAU1701 before Si4684 (needed so
  the EEPROM read, which borrows ADAU1701's I2C bus, can happen before
  Si4684's boot() call, which takes the crystal trim as an argument),
  loads the saved trim if present, and falls back to the compiled-in
  defaults (ibias=72, ctun=0, xtalFreqHz=19199750) otherwise.
- POST /api/tuner/xtal-calibrate now persists every successful live
  recalibration automatically ("persisted":true/false in the response)
  via a new saveXtalCalibration()/net::AntennaCalibration::saveXtal
  bridge, mirroring the ANTCAP save pattern.

Verified live: boot log confirms "Xtal not calibrated" before the first
save, "Xtal calibration loaded: ibias=72 ctun=0 xtal_freq_hz=19199750"
after, surviving a reboot; DAB/FM tuning unaffected (DAB CNR 17-19dB,
locked).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-25 15:22:03 +02:00
co-authored by Claude Sonnet 5
parent d17902f19c
commit a7a5311c2c
9 changed files with 351 additions and 25 deletions
@@ -112,6 +112,11 @@ struct AntennaCalibration {
* @return false if the chip was never booted or the reboot failed. */
bool (*recalibrateXtal)(std::uint8_t ibias, std::uint8_t ctun,
std::uint32_t xtalFreqHz);
/** Persist a new Si4684 crystal trim (ibias/ctun/xtalFreqHz) to EEPROM,
* so it survives a reboot instead of only lasting until the next power
* cycle. @return false on an I2C failure. */
bool (*saveXtal)(std::uint8_t ibias, std::uint8_t ctun,
std::uint32_t xtalFreqHz);
};
/**