mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-09-02 18:23:47 +00:00
203 lines
6.0 KiB
C++
203 lines
6.0 KiB
C++
/*
|
|
* SPDX-License-Identifier: MIT
|
|
* LR2021 hardware hooks for LoRaRadioBase.
|
|
*/
|
|
|
|
#include "LR2021Radio.h"
|
|
#include <zephyr/kernel.h>
|
|
|
|
/* LR20xx driver extension API */
|
|
extern "C" {
|
|
#include "lr20xx_lora.h"
|
|
}
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(lr2021_radio, CONFIG_ZEPHCORE_LORA_LOG_LEVEL);
|
|
|
|
namespace mesh {
|
|
|
|
K_THREAD_STACK_DEFINE(lr20xx_tx_wait_stack, TX_WAIT_THREAD_STACK_SIZE);
|
|
|
|
LR2021Radio::LR2021Radio(const struct device *lora_dev, MainBoard &board,
|
|
NodePrefs *prefs)
|
|
: LoRaRadioBase(lora_dev, board, prefs)
|
|
{
|
|
}
|
|
|
|
void LR2021Radio::begin()
|
|
{
|
|
startTxThread(lr20xx_tx_wait_stack,
|
|
K_THREAD_STACK_SIZEOF(lr20xx_tx_wait_stack));
|
|
LoRaRadioBase::begin();
|
|
}
|
|
|
|
bool LR2021Radio::configSideDetectors(const uint8_t *sfs, uint8_t num)
|
|
{
|
|
/* The driver validates against the configured SF/BW, so this must run
|
|
* after the modem is configured — which it is: the CLI path only
|
|
* reaches here on a live radio. */
|
|
int ret = lr20xx_configure_side_detectors(_dev, sfs, num);
|
|
|
|
if (ret < 0) {
|
|
LOG_WRN("side detector config rejected: %d", ret);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void LR2021Radio::resetStats()
|
|
{
|
|
LoRaRadioBase::resetStats();
|
|
lr20xx_reset_freq_offset(_dev);
|
|
}
|
|
|
|
uint32_t LR2021Radio::getDutyCycleTimeoutRestarts() const
|
|
{
|
|
return lr20xx_get_dc_timeout_restarts(_dev);
|
|
}
|
|
|
|
void LR2021Radio::resetDutyCycleTimeoutRestarts()
|
|
{
|
|
lr20xx_reset_dc_timeout_restarts(_dev);
|
|
}
|
|
|
|
int LR2021Radio::formatFreqErrorStatus(char *buf, int cap)
|
|
{
|
|
struct lr20xx_freq_offset_stats st;
|
|
|
|
if (lr20xx_get_freq_offset(_dev, &st) == 0) {
|
|
return snprintf(buf, cap, "no packets measured yet");
|
|
}
|
|
|
|
/* Spread and count matter as much as the mean: the mean only
|
|
* approximates THIS node's reference error once it is averaged over
|
|
* many different peers, because their individual errors cancel and ours
|
|
* does not. A tight spread over a handful of packets is one neighbour,
|
|
* not a population. */
|
|
return snprintf(buf, cap,
|
|
"mean %d Hz, min %d, max %d, %u pkts",
|
|
st.mean_hz, st.min_hz, st.max_hz,
|
|
(unsigned)st.count);
|
|
}
|
|
|
|
/* ── Hardware primitives ──────────────────────────────────────────────── */
|
|
|
|
bool LR2021Radio::hwConfigure(const struct lora_modem_config &cfg)
|
|
{
|
|
int ret = lora_config(_dev, const_cast<struct lora_modem_config *>(&cfg));
|
|
if (ret < 0) {
|
|
LOG_ERR("lora_config failed: %d", ret);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void LR2021Radio::hwCancelReceive()
|
|
{
|
|
lora_recv_async(_dev, NULL, NULL);
|
|
}
|
|
|
|
int LR2021Radio::hwSendAsync(uint8_t *buf, uint32_t len,
|
|
struct k_poll_signal *sig)
|
|
{
|
|
return lora_send_async(_dev, buf, len, sig);
|
|
}
|
|
|
|
int16_t LR2021Radio::hwGetCurrentRSSI()
|
|
{
|
|
return lr20xx_get_rssi_inst(_dev);
|
|
}
|
|
|
|
/* Deliberately no hwIsChipBusy() override, matching the LR11xx.
|
|
*
|
|
* It backs LoRaRadioBase::isRadioReady(), which gates TX in startSendRaw() as
|
|
* well as the probes. Wiring BUSY into the transmit path stalled sends until
|
|
* the dispatcher's 4 s CAD timeout, because the event-driven loop has nothing
|
|
* to re-wake a deferred send. The duty-cycle sleep window is handled where it
|
|
* belongs instead: skipped in the two incidental pollers
|
|
* (lr20xx_is_receiving, lr20xx_get_rssi_inst) and stood down deliberately for
|
|
* TX inside the driver. */
|
|
|
|
bool LR2021Radio::hwIsReceiving()
|
|
{
|
|
/* The poll itself is non-destructive: lr20xx_is_receiving() reads the
|
|
* IRQ register via get_status(), which clears nothing.
|
|
*
|
|
* Foreign-preamble release is NOT hardware-driven, whatever the comment
|
|
* that stood here (copy-pasted from LR1110Radio.cpp) claimed. DS §5.7:
|
|
* IRQ status bits are latched until ClearIrq, and continuous RX has no
|
|
* timeout — so the driver releases them in software, on an SF-aware
|
|
* grace for the preamble and a max-airtime deadline for the header,
|
|
* exactly as the LR11xx and SX126x drivers do. */
|
|
return lr20xx_is_receiving(_dev);
|
|
}
|
|
|
|
void LR2021Radio::hwSetRxBoost(bool enable)
|
|
{
|
|
lr20xx_set_rx_boost(_dev, enable);
|
|
}
|
|
|
|
int LR2021Radio::hwCadProbe(int8_t level)
|
|
{
|
|
return lr20xx_cad_probe(_dev, level);
|
|
}
|
|
|
|
void LR2021Radio::hwCadSetPeakOffset(int8_t offset)
|
|
{
|
|
lr20xx_cad_set_peak_offset(_dev, offset);
|
|
}
|
|
|
|
uint8_t LR2021Radio::hwCadBasePeak()
|
|
{
|
|
return lr20xx_cad_base_peak(_dev);
|
|
}
|
|
|
|
/* The detPeak range lr20xx_do_cad() will actually program. Must match the
|
|
* driver's clamp exactly: if the adapter thinks the range is wider, the
|
|
* staircase explores offsets that collapse onto one peak and reads the noise
|
|
* between them as curvature — which is how it random-walked to -8 at SF7,
|
|
* where base 51 puts everything from -3 down onto peak 48. */
|
|
uint8_t LR2021Radio::hwCadPeakMin()
|
|
{
|
|
return lr20xx_cad_peak_min();
|
|
}
|
|
|
|
uint8_t LR2021Radio::hwCadPeakMax()
|
|
{
|
|
return lr20xx_cad_peak_max();
|
|
}
|
|
|
|
/* No hwResetAgc() override, and no hwNeedsAgcReset(): this part has no
|
|
* jammed-AGC fault to remedy — that one belongs to the SX126x. The driver
|
|
* exposes lr20xx_recalibrate() instead, which is the temperature-drift path
|
|
* this family genuinely does need, named for what it actually does. */
|
|
|
|
|
|
void LR2021Radio::hwRecalibrate()
|
|
{
|
|
/* Front-end calibration included: this is the temperature-drift path, and
|
|
* image/FE cal is the part a temperature swing actually invalidates. */
|
|
lr20xx_recalibrate(_dev);
|
|
}
|
|
|
|
/* DS 6.4.2 gives an image/FE calibration temperature range (and advises redoing
|
|
* PLL/AAF beyond +/-20 C), so drift recalibration is active here. The reading
|
|
* comes from the board — see LoRaRadioBase::imageCalMaintenance(). */
|
|
bool LR2021Radio::hwHasDriftRecal()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
uint32_t LR2021Radio::hwWakeupTimeUs()
|
|
{
|
|
/* Per-device, because the TCXO term dominates and is board-specific:
|
|
* meshtracker_x1 declares tcxo-startup-delay-ms = <5>, promicro_lr2021
|
|
* is XTAL-only. Inheriting the base class's flat 1500 us under-counted
|
|
* the X1's real deaf time by ~4.6 ms, which oversized the duty-cycle
|
|
* sleep and dropped window-edge preambles regardless of signal strength.
|
|
* Same shape as the SX126x and LR11xx overrides. */
|
|
return lr20xx_get_wakeup_time_us(_dev);
|
|
}
|
|
|
|
} /* namespace mesh */
|