Add T-Beam 1W external LNA control

This commit is contained in:
mikecarper committed 2026-08-14 17:45:51 -07:00
1 parent 759a35fc4f
commit 366f6ab73d
5 files changed
+106 -2

No files matched your search

+39
View File
@@ -156,6 +156,45 @@ class CustomSX1262 : public SX1262 {
return _rx_ps_rf_rx_disabled;
}
// Select whether a host-controlled RXEN pin is asserted in receive mode.
// Some FEM modules (notably the LilyGo T-Beam 1W XY16P35) expose their
// external LNA supply on RXEN while DIO2 independently selects the RF
// switch. Swapping RadioLib's table keeps the requested state intact
// across RX, CAD, TX, standby, and duty-cycle transitions; a one-shot
// digitalWrite() would be overwritten on the next mode change.
bool setExternalRxLnaEnabled(bool enabled) {
#if defined(SX126X_RXEN)
#if defined(SX126X_TXEN)
static constexpr uint32_t tx_en = SX126X_TXEN;
#else
static constexpr uint32_t tx_en = RADIOLIB_NC;
#endif
static const uint32_t pins[Module::RFSWITCH_MAX_PINS] = {
SX126X_RXEN, tx_en, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC,
};
static const Module::RfSwitchMode_t lna_enabled_table[] = {
{ Module::MODE_IDLE, { LOW, LOW } },
{ Module::MODE_RX, { HIGH, LOW } },
{ Module::MODE_TX, { LOW, HIGH } },
END_OF_MODE_TABLE,
};
static const Module::RfSwitchMode_t lna_disabled_table[] = {
{ Module::MODE_IDLE, { LOW, LOW } },
{ Module::MODE_RX, { LOW, LOW } },
{ Module::MODE_TX, { LOW, HIGH } },
END_OF_MODE_TABLE,
};
this->mod->setRfSwitchTable(
pins, enabled ? lna_enabled_table : lna_disabled_table);
return true;
#else
(void)enabled;
return false;
#endif
}
// BUSY high means the chip is asleep (RX duty-cycle sleep window) or mid
// command; any SPI access would stall until the chip's next listen window.
bool isChipBusy() {
+29 -1
View File
@@ -78,6 +78,25 @@ public:
return ((CustomSX1262 *)_radio)->isRxPowerSavingRfRxDisabled();
}
// Reconfigure through the normal bounded radio transition so RXEN is never
// asserted while a transmission or unread packet owns the radio. The
// selected table is then honored by every RadioLib mode transition.
bool setExternalRxLnaEnabled(bool enabled) {
#if defined(SX126X_RXEN)
if (SX126X_RXEN == RADIOLIB_NC) return false;
uint8_t resume_rx = beginReconfigure();
if (resume_rx > 1) return false;
bool success = ((CustomSX1262 *)_radio)->setExternalRxLnaEnabled(enabled);
endReconfigure(resume_rx);
return success;
#else
(void)enabled;
return false;
#endif
}
protected:
int startReceiveMode() override {
if (_rx_ps_armed) {
@@ -118,7 +137,16 @@ protected:
bool radioDeepInit() override {
// std_init() re-runs RadioLib begin(), which starts with a hardware reset
// via NRST - the only way out of a hard-locked chip (BUSY stuck high).
return ((CustomSX1262 *)_radio)->std_init();
bool success = ((CustomSX1262 *)_radio)->std_init();
#if defined(TBEAM_1W)
// std_init() restores RadioLib's default RXEN table. Reapply the T-Beam
// 1W's persisted external-LNA selection before receive mode is re-armed.
if (success && _board->canControlLoRaFemLna()) {
success = ((CustomSX1262 *)_radio)->setExternalRxLnaEnabled(
_board->isLoRaFemLnaEnabled());
}
#endif
return success;
}
bool supportsRadioDeepInit() const override { return true; }
+28 -1
View File
@@ -1,4 +1,5 @@
#include "TBeam1WBoard.h"
#include <helpers/radiolib/CustomSX1262Wrapper.h>
void TBeam1WBoard::begin() {
ESP32Board::begin();
@@ -9,7 +10,10 @@ void TBeam1WBoard::begin() {
radio_powered = true;
delay(10); // Allow radio to power up
// RF switch RXEN pin handled by RadioLib via setRfSwitchPins()
// Keep the external LNA off until RadioLib deliberately enters RX. GPIO21
// is subsequently controlled by RadioLib's target-specific RF switch table.
pinMode(SX126X_RXEN, OUTPUT);
digitalWrite(SX126X_RXEN, LOW);
// Initialize LED
pinMode(LED_PIN, OUTPUT);
@@ -62,6 +66,29 @@ void TBeam1WBoard::powerOff() {
ESP32Board::powerOff();
}
void TBeam1WBoard::attachRadioDriver(CustomSX1262Wrapper* driver) {
radio_driver = driver;
}
bool TBeam1WBoard::setLoRaFemLnaEnabled(bool enable) {
if (radio_driver == nullptr || !radio_driver->setExternalRxLnaEnabled(enable)) {
return false;
}
// This is the requested RX-mode state. TX and standby always force CTRL low
// regardless of this preference, protecting both the LNA and PA paths.
lna_enabled = enable;
return true;
}
bool TBeam1WBoard::canControlLoRaFemLna() const {
return radio_driver != nullptr;
}
bool TBeam1WBoard::isLoRaFemLnaEnabled() const {
return lna_enabled;
}
void TBeam1WBoard::setFanEnabled(bool enabled) {
digitalWrite(FAN_CTRL_PIN, enabled ? HIGH : LOW);
}
+9
View File
@@ -4,6 +4,8 @@
#include <helpers/ESP32Board.h>
#include "variant.h"
class CustomSX1262Wrapper;
// LilyGo T-Beam 1W with SX1262 + external PA (XY16P35 module)
//
// Power architecture (LDO is separate chip on T-Beam board, not inside XY16P35):
@@ -30,6 +32,8 @@
class TBeam1WBoard : public ESP32Board {
private:
bool radio_powered = false;
bool lna_enabled = true;
CustomSX1262Wrapper* radio_driver = nullptr;
public:
void begin();
@@ -39,6 +43,11 @@ public:
const char* getManufacturerName() const override;
void powerOff() override;
void attachRadioDriver(CustomSX1262Wrapper* driver);
bool setLoRaFemLnaEnabled(bool enable) override;
bool canControlLoRaFemLna() const override;
bool isLoRaFemLnaEnabled() const override;
// Fan control methods
void setFanEnabled(bool enabled);
bool isFanEnabled() const;
+1
View File
@@ -36,6 +36,7 @@ bool radio_init() {
bool success = radio.std_init(&spi);
if (success) {
board.attachRadioDriver(&radio_driver);
// T-Beam 1W has external PA requiring longer ramp time (>800us recommended)
// RADIOLIB_SX126X_PA_RAMP_800U = 0x05
radio.setTxParams(LORA_TX_POWER, RADIOLIB_SX126X_PA_RAMP_800U);