diff --git a/src/configuration.h b/src/configuration.h index 53ae30d51d2..ee754f3227b 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -163,6 +163,10 @@ along with this program. If not, see . #define TX_GAIN_LORA 0 #endif +#ifndef HAS_LORA_FEM +#define HAS_LORA_FEM 0 +#endif + // ----------------------------------------------------------------------------- // Feature toggles // ----------------------------------------------------------------------------- diff --git a/src/mesh/LoRaFEMInterface.cpp b/src/mesh/LoRaFEMInterface.cpp new file mode 100644 index 00000000000..a1b56320e20 --- /dev/null +++ b/src/mesh/LoRaFEMInterface.cpp @@ -0,0 +1,193 @@ +#if HAS_LORA_FEM +#include "LoRaFEMInterface.h" + +#if defined(ARCH_ESP32) +#include +#include +#endif + +LoRaFEMInterface loraFEMInterface; +void LoRaFEMInterface::init(void) +{ + setLnaCanControl(false); // Default is uncontrollable +#ifdef HELTEC_V4 + pinMode(LORA_PA_POWER, OUTPUT); + digitalWrite(LORA_PA_POWER, HIGH); + rtc_gpio_hold_dis((gpio_num_t)LORA_PA_POWER); + delay(1); + rtc_gpio_hold_dis((gpio_num_t)LORA_KCT8103L_PA_CSD); + pinMode(LORA_KCT8103L_PA_CSD, INPUT); // detect which FEM is used + delay(1); + if (digitalRead(LORA_KCT8103L_PA_CSD) == HIGH) { + // FEM is KCT8103L + fem_type = KCT8103L_PA; + rtc_gpio_hold_dis((gpio_num_t)LORA_KCT8103L_PA_CTX); + pinMode(LORA_KCT8103L_PA_CSD, OUTPUT); + digitalWrite(LORA_KCT8103L_PA_CSD, HIGH); + pinMode(LORA_KCT8103L_PA_CTX, OUTPUT); + digitalWrite(LORA_KCT8103L_PA_CTX, HIGH); + setLnaCanControl(true); + } else if (digitalRead(LORA_KCT8103L_PA_CSD) == LOW) { + // FEM is GC1109 + fem_type = GC1109_PA; + // LORA_GC1109_PA_EN and LORA_KCT8103L_PA_CSD are the same pin and do not need to be repeatedly turned off and held. + // rtc_gpio_hold_dis((gpio_num_t)LORA_GC1109_PA_EN); + pinMode(LORA_GC1109_PA_EN, OUTPUT); + digitalWrite(LORA_GC1109_PA_EN, HIGH); + pinMode(LORA_GC1109_PA_TX_EN, OUTPUT); + digitalWrite(LORA_GC1109_PA_TX_EN, LOW); + } else { + fem_type = OTHER_FEM_TYPES; + } +#elif defined(USE_GC1109_PA) + fem_type = GC1109_PA; +#if defined(ARCH_ESP32) + rtc_gpio_hold_dis((gpio_num_t)LORA_PA_POWER); + rtc_gpio_hold_dis((gpio_num_t)LORA_GC1109_PA_EN); + rtc_gpio_hold_dis((gpio_num_t)LORA_GC1109_PA_TX_EN); +#endif + pinMode(LORA_PA_POWER, OUTPUT); + digitalWrite(LORA_PA_POWER, HIGH); + delay(1); + pinMode(LORA_GC1109_PA_EN, OUTPUT); + digitalWrite(LORA_GC1109_PA_EN, HIGH); + pinMode(LORA_GC1109_PA_TX_EN, OUTPUT); + digitalWrite(LORA_GC1109_PA_TX_EN, LOW); +#endif +} + +void LoRaFEMInterface::setSleepModeEnable(void) +{ +#ifdef HELTEC_V4 + if (fem_type == GC1109_PA) { + /* + * Do not switch the power on and off frequently. + * After turning off LORA_GC1109_PA_EN, the power consumption has dropped to the uA level. + */ + digitalWrite(LORA_GC1109_PA_EN, LOW); + digitalWrite(LORA_GC1109_PA_TX_EN, LOW); + } else if (fem_type == KCT8103L_PA) { + // shutdown the PA + digitalWrite(LORA_KCT8103L_PA_CSD, LOW); + } +#elif defined(USE_GC1109_PA) + digitalWrite(LORA_GC1109_PA_EN, LOW); + digitalWrite(LORA_GC1109_PA_TX_EN, LOW); +#endif +} + +void LoRaFEMInterface::setTxModeEnable(void) +{ +#ifdef HELTEC_V4 + if (fem_type == GC1109_PA) { + digitalWrite(LORA_GC1109_PA_EN, HIGH); // CSD=1: Chip enabled + digitalWrite(LORA_GC1109_PA_TX_EN, HIGH); // CPS: 1=full PA, 0=bypass (for RX, CPS is don't care) + } else if (fem_type == KCT8103L_PA) { + digitalWrite(LORA_KCT8103L_PA_CSD, HIGH); + digitalWrite(LORA_KCT8103L_PA_CTX, HIGH); + } +#elif defined(USE_GC1109_PA) + digitalWrite(LORA_GC1109_PA_EN, HIGH); // CSD=1: Chip enabled + digitalWrite(LORA_GC1109_PA_TX_EN, HIGH); // CPS: 1=full PA, 0=bypass (for RX, CPS is don't care) +#endif +} + +void LoRaFEMInterface::setRxModeEnable(void) +{ +#ifdef HELTEC_V4 + if (fem_type == GC1109_PA) { + digitalWrite(LORA_GC1109_PA_EN, HIGH); // CSD=1: Chip enabled + digitalWrite(LORA_GC1109_PA_TX_EN, LOW); + } else if (fem_type == KCT8103L_PA) { + digitalWrite(LORA_KCT8103L_PA_CSD, HIGH); + if (lna_enabled) { + digitalWrite(LORA_KCT8103L_PA_CTX, LOW); + } else { + digitalWrite(LORA_KCT8103L_PA_CTX, HIGH); + } + } +#elif defined(USE_GC1109_PA) + digitalWrite(LORA_GC1109_PA_EN, HIGH); // CSD=1: Chip enabled + digitalWrite(LORA_GC1109_PA_TX_EN, LOW); +#endif +} + +void LoRaFEMInterface::setRxModeEnableWhenMCUSleep(void) +{ + +#ifdef HELTEC_V4 + // Keep GC1109 FEM powered during deep sleep so LNA remains active for RX wake. + // Set PA_POWER and PA_EN HIGH (overrides SX126xInterface::sleep() shutdown), + // then latch with RTC hold so the state survives deep sleep. + digitalWrite(LORA_PA_POWER, HIGH); + rtc_gpio_hold_en((gpio_num_t)LORA_PA_POWER); + if (fem_type == GC1109_PA) { + digitalWrite(LORA_GC1109_PA_EN, HIGH); + rtc_gpio_hold_en((gpio_num_t)LORA_GC1109_PA_EN); + gpio_pulldown_en((gpio_num_t)LORA_GC1109_PA_TX_EN); + } else if (fem_type == KCT8103L_PA) { + digitalWrite(LORA_KCT8103L_PA_CSD, HIGH); + rtc_gpio_hold_en((gpio_num_t)LORA_KCT8103L_PA_CSD); + if (lna_enabled) { + digitalWrite(LORA_KCT8103L_PA_CTX, LOW); + } else { + digitalWrite(LORA_KCT8103L_PA_CTX, HIGH); + } + rtc_gpio_hold_en((gpio_num_t)LORA_KCT8103L_PA_CTX); + } +#elif defined(USE_GC1109_PA) + digitalWrite(LORA_PA_POWER, HIGH); + digitalWrite(LORA_GC1109_PA_EN, HIGH); +#if defined(ARCH_ESP32) + rtc_gpio_hold_en((gpio_num_t)LORA_PA_POWER); + rtc_gpio_hold_en((gpio_num_t)LORA_GC1109_PA_EN); + gpio_pulldown_en((gpio_num_t)LORA_GC1109_PA_TX_EN); +#endif +#endif +} + +void LoRaFEMInterface::setLNAEnable(bool enabled) +{ + lna_enabled = enabled; +} + +int8_t LoRaFEMInterface::powerConversion(int8_t loraOutputPower) +{ +#ifdef HELTEC_V4 + const uint16_t gc1109_tx_gain[] = {11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 9, 9, 8, 7}; + const uint16_t kct8103l_tx_gain[] = {13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 12, 12, 11, 11, 10, 9, 8, 7}; + const uint16_t *tx_gain; + uint16_t tx_gain_num; + if (fem_type == GC1109_PA) { + tx_gain = gc1109_tx_gain; + tx_gain_num = sizeof(gc1109_tx_gain) / sizeof(gc1109_tx_gain[0]); + } else if (fem_type == KCT8103L_PA) { + tx_gain = kct8103l_tx_gain; + tx_gain_num = sizeof(kct8103l_tx_gain) / sizeof(kct8103l_tx_gain[0]); + } else { + return loraOutputPower; + } +#else +#ifdef ARCH_PORTDUINO + size_t num_pa_points = portduino_config.num_pa_points; + const uint16_t *tx_gain = portduino_config.tx_gain_lora; + uint16_t tx_gain_num = num_pa_points; +#else + size_t num_pa_points = NUM_PA_POINTS; + const uint16_t tx_gain[NUM_PA_POINTS] = {TX_GAIN_LORA}; + uint16_t tx_gain_num = NUM_PA_POINTS; +#endif +#endif + for (int radio_dbm = 0; radio_dbm < tx_gain_num; radio_dbm++) { + if (((radio_dbm + tx_gain[radio_dbm]) > loraOutputPower) || + ((radio_dbm == (tx_gain_num - 1)) && ((radio_dbm + tx_gain[radio_dbm]) <= loraOutputPower))) { + // we've exceeded the power limit, or hit the max we can do + LOG_INFO("Requested Tx power: %d dBm; Device LoRa Tx gain: %d dB", loraOutputPower, tx_gain[radio_dbm]); + loraOutputPower -= tx_gain[radio_dbm]; + break; + } + } + return loraOutputPower; +} + +#endif \ No newline at end of file diff --git a/src/mesh/LoRaFEMInterface.h b/src/mesh/LoRaFEMInterface.h new file mode 100644 index 00000000000..0a7c810ef8b --- /dev/null +++ b/src/mesh/LoRaFEMInterface.h @@ -0,0 +1,30 @@ +#pragma once +#if HAS_LORA_FEM +#include "configuration.h" +#include + +typedef enum { GC1109_PA, KCT8103L_PA, OTHER_FEM_TYPES } LoRaFEMType; + +class LoRaFEMInterface +{ + public: + LoRaFEMInterface() {} + virtual ~LoRaFEMInterface() {} + void init(void); + void setSleepModeEnable(void); + void setTxModeEnable(void); + void setRxModeEnable(void); + void setRxModeEnableWhenMCUSleep(void); + void setLNAEnable(bool enabled); + int8_t powerConversion(int8_t loraOutputPower); + bool isLnaCanControl(void) { return lna_can_control; } + void setLnaCanControl(bool can_control) { lna_can_control = can_control; } + + private: + LoRaFEMType fem_type; + bool lna_enabled = false; + bool lna_can_control = false; +}; +extern LoRaFEMInterface loraFEMInterface; + +#endif \ No newline at end of file diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 2a481ac2537..4defd00ed4c 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -915,6 +915,12 @@ void RadioInterface::limitPower(int8_t loraMaxPower) power = maxPower; } +#if HAS_LORA_FEM + if (!devicestate.owner.is_licensed) { + power = loraFEMInterface.powerConversion(power); + } +#else +// todo:All entries containing "lora fem" are grouped together above. #ifdef ARCH_PORTDUINO size_t num_pa_points = portduino_config.num_pa_points; const uint16_t *tx_gain = portduino_config.tx_gain_lora; @@ -940,7 +946,7 @@ void RadioInterface::limitPower(int8_t loraMaxPower) } } } - +#endif if (power > loraMaxPower) // Clamp power to maximum defined level power = loraMaxPower; diff --git a/src/mesh/RadioInterface.h b/src/mesh/RadioInterface.h index 1fe3dd7b0f5..05825dce1c9 100644 --- a/src/mesh/RadioInterface.h +++ b/src/mesh/RadioInterface.h @@ -8,6 +8,10 @@ #include "error.h" #include +#if HAS_LORA_FEM +#include "LoRaFEMInterface.h" +#endif + // Forward decl to avoid a direct include of generated config headers / full LoRaConfig definition in this widely-included file. typedef struct _meshtastic_Config_LoRaConfig meshtastic_Config_LoRaConfig; diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index 553cd4ee5bf..5c9ab35979c 100644 --- a/src/mesh/SX126xInterface.cpp +++ b/src/mesh/SX126xInterface.cpp @@ -6,7 +6,7 @@ #ifdef ARCH_PORTDUINO #include "PortduinoGlue.h" #endif -#if defined(USE_GC1109_PA) && defined(ARCH_ESP32) +#if defined(ARCH_ESP32) #include #include #endif @@ -56,41 +56,8 @@ template bool SX126xInterface::init() pinMode(SX126X_POWER_EN, OUTPUT); #endif -#if defined(USE_GC1109_PA) - // GC1109 FEM chip initialization - // See variant.h for full pin mapping and control logic documentation - // - // On deep sleep wake, PA_POWER and PA_EN are held HIGH by RTC latch (set in - // enableLoraInterrupt). We configure GPIO registers before releasing the hold - // so the pad transitions atomically from held-HIGH to register-HIGH with no - // power glitch. On cold boot the hold_dis is a harmless no-op. - - // VFEM_Ctrl (LORA_PA_POWER): Power enable for GC1109 LDO (always on) - pinMode(LORA_PA_POWER, OUTPUT); - digitalWrite(LORA_PA_POWER, HIGH); - rtc_gpio_hold_dis((gpio_num_t)LORA_PA_POWER); - - // TLV75733P LDO has ~550us startup time (datasheet tSTR). On cold boot, wait - // for VBAT to stabilise before driving CSD/CPS, per GC1109 requirement: - // "VBAT must be prior to CSD/CPS/CTX for the power on sequence" - // On deep sleep wake the LDO was held on via RTC latch, so no delay needed. -#if defined(ARCH_ESP32) - if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_UNDEFINED) { - delayMicroseconds(1000); - } -#else - delayMicroseconds(1000); -#endif - - // CSD (LORA_PA_EN): Chip enable - must be HIGH to enable GC1109 for both RX and TX - pinMode(LORA_PA_EN, OUTPUT); - digitalWrite(LORA_PA_EN, HIGH); - rtc_gpio_hold_dis((gpio_num_t)LORA_PA_EN); - - // CPS (LORA_PA_TX_EN): PA mode select - HIGH enables full PA during TX, LOW for RX (don't care) - // Note: TX/RX path switching (CTX) is handled by DIO2 via SX126X_DIO2_AS_RF_SWITCH - pinMode(LORA_PA_TX_EN, OUTPUT); - digitalWrite(LORA_PA_TX_EN, LOW); // Start in RX-ready state +#if HAS_LORA_FEM + loraFEMInterface.init(); #endif #ifdef RF95_FAN_EN @@ -419,15 +386,10 @@ template bool SX126xInterface::sleep() digitalWrite(SX126X_POWER_EN, LOW); #endif -#if defined(USE_GC1109_PA) - /* - * Do not switch the power on and off frequently. - * After turning off LORA_PA_EN, the power consumption has dropped to the uA level. - * // digitalWrite(LORA_PA_POWER, LOW); - */ - digitalWrite(LORA_PA_EN, LOW); - digitalWrite(LORA_PA_TX_EN, LOW); +#if HAS_LORA_FEM + loraFEMInterface.setSleepModeEnable(); #endif + return true; } @@ -489,10 +451,12 @@ template void SX126xInterface::resetAGC() /** Control PA mode for GC1109 FEM - CPS pin selects full PA (txon=true) or bypass mode (txon=false) */ template void SX126xInterface::setTransmitEnable(bool txon) { -#if defined(USE_GC1109_PA) - digitalWrite(LORA_PA_POWER, HIGH); // Ensure LDO is on - digitalWrite(LORA_PA_EN, HIGH); // CSD=1: Chip enabled - digitalWrite(LORA_PA_TX_EN, txon ? 1 : 0); // CPS: 1=full PA, 0=bypass (for RX, CPS is don't care) +#if HAS_LORA_FEM + if (txon) { + loraFEMInterface.setTxModeEnable(); + } else { + loraFEMInterface.setRxModeEnable(); + } #endif } diff --git a/src/sleep.cpp b/src/sleep.cpp index 8470e9273c7..8603603eaf5 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -163,13 +163,6 @@ void initDeepSleep() if (wakeCause != ESP_SLEEP_WAKEUP_UNDEFINED) { LOG_DEBUG("Disable any holds on RTC IO pads"); for (uint8_t i = 0; i <= GPIO_NUM_MAX; i++) { -#if defined(USE_GC1109_PA) - // Skip GC1109 FEM power pins - they are held HIGH during deep sleep to keep - // the LNA active for RX wake. Released later in SX126xInterface::init() after - // GPIO registers are set HIGH first, avoiding a power glitch. - if (i == LORA_PA_POWER || i == LORA_PA_EN) - continue; -#endif if (rtc_gpio_is_valid_gpio((gpio_num_t)i)) rtc_gpio_hold_dis((gpio_num_t)i); @@ -567,15 +560,8 @@ void enableLoraInterrupt() gpio_pullup_en((gpio_num_t)LORA_CS); #endif -#if defined(USE_GC1109_PA) - // Keep GC1109 FEM powered during deep sleep so LNA remains active for RX wake. - // Set PA_POWER and PA_EN HIGH (overrides SX126xInterface::sleep() shutdown), - // then latch with RTC hold so the state survives deep sleep. - digitalWrite(LORA_PA_POWER, HIGH); - rtc_gpio_hold_en((gpio_num_t)LORA_PA_POWER); - digitalWrite(LORA_PA_EN, HIGH); - rtc_gpio_hold_en((gpio_num_t)LORA_PA_EN); - gpio_pulldown_en((gpio_num_t)LORA_PA_TX_EN); +#if HAS_LORA_FEM + loraFEMInterface.setRxModeEnableWhenMCUSleep(); #endif LOG_INFO("setup LORA_DIO1 (GPIO%02d) with wakeup by gpio interrupt", LORA_DIO1); diff --git a/variants/esp32s3/heltec_v4/platformio.ini b/variants/esp32s3/heltec_v4/platformio.ini index 72c53ded095..9591f2dc1bc 100644 --- a/variants/esp32s3/heltec_v4/platformio.ini +++ b/variants/esp32s3/heltec_v4/platformio.ini @@ -6,6 +6,7 @@ board_build.partitions = default_16MB.csv build_flags = ${esp32s3_base.build_flags} -D HELTEC_V4 + -D HAS_LORA_FEM=1 -I variants/esp32s3/heltec_v4 diff --git a/variants/esp32s3/heltec_v4/variant.h b/variants/esp32s3/heltec_v4/variant.h index 1c1168d9459..8843f75c9fb 100644 --- a/variants/esp32s3/heltec_v4/variant.h +++ b/variants/esp32s3/heltec_v4/variant.h @@ -30,8 +30,8 @@ #define SX126X_DIO3_TCXO_VOLTAGE 1.8 // ---- GC1109 RF FRONT END CONFIGURATION ---- -// The Heltec V4 uses a GC1109 FEM chip with integrated PA and LNA -// RF path: SX1262 -> GC1109 PA -> Pi attenuator -> Antenna +// The Heltec V4.2 uses a GC1109 FEM chip with integrated PA and LNA +// RF path: SX1262 -> Pi attenuator -> GC1109 PA -> Antenna // Measured net TX gain (non-linear due to PA compression): // +11dB at 0-15dBm input (e.g., 10dBm in -> 21dBm out) // +10dB at 16-17dBm input @@ -47,15 +47,31 @@ // CSD (pin 4) -> GPIO2: Chip enable (HIGH=on, LOW=shutdown) // CPS (pin 5) -> GPIO46: PA mode select (HIGH=full PA, LOW=bypass) // VCC0/VCC1 -> Vfem via U3 LDO, controlled by GPIO7 -#define USE_GC1109_PA -#define LORA_PA_POWER 7 // VFEM_Ctrl - GC1109 LDO power enable -#define LORA_PA_EN 2 // CSD - GC1109 chip enable (HIGH=on) -#define LORA_PA_TX_EN 46 // CPS - GC1109 PA mode (HIGH=full PA, LOW=bypass) - // GC1109 FEM: TX/RX path switching is handled by DIO2 -> CTX pin (via SX126X_DIO2_AS_RF_SWITCH) -// GPIO46 is CPS (PA mode), not TX control - setTransmitEnable() handles it in SX126xInterface.cpp // Do NOT use SX126X_TXEN/RXEN as that would cause double-control of GPIO46 +#define LORA_PA_POWER 7 // VFEM_Ctrl - GC1109 and KCT8103L LDO power enable +#define LORA_GC1109_PA_EN 2 // CSD - GC1109 chip enable (HIGH=on) +#define LORA_GC1109_PA_TX_EN 46 // CPS - GC1109 PA mode (HIGH=full PA, LOW=bypass) + +// ---- KCT8103L RF FRONT END CONFIGURATION ---- +// The Heltec V4.3 uses a KCT8103L FEM chip with integrated PA and LNA +// RF path: SX1262 -> Pi attenuator -> KCT8103L PA -> Antenna +// Control logic (from KCT8103L datasheet): +// Transmit PA: CSD=1, CTX=1, CPS=1 +// Receive LNA: CSD=1, CTX=0, CPS=X (21dB gain, 1.9dB NF) +// Receive bypass: CSD=1, CTX=1, CPS=0 +// Shutdown: CSD=0, CTX=X, CPS=X +// Pin mapping: +// CPS (pin 5) -> SX1262 DIO2: TX/RX path select (automatic via SX126X_DIO2_AS_RF_SWITCH) +// CSD (pin 4) -> GPIO2: Chip enable (HIGH=on, LOW=shutdown) +// CTX (pin 6) -> GPIO5: Switch between Receive LNA Mode and Receive Bypass Mode. (HIGH=bypass PA, LOW=LNA) +// VCC0/VCC1 -> Vfem via U3 LDO, controlled by GPIO7 +// KCT8103L FEM: TX/RX path switching is handled by DIO2 -> CPS pin (via SX126X_DIO2_AS_RF_SWITCH) + +#define LORA_KCT8103L_PA_CSD 2 // CSD - KCT8103L chip enable (HIGH=on) +#define LORA_KCT8103L_PA_CTX 5 // CTX - Switch between Receive LNA Mode and Receive Bypass Mode. (HIGH=bypass PA, LOW=LNA) + #if HAS_TFT #define USE_TFTDISPLAY 1 #endif diff --git a/variants/esp32s3/heltec_wireless_tracker_v2/platformio.ini b/variants/esp32s3/heltec_wireless_tracker_v2/platformio.ini index a5277ba196d..ebf0118bbed 100644 --- a/variants/esp32s3/heltec_wireless_tracker_v2/platformio.ini +++ b/variants/esp32s3/heltec_wireless_tracker_v2/platformio.ini @@ -17,6 +17,7 @@ build_flags = ${esp32s3_base.build_flags} -I variants/esp32s3/heltec_wireless_tracker_v2 -D HELTEC_WIRELESS_TRACKER_V2 + -D HAS_LORA_FEM=1 lib_deps = ${esp32s3_base.lib_deps} # renovate: datasource=custom.pio depName=LovyanGFX packageName=lovyan03/library/LovyanGFX diff --git a/variants/esp32s3/heltec_wireless_tracker_v2/variant.h b/variants/esp32s3/heltec_wireless_tracker_v2/variant.h index 0ca2dfc033f..7937039ba69 100644 --- a/variants/esp32s3/heltec_wireless_tracker_v2/variant.h +++ b/variants/esp32s3/heltec_wireless_tracker_v2/variant.h @@ -92,9 +92,9 @@ // CPS (pin 5) -> GPIO46: PA mode select (HIGH=full PA, LOW=bypass) // VCC0/VCC1 -> Vfem via U3 LDO, controlled by GPIO7 #define USE_GC1109_PA -#define LORA_PA_POWER 7 // VFEM_Ctrl - GC1109 LDO power enable -#define LORA_PA_EN 4 // CSD - GC1109 chip enable (HIGH=on) -#define LORA_PA_TX_EN 46 // CPS - GC1109 PA mode (HIGH=full PA, LOW=bypass) +#define LORA_PA_POWER 7 // VFEM_Ctrl - GC1109 LDO power enable +#define LORA_GC1109_PA_EN 4 // CSD - GC1109 chip enable (HIGH=on) +#define LORA_GC1109_PA_TX_EN 46 // CPS - GC1109 PA mode (HIGH=full PA, LOW=bypass) // GC1109 FEM: TX/RX path switching is handled by DIO2 -> CTX pin (via SX126X_DIO2_AS_RF_SWITCH) // GPIO46 is CPS (PA mode), not TX control - setTransmitEnable() handles it in SX126xInterface.cpp