mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-09-02 08:43:43 +00:00
update dutycycling and LBT to match newest zephyr API-s
This commit is contained in:
@@ -41,23 +41,6 @@ void LR1110Radio::hwConfigure(const struct lora_modem_config &cfg)
|
||||
}
|
||||
}
|
||||
|
||||
void LR1110Radio::hwStartReceive()
|
||||
{
|
||||
int ret = lora_recv_async(_dev, rxCallbackStatic, this);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("lora_recv_async failed: %d", ret);
|
||||
atomic_set(&_in_recv_mode, 0);
|
||||
return;
|
||||
}
|
||||
atomic_set(&_in_recv_mode, 1);
|
||||
|
||||
/* RX boost: set once via setRxBoost(), LR1110 SetRxBoosted
|
||||
* command persists through SetRx calls. */
|
||||
if (_rx_duty_cycle_enabled) {
|
||||
lr11xx_set_rx_duty_cycle(_dev, true);
|
||||
}
|
||||
}
|
||||
|
||||
void LR1110Radio::hwCancelReceive()
|
||||
{
|
||||
lora_recv_async(_dev, NULL, NULL);
|
||||
@@ -84,11 +67,6 @@ void LR1110Radio::hwSetRxBoost(bool enable)
|
||||
lr11xx_set_rx_boost(_dev, enable);
|
||||
}
|
||||
|
||||
void LR1110Radio::hwSetRxDutyCycle(bool enable)
|
||||
{
|
||||
lr11xx_set_rx_duty_cycle(_dev, enable);
|
||||
}
|
||||
|
||||
void LR1110Radio::hwResetAGC()
|
||||
{
|
||||
/* Warm sleep → Calibrate(ALL) → re-calibrate image → re-apply RX boost */
|
||||
|
||||
@@ -21,14 +21,12 @@ public:
|
||||
protected:
|
||||
/* Hardware primitives */
|
||||
void hwConfigure(const struct lora_modem_config &cfg) override;
|
||||
void hwStartReceive() override;
|
||||
void hwCancelReceive() override;
|
||||
int hwSendAsync(uint8_t *buf, uint32_t len,
|
||||
struct k_poll_signal *sig) override;
|
||||
int16_t hwGetCurrentRSSI() override;
|
||||
bool hwIsPreambleDetected() override;
|
||||
void hwSetRxBoost(bool enable) override;
|
||||
void hwSetRxDutyCycle(bool enable) override;
|
||||
void hwResetAGC() override;
|
||||
};
|
||||
|
||||
|
||||
@@ -41,21 +41,6 @@ void LR2021Radio::hwConfigure(const struct lora_modem_config &cfg)
|
||||
}
|
||||
}
|
||||
|
||||
void LR2021Radio::hwStartReceive()
|
||||
{
|
||||
int ret = lora_recv_async(_dev, rxCallbackStatic, this);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("lora_recv_async failed: %d", ret);
|
||||
atomic_set(&_in_recv_mode, 0);
|
||||
return;
|
||||
}
|
||||
atomic_set(&_in_recv_mode, 1);
|
||||
|
||||
if (_rx_duty_cycle_enabled) {
|
||||
lr20xx_set_rx_duty_cycle(_dev, true);
|
||||
}
|
||||
}
|
||||
|
||||
void LR2021Radio::hwCancelReceive()
|
||||
{
|
||||
lora_recv_async(_dev, NULL, NULL);
|
||||
@@ -82,11 +67,6 @@ void LR2021Radio::hwSetRxBoost(bool enable)
|
||||
lr20xx_set_rx_boost(_dev, enable);
|
||||
}
|
||||
|
||||
void LR2021Radio::hwSetRxDutyCycle(bool enable)
|
||||
{
|
||||
lr20xx_set_rx_duty_cycle(_dev, enable);
|
||||
}
|
||||
|
||||
void LR2021Radio::hwResetAGC()
|
||||
{
|
||||
lr20xx_reset_agc(_dev);
|
||||
|
||||
@@ -21,14 +21,12 @@ public:
|
||||
protected:
|
||||
/* Hardware primitives */
|
||||
void hwConfigure(const struct lora_modem_config &cfg) override;
|
||||
void hwStartReceive() override;
|
||||
void hwCancelReceive() override;
|
||||
int hwSendAsync(uint8_t *buf, uint32_t len,
|
||||
struct k_poll_signal *sig) override;
|
||||
int16_t hwGetCurrentRSSI() override;
|
||||
bool hwIsPreambleDetected() override;
|
||||
void hwSetRxBoost(bool enable) override;
|
||||
void hwSetRxDutyCycle(bool enable) override;
|
||||
void hwResetAGC() override;
|
||||
};
|
||||
|
||||
|
||||
@@ -210,6 +210,11 @@ void LoRaRadioBase::buildModemConfig(struct lora_modem_config &cfg, bool tx)
|
||||
cfg.iq_inverted = false;
|
||||
cfg.public_network = false;
|
||||
cfg.packet_crc_disable = false;
|
||||
|
||||
/* LBT: driver performs hardware CAD before TX, returns -EBUSY if busy */
|
||||
if (tx) {
|
||||
cfg.cad.mode = LORA_CAD_MODE_LBT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -374,7 +379,63 @@ void LoRaRadioBase::reconfigureWithParams(float freq, float bw, uint8_t sf, uint
|
||||
void LoRaRadioBase::startReceive()
|
||||
{
|
||||
configureRx();
|
||||
hwStartReceive();
|
||||
|
||||
int ret;
|
||||
|
||||
if (_rx_duty_cycle_enabled) {
|
||||
/* Compute duty cycle timing from modem config (RadioLib algorithm).
|
||||
* The driver converts these to hardware-specific units. */
|
||||
struct lora_modem_config cfg;
|
||||
buildModemConfig(cfg, false);
|
||||
|
||||
uint8_t sf = (uint8_t)cfg.datarate;
|
||||
uint32_t bw_hz = bandwidth_to_hz(cfg.bandwidth);
|
||||
float bw_khz = (float)bw_hz / 1000.0f;
|
||||
uint16_t preamble_len = cfg.preamble_len;
|
||||
uint16_t min_symbols = (sf >= 7) ? 8 : 12;
|
||||
int16_t sleep_symbols = (int16_t)preamble_len -
|
||||
(int16_t)min_symbols;
|
||||
|
||||
if (sleep_symbols > 0) {
|
||||
uint32_t symbol_us = (uint32_t)((float)(1 << sf) *
|
||||
1000.0f / bw_khz);
|
||||
int16_t safe = sleep_symbols - 2;
|
||||
if (safe < 1) safe = 1;
|
||||
uint32_t sleep_us = (uint16_t)safe * symbol_us;
|
||||
|
||||
uint32_t preamble_us = (preamble_len + 1) * symbol_us;
|
||||
int32_t w1 = ((int32_t)preamble_us -
|
||||
((int32_t)sleep_us - 1000)) / 2;
|
||||
uint32_t w2 = (min_symbols + 1) * symbol_us;
|
||||
uint32_t rx_us = (w1 > 0 && (uint32_t)w1 > w2)
|
||||
? (uint32_t)w1 : w2;
|
||||
|
||||
ret = lora_recv_duty_cycle(_dev,
|
||||
K_USEC(rx_us),
|
||||
K_USEC(sleep_us),
|
||||
rxCallbackStatic, this);
|
||||
if (ret == 0) {
|
||||
atomic_set(&_in_recv_mode, 1);
|
||||
return;
|
||||
}
|
||||
if (ret != -ENOSYS) {
|
||||
LOG_ERR("lora_recv_duty_cycle failed: %d", ret);
|
||||
}
|
||||
} else {
|
||||
LOG_WRN("Preamble too short for duty cycle "
|
||||
"(need >%d, have %d)",
|
||||
min_symbols, preamble_len);
|
||||
}
|
||||
/* Fall through to normal recv_async */
|
||||
}
|
||||
|
||||
ret = lora_recv_async(_dev, rxCallbackStatic, this);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("lora_recv_async failed: %d", ret);
|
||||
atomic_set(&_in_recv_mode, 0);
|
||||
return;
|
||||
}
|
||||
atomic_set(&_in_recv_mode, 1);
|
||||
}
|
||||
|
||||
/* ── RX/TX ────────────────────────────────────────────────────────────── */
|
||||
@@ -524,18 +585,6 @@ void LoRaRadioBase::triggerNoiseFloorCalibrate(int threshold)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Random delay 0-500 ms before sampling. Breaks phase-lock with
|
||||
* periodic interference that might be synchronized with our fixed
|
||||
* 5-second housekeeping cadence. */
|
||||
uint32_t jitter;
|
||||
sys_rand_get(&jitter, sizeof(jitter));
|
||||
k_sleep(K_MSEC(jitter % 500));
|
||||
|
||||
/* Re-check after the delay — a packet may have arrived. */
|
||||
if (isReceiving()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Median of multiple RSSI reads (~200 us). Rejects up to N/2-1
|
||||
* outliers in either direction without the downward bias of min
|
||||
* or the spike sensitivity of average. Insertion sort is fine
|
||||
@@ -663,7 +712,10 @@ void LoRaRadioBase::enableRxDutyCycle(bool enable)
|
||||
_rx_duty_cycle_enabled = enable;
|
||||
LOG_INF("RX duty cycle %s", enable ? "enabled" : "disabled");
|
||||
if (atomic_get(&_in_recv_mode)) {
|
||||
hwSetRxDutyCycle(enable);
|
||||
/* Restart receive to apply new duty cycle state */
|
||||
hwCancelReceive();
|
||||
atomic_set(&_in_recv_mode, 0);
|
||||
startReceive();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -83,9 +83,6 @@ protected:
|
||||
/** Apply lora_modem_config + any chip-specific extras (image cal, etc.) */
|
||||
virtual void hwConfigure(const struct lora_modem_config &cfg) = 0;
|
||||
|
||||
/** Start async receive (lora_recv_async + boost/duty-cycle setup) */
|
||||
virtual void hwStartReceive() = 0;
|
||||
|
||||
/** Cancel current async receive */
|
||||
virtual void hwCancelReceive() = 0;
|
||||
|
||||
@@ -102,9 +99,6 @@ protected:
|
||||
/** Set RX LNA boost on/off */
|
||||
virtual void hwSetRxBoost(bool enable) = 0;
|
||||
|
||||
/** Set RX duty cycle on/off (hardware level) */
|
||||
virtual void hwSetRxDutyCycle(bool enable) = 0;
|
||||
|
||||
/** Reset AGC (chip-specific, may be no-op) */
|
||||
virtual void hwResetAGC() = 0;
|
||||
|
||||
@@ -159,7 +153,7 @@ protected:
|
||||
struct lora_modem_config _last_cfg;
|
||||
bool _config_cached;
|
||||
|
||||
/* Static RX callback — passed to lora_recv_async() by subclass hwStartReceive() */
|
||||
/* Static RX callback — passed to lora_recv_async() / lora_recv_duty_cycle() */
|
||||
static void rxCallbackStatic(const struct device *dev, uint8_t *data,
|
||||
uint16_t size, int16_t rssi, int8_t snr,
|
||||
void *user_data);
|
||||
|
||||
@@ -41,23 +41,6 @@ void SX126xRadio::hwConfigure(const struct lora_modem_config &cfg)
|
||||
}
|
||||
}
|
||||
|
||||
void SX126xRadio::hwStartReceive()
|
||||
{
|
||||
int ret = lora_recv_async(_dev, rxCallbackStatic, this);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("lora_recv_async failed: %d", ret);
|
||||
atomic_set(&_in_recv_mode, 0);
|
||||
return;
|
||||
}
|
||||
atomic_set(&_in_recv_mode, 1);
|
||||
|
||||
/* RX boost: set once via setRxBoost(), preserved by SX126x
|
||||
* hardware retention registers (DS §9.6). */
|
||||
if (_rx_duty_cycle_enabled) {
|
||||
sx126x_set_rx_duty_cycle(_dev, true);
|
||||
}
|
||||
}
|
||||
|
||||
void SX126xRadio::hwCancelReceive()
|
||||
{
|
||||
lora_recv_async(_dev, NULL, NULL);
|
||||
@@ -84,11 +67,6 @@ void SX126xRadio::hwSetRxBoost(bool enable)
|
||||
sx126x_set_rx_boost(_dev, enable);
|
||||
}
|
||||
|
||||
void SX126xRadio::hwSetRxDutyCycle(bool enable)
|
||||
{
|
||||
sx126x_set_rx_duty_cycle(_dev, enable);
|
||||
}
|
||||
|
||||
void SX126xRadio::hwResetAGC()
|
||||
{
|
||||
/* Warm sleep → Calibrate(ALL) → re-calibrate image → re-apply RX settings */
|
||||
|
||||
@@ -19,14 +19,12 @@ public:
|
||||
protected:
|
||||
/* Hardware primitives */
|
||||
void hwConfigure(const struct lora_modem_config &cfg) override;
|
||||
void hwStartReceive() override;
|
||||
void hwCancelReceive() override;
|
||||
int hwSendAsync(uint8_t *buf, uint32_t len,
|
||||
struct k_poll_signal *sig) override;
|
||||
int16_t hwGetCurrentRSSI() override;
|
||||
bool hwIsPreambleDetected() override;
|
||||
void hwSetRxBoost(bool enable) override;
|
||||
void hwSetRxDutyCycle(bool enable) override;
|
||||
void hwResetAGC() override;
|
||||
bool hwIsChipBusy() override;
|
||||
};
|
||||
|
||||
@@ -45,6 +45,24 @@ typedef void (*RadioTxDoneCallback)(void *user_data);
|
||||
|
||||
/* --- Zephyr enum mapping utilities --- */
|
||||
|
||||
/* Map Zephyr bandwidth enum to Hz */
|
||||
static inline uint32_t bandwidth_to_hz(enum lora_signal_bandwidth bw)
|
||||
{
|
||||
switch (bw) {
|
||||
case BW_7_KHZ: return 7812;
|
||||
case BW_10_KHZ: return 10417;
|
||||
case BW_15_KHZ: return 15625;
|
||||
case BW_20_KHZ: return 20833;
|
||||
case BW_31_KHZ: return 31250;
|
||||
case BW_41_KHZ: return 41667;
|
||||
case BW_62_KHZ: return 62500;
|
||||
case BW_125_KHZ: return 125000;
|
||||
case BW_250_KHZ: return 250000;
|
||||
case BW_500_KHZ: return 500000;
|
||||
default: return 125000;
|
||||
}
|
||||
}
|
||||
|
||||
/* Map kHz bandwidth value to Zephyr enum.
|
||||
* Input is (uint16_t)(float_bw) — truncated, e.g. 7.8→7, 10.4→10, 62.5→62.
|
||||
* Zephyr >=4.4 has narrow BWs (7-62 kHz); <=4.3 only has 125/250/500. */
|
||||
|
||||
@@ -83,6 +83,13 @@ struct lr11xx_data {
|
||||
bool rx_boost_enabled;
|
||||
bool rx_boost_applied; /* RX boost register written to hardware */
|
||||
|
||||
/* CAD state */
|
||||
lora_cad_cb cad_cb;
|
||||
void *cad_user_data;
|
||||
struct k_sem cad_sem;
|
||||
int cad_result;
|
||||
bool cad_active;
|
||||
|
||||
/* Deferred hardware init — heavy SPI/radio work runs on first config() */
|
||||
bool hw_initialized;
|
||||
|
||||
@@ -439,6 +446,29 @@ static void lr11xx_dio1_work_handler(struct k_work *work)
|
||||
rx_restarted = true;
|
||||
}
|
||||
|
||||
/* ── CAD done ── */
|
||||
if (irq & LR11XX_SYSTEM_IRQ_CAD_DONE) {
|
||||
bool detected = (irq & LR11XX_SYSTEM_IRQ_CAD_DETECTED) != 0;
|
||||
|
||||
LOG_DBG("CAD done: %s", detected ? "activity" : "free");
|
||||
data->cad_active = false;
|
||||
|
||||
if (data->cad_cb) {
|
||||
lora_cad_cb cb = data->cad_cb;
|
||||
void *ud = data->cad_user_data;
|
||||
|
||||
data->cad_cb = NULL;
|
||||
data->cad_user_data = NULL;
|
||||
k_mutex_unlock(&data->spi_mutex);
|
||||
cb(data->dev, detected, ud);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Blocking CAD: signal the semaphore */
|
||||
data->cad_result = detected ? 1 : 0;
|
||||
k_sem_give(&data->cad_sem);
|
||||
}
|
||||
|
||||
/* ── TX done ── */
|
||||
if (irq & LR11XX_SYSTEM_IRQ_TX_DONE) {
|
||||
LOG_DBG("TX done");
|
||||
@@ -602,6 +632,9 @@ static uint32_t lr11xx_lora_airtime(const struct device *dev,
|
||||
return (uint32_t)((t_preamble + t_payload) * 1000.0f);
|
||||
}
|
||||
|
||||
/* Forward declaration — needed by LBT in send_async */
|
||||
static int lr11xx_lora_cad(const struct device *dev, k_timeout_t timeout);
|
||||
|
||||
/* ── Driver API: send_async ─────────────────────────────────────────── */
|
||||
|
||||
static int lr11xx_lora_send_async(const struct device *dev,
|
||||
@@ -616,6 +649,19 @@ static int lr11xx_lora_send_async(const struct device *dev,
|
||||
if (data->tx_active) return -EBUSY;
|
||||
if (data_len > 255 || data_len == 0) return -EINVAL;
|
||||
|
||||
/* LBT: perform blocking CAD before transmitting */
|
||||
if (data->modem_cfg.cad.mode == LORA_CAD_MODE_LBT) {
|
||||
int cad_ret = lr11xx_lora_cad(dev, K_MSEC(200));
|
||||
if (cad_ret > 0) {
|
||||
LOG_DBG("LBT: channel busy");
|
||||
return -EBUSY;
|
||||
}
|
||||
if (cad_ret < 0 && cad_ret != -ENOSYS) {
|
||||
LOG_WRN("LBT: CAD failed (%d), proceeding with TX",
|
||||
cad_ret);
|
||||
}
|
||||
}
|
||||
|
||||
k_mutex_lock(&data->spi_mutex, K_FOREVER);
|
||||
|
||||
/* Cancel RX */
|
||||
@@ -771,13 +817,6 @@ bool lr11xx_is_receiving(const struct device *dev)
|
||||
LR11XX_SYSTEM_IRQ_SYNC_WORD_HEADER_VALID)) != 0;
|
||||
}
|
||||
|
||||
void lr11xx_set_rx_duty_cycle(const struct device *dev, bool enable)
|
||||
{
|
||||
/* LR1110 duty cycle is broken — always continuous RX. Ignore. */
|
||||
(void)dev;
|
||||
(void)enable;
|
||||
}
|
||||
|
||||
void lr11xx_set_rx_boost(const struct device *dev, bool enable)
|
||||
{
|
||||
struct lr11xx_data *data = dev->data;
|
||||
@@ -858,6 +897,131 @@ void lr11xx_reset_agc(const struct device *dev)
|
||||
|
||||
/* ── Deferred hardware init (runs on first lora_config call) ────────── */
|
||||
|
||||
/* ── Driver API: CAD ────────────────────────────────────────────────── */
|
||||
|
||||
/* Recommended cad_detect_peak values per SF for 2-symbol CAD.
|
||||
* From Semtech SX1261/62/68 / LR1110 reference (same silicon IP). */
|
||||
static uint8_t lr11xx_cad_detect_peak(uint8_t sf)
|
||||
{
|
||||
switch (sf) {
|
||||
case 5: case 6: return 56;
|
||||
case 7: return 56;
|
||||
case 8: return 58;
|
||||
case 9: return 58;
|
||||
case 10: return 60;
|
||||
case 11: return 64;
|
||||
case 12: return 68;
|
||||
default: return 60;
|
||||
}
|
||||
}
|
||||
|
||||
static int lr11xx_do_cad(struct lr11xx_data *data)
|
||||
{
|
||||
void *ctx = &data->hal_ctx;
|
||||
struct lora_modem_config *mc = &data->modem_cfg;
|
||||
|
||||
uint8_t sf = (uint8_t)mc->datarate;
|
||||
uint8_t symb_nb = 2;
|
||||
uint8_t detect_peak = lr11xx_cad_detect_peak(sf);
|
||||
|
||||
if (mc->cad.symbol_num != 0) {
|
||||
symb_nb = (uint8_t)mc->cad.symbol_num;
|
||||
}
|
||||
if (mc->cad.detection_peak != 0) {
|
||||
detect_peak = mc->cad.detection_peak;
|
||||
}
|
||||
|
||||
lr11xx_radio_cad_params_t cad = {
|
||||
.cad_symb_nb = symb_nb,
|
||||
.cad_detect_peak = detect_peak,
|
||||
.cad_detect_min = mc->cad.detection_minimum ? mc->cad.detection_minimum : 10,
|
||||
.cad_exit_mode = LR11XX_RADIO_CAD_EXIT_MODE_STANDBYRC,
|
||||
.cad_timeout = 0,
|
||||
};
|
||||
|
||||
lr11xx_radio_set_cad_params(ctx, &cad);
|
||||
|
||||
lr11xx_system_clear_irq_status(ctx, LR11XX_SYSTEM_IRQ_ALL_MASK);
|
||||
data->cad_active = true;
|
||||
lr11xx_radio_set_cad(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lr11xx_lora_cad(const struct device *dev, k_timeout_t timeout)
|
||||
{
|
||||
struct lr11xx_data *data = dev->data;
|
||||
int ret;
|
||||
|
||||
if (!data->configured) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
k_mutex_lock(&data->spi_mutex, K_FOREVER);
|
||||
|
||||
bool was_in_rx = data->in_rx_mode;
|
||||
|
||||
if (was_in_rx) {
|
||||
data->in_rx_mode = false;
|
||||
lr11xx_system_set_standby(&data->hal_ctx,
|
||||
LR11XX_SYSTEM_STANDBY_CFG_RC);
|
||||
}
|
||||
|
||||
k_sem_reset(&data->cad_sem);
|
||||
data->cad_result = -ETIMEDOUT;
|
||||
data->cad_cb = NULL;
|
||||
|
||||
ret = lr11xx_do_cad(data);
|
||||
k_mutex_unlock(&data->spi_mutex);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = k_sem_take(&data->cad_sem, timeout);
|
||||
if (ret == -EAGAIN) {
|
||||
data->cad_active = false;
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return data->cad_result;
|
||||
}
|
||||
|
||||
static int lr11xx_lora_cad_async(const struct device *dev,
|
||||
lora_cad_cb cb, void *user_data)
|
||||
{
|
||||
struct lr11xx_data *data = dev->data;
|
||||
|
||||
if (cb == NULL) {
|
||||
data->cad_cb = NULL;
|
||||
data->cad_user_data = NULL;
|
||||
data->cad_active = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!data->configured) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
k_mutex_lock(&data->spi_mutex, K_FOREVER);
|
||||
|
||||
if (data->in_rx_mode) {
|
||||
data->in_rx_mode = false;
|
||||
lr11xx_system_set_standby(&data->hal_ctx,
|
||||
LR11XX_SYSTEM_STANDBY_CFG_RC);
|
||||
}
|
||||
|
||||
data->cad_cb = cb;
|
||||
data->cad_user_data = user_data;
|
||||
|
||||
int ret = lr11xx_do_cad(data);
|
||||
k_mutex_unlock(&data->spi_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ── Deferred hardware init ─────────────────────────────────────────── */
|
||||
|
||||
static int lr11xx_hw_init(struct lr11xx_data *data,
|
||||
const struct lr11xx_config *cfg)
|
||||
{
|
||||
@@ -975,6 +1139,7 @@ static int lr11xx_lora_init(const struct device *dev)
|
||||
data->hw_initialized = false;
|
||||
|
||||
k_mutex_init(&data->spi_mutex);
|
||||
k_sem_init(&data->cad_sem, 0, 1);
|
||||
k_work_init(&data->dio1_work, lr11xx_dio1_work_handler);
|
||||
|
||||
/* Start dedicated DIO1 work queue at high priority */
|
||||
@@ -1035,6 +1200,9 @@ static DEVICE_API(lora, lr11xx_lora_api) = {
|
||||
.send_async = lr11xx_lora_send_async,
|
||||
.recv = lr11xx_lora_recv,
|
||||
.recv_async = lr11xx_lora_recv_async,
|
||||
.cad = lr11xx_lora_cad,
|
||||
.cad_async = lr11xx_lora_cad_async,
|
||||
/* .recv_duty_cycle = NULL — LR1110 duty cycle broken */
|
||||
};
|
||||
|
||||
#define LR11XX_INIT(n) \
|
||||
|
||||
@@ -40,19 +40,6 @@ int16_t lr11xx_get_rssi_inst(const struct device *dev);
|
||||
*/
|
||||
bool lr11xx_is_receiving(const struct device *dev);
|
||||
|
||||
/**
|
||||
* @brief Enable/disable RX duty cycle mode
|
||||
*
|
||||
* When enabled, the radio alternates between RX and sleep using the
|
||||
* RadioLib preamble detection algorithm. Saves ~60-70% RX current.
|
||||
*
|
||||
* Takes effect on the next RX start (recv_async or after TX).
|
||||
*
|
||||
* @param dev LoRa device
|
||||
* @param enable true to enable, false for continuous RX
|
||||
*/
|
||||
void lr11xx_set_rx_duty_cycle(const struct device *dev, bool enable);
|
||||
|
||||
/**
|
||||
* @brief Enable/disable RX boosted mode
|
||||
*
|
||||
|
||||
@@ -86,6 +86,13 @@ struct lr20xx_data {
|
||||
bool rx_boost_enabled;
|
||||
bool rx_boost_applied;
|
||||
|
||||
/* CAD state */
|
||||
lora_cad_cb cad_cb;
|
||||
void *cad_user_data;
|
||||
struct k_sem cad_sem;
|
||||
int cad_result; /* 0=free, 1=busy, <0=error */
|
||||
bool cad_active;
|
||||
|
||||
/* Deferred hardware init */
|
||||
bool hw_initialized;
|
||||
|
||||
@@ -675,6 +682,29 @@ static void lr20xx_dio1_work_handler(struct k_work *work)
|
||||
rx_restarted = true;
|
||||
}
|
||||
|
||||
/* ── CAD done ── */
|
||||
if (irq & LR20XX_SYSTEM_IRQ_CAD_DONE) {
|
||||
bool detected = (irq & LR20XX_SYSTEM_IRQ_CAD_DETECTED) != 0;
|
||||
|
||||
LOG_DBG("CAD done: %s", detected ? "activity" : "free");
|
||||
data->cad_active = false;
|
||||
|
||||
if (data->cad_cb) {
|
||||
lora_cad_cb cb = data->cad_cb;
|
||||
void *ud = data->cad_user_data;
|
||||
|
||||
data->cad_cb = NULL;
|
||||
data->cad_user_data = NULL;
|
||||
k_mutex_unlock(&data->spi_mutex);
|
||||
cb(data->dev, detected, ud);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Blocking CAD: signal the semaphore */
|
||||
data->cad_result = detected ? 1 : 0;
|
||||
k_sem_give(&data->cad_sem);
|
||||
}
|
||||
|
||||
/* ── TX done ── */
|
||||
if (irq & LR20XX_SYSTEM_IRQ_TX_DONE) {
|
||||
LOG_DBG("TX done");
|
||||
@@ -826,6 +856,8 @@ static uint32_t lr20xx_lora_airtime(const struct device *dev,
|
||||
|
||||
/* ── Driver API: send_async ─────────────────────────────────────────── */
|
||||
|
||||
static int lr20xx_lora_cad(const struct device *dev, k_timeout_t timeout);
|
||||
|
||||
static int lr20xx_lora_send_async(const struct device *dev,
|
||||
uint8_t *buf, uint32_t data_len,
|
||||
struct k_poll_signal *async)
|
||||
@@ -838,6 +870,19 @@ static int lr20xx_lora_send_async(const struct device *dev,
|
||||
if (data->tx_active) return -EBUSY;
|
||||
if (data_len > 255 || data_len == 0) return -EINVAL;
|
||||
|
||||
/* LBT: perform blocking CAD before transmitting */
|
||||
if (data->modem_cfg.cad.mode == LORA_CAD_MODE_LBT) {
|
||||
int cad_ret = lr20xx_lora_cad(dev, K_MSEC(200));
|
||||
if (cad_ret > 0) {
|
||||
LOG_DBG("LBT: channel busy");
|
||||
return -EBUSY;
|
||||
}
|
||||
if (cad_ret < 0 && cad_ret != -ENOSYS) {
|
||||
LOG_WRN("LBT: CAD failed (%d), proceeding with TX",
|
||||
cad_ret);
|
||||
}
|
||||
}
|
||||
|
||||
k_mutex_lock(&data->spi_mutex, K_FOREVER);
|
||||
|
||||
data->async_rx_cb = NULL;
|
||||
@@ -956,15 +1001,13 @@ static int lr20xx_lora_recv_async(const struct device *dev,
|
||||
|
||||
data->async_rx_cb = cb;
|
||||
data->async_rx_user_data = user_data;
|
||||
data->rx_duty_cycle_enabled = false;
|
||||
|
||||
lr20xx_start_rx(data, cfg);
|
||||
|
||||
k_mutex_unlock(&data->spi_mutex);
|
||||
|
||||
LOG_INF("recv_async: %s%s",
|
||||
data->rx_duty_cycle_enabled
|
||||
? "RX duty cycle"
|
||||
: "continuous RX",
|
||||
LOG_INF("recv_async: continuous RX%s",
|
||||
data->rx_boost_enabled ? ", boosted" : "");
|
||||
|
||||
return 0;
|
||||
@@ -1022,19 +1065,6 @@ bool lr20xx_is_receiving(const struct device *dev)
|
||||
LR20XX_SYSTEM_IRQ_SYNC_WORD_HEADER_VALID)) != 0;
|
||||
}
|
||||
|
||||
void lr20xx_set_rx_duty_cycle(const struct device *dev, bool enable)
|
||||
{
|
||||
struct lr20xx_data *data = dev->data;
|
||||
|
||||
if (data->rx_duty_cycle_enabled == enable) {
|
||||
return;
|
||||
}
|
||||
|
||||
data->rx_duty_cycle_enabled = enable;
|
||||
LOG_INF("RX duty cycle %s (takes effect on next RX start)",
|
||||
enable ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
void lr20xx_set_rx_boost(const struct device *dev, bool enable)
|
||||
{
|
||||
struct lr20xx_data *data = dev->data;
|
||||
@@ -1113,6 +1143,188 @@ void lr20xx_reset_agc(const struct device *dev)
|
||||
k_mutex_unlock(&data->spi_mutex);
|
||||
}
|
||||
|
||||
/* ── Driver API: CAD ────────────────────────────────────────────────── */
|
||||
|
||||
/* Recommended cad_detect_peak values per SF for 2-symbol CAD.
|
||||
* From Semtech LR20xx datasheet table. Using 2 symbols as a
|
||||
* good balance between speed (~2 symbol durations) and reliability. */
|
||||
static uint8_t lr20xx_cad_detect_peak(uint8_t sf)
|
||||
{
|
||||
switch (sf) {
|
||||
case 5: case 6: return 56;
|
||||
case 7: return 56;
|
||||
case 8: return 58;
|
||||
case 9: return 58;
|
||||
case 10: return 60;
|
||||
case 11: return 64;
|
||||
case 12: return 68;
|
||||
default: return 60;
|
||||
}
|
||||
}
|
||||
|
||||
static int lr20xx_do_cad(struct lr20xx_data *data)
|
||||
{
|
||||
void *ctx = &data->hal_ctx;
|
||||
struct lora_modem_config *mc = &data->modem_cfg;
|
||||
|
||||
uint8_t sf = (uint8_t)mc->datarate;
|
||||
lr20xx_radio_lora_cad_params_t cad = {
|
||||
.cad_symb_nb = 2,
|
||||
.pnr_delta = 0, /* exact symbol count, no best-effort */
|
||||
.cad_exit_mode = LR20XX_RADIO_LORA_CAD_EXIT_MODE_STANDBYRC,
|
||||
.cad_timeout_in_pll_step = 0,
|
||||
.cad_detect_peak = lr20xx_cad_detect_peak(sf),
|
||||
};
|
||||
|
||||
/* Override from modem config if caller set non-zero values */
|
||||
if (mc->cad.symbol_num != 0) {
|
||||
cad.cad_symb_nb = (uint8_t)mc->cad.symbol_num;
|
||||
}
|
||||
if (mc->cad.detection_peak != 0) {
|
||||
cad.cad_detect_peak = mc->cad.detection_peak;
|
||||
}
|
||||
|
||||
lr20xx_radio_lora_configure_cad_params(ctx, &cad);
|
||||
|
||||
/* Clear any pending IRQ flags, then start CAD */
|
||||
lr20xx_system_clear_irq_status(ctx, LR20XX_SYSTEM_IRQ_ALL_MASK);
|
||||
data->cad_active = true;
|
||||
lr20xx_radio_lora_set_cad(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lr20xx_lora_cad(const struct device *dev, k_timeout_t timeout)
|
||||
{
|
||||
struct lr20xx_data *data = dev->data;
|
||||
int ret;
|
||||
|
||||
if (!data->configured) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
k_mutex_lock(&data->spi_mutex, K_FOREVER);
|
||||
|
||||
/* Stop async RX if active — CAD needs the radio */
|
||||
bool was_in_rx = data->in_rx_mode;
|
||||
|
||||
if (was_in_rx) {
|
||||
data->in_rx_mode = false;
|
||||
lr20xx_system_set_standby_mode(&data->hal_ctx,
|
||||
LR20XX_SYSTEM_STANDBY_MODE_RC);
|
||||
}
|
||||
|
||||
k_sem_reset(&data->cad_sem);
|
||||
data->cad_result = -ETIMEDOUT;
|
||||
data->cad_cb = NULL;
|
||||
|
||||
ret = lr20xx_do_cad(data);
|
||||
k_mutex_unlock(&data->spi_mutex);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Wait for DIO1 handler to signal CAD_DONE */
|
||||
ret = k_sem_take(&data->cad_sem, timeout);
|
||||
if (ret == -EAGAIN) {
|
||||
data->cad_active = false;
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return data->cad_result;
|
||||
}
|
||||
|
||||
static int lr20xx_lora_cad_async(const struct device *dev,
|
||||
lora_cad_cb cb, void *user_data)
|
||||
{
|
||||
struct lr20xx_data *data = dev->data;
|
||||
|
||||
if (cb == NULL) {
|
||||
/* Cancel pending CAD */
|
||||
data->cad_cb = NULL;
|
||||
data->cad_user_data = NULL;
|
||||
data->cad_active = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!data->configured) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
k_mutex_lock(&data->spi_mutex, K_FOREVER);
|
||||
|
||||
bool was_in_rx = data->in_rx_mode;
|
||||
|
||||
if (was_in_rx) {
|
||||
data->in_rx_mode = false;
|
||||
lr20xx_system_set_standby_mode(&data->hal_ctx,
|
||||
LR20XX_SYSTEM_STANDBY_MODE_RC);
|
||||
}
|
||||
|
||||
data->cad_cb = cb;
|
||||
data->cad_user_data = user_data;
|
||||
|
||||
int ret = lr20xx_do_cad(data);
|
||||
k_mutex_unlock(&data->spi_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ── Driver API: recv_duty_cycle ────────────────────────────────────── */
|
||||
|
||||
static int lr20xx_lora_recv_duty_cycle(const struct device *dev,
|
||||
k_timeout_t rx_period,
|
||||
k_timeout_t sleep_period,
|
||||
lora_recv_cb cb, void *user_data)
|
||||
{
|
||||
struct lr20xx_data *data = dev->data;
|
||||
const struct lr20xx_config *cfg = dev->config;
|
||||
|
||||
if (cb == NULL) {
|
||||
/* Cancel — same as recv_async(NULL) */
|
||||
k_mutex_lock(&data->spi_mutex, K_FOREVER);
|
||||
data->async_rx_cb = NULL;
|
||||
data->async_rx_user_data = NULL;
|
||||
data->in_rx_mode = false;
|
||||
k_mutex_unlock(&data->spi_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!data->configured) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
k_mutex_lock(&data->spi_mutex, K_FOREVER);
|
||||
|
||||
data->async_rx_cb = cb;
|
||||
data->async_rx_user_data = user_data;
|
||||
|
||||
void *ctx = &data->hal_ctx;
|
||||
|
||||
lr20xx_system_set_standby_mode(ctx, LR20XX_SYSTEM_STANDBY_MODE_RC);
|
||||
lr20xx_system_clear_irq_status(ctx, LR20XX_SYSTEM_IRQ_ALL_MASK);
|
||||
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;
|
||||
|
||||
data->rx_duty_cycle_enabled = true;
|
||||
lr20xx_radio_common_set_rx_duty_cycle(ctx, rx_ms, slp_ms,
|
||||
LR20XX_RADIO_COMMON_RX_DUTY_CYCLE_MODE_RX);
|
||||
LOG_INF("recv_duty_cycle: rx=%ums sleep=%ums", rx_ms, slp_ms);
|
||||
|
||||
lr20xx_system_clear_irq_status(ctx, LR20XX_SYSTEM_IRQ_ALL_MASK);
|
||||
data->in_rx_mode = true;
|
||||
data->tx_active = false;
|
||||
|
||||
k_mutex_unlock(&data->spi_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ── Deferred hardware init ─────────────────────────────────────────── */
|
||||
|
||||
static int lr20xx_hw_init(struct lr20xx_data *data,
|
||||
@@ -1285,6 +1497,7 @@ static int lr20xx_lora_init(const struct device *dev)
|
||||
data->hw_initialized = false;
|
||||
|
||||
k_mutex_init(&data->spi_mutex);
|
||||
k_sem_init(&data->cad_sem, 0, 1);
|
||||
k_work_init(&data->dio1_work, lr20xx_dio1_work_handler);
|
||||
|
||||
k_work_queue_start(&data->dio1_wq, lr20xx_dio1_wq_stack,
|
||||
@@ -1327,12 +1540,15 @@ static int lr20xx_lora_init(const struct device *dev)
|
||||
/* ── Device instantiation ───────────────────────────────────────────── */
|
||||
|
||||
static DEVICE_API(lora, lr20xx_lora_api) = {
|
||||
.config = lr20xx_lora_config,
|
||||
.airtime = lr20xx_lora_airtime,
|
||||
.send = lr20xx_lora_send,
|
||||
.send_async = lr20xx_lora_send_async,
|
||||
.recv = lr20xx_lora_recv,
|
||||
.recv_async = lr20xx_lora_recv_async,
|
||||
.config = lr20xx_lora_config,
|
||||
.airtime = lr20xx_lora_airtime,
|
||||
.send = lr20xx_lora_send,
|
||||
.send_async = lr20xx_lora_send_async,
|
||||
.recv = lr20xx_lora_recv,
|
||||
.recv_async = lr20xx_lora_recv_async,
|
||||
.cad = lr20xx_lora_cad,
|
||||
.cad_async = lr20xx_lora_cad_async,
|
||||
.recv_duty_cycle = lr20xx_lora_recv_duty_cycle,
|
||||
};
|
||||
|
||||
#define LR20XX_INIT(n) \
|
||||
|
||||
@@ -37,17 +37,6 @@ int16_t lr20xx_get_rssi_inst(const struct device *dev);
|
||||
*/
|
||||
bool lr20xx_is_receiving(const struct device *dev);
|
||||
|
||||
/**
|
||||
* @brief Enable/disable RX duty cycle mode
|
||||
*
|
||||
* When enabled, radio alternates between RX and sleep.
|
||||
* Takes effect on the next RX start.
|
||||
*
|
||||
* @param dev LoRa device
|
||||
* @param enable true to enable, false for continuous RX
|
||||
*/
|
||||
void lr20xx_set_rx_duty_cycle(const struct device *dev, bool enable);
|
||||
|
||||
/**
|
||||
* @brief Enable/disable RX boosted mode
|
||||
*
|
||||
|
||||
@@ -40,17 +40,6 @@ int16_t sx126x_get_rssi_inst(const struct device *dev);
|
||||
*/
|
||||
bool sx126x_is_receiving(const struct device *dev);
|
||||
|
||||
/**
|
||||
* @brief Enable/disable RX duty cycle mode
|
||||
*
|
||||
* When enabled, the radio alternates between RX and sleep using the
|
||||
* RadioLib preamble detection algorithm. Saves ~60-70% RX current.
|
||||
*
|
||||
* @param dev LoRa device
|
||||
* @param enable true to enable, false for continuous RX
|
||||
*/
|
||||
void sx126x_set_rx_duty_cycle(const struct device *dev, bool enable);
|
||||
|
||||
/**
|
||||
* @brief Enable/disable RX boosted mode
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/drivers/lora/native/sx126x/sx126x.c b/drivers/lora/native/sx126x/sx126x.c
|
||||
index 7b0b67f89d5..a6c1ceba162 100644
|
||||
index 7b0b67f89d5..0225ff4360d 100644
|
||||
--- a/drivers/lora/native/sx126x/sx126x.c
|
||||
+++ b/drivers/lora/native/sx126x/sx126x.c
|
||||
@@ -9,10 +9,19 @@
|
||||
@@ -177,7 +177,7 @@ index 7b0b67f89d5..a6c1ceba162 100644
|
||||
}
|
||||
|
||||
static void sx126x_set_rf_path(const struct device *dev, bool enable, bool tx)
|
||||
@@ -405,11 +466,133 @@ static void sx126x_set_rf_path(const struct device *dev, bool enable, bool tx)
|
||||
@@ -405,11 +466,70 @@ 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);
|
||||
@@ -194,74 +194,6 @@ index 7b0b67f89d5..a6c1ceba162 100644
|
||||
}
|
||||
}
|
||||
|
||||
+/* ── RX duty cycle (RadioLib algorithm) ─────────────────────────────── */
|
||||
+
|
||||
+#define SX126X_DC_MIN_SYMBOLS_SF7_PLUS 8
|
||||
+#define SX126X_DC_MIN_SYMBOLS_SF6_LESS 12
|
||||
+#define SX126X_DC_TCXO_DELAY_US 1000
|
||||
+
|
||||
+static void sx126x_apply_rx_duty_cycle(struct sx126x_data *data)
|
||||
+{
|
||||
+ const struct device *dev = data->dev;
|
||||
+ struct lora_modem_config *mc = &data->config;
|
||||
+
|
||||
+ uint8_t sf = (uint8_t)mc->datarate;
|
||||
+ uint32_t bw_hz = bandwidth_to_hz(mc->bandwidth);
|
||||
+ float bw_khz = (float)bw_hz / 1000.0f;
|
||||
+ uint16_t preamble_len = mc->preamble_len;
|
||||
+
|
||||
+ uint16_t min_symbols = (sf >= 7) ? SX126X_DC_MIN_SYMBOLS_SF7_PLUS
|
||||
+ : SX126X_DC_MIN_SYMBOLS_SF6_LESS;
|
||||
+
|
||||
+ int16_t sleep_symbols = (int16_t)preamble_len - (int16_t)min_symbols;
|
||||
+ if (sleep_symbols <= 0) {
|
||||
+ LOG_WRN("Preamble too short for duty cycle (need >%d, have %d)",
|
||||
+ min_symbols, preamble_len);
|
||||
+ data->rx_duty_cycle_enabled = false;
|
||||
+ sx126x_set_rx(dev, 0);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ uint32_t symbol_us = (uint32_t)((float)(1 << sf) * 1000.0f / bw_khz);
|
||||
+
|
||||
+ /* Shave 2 symbols off sleep for timing margin */
|
||||
+ int16_t sleep_symbols_safe = sleep_symbols - 2;
|
||||
+ if (sleep_symbols_safe < 1) {
|
||||
+ sleep_symbols_safe = 1;
|
||||
+ }
|
||||
+ uint32_t sleep_period_us = (uint16_t)sleep_symbols_safe * symbol_us;
|
||||
+
|
||||
+ uint32_t preamble_total_us = (preamble_len + 1) * symbol_us;
|
||||
+ int32_t wake_calc1 = ((int32_t)preamble_total_us -
|
||||
+ ((int32_t)sleep_period_us - SX126X_DC_TCXO_DELAY_US)) / 2;
|
||||
+ uint32_t wake_calc2 = (min_symbols + 1) * symbol_us;
|
||||
+
|
||||
+ uint32_t wake_period_us = (wake_calc1 > 0 && (uint32_t)wake_calc1 > wake_calc2)
|
||||
+ ? (uint32_t)wake_calc1 : wake_calc2;
|
||||
+
|
||||
+ /* SetRxDutyCycle takes times in 15.625us steps (multiply by 64/1000) */
|
||||
+ uint32_t rx_time = (wake_period_us * 64) / 1000;
|
||||
+ uint32_t sleep_time = (sleep_period_us * 64) / 1000;
|
||||
+
|
||||
+ if (rx_time < 64) {
|
||||
+ rx_time = 64;
|
||||
+ }
|
||||
+ if (sleep_time < 64) {
|
||||
+ sleep_time = 64;
|
||||
+ }
|
||||
+
|
||||
+ /* SPI command: 3 bytes rx_period + 3 bytes sleep_period */
|
||||
+ uint8_t buf[6];
|
||||
+
|
||||
+ sys_put_be24(rx_time, &buf[0]);
|
||||
+ sys_put_be24(sleep_time, &buf[3]);
|
||||
+ sx126x_hal_write_cmd(dev, SX126X_CMD_SET_RX_DUTY_CYCLE, buf, 6);
|
||||
+
|
||||
+ uint32_t rx_ms = (rx_time * 1000) / 64000;
|
||||
+ uint32_t sleep_ms = (sleep_time * 1000) / 64000;
|
||||
+ LOG_DBG("RX duty cycle: SF%d rx=%ums sleep=%ums", sf, rx_ms, sleep_ms);
|
||||
+}
|
||||
+
|
||||
+/* ── Lightweight RX restart ─────────────────────────────────────────── */
|
||||
+
|
||||
+/* Restart RX as fast as possible — used for RX→RX transitions in the
|
||||
@@ -290,7 +222,12 @@ index 7b0b67f89d5..a6c1ceba162 100644
|
||||
+ sx126x_set_dio2_as_rf_switch(dev, true);
|
||||
+ }
|
||||
+
|
||||
+ sx126x_apply_rx_duty_cycle(data);
|
||||
+ /* Re-apply duty cycle with stored timing */
|
||||
+ uint8_t dc_buf[6];
|
||||
+ sys_put_be24(data->dc_rx_time, &dc_buf[0]);
|
||||
+ sys_put_be24(data->dc_sleep_time, &dc_buf[3]);
|
||||
+ sx126x_hal_write_cmd(dev, SX126X_CMD_SET_RX_DUTY_CYCLE,
|
||||
+ dc_buf, 6);
|
||||
+ } else {
|
||||
+ uint8_t buf[3];
|
||||
+
|
||||
@@ -312,7 +249,7 @@ index 7b0b67f89d5..a6c1ceba162 100644
|
||||
static int sx126x_set_sleep(const struct device *dev)
|
||||
{
|
||||
struct sx126x_data *data = dev->data;
|
||||
@@ -521,19 +704,23 @@ static void sx126x_handle_irq_rx_done(const struct device *dev, uint16_t irq_sta
|
||||
@@ -521,19 +641,23 @@ static void sx126x_handle_irq_rx_done(const struct device *dev, uint16_t irq_sta
|
||||
|
||||
/* Handle async callback or signal sync receiver */
|
||||
if (data->rx_cb != NULL) {
|
||||
@@ -345,7 +282,33 @@ index 7b0b67f89d5..a6c1ceba162 100644
|
||||
} else {
|
||||
/* Sync mode */
|
||||
sx126x_set_sleep(dev);
|
||||
@@ -653,6 +840,29 @@ static int sx126x_lora_config(const struct device *dev,
|
||||
@@ -592,6 +716,25 @@ static void sx126x_irq_work_handler(struct k_work *work)
|
||||
sx126x_handle_irq_timeout(dev);
|
||||
}
|
||||
|
||||
+ if (irq_status & SX126X_IRQ_CAD_DONE) {
|
||||
+ bool detected = (irq_status & SX126X_IRQ_CAD_ACTIVITY_DETECTED) != 0;
|
||||
+
|
||||
+ LOG_DBG("CAD done: %s", detected ? "activity" : "free");
|
||||
+ data->cad_active = false;
|
||||
+
|
||||
+ if (data->cad_cb) {
|
||||
+ lora_cad_cb cb = data->cad_cb;
|
||||
+ void *ud = data->cad_user_data;
|
||||
+
|
||||
+ data->cad_cb = NULL;
|
||||
+ data->cad_user_data = NULL;
|
||||
+ cb(dev, detected, ud);
|
||||
+ } else {
|
||||
+ data->cad_result = detected ? 1 : 0;
|
||||
+ k_sem_give(&data->cad_sem);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
/* 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);
|
||||
@@ -653,6 +796,29 @@ static int sx126x_lora_config(const struct device *dev,
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -375,7 +338,51 @@ index 7b0b67f89d5..a6c1ceba162 100644
|
||||
/* Set sync word */
|
||||
ret = sx126x_set_sync_word(dev, config->public_network);
|
||||
if (ret < 0) {
|
||||
@@ -732,6 +942,29 @@ static int sx126x_lora_send_async(const struct device *dev,
|
||||
@@ -676,6 +842,8 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
+static int sx126x_lora_cad(const struct device *dev, k_timeout_t timeout);
|
||||
+
|
||||
static int sx126x_lora_send_async(const struct device *dev,
|
||||
uint8_t *data_buf, uint32_t data_len,
|
||||
struct k_poll_signal *async)
|
||||
@@ -707,6 +875,34 @@ static int sx126x_lora_send_async(const struct device *dev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
+ /* LBT: perform blocking CAD before transmitting */
|
||||
+ if (data->config.cad.mode == LORA_CAD_MODE_LBT) {
|
||||
+ k_mutex_unlock(&data->lock);
|
||||
+ atomic_set(&data->state, SX126X_REST_STATE);
|
||||
+ int cad_ret = sx126x_lora_cad(dev, K_MSEC(200));
|
||||
+ if (cad_ret > 0) {
|
||||
+ LOG_DBG("LBT: channel busy");
|
||||
+ return -EBUSY;
|
||||
+ }
|
||||
+ if (cad_ret < 0 && cad_ret != -ENOSYS) {
|
||||
+ LOG_WRN("LBT: CAD failed (%d), proceeding with TX",
|
||||
+ cad_ret);
|
||||
+ }
|
||||
+ /* Re-acquire state and lock for TX */
|
||||
+ if (!atomic_cas(&data->state, SX126X_REST_STATE,
|
||||
+ SX126X_STATE_TX)) {
|
||||
+ LOG_ERR("Busy after CAD");
|
||||
+ return -EBUSY;
|
||||
+ }
|
||||
+ k_mutex_lock(&data->lock, K_FOREVER);
|
||||
+ ret = sx126x_ensure_ready(dev);
|
||||
+ if (ret < 0) {
|
||||
+ k_mutex_unlock(&data->lock);
|
||||
+ atomic_set(&data->state, SX126X_REST_STATE);
|
||||
+ return ret;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
data->tx_async_signal = async;
|
||||
k_msgq_purge(&data->tx_msgq);
|
||||
|
||||
@@ -732,6 +928,29 @@ static int sx126x_lora_send_async(const struct device *dev,
|
||||
/* Enable antenna and set TX path */
|
||||
sx126x_set_rf_path(dev, true, true);
|
||||
|
||||
@@ -405,7 +412,15 @@ index 7b0b67f89d5..a6c1ceba162 100644
|
||||
/* Start transmission with 10 second timeout */
|
||||
ret = sx126x_set_tx(dev, 10000);
|
||||
if (ret < 0) {
|
||||
@@ -1038,6 +1271,126 @@ static int sx126x_lora_test_cw(const struct device *dev, uint32_t frequency,
|
||||
@@ -902,6 +1121,7 @@ static int sx126x_lora_recv_async(const struct device *dev,
|
||||
|
||||
data->rx_cb = cb;
|
||||
data->rx_cb_user_data = user_data;
|
||||
+ data->rx_duty_cycle_enabled = false;
|
||||
|
||||
/* Set packet parameters */
|
||||
ret = sx126x_set_packet_params(dev,
|
||||
@@ -1038,14 +1258,407 @@ static int sx126x_lora_test_cw(const struct device *dev, uint32_t frequency,
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -452,25 +467,7 @@ index 7b0b67f89d5..a6c1ceba162 100644
|
||||
+ SX126X_IRQ_HEADER_VALID)) != 0;
|
||||
+}
|
||||
+
|
||||
+void sx126x_set_rx_duty_cycle(const struct device *dev, bool enable)
|
||||
+{
|
||||
+ struct sx126x_data *data = dev->data;
|
||||
+
|
||||
+ data->rx_duty_cycle_enabled = enable;
|
||||
+ LOG_DBG("RX duty cycle %s", enable ? "enabled" : "disabled");
|
||||
+
|
||||
+ /* If currently in RX, apply immediately */
|
||||
+ if (atomic_get(&data->state) == SX126X_STATE_RX) {
|
||||
+ k_mutex_lock(&data->lock, K_FOREVER);
|
||||
+ if (enable && data->config_valid) {
|
||||
+ sx126x_apply_rx_duty_cycle(data);
|
||||
+ } else {
|
||||
+ sx126x_set_rx(dev, 0);
|
||||
+ }
|
||||
+ /* RX gain preserved by hardware retention (§9.6) */
|
||||
+ k_mutex_unlock(&data->lock);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void sx126x_set_rx_boost(const struct device *dev, bool enable)
|
||||
+{
|
||||
@@ -528,11 +525,321 @@ index 7b0b67f89d5..a6c1ceba162 100644
|
||||
+
|
||||
+ k_mutex_unlock(&data->lock);
|
||||
+}
|
||||
+
|
||||
+/* ── Driver API: CAD ────────────────────────────────────────────────── */
|
||||
+
|
||||
+/* Recommended cad_detect_peak values per SF for 2-symbol CAD.
|
||||
+ * From Semtech SX1261/62/68 datasheet AN1200.48. */
|
||||
+static uint8_t sx126x_cad_detect_peak(uint8_t sf)
|
||||
+{
|
||||
+ switch (sf) {
|
||||
+ case 5: case 6: return 56;
|
||||
+ case 7: return 56;
|
||||
+ case 8: return 58;
|
||||
+ case 9: return 58;
|
||||
+ case 10: return 60;
|
||||
+ case 11: return 64;
|
||||
+ case 12: return 68;
|
||||
+ default: return 60;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int sx126x_do_cad(const struct device *dev, struct sx126x_data *data)
|
||||
+{
|
||||
+ struct lora_modem_config *mc = &data->config;
|
||||
+ uint8_t sf = (uint8_t)mc->datarate;
|
||||
+ uint8_t symb_nb = 2;
|
||||
+ uint8_t detect_peak = sx126x_cad_detect_peak(sf);
|
||||
+
|
||||
+ if (mc->cad.symbol_num != 0) {
|
||||
+ symb_nb = (uint8_t)mc->cad.symbol_num;
|
||||
+ }
|
||||
+ if (mc->cad.detection_peak != 0) {
|
||||
+ detect_peak = mc->cad.detection_peak;
|
||||
+ }
|
||||
+
|
||||
+ /* SetCadParams: symb_nb, detect_peak, detect_min, exit_mode,
|
||||
+ * timeout (3 bytes, unused for STANDBYRC exit) */
|
||||
+ uint8_t buf[7];
|
||||
+
|
||||
+ buf[0] = symb_nb;
|
||||
+ buf[1] = detect_peak;
|
||||
+ buf[2] = mc->cad.detection_minimum ? mc->cad.detection_minimum : 10;
|
||||
+ buf[3] = 0x00; /* CAD_ONLY (exit to STANDBYRC) */
|
||||
+ buf[4] = 0; /* timeout[23:16] */
|
||||
+ buf[5] = 0; /* timeout[15:8] */
|
||||
+ buf[6] = 0; /* timeout[7:0] */
|
||||
+
|
||||
+ sx126x_hal_write_cmd(dev, SX126X_CMD_SET_CAD_PARAMS, buf, 7);
|
||||
+
|
||||
+ sx126x_clear_irq_status(dev, SX126X_IRQ_CAD_DONE |
|
||||
+ SX126X_IRQ_CAD_ACTIVITY_DETECTED);
|
||||
+ data->cad_active = true;
|
||||
+
|
||||
+ /* Enable antenna in RX mode for CAD */
|
||||
+ sx126x_set_rf_path(dev, true, false);
|
||||
+ sx126x_hal_write_cmd(dev, SX126X_CMD_SET_CAD, NULL, 0);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int sx126x_lora_cad(const struct device *dev, k_timeout_t timeout)
|
||||
+{
|
||||
+ struct sx126x_data *data = dev->data;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (!data->config_valid) {
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ /* Transition from REST → IDLE for the CAD operation */
|
||||
+ if (!atomic_cas(&data->state, SX126X_REST_STATE, SX126X_STATE_IDLE)) {
|
||||
+ /* If we're in RX, force to IDLE */
|
||||
+ if (!atomic_cas(&data->state, SX126X_STATE_RX, SX126X_STATE_IDLE)) {
|
||||
+ return -EBUSY;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ k_mutex_lock(&data->lock, K_FOREVER);
|
||||
+
|
||||
+ ret = sx126x_ensure_ready(dev);
|
||||
+ if (ret < 0) {
|
||||
+ k_mutex_unlock(&data->lock);
|
||||
+ atomic_set(&data->state, SX126X_REST_STATE);
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ k_sem_reset(&data->cad_sem);
|
||||
+ data->cad_result = -ETIMEDOUT;
|
||||
+ data->cad_cb = NULL;
|
||||
+
|
||||
+ ret = sx126x_do_cad(dev, data);
|
||||
+ k_mutex_unlock(&data->lock);
|
||||
+
|
||||
+ if (ret < 0) {
|
||||
+ atomic_set(&data->state, SX126X_REST_STATE);
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ ret = k_sem_take(&data->cad_sem, timeout);
|
||||
+ if (ret == -EAGAIN) {
|
||||
+ data->cad_active = false;
|
||||
+ atomic_set(&data->state, SX126X_REST_STATE);
|
||||
+ return -ETIMEDOUT;
|
||||
+ }
|
||||
+
|
||||
+ atomic_set(&data->state, SX126X_REST_STATE);
|
||||
+ return data->cad_result;
|
||||
+}
|
||||
+
|
||||
+static int sx126x_lora_cad_async(const struct device *dev,
|
||||
+ lora_cad_cb cb, void *user_data)
|
||||
+{
|
||||
+ struct sx126x_data *data = dev->data;
|
||||
+
|
||||
+ if (cb == NULL) {
|
||||
+ data->cad_cb = NULL;
|
||||
+ data->cad_user_data = NULL;
|
||||
+ data->cad_active = false;
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ if (!data->config_valid) {
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ if (!atomic_cas(&data->state, SX126X_REST_STATE, SX126X_STATE_IDLE)) {
|
||||
+ if (!atomic_cas(&data->state, SX126X_STATE_RX, SX126X_STATE_IDLE)) {
|
||||
+ return -EBUSY;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ k_mutex_lock(&data->lock, K_FOREVER);
|
||||
+
|
||||
+ int ret = sx126x_ensure_ready(dev);
|
||||
+
|
||||
+ if (ret < 0) {
|
||||
+ k_mutex_unlock(&data->lock);
|
||||
+ atomic_set(&data->state, SX126X_REST_STATE);
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ data->cad_cb = cb;
|
||||
+ data->cad_user_data = user_data;
|
||||
+
|
||||
+ ret = sx126x_do_cad(dev, data);
|
||||
+ k_mutex_unlock(&data->lock);
|
||||
+
|
||||
+ if (ret < 0) {
|
||||
+ atomic_set(&data->state, SX126X_REST_STATE);
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+/* ── Driver API: recv_duty_cycle ────────────────────────────────────── */
|
||||
+
|
||||
+static int sx126x_lora_recv_duty_cycle(const struct device *dev,
|
||||
+ k_timeout_t rx_period,
|
||||
+ k_timeout_t sleep_period,
|
||||
+ lora_recv_cb cb, void *user_data)
|
||||
+{
|
||||
+ struct sx126x_data *data = dev->data;
|
||||
+ int ret;
|
||||
+
|
||||
+ k_mutex_lock(&data->lock, K_FOREVER);
|
||||
+
|
||||
+ if (cb == NULL) {
|
||||
+ data->rx_cb = NULL;
|
||||
+ data->rx_cb_user_data = NULL;
|
||||
+ if (atomic_cas(&data->state, SX126X_STATE_RX, SX126X_STATE_IDLE)) {
|
||||
+ sx126x_set_standby(dev, SX126X_STANDBY_RC);
|
||||
+ sx126x_set_sleep(dev);
|
||||
+ }
|
||||
+ k_mutex_unlock(&data->lock);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ if (!data->config_valid) {
|
||||
+ k_mutex_unlock(&data->lock);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ if (!atomic_cas(&data->state, SX126X_REST_STATE, SX126X_STATE_RX)) {
|
||||
+ k_mutex_unlock(&data->lock);
|
||||
+ return -EBUSY;
|
||||
+ }
|
||||
+
|
||||
+ ret = sx126x_ensure_ready(dev);
|
||||
+ if (ret < 0) {
|
||||
+ k_mutex_unlock(&data->lock);
|
||||
+ atomic_set(&data->state, SX126X_REST_STATE);
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ data->rx_cb = cb;
|
||||
+ data->rx_cb_user_data = user_data;
|
||||
+
|
||||
+ /* Set packet parameters (same as recv_async) */
|
||||
+ ret = sx126x_set_packet_params(dev,
|
||||
+ data->config.preamble_len,
|
||||
+ SX126X_LORA_HEADER_EXPLICIT,
|
||||
+ SX126X_MAX_PAYLOAD_LEN,
|
||||
+ data->config.packet_crc_disable ?
|
||||
+ SX126X_LORA_CRC_OFF : SX126X_LORA_CRC_ON,
|
||||
+ data->config.iq_inverted ?
|
||||
+ SX126X_LORA_IQ_INVERTED : SX126X_LORA_IQ_STANDARD);
|
||||
+ if (ret < 0) {
|
||||
+ data->rx_cb = NULL;
|
||||
+ sx126x_set_sleep(dev);
|
||||
+ k_mutex_unlock(&data->lock);
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ sx126x_set_rf_path(dev, true, false);
|
||||
+
|
||||
+ uint32_t rx_time, sleep_time;
|
||||
+
|
||||
+ if (K_TIMEOUT_EQ(rx_period, K_FOREVER) ||
|
||||
+ K_TIMEOUT_EQ(sleep_period, K_FOREVER)) {
|
||||
+ /* Auto-compute from modem config (RadioLib algorithm) */
|
||||
+ struct lora_modem_config *mc = &data->config;
|
||||
+ uint8_t sf = (uint8_t)mc->datarate;
|
||||
+ uint32_t bw_hz = bandwidth_to_hz(mc->bandwidth);
|
||||
+ float bw_khz = (float)bw_hz / 1000.0f;
|
||||
+ uint16_t preamble_len = mc->preamble_len;
|
||||
+
|
||||
+ uint16_t min_symbols = (sf >= 7) ? 8 : 12;
|
||||
+ int16_t sleep_symbols = (int16_t)preamble_len -
|
||||
+ (int16_t)min_symbols;
|
||||
+ if (sleep_symbols <= 0) {
|
||||
+ LOG_WRN("Preamble too short for duty cycle "
|
||||
+ "(need >%d, have %d)",
|
||||
+ min_symbols, preamble_len);
|
||||
+ data->rx_cb = NULL;
|
||||
+ sx126x_set_sleep(dev);
|
||||
+ k_mutex_unlock(&data->lock);
|
||||
+ atomic_set(&data->state, SX126X_REST_STATE);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ uint32_t symbol_us = (uint32_t)((float)(1 << sf) *
|
||||
+ 1000.0f / bw_khz);
|
||||
+
|
||||
+ int16_t sleep_symbols_safe = sleep_symbols - 2;
|
||||
+ if (sleep_symbols_safe < 1) sleep_symbols_safe = 1;
|
||||
+ uint32_t sleep_us = (uint16_t)sleep_symbols_safe * symbol_us;
|
||||
+
|
||||
+ uint32_t preamble_us = (preamble_len + 1) * symbol_us;
|
||||
+ int32_t wake1 = ((int32_t)preamble_us -
|
||||
+ ((int32_t)sleep_us - 1000)) / 2;
|
||||
+ uint32_t wake2 = (min_symbols + 1) * symbol_us;
|
||||
+ uint32_t rx_us = (wake1 > 0 && (uint32_t)wake1 > wake2)
|
||||
+ ? (uint32_t)wake1 : wake2;
|
||||
+
|
||||
+ /* Convert us to 15.625us steps */
|
||||
+ rx_time = (rx_us * 64) / 1000;
|
||||
+ sleep_time = (sleep_us * 64) / 1000;
|
||||
+ } else {
|
||||
+ /* Explicit timing from caller */
|
||||
+ uint32_t rx_us = k_ticks_to_us_ceil32(rx_period.ticks);
|
||||
+ uint32_t slp_us = k_ticks_to_us_ceil32(sleep_period.ticks);
|
||||
+ rx_time = (rx_us * 64) / 1000;
|
||||
+ sleep_time = (slp_us * 64) / 1000;
|
||||
+ }
|
||||
+
|
||||
+ if (rx_time < 64) rx_time = 64;
|
||||
+ if (sleep_time < 64) sleep_time = 64;
|
||||
+
|
||||
+ /* Store for restart_rx to re-use */
|
||||
+ data->dc_rx_time = rx_time;
|
||||
+ data->dc_sleep_time = sleep_time;
|
||||
+ data->rx_duty_cycle_enabled = true;
|
||||
+
|
||||
+ uint8_t buf[6];
|
||||
+
|
||||
+ sys_put_be24(rx_time, &buf[0]);
|
||||
+ sys_put_be24(sleep_time, &buf[3]);
|
||||
+ sx126x_hal_write_cmd(dev, SX126X_CMD_SET_RX_DUTY_CYCLE, buf, 6);
|
||||
+
|
||||
+ /* Apply RX gain */
|
||||
+ uint8_t gain = data->rx_boost_enabled ? SX126X_RX_GAIN_BOOSTED
|
||||
+ : SX126X_RX_GAIN_POWER_SAVING;
|
||||
+ sx126x_hal_write_regs(dev, SX126X_REG_RX_GAIN, &gain, 1);
|
||||
+
|
||||
+ k_mutex_unlock(&data->lock);
|
||||
+
|
||||
+ LOG_DBG("recv_duty_cycle: rx=%uus sleep=%uus",
|
||||
+ (rx_time * 1000) / 64, (sleep_time * 1000) / 64);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static DEVICE_API(lora, sx126x_lora_api) = {
|
||||
.config = sx126x_lora_config,
|
||||
.send = sx126x_lora_send,
|
||||
@@ -1063,6 +1416,14 @@ static int sx126x_init(const struct device *dev)
|
||||
- .config = sx126x_lora_config,
|
||||
- .send = sx126x_lora_send,
|
||||
- .send_async = sx126x_lora_send_async,
|
||||
- .recv = sx126x_lora_recv,
|
||||
- .recv_async = sx126x_lora_recv_async,
|
||||
- .airtime = sx126x_lora_airtime,
|
||||
- .test_cw = sx126x_lora_test_cw,
|
||||
+ .config = sx126x_lora_config,
|
||||
+ .send = sx126x_lora_send,
|
||||
+ .send_async = sx126x_lora_send_async,
|
||||
+ .recv = sx126x_lora_recv,
|
||||
+ .recv_async = sx126x_lora_recv_async,
|
||||
+ .airtime = sx126x_lora_airtime,
|
||||
+ .cad = sx126x_lora_cad,
|
||||
+ .cad_async = sx126x_lora_cad_async,
|
||||
+ .recv_duty_cycle = sx126x_lora_recv_duty_cycle,
|
||||
+ .test_cw = sx126x_lora_test_cw,
|
||||
};
|
||||
|
||||
static int sx126x_init(const struct device *dev)
|
||||
@@ -1060,9 +1673,18 @@ 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);
|
||||
+ k_sem_init(&data->cad_sem, 0, 1);
|
||||
data->dev = dev;
|
||||
atomic_set(&data->state, SX126X_STATE_IDLE);
|
||||
data->config_valid = false;
|
||||
@@ -548,10 +855,10 @@ index 7b0b67f89d5..a6c1ceba162 100644
|
||||
/* Initialize HAL */
|
||||
ret = sx126x_hal_init(dev);
|
||||
diff --git a/drivers/lora/native/sx126x/sx126x.h b/drivers/lora/native/sx126x/sx126x.h
|
||||
index 9dbf3f26586..dabe5495853 100644
|
||||
index 9dbf3f26586..3aa0c7e588c 100644
|
||||
--- a/drivers/lora/native/sx126x/sx126x.h
|
||||
+++ b/drivers/lora/native/sx126x/sx126x.h
|
||||
@@ -62,7 +62,12 @@ struct sx126x_data {
|
||||
@@ -62,7 +62,21 @@ struct sx126x_data {
|
||||
|
||||
/* Deferred work for interrupt handling */
|
||||
struct k_work irq_work;
|
||||
@@ -561,6 +868,15 @@ index 9dbf3f26586..dabe5495853 100644
|
||||
+ /* Extension features (duty cycle, boost) */
|
||||
+ bool rx_duty_cycle_enabled;
|
||||
+ bool rx_boost_enabled;
|
||||
+ uint32_t dc_rx_time; /* stored duty cycle rx period (15.625us steps) */
|
||||
+ uint32_t dc_sleep_time; /* stored duty cycle sleep period (15.625us steps) */
|
||||
+
|
||||
+ /* CAD state */
|
||||
+ lora_cad_cb cad_cb;
|
||||
+ void *cad_user_data;
|
||||
+ struct k_sem cad_sem;
|
||||
+ int cad_result;
|
||||
+ bool cad_active;
|
||||
};
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_LORA_SX126X_SX126X_INTERNAL_H_ */
|
||||
|
||||
Reference in New Issue
Block a user