mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-09-01 21:38:20 +00:00
implement radio.fem.rxgain
This commit is contained in:
@@ -191,6 +191,31 @@ An LR1110 running firmware older than 0x0303 cannot be moved off the public LoRa
|
||||
and receives perfectly well, but nobody on your mesh can see it. That now appears in the log instead of
|
||||
looking like a broken radio.
|
||||
|
||||
## New setting: switch the antenna amplifier's receive gain off
|
||||
|
||||
Some boards carry an extra amplifier chip between the radio and the antenna. It boosts what the node
|
||||
transmits, and on the way in it boosts what the node hears. It has always been switched on for both, and
|
||||
it stays that way — **nothing changes unless you change it.**
|
||||
|
||||
The new `set radio.fem.rxgain 0` switches off only the receive side, to save the current that part of the
|
||||
amplifier draws. Transmitting is untouched: every packet the node sends still goes out through the
|
||||
amplifier at full strength. `set radio.fem.rxgain 1` puts it back, and `get radio.fem.rxgain` shows where
|
||||
it stands. The node keeps the setting across reboots.
|
||||
|
||||
> [!WARNING]
|
||||
> **This costs range, and a lot of it.** With the receive side off the node goes substantially deafer —
|
||||
> on a Wireless Tracker V2 the measured noise floor moves by about 23 dB between the two settings. Distant
|
||||
> and weak neighbours simply stop being heard, while the node's own transmissions carry exactly as far as
|
||||
> before, so from the outside it still looks perfectly healthy. Only worth doing on a battery-powered node
|
||||
> where you already know every neighbour is close and strong.
|
||||
|
||||
Supported on the **Heltec T096**, **Wireless Tracker V2**, **WiFi LoRa 32 V4** and **WiFi LoRa 32 V4.3**.
|
||||
Everything else replies `Error: unsupported` — either the board has no such amplifier, or its amplifier is
|
||||
switched on by a line the radio driver cannot reach. The **RAK3401 1 W** is in that second group.
|
||||
|
||||
Proposed by **bisbille** ([@bisbille](https://github.com/bisbille)) —
|
||||
[#74](https://github.com/liquidraver/ZephCore/pull/74).
|
||||
|
||||
---
|
||||
|
||||
## Also in this release
|
||||
@@ -219,3 +244,7 @@ Housekeeping, listed for completeness — nothing here changes how a node behave
|
||||
but no real node has been broken and recovered. Normal healthy nodes are unaffected.
|
||||
- **Why that one node's identity broke is still unknown.** This release makes sure the same thing can no
|
||||
longer go unnoticed, but the original cause has not been found.
|
||||
- **The antenna amplifier receive setting is compile-checked on every affected board, but has not been
|
||||
measured on hardware in this form.** The 23 dB figure above comes from the original proposal, which
|
||||
reached the same pin by a different route. Leaving the setting alone keeps every node behaving exactly
|
||||
as it did before, so a node nobody deliberately switches over is not affected at all.
|
||||
|
||||
@@ -260,6 +260,7 @@ All `set uplink.*` changes are saved immediately and only applied after reboot.
|
||||
| `get path.hash.mode` | Path hashing algorithm: `0`, `1`, or `2` |
|
||||
| `get loop.detect` | Loop detection level: `off`, `minimal`, `moderate`, or `strict` |
|
||||
| `get radio.rxgain` | RX gain boost: `0` or `1` |
|
||||
| `get radio.fem.rxgain` | External FEM/LNA active during RX: `0` or `1` (default `1`) |
|
||||
| `get rxduty` | RX duty cycle mode: `0` or `1` |
|
||||
| `get gps duty` | Now-effective GPS duty interval in seconds (`always on (0)` when continuous) |
|
||||
| `get gps diag` | What the last GPS module-configuration attempt did — which path ran, bytes sent, and tracked satellites per constellation. See **GPS configuration diagnostics** in the GPS section for the field reference |
|
||||
@@ -310,6 +311,7 @@ Changes are persisted immediately unless noted. Some require a reboot.
|
||||
| `set path.hash.mode <mode>` | 0, 1, or 2 | Path hashing algorithm |
|
||||
| `set loop.detect <mode>` | `off`, `minimal`, `moderate`, `strict` | Loop detection sensitivity |
|
||||
| `set radio.rxgain <0\|1\|on\|off>` | | RX gain boost, applied live. Replies `Error: unsupported` on radios without RX boost (SX127x); the pref is still saved. |
|
||||
| `set radio.fem.rxgain <0\|1\|on\|off>` | default **1** | External FEM/LNA gating in the RX direction, applied live. Supported only where the FEM's chip-enable is wired to the radio node as `antenna-enable-gpios` — today `heltec_t096`, `heltec_wireless_tracker_v2`, `heltec_wifi_lora32_v4` and `heltec_wifi_lora32_v43`. Every other board reports `Error: unsupported`, for one of three reasons: it has no external amplifier at all; its amplifier has no separate receive-side enable, only the DIO2/TXEN/RXEN transmit-receive switch (`station_g2`, `gat562_30s`, `ikoka_nano_30dbm`, `promicro_sx1262`); or its amplifier enable is a standalone always-on regulator outside the radio node (`rak3401_1watt` — moving that pin onto the lora node as `antenna-enable-gpios` is what would enable it). Non-SX126x radios (LR1110, LR2021, SX127x) never support it. Setting `0` drops the FEM's RX gain (~16 dB on the SKY66122) and its supply current (~6.5 mA) — sensitivity for battery life, not a tuning knob. Transmit always uses the FEM, and the driver's idle/sleep gating is unchanged. Replies `Error: unsupported` on non-SX126x radios and on boards with no FEM wired; the pref is still saved. |
|
||||
| `set rxduty <0\|1\|on\|off>` | | RX duty cycle mode *(reboot required)*. Window timing auto-sized per SF/BW/preamble from the SX126x datasheet constraints (boot log line `rxduty:` shows the result). Zero-loss guarantee assumes senders on preamble-32 firmware (current MeshCore at SF≤8); legacy preamble-16 senders are only caught ~50% worst-phase — keep off until the local mesh has converted. Presets with 16-symbol preambles (SF≥9) fall back to continuous RX automatically. |
|
||||
| `set adc.multiplier <mult>` | `0` (use board default) or 100–30000 | Battery voltage ADC calibration multiplier, set directly. Rejects non-numeric input, NaN/inf and negatives. |
|
||||
| `set adc.multiplier target <mv>` | 3000–4400 mV | Calibrate against a voltage you measured with a multimeter: rescales the current multiplier so the ADC reads `<mv>`. Replies with the old and new multiplier plus the before/after reading. `Error: no ADC reading on this board` if the board has no battery ADC. |
|
||||
|
||||
@@ -866,6 +866,14 @@ void ZephyrDataStore::loadPrefs(NodePrefs &prefs)
|
||||
prefs.v_contact_flags = buf[off++];
|
||||
}
|
||||
|
||||
/* Offset 167: fem_rxgain (ZephCore extension). Absent in pre-existing
|
||||
* files → keeps the caller's initNodePrefs() default of 1, which is the
|
||||
* behaviour every deployed node already has (the FEM's chip-enable has
|
||||
* always been asserted for RX). */
|
||||
if (off < len) {
|
||||
prefs.fem_rxgain = buf[off++];
|
||||
}
|
||||
|
||||
sanitizeNodePrefs(&prefs);
|
||||
}
|
||||
|
||||
@@ -966,7 +974,9 @@ void ZephyrDataStore::savePrefs(const NodePrefs &prefs)
|
||||
off += EXTRA_SF_MAX;
|
||||
/* Offset 166: v_contact_flags (ZephCore extension) */
|
||||
buf[off++] = prefs.v_contact_flags;
|
||||
/* Total: 167 bytes */
|
||||
/* Offset 167: fem_rxgain (ZephCore extension, external FEM LNA in RX) */
|
||||
buf[off++] = prefs.fem_rxgain;
|
||||
/* Total: 168 bytes */
|
||||
|
||||
bool ok = atomicReplaceFile(PREFS_FILE, buf, off);
|
||||
LOG_DBG("savePrefs: wrote %s, ok=%d (%d bytes), name='%.16s'",
|
||||
|
||||
@@ -85,6 +85,16 @@ public:
|
||||
virtual bool setRxBoost(bool enable);
|
||||
bool isRxBoostEnabled() const { return _rx_boost_enabled; }
|
||||
|
||||
/* External FEM/LNA gain during RX. Boards carrying a front-end module
|
||||
* wire its chip-enable to the radio's antenna-enable line, which the
|
||||
* driver asserts for RX and TX alike; disabling this withholds it in the
|
||||
* RX direction only, trading the FEM's RX gain for its supply current.
|
||||
* The transmit path and the driver's idle gating are unaffected.
|
||||
* Returns false on radios that do not implement the knob (everything but
|
||||
* the native SX126x today); on an SX126x board with no FEM wired it
|
||||
* succeeds and does nothing, since there is no pin to withhold. */
|
||||
virtual bool setFemRxEnable(bool enable) { (void)enable; return false; }
|
||||
|
||||
/* Multi-SF receive via LoRa side detectors. Only the LR2021 has them;
|
||||
* every other radio reports the feature as unsupported. `num` = 0
|
||||
* disables. Returns false if the radio has no side detectors or the
|
||||
|
||||
@@ -101,6 +101,20 @@ void SX126xRadio::hwSetRxBoost(bool enable)
|
||||
sx126x_set_rx_boost(_dev, enable);
|
||||
}
|
||||
|
||||
bool SX126xRadio::setFemRxEnable(bool enable)
|
||||
{
|
||||
/* Pure driver-side flag -- no SPI, no chip state. It takes effect at the
|
||||
* next RX/TX/sleep transition, and startReceive() goes through one, so
|
||||
* there is nothing to re-apply here. Returns false when this board wires
|
||||
* no antenna-enable line, so the CLI reports "unsupported" rather than
|
||||
* acknowledging a setting that cannot do anything. */
|
||||
if (!sx126x_set_fem_rx_enable(_dev, enable)) {
|
||||
return false;
|
||||
}
|
||||
LOG_INF("FEM RX gain %s", enable ? "enabled" : "disabled");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SX126xRadio::hwIsChipBusy()
|
||||
{
|
||||
return sx126x_is_chip_busy(_dev);
|
||||
|
||||
@@ -16,6 +16,11 @@ public:
|
||||
|
||||
void begin() override;
|
||||
|
||||
/* Gate the external FEM's LNA in the RX direction (radio.fem.rxgain).
|
||||
* Delegates to the driver, which owns the antenna-enable pin; returns
|
||||
* false on a board that has no such pin, i.e. no FEM to gate. */
|
||||
bool setFemRxEnable(bool enable) override;
|
||||
|
||||
/* Duty-cycle preamble false-positive stats (SX126x-specific) */
|
||||
uint32_t getDutyCycleTimeoutRestarts() const override;
|
||||
void resetDutyCycleTimeoutRestarts() override;
|
||||
|
||||
@@ -236,6 +236,10 @@ bool RepeaterDataStore::loadPrefs(NodePrefs& prefs) {
|
||||
/* LR2021 side-detector SFs, offsets 301-303. Absent in <304-byte files;
|
||||
* the no-op EOF read leaves the zeroed default = feature off. */
|
||||
fs_read(&file, prefs.extra_sf, sizeof(prefs.extra_sf));
|
||||
/* External FEM RX gain, offset 304. Absent in <305-byte files; the no-op
|
||||
* EOF read keeps the initNodePrefs() default fem_rxgain=1, which is what
|
||||
* every already-deployed node has been running. */
|
||||
fs_read(&file, &prefs.fem_rxgain, sizeof(prefs.fem_rxgain));
|
||||
|
||||
fs_close(&file);
|
||||
|
||||
@@ -379,6 +383,8 @@ bool RepeaterDataStore::savePrefs(const NodePrefs& prefs) {
|
||||
fs_write(&file, &prefs.cad_busycap, sizeof(prefs.cad_busycap));
|
||||
/* LR2021 side-detector SFs (offsets 301-303) */
|
||||
fs_write(&file, prefs.extra_sf, sizeof(prefs.extra_sf));
|
||||
/* External FEM RX gain (offset 304) */
|
||||
fs_write(&file, &prefs.fem_rxgain, sizeof(prefs.fem_rxgain));
|
||||
|
||||
ret = fs_sync(&file);
|
||||
fs_close(&file);
|
||||
|
||||
@@ -1253,6 +1253,10 @@ bool RepeaterMesh::setRxBoostedGain(bool enable) {
|
||||
return getRadioDriver(_radio).setRxBoost(enable);
|
||||
}
|
||||
|
||||
bool RepeaterMesh::setFemRxGain(bool enable) {
|
||||
return getRadioDriver(_radio).setFemRxEnable(enable);
|
||||
}
|
||||
|
||||
bool RepeaterMesh::configSideDetectors(const uint8_t* sfs, uint8_t num) {
|
||||
return getRadioDriver(_radio).configSideDetectors(sfs, num);
|
||||
}
|
||||
|
||||
@@ -258,6 +258,7 @@ public:
|
||||
void dumpLogFile() override;
|
||||
void setTxPower(int8_t power_dbm) override;
|
||||
bool setRxBoostedGain(bool enable) override;
|
||||
bool setFemRxGain(bool enable) override;
|
||||
bool configSideDetectors(const uint8_t* sfs, uint8_t num) override;
|
||||
void formatNeighborsReply(char* reply) override;
|
||||
void removeNeighbor(const uint8_t* pubkey, int key_len) override;
|
||||
|
||||
@@ -957,6 +957,10 @@ bool RoomServerMesh::setRxBoostedGain(bool enable) {
|
||||
return getRadioDriver(_radio).setRxBoost(enable);
|
||||
}
|
||||
|
||||
bool RoomServerMesh::setFemRxGain(bool enable) {
|
||||
return getRadioDriver(_radio).setFemRxEnable(enable);
|
||||
}
|
||||
|
||||
bool RoomServerMesh::configSideDetectors(const uint8_t* sfs, uint8_t num) {
|
||||
return getRadioDriver(_radio).configSideDetectors(sfs, num);
|
||||
}
|
||||
|
||||
@@ -212,6 +212,7 @@ public:
|
||||
void dumpLogFile() override;
|
||||
void setTxPower(int8_t power_dbm) override;
|
||||
bool setRxBoostedGain(bool enable) override;
|
||||
bool setFemRxGain(bool enable) override;
|
||||
bool configSideDetectors(const uint8_t* sfs, uint8_t num) override;
|
||||
void formatNeighborsReply(char* reply) override;
|
||||
void formatStatsReply(char* reply) override;
|
||||
|
||||
@@ -544,6 +544,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
|
||||
snprintf(reply, CLI_REPLY_SIZE, "> %.6f", _prefs->node_lat);
|
||||
} else if (memcmp(config, "lon", 3) == 0) {
|
||||
snprintf(reply, CLI_REPLY_SIZE, "> %.6f", _prefs->node_lon);
|
||||
} else if (memcmp(config, "radio.fem.rxgain", 16) == 0) {
|
||||
snprintf(reply, CLI_REPLY_SIZE, "> %d", (int)_prefs->fem_rxgain);
|
||||
} else if (memcmp(config, "radio.rxgain", 12) == 0) {
|
||||
snprintf(reply, CLI_REPLY_SIZE, "> %d", (int)_prefs->rx_boost);
|
||||
} else if (memcmp(config, "radio", 5) == 0) {
|
||||
@@ -1171,6 +1173,25 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
|
||||
} else {
|
||||
strcpy(reply, "Error: unsupported by this board");
|
||||
}
|
||||
} else if (memcmp(config, "radio.fem.rxgain ", 17) == 0) {
|
||||
const char* arg = &config[17];
|
||||
int val = -1;
|
||||
if (memcmp(arg, "on", 2) == 0) val = 1;
|
||||
else if (memcmp(arg, "off", 3) == 0) val = 0;
|
||||
else if (arg[0] == '0' || arg[0] == '1') val = atoi(arg);
|
||||
if (val == 0 || val == 1) {
|
||||
/* Same shape as radio.rxgain: always save, then apply live and
|
||||
* report when the radio driver has no FEM gate. */
|
||||
_prefs->fem_rxgain = (uint8_t)val;
|
||||
savePrefs();
|
||||
if (_callbacks->setFemRxGain(val == 1)) {
|
||||
snprintf(reply, CLI_REPLY_SIZE, "OK - radio.fem.rxgain=%d", _prefs->fem_rxgain);
|
||||
} else {
|
||||
strcpy(reply, "Error: unsupported");
|
||||
}
|
||||
} else {
|
||||
strcpy(reply, "Error: must be 0, 1, on, or off");
|
||||
}
|
||||
} else if (memcmp(config, "radio.rxgain ", 13) == 0) {
|
||||
const char* arg = &config[13];
|
||||
int val = -1;
|
||||
|
||||
@@ -46,6 +46,10 @@ public:
|
||||
/* Apply RX boosted gain live; returns false when the radio has no
|
||||
* RX boost feature (upstream PR #2844 semantics). */
|
||||
virtual bool setRxBoostedGain(bool enable) { (void)enable; return false; }
|
||||
/* Gate an external FEM/LNA in the RX direction, live. Same semantics:
|
||||
* false means the radio driver has no such knob. On a supported radio
|
||||
* whose board has no FEM wired it returns true and does nothing. */
|
||||
virtual bool setFemRxGain(bool enable) { (void)enable; return false; }
|
||||
/* Configure LR2021 side detectors (multi-SF receive); num = 0 disables.
|
||||
* Returns false when the radio has no side detectors, or when the set
|
||||
* violates a chip constraint — the driver is the validator. */
|
||||
|
||||
@@ -93,6 +93,7 @@ struct NodePrefs {
|
||||
float adc_multiplier;
|
||||
char owner_info[120];
|
||||
uint8_t rx_boost; // 1 = boosted RX gain (+3dB), 0 = power save
|
||||
uint8_t fem_rxgain; // 1 = external FEM LNA active during RX, 0 = off (power save)
|
||||
uint8_t rx_duty_cycle; // 1 = RX duty cycle, 0 = continuous RX
|
||||
/* RESERVED — formerly apc_enabled / apc_margin (Adaptive Power Control,
|
||||
* removed in 1.16.6). These two bytes are still read and written at their
|
||||
@@ -217,6 +218,7 @@ static inline void sanitizeNodePrefs(NodePrefs* p) {
|
||||
p->powersaving_enabled = saneBool<uint8_t>(p->powersaving_enabled, 0);
|
||||
/* Defaults that are on, not off. */
|
||||
p->rx_boost = saneBool<uint8_t>(p->rx_boost, 1);
|
||||
p->fem_rxgain = saneBool<uint8_t>(p->fem_rxgain, 1);
|
||||
p->wake_on_msg = saneBool<uint8_t>(p->wake_on_msg, 1);
|
||||
p->v_contact_enabled = saneBool<uint8_t>(p->v_contact_enabled, 1);
|
||||
/* Never let a corrupt byte take the radio off the air or hide BLE — both
|
||||
@@ -302,6 +304,10 @@ static inline void initNodePrefs(NodePrefs* prefs) {
|
||||
prefs->advert_loc_policy = ADVERT_LOC_NONE;
|
||||
prefs->adc_multiplier = 0.0f;
|
||||
prefs->rx_boost = 1; // Default to boosted RX for better sensitivity
|
||||
/* Default ON: the FEM's chip-enable has always been asserted for RX on the
|
||||
* boards that have one, so 1 is the historical behaviour and the only safe
|
||||
* default -- 0 costs the FEM's RX gain (~16 dB on SKY66122). */
|
||||
prefs->fem_rxgain = 1;
|
||||
prefs->rx_duty_cycle = 0; // Default OFF — continuous RX for best reliability
|
||||
prefs->_reserved_apc_enabled = 0; // reserved (was APC), see NodePrefs
|
||||
prefs->_reserved_apc_margin = 0; // reserved (was APC), see NodePrefs
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
* SX126x native driver — extension API
|
||||
*
|
||||
* Functions extending the standard Zephyr lora_driver_api with
|
||||
* SX126x-specific features (duty cycle, RX boost, RSSI readout,
|
||||
* preamble detection).
|
||||
* SX126x-specific features (duty cycle, RX boost, external FEM gating,
|
||||
* RSSI readout, preamble detection).
|
||||
*/
|
||||
|
||||
#ifndef SX126X_EXT_H
|
||||
@@ -50,6 +50,26 @@ bool sx126x_is_receiving(const struct device *dev);
|
||||
*/
|
||||
void sx126x_set_rx_boost(const struct device *dev, bool enable);
|
||||
|
||||
/**
|
||||
* @brief Enable/disable an external FEM's LNA in the RX direction
|
||||
*
|
||||
* Boards with an external front-end module wire its chip-enable to
|
||||
* antenna-enable-gpios (KCT8103L CSD, SKY66122 CSD+CPS). The driver normally
|
||||
* asserts that line for both RX and TX. Passing false withholds it for RX
|
||||
* only: the FEM's LNA and its supply current (~6.5 mA on SKY66122) drop out,
|
||||
* at the cost of the FEM's RX gain (~16 dB on the same part). TX always
|
||||
* asserts the line, and sleep/idle always clears it, so neither the transmit
|
||||
* path nor the driver's idle gating is affected.
|
||||
*
|
||||
* Default is enabled.
|
||||
*
|
||||
* @param dev LoRa device
|
||||
* @param enable true to keep the FEM active during RX
|
||||
* @return true if this board has an antenna-enable line to gate, false if it
|
||||
* has none (in which case the call did nothing)
|
||||
*/
|
||||
bool sx126x_set_fem_rx_enable(const struct device *dev, bool enable);
|
||||
|
||||
/**
|
||||
* @brief Check if the radio chip is busy (cannot accept SPI commands)
|
||||
*
|
||||
|
||||
@@ -38,10 +38,30 @@ top of upstream:
|
||||
C++ caller. sx126x_lora_recv_async() has an idempotent fast path, so the
|
||||
failure-path startReceive() on that side is a no-op.
|
||||
|
||||
* External FEM/LNA RX gating -- sx126x_set_fem_rx_enable() plus the
|
||||
fem_rx_off flag it sets. antenna-enable-gpios is the chip-enable line of
|
||||
the external FEM on the boards that route it there (KCT8103L CSD on
|
||||
heltec_t096, heltec_wireless_tracker_v2 and heltec_wifi_lora32_v43;
|
||||
GC1109 CSD on heltec_wifi_lora32_v4), and the driver asserts it for RX and
|
||||
TX alike. With the flag set, sx126x_set_rf_path() withholds it in the RX
|
||||
direction only, so the FEM's LNA and its supply current drop out while TX
|
||||
still gets the PA path and sleep/idle keeps clearing the line as before.
|
||||
The setter returns false where the board declares no antenna-enable line,
|
||||
so the CLI can report that rather than acknowledging a no-op -- that is
|
||||
the rak3401_1watt case, where the SKY66122 enable is a standalone
|
||||
regulator-boot-on rail outside the radio node.
|
||||
|
||||
The pin stays driver-owned throughout. Taking it over from devicetree
|
||||
instead would cost the idle gating, drop the ESP32 sleep-hold flags those
|
||||
nodes carry, and on the boards whose CTX net is a separate tx-enable-gpios
|
||||
it would desynchronise the external switch from DIO2. Backs
|
||||
`set radio.fem.rxgain` (default on = historical behaviour).
|
||||
|
||||
* Extension API for the C++ adapter (sx126x_ext.h): sx126x_is_chip_busy,
|
||||
sx126x_is_receiving, sx126x_set_rx_boost, sx126x_reset_agc,
|
||||
sx126x_get_rssi_inst, sx126x_get_wakeup_time_us, the dc-restart counters,
|
||||
the CAD tuning trio, and sx126x_apply_heltec_reg_patch.
|
||||
sx126x_is_receiving, sx126x_set_rx_boost, sx126x_set_fem_rx_enable,
|
||||
sx126x_reset_agc, sx126x_get_rssi_inst, sx126x_get_wakeup_time_us, the
|
||||
dc-restart counters, the CAD tuning trio, and
|
||||
sx126x_apply_heltec_reg_patch.
|
||||
|
||||
In sx126x_hal.c, SX1262 Tx params use a measured-optimal PaConfig table indexed
|
||||
by requested power (RadioLib PR #1662) instead of the datasheet's fixed
|
||||
@@ -1099,11 +1119,18 @@ index 30243ba5dc7..3a8bc017a52 100644
|
||||
}
|
||||
|
||||
static void sx126x_set_rf_path(const struct device *dev, bool enable, bool tx)
|
||||
@@ -406,7 +900,20 @@ static void sx126x_set_rf_path(const struct device *dev, bool enable, bool tx)
|
||||
@@ -406,7 +900,26 @@ static void sx126x_set_rf_path(const struct device *dev, bool enable, bool tx)
|
||||
const struct sx126x_hal_config *config = dev->config;
|
||||
+ struct sx126x_data *data = dev->data;
|
||||
|
||||
sx126x_hal_set_antenna_enable(dev, enable);
|
||||
- sx126x_hal_set_antenna_enable(dev, enable);
|
||||
- if (!config->dio2_tx_enable) {
|
||||
+ /* An external FEM's enable line is asserted for TX unconditionally. For
|
||||
+ * RX it follows fem_rx_off (the radio.fem.rxgain pref, applied through
|
||||
+ * sx126x_set_fem_rx_enable): clearing it drops the FEM's LNA and its
|
||||
+ * supply current, at the cost of that gain. `enable` still wins, so
|
||||
+ * sleep and idle keep gating the line exactly as they did before. */
|
||||
+ sx126x_hal_set_antenna_enable(dev, enable && (tx || !data->fem_rx_off));
|
||||
+ if (config->dio2_tx_enable) {
|
||||
+ /* DIO2 handles TX enable in hardware -- but rx-enable-gpios
|
||||
+ * (e.g. E22-900M30S RXEN) and some board-specific tx-enable
|
||||
@@ -1971,7 +1998,7 @@ index 30243ba5dc7..3a8bc017a52 100644
|
||||
if (ret < 0) {
|
||||
sx126x_set_sleep(dev);
|
||||
k_mutex_unlock(&data->lock);
|
||||
@@ -1083,14 +2213,812 @@ static int sx126x_lora_test_cw(const struct device *dev, uint32_t frequency,
|
||||
@@ -1083,14 +2213,828 @@ static int sx126x_lora_test_cw(const struct device *dev, uint32_t frequency,
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2126,6 +2153,22 @@ index 30243ba5dc7..3a8bc017a52 100644
|
||||
+ k_mutex_unlock(&data->lock);
|
||||
+}
|
||||
+
|
||||
+bool sx126x_set_fem_rx_enable(const struct device *dev, bool enable)
|
||||
+{
|
||||
+ struct sx126x_data *data = dev->data;
|
||||
+ const struct sx126x_hal_config *config = dev->config;
|
||||
+
|
||||
+ /* Stored inverted so the zero-initialised default is "FEM on in RX",
|
||||
+ * which is what the driver did before this knob existed. No SPI and no
|
||||
+ * chip state: sx126x_set_rf_path() reads it on the next RX/TX/sleep
|
||||
+ * transition, and every RX (re)start passes through there. */
|
||||
+ data->fem_rx_off = !enable;
|
||||
+
|
||||
+ /* False when the board wires no antenna-enable line: there is no FEM to
|
||||
+ * gate, so the caller can say so instead of acknowledging a no-op. */
|
||||
+ return config->antenna_enable.port != NULL;
|
||||
+}
|
||||
+
|
||||
+void sx126x_reset_agc(const struct device *dev)
|
||||
+{
|
||||
+ struct sx126x_data *data = dev->data;
|
||||
@@ -2837,7 +2880,7 @@ diff --git a/drivers/lora/native/sx126x/sx126x.h b/drivers/lora/native/sx126x/sx
|
||||
index 9dbf3f26586..b6df07ce7ab 100644
|
||||
--- a/drivers/lora/native/sx126x/sx126x.h
|
||||
+++ b/drivers/lora/native/sx126x/sx126x.h
|
||||
@@ -56,13 +56,96 @@ struct sx126x_data {
|
||||
@@ -56,13 +56,104 @@ struct sx126x_data {
|
||||
/* Async RX callback */
|
||||
lora_recv_cb rx_cb;
|
||||
void *rx_cb_user_data;
|
||||
@@ -2855,6 +2898,14 @@ index 9dbf3f26586..b6df07ce7ab 100644
|
||||
+ bool rx_duty_cycle_enabled;
|
||||
+ bool rx_boost_enabled;
|
||||
+
|
||||
+ /* External FEM/LNA gating for the RX direction, set by
|
||||
+ * sx126x_set_fem_rx_enable(). Negative logic so the zero-initialised
|
||||
+ * default preserves the historical behaviour -- antenna-enable-gpios
|
||||
+ * asserted for both RX and TX. When set, sx126x_set_rf_path() drops
|
||||
+ * the line for RX only: TX still asserts it and sleep/idle still
|
||||
+ * clears it, so the driver keeps sole ownership of the pin. */
|
||||
+ bool fem_rx_off;
|
||||
+
|
||||
+ /* AgcSensiAdjust value for the configured band (DS Table 6-4), cached
|
||||
+ * because it shares register 0x08AC with the RX gain bits and so has
|
||||
+ * to be re-applied by every RX-gain write. Defaults to the 868-915
|
||||
|
||||
@@ -748,6 +748,10 @@ public:
|
||||
return lora_radio.setRxBoost(enable);
|
||||
}
|
||||
|
||||
bool setFemRxGain(bool enable) override {
|
||||
return lora_radio.setFemRxEnable(enable);
|
||||
}
|
||||
|
||||
bool configSideDetectors(const uint8_t* sfs, uint8_t num) override {
|
||||
return lora_radio.configSideDetectors(sfs, num);
|
||||
}
|
||||
@@ -1589,6 +1593,7 @@ int main(void)
|
||||
|
||||
/* Apply RX boost and duty cycle from prefs */
|
||||
lora_radio.setRxBoost(companion_mesh.prefs.rx_boost != 0);
|
||||
lora_radio.setFemRxEnable(companion_mesh.prefs.fem_rxgain != 0);
|
||||
lora_radio.enableRxDutyCycle(companion_mesh.prefs.rx_duty_cycle != 0);
|
||||
lora_radio.setCadParams(companion_mesh.prefs.cad_auto != 0,
|
||||
companion_mesh.prefs.cad_offset,
|
||||
|
||||
@@ -745,6 +745,7 @@ int main(void)
|
||||
|
||||
/* Apply RX boost and duty cycle from prefs */
|
||||
lora_radio.setRxBoost(prefs->rx_boost != 0);
|
||||
lora_radio.setFemRxEnable(prefs->fem_rxgain != 0);
|
||||
lora_radio.enableRxDutyCycle(prefs->rx_duty_cycle != 0);
|
||||
lora_radio.setCadParams(prefs->cad_auto != 0, prefs->cad_offset,
|
||||
prefs->probe_interval, prefs->cad_busycap);
|
||||
|
||||
@@ -633,6 +633,7 @@ int main(void)
|
||||
|
||||
/* Apply RX boost and duty cycle from prefs */
|
||||
lora_radio.setRxBoost(prefs->rx_boost != 0);
|
||||
lora_radio.setFemRxEnable(prefs->fem_rxgain != 0);
|
||||
lora_radio.enableRxDutyCycle(prefs->rx_duty_cycle != 0);
|
||||
lora_radio.setCadParams(prefs->cad_auto != 0, prefs->cad_offset,
|
||||
prefs->probe_interval, prefs->cad_busycap);
|
||||
|
||||
Reference in New Issue
Block a user