mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-09-01 23:38:18 +00:00
reg patch for rak1w
This commit is contained in:
+4
-2
@@ -512,14 +512,16 @@ config ZEPHCORE_MAX_TX_POWER_DBM
|
||||
Example: Station G2 caps at 19 dBm to stay below PA P1dB (35 dBm).
|
||||
|
||||
config ZEPHCORE_SX126X_HELTEC_REG_PATCH
|
||||
bool "Apply undocumented SX126x register 0x8B5 RX improvement (Heltec V4)"
|
||||
bool "Apply undocumented SX126x register 0x8B5 RX improvement (external PA/FEM)"
|
||||
depends on ZEPHCORE_RADIO_NATIVE
|
||||
default n
|
||||
help
|
||||
Sets the LSB of undocumented register 0x8B5 after the first lora_config().
|
||||
Described by Heltec engineer @Quency-D in MeshCore PR#1398 — consistently
|
||||
improves RX reception on boards with GC1109 or KCT8103L PA (Heltec V4/V4.3).
|
||||
Enable in board.conf for Heltec V4 boards.
|
||||
Upstream MeshCore also enables it for the RAK3401's SKY66122 FEM, so it is
|
||||
not Heltec-specific despite the symbol name. Enable in board.conf for boards
|
||||
with an external PA/FEM in the RX path.
|
||||
|
||||
endmenu # Radio Configuration
|
||||
|
||||
|
||||
@@ -19,3 +19,10 @@ CONFIG_BT_DIS_MODEL_NUMBER_STR="RAK3401 1W"
|
||||
|
||||
# SoftDevice firmware ID (s140 v6.1.1)
|
||||
CONFIG_ZEPHCORE_SD_FWID=0x00B6
|
||||
|
||||
# Undocumented register 0x8B5 RX improvement.
|
||||
# Upstream MeshCore enables SX126X_REGISTER_PATCH for this variant with the
|
||||
# comment "Patch register 0x8B5 for improved RX with SKY66122 FEM" — the same
|
||||
# patch we already ship for the Heltec V4/V4.3 PA boards. Unquantified in both
|
||||
# upstreams; see memory/rak3401-sky66122-fem.md for how to A/B it on hardware.
|
||||
CONFIG_ZEPHCORE_SX126X_HELTEC_REG_PATCH=y
|
||||
|
||||
@@ -0,0 +1,286 @@
|
||||
Subject: [PATCH] lora: sx126x: band-aware RSSI/AGC calibration (DS Sec 6.1.6)
|
||||
|
||||
The SX126x RSSI and AGC gain-tune registers are band-specific. Semtech ships
|
||||
the chip calibrated for 868-915 MHz on their EVK and publishes a second value
|
||||
set for 470-490 MHz (DS rev 2.2 Table 6-4, section added Dec 2024). We wrote
|
||||
none of them, so sub-GHz builds ran the 868-915 calibration.
|
||||
|
||||
That is not cosmetic. Per DS Sec 6.1.6 an incorrect RSSI produces "an
|
||||
incorrect gain selection in LoRa and GFSK mode", which "can result in a missed
|
||||
detection (packet loss) or decreased resistance to interference".
|
||||
|
||||
Register 0x08AC is shared: bits 7:2 are AgcSensiAdjust, bits 1:0 select the RX
|
||||
gain mode. The driver's 0x94/0x96 constants therefore also wrote the 868-915
|
||||
AgcSensiAdjust (0x94 >> 2 == 0x25) at every RX-gain re-apply, which would have
|
||||
clobbered any calibration installed elsewhere. All four write sites now build
|
||||
the byte from the cached per-band value.
|
||||
|
||||
The low-band column is the datasheet's 470-490 MHz EVK calibration, applied
|
||||
below 600 MHz. It is NOT a 433 MHz calibration -- Semtech publishes no such
|
||||
column -- but it is the nearest published low-band data and beats leaving the
|
||||
868-915 tunes in place. A true 433 figure needs the DS Sec 6.1.6 generator
|
||||
sweep on real hardware.
|
||||
|
||||
Applied from sx126x_config() after SetRfFrequency so a runtime band change
|
||||
re-selects it, and re-applied after each Calibrate(ALL) alongside the existing
|
||||
CalibrateImage re-issue.
|
||||
|
||||
--- a/drivers/lora/native/sx126x/sx126x_regs.h
|
||||
+++ b/drivers/lora/native/sx126x/sx126x_regs.h
|
||||
@@ -175,10 +175,34 @@
|
||||
#define SX126X_LORA_SYNC_WORD_PUBLIC 0x3444
|
||||
#define SX126X_LORA_SYNC_WORD_PRIVATE 0x1424
|
||||
|
||||
-/* RX Gain */
|
||||
+/* RX Gain.
|
||||
+ *
|
||||
+ * 0x08AC is a shared register: bits 7:2 are AgcSensiAdjust (DS Table 6-4,
|
||||
+ * band-dependent RSSI/AGC calibration) and bits 1:0 select the RX gain mode.
|
||||
+ * The legacy 0x94/0x96 constants are the 868-915 MHz band values -- they
|
||||
+ * happen to encode AgcSensiAdjust 0x25 (0x94 >> 2 == 0x25). Writing them
|
||||
+ * verbatim in a 470-490 MHz build silently installs the wrong AGC
|
||||
+ * calibration, so build the byte with SX126X_RX_GAIN_VAL() instead.
|
||||
+ */
|
||||
#define SX126X_REG_RX_GAIN 0x08AC
|
||||
#define SX126X_RX_GAIN_POWER_SAVING 0x94
|
||||
#define SX126X_RX_GAIN_BOOSTED 0x96
|
||||
+#define SX126X_RX_GAIN_BOOST_BITS 0x02
|
||||
+#define SX126X_RX_GAIN_VAL(sensi, boosted) \
|
||||
+ (uint8_t)(((sensi) << 2) | ((boosted) ? SX126X_RX_GAIN_BOOST_BITS : 0U))
|
||||
+
|
||||
+/* RSSI / AGC calibration registers (DS Sec 6.1.6, Table 6-4).
|
||||
+ * The chip ships calibrated for 868-915 MHz on the Semtech EVK; the
|
||||
+ * datasheet publishes a second column for 470-490 MHz. */
|
||||
+#define SX126X_REG_AGC_RSSI_MEAS_CAL_H 0x089C /* bits 4:0 */
|
||||
+#define SX126X_REG_AGC_RSSI_MEAS_CAL_L 0x089D
|
||||
+#define SX126X_REG_AGC_GFORST_POWTHR 0x08B9
|
||||
+#define SX126X_REG_AGC_GAIN_TUNE_1_2 0x08F5 /* .. 0x08FB, 7 bytes */
|
||||
+#define SX126X_AGC_GAIN_TUNE_LEN 7
|
||||
+
|
||||
+/* AgcSensiAdjust (0x08AC bits 7:2) per band */
|
||||
+#define SX126X_AGC_SENSI_ADJUST_HIGH_BAND 0x25 /* 868-915 MHz (default) */
|
||||
+#define SX126X_AGC_SENSI_ADJUST_LOW_BAND 0x22 /* 470-490 MHz */
|
||||
|
||||
/* RX Gain Retention (DS Sec 9.6) -- tells chip to preserve 0x08AC across
|
||||
* mode transitions (sleep/standby/TX -> RX). Without retention, the
|
||||
--- a/drivers/lora/native/sx126x/sx126x.h
|
||||
+++ b/drivers/lora/native/sx126x/sx126x.h
|
||||
@@ -70,6 +70,13 @@
|
||||
bool rx_duty_cycle_enabled;
|
||||
bool rx_boost_enabled;
|
||||
|
||||
+ /* 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
|
||||
+ * value the chip ships with; sx126x_config() re-selects it per
|
||||
+ * frequency. */
|
||||
+ uint8_t agc_sensi_adjust;
|
||||
+
|
||||
/* RX-busy latch: set by the work handler when HEADER_VALID fires for
|
||||
* a real packet, cleared on RX_DONE / CRC_ERR / RX_TX_TIMEOUT / RX
|
||||
* (re)start / TX-state entry. Read by sx126x_is_receiving() as the
|
||||
--- a/drivers/lora/native/sx126x/sx126x.c
|
||||
+++ b/drivers/lora/native/sx126x/sx126x.c
|
||||
@@ -307,9 +307,117 @@
|
||||
return sx126x_hal_write_regs(dev, SX126X_REG_LORA_SYNC_WORD_MSB, buf, 2);
|
||||
}
|
||||
|
||||
+/* RSSI / AGC band calibration (DS Sec 6.1.6, Table 6-4).
|
||||
+ *
|
||||
+ * Semtech: "The power seen by the SX1261/2 analog front-end is affected by
|
||||
+ * external components such as the matching network and RF switches. An
|
||||
+ * incorrect RSSI results in a sensitivity degradation in (G)FSK mode and an
|
||||
+ * incorrect gain selection in LoRa and GFSK mode. An incorrect gain can
|
||||
+ * result in a missed detection (packet loss) or decreased resistance to
|
||||
+ * interference." So this is an RX-performance register set, not telemetry
|
||||
+ * cosmetics.
|
||||
+ *
|
||||
+ * The chip ships calibrated for 868-915 MHz on the Semtech EVK, which is why
|
||||
+ * the high-band tunes are all zero -- writing them is a no-op against a fresh
|
||||
+ * chip and exists only so that a runtime band change (`set freq`) restores
|
||||
+ * them after a low-band build has installed the other set.
|
||||
+ *
|
||||
+ * IMPORTANT: the low-band column is the datasheet's 470-490 MHz EVK
|
||||
+ * calibration. It is NOT a 433 MHz calibration -- Semtech publishes no such
|
||||
+ * column. Applying it below 600 MHz is "the nearest published values for a
|
||||
+ * low band" and is expected to beat leaving the 868-915 calibration in place,
|
||||
+ * but a true 433 MHz figure needs the DS Sec 6.1.6 generator sweep on real
|
||||
+ * hardware. Do not present these numbers as calibrated for 433.
|
||||
+ */
|
||||
+struct sx126x_agc_band_cal {
|
||||
+ uint8_t rssi_meas_cal_h; /* 0x089C, bits 4:0 */
|
||||
+ uint8_t rssi_meas_cal_l; /* 0x089D */
|
||||
+ uint8_t gforst_powthr; /* 0x08B9 */
|
||||
+ uint8_t sensi_adjust; /* 0x08AC, bits 7:2 */
|
||||
+ uint8_t gain_tune[SX126X_AGC_GAIN_TUNE_LEN]; /* 0x08F5 .. 0x08FB */
|
||||
+};
|
||||
+
|
||||
+static const struct sx126x_agc_band_cal sx126x_agc_cal_high = {
|
||||
+ .rssi_meas_cal_h = 0x01,
|
||||
+ .rssi_meas_cal_l = 0x53,
|
||||
+ .gforst_powthr = 0x0A,
|
||||
+ .sensi_adjust = SX126X_AGC_SENSI_ADJUST_HIGH_BAND,
|
||||
+ .gain_tune = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
+};
|
||||
+
|
||||
+static const struct sx126x_agc_band_cal sx126x_agc_cal_low = {
|
||||
+ .rssi_meas_cal_h = 0x01,
|
||||
+ .rssi_meas_cal_l = 0x27,
|
||||
+ .gforst_powthr = 0x04,
|
||||
+ .sensi_adjust = SX126X_AGC_SENSI_ADJUST_LOW_BAND,
|
||||
+ /* GainTune_1_2, _3_4, _5_6, _7_8, _9_10, _11_12, _13 */
|
||||
+ .gain_tune = { 0xDE, 0xE2, 0x32, 0x44, 0x33, 0x34, 0x04 },
|
||||
+};
|
||||
+
|
||||
+/* Band split. The datasheet only characterises 470-490 and 868-915, so any
|
||||
+ * threshold between them is a judgement call; 600 MHz sits clear of both the
|
||||
+ * 433/470 group and the 779/868/915 group that sx126x_calibrate_image()
|
||||
+ * already distinguishes. */
|
||||
+#define SX126X_AGC_LOW_BAND_MAX_HZ 600000000
|
||||
+
|
||||
+static const struct sx126x_agc_band_cal *sx126x_agc_cal_for(uint32_t freq)
|
||||
+{
|
||||
+ return (freq < SX126X_AGC_LOW_BAND_MAX_HZ) ? &sx126x_agc_cal_low
|
||||
+ : &sx126x_agc_cal_high;
|
||||
+}
|
||||
+
|
||||
+/* Apply the band's RSSI/AGC calibration. Caches AgcSensiAdjust for the RX
|
||||
+ * gain writes, which share register 0x08AC, but does NOT touch 0x08AC itself
|
||||
+ * -- sx126x_set_rx_gain() owns that register and runs later in
|
||||
+ * sx126x_config(). */
|
||||
+static int sx126x_apply_agc_band_cal(const struct device *dev, uint32_t freq)
|
||||
+{
|
||||
+ struct sx126x_data *data = dev->data;
|
||||
+ const struct sx126x_agc_band_cal *cal = sx126x_agc_cal_for(freq);
|
||||
+ uint8_t val;
|
||||
+ int ret;
|
||||
+
|
||||
+ data->agc_sensi_adjust = cal->sensi_adjust;
|
||||
+
|
||||
+ /* Bits 4:0 only -- preserve the rest of 0x089C. */
|
||||
+ ret = sx126x_hal_read_regs(dev, SX126X_REG_AGC_RSSI_MEAS_CAL_H, &val, 1);
|
||||
+ if (ret < 0) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+ val = (val & ~0x1FU) | (cal->rssi_meas_cal_h & 0x1FU);
|
||||
+ ret = sx126x_hal_write_regs(dev, SX126X_REG_AGC_RSSI_MEAS_CAL_H, &val, 1);
|
||||
+ if (ret < 0) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ val = cal->rssi_meas_cal_l;
|
||||
+ ret = sx126x_hal_write_regs(dev, SX126X_REG_AGC_RSSI_MEAS_CAL_L, &val, 1);
|
||||
+ if (ret < 0) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ val = cal->gforst_powthr;
|
||||
+ ret = sx126x_hal_write_regs(dev, SX126X_REG_AGC_GFORST_POWTHR, &val, 1);
|
||||
+ if (ret < 0) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ return sx126x_hal_write_regs(dev, SX126X_REG_AGC_GAIN_TUNE_1_2,
|
||||
+ cal->gain_tune,
|
||||
+ SX126X_AGC_GAIN_TUNE_LEN);
|
||||
+}
|
||||
+
|
||||
+/* Byte to write to 0x08AC: band AgcSensiAdjust in bits 7:2, gain mode in 1:0. */
|
||||
+static uint8_t sx126x_rx_gain_byte(const struct device *dev, bool boosted)
|
||||
+{
|
||||
+ struct sx126x_data *data = dev->data;
|
||||
+
|
||||
+ return SX126X_RX_GAIN_VAL(data->agc_sensi_adjust, boosted);
|
||||
+}
|
||||
+
|
||||
static int sx126x_set_rx_gain(const struct device *dev, bool boosted)
|
||||
{
|
||||
- uint8_t val = boosted ? SX126X_RX_GAIN_BOOSTED : SX126X_RX_GAIN_POWER_SAVING;
|
||||
+ uint8_t val = sx126x_rx_gain_byte(dev, boosted);
|
||||
|
||||
/* Host-driven RX paths do not rely on retention: the chip resets
|
||||
* 0x08AC to power-saving (0x94) on every SetRx command, and
|
||||
@@ -661,6 +769,11 @@
|
||||
* EU868 / 433 / 779 MHz operation. */
|
||||
if (data->config_valid) {
|
||||
sx126x_calibrate_image(dev, data->config.frequency);
|
||||
+ /* Same reasoning for the AGC band calibration (DS
|
||||
+ * Sec 6.1.6): cheap insurance against Calibrate(ALL)
|
||||
+ * disturbing the RSSI / gain-tune registers. 0x08AC
|
||||
+ * itself is restored by the RX-gain write below. */
|
||||
+ sx126x_apply_agc_band_cal(dev, data->config.frequency);
|
||||
}
|
||||
|
||||
/* Re-apply DIO2 as RF switch -- Calibrate resets it */
|
||||
@@ -715,8 +828,8 @@
|
||||
* after SetRx) before writing. The gain is therefore applied after the radio
|
||||
* has entered RX but before any preamble can arrive -- giving a clean AGC
|
||||
* state for each new packet regardless of the previous packet's signal level. */
|
||||
- uint8_t gain = data->rx_boost_enabled ? SX126X_RX_GAIN_BOOSTED
|
||||
- : SX126X_RX_GAIN_POWER_SAVING;
|
||||
+ uint8_t gain = sx126x_rx_gain_byte(dev, data->rx_boost_enabled);
|
||||
+
|
||||
sx126x_hal_write_regs(dev, SX126X_REG_RX_GAIN, &gain, 1);
|
||||
return 0;
|
||||
}
|
||||
@@ -1156,6 +1269,15 @@
|
||||
goto out;
|
||||
}
|
||||
|
||||
+ /* Install the band's RSSI/AGC calibration (DS Sec 6.1.6) before the
|
||||
+ * RX gain write below, which consumes the AgcSensiAdjust this caches.
|
||||
+ * Re-run on every config so a runtime frequency change switches bands
|
||||
+ * instead of leaving the previous band's tunes installed. */
|
||||
+ ret = sx126x_apply_agc_band_cal(dev, config->frequency);
|
||||
+ if (ret < 0) {
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
/* Configure PA and TX power based on chip variant and frequency */
|
||||
ret = sx126x_hal_configure_tx_params(dev, config->tx_power,
|
||||
config->frequency,
|
||||
@@ -1607,8 +1729,8 @@
|
||||
* SetRx without retention, and lora_config sets gain before going to
|
||||
* sleep, so without this the first packet would be received in
|
||||
* power-saving gain regardless of DTS rx-boosted. */
|
||||
- uint8_t gain = data->rx_boost_enabled ? SX126X_RX_GAIN_BOOSTED
|
||||
- : SX126X_RX_GAIN_POWER_SAVING;
|
||||
+ uint8_t gain = sx126x_rx_gain_byte(dev, data->rx_boost_enabled);
|
||||
+
|
||||
sx126x_hal_write_regs(dev, SX126X_REG_RX_GAIN, &gain, 1);
|
||||
|
||||
k_mutex_unlock(&data->lock);
|
||||
@@ -2281,6 +2403,11 @@
|
||||
* (902-928 MHz band) which kills RX sensitivity on
|
||||
* EU868 / 433 / 779 MHz operation. */
|
||||
sx126x_calibrate_image(dev, data->config.frequency);
|
||||
+ /* Same reasoning for the AGC band calibration (DS Sec 6.1.6):
|
||||
+ * cheap insurance against Calibrate(ALL) disturbing the RSSI /
|
||||
+ * gain-tune registers. 0x08AC itself is restored by the
|
||||
+ * RX-gain write on the restart path. */
|
||||
+ sx126x_apply_agc_band_cal(dev, data->config.frequency);
|
||||
|
||||
/* Re-apply DIO2 as RF switch -- Calibrate resets it. */
|
||||
if (hal_cfg->dio2_tx_enable) {
|
||||
@@ -2352,8 +2479,8 @@
|
||||
* chip-internal sleep->RX wakes restore it (DS sec 9.6: mandatory for
|
||||
* SetRxDutyCycle -- without this every window after the first sleep
|
||||
* listens at power-saving gain, a silent -3 dB sensitivity loss). */
|
||||
- uint8_t gain = data->rx_boost_enabled ? SX126X_RX_GAIN_BOOSTED
|
||||
- : SX126X_RX_GAIN_POWER_SAVING;
|
||||
+ uint8_t gain = sx126x_rx_gain_byte(dev, data->rx_boost_enabled);
|
||||
+
|
||||
sx126x_hal_write_regs(dev, SX126X_REG_RX_GAIN, &gain, 1);
|
||||
sx126x_retain_rx_gain(dev);
|
||||
|
||||
@@ -2427,6 +2554,10 @@
|
||||
* pick up the boost setting even when the user never calls the
|
||||
* sx126x_set_rx_boost() extension API. */
|
||||
data->rx_boost_enabled = config->rx_boosted;
|
||||
+ /* Match the calibration the chip ships with, so any 0x08AC write that
|
||||
+ * happens before the first sx126x_config() keeps the factory value
|
||||
+ * rather than zeroing AgcSensiAdjust. */
|
||||
+ data->agc_sensi_adjust = SX126X_AGC_SENSI_ADJUST_HIGH_BAND;
|
||||
atomic_set(&data->dc_timeout_restarts, 0);
|
||||
k_work_init_delayable(&data->dc_watchdog_work,
|
||||
sx126x_dc_watchdog_handler);
|
||||
Reference in New Issue
Block a user