mirror of
https://github.com/alexhopeoconnor/firmware.git
synced 2026-10-17 17:11:30 +10:00
Lr1121 tcxo optional tries xtal first, and get all my yamls in a row (#11215)
* LR11x0: try XTAL before TCXO when oscillator type is uncertain On boards with TCXO_OPTIONAL, a TCXO-first attempt either hangs RadioLib's calibration wait forever on a bare/non-TCXO module (unpatched upstream), or costs a slow failed attempt before falling back even once that's fixed with a timeout. Measured on hardware: XTAL succeeds immediately on a bare module (~350ms) and fails fast and cleanly on a genuine TCXO module (~300ms, RADIOLIB_ERR_SPI_CMD_FAILED), so trying XTAL first is a strict improvement for hang-avoidance regardless of which oscillator is actually present. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * compacted * fix review comment * fix femtofox switches * correct the correction * 13 * 3s timeout * Treat SPI_CMD_TIMEOUT as an LR11x0 init failure The BUSY watchdog breaks RadioLib's wait, so the next bounded transfer returns SPI_CMD_TIMEOUT rather than SPI_CMD_FAILED. Only the latter was checked, so a watchdog-triggered failure fell through to getVersionInfo(), setRfSwitchTable() and startReceive() against an unresponsive chip. Also use Throttle::isWithinTimespanMs() for the watchdog's elapsed-time check instead of raw millis() arithmetic. * Drop the BUSY watchdog and probe XTAL before TCXO The watchdog bounded RadioLib's unbounded BUSY wait in LR11x0::config() by having LockingArduinoHal::digitalRead() report a stuck pin low exactly once. That let a TCXO-first attempt fail cleanly rather than hang, but it meant lying to RadioLib about a GPIO from a HAL shared by every radio driver. Ordering the attempts XTAL-first avoids the hang outright instead: attempt 1 configures no DIO3 Vref, so there is no calibration wait to get stuck in, and the TCXO fallback is only reached on a module that answered and refused XTAL. Attempts are now XTAL, then TCXO, then a settling retry on whichever oscillator was settled on - after a fallback that is a second TCXO attempt. Only TCXO_OPTIONAL builds probe XTAL; a variant that declares a Vref unconditionally still goes straight to it and never probes XTAL at all. SPI_CMD_TIMEOUT stays a failure alongside SPI_CMD_FAILED: a bounded per-command BUSY wait in Module::SPItransferStream() reports it in its own right, independently of the removed watchdog. * Drop a stray tab from the promicro TCXO readme trunk fmt: prettier flags the whitespace-only line inside the <summary> block, which was the only failing check on the PR. --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
Thomas Göttgens
parent
03e6b80989
commit
e78b121d9f
@@ -47,7 +47,6 @@ static const Module::RfSwitchMode_t rfswitch_table[] = {
|
||||
#endif
|
||||
|
||||
// the 2.4G part maxes at 13dBm
|
||||
|
||||
#if ARCH_PORTDUINO
|
||||
#define LR1120_MAX_POWER portduino_config.lr1120_max_power
|
||||
#endif
|
||||
@@ -55,6 +54,23 @@ static const Module::RfSwitchMode_t rfswitch_table[] = {
|
||||
#define LR1120_MAX_POWER 13
|
||||
#endif
|
||||
|
||||
// Vref to assume for a board that declares a TCXO may be fitted without saying at what voltage.
|
||||
// "TCXO reference voltage to be set on DIO3. Defaults to 1.6 V, set to 0 to skip." per
|
||||
// https://github.com/jgromes/RadioLib/blob/690a050ebb46e6097c5d00c371e961c1caa3b52e/src/modules/LR11x0/LR11x0.h#L471C26-L471C104
|
||||
#if defined(TCXO_OPTIONAL)
|
||||
#define LR11X0_TCXO_DEFAULT_VOLTAGE 1.6f
|
||||
#else
|
||||
#define LR11X0_TCXO_DEFAULT_VOLTAGE 0
|
||||
#endif
|
||||
|
||||
// A chip that never answers can surface either way depending on where RadioLib gave up: a bounded
|
||||
// per-command BUSY wait in Module::SPItransferStream() reports SPI_CMD_TIMEOUT rather than
|
||||
// SPI_CMD_FAILED, so both have to count as "the chip did not talk to us"
|
||||
static inline bool lr11x0SpiFailed(int res)
|
||||
{
|
||||
return res == RADIOLIB_ERR_SPI_CMD_FAILED || res == RADIOLIB_ERR_SPI_CMD_TIMEOUT;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
LR11x0Interface<T>::LR11x0Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
|
||||
RADIOLIB_PIN_TYPE busy)
|
||||
@@ -73,22 +89,26 @@ template <typename T> bool LR11x0Interface<T>::init()
|
||||
digitalWrite(LR11X0_POWER_EN, HIGH);
|
||||
#endif
|
||||
|
||||
// An explicit Vref always wins; TCXO_OPTIONAL only supplies a default for boards that declare a
|
||||
// TCXO may be fitted without saying at what voltage. Both may appear in the same variant file.
|
||||
#if ARCH_PORTDUINO
|
||||
float tcxoVoltage = (float)portduino_config.dio3_tcxo_voltage / 1000;
|
||||
// FIXME: correct logic to default to not using TCXO if no voltage is specified for LR11x0_DIO3_TCXO_VOLTAGE
|
||||
// Portduino leaves dio3_tcxo_voltage at 0 whenever the YAML omits DIO3_TCXO_VOLTAGE, which is the
|
||||
// "no explicit Vref" case, so the TCXO_OPTIONAL default still has to apply there
|
||||
float tcxoVoltage =
|
||||
portduino_config.dio3_tcxo_voltage > 0 ? (float)portduino_config.dio3_tcxo_voltage / 1000 : LR11X0_TCXO_DEFAULT_VOLTAGE;
|
||||
#elif defined(LR11X0_DIO3_TCXO_VOLTAGE)
|
||||
float tcxoVoltage = LR11X0_DIO3_TCXO_VOLTAGE;
|
||||
LOG_DEBUG("LR11X0_DIO3_TCXO_VOLTAGE defined, using DIO3 as TCXO reference voltage at %f V", LR11X0_DIO3_TCXO_VOLTAGE);
|
||||
// (DIO3 is not free to be used as an IRQ)
|
||||
#elif defined(TCXO_OPTIONAL)
|
||||
float tcxoVoltage = 1.6f; // TCXO_OPTIONAL: try default 1.6 V first, fall back to XTAL on failure
|
||||
LOG_DEBUG("TCXO_OPTIONAL: no LR11X0_DIO3_TCXO_VOLTAGE defined, trying default TCXO Vref 1.6 V first");
|
||||
#else
|
||||
float tcxoVoltage =
|
||||
0; // "TCXO reference voltage to be set on DIO3. Defaults to 1.6 V, set to 0 to skip." per
|
||||
// https://github.com/jgromes/RadioLib/blob/690a050ebb46e6097c5d00c371e961c1caa3b52e/src/modules/LR11x0/LR11x0.h#L471C26-L471C104
|
||||
// (DIO3 is free to be used as an IRQ)
|
||||
LOG_DEBUG("LR11X0_DIO3_TCXO_VOLTAGE not defined, not using DIO3 as TCXO reference voltage");
|
||||
float tcxoVoltage = LR11X0_TCXO_DEFAULT_VOLTAGE;
|
||||
#endif
|
||||
|
||||
// DIO3 is free to be used as an IRQ only while no TCXO Vref is driven on it
|
||||
if (tcxoVoltage > 0)
|
||||
LOG_DEBUG("LR11x0 TCXO Vref %f V on DIO3 (DIO3 unavailable as an IRQ)", tcxoVoltage);
|
||||
else
|
||||
LOG_DEBUG("LR11x0 no TCXO Vref, XTAL only (DIO3 free as an IRQ)");
|
||||
#if defined(TCXO_OPTIONAL)
|
||||
LOG_DEBUG("TCXO_OPTIONAL: oscillator type unknown, probing XTAL first and using any TCXO Vref only as fallback");
|
||||
#endif
|
||||
|
||||
RadioLibInterface::init();
|
||||
@@ -114,29 +134,48 @@ template <typename T> bool LR11x0Interface<T>::init()
|
||||
// Allow extra time for TCXO to stabilize after power-on
|
||||
delay(10);
|
||||
|
||||
int res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage);
|
||||
|
||||
// Retry if we get SPI command failed - some units need extra TCXO stabilization time
|
||||
if (res == RADIOLIB_ERR_SPI_CMD_FAILED) {
|
||||
LOG_WARN("LR11x0 init failed with %d (SPI_CMD_FAILED), retrying after delay...", res);
|
||||
delay(100);
|
||||
res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage);
|
||||
}
|
||||
// Timestamped brackets so a hang inside RadioLib leaves a dangling "attempt" line in the boot log
|
||||
auto tryBegin = [&](int attempt, float vref) {
|
||||
uint32_t attemptStart = millis();
|
||||
LOG_INFO("LR11x0 begin() attempt %d: tcxoVoltage=%.3fV at t=%ums", attempt, vref, attemptStart);
|
||||
int res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength, vref);
|
||||
LOG_INFO("LR11x0 begin() attempt %d returned %d after %ums", attempt, res, millis() - attemptStart);
|
||||
return res;
|
||||
};
|
||||
|
||||
#if defined(TCXO_OPTIONAL)
|
||||
// If init failed for any reason other than chip not found, retry without TCXO (XTAL mode)
|
||||
// 1. XTAL, because a TCXO-first attempt hangs RadioLib's unbounded calibration wait on a module
|
||||
// with no TCXO fitted, whereas XTAL fails fast and cleanly on a module that does have one
|
||||
float attemptVoltage = 0;
|
||||
#else
|
||||
// 1. Whatever Vref the variant configured, which it declared unconditionally
|
||||
float attemptVoltage = tcxoVoltage;
|
||||
#endif
|
||||
int res = tryBegin(1, attemptVoltage);
|
||||
|
||||
#if defined(TCXO_OPTIONAL)
|
||||
// 2. XTAL failed with the chip present, so fall back to the TCXO if the variant configured one
|
||||
if (res != RADIOLIB_ERR_NONE && res != RADIOLIB_ERR_CHIP_NOT_FOUND && tcxoVoltage > 0) {
|
||||
LOG_WARN("LR11x0 init failed with TCXO Vref %f V (err %d), retrying without TCXO", tcxoVoltage, res);
|
||||
tcxoVoltage = 0;
|
||||
res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage);
|
||||
LOG_WARN("LR11x0 XTAL init failed (err %d), retrying with TCXO Vref %f V", res, tcxoVoltage);
|
||||
attemptVoltage = tcxoVoltage;
|
||||
res = tryBegin(2, attemptVoltage);
|
||||
if (res == RADIOLIB_ERR_NONE)
|
||||
LOG_INFO("LR11x0 init success without TCXO (XTAL mode)");
|
||||
LOG_INFO("LR11x0 init success with TCXO Vref %f V", tcxoVoltage);
|
||||
}
|
||||
#endif
|
||||
|
||||
// 3. Some units need extra settling time, so give whichever oscillator we settled on one retry.
|
||||
// After a step 2 fallback that is a second TCXO attempt, which is where settling actually matters.
|
||||
if (lr11x0SpiFailed(res)) {
|
||||
LOG_WARN("LR11x0 init failed with %d (SPI command failure), retrying after delay...", res);
|
||||
delay(100);
|
||||
res = tryBegin(3, attemptVoltage);
|
||||
}
|
||||
|
||||
// \todo Display actual typename of the adapter, not just `LR11x0`
|
||||
LOG_INFO("LR11x0 init result %d", res);
|
||||
if (res == RADIOLIB_ERR_CHIP_NOT_FOUND || res == RADIOLIB_ERR_SPI_CMD_FAILED) {
|
||||
|
||||
if (res == RADIOLIB_ERR_CHIP_NOT_FOUND || lr11x0SpiFailed(res)) {
|
||||
#ifdef LR11X0_UPDATE_FIRMWARE_TO
|
||||
// An interrupted update leaves the radio sitting in bootloader mode, where begin() fails. Retry the
|
||||
// flash from here rather than giving up, otherwise the device could never recover on its own.
|
||||
|
||||
Reference in New Issue
Block a user