mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-10-23 20:12:26 +00:00
lr2021 cleanup
This commit is contained in:
1 parent
6705ff4507
commit
9ab9a9b950
7 files changed
+114
-110
No files matched your search
@@ -1427,44 +1427,6 @@ uint32_t lr11xx_get_random(const struct device *dev)
|
||||
return random;
|
||||
}
|
||||
|
||||
void lr11xx_reset_agc(const struct device *dev)
|
||||
{
|
||||
struct lr11xx_data *data = dev->data;
|
||||
|
||||
k_mutex_lock(&data->spi_mutex, K_FOREVER);
|
||||
|
||||
/* Warm sleep — powers down analog frontend (resets AGC gain state) */
|
||||
lr11xx_system_sleep_cfg_t sleep_cfg = {
|
||||
.is_warm_start = true,
|
||||
.is_rtc_timeout = false,
|
||||
};
|
||||
lr11xx_system_set_sleep(&data->hal_ctx, sleep_cfg, 0);
|
||||
k_sleep(K_USEC(500));
|
||||
|
||||
/* Wake to STDBY_RC for calibration */
|
||||
lr11xx_system_set_standby(&data->hal_ctx, LR11XX_SYSTEM_STANDBY_CFG_RC);
|
||||
|
||||
/* Recalibrate all blocks */
|
||||
lr11xx_system_calibrate(&data->hal_ctx, 0x3F);
|
||||
|
||||
/* Re-calibrate image rejection for the actual operating frequency */
|
||||
if (data->configured) {
|
||||
uint16_t freq_mhz = data->modem_cfg.frequency / 1000000;
|
||||
lr11xx_system_calibrate_image_in_mhz(&data->hal_ctx,
|
||||
freq_mhz - 4, freq_mhz + 4);
|
||||
}
|
||||
|
||||
/* Re-apply RX boost if it was enabled */
|
||||
if (data->rx_boost_enabled) {
|
||||
lr11xx_radio_cfg_rx_boosted(&data->hal_ctx, true);
|
||||
lr11xx_system_clear_irq_status(&data->hal_ctx,
|
||||
LR11XX_SYSTEM_IRQ_ALL_MASK);
|
||||
data->rx_boost_applied = true;
|
||||
}
|
||||
|
||||
k_mutex_unlock(&data->spi_mutex);
|
||||
}
|
||||
|
||||
/* ── Deferred hardware init (runs on first lora_config call) ────────── */
|
||||
|
||||
/* ── Driver API: CAD ────────────────────────────────────────────────── */
|
||||
|
||||
@@ -95,19 +95,6 @@ void lr11xx_reset_dc_timeout_restarts(const struct device *dev);
|
||||
*/
|
||||
uint32_t lr11xx_get_random(const struct device *dev);
|
||||
|
||||
/**
|
||||
* @brief Reset AGC by performing warm sleep + full recalibration
|
||||
*
|
||||
* Warm sleep powers down the analog frontend (resets AGC gain state),
|
||||
* then Calibrate(0x3F) refreshes all blocks. Re-applies image
|
||||
* calibration for the operating frequency and RX boost afterward.
|
||||
*
|
||||
* Must be called while NOT actively receiving a packet.
|
||||
*
|
||||
* @param dev LoRa device
|
||||
*/
|
||||
void lr11xx_reset_agc(const struct device *dev);
|
||||
|
||||
/**
|
||||
* @brief Set the adaptive-CAD operating detPeak offset
|
||||
*
|
||||
|
||||
@@ -491,10 +491,10 @@ static lr20xx_status_t lr20xx_calibrate_front_end(void *ctx, uint32_t freq_hz);
|
||||
* because the patch supersedes them.
|
||||
*
|
||||
* The image is volatile: lost on reset and on cold start, preserved by sleep
|
||||
* with retention (§22.3). Every sleep this driver issues sets retention
|
||||
* (lr20xx_reset_agc), and duty-cycle sleep is retention by definition (§6.3.8),
|
||||
* so the reset paths are the only places it has to be (re)loaded. Costs 2240
|
||||
* bytes of flash and +80 nA of retention sleep current.
|
||||
* with retention (§22.3). Duty-cycle sleep is retention by definition (§6.3.8)
|
||||
* and this driver issues no other sleep, so the reset paths are the only places
|
||||
* it has to be (re)loaded. Costs 2240 bytes of flash and +80 nA of retention
|
||||
* sleep current.
|
||||
*
|
||||
* Best-effort: a chip without the patch still works, so a failure here is a
|
||||
* warning, not a reason to fail init. */
|
||||
@@ -609,7 +609,7 @@ static void lr20xx_hardware_reset(struct lr20xx_data *data,
|
||||
* caller of this function is post-config (the DIO1 stuck-recovery path
|
||||
* and three standby-failure paths), and lr20xx_start_rx() follows
|
||||
* immediately, so calibrate for the frequency actually about to be
|
||||
* received on. Same shape as lr20xx_reset_agc(). */
|
||||
* received on. */
|
||||
if (data->configured) {
|
||||
lr20xx_calibrate_front_end(ctx, data->modem_cfg.frequency);
|
||||
}
|
||||
@@ -975,6 +975,32 @@ static bool lr20xx_apply_rx_duty_cycle(struct lr20xx_data *data)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Re-apply RX boost before re-arming — same warm-start caution as
|
||||
* lr11xx_restart_rx(), which has always done this on its duty-cycle
|
||||
* path. A duty cycle sleeps the chip between every window (DS §6.3.8),
|
||||
* and DS §6.3.4 (SetAdditionalRegToRetain, "specifies the address of an
|
||||
* additional register to retain in Sleep mode") establishes that not
|
||||
* every register survives that on its own. This driver holds no
|
||||
* retention entries at all, and the only place set_rx_path was ever
|
||||
* issued is apply_modem_config() — which the re-arm path never calls, so
|
||||
* before this the boost was programmed once at RX entry and never again.
|
||||
* If it does not survive the sleep, every window after the first ran
|
||||
* 3 dB down: the same failure the SX126x hit, where the fix was pinning
|
||||
* RX gain into the warm-start retention list.
|
||||
*
|
||||
* One SPI command, correct either way. Whether the LR2021 actually needs
|
||||
* it — or wants a retention entry instead — is still an open question;
|
||||
* read the gain back after a window, or A/B it on PDR.
|
||||
*
|
||||
* Not conditioned on rx_boost_applied: that flag tracks what we
|
||||
* programmed, not what survived. */
|
||||
if (data->rx_boost_enabled) {
|
||||
lr20xx_radio_common_set_rx_path(
|
||||
ctx, LR20XX_RADIO_COMMON_RX_PATH_LF,
|
||||
LR20XX_RADIO_COMMON_RX_PATH_BOOST_MODE_7);
|
||||
data->rx_boost_applied = true;
|
||||
}
|
||||
|
||||
/* Clear the CMD_PERR the HAL's clockless wake frame provokes: it is
|
||||
* self-inflicted by the wake and says nothing about the command below.
|
||||
* Left set it holds DIO1 high and poisons the re-arm. */
|
||||
@@ -2120,6 +2146,28 @@ static int lr20xx_lora_recv_async(const struct device *dev,
|
||||
|
||||
k_mutex_lock(&data->spi_mutex, K_FOREVER);
|
||||
|
||||
/* Idempotent fast path: the chip is already in continuous RX, so there
|
||||
* is nothing to program — refresh the callback and leave the receiver on
|
||||
* air. The usual caller is LoRaRadioBase::startSendRaw()'s failure path:
|
||||
* the LBT branch of send_async has already run lr20xx_start_rx() before
|
||||
* returning -EBUSY, so without this every CAD-busy verdict paid a SECOND
|
||||
* full rebuild (standby -> ClearRxFifo -> apply_modem_config -> SetRx) on
|
||||
* top of the driver's own. startSendRaw's comment has asserted this was
|
||||
* "a no-op there" ever since the SX126x gained its Phase 2 fast path; on
|
||||
* this driver it was not, until now.
|
||||
*
|
||||
* rx_duty_cycle_enabled is part of the guard, not decoration. This is
|
||||
* the CONTINUOUS entry point and it clears that flag below, so a caller
|
||||
* moving a duty-cycled radio to continuous RX has to take the full path:
|
||||
* fast-pathing there would leave the cycle running on a chip whose
|
||||
* bookkeeping claims continuous, and nothing would ever stand it down. */
|
||||
if (data->in_rx_mode && !data->rx_duty_cycle_enabled) {
|
||||
data->async_rx_cb = cb;
|
||||
data->async_rx_user_data = user_data;
|
||||
k_mutex_unlock(&data->spi_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
data->async_rx_cb = cb;
|
||||
data->async_rx_user_data = user_data;
|
||||
data->rx_duty_cycle_enabled = false;
|
||||
@@ -2511,42 +2559,6 @@ uint32_t lr20xx_get_random(const struct device *dev)
|
||||
return random;
|
||||
}
|
||||
|
||||
void lr20xx_reset_agc(const struct device *dev)
|
||||
{
|
||||
struct lr20xx_data *data = dev->data;
|
||||
void *ctx = &data->hal_ctx;
|
||||
|
||||
k_mutex_lock(&data->spi_mutex, K_FOREVER);
|
||||
|
||||
/* Warm sleep — powers down analog frontend (resets AGC gain state).
|
||||
* is_ram_retention_enabled=true = warm sleep (equivalent to LR11xx warm_start). */
|
||||
lr20xx_system_sleep_cfg_t sleep_cfg = {
|
||||
.is_clk_32k_enabled = false,
|
||||
.is_ram_retention_enabled = true,
|
||||
};
|
||||
/* No delay after the sleep opcode — lr20xx_hal_write() already does a
|
||||
* k_busy_wait(1000) when it sees 0x0127, so this was a duplicate guard
|
||||
* (and k_sleep() with a sub-tick duration rounds up to a full tick). */
|
||||
lr20xx_system_set_sleep_mode(ctx, &sleep_cfg, 0);
|
||||
|
||||
lr20xx_system_set_standby_mode(ctx, LR20XX_SYSTEM_STANDBY_MODE_RC);
|
||||
|
||||
lr20xx_system_calibrate(ctx, 0x6F);
|
||||
|
||||
if (data->configured) {
|
||||
lr20xx_calibrate_front_end(ctx, data->modem_cfg.frequency);
|
||||
}
|
||||
|
||||
if (data->rx_boost_enabled) {
|
||||
lr20xx_radio_common_set_rx_path(
|
||||
ctx, LR20XX_RADIO_COMMON_RX_PATH_LF,
|
||||
LR20XX_RADIO_COMMON_RX_PATH_BOOST_MODE_7);
|
||||
data->rx_boost_applied = true;
|
||||
}
|
||||
|
||||
k_mutex_unlock(&data->spi_mutex);
|
||||
}
|
||||
|
||||
/* ── Driver API: CAD ────────────────────────────────────────────────── */
|
||||
|
||||
/* Datasheet Table 6-19 in full: rows = CAD symbol count 1..4, columns =
|
||||
@@ -2852,8 +2864,31 @@ static int lr20xx_lora_recv_duty_cycle(const struct device *dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
uint32_t rx_ms = k_ticks_to_ms_ceil32(rx_period.ticks);
|
||||
uint32_t slp_ms = k_ticks_to_ms_ceil32(sleep_period.ticks);
|
||||
if (rx_ms < 1) rx_ms = 1;
|
||||
if (slp_ms < 1) slp_ms = 1;
|
||||
|
||||
k_mutex_lock(&data->spi_mutex, K_FOREVER);
|
||||
|
||||
/* Idempotent fast path, the duty-cycle counterpart of the one in
|
||||
* recv_async and the same shape as the SX126x's: a cycle already armed
|
||||
* on exactly these periods has nothing to re-arm, so refresh the
|
||||
* callback and stay on air instead of tearing a live cycle down and
|
||||
* paying a full rebuild for it. Timings are computed before the lock so
|
||||
* they can be compared here.
|
||||
*
|
||||
* Any other combination — continuous RX, or a cycle on different periods
|
||||
* — falls through to the full path below, which re-arms via
|
||||
* lr20xx_apply_rx_duty_cycle() and therefore still gets the AGC reset. */
|
||||
if (data->in_rx_mode && data->rx_duty_cycle_enabled &&
|
||||
data->dc_rx_ms == rx_ms && data->dc_sleep_ms == slp_ms) {
|
||||
data->async_rx_cb = cb;
|
||||
data->async_rx_user_data = user_data;
|
||||
k_mutex_unlock(&data->spi_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
data->async_rx_cb = cb;
|
||||
data->async_rx_user_data = user_data;
|
||||
|
||||
@@ -2864,11 +2899,6 @@ static int lr20xx_lora_recv_duty_cycle(const struct device *dev,
|
||||
lr20xx_radio_fifo_clear_rx(ctx);
|
||||
lr20xx_apply_modem_config(data, cfg, false);
|
||||
|
||||
uint32_t rx_ms = k_ticks_to_ms_ceil32(rx_period.ticks);
|
||||
uint32_t slp_ms = k_ticks_to_ms_ceil32(sleep_period.ticks);
|
||||
if (rx_ms < 1) rx_ms = 1;
|
||||
if (slp_ms < 1) slp_ms = 1;
|
||||
|
||||
/* Store for the re-arm paths (lr20xx_start_rx / lr20xx_restart_rx)
|
||||
* so every re-entry uses exactly this timing. */
|
||||
data->dc_rx_ms = rx_ms;
|
||||
|
||||
@@ -168,13 +168,6 @@ int lr20xx_configure_side_detectors(const struct device *dev,
|
||||
*/
|
||||
uint32_t lr20xx_get_random(const struct device *dev);
|
||||
|
||||
/**
|
||||
* @brief Reset AGC by performing warm sleep + full recalibration
|
||||
*
|
||||
* @param dev LoRa device
|
||||
*/
|
||||
void lr20xx_reset_agc(const struct device *dev);
|
||||
|
||||
/**
|
||||
* @brief Set the adaptive-CAD operating detPeak offset
|
||||
*
|
||||
|
||||
@@ -517,6 +517,32 @@ void Dispatcher::checkSend()
|
||||
* continuously on a busy site and buries real
|
||||
* faults. */
|
||||
LOG_DBG("checkSend: startSendRaw refused, re-queuing delay=%u", retry);
|
||||
|
||||
/* Escalate a refusal that will not end. This branch
|
||||
* used to leave cad_busy_start alone, so a packet the
|
||||
* driver's own LBT kept rejecting — the pre-TX gate
|
||||
* having passed — looped indefinitely with no counter,
|
||||
* no error flag and nothing above DBG. At default log
|
||||
* level a node in that state looks completely idle
|
||||
* while it never transmits, which is exactly how a
|
||||
* chip-side CAD (LR20xx CAD_LBT) fails.
|
||||
*
|
||||
* Deliberately no recoverRxState() here, unlike the
|
||||
* isReceiving() branch above: that one recovers a chip
|
||||
* suspected of being stuck in RX, whereas a busy
|
||||
* channel is a true reading and walking the radio
|
||||
* through REST would only add deaf time. Report and
|
||||
* keep retrying. */
|
||||
if (cad_busy_start == 0) {
|
||||
cad_busy_start = now;
|
||||
} else if (now - cad_busy_start > getCADFailMaxDuration()) {
|
||||
_err_flags |= ERR_EVENT_CAD_TIMEOUT;
|
||||
LOG_WRN("checkSend: LBT has refused TX for %ums "
|
||||
"(len=%d, noise=%d) — channel busy or CAD too sensitive",
|
||||
(unsigned)(now - cad_busy_start), len,
|
||||
_radio->getNoiseFloor());
|
||||
cad_busy_start = now;
|
||||
}
|
||||
logTxFail(outbound, outbound->getRawLength());
|
||||
_mgr->queueOutbound(outbound, outbound_priority, futureMillis((int)retry));
|
||||
outbound = nullptr;
|
||||
@@ -525,6 +551,12 @@ void Dispatcher::checkSend()
|
||||
}
|
||||
} else {
|
||||
outbound_expiry = futureMillis((int)max_airtime);
|
||||
/* A transmit got out, so the refusal streak above is
|
||||
* over. Without this the timer keeps running across
|
||||
* successful sends and the next isolated refusal
|
||||
* inherits a stale start, reporting a stall that
|
||||
* already ended. */
|
||||
cad_busy_start = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -511,9 +511,9 @@ static void mesh_event_loop(void)
|
||||
|
||||
/* Periodic housekeeping — maintenance + UI refresh */
|
||||
if (events & MESH_EVENT_HOUSEKEEPING) {
|
||||
/* Radio maintenance: noise floor calibration, AGC reset,
|
||||
* RX watchdog. Separated from loop() so these never run
|
||||
* on packet-driven events. */
|
||||
/* Radio maintenance: noise floor calibration, adaptive-CAD
|
||||
* probe, RX watchdog. Separated from loop() so these
|
||||
* never run on packet-driven events. */
|
||||
if (companion_mesh_ptr) {
|
||||
companion_mesh_ptr->maintenanceLoop();
|
||||
}
|
||||
|
||||
@@ -443,9 +443,9 @@ static void room_event_loop(void)
|
||||
/* Periodic housekeeping — maintenance + display refresh */
|
||||
if (events & MESH_EVENT_HOUSEKEEPING) {
|
||||
#ifdef ZEPHCORE_LORA
|
||||
/* Radio maintenance: noise floor calibration, AGC reset,
|
||||
* RX watchdog. Separated from loop() so these never run
|
||||
* on packet-driven events. */
|
||||
/* Radio maintenance: noise floor calibration, adaptive-CAD
|
||||
* probe, RX watchdog. Separated from loop() so these
|
||||
* never run on packet-driven events. */
|
||||
if (room_mesh_ptr) {
|
||||
room_mesh_ptr->maintenanceLoop();
|
||||
/* Also drive loop() so time-based actions (advert
|
||||
|
||||
Reference in new issue
Block a user