sx1262 fixes

- calibrate_image: revert to datasheet band table.  Narrow ±2 MHz
  window had truncation bug placing 433/869 MHz operating freq
  outside their own calibration windows.  DS §9.2.1 confirms cal
  is range-validity, not point-precision
- reset_agc: K_FOREVER mutex → K_MSEC(50).  Was holding dispatcher
  thread up to ~3 s if a long TX/RX held the SPI lock.  Now bails
  with WARN log; caller retries next maintenance interval.
This commit is contained in:
liquidraver
2026-05-11 11:26:33 +02:00
parent 2ee0fffa69
commit e78e197dee
2 changed files with 48 additions and 61 deletions
+1
View File
@@ -94,3 +94,4 @@ THINKNODE_M1_HANDOVER.md
CLAUDE.md
zephcore/apc_checklist.md
zephcore/apc.md
RADIO_AUDIT_INDEX.md
@@ -68,7 +68,7 @@ index 17689720dd2..09982dc2e70 100644
return -EINVAL;
}
diff --git a/drivers/lora/native/sx126x/sx126x.c b/drivers/lora/native/sx126x/sx126x.c
index 30243ba5dc7..befb84797eb 100644
index 30243ba5dc7..86c10f39fc3 100644
--- a/drivers/lora/native/sx126x/sx126x.c
+++ b/drivers/lora/native/sx126x/sx126x.c
@@ -10,10 +10,16 @@
@@ -88,34 +88,7 @@ index 30243ba5dc7..befb84797eb 100644
#define SX126X_REST_STATE \
(IS_ENABLED(CONFIG_LORA_SX126X_NATIVE_SLEEP) \
? SX126X_STATE_SLEEP : SX126X_STATE_IDLE)
@@ -177,22 +183,10 @@ static int sx126x_calibrate_image(const struct device *dev, uint32_t freq)
{
uint8_t buf[2];
- if (freq > 900000000) {
- buf[0] = 0xE1;
- buf[1] = 0xE9;
- } else if (freq > 850000000) {
- buf[0] = 0xD7;
- buf[1] = 0xDB;
- } else if (freq > 770000000) {
- buf[0] = 0xC1;
- buf[1] = 0xC5;
- } else if (freq > 460000000) {
- buf[0] = 0x75;
- buf[1] = 0x81;
- } else {
- buf[0] = 0x6B;
- buf[1] = 0x6F;
- }
+ /* Calibrate ±2 MHz around the actual frequency.
+ * CalibrateImage bytes are in 4 MHz steps (freq_Hz / 4000000). */
+ buf[0] = (uint8_t)((freq - 2000000) / 4000000);
+ buf[1] = (uint8_t)((freq + 2000000) / 4000000);
return sx126x_hal_write_cmd(dev, SX126X_CMD_CALIBRATE_IMAGE, buf, 2);
}
@@ -222,6 +216,7 @@ static int sx126x_set_packet_params(const struct device *dev,
@@ -222,6 +228,7 @@ static int sx126x_set_packet_params(const struct device *dev,
uint8_t invert_iq)
{
uint8_t buf[6];
@@ -123,7 +96,7 @@ index 30243ba5dc7..befb84797eb 100644
sys_put_be16(preamble_len, &buf[0]);
buf[2] = header_type;
@@ -229,7 +224,31 @@ static int sx126x_set_packet_params(const struct device *dev,
@@ -229,7 +236,31 @@ static int sx126x_set_packet_params(const struct device *dev,
buf[4] = crc_mode;
buf[5] = invert_iq;
@@ -156,7 +129,7 @@ index 30243ba5dc7..befb84797eb 100644
}
static int sx126x_set_sync_word(const struct device *dev, bool public_network)
@@ -247,6 +266,13 @@ static int sx126x_set_rx_gain(const struct device *dev, bool boosted)
@@ -247,6 +278,13 @@ 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;
@@ -170,7 +143,7 @@ index 30243ba5dc7..befb84797eb 100644
return sx126x_hal_write_regs(dev, SX126X_REG_RX_GAIN, &val, 1);
}
@@ -296,7 +322,7 @@ static int sx126x_get_packet_status(const struct device *dev,
@@ -296,7 +334,7 @@ static int sx126x_get_packet_status(const struct device *dev,
uint8_t buf[3];
int ret;
@@ -179,15 +152,20 @@ index 30243ba5dc7..befb84797eb 100644
if (ret == 0) {
/* RSSI is -value/2 dBm */
*rssi = -((int16_t)buf[0] >> 1);
@@ -367,6 +393,21 @@ static int sx126x_chip_init(const struct device *dev)
@@ -367,6 +405,26 @@ static int sx126x_chip_init(const struct device *dev)
return ret;
}
+ /* After TX/RX, fall back to STDBY_XOSC instead of STDBY_RC.
+ * Keeps the crystal warm so the next SetRx skips the ~500 us
+ * XOSC startup, reducing the deaf window between packets. */
+ /* After TX/RX, fall back to FS mode instead of the default STDBY_RC.
+ * FS keeps both XOSC and PLL active, so the next SetRx skips both the
+ * ~500 us XOSC startup AND the ~50-100 us PLL settle — minimising the
+ * deaf window between packets. Costs ~5-10 mA while in this state, but
+ * we are typically only here for the brief ISR-to-handler window before
+ * sleep or the next operation. Critical for duty-cycle restart where
+ * every microsecond of deafness can mean a missed preamble.
+ * (datasheet Table 13-23: 0x40=FS, 0x30=STDBY_XOSC, 0x20=STDBY_RC) */
+ {
+ uint8_t fallback = 0x40; /* STDBY_XOSC */
+ uint8_t fallback = 0x40; /* FS mode */
+
+ ret = sx126x_hal_write_cmd(dev,
+ SX126X_CMD_SET_RX_TX_FALLBACK_MODE,
@@ -201,8 +179,7 @@ index 30243ba5dc7..befb84797eb 100644
/* Set packet type to LoRa */
ret = sx126x_set_packet_type(dev, SX126X_PACKET_TYPE_LORA);
if (ret < 0) {
@@ -374,10 +415,16 @@ static int sx126x_chip_init(const struct device *dev)
LOG_ERR("Set packet type failed: %d", ret);
@@ -374,10 +432,16 @@ static int sx126x_chip_init(const struct device *dev)
return ret;
}
@@ -221,7 +198,8 @@ index 30243ba5dc7..befb84797eb 100644
+ ret = sx126x_set_dio_irq_params(dev, irq_mask, dio1_mask, 0, 0);
if (ret < 0) {
LOG_ERR("Set IRQ params failed: %d", ret);
@@ -398,7 +445,7 @@ static void sx126x_dio1_callback(const struct device *dev)
return ret;
@@ -398,7 +462,7 @@ static void sx126x_dio1_callback(const struct device *dev)
{
struct sx126x_data *data = dev->data;
@@ -230,7 +208,7 @@ index 30243ba5dc7..befb84797eb 100644
}
static void sx126x_set_rf_path(const struct device *dev, bool enable, bool tx)
@@ -406,7 +453,20 @@ static void sx126x_set_rf_path(const struct device *dev, bool enable, bool tx)
@@ -406,7 +470,20 @@ static void sx126x_set_rf_path(const struct device *dev, bool enable, bool tx)
const struct sx126x_hal_config *config = dev->config;
sx126x_hal_set_antenna_enable(dev, enable);
@@ -252,7 +230,7 @@ index 30243ba5dc7..befb84797eb 100644
sx126x_hal_set_rf_switch(dev, enable, tx);
}
}
@@ -455,6 +515,80 @@ static int sx126x_reconnect_rf_gpios(const struct device *dev)
@@ -455,6 +532,80 @@ static int sx126x_reconnect_rf_gpios(const struct device *dev)
}
#endif /* CONFIG_PM_DEVICE */
@@ -333,7 +311,7 @@ index 30243ba5dc7..befb84797eb 100644
static int sx126x_set_sleep(const struct device *dev)
{
struct sx126x_data *data = dev->data;
@@ -533,6 +667,9 @@ static void sx126x_handle_irq_rx_done(const struct device *dev, uint16_t irq_sta
@@ -533,6 +684,9 @@ static void sx126x_handle_irq_rx_done(const struct device *dev, uint16_t irq_sta
struct sx126x_data *data = dev->data;
struct sx126x_rx_result result = { 0 };
uint8_t payload_len = 0, offset = 0;
@@ -343,7 +321,7 @@ index 30243ba5dc7..befb84797eb 100644
int ret;
/* Get received packet info */
@@ -564,20 +701,34 @@ static void sx126x_handle_irq_rx_done(const struct device *dev, uint16_t irq_sta
@@ -564,20 +718,34 @@ static void sx126x_handle_irq_rx_done(const struct device *dev, uint16_t irq_sta
}
}
@@ -390,7 +368,7 @@ index 30243ba5dc7..befb84797eb 100644
}
} else {
/* Sync mode */
@@ -589,8 +740,46 @@ static void sx126x_handle_irq_rx_done(const struct device *dev, uint16_t irq_sta
@@ -589,8 +757,46 @@ static void sx126x_handle_irq_rx_done(const struct device *dev, uint16_t irq_sta
static void sx126x_handle_irq_timeout(const struct device *dev)
{
struct sx126x_data *data = dev->data;
@@ -437,7 +415,7 @@ index 30243ba5dc7..befb84797eb 100644
sx126x_set_sleep(dev);
if (data->tx_async_signal != NULL) {
@@ -637,6 +826,25 @@ static void sx126x_irq_work_handler(struct k_work *work)
@@ -637,6 +843,25 @@ static void sx126x_irq_work_handler(struct k_work *work)
sx126x_handle_irq_timeout(dev);
}
@@ -463,7 +441,7 @@ index 30243ba5dc7..befb84797eb 100644
/* Re-enable the DIO1 interrupt for the next event (unless sleeping) */
if (atomic_get(&data->state) != SX126X_REST_STATE) {
sx126x_hal_dio1_irq_enable(dev);
@@ -682,7 +890,7 @@ static int sx126x_lora_config(const struct device *dev,
@@ -682,7 +907,7 @@ static int sx126x_lora_config(const struct device *dev,
/* Configure PA and TX power based on chip variant and frequency */
ret = sx126x_hal_configure_tx_params(dev, config->tx_power,
config->frequency,
@@ -472,7 +450,7 @@ index 30243ba5dc7..befb84797eb 100644
if (ret < 0) {
goto out;
}
@@ -698,6 +906,29 @@ static int sx126x_lora_config(const struct device *dev,
@@ -698,6 +923,29 @@ static int sx126x_lora_config(const struct device *dev,
goto out;
}
@@ -502,7 +480,7 @@ index 30243ba5dc7..befb84797eb 100644
/* Set sync word */
ret = sx126x_set_sync_word(dev, config->public_network);
if (ret < 0) {
@@ -721,6 +952,8 @@ out:
@@ -721,6 +969,8 @@ out:
return ret;
}
@@ -511,7 +489,7 @@ index 30243ba5dc7..befb84797eb 100644
static int sx126x_lora_send_async(const struct device *dev,
uint8_t *data_buf, uint32_t data_len,
struct k_poll_signal *async)
@@ -752,6 +985,65 @@ static int sx126x_lora_send_async(const struct device *dev,
@@ -752,6 +1002,65 @@ static int sx126x_lora_send_async(const struct device *dev,
return ret;
}
@@ -577,7 +555,7 @@ index 30243ba5dc7..befb84797eb 100644
data->tx_async_signal = async;
k_msgq_purge(&data->tx_msgq);
@@ -777,6 +1069,29 @@ static int sx126x_lora_send_async(const struct device *dev,
@@ -777,6 +1086,29 @@ static int sx126x_lora_send_async(const struct device *dev,
/* Enable antenna and set TX path */
sx126x_set_rf_path(dev, true, true);
@@ -607,7 +585,7 @@ index 30243ba5dc7..befb84797eb 100644
/* Start transmission with 10 second timeout */
ret = sx126x_set_tx(dev, 10000);
if (ret < 0) {
@@ -847,6 +1162,8 @@ static int sx126x_lora_recv(const struct device *dev, uint8_t *data_buf,
@@ -847,6 +1179,8 @@ static int sx126x_lora_recv(const struct device *dev, uint8_t *data_buf,
}
data->rx_cb = NULL;
@@ -616,7 +594,7 @@ index 30243ba5dc7..befb84797eb 100644
k_msgq_purge(&data->rx_msgq);
/* Set packet parameters for variable length reception */
@@ -918,6 +1235,7 @@ static int sx126x_lora_recv_async(const struct device *dev,
@@ -918,6 +1252,7 @@ static int sx126x_lora_recv_async(const struct device *dev,
/* Stop async reception */
data->rx_cb = NULL;
data->rx_cb_user_data = NULL;
@@ -624,7 +602,7 @@ index 30243ba5dc7..befb84797eb 100644
if (atomic_cas(&data->state, SX126X_STATE_RX, SX126X_STATE_IDLE)) {
sx126x_set_standby(dev, SX126X_STANDBY_RC);
sx126x_set_sleep(dev);
@@ -947,6 +1265,8 @@ static int sx126x_lora_recv_async(const struct device *dev,
@@ -947,6 +1282,8 @@ static int sx126x_lora_recv_async(const struct device *dev,
data->rx_cb = cb;
data->rx_cb_user_data = user_data;
@@ -633,7 +611,7 @@ index 30243ba5dc7..befb84797eb 100644
/* Set packet parameters */
ret = sx126x_set_packet_params(dev,
@@ -959,6 +1279,7 @@ static int sx126x_lora_recv_async(const struct device *dev,
@@ -959,6 +1296,7 @@ static int sx126x_lora_recv_async(const struct device *dev,
SX126X_LORA_IQ_INVERTED : SX126X_LORA_IQ_STANDARD);
if (ret < 0) {
data->rx_cb = NULL;
@@ -641,7 +619,7 @@ index 30243ba5dc7..befb84797eb 100644
sx126x_set_sleep(dev);
k_mutex_unlock(&data->lock);
return ret;
@@ -971,11 +1292,21 @@ static int sx126x_lora_recv_async(const struct device *dev,
@@ -971,11 +1309,21 @@ static int sx126x_lora_recv_async(const struct device *dev,
ret = sx126x_set_rx(dev, 0);
if (ret < 0) {
data->rx_cb = NULL;
@@ -663,7 +641,7 @@ index 30243ba5dc7..befb84797eb 100644
k_mutex_unlock(&data->lock);
return 0;
}
@@ -1051,7 +1382,7 @@ static int sx126x_lora_test_cw(const struct device *dev, uint32_t frequency,
@@ -1051,7 +1399,7 @@ static int sx126x_lora_test_cw(const struct device *dev, uint32_t frequency,
/* Set PA config and TX power */
ret = sx126x_hal_configure_tx_params(dev, tx_power, frequency,
@@ -672,7 +650,7 @@ index 30243ba5dc7..befb84797eb 100644
if (ret < 0) {
sx126x_set_sleep(dev);
k_mutex_unlock(&data->lock);
@@ -1083,14 +1414,505 @@ static int sx126x_lora_test_cw(const struct device *dev, uint32_t frequency,
@@ -1083,14 +1431,513 @@ static int sx126x_lora_test_cw(const struct device *dev, uint32_t frequency,
return 0;
}
@@ -753,7 +731,15 @@ index 30243ba5dc7..befb84797eb 100644
+ struct sx126x_data *data = dev->data;
+ const struct sx126x_hal_config *config = dev->config;
+
+ k_mutex_lock(&data->lock, K_FOREVER);
+ /* Bounded wait: a long TX/RX (e.g. SF12 packet ~3 s airtime) holds
+ * data->lock for its full duration. K_FOREVER here would stall the
+ * caller (typically the dispatcher main thread) for that whole time
+ * and freeze packet routing. Instead, give up after 50 ms and let
+ * the caller retry on the next maintenance interval. */
+ if (k_mutex_lock(&data->lock, K_MSEC(50)) != 0) {
+ LOG_WRN("reset_agc: mutex busy, skipping this cycle");
+ return;
+ }
+
+ /* Warm sleep — powers down the analog frontend (resets AGC state)
+ * but preserves register configuration. */
@@ -1185,7 +1171,7 @@ index 30243ba5dc7..befb84797eb 100644
};
#ifdef CONFIG_PM_DEVICE
@@ -1112,6 +1934,7 @@ static int sx126x_pm_action(const struct device *dev,
@@ -1112,6 +1959,7 @@ static int sx126x_pm_action(const struct device *dev,
static int sx126x_init(const struct device *dev)
{
struct sx126x_data *data = dev->data;
@@ -1193,7 +1179,7 @@ index 30243ba5dc7..befb84797eb 100644
int ret;
/* Initialize data structures */
@@ -1121,9 +1944,25 @@ static int sx126x_init(const struct device *dev)
@@ -1121,9 +1969,25 @@ static int sx126x_init(const struct device *dev)
k_msgq_init(&data->rx_msgq, (char *)&data->rx_result,
sizeof(struct sx126x_rx_result), 1);
k_work_init(&data->irq_work, sx126x_irq_work_handler);