Compare commits

...

3 Commits

Author SHA1 Message Date
crosstyan e6f96ea0e3 fix(llcc68): cache detected chip type
Store the detected chip type after the first successful version-register read so repeated radio operations do not keep querying the chip identity.

This preserves Unknown as uncached so transient read failures or unsupported responses can still be retried on a later call.
2026-06-16 19:36:45 +08:00
crosstyan b125dd33b9 style(llcc68): apply shared clang-format
Add a .clang-format file to the LLCC68 submodule so it can be formatted consistently when edited or checked out independently from the parent repository.

Reformat the tracked C and C++ driver sources with the shared style configuration.
2026-06-16 19:36:45 +08:00
crosstyan d382bdfd1e fix(llcc68): apply full radio profile before tx and rx
Reapply packet type, sync word, RF frequency, modulation, packet, CRC, and whitening settings on async TX/RX entry points.

This lets callers switch between preconfigured profiles immediately before a send or listen operation instead of relying on modem-init state.
2026-06-16 19:35:59 +08:00
5 changed files with 2172 additions and 2075 deletions
+25
View File
@@ -0,0 +1,25 @@
# C++ specific configuration (akin to Google's C++ style)
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html#adding-additional-style-options
---
Language: Cpp
BasedOnStyle: LLVM
UseTab: ForContinuationAndIndentation
IndentWidth: 4
TabWidth: 4
AccessModifierOffset: -4
ColumnLimit: 0
NamespaceIndentation: Inner
FixNamespaceComments: false
AllowShortIfStatementsOnASingleLine: WithoutElse
AllowShortLoopsOnASingleLine: true
AllowShortBlocksOnASingleLine: Empty
IndentCaseLabels: false
SortIncludes: Never
AlignConsecutiveMacros: AcrossEmptyLines
AlignConsecutiveAssignments: Consecutive
BreakStringLiterals: true
LineEnding: LF
MaxEmptyLinesToKeep: 2
BreakBeforeBraces: Attach
InsertBraces: true
BreakAfterAttributes: Always
+1
View File
@@ -303,6 +303,7 @@ struct LLCC68 {
/** properties */ /** properties */
const struct device *dev; const struct device *dev;
std::optional<ChipType> cached_chip_type{};
}; };
} // namespace app::driver::llcc68 } // namespace app::driver::llcc68
+2 -1
View File
@@ -1143,7 +1143,8 @@ namespace llcc68 = app::driver::llcc68;
} }
namespace std { namespace std {
template <> struct is_error_code_enum<app::driver::llcc68::Errc> : true_type {}; template <>
struct is_error_code_enum<app::driver::llcc68::Errc> : true_type {};
} // namespace std } // namespace std
#endif /* ECC594CF_EDF0_42B5_8518_0EB3B3583727 */ #endif /* ECC594CF_EDF0_42B5_8518_0EB3B3583727 */
+94 -24
View File
@@ -108,17 +108,17 @@ error_code LLCC68::init() {
// Internal helpers (TU-local) // Internal helpers (TU-local)
namespace { namespace {
// Max payload the radio supports and buffer sizing helpers // Max payload the radio supports and buffer sizing helpers
constexpr size_t kMaxPayload = 32; constexpr size_t kMaxPayload = 32;
constexpr uint64_t ceil_div_u64(uint64_t numerator, uint64_t denominator) { constexpr uint64_t ceil_div_u64(uint64_t numerator, uint64_t denominator) {
if (denominator == 0) { if (denominator == 0) {
return 0; return 0;
} }
return numerator / denominator + (numerator % denominator == 0 ? 0 : 1); return numerator / denominator + (numerator % denominator == 0 ? 0 : 1);
} }
constexpr uint32_t lora_bw_hz(LoRaBandwidth bw) { constexpr uint32_t lora_bw_hz(LoRaBandwidth bw) {
switch (bw) { switch (bw) {
case LoRaBandwidth::BW_7_8: case LoRaBandwidth::BW_7_8:
return 7'810; return 7'810;
@@ -142,9 +142,9 @@ constexpr uint32_t lora_bw_hz(LoRaBandwidth bw) {
return 500'000; return 500'000;
} }
return 0; return 0;
} }
error_code command_status_to_error(status_t st) { error_code command_status_to_error(status_t st) {
switch (st.command_status) { switch (st.command_status) {
case CommandStatus::FAILURE_TO_EXECUTE_COMMAND: case CommandStatus::FAILURE_TO_EXECUTE_COMMAND:
return make_error_code(Errc::FailureToExecuteCommand); return make_error_code(Errc::FailureToExecuteCommand);
@@ -155,9 +155,9 @@ error_code command_status_to_error(status_t st) {
default: default:
return {}; return {};
} }
} }
int wait_for_not_busy(const gpio_dt_spec &busy_gpio, uint16_t timeout_ms) { int wait_for_not_busy(const gpio_dt_spec &busy_gpio, uint16_t timeout_ms) {
if (not device_is_ready(busy_gpio.port)) { if (not device_is_ready(busy_gpio.port)) {
return -ENODEV; return -ENODEV;
} }
@@ -169,7 +169,7 @@ int wait_for_not_busy(const gpio_dt_spec &busy_gpio, uint16_t timeout_ms) {
k_busy_wait(50); // ~50 us poll interval k_busy_wait(50); // ~50 us poll interval
} }
return 0; return 0;
} }
} // namespace } // namespace
expected<unit, error_code> LLCC68::set_rf_switch_state(RfSwitchState state) { expected<unit, error_code> LLCC68::set_rf_switch_state(RfSwitchState state) {
@@ -186,6 +186,7 @@ expected<unit, error_code> LLCC68::set_rf_switch_state(RfSwitchState state) {
}; };
switch (config().rf_switch_mode) { switch (config().rf_switch_mode) {
case LLCC68_RF_SWITCH_DIO2_SINGLE:
case LLCC68_RF_SWITCH_NONE: case LLCC68_RF_SWITCH_NONE:
return unit{}; return unit{};
case LLCC68_RF_SWITCH_GPIO_COMPLEMENTARY: { case LLCC68_RF_SWITCH_GPIO_COMPLEMENTARY: {
@@ -205,10 +206,6 @@ expected<unit, error_code> LLCC68::set_rf_switch_state(RfSwitchState state) {
APP_RADIO_RETURN_ERR(r); APP_RADIO_RETURN_ERR(r);
return set_gpio(*rx, state == RfSwitchState::RX ? 1 : 0); return set_gpio(*rx, state == RfSwitchState::RX ? 1 : 0);
} }
case LLCC68_RF_SWITCH_DIO2_SINGLE:
if (auto rx = rx_enable_gpio()) {
return set_gpio(*rx, 1);
}
return unit{}; return unit{};
default: default:
return ue(-EINVAL); return ue(-EINVAL);
@@ -677,6 +674,10 @@ expected<unit, error_code> LLCC68::set_standby() {
} }
expected<ChipType, error_code> LLCC68::hal_get_chip_type() { expected<ChipType, error_code> LLCC68::hal_get_chip_type() {
if (cached_chip_type.has_value()) {
return *cached_chip_type;
}
constexpr auto SX1262_CHIP_TYPE = "SX1262"; constexpr auto SX1262_CHIP_TYPE = "SX1262";
constexpr auto LLCC68_CHIP_TYPE = "LLCC68"; constexpr auto LLCC68_CHIP_TYPE = "LLCC68";
constexpr auto SX1261_CHIP_TYPE = "SX1261"; constexpr auto SX1261_CHIP_TYPE = "SX1261";
@@ -687,23 +688,31 @@ expected<ChipType, error_code> LLCC68::hal_get_chip_type() {
auto r = read_register(RADIOLIB_SX126X_REG_VERSION_STRING, version_buf); auto r = read_register(RADIOLIB_SX126X_REG_VERSION_STRING, version_buf);
APP_RADIO_RETURN_ERR(r); APP_RADIO_RETURN_ERR(r);
LOG_HEXDUMP_DBG(version, sizeof(version), "version dump"); LOG_HEXDUMP_DBG(version, sizeof(version), "version dump");
ChipType chip_type = ChipType::Unknown;
if (strncmp(version, LLCC68_CHIP_TYPE, 6) == 0) { if (strncmp(version, LLCC68_CHIP_TYPE, 6) == 0) {
return ChipType::LLCC68; chip_type = ChipType::LLCC68;
} else if (strncmp(version, SX1261_CHIP_TYPE, 6) == 0) {
chip_type = ChipType::SX1261;
} else if (strncmp(version, SX1262_CHIP_TYPE, 6) == 0) {
chip_type = ChipType::SX1262;
} }
if (strncmp(version, SX1261_CHIP_TYPE, 6) == 0) { if (chip_type != ChipType::Unknown) {
return ChipType::SX1261; cached_chip_type = chip_type;
} }
if (strncmp(version, SX1262_CHIP_TYPE, 6) == 0) { return chip_type;
return ChipType::SX1262;
}
return ChipType::Unknown;
} }
expected<unit, error_code> LLCC68::set_dio_irq_params(irq_params_t params) { expected<unit, error_code> LLCC68::set_dio_irq_params(irq_params_t params) {
const uint8_t data[8] = { const uint8_t data[8] = {
params.irqMask.msb(), params.irqMask.lsb(), params.dio1Mask.msb(), params.irqMask.msb(),
params.dio1Mask.lsb(), params.dio2Mask.msb(), params.dio2Mask.lsb(), params.irqMask.lsb(),
params.dio3Mask.msb(), params.dio3Mask.lsb(), params.dio1Mask.msb(),
params.dio1Mask.lsb(),
params.dio2Mask.msb(),
params.dio2Mask.lsb(),
params.dio3Mask.msb(),
params.dio3Mask.lsb(),
}; };
return write_stream(RADIOLIB_SX126X_CMD_SET_DIO_IRQ_PARAMS, data); return write_stream(RADIOLIB_SX126X_CMD_SET_DIO_IRQ_PARAMS, data);
} }
@@ -1369,6 +1378,11 @@ LLCC68::hal_async_flush(lora_parameters_t params) {
return ue(Errc::InvalidState); return ue(Errc::InvalidState);
} }
APP_RADIO_RETURN_ERR_CTX(set_standby(), "tx::standby"); APP_RADIO_RETURN_ERR_CTX(set_standby(), "tx::standby");
APP_RADIO_RETURN_ERR_CTX(set_packet_type_lora(), "tx::set_packet_type");
APP_RADIO_RETURN_ERR_CTX(set_lora_sync_word(params.sync_word),
"tx::set_lora_sync_word");
APP_RADIO_RETURN_ERR_CTX(set_rf_frequency(params.frequency_mhz),
"tx::set_rf_frequency");
APP_RADIO_RETURN_ERR_CTX( APP_RADIO_RETURN_ERR_CTX(
set_modulation_params(params.mod_params.sf, params.mod_params.bw, set_modulation_params(params.mod_params.sf, params.mod_params.bw,
params.mod_params.cr, params.mod_params.cr,
@@ -1430,10 +1444,35 @@ LLCC68::hal_gfsk_async_flush(gfsk_parameters_t params) {
if (data().tx_xfer_size == 0 || data().tx_xfer_size > MAX_BUFFER_PAYLOAD) { if (data().tx_xfer_size == 0 || data().tx_xfer_size > MAX_BUFFER_PAYLOAD) {
return ue(-EINVAL); return ue(-EINVAL);
} }
if (params.sync_word_length > params.sync_word.size() ||
params.packet_params.sync_length_bits > params.sync_word.size() * 8) {
return ue(-EINVAL);
}
APP_RADIO_RETURN_ERR_CTX(set_standby(), "gfsk_tx::standby"); APP_RADIO_RETURN_ERR_CTX(set_standby(), "gfsk_tx::standby");
APP_RADIO_RETURN_ERR_CTX(set_packet_type_gfsk(),
"gfsk_tx::set_packet_type");
APP_RADIO_RETURN_ERR_CTX(set_rf_frequency(params.frequency_mhz),
"gfsk_tx::set_rf_frequency");
APP_RADIO_RETURN_ERR_CTX(set_gfsk_modulation_params(params.mod_params), APP_RADIO_RETURN_ERR_CTX(set_gfsk_modulation_params(params.mod_params),
"gfsk_tx::set_modulation_params"); "gfsk_tx::set_modulation_params");
APP_RADIO_RETURN_ERR_CTX(
set_gfsk_sync_word(std::span<const uint8_t>{params.sync_word.data(),
params.sync_word_length}),
"gfsk_tx::set_sync_word");
APP_RADIO_RETURN_ERR_CTX(set_gfsk_crc_seed(params.crc_seed),
"gfsk_tx::set_crc_seed");
APP_RADIO_RETURN_ERR_CTX(set_gfsk_crc_polynomial(params.crc_polynomial),
"gfsk_tx::set_crc_polynomial");
APP_RADIO_RETURN_ERR_CTX(set_gfsk_whitening_seed(params.whitening_seed),
"gfsk_tx::set_whitening_seed");
if (params.packet_params.address_filtering !=
GfskAddressFiltering::Disabled) {
APP_RADIO_RETURN_ERR_CTX(
set_gfsk_address_filtering(params.node_address.value_or(0),
params.broadcast_address.value_or(0)),
"gfsk_tx::set_address_filtering");
}
auto packet_params = params.packet_params; auto packet_params = params.packet_params;
packet_params.payload_length = static_cast<uint8_t>(data().tx_xfer_size); packet_params.payload_length = static_cast<uint8_t>(data().tx_xfer_size);
APP_RADIO_RETURN_ERR_CTX(set_gfsk_packet_params(packet_params), APP_RADIO_RETURN_ERR_CTX(set_gfsk_packet_params(packet_params),
@@ -1477,6 +1516,11 @@ LLCC68::hal_gfsk_async_transmit(std::span<const uint8_t> data,
expected<unit, error_code> LLCC68::hal_async_rx(lora_parameters_t params) { expected<unit, error_code> LLCC68::hal_async_rx(lora_parameters_t params) {
APP_RADIO_RETURN_ERR_CTX(set_standby(), "rx::standby"); APP_RADIO_RETURN_ERR_CTX(set_standby(), "rx::standby");
APP_RADIO_RETURN_ERR_CTX(set_packet_type_lora(), "rx::set_packet_type");
APP_RADIO_RETURN_ERR_CTX(set_lora_sync_word(params.sync_word),
"rx::set_lora_sync_word");
APP_RADIO_RETURN_ERR_CTX(set_rf_frequency(params.frequency_mhz),
"rx::set_rf_frequency");
APP_RADIO_RETURN_ERR_CTX(set_buffer_base_address(), APP_RADIO_RETURN_ERR_CTX(set_buffer_base_address(),
"rx::set_buffer_base_address"); "rx::set_buffer_base_address");
@@ -1511,11 +1555,37 @@ expected<unit, error_code> LLCC68::hal_async_rx(lora_parameters_t params) {
} }
expected<unit, error_code> LLCC68::hal_gfsk_async_rx(gfsk_parameters_t params) { expected<unit, error_code> LLCC68::hal_gfsk_async_rx(gfsk_parameters_t params) {
if (params.sync_word_length > params.sync_word.size() ||
params.packet_params.sync_length_bits > params.sync_word.size() * 8) {
return ue(-EINVAL);
}
APP_RADIO_RETURN_ERR_CTX(set_standby(), "gfsk_rx::standby"); APP_RADIO_RETURN_ERR_CTX(set_standby(), "gfsk_rx::standby");
APP_RADIO_RETURN_ERR_CTX(set_packet_type_gfsk(),
"gfsk_rx::set_packet_type");
APP_RADIO_RETURN_ERR_CTX(set_rf_frequency(params.frequency_mhz),
"gfsk_rx::set_rf_frequency");
APP_RADIO_RETURN_ERR_CTX(set_buffer_base_address(), APP_RADIO_RETURN_ERR_CTX(set_buffer_base_address(),
"gfsk_rx::set_buffer_base_address"); "gfsk_rx::set_buffer_base_address");
APP_RADIO_RETURN_ERR_CTX(set_gfsk_modulation_params(params.mod_params), APP_RADIO_RETURN_ERR_CTX(set_gfsk_modulation_params(params.mod_params),
"gfsk_rx::set_modulation_params"); "gfsk_rx::set_modulation_params");
APP_RADIO_RETURN_ERR_CTX(
set_gfsk_sync_word(std::span<const uint8_t>{params.sync_word.data(),
params.sync_word_length}),
"gfsk_rx::set_sync_word");
APP_RADIO_RETURN_ERR_CTX(set_gfsk_crc_seed(params.crc_seed),
"gfsk_rx::set_crc_seed");
APP_RADIO_RETURN_ERR_CTX(set_gfsk_crc_polynomial(params.crc_polynomial),
"gfsk_rx::set_crc_polynomial");
APP_RADIO_RETURN_ERR_CTX(set_gfsk_whitening_seed(params.whitening_seed),
"gfsk_rx::set_whitening_seed");
if (params.packet_params.address_filtering !=
GfskAddressFiltering::Disabled) {
APP_RADIO_RETURN_ERR_CTX(
set_gfsk_address_filtering(params.node_address.value_or(0),
params.broadcast_address.value_or(0)),
"gfsk_rx::set_address_filtering");
}
APP_RADIO_RETURN_ERR_CTX(set_gfsk_packet_params(params.packet_params), APP_RADIO_RETURN_ERR_CTX(set_gfsk_packet_params(params.packet_params),
"gfsk_rx::set_packet_params"); "gfsk_rx::set_packet_params");