Implement 'agc' reset for SX126x & LR11x0 chip families (#9705)
* Implement 'agc' reset for SX126x chip family There's no actual agc on SX126x chips but you can reset the analog registers by doing a warm sleep & running calibration. * Address PR comments & implement for LR11x0 too * calibrate for configured frequency band * Gate LR11X0_AGC_RESET
This commit is contained in:
@@ -1125,6 +1125,13 @@ void loop()
|
|||||||
lastRadioMissedIrqPoll = millis();
|
lastRadioMissedIrqPoll = millis();
|
||||||
RadioLibInterface::instance->pollMissedIrqs();
|
RadioLibInterface::instance->pollMissedIrqs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Periodic AGC reset — warm sleep + recalibrate to prevent stuck AGC gain
|
||||||
|
static uint32_t lastAgcReset;
|
||||||
|
if (!Throttle::isWithinTimespanMs(lastAgcReset, AGC_RESET_INTERVAL_MS)) {
|
||||||
|
lastAgcReset = millis();
|
||||||
|
RadioLibInterface::instance->resetAGC();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_STACK
|
#ifdef DEBUG_STACK
|
||||||
|
|||||||
@@ -299,6 +299,38 @@ template <typename T> bool LR11x0Interface<T>::isActivelyReceiving()
|
|||||||
RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED);
|
RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef LR11X0_AGC_RESET
|
||||||
|
template <typename T> void LR11x0Interface<T>::resetAGC()
|
||||||
|
{
|
||||||
|
// Safety: don't reset mid-packet
|
||||||
|
if (sendingPacket != NULL || (isReceiving && isActivelyReceiving()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
LOG_DEBUG("LR11x0 AGC reset: warm sleep + Calibrate(0x3F)");
|
||||||
|
|
||||||
|
// 1. Warm sleep — powers down the analog frontend, resetting AGC state
|
||||||
|
lora.sleep(true, 0);
|
||||||
|
|
||||||
|
// 2. Wake to RC standby for stable calibration
|
||||||
|
lora.standby(RADIOLIB_LR11X0_STANDBY_RC, true);
|
||||||
|
|
||||||
|
// 3. Calibrate all blocks (PLL, ADC, image, RC oscillators)
|
||||||
|
// calibrate() is protected on LR11x0, so use raw SPI (same as internal implementation)
|
||||||
|
uint8_t calData = RADIOLIB_LR11X0_CALIBRATE_ALL;
|
||||||
|
module.SPIwriteStream(RADIOLIB_LR11X0_CMD_CALIBRATE, &calData, 1, true, true);
|
||||||
|
|
||||||
|
// 4. Re-calibrate image rejection for actual operating frequency
|
||||||
|
// Calibrate(0x3F) defaults to 902-928 MHz which is wrong for other regions.
|
||||||
|
lora.calibrateImageRejection(getFreq() - 4.0f, getFreq() + 4.0f);
|
||||||
|
|
||||||
|
// 5. Re-apply RX boosted gain mode
|
||||||
|
lora.setRxBoostedGainMode(config.lora.sx126x_rx_boosted_gain);
|
||||||
|
|
||||||
|
// 6. Resume receiving
|
||||||
|
startReceive();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
template <typename T> bool LR11x0Interface<T>::sleep()
|
template <typename T> bool LR11x0Interface<T>::sleep()
|
||||||
{
|
{
|
||||||
// \todo Display actual typename of the adapter, not just `LR11x0`
|
// \todo Display actual typename of the adapter, not just `LR11x0`
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ template <class T> class LR11x0Interface : public RadioLibInterface
|
|||||||
|
|
||||||
bool isIRQPending() override { return lora.getIrqFlags() != 0; }
|
bool isIRQPending() override { return lora.getIrqFlags() != 0; }
|
||||||
|
|
||||||
|
#ifdef LR11X0_AGC_RESET
|
||||||
|
void resetAGC() override;
|
||||||
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
* Specific module instance
|
* Specific module instance
|
||||||
|
|||||||
@@ -529,6 +529,11 @@ void RadioLibInterface::pollMissedIrqs()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RadioLibInterface::resetAGC()
|
||||||
|
{
|
||||||
|
// Base implementation: no-op. Override in chip-specific subclasses.
|
||||||
|
}
|
||||||
|
|
||||||
void RadioLibInterface::checkRxDoneIrqFlag()
|
void RadioLibInterface::checkRxDoneIrqFlag()
|
||||||
{
|
{
|
||||||
if (iface->checkIrq(RADIOLIB_IRQ_RX_DONE)) {
|
if (iface->checkIrq(RADIOLIB_IRQ_RX_DONE)) {
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
// In addition to the default Rx flags, we need the PREAMBLE_DETECTED flag to detect whether we are actively receiving
|
// In addition to the default Rx flags, we need the PREAMBLE_DETECTED flag to detect whether we are actively receiving
|
||||||
#define MESHTASTIC_RADIOLIB_IRQ_RX_FLAGS (RADIOLIB_IRQ_RX_DEFAULT_FLAGS | (1 << RADIOLIB_IRQ_PREAMBLE_DETECTED))
|
#define MESHTASTIC_RADIOLIB_IRQ_RX_FLAGS (RADIOLIB_IRQ_RX_DEFAULT_FLAGS | (1 << RADIOLIB_IRQ_PREAMBLE_DETECTED))
|
||||||
|
|
||||||
|
#define AGC_RESET_INTERVAL_MS (60 * 1000) // 60 seconds
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We need to override the RadioLib ArduinoHal class to add mutex protection for SPI bus access
|
* We need to override the RadioLib ArduinoHal class to add mutex protection for SPI bus access
|
||||||
*/
|
*/
|
||||||
@@ -117,6 +119,13 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
|
|||||||
*/
|
*/
|
||||||
void pollMissedIrqs();
|
void pollMissedIrqs();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset AGC by power-cycling the analog frontend.
|
||||||
|
* Subclasses override with chip-specific calibration sequences.
|
||||||
|
* Safe to call periodically — skips if currently sending or receiving.
|
||||||
|
*/
|
||||||
|
virtual void resetAGC();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Debugging counts
|
* Debugging counts
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -434,6 +434,61 @@ template <typename T> bool SX126xInterface<T>::sleep()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T> void SX126xInterface<T>::resetAGC()
|
||||||
|
{
|
||||||
|
// Safety: don't reset mid-packet
|
||||||
|
if (sendingPacket != NULL || (isReceiving && isActivelyReceiving()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
LOG_DEBUG("SX126x AGC reset: warm sleep + Calibrate(0x7F)");
|
||||||
|
|
||||||
|
// 1. Warm sleep — powers down the entire analog frontend, resetting AGC state.
|
||||||
|
// A plain standby→startReceive cycle does NOT reset the AGC.
|
||||||
|
lora.sleep(true);
|
||||||
|
|
||||||
|
// 2. Wake to RC standby for stable calibration
|
||||||
|
lora.standby(RADIOLIB_SX126X_STANDBY_RC, true);
|
||||||
|
|
||||||
|
// 3. Calibrate all blocks (ADC, PLL, image, RC oscillators)
|
||||||
|
uint8_t calData = RADIOLIB_SX126X_CALIBRATE_ALL;
|
||||||
|
module.SPIwriteStream(RADIOLIB_SX126X_CMD_CALIBRATE, &calData, 1, true, false);
|
||||||
|
|
||||||
|
// 4. Wait for calibration to complete (BUSY pin goes low)
|
||||||
|
module.hal->delay(5);
|
||||||
|
uint32_t start = millis();
|
||||||
|
while (module.hal->digitalRead(module.getGpio())) {
|
||||||
|
if (millis() - start > 50)
|
||||||
|
break;
|
||||||
|
module.hal->yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (module.hal->digitalRead(module.getGpio())) {
|
||||||
|
LOG_WARN("SX126x AGC reset: calibration did not complete within 50ms");
|
||||||
|
startReceive();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Re-calibrate image rejection for actual operating frequency
|
||||||
|
// Calibrate(0x7F) defaults to 902-928 MHz which is wrong for other regions.
|
||||||
|
lora.calibrateImage(getFreq());
|
||||||
|
|
||||||
|
// Re-apply settings that calibration may have reset
|
||||||
|
|
||||||
|
// DIO2 as RF switch
|
||||||
|
#ifdef SX126X_DIO2_AS_RF_SWITCH
|
||||||
|
lora.setDio2AsRfSwitch(true);
|
||||||
|
#elif defined(ARCH_PORTDUINO)
|
||||||
|
if (portduino_config.dio2_as_rf_switch)
|
||||||
|
lora.setDio2AsRfSwitch(true);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// RX boosted gain mode
|
||||||
|
lora.setRxBoostedGainMode(config.lora.sx126x_rx_boosted_gain);
|
||||||
|
|
||||||
|
// 6. Resume receiving
|
||||||
|
startReceive();
|
||||||
|
}
|
||||||
|
|
||||||
/** Control PA mode for GC1109 FEM - CPS pin selects full PA (txon=true) or bypass mode (txon=false) */
|
/** Control PA mode for GC1109 FEM - CPS pin selects full PA (txon=true) or bypass mode (txon=false) */
|
||||||
template <typename T> void SX126xInterface<T>::setTransmitEnable(bool txon)
|
template <typename T> void SX126xInterface<T>::setTransmitEnable(bool txon)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ template <class T> class SX126xInterface : public RadioLibInterface
|
|||||||
|
|
||||||
bool isIRQPending() override { return lora.getIrqFlags() != 0; }
|
bool isIRQPending() override { return lora.getIrqFlags() != 0; }
|
||||||
|
|
||||||
|
void resetAGC() override;
|
||||||
|
|
||||||
void setTCXOVoltage(float voltage) { tcxoVoltage = voltage; }
|
void setTCXOVoltage(float voltage) { tcxoVoltage = voltage; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
Reference in New Issue
Block a user