diff --git a/src/configuration.h b/src/configuration.h index 53ae30d51d2..1824f018813 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -143,12 +143,6 @@ along with this program. If not, see . #define SX126X_MAX_POWER 22 #endif -#ifdef USE_GC1109_PA -// Power Amps are often non-linear, so we can use an array of values for the power curve -#define NUM_PA_POINTS 22 -#define TX_GAIN_LORA 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 9, 9, 8, 7 -#endif - #ifdef RAK13302 #define NUM_PA_POINTS 22 #define TX_GAIN_LORA 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8 diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp index 553cd4ee5bf..f7db20d30f6 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(USE_LORA_FEM) && defined(ARCH_ESP32) #include #include #endif @@ -56,41 +56,53 @@ template bool SX126xInterface::init() pinMode(SX126X_POWER_EN, OUTPUT); #endif -#if defined(USE_GC1109_PA) - // GC1109 FEM chip initialization +#if defined(USE_LORA_FEM) + // FEM chip initialization (GC1109 / SKY66122-11) // 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. + // On deep sleep wake (ESP32 with LORA_FEM_POWER), power and CSD are held HIGH + // by RTC latch. We configure GPIO registers before releasing the hold so the + // pad transitions atomically from held-HIGH to register-HIGH with no glitch. + +#ifdef LORA_FEM_POWER + pinMode(LORA_FEM_POWER, OUTPUT); + digitalWrite(LORA_FEM_POWER, HIGH); +#if SOC_RTCIO_HOLD_SUPPORTED + rtc_gpio_hold_dis((gpio_num_t)LORA_FEM_POWER); +#endif + // LDO startup delay — on cold boot, wait for supply to stabilise before + // driving CSD/CPS/CTX. On deep sleep wake the LDO was held on via RTC latch. #if defined(ARCH_ESP32) - if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_UNDEFINED) { + if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_UNDEFINED) delayMicroseconds(1000); - } #else delayMicroseconds(1000); #endif +#endif + +#ifdef LORA_FEM_CTX + // MCU-driven CTX: drive LOW first to prevent transient TX mode while + // CSD/CPS are being enabled (no pull-downs on RAK13302). + pinMode(LORA_FEM_CTX, OUTPUT); + digitalWrite(LORA_FEM_CTX, LOW); +#endif + + pinMode(LORA_FEM_CSD, OUTPUT); + digitalWrite(LORA_FEM_CSD, HIGH); +#if SOC_RTCIO_HOLD_SUPPORTED + rtc_gpio_hold_dis((gpio_num_t)LORA_FEM_CSD); +#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); + pinMode(LORA_FEM_CPS, OUTPUT); +#ifdef LORA_FEM_CTX + // MCU-driven CTX: CPS stays HIGH for both TX and RX + digitalWrite(LORA_FEM_CPS, HIGH); +#else + // DIO2-driven CTX: CPS selects PA mode, start in RX-ready + digitalWrite(LORA_FEM_CPS, LOW); +#endif - // 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 + delay(1); // FEM settling time #endif #ifdef RF95_FAN_EN @@ -197,9 +209,9 @@ template bool SX126xInterface::init() // Undocumented SX1262 register patch recommended by Heltec/Semtech for improved RX sensitivity. // Sets bit 0 of register 0x8B5. if (module.SPIsetRegValue(0x8B5, 0x01, 0, 0) == RADIOLIB_ERR_NONE) { - LOG_INFO("Applied SX1262 register 0x8B5 patch for GC1109 RX improvement"); + LOG_INFO("Applied SX1262 register 0x8B5 patch for FEM RX improvement"); } else { - LOG_WARN("Failed to apply SX1262 register 0x8B5 patch for GC1109"); + LOG_WARN("Failed to apply SX1262 register 0x8B5 patch"); } #if 0 @@ -419,14 +431,14 @@ 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 defined(USE_LORA_FEM) +#ifdef LORA_FEM_CTX + digitalWrite(LORA_FEM_CTX, LOW); +#endif + digitalWrite(LORA_FEM_CPS, LOW); + digitalWrite(LORA_FEM_CSD, LOW); + // LORA_FEM_POWER intentionally left on — avoids frequent power cycling + // and deep sleep code in sleep.cpp re-latches CSD if LNA wake is needed #endif return true; } @@ -486,13 +498,25 @@ template void SX126xInterface::resetAGC() startReceive(); } -/** Control PA mode for GC1109 FEM - CPS pin selects full PA (txon=true) or bypass mode (txon=false) */ +/** Control FEM PA mode. + * - With MCU-driven CTX (LORA_FEM_CTX defined), txon=true selects the TX path and txon=false selects the RX path. + * - With DIO2-driven CTX (no LORA_FEM_CTX), this toggles CPS (bypass vs active path); TX/RX path is controlled by the radio. + */ 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 defined(USE_LORA_FEM) +#ifdef LORA_FEM_POWER + digitalWrite(LORA_FEM_POWER, HIGH); +#endif + digitalWrite(LORA_FEM_CSD, HIGH); +#ifdef LORA_FEM_CTX + // MCU-driven CTX: CPS always HIGH, toggle CTX for TX/RX + digitalWrite(LORA_FEM_CPS, HIGH); + digitalWrite(LORA_FEM_CTX, txon ? HIGH : LOW); +#else + // DIO2-driven CTX: toggle CPS for PA mode + digitalWrite(LORA_FEM_CPS, txon ? HIGH : LOW); +#endif #endif } diff --git a/src/sleep.cpp b/src/sleep.cpp index 8470e9273c7..751d3acc015 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -163,11 +163,15 @@ 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 +#if defined(USE_LORA_FEM) + // Skip 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) + if (false +#ifdef LORA_FEM_POWER + || i == LORA_FEM_POWER +#endif + || i == LORA_FEM_CSD) continue; #endif if (rtc_gpio_is_valid_gpio((gpio_num_t)i)) @@ -567,15 +571,15 @@ 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), +#if defined(USE_LORA_FEM) && defined(LORA_FEM_POWER) + // Keep FEM powered during deep sleep so LNA remains active for RX wake. + // Set POWER and CSD 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); + digitalWrite(LORA_FEM_POWER, HIGH); + rtc_gpio_hold_en((gpio_num_t)LORA_FEM_POWER); + digitalWrite(LORA_FEM_CSD, HIGH); + rtc_gpio_hold_en((gpio_num_t)LORA_FEM_CSD); + gpio_pulldown_en((gpio_num_t)LORA_FEM_CPS); #endif LOG_INFO("setup LORA_DIO1 (GPIO%02d) with wakeup by gpio interrupt", LORA_DIO1); diff --git a/variants/esp32s3/heltec_v4/variant.h b/variants/esp32s3/heltec_v4/variant.h index 1c1168d9459..8f9e7248971 100644 --- a/variants/esp32s3/heltec_v4/variant.h +++ b/variants/esp32s3/heltec_v4/variant.h @@ -47,15 +47,20 @@ // 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) +#define USE_LORA_FEM +#define LORA_FEM_POWER 7 // VFEM_Ctrl - LDO power enable +#define LORA_FEM_CSD 2 // Chip enable (HIGH=on, LOW=shutdown) +#define LORA_FEM_CPS 46 // PA mode (HIGH=full PA, LOW=bypass) +// CTX handled by DIO2 via SX126X_DIO2_AS_RF_SWITCH // 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 +// Power Amps are often non-linear, so we use an array of values for the power curve +#define NUM_PA_POINTS 22 +#define TX_GAIN_LORA 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 9, 9, 8, 7 + #if HAS_TFT #define USE_TFTDISPLAY 1 #endif diff --git a/variants/esp32s3/heltec_wireless_tracker_v2/variant.h b/variants/esp32s3/heltec_wireless_tracker_v2/variant.h index 0ca2dfc033f..fa8069fb949 100644 --- a/variants/esp32s3/heltec_wireless_tracker_v2/variant.h +++ b/variants/esp32s3/heltec_wireless_tracker_v2/variant.h @@ -91,11 +91,16 @@ // CSD (pin 4) -> GPIO4: 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 4 // CSD - GC1109 chip enable (HIGH=on) -#define LORA_PA_TX_EN 46 // CPS - GC1109 PA mode (HIGH=full PA, LOW=bypass) +#define USE_LORA_FEM +#define LORA_FEM_POWER 7 // VFEM_Ctrl - LDO power enable +#define LORA_FEM_CSD 4 // Chip enable (HIGH=on, LOW=shutdown) +#define LORA_FEM_CPS 46 // PA mode (HIGH=full PA, LOW=bypass) +// CTX handled by DIO2 via SX126X_DIO2_AS_RF_SWITCH // 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 \ No newline at end of file +// Do NOT use SX126X_TXEN/RXEN as that would cause double-control of GPIO46 + +// Power Amps are often non-linear, so we use an array of values for the power curve +#define NUM_PA_POINTS 22 +#define TX_GAIN_LORA 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 9, 9, 8, 7 \ No newline at end of file diff --git a/variants/nrf52840/rak3401_1watt/variant.h b/variants/nrf52840/rak3401_1watt/variant.h index 80b09cf695f..35b82ac0a78 100644 --- a/variants/nrf52840/rak3401_1watt/variant.h +++ b/variants/nrf52840/rak3401_1watt/variant.h @@ -158,8 +158,23 @@ static const uint8_t SCK = PIN_SPI_SCK; #define SX126X_BUSY (9) #define SX126X_RESET (4) -#define SX126X_POWER_EN (21) -// DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3 +/* + * SKY66122-11 Front-End Module control (RAK13302 1W module) + * + * CSD (P0.24) — Chip enable: HIGH to power up the FEM, LOW for shutdown (<1 μA) + * CPS (P0.21) — Path select: HIGH = active (required for TX and RX), LOW = shutdown + * CTX (P0.31) — TX/RX select + 5 V boost enable: + * HIGH = TX mode (boost on, PA energized at 5 V) + * LOW = RX mode (boost off, LNA on 3.3 V) + * + * R25 (DIO2→CTX) is NC by default on the RAK13302, so the MCU must drive CTX directly. + */ +#define USE_LORA_FEM +#define LORA_FEM_CSD (24) // Chip enable +#define LORA_FEM_CPS (21) // Path select (HIGH for both TX and RX) +#define LORA_FEM_CTX (31) // TX/RX select + 5V boost (MCU-driven, R25 NC) + +// Configure SX1262 DIO2 as RF-switch control output (not connected on this module, R25 NC) #define SX126X_DIO2_AS_RF_SWITCH #define SX126X_DIO3_TCXO_VOLTAGE 1.8