feat(llcc68): add configurable RF switch modes
Add an explicit rf-switch-mode devicetree property for LLCC68 instances, covering no switch handling, TXEN/RXEN complementary GPIO control, and DIO2 single-pin control for PE4259-style RF switches. Preserve the existing default behavior with an auto Kconfig default that only enables complementary GPIO handling when both TXEN and RXEN GPIOs are present. Resolve the RF switch mode into llcc68_config at build time and validate incompatible devicetree combinations with BUILD_ASSERT checks. Configure optional RXEN GPIO handling for DIO2 single-pin mode and keep DIO2 RF switch control disabled unless that mode is selected. Replace the old fire-and-forget TX/RX GPIO helper with a result-returning mode-aware RF switch state helper, and apply it across standby, sleep, CAD, TX, RX, continuous wave, infinite preamble, and modem init paths. Add SetRxDutyCycle support with explicit raw 24-bit LLCC68 period units, plus helpers and a millisecond wrapper for callers that work in time units. Select the RX RF path before issuing the duty-cycle command so RXEN stays valid for duty-cycle listen windows.
This commit is contained in:
+94
-19
@@ -172,18 +172,46 @@ int wait_for_not_busy(const gpio_dt_spec &busy_gpio, uint16_t timeout_ms) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void LLCC68::tx_rx_en_pin_set(TxRxPinState state) {
|
||||
if (not tx_enable_gpio() or (not rx_enable_gpio())) {
|
||||
return;
|
||||
expected<unit, error_code> LLCC68::set_rf_switch_state(RfSwitchState state) {
|
||||
auto set_gpio = [](const gpio_dt_spec &gpio, int value)
|
||||
-> expected<unit, error_code> {
|
||||
if (not device_is_ready(gpio.port)) {
|
||||
return ue(-ENODEV);
|
||||
}
|
||||
const int ret = gpio_pin_set_dt(&gpio, value);
|
||||
if (ret < 0) {
|
||||
return ue(ret);
|
||||
}
|
||||
return unit{};
|
||||
};
|
||||
|
||||
switch (config().rf_switch_mode) {
|
||||
case LLCC68_RF_SWITCH_NONE:
|
||||
return unit{};
|
||||
case LLCC68_RF_SWITCH_GPIO_COMPLEMENTARY: {
|
||||
const auto tx = tx_enable_gpio();
|
||||
const auto rx = rx_enable_gpio();
|
||||
if (not tx or not rx) {
|
||||
return ue(-ENODEV);
|
||||
}
|
||||
|
||||
expected<unit, error_code> r;
|
||||
if (state == RfSwitchState::TX) {
|
||||
r = set_gpio(*rx, 0);
|
||||
APP_RADIO_RETURN_ERR(r);
|
||||
return set_gpio(*tx, 1);
|
||||
}
|
||||
r = set_gpio(*tx, 0);
|
||||
APP_RADIO_RETURN_ERR(r);
|
||||
return set_gpio(*rx, state == RfSwitchState::RX ? 1 : 0);
|
||||
}
|
||||
auto t = *tx_enable_gpio();
|
||||
auto r = *rx_enable_gpio();
|
||||
if (state == TxRxPinState::TX) {
|
||||
gpio_pin_set_dt(&t, 1);
|
||||
gpio_pin_set_dt(&r, 0);
|
||||
} else if (state == TxRxPinState::RX) {
|
||||
gpio_pin_set_dt(&t, 0);
|
||||
gpio_pin_set_dt(&r, 1);
|
||||
case LLCC68_RF_SWITCH_DIO2_SINGLE:
|
||||
if (auto rx = rx_enable_gpio()) {
|
||||
return set_gpio(*rx, 1);
|
||||
}
|
||||
return unit{};
|
||||
default:
|
||||
return ue(-EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -643,7 +671,9 @@ expected<unit, error_code> LLCC68::reset() {
|
||||
|
||||
expected<unit, error_code> LLCC68::set_standby() {
|
||||
const uint8_t data[] = {RADIOLIB_SX126X_STANDBY_RC};
|
||||
return write_stream(RADIOLIB_SX126X_CMD_SET_STANDBY, data);
|
||||
auto r = write_stream(RADIOLIB_SX126X_CMD_SET_STANDBY, data);
|
||||
APP_RADIO_RETURN_ERR(r);
|
||||
return set_rf_switch_state(RfSwitchState::Idle);
|
||||
}
|
||||
|
||||
expected<ChipType, error_code> LLCC68::hal_get_chip_type() {
|
||||
@@ -1080,6 +1110,8 @@ expected<unit, error_code> LLCC68::set_cad_params(cad_params_t params) {
|
||||
|
||||
expected<unit, error_code> LLCC68::set_cad() {
|
||||
auto dummy = std::span<uint8_t>{};
|
||||
auto r = set_rf_switch_state(RfSwitchState::RX);
|
||||
APP_RADIO_RETURN_ERR(r);
|
||||
return write_stream(RADIOLIB_SX126X_CMD_SET_CAD, dummy);
|
||||
}
|
||||
|
||||
@@ -1101,21 +1133,56 @@ expected<unit, error_code> LLCC68::set_rx(uint32_t timeout) {
|
||||
return write_stream(RADIOLIB_SX126X_CMD_SET_RX, data);
|
||||
}
|
||||
|
||||
expected<unit, error_code> LLCC68::set_rx_duty_cycle(uint32_t rx_period,
|
||||
uint32_t sleep_period) {
|
||||
if (rx_period > RX_DUTY_CYCLE_PERIOD_MAX ||
|
||||
sleep_period > RX_DUTY_CYCLE_PERIOD_MAX) {
|
||||
return ue(-EINVAL);
|
||||
}
|
||||
auto r = set_rf_switch_state(RfSwitchState::RX);
|
||||
APP_RADIO_RETURN_ERR(r);
|
||||
const uint8_t data[] = {
|
||||
static_cast<uint8_t>((rx_period >> 16) & 0xFF),
|
||||
static_cast<uint8_t>((rx_period >> 8) & 0xFF),
|
||||
static_cast<uint8_t>(rx_period & 0xFF),
|
||||
static_cast<uint8_t>((sleep_period >> 16) & 0xFF),
|
||||
static_cast<uint8_t>((sleep_period >> 8) & 0xFF),
|
||||
static_cast<uint8_t>(sleep_period & 0xFF),
|
||||
};
|
||||
return write_stream(RADIOLIB_SX126X_CMD_SET_RX_DUTY_CYCLE, data);
|
||||
}
|
||||
|
||||
expected<unit, error_code>
|
||||
LLCC68::set_rx_duty_cycle_ms(uint32_t rx_period_ms, uint32_t sleep_period_ms) {
|
||||
const auto rx_period = rx_duty_cycle_period_from_ms(rx_period_ms);
|
||||
const auto sleep_period = rx_duty_cycle_period_from_ms(sleep_period_ms);
|
||||
if (not rx_period or not sleep_period) {
|
||||
return ue(-EINVAL);
|
||||
}
|
||||
return set_rx_duty_cycle(*rx_period, *sleep_period);
|
||||
}
|
||||
|
||||
expected<unit, error_code> LLCC68::set_sleep(sleep_config_t config) {
|
||||
auto c = *reinterpret_cast<const uint8_t *>(&config);
|
||||
const uint8_t data[] = {c};
|
||||
return write_stream(RADIOLIB_SX126X_CMD_SET_SLEEP, data);
|
||||
auto r = write_stream(RADIOLIB_SX126X_CMD_SET_SLEEP, data);
|
||||
APP_RADIO_RETURN_ERR(r);
|
||||
return set_rf_switch_state(RfSwitchState::Idle);
|
||||
}
|
||||
|
||||
expected<unit, error_code> LLCC68::set_tx_continuous_wave() {
|
||||
auto dummy = std::span<uint8_t>{};
|
||||
// const uint8_t dummy[] = {RADIOLIB_SX126X_CMD_NOP};
|
||||
auto r = set_rf_switch_state(RfSwitchState::TX);
|
||||
APP_RADIO_RETURN_ERR(r);
|
||||
return write_stream(RADIOLIB_SX126X_CMD_SET_TX_CONTINUOUS_WAVE, dummy);
|
||||
}
|
||||
|
||||
expected<unit, error_code> LLCC68::set_tx_infinite_preamble() {
|
||||
auto dummy = std::span<uint8_t>{};
|
||||
// const uint8_t dummy[] = {RADIOLIB_SX126X_CMD_NOP};
|
||||
auto r = set_rf_switch_state(RfSwitchState::TX);
|
||||
APP_RADIO_RETURN_ERR(r);
|
||||
return write_stream(RADIOLIB_SX126X_CMD_SET_TX_INFINITE_PREAMBLE, dummy);
|
||||
}
|
||||
|
||||
@@ -1222,7 +1289,9 @@ expected<unit, error_code> LLCC68::hal_modem_init(lora_parameters_t params) {
|
||||
"modem_init::set_modulation_params");
|
||||
APP_RADIO_RETURN_ERR_CTX(set_lora_sync_word(params.sync_word),
|
||||
"modem_init::set_lora_sync_word");
|
||||
APP_RADIO_RETURN_ERR_CTX(set_dio2_as_rf_switch(false),
|
||||
APP_RADIO_RETURN_ERR_CTX(set_dio2_as_rf_switch(
|
||||
config().rf_switch_mode ==
|
||||
LLCC68_RF_SWITCH_DIO2_SINGLE),
|
||||
"modem_init::set_dio2_as_rf_switch");
|
||||
APP_RADIO_RETURN_ERR_CTX(set_rf_frequency(params.frequency_mhz),
|
||||
"modem_init::set_rf_frequency");
|
||||
@@ -1278,7 +1347,9 @@ LLCC68::hal_gfsk_modem_init(gfsk_parameters_t params) {
|
||||
params.broadcast_address.value_or(0)),
|
||||
"gfsk_init::set_address_filtering");
|
||||
}
|
||||
APP_RADIO_RETURN_ERR_CTX(set_dio2_as_rf_switch(false),
|
||||
APP_RADIO_RETURN_ERR_CTX(set_dio2_as_rf_switch(
|
||||
config().rf_switch_mode ==
|
||||
LLCC68_RF_SWITCH_DIO2_SINGLE),
|
||||
"gfsk_init::set_dio2_as_rf_switch");
|
||||
APP_RADIO_RETURN_ERR_CTX(set_rf_frequency(params.frequency_mhz),
|
||||
"gfsk_init::set_rf_frequency");
|
||||
@@ -1330,7 +1401,8 @@ LLCC68::hal_async_flush(lora_parameters_t params) {
|
||||
"tx::set_dio_irq_params");
|
||||
APP_RADIO_RETURN_ERR_CTX(clear_irq_status(irq_params.irqMask),
|
||||
"tx::clear_irq_status");
|
||||
tx_rx_en_pin_set(TxRxPinState::TX);
|
||||
APP_RADIO_RETURN_ERR_CTX(set_rf_switch_state(RfSwitchState::TX),
|
||||
"tx::set_rf_switch");
|
||||
APP_RADIO_RETURN_ERR_CTX(set_tx(), "tx::set_tx_params");
|
||||
auto air = calc_time_on_air(
|
||||
data().tx_xfer_size, params.mod_params.sf, params.mod_params.bw,
|
||||
@@ -1387,7 +1459,8 @@ LLCC68::hal_gfsk_async_flush(gfsk_parameters_t params) {
|
||||
"gfsk_tx::set_dio_irq_params");
|
||||
APP_RADIO_RETURN_ERR_CTX(clear_irq_status(irq_params.irqMask),
|
||||
"gfsk_tx::clear_irq_status");
|
||||
tx_rx_en_pin_set(TxRxPinState::TX);
|
||||
APP_RADIO_RETURN_ERR_CTX(set_rf_switch_state(RfSwitchState::TX),
|
||||
"gfsk_tx::set_rf_switch");
|
||||
APP_RADIO_RETURN_ERR_CTX(set_tx(), "gfsk_tx::set_tx");
|
||||
auto air_estimated =
|
||||
calc_gfsk_time_on_air(params, static_cast<uint8_t>(data().tx_xfer_size));
|
||||
@@ -1431,7 +1504,8 @@ expected<unit, error_code> LLCC68::hal_async_rx(lora_parameters_t params) {
|
||||
APP_RADIO_RETURN_ERR_CTX(clear_irq_status(irq_params.irqMask),
|
||||
"rx::clear_irq_status");
|
||||
|
||||
tx_rx_en_pin_set(TxRxPinState::RX);
|
||||
APP_RADIO_RETURN_ERR_CTX(set_rf_switch_state(RfSwitchState::RX),
|
||||
"rx::set_rf_switch");
|
||||
APP_RADIO_RETURN_ERR_CTX(set_rx(), "rx::set_rx");
|
||||
return unit{};
|
||||
}
|
||||
@@ -1457,7 +1531,8 @@ expected<unit, error_code> LLCC68::hal_gfsk_async_rx(gfsk_parameters_t params) {
|
||||
APP_RADIO_RETURN_ERR_CTX(clear_irq_status(irq_params.irqMask),
|
||||
"gfsk_rx::clear_irq_status");
|
||||
|
||||
tx_rx_en_pin_set(TxRxPinState::RX);
|
||||
APP_RADIO_RETURN_ERR_CTX(set_rf_switch_state(RfSwitchState::RX),
|
||||
"gfsk_rx::set_rf_switch");
|
||||
APP_RADIO_RETURN_ERR_CTX(set_rx(), "gfsk_rx::set_rx");
|
||||
return unit{};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user