Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 32a0c80ab0 | |||
| d8db9e1eb0 | |||
| ce56757dac |
@@ -14,6 +14,17 @@ config LLCC68_INIT_PRIORITY
|
||||
The priority of the LLCC68 initialization. Lower numbers indicate
|
||||
higher priority.
|
||||
|
||||
config LLCC68_MAX_PAYLOAD_LENGTH
|
||||
int "LLCC68 maximum payload length"
|
||||
default 128
|
||||
range 1 255
|
||||
help
|
||||
Maximum radio payload length accepted by the LLCC68 driver.
|
||||
|
||||
The LLCC68 packet length fields support up to 255 bytes. Keep this
|
||||
lower when the application has a known payload ceiling to reduce RAM
|
||||
used by the driver's SPI staging buffers.
|
||||
|
||||
config LLCC68_ALWAYS_USE_SX1262_HIGH_PA
|
||||
bool "LLCC68 Always Use SX1262 High Power Amplifier"
|
||||
default y
|
||||
|
||||
@@ -46,7 +46,7 @@ int llcc68_init(const struct device *dev) {
|
||||
static struct llcc68_data llcc68_data_##inst; \
|
||||
static const struct llcc68_config llcc68_config_##inst = \
|
||||
{ \
|
||||
.spi = SPI_DT_SPEC_INST_GET(inst, SPI_WORD_SET(8) | SPI_TRANSFER_MSB, 100), \
|
||||
.spi = SPI_DT_SPEC_INST_GET(inst, SPI_WORD_SET(8) | SPI_TRANSFER_MSB), \
|
||||
.reset_gpio = GPIO_DT_SPEC_INST_GET(inst, reset_gpios), \
|
||||
.busy_gpio = GPIO_DT_SPEC_INST_GET(inst, busy_gpios), \
|
||||
.dio1_gpio = GPIO_DT_SPEC_INST_GET(inst, dio1_gpios), \
|
||||
|
||||
@@ -47,3 +47,9 @@ properties:
|
||||
description: |
|
||||
Antenna switch RX enable GPIO. If set, the driver tracks the
|
||||
state of the radio and controls the RF switch.
|
||||
|
||||
spi-cs-setup-delay-ns:
|
||||
default: 100000
|
||||
|
||||
spi-cs-hold-delay-ns:
|
||||
default: 100000
|
||||
|
||||
+3
-2
@@ -11,7 +11,7 @@
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
|
||||
namespace app::driver::llcc68 {
|
||||
constexpr size_t MAX_BUFFER_PAYLOAD = 128;
|
||||
constexpr size_t MAX_BUFFER_PAYLOAD = CONFIG_LLCC68_MAX_PAYLOAD_LENGTH;
|
||||
constexpr uint32_t TIMEOUT_NONE = 0;
|
||||
constexpr uint32_t TIMEOUT_INF = 0xffffffff;
|
||||
|
||||
@@ -41,12 +41,13 @@ constexpr auto DEFAULT_BUSY_TIMEOUT_MS = 100;
|
||||
* @param preamble_length Preamble length in symbols
|
||||
* @param header_type Header type (implicit or explicit).
|
||||
* @param crc_type CRC type (none or 16-bit)
|
||||
* @param ldro_on Low data rate optimization setting sent to the radio
|
||||
*/
|
||||
constexpr airtime_t calc_time_on_air(uint8_t len, uint8_t sf, LoRaBandwidth bw,
|
||||
LoRaCodingRate cr,
|
||||
uint16_t preamble_length,
|
||||
LoRaHeaderType header_type,
|
||||
LoRaCrcType crc_type);
|
||||
LoRaCrcType crc_type, bool ldro_on);
|
||||
|
||||
struct LLCC68 {
|
||||
/** trivial getter */
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
namespace app::driver::llcc68 {
|
||||
struct LLCC68;
|
||||
using airtime_t = std::chrono::duration<uint32_t, std::micro>;
|
||||
using airtime_t = std::chrono::microseconds;
|
||||
using error_code = std::error_code;
|
||||
|
||||
enum class Errc : uint8_t {
|
||||
@@ -376,6 +376,55 @@ enum class GfskRxBandwidth : uint8_t {
|
||||
Bw467000 = 0x09,
|
||||
};
|
||||
|
||||
inline uint32_t rx_bandwidth_hz(GfskRxBandwidth bandwidth) {
|
||||
using enum GfskRxBandwidth;
|
||||
switch (bandwidth) {
|
||||
case Bw4800:
|
||||
return 4800;
|
||||
case Bw5800:
|
||||
return 5800;
|
||||
case Bw7300:
|
||||
return 7300;
|
||||
case Bw9700:
|
||||
return 9700;
|
||||
case Bw11700:
|
||||
return 11700;
|
||||
case Bw14600:
|
||||
return 14600;
|
||||
case Bw19500:
|
||||
return 19500;
|
||||
case Bw23400:
|
||||
return 23400;
|
||||
case Bw29300:
|
||||
return 29300;
|
||||
case Bw39000:
|
||||
return 39000;
|
||||
case Bw46900:
|
||||
return 46900;
|
||||
case Bw58600:
|
||||
return 58600;
|
||||
case Bw78200:
|
||||
return 78200;
|
||||
case Bw93800:
|
||||
return 93800;
|
||||
case Bw117300:
|
||||
return 117300;
|
||||
case Bw156200:
|
||||
return 156200;
|
||||
case Bw187200:
|
||||
return 187200;
|
||||
case Bw234300:
|
||||
return 234300;
|
||||
case Bw312000:
|
||||
return 312000;
|
||||
case Bw373600:
|
||||
return 373600;
|
||||
case Bw467000:
|
||||
return 467000;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum class GfskPreambleDetector : uint8_t {
|
||||
Off = 0x00,
|
||||
Bits8 = 0x04,
|
||||
@@ -478,7 +527,7 @@ struct gfsk_parameters_t {
|
||||
}
|
||||
|
||||
static constexpr gfsk_parameters_t
|
||||
Gmsk100kFixed6(freq_t frequency_mhz = 434.18,
|
||||
Gfsk100kFixed6(freq_t frequency_mhz = 434.18,
|
||||
tx_params_t tx_params = tx_params_t::Default()) {
|
||||
return {
|
||||
.mod_params =
|
||||
@@ -1048,7 +1097,7 @@ struct transmit_result {
|
||||
error_code post_action();
|
||||
|
||||
LLCC68 *self{};
|
||||
std::chrono::milliseconds airtime_estimated;
|
||||
airtime_t airtime_estimated;
|
||||
};
|
||||
} // namespace app::driver::llcc68
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <zephyr/types.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define LLCC68_MAX_BUFFER_PAYLOAD 128
|
||||
#define LLCC68_MAX_BUFFER_PAYLOAD CONFIG_LLCC68_MAX_PAYLOAD_LENGTH
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
||||
+79
-27
@@ -111,6 +111,39 @@ namespace {
|
||||
// Max payload the radio supports and buffer sizing helpers
|
||||
constexpr size_t kMaxPayload = 32;
|
||||
|
||||
constexpr uint64_t ceil_div_u64(uint64_t numerator, uint64_t denominator) {
|
||||
if (denominator == 0) {
|
||||
return 0;
|
||||
}
|
||||
return numerator / denominator + (numerator % denominator == 0 ? 0 : 1);
|
||||
}
|
||||
|
||||
constexpr uint32_t lora_bw_hz(LoRaBandwidth bw) {
|
||||
switch (bw) {
|
||||
case LoRaBandwidth::BW_7_8:
|
||||
return 7'810;
|
||||
case LoRaBandwidth::BW_10_4:
|
||||
return 10'420;
|
||||
case LoRaBandwidth::BW_15_6:
|
||||
return 15'630;
|
||||
case LoRaBandwidth::BW_20_8:
|
||||
return 20'830;
|
||||
case LoRaBandwidth::BW_31_25:
|
||||
return 31'250;
|
||||
case LoRaBandwidth::BW_41_7:
|
||||
return 41'670;
|
||||
case LoRaBandwidth::BW_62_5:
|
||||
return 62'500;
|
||||
case LoRaBandwidth::BW_125_0:
|
||||
return 125'000;
|
||||
case LoRaBandwidth::BW_250_0:
|
||||
return 250'000;
|
||||
case LoRaBandwidth::BW_500_0:
|
||||
return 500'000;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
error_code command_status_to_error(status_t st) {
|
||||
switch (st.command_status) {
|
||||
case CommandStatus::FAILURE_TO_EXECUTE_COMMAND:
|
||||
@@ -163,47 +196,57 @@ constexpr airtime_t calc_time_on_air(uint8_t len, uint8_t sf, LoRaBandwidth bw,
|
||||
LoRaCodingRate cr,
|
||||
uint16_t preamble_length,
|
||||
LoRaHeaderType header_type,
|
||||
LoRaCrcType crc_type) {
|
||||
// everything is in microseconds to allow integer arithmetic
|
||||
// some constants have .25, these are multiplied by 4, and have _x4 postfix to
|
||||
// indicate that fact
|
||||
const auto bw_ = bw_khz(bw);
|
||||
const auto ubw = static_cast<uint32_t>(bw_ * 10);
|
||||
const uint32_t symbolLength_us =
|
||||
(static_cast<uint32_t>(1000 * 10) << sf) / ubw;
|
||||
LoRaCrcType crc_type, bool ldro_on) {
|
||||
// Everything is in microseconds to allow integer arithmetic. Some constants
|
||||
// have .25, these are multiplied by 4 and have an _x4 postfix.
|
||||
const uint32_t bw_hz = lora_bw_hz(bw);
|
||||
if (bw_hz == 0) {
|
||||
return airtime_t{0};
|
||||
}
|
||||
|
||||
uint8_t sfCoeff1_x4 = 17; // (4.25 * 4)
|
||||
uint8_t sfCoeff2 = 8;
|
||||
if (sf == 5 || sf == 6) {
|
||||
sfCoeff1_x4 = 25; // 6.25 * 4
|
||||
sfCoeff2 = 0;
|
||||
}
|
||||
uint8_t sfDivisor = 4 * sf;
|
||||
if (symbolLength_us >= 16000) {
|
||||
sfDivisor = 4 * (sf - 2);
|
||||
const uint8_t ldro_sf_offset = ldro_on ? 2 : 0;
|
||||
if (sf <= ldro_sf_offset) {
|
||||
return airtime_t{0};
|
||||
}
|
||||
constexpr int8_t bitsPerCrc = 16;
|
||||
int8_t N_symbol_header =
|
||||
header_type == RADIOLIB_SX126X_LORA_HEADER_EXPLICIT ? 20 : 0;
|
||||
const uint8_t sfDivisor = 4 * (sf - ldro_sf_offset);
|
||||
const int16_t header_bits =
|
||||
header_type == LoRaHeaderType::HEADER_EXPLICIT ? 20 : 0;
|
||||
|
||||
// numerator of equation in section 6.1.4 of SX1268 datasheet v1.1 (might
|
||||
// not actually be bitcount, but it has len * 8)
|
||||
auto bit_count = static_cast<int16_t>(
|
||||
static_cast<int16_t>(8) * static_cast<int16_t>(len) +
|
||||
static_cast<int16_t>(crc_type * bitsPerCrc) -
|
||||
static_cast<int16_t>(crc_type == LoRaCrcType::CRC_ON ? 16 : 0) -
|
||||
static_cast<int16_t>(4 * sf) + static_cast<int16_t>(sfCoeff2) +
|
||||
static_cast<int16_t>(N_symbol_header));
|
||||
header_bits);
|
||||
bit_count = std::max<int16_t>(bit_count, 0);
|
||||
// add (sfDivisor) - 1 to the numerator to give integer CEIL(...)
|
||||
const uint16_t nPreCodedSymbols = (bit_count + (sfDivisor - 1)) / (sfDivisor);
|
||||
|
||||
const auto de = std::get<1>(cr_to_ratio(cr));
|
||||
const auto cr_denominator = std::get<1>(cr_to_ratio(cr));
|
||||
// preamble can be 65k, therefore nSymbol_x4 needs to be 32 bit
|
||||
const uint32_t nSymbol_x4 =
|
||||
(preamble_length + 8) * 4 + sfCoeff1_x4 + nPreCodedSymbols * de * 4;
|
||||
(preamble_length + 8) * 4 + sfCoeff1_x4 +
|
||||
nPreCodedSymbols * cr_denominator * 4;
|
||||
|
||||
return airtime_t{symbolLength_us * nSymbol_x4 / 4};
|
||||
const uint64_t time_us = ceil_div_u64(
|
||||
(static_cast<uint64_t>(1'000'000) << sf) * nSymbol_x4,
|
||||
static_cast<uint64_t>(bw_hz) * 4U);
|
||||
return airtime_t{static_cast<airtime_t::rep>(time_us)};
|
||||
}
|
||||
|
||||
static_assert(calc_time_on_air(6, 7, LoRaBandwidth::BW_125_0,
|
||||
LoRaCodingRate::CR_4_5, 8,
|
||||
LoRaHeaderType::HEADER_IMPLICIT,
|
||||
LoRaCrcType::CRC_OFF, false) ==
|
||||
airtime_t{25'856});
|
||||
|
||||
// SPI helpers implementing the LLCC68 wire protocol
|
||||
expected<unit, error_code> LLCC68::read_stream(uint8_t cmd,
|
||||
std::span<uint8_t> data,
|
||||
@@ -672,7 +715,7 @@ static constexpr uint8_t gfsk_crc_len_bytes(GfskCrcType crc_type) {
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr std::chrono::milliseconds
|
||||
static constexpr airtime_t
|
||||
calc_gfsk_time_on_air(gfsk_parameters_t params, uint8_t payload_len) {
|
||||
const auto address_bits =
|
||||
params.packet_params.address_filtering == GfskAddressFiltering::Disabled
|
||||
@@ -688,9 +731,12 @@ calc_gfsk_time_on_air(gfsk_parameters_t params, uint8_t payload_len) {
|
||||
(static_cast<uint32_t>(payload_len) +
|
||||
gfsk_crc_len_bytes(params.packet_params.crc_type)) *
|
||||
8U;
|
||||
const uint32_t ms = (bits * 1000U + params.mod_params.bitrate_bps - 1U) /
|
||||
params.mod_params.bitrate_bps;
|
||||
return std::chrono::milliseconds{ms};
|
||||
if (params.mod_params.bitrate_bps == 0) {
|
||||
return airtime_t{0};
|
||||
}
|
||||
const uint64_t us = ceil_div_u64(static_cast<uint64_t>(bits) * 1'000'000U,
|
||||
params.mod_params.bitrate_bps);
|
||||
return airtime_t{static_cast<airtime_t::rep>(us)};
|
||||
}
|
||||
|
||||
static expected<unit, error_code>
|
||||
@@ -1250,6 +1296,11 @@ LLCC68::hal_async_flush(lora_parameters_t params) {
|
||||
return ue(Errc::InvalidState);
|
||||
}
|
||||
APP_RADIO_RETURN_ERR_CTX(set_standby(), "tx::standby");
|
||||
APP_RADIO_RETURN_ERR_CTX(
|
||||
set_modulation_params(params.mod_params.sf, params.mod_params.bw,
|
||||
params.mod_params.cr,
|
||||
params.mod_params.ldr_optimize),
|
||||
"tx::set_modulation_params");
|
||||
APP_RADIO_RETURN_ERR_CTX(set_packet_params(params.packet_params.preamble_len,
|
||||
data().tx_xfer_size,
|
||||
params.packet_params.crc_type,
|
||||
@@ -1278,10 +1329,9 @@ LLCC68::hal_async_flush(lora_parameters_t params) {
|
||||
auto air = calc_time_on_air(
|
||||
data().tx_xfer_size, params.mod_params.sf, params.mod_params.bw,
|
||||
params.mod_params.cr, params.packet_params.preamble_len,
|
||||
params.packet_params.hdr_type, params.packet_params.crc_type);
|
||||
auto air_estimated =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(air);
|
||||
return transmit_result{this, air_estimated};
|
||||
params.packet_params.hdr_type, params.packet_params.crc_type,
|
||||
params.mod_params.ldr_optimize == LoRaLowDataRateType::LDR_ON);
|
||||
return transmit_result{this, air};
|
||||
}
|
||||
|
||||
expected<transmit_result, error_code>
|
||||
@@ -1304,6 +1354,8 @@ LLCC68::hal_gfsk_async_flush(gfsk_parameters_t params) {
|
||||
}
|
||||
|
||||
APP_RADIO_RETURN_ERR_CTX(set_standby(), "gfsk_tx::standby");
|
||||
APP_RADIO_RETURN_ERR_CTX(set_gfsk_modulation_params(params.mod_params),
|
||||
"gfsk_tx::set_modulation_params");
|
||||
auto packet_params = params.packet_params;
|
||||
packet_params.payload_length = static_cast<uint8_t>(data().tx_xfer_size);
|
||||
APP_RADIO_RETURN_ERR_CTX(set_gfsk_packet_params(packet_params),
|
||||
|
||||
Reference in New Issue
Block a user