Skip to content

Commit a38ebee

Browse files
author
Wessel Nieboer
committed
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.
1 parent 417ca86 commit a38ebee

5 files changed

Lines changed: 68 additions & 0 deletions

File tree

src/main.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,6 +1125,13 @@ void loop()
11251125
lastRadioMissedIrqPoll = millis();
11261126
RadioLibInterface::instance->pollMissedIrqs();
11271127
}
1128+
1129+
// Periodic AGC reset — warm sleep + recalibrate to prevent stuck AGC gain
1130+
static uint32_t lastAgcReset;
1131+
if (!Throttle::isWithinTimespanMs(lastAgcReset, AGC_RESET_INTERVAL_MS)) {
1132+
lastAgcReset = millis();
1133+
RadioLibInterface::instance->resetAGC();
1134+
}
11281135
}
11291136

11301137
#ifdef DEBUG_STACK

src/mesh/RadioLibInterface.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,11 @@ void RadioLibInterface::pollMissedIrqs()
529529
}
530530
}
531531

532+
void RadioLibInterface::resetAGC()
533+
{
534+
// Base implementation: no-op. Override in chip-specific subclasses.
535+
}
536+
532537
void RadioLibInterface::checkRxDoneIrqFlag()
533538
{
534539
if (iface->checkIrq(RADIOLIB_IRQ_RX_DONE)) {

src/mesh/RadioLibInterface.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
// In addition to the default Rx flags, we need the PREAMBLE_DETECTED flag to detect whether we are actively receiving
2020
#define MESHTASTIC_RADIOLIB_IRQ_RX_FLAGS (RADIOLIB_IRQ_RX_DEFAULT_FLAGS | (1 << RADIOLIB_IRQ_PREAMBLE_DETECTED))
2121

22+
#define AGC_RESET_INTERVAL_MS (60 * 1000) // 60 seconds
23+
2224
/**
2325
* We need to override the RadioLib ArduinoHal class to add mutex protection for SPI bus access
2426
*/
@@ -117,6 +119,13 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
117119
*/
118120
void pollMissedIrqs();
119121

122+
/**
123+
* Reset AGC by power-cycling the analog frontend.
124+
* Subclasses override with chip-specific calibration sequences.
125+
* Safe to call periodically — skips if currently sending or receiving.
126+
*/
127+
virtual void resetAGC();
128+
120129
/**
121130
* Debugging counts
122131
*/

src/mesh/SX126xInterface.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,51 @@ template <typename T> bool SX126xInterface<T>::sleep()
434434
return true;
435435
}
436436

437+
template <typename T> void SX126xInterface<T>::resetAGC()
438+
{
439+
// Safety: don't reset mid-packet
440+
if (sendingPacket != NULL || (isReceiving && isActivelyReceiving()))
441+
return;
442+
443+
LOG_INFO("SX126x AGC reset: warm sleep + Calibrate(0x7F)");
444+
445+
// 1. Warm sleep — powers down the entire analog frontend, resetting AGC state.
446+
// A plain standby→startReceive cycle does NOT reset the AGC.
447+
lora.sleep(true);
448+
449+
// 2. Wake to RC standby for stable calibration
450+
lora.standby(RADIOLIB_SX126X_STANDBY_RC, true);
451+
452+
// 3. Calibrate all blocks (ADC, PLL, image, RC oscillators)
453+
uint8_t calData = RADIOLIB_SX126X_CALIBRATE_ALL;
454+
module.SPIwriteStream(RADIOLIB_SX126X_CMD_CALIBRATE, &calData, 1, true, false);
455+
456+
// 4. Wait for calibration to complete (BUSY pin goes low)
457+
module.hal->delay(5);
458+
uint32_t start = millis();
459+
while (module.hal->digitalRead(module.getGpio())) {
460+
if (millis() - start > 50)
461+
break;
462+
module.hal->yield();
463+
}
464+
465+
// 5. Re-apply settings that calibration may have reset
466+
467+
// DIO2 as RF switch
468+
#ifdef SX126X_DIO2_AS_RF_SWITCH
469+
lora.setDio2AsRfSwitch(true);
470+
#elif defined(ARCH_PORTDUINO)
471+
if (portduino_config.dio2_as_rf_switch)
472+
lora.setDio2AsRfSwitch(true);
473+
#endif
474+
475+
// RX boosted gain mode
476+
lora.setRxBoostedGainMode(config.lora.sx126x_rx_boosted_gain);
477+
478+
// 6. Resume receiving
479+
startReceive();
480+
}
481+
437482
/** Control PA mode for GC1109 FEM - CPS pin selects full PA (txon=true) or bypass mode (txon=false) */
438483
template <typename T> void SX126xInterface<T>::setTransmitEnable(bool txon)
439484
{

src/mesh/SX126xInterface.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ template <class T> class SX126xInterface : public RadioLibInterface
2828

2929
bool isIRQPending() override { return lora.getIrqFlags() != 0; }
3030

31+
void resetAGC() override;
32+
3133
void setTCXOVoltage(float voltage) { tcxoVoltage = voltage; }
3234

3335
protected:

0 commit comments

Comments
 (0)