This commit is contained in:
2025-05-14 12:17:06 +08:00
commit d24d9d5dda
16 changed files with 5955 additions and 0 deletions

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
*.pdf filter=lfs diff=lfs merge=lfs -text

17
CMakeLists.txt Normal file
View File

@ -0,0 +1,17 @@
idf_component_register(
SRCS
src/llcc68.cpp
src/hal_spi.cpp
INCLUDE_DIRS
inc/
REQUIRES
driver
app_utils
app_constant
)
# This option would make LLCC68 ignore `SPI_CMD_INVALID` error.
option(APP_SPI_DISABLE_INVALID_STATUS_CHECK "Disable invalid status check" ON)
if (APP_SPI_DISABLE_INVALID_STATUS_CHECK)
target_compile_definitions(${COMPONENT_LIB} PUBLIC APP_SPI_DISABLE_INVALID_STATUS_CHECK)
endif()

19
README.md Normal file
View File

@ -0,0 +1,19 @@
# LLCC68 driver
in-house driver for LLCC68 that should NOT be used other than for LLCC68.
Internal buffer, C++ 20 features is used.
## Usage
See [app_const_llcc68_template.hpp](inc/template/app_const_llcc68_template.hpp)
## TODO
- [ ] Long-Range Frequency Hopping Spread Spectrum (LR-FHSS)
- [ ] cleanup interface for public use
## See
- [Lora-net/sx126x_driver](https://github.com/Lora-net/sx126x_driver)
- [Lora-net/SWDM001](https://github.com/Lora-net/SWDM001)
- [jgromes/RadioLib](https://github.com/jgromes/RadioLib/tree/master/src/modules/SX126x)

1
docs/.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
*.{pdf,PDF} filter=lfs diff=lfs merge=lfs -text

3082
docs/DS_LLCC68_V1.0.md Normal file

File diff suppressed because it is too large Load Diff

BIN
docs/DS_LLCC68_V1.0.pdf LFS Normal file

Binary file not shown.

BIN
docs/DS_SX1261-2_V2_1.pdf LFS Normal file

Binary file not shown.

105
inc/hal_error.hpp Normal file
View File

@ -0,0 +1,105 @@
#ifndef D4D45A87_A012_494D_90D2_B4AE0DDE2487
#define D4D45A87_A012_494D_90D2_B4AE0DDE2487
#include <array>
#include <tuple>
#define APP_ERR_TBL_IT(err) {err, #err}
namespace app::driver::hal::error {
using t = int;
// same definition as esp_err.h
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/error-codes.html
constexpr t OK = 0;
constexpr t FAILED = -1;
constexpr t INVALID_ARG = 0x102; /*!< Invalid argument */
constexpr t INVALID_STATE = 0x103; /*!< Invalid state */
constexpr t INVALID_SIZE = 0x104; /*!< Invalid size */
constexpr t NOT_FOUND = 0x105; /*!< Requested resource not found */
constexpr t NOT_SUPPORTED = 0x106; /*!< Operation or feature not supported */
constexpr t TIMEOUT = 0x107; /*!< Operation timed out */
constexpr t INVALID_RESPONSE = 0x108; /*!< Received response was invalid */
constexpr t INVALID_CRC = 0x109; /*!< CRC or checksum was invalid */
constexpr t INVALID_VERSION = 0x10A; /*!< Version was invalid */
constexpr t INVALID_MAC = 0x10B; /*!< MAC address was invalid */
constexpr t NOT_FINISHED = 0x10C; /*!< Operation has not fully completed */
constexpr t NOT_ALLOWED = 0x10D; /*!< Operation is not allowed */
// new defined generic error codes
constexpr t GENERIC_ERR_BASE = 0x120;
constexpr t AGAIN = GENERIC_ERR_BASE + 1; /*!< Operation failed, retry */
constexpr t BUSY = GENERIC_ERR_BASE + 2; /*!< Busy */
constexpr t SPI_ERR_BASE = 0x1'2000;
// A transaction from host took too long to complete and triggered an internal watchdog.
// The watchdog mechanism can be disabled by host; it is meant to ensure all outcomes are flagged to the host MCU.
constexpr t SPI_TIMEOUT = SPI_ERR_BASE + 2;
// Processor was unable to process command either because of an invalid opcode or
// because an incorrect number of parameters has been provided.
constexpr t SPI_CMD_INVALID = SPI_ERR_BASE + 3;
// The command was successfully processed, however the chip could not execute the command;
// for instance it was unable to enter the specified device mode or send the requested data
constexpr t SPI_CMD_FAILED = SPI_ERR_BASE + 4;
constexpr t RADIO_ERR_BASE = 0x1'3000;
constexpr t RADIO_CHIP_NOT_FOUND = RADIO_ERR_BASE + 1;
constexpr t RADIO_INVALID_TCXO_VOLTAGE = RADIO_ERR_BASE + 2;
constexpr t RADIO_INVALID_CODING_RATE = RADIO_ERR_BASE + 3;
constexpr t RADIO_INVALID_SPREADING_FACTOR = RADIO_ERR_BASE + 4;
constexpr t RADIO_INVALID_BANDWIDTH = RADIO_ERR_BASE + 5;
constexpr t RADIO_INVALID_FREQUENCY = RADIO_ERR_BASE + 6;
constexpr t RADIO_INVALID_OUTPUT_POWER = RADIO_ERR_BASE + 7;
constexpr t RADIO_INVALID_CAD_RESULT = RADIO_ERR_BASE + 8;
constexpr t RADIO_WRONG_MODERN = RADIO_ERR_BASE + 9;
constexpr t RADIO_RX_TIMEOUT = RADIO_ERR_BASE + 10;
constexpr t RADIO_CRC_MISMATCH = RADIO_ERR_BASE + 11;
constexpr t RADIO_BUSY_TX = RADIO_ERR_BASE + 12;
constexpr auto error_table = std::to_array<std::tuple<t, const char *>>(
{
APP_ERR_TBL_IT(OK),
APP_ERR_TBL_IT(FAILED),
APP_ERR_TBL_IT(INVALID_ARG),
APP_ERR_TBL_IT(INVALID_STATE),
APP_ERR_TBL_IT(INVALID_SIZE),
APP_ERR_TBL_IT(NOT_FOUND),
APP_ERR_TBL_IT(NOT_SUPPORTED),
APP_ERR_TBL_IT(TIMEOUT),
APP_ERR_TBL_IT(INVALID_RESPONSE),
APP_ERR_TBL_IT(INVALID_CRC),
APP_ERR_TBL_IT(INVALID_VERSION),
APP_ERR_TBL_IT(AGAIN),
APP_ERR_TBL_IT(BUSY),
APP_ERR_TBL_IT(SPI_TIMEOUT),
APP_ERR_TBL_IT(SPI_CMD_INVALID),
APP_ERR_TBL_IT(SPI_CMD_FAILED),
APP_ERR_TBL_IT(RADIO_CHIP_NOT_FOUND),
APP_ERR_TBL_IT(RADIO_INVALID_TCXO_VOLTAGE),
APP_ERR_TBL_IT(RADIO_INVALID_CODING_RATE),
APP_ERR_TBL_IT(RADIO_INVALID_SPREADING_FACTOR),
APP_ERR_TBL_IT(RADIO_INVALID_BANDWIDTH),
APP_ERR_TBL_IT(RADIO_INVALID_FREQUENCY),
APP_ERR_TBL_IT(RADIO_INVALID_OUTPUT_POWER),
APP_ERR_TBL_IT(RADIO_INVALID_CAD_RESULT),
APP_ERR_TBL_IT(RADIO_WRONG_MODERN),
APP_ERR_TBL_IT(RADIO_RX_TIMEOUT),
APP_ERR_TBL_IT(RADIO_CRC_MISMATCH),
APP_ERR_TBL_IT(RADIO_BUSY_TX),
});
inline const char *err_to_str(t err) {
for (const auto &[code, name] : error_table) {
if (code == err) {
return name;
}
}
return "UNKNOWN";
}
}
#undef APP_ERR_TBL_IT
#endif /* D4D45A87_A012_494D_90D2_B4AE0DDE2487 */

33
inc/hal_gpio.hpp Normal file
View File

@ -0,0 +1,33 @@
//
// Created by Kurosu Chan on 2024/1/15.
//
#ifndef B0AACD4A_5B6F_438C_B35C_C49E28A07356
#define B0AACD4A_5B6F_438C_B35C_C49E28A07356
#include <driver/gpio.h>
namespace app::driver::hal::gpio {
using pin_t = gpio_num_t;
using mode_t = gpio_mode_t;
enum class Mode {
INPUT = GPIO_MODE_INPUT,
OUTPUT = GPIO_MODE_OUTPUT,
};
constexpr bool HIGH = true;
constexpr bool LOW = false;
inline void digital_write(const pin_t pin, const bool val) {
gpio_set_level(pin, val);
}
inline bool digital_read(const pin_t pin) { return gpio_get_level(pin); }
inline void set_mode(const pin_t pin, const Mode mode) {
gpio_set_direction(pin, static_cast<gpio_mode_t>(mode));
}
} // namespace app::driver::hal::gpio
#endif /* B0AACD4A_5B6F_438C_B35C_C49E28A07356 */

118
inc/hal_spi.hpp Normal file
View File

@ -0,0 +1,118 @@
//
// Created by Kurosu Chan on 2024/1/17.
//
#ifndef B7431E31_4075_4B09_B4B3_EAD0234EFB40
#define B7431E31_4075_4B09_B4B3_EAD0234EFB40
#include <cstdint>
#include <span>
#include "radiolib_definitions.hpp"
#include "llcc68_definitions.hpp"
#include "utils/app_result.hpp"
#include "hal_error.hpp"
#include "app_const_llcc68.hpp"
namespace app::driver::hal::spi {
template <typename T, typename E>
using Result = app::utils::Result<T, E>;
using Unit = app::utils::Unit;
constexpr auto TAG = "spi";
constexpr auto MOSI_PIN = llcc68::MOSI_PIN;
constexpr auto MISO_PIN = llcc68::MISO_PIN;
constexpr auto SCLK_PIN = llcc68::SCLK_PIN;
constexpr auto BUSY_PIN = llcc68::BUSY_PIN;
constexpr auto CS_PIN = llcc68::CS_PIN;
using error_t = error::t;
using ue_t = app::utils::unexpected<error_t>;
constexpr uint8_t SPI_READ_COMMAND = RADIOLIB_SX126X_CMD_READ_REGISTER;
constexpr uint8_t SPI_WRITE_COMMAND = RADIOLIB_SX126X_CMD_WRITE_REGISTER;
constexpr uint8_t SPI_NOP_COMMAND = RADIOLIB_SX126X_CMD_NOP;
constexpr uint8_t SPI_WRITE_BUFFER_CMD = RADIOLIB_SX126X_CMD_WRITE_BUFFER;
constexpr uint8_t SPI_READ_BUFFER_CMD = RADIOLIB_SX126X_CMD_READ_BUFFER;
constexpr size_t DEFAULT_TIMEOUT_MS = 1000;
std::span<uint8_t> mut_shared_buffer();
/*!
\brief Initialize the SPI interface.
*/
void init();
inline error_t status_to_err(const uint8_t status) {
const auto st = *reinterpret_cast<const llcc68::status_t *>(&status);
switch (st.command_status) {
case llcc68::CommandStatus::COMMAND_TIMEOUT:
return error::SPI_TIMEOUT;
case llcc68::CommandStatus::FAILURE_TO_EXECUTE_COMMAND:
return error::SPI_CMD_FAILED;
case llcc68::CommandStatus::COMMAND_PROCESSING_ERROR:
#ifdef APP_SPI_DISABLE_INVALID_STATUS_CHECK
return error::OK;
#else
return error::SPI_CMD_INVALID;
#endif
default:
return error::OK;
}
}
/*!
See also LLCC68 chapter 10 Host Controller Interface
*/
/*! Common API patterns */
/*!
READ (no parameters, only OPCODE, but has data and status)
OPCODE() -> (status, data)
*/
Result<Unit, error_t>
read_stream(uint8_t cmd, std::span<uint8_t> data, const size_t timeout_ms = DEFAULT_TIMEOUT_MS);
/*!
WRITE (having parameters, no excessive return values but status)
OPCODE(parameters) -> status
*/
Result<Unit, error_t>
write_stream(uint8_t cmd, std::span<const uint8_t> data, const size_t timeout_ms = DEFAULT_TIMEOUT_MS);
/*!
READ REGISTER
READ_REGISTER(register, offset) -> (status, data)
*/
Result<Unit, error_t>
read_register(uint16_t reg, std::span<uint8_t> data, const size_t timeout_ms = DEFAULT_TIMEOUT_MS);
/*!
WRITE REGISTER
WRITE_REGISTER(register, offset, data) -> status
*/
Result<Unit, error_t>
write_register(uint16_t offset, std::span<const uint8_t> data, const size_t timeout_ms = DEFAULT_TIMEOUT_MS);
/*!
WRITE BUFFER
WRITE_BUFFER(offset, data) -> status
*/
Result<Unit, error_t>
write_buffer(uint8_t offset, std::span<const uint8_t> data_from_host, const size_t timeout_ms = DEFAULT_TIMEOUT_MS);
/*!
READ BUFFER
READ_BUFFER(offset) -> (status, data)
*/
Result<std::span<const uint8_t>, error_t>
read_buffer(uint8_t offset, uint8_t size, const size_t timeout_ms = DEFAULT_TIMEOUT_MS);
}
#endif /* B7431E31_4075_4B09_B4B3_EAD0234EFB40 */

1263
inc/llcc68.hpp Normal file

File diff suppressed because it is too large Load Diff

391
inc/llcc68_definitions.hpp Normal file
View File

@ -0,0 +1,391 @@
//
// Created by Kurosu Chan on 2024/1/26.
//
#ifndef LLCC68_DEFINITIONS_H
#define LLCC68_DEFINITIONS_H
#include <cstddef>
#include <cstdint>
#include <tuple>
#include <variant>
#include "radiolib_definitions.hpp"
namespace app::driver::llcc68 {
using freq_t = float;
constexpr uint8_t DEFAULT_SYNC_WORD = RADIOLIB_SX126X_SYNC_WORD_PRIVATE;
constexpr uint16_t DEFAULT_PREAMBLE_LEN = 6; // recommended is 8 symbols though
constexpr uint8_t DEFAULT_CRC_TYPE = RADIOLIB_SX126X_LORA_CRC_OFF;
constexpr uint8_t DEFAULT_HEADER_TYPE = RADIOLIB_SX126X_LORA_HEADER_EXPLICIT;
constexpr uint8_t DEFAULT_IQ_TYPE = RADIOLIB_SX126X_LORA_IQ_STANDARD;
constexpr uint8_t PACKET_TYPE = RADIOLIB_SX126X_PACKET_TYPE_LORA;
constexpr auto DEFAULT_BW = RADIOLIB_SX126X_LORA_BW_125_0;
constexpr auto DEFAULT_SF = 9;
constexpr auto DEFAULT_CR = RADIOLIB_SX126X_LORA_CR_4_6;
constexpr auto DEFAULT_LDR_OPTIMIZE = RADIOLIB_SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_ON;
constexpr auto DEFAULT_FREQUENCY = freq_t{433.2};
enum class ChipType {
Unknown,
LLCC68,
SX1261,
SX1262,
};
inline const char *chip_type_str(const ChipType &chip) {
switch (chip) {
case ChipType::LLCC68:
return "LLCC68";
case ChipType::SX1261:
return "SX1261";
case ChipType::SX1262:
return "SX1262";
default:
return "Unknown";
}
}
/// @note suitable SX1262/LLCC68
enum class PaOptimalPresetHigh : uint8_t {
DBM_14,
DBM_17,
DBM_20,
DBM_22,
};
/// @note suitable SX1261
enum class PaOptimalPresetLow : uint8_t {
DBM_10,
DBM_14,
DBM_15,
};
using PaOptimalPreset = std::variant<PaOptimalPresetHigh, PaOptimalPresetLow>;
struct pa_config_t {
uint8_t pa_duty_cycle;
uint8_t hp_max;
uint8_t device_sel;
};
enum class PacketType : uint8_t {
FSK = RADIOLIB_SX126X_PACKET_TYPE_GFSK,
LORA = RADIOLIB_SX126X_PACKET_TYPE_LORA,
LR_FHSS = RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS,
};
enum class TcxoVoltage : uint8_t {
V_1_6 = 0x00,
V_1_7 = 0x01,
V_1_8 = 0x02,
V_2_2 = 0x03,
V_2_4 = 0x04,
V_2_7 = 0x05,
V_3_0 = 0x06,
V_3_3 = 0x07,
};
enum class CommandStatus : uint8_t {
RESERVED = 0x00, // 0b000
RFU = 0x01, // 0b001
DATA_AVAILABLE = 0x02, // 0b010
COMMAND_TIMEOUT = 0x03, // 0b011 i.e. TIMEOUT
COMMAND_PROCESSING_ERROR = 0x04, // 0b100 i.e. INVALID
FAILURE_TO_EXECUTE_COMMAND = 0x05, // 0b101 i.e. FAILED
COMMAND_TX_DONE = 0x06, // 0b110
};
enum class ChipMode : uint8_t {
UNUSED = 0x00, // 0b000
RFU = 0x01, // 0b001
STBY_RC = 0x02, // 0b010
STBY_XOSC = 0x03, // 0b011
FS = 0x04, // 0b100
RX = 0x05, // 0b101
TX = 0x06, // 0b110
};
struct __attribute__((packed)) status_t {
uint8_t reserved_1 : 1;
CommandStatus command_status : 3;
ChipMode chip_mode : 3;
uint8_t reserved_2 : 1;
};
static_assert(sizeof(status_t) == 1);
struct parameters_t {
uint8_t bw;
uint8_t sf;
uint8_t cr;
uint8_t ldr_optimize;
uint16_t preamble_len;
uint8_t sync_word;
freq_t frequency;
static parameters_t Default() {
return parameters_t{
.bw = DEFAULT_BW,
.sf = DEFAULT_SF,
.cr = DEFAULT_CR,
.ldr_optimize = DEFAULT_LDR_OPTIMIZE,
.preamble_len = DEFAULT_PREAMBLE_LEN,
.sync_word = DEFAULT_SYNC_WORD,
.frequency = DEFAULT_FREQUENCY,
};
}
constexpr static size_t size() {
return sizeof(parameters_t);
}
} __attribute__((packed));
struct __attribute__((packed)) lora_packet_status_t {
// Average over last packet received of RSSI
// Actual signal power is RssiPkt/2 (dBm)
uint8_t rssi_pkt;
// Estimation of SNR on last packet received in twos compliment format multiplied by 4.
// Actual SNR in dB =SnrPkt/4
uint8_t snr_pkt;
// Estimation of RSSI of the LoRa® signal (after despreading) on last packet received.
// Actual Rssi in dB = -SignalRssiPkt/2
uint8_t signal_rssi_pkt;
float rssi_pkt_dbm() const {
return -rssi_pkt / 2.0f;
}
float snr_pkt_db() const {
return snr_pkt / 4.0f;
}
float signal_rssi_pkt_dbm() const {
return -signal_rssi_pkt / 2.0f;
}
};
struct __attribute__((packed)) fsk_packet_status_t {
// bit 7: preamble err
// bit 6: sync err
// bit 5: adrs err
// bit 4: crc err
// bit 3: length err
// bit 2: abort err
// bit 1: pkt received
// bit 0: pkt sent
uint8_t rx_status;
// RSSI value latched upon the detection of the sync address.
// [negated, dBm, fixdt(0,8,1)]
// Actual signal power is RssiSync/2 (dBm)
uint8_t rssi_sync;
// RSSI average value over the payload of the received packet. Latched upon the pkt_done IRQ.
// [negated, dBm, fixdt(0,8,1)]
// Actual signal power is RssiAvg/2 (dBm)
uint8_t rssi_avg;
};
union packet_status_t {
uint8_t raw[3];
lora_packet_status_t lora;
fsk_packet_status_t fsk;
};
struct __attribute__((packed)) op_error_t {
bool RC64K_CALIB_ERR : 1; // RC64k calibration failed
bool RC13M_CALIB_ERR : 1; // RC13M calibration failed
bool PLL_CALIB_ERR : 1; // PLL calibration failed
bool ADC_CALIB_ERR : 1; // ADC calibration failed
bool IMG_CALIB_ERR : 1; // IMG calibration failed
bool XOSC_START_ERR : 1; // XOSC failed to start
bool PLL_LOCK_ERR : 1; // PLL failed to lock
uint8_t rfu_1 : 1; // RFU
bool PA_RAMP_ERR : 1; // PA ramping failed
uint8_t rfu_2 : 7; // RFU
};
static_assert(sizeof(op_error_t) == 2);
struct calc_time_on_air_t {
uint8_t bw;
uint8_t sf;
uint8_t cr;
uint16_t preamble_length;
static constexpr uint8_t ldro = DEFAULT_LDR_OPTIMIZE;
static constexpr uint8_t crc_type = DEFAULT_CRC_TYPE;
static constexpr uint8_t header_type = DEFAULT_HEADER_TYPE;
static constexpr uint8_t iq_type = DEFAULT_IQ_TYPE;
static constexpr uint8_t sync_word = DEFAULT_SYNC_WORD;
static calc_time_on_air_t from_parameters(const parameters_t &params) {
return calc_time_on_air_t{
.bw = params.bw,
.sf = params.sf,
.cr = params.cr,
.preamble_length = params.preamble_len};
}
};
constexpr bool in_range(const auto v, const auto min, const auto max) {
return v >= min && v <= max;
}
constexpr bool valid_cr(const uint8_t cr) {
switch (cr) {
case RADIOLIB_SX126X_LORA_CR_4_5:
case RADIOLIB_SX126X_LORA_CR_4_6:
case RADIOLIB_SX126X_LORA_CR_4_7:
case RADIOLIB_SX126X_LORA_CR_4_8:
return true;
default:
return false;
}
}
constexpr std::tuple<uint8_t, uint8_t>
cr_to_ratio(const uint8_t cr) {
switch (cr) {
case RADIOLIB_SX126X_LORA_CR_4_5:
return {4, 5};
case RADIOLIB_SX126X_LORA_CR_4_6:
return {4, 6};
case RADIOLIB_SX126X_LORA_CR_4_7:
return {4, 7};
case RADIOLIB_SX126X_LORA_CR_4_8:
return {4, 8};
default:
return {0, 1};
}
}
constexpr bool valid_ldr_optimize(const uint8_t ldr_optimize) {
if (ldr_optimize > RADIOLIB_SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_ON) {
return false;
}
return true;
}
constexpr bool valid_bw(const uint8_t bw) {
switch (bw) {
case RADIOLIB_SX126X_LORA_BW_7_8:
case RADIOLIB_SX126X_LORA_BW_10_4:
case RADIOLIB_SX126X_LORA_BW_15_6:
case RADIOLIB_SX126X_LORA_BW_20_8:
case RADIOLIB_SX126X_LORA_BW_31_25:
case RADIOLIB_SX126X_LORA_BW_41_7:
case RADIOLIB_SX126X_LORA_BW_62_5:
case RADIOLIB_SX126X_LORA_BW_125_0:
case RADIOLIB_SX126X_LORA_BW_250_0:
case RADIOLIB_SX126X_LORA_BW_500_0:
return true;
default:
return false;
}
}
using bw_t = float;
constexpr bw_t bw_khz(const uint8_t bw) {
switch (bw) {
case RADIOLIB_SX126X_LORA_BW_7_8:
return bw_t{7.8f};
case RADIOLIB_SX126X_LORA_BW_10_4:
return bw_t{10.4f};
case RADIOLIB_SX126X_LORA_BW_15_6:
return bw_t{15.6f};
case RADIOLIB_SX126X_LORA_BW_20_8:
return bw_t{20.8f};
case RADIOLIB_SX126X_LORA_BW_31_25:
return bw_t{31.25f};
case RADIOLIB_SX126X_LORA_BW_41_7:
return bw_t{41.7f};
case RADIOLIB_SX126X_LORA_BW_62_5:
return bw_t{62.5f};
case RADIOLIB_SX126X_LORA_BW_125_0:
return bw_t{125.0f};
case RADIOLIB_SX126X_LORA_BW_250_0:
return bw_t{250.0f};
case RADIOLIB_SX126X_LORA_BW_500_0:
return bw_t{500.0f};
default:
return bw_t{0};
}
}
constexpr bool valid_sf(const uint8_t bw, const uint8_t sf) {
if (const bool ok = valid_bw(bw); not ok) {
return false;
}
switch (bw) {
case RADIOLIB_SX126X_LORA_BW_125_0:
return in_range(sf, 5, 9);
case RADIOLIB_SX126X_LORA_BW_250_0:
return in_range(sf, 5, 10);
case RADIOLIB_SX126X_LORA_BW_500_0:
return in_range(sf, 5, 11);
default:
return in_range(sf, 5, 12);
}
}
constexpr bool valid_freq(const freq_t freq) {
return in_range(freq, freq_t{150.0}, freq_t{960.0});
}
/**
* \brief check if the parameters are valid. if not, set them to default.
* \param params the parameters to check
* \return if the parameters are valid, and the modified parameters
*/
constexpr std::tuple<bool, parameters_t>
check_fix_params(parameters_t params) {
bool ok = true;
if (not valid_bw(params.bw)) {
params.bw = DEFAULT_BW;
ok = false;
}
if (not valid_sf(params.bw, params.sf)) {
params.sf = DEFAULT_SF;
ok = false;
}
if (not valid_cr(params.cr)) {
params.cr = DEFAULT_CR;
ok = false;
}
if (not valid_freq(params.frequency)) {
params.frequency = DEFAULT_FREQUENCY;
ok = false;
}
if (not valid_ldr_optimize(params.ldr_optimize)) {
params.ldr_optimize = DEFAULT_LDR_OPTIMIZE;
ok = false;
}
return {ok, params};
}
/**
* \brief only do the check
*/
constexpr bool check_params(const parameters_t &params) {
if (not valid_bw(params.bw)) {
return false;
}
if (not valid_sf(params.bw, params.sf)) {
return false;
}
if (not valid_cr(params.cr)) {
return false;
}
if (not valid_freq(params.frequency)) {
return false;
}
if (not valid_ldr_optimize(params.ldr_optimize)) {
return false;
}
return true;
}
}
#endif // LLCC68_DEFINITIONS_H

View File

@ -0,0 +1,439 @@
//
// Created by Kurosu Chan on 2024/1/18.
//
#ifndef ADC87656_291B_49BB_A611_52533D666023
#define ADC87656_291B_49BB_A611_52533D666023
#include <cstdint>
// SX126X physical layer properties
constexpr auto RADIOLIB_SX126X_FREQUENCY_STEP_SIZE = 0.9536743164;
constexpr auto RADIOLIB_SX126X_MAX_PACKET_LENGTH = 255;
constexpr auto RADIOLIB_SX126X_CRYSTAL_FREQ = 32.0;
constexpr auto RADIOLIB_SX126X_DIV_EXPONENT = 25;
// SX126X SPI commands
// operational modes commands
constexpr uint8_t RADIOLIB_SX126X_CMD_NOP = 0x00;
constexpr uint8_t RADIOLIB_SX126X_CMD_SET_SLEEP = 0x84;
constexpr uint8_t RADIOLIB_SX126X_CMD_SET_STANDBY = 0x80;
constexpr uint8_t RADIOLIB_SX126X_CMD_SET_FS = 0xC1;
constexpr uint8_t RADIOLIB_SX126X_CMD_SET_TX = 0x83;
constexpr uint8_t RADIOLIB_SX126X_CMD_SET_RX = 0x82;
constexpr uint8_t RADIOLIB_SX126X_CMD_STOP_TIMER_ON_PREAMBLE = 0x9F;
constexpr uint8_t RADIOLIB_SX126X_CMD_SET_RX_DUTY_CYCLE = 0x94;
constexpr uint8_t RADIOLIB_SX126X_CMD_SET_CAD = 0xC5;
constexpr uint8_t RADIOLIB_SX126X_CMD_SET_TX_CONTINUOUS_WAVE = 0xD1;
constexpr uint8_t RADIOLIB_SX126X_CMD_SET_TX_INFINITE_PREAMBLE = 0xD2;
constexpr uint8_t RADIOLIB_SX126X_CMD_SET_REGULATOR_MODE = 0x96;
constexpr uint8_t RADIOLIB_SX126X_CMD_CALIBRATE = 0x89;
constexpr uint8_t RADIOLIB_SX126X_CMD_CALIBRATE_IMAGE = 0x98;
constexpr uint8_t RADIOLIB_SX126X_CMD_SET_PA_CONFIG = 0x95;
constexpr uint8_t RADIOLIB_SX126X_CMD_SET_RX_TX_FALLBACK_MODE = 0x93;
// register and buffer access commands
constexpr uint8_t RADIOLIB_SX126X_CMD_WRITE_REGISTER = 0x0D;
constexpr uint8_t RADIOLIB_SX126X_CMD_READ_REGISTER = 0x1D;
constexpr uint8_t RADIOLIB_SX126X_CMD_WRITE_BUFFER = 0x0E;
constexpr uint8_t RADIOLIB_SX126X_CMD_READ_BUFFER = 0x1E;
// DIO and IRQ control
constexpr uint8_t RADIOLIB_SX126X_CMD_SET_DIO_IRQ_PARAMS = 0x08;
constexpr uint8_t RADIOLIB_SX126X_CMD_GET_IRQ_STATUS = 0x12;
constexpr uint8_t RADIOLIB_SX126X_CMD_CLEAR_IRQ_STATUS = 0x02;
constexpr uint8_t RADIOLIB_SX126X_CMD_SET_DIO2_AS_RF_SWITCH_CTRL = 0x9D;
constexpr uint8_t RADIOLIB_SX126X_CMD_SET_DIO3_AS_TCXO_CTRL = 0x97;
// RF, modulation and packet commands
constexpr uint8_t RADIOLIB_SX126X_CMD_SET_RF_FREQUENCY = 0x86;
constexpr uint8_t RADIOLIB_SX126X_CMD_SET_PACKET_TYPE = 0x8A;
constexpr uint8_t RADIOLIB_SX126X_CMD_GET_PACKET_TYPE = 0x11;
constexpr uint8_t RADIOLIB_SX126X_CMD_SET_TX_PARAMS = 0x8E;
constexpr uint8_t RADIOLIB_SX126X_CMD_SET_MODULATION_PARAMS = 0x8B;
constexpr uint8_t RADIOLIB_SX126X_CMD_SET_PACKET_PARAMS = 0x8C;
constexpr uint8_t RADIOLIB_SX126X_CMD_SET_CAD_PARAMS = 0x88;
constexpr uint8_t RADIOLIB_SX126X_CMD_SET_BUFFER_BASE_ADDRESS = 0x8F;
constexpr uint8_t RADIOLIB_SX126X_CMD_SET_LORA_SYMB_NUM_TIMEOUT = 0x0A;
// status commands
constexpr uint8_t RADIOLIB_SX126X_CMD_GET_STATUS = 0xC0;
constexpr uint8_t RADIOLIB_SX126X_CMD_GET_RSSI_INST = 0x15;
constexpr uint8_t RADIOLIB_SX126X_CMD_GET_RX_BUFFER_STATUS = 0x13;
constexpr uint8_t RADIOLIB_SX126X_CMD_GET_PACKET_STATUS = 0x14;
constexpr uint8_t RADIOLIB_SX126X_CMD_GET_DEVICE_ERRORS = 0x17;
constexpr uint8_t RADIOLIB_SX126X_CMD_CLEAR_DEVICE_ERRORS = 0x07;
constexpr uint8_t RADIOLIB_SX126X_CMD_GET_STATS = 0x10;
constexpr uint8_t RADIOLIB_SX126X_CMD_RESET_STATS = 0x00;
constexpr uint8_t RADIOLIB_SX126X_CMD_PRAM_UPDATE = 0xD9;
constexpr uint8_t RADIOLIB_SX126X_CMD_SET_LBT_SCAN_PARAMS = 0x9A;
constexpr uint8_t RADIOLIB_SX126X_CMD_SET_SPECTR_SCAN_PARAMS = 0x9B;
#define RADIOLIB_SX126X_REG_LR_FHSS_NUM_SYMBOLS_FREQX_MSB(X) (0x0388 + (X) * 6)
#define RADIOLIB_SX126X_REG_LR_FHSS_NUM_SYMBOLS_FREQX_LSB(X) (0x0389 + (X) * 6)
#define RADIOLIB_SX126X_REG_LR_FHSS_FREQX_0(X) (0x038A + (X) * 6)
#define RADIOLIB_SX126X_REG_LR_FHSS_FREQX_1(X) (0x038B + (X) * 6)
#define RADIOLIB_SX126X_REG_LR_FHSS_FREQX_2(X) (0x038C + (X) * 6)
#define RADIOLIB_SX126X_REG_LR_FHSS_FREQX_3(X) (0x038D + (X) * 6)
// SX126X register map
constexpr uint16_t RADIOLIB_SX126X_REG_RX_GAIN_RETENTION_0 = 0x029F; // SX1268 datasheet v1.1, section 9.6
constexpr uint16_t RADIOLIB_SX126X_REG_RX_GAIN_RETENTION_1 = 0x02A0; // SX1268 datasheet v1.1, section 9.6
constexpr uint16_t RADIOLIB_SX126X_REG_RX_GAIN_RETENTION_2 = 0x02A1; // SX1268 datasheet v1.1, section 9.6
constexpr uint16_t RADIOLIB_SX126X_REG_VERSION_STRING = 0x0320;
constexpr uint16_t RADIOLIB_SX126X_REG_HOPPING_ENABLE = 0x0385;
constexpr uint16_t RADIOLIB_SX126X_REG_LR_FHSS_PACKET_LENGTH = 0x0386;
constexpr uint16_t RADIOLIB_SX126X_REG_LR_FHSS_NUM_HOPPING_BLOCKS = 0x0387;
constexpr uint16_t RADIOLIB_SX126X_REG_SPECTRAL_SCAN_RESULT = 0x0401;
constexpr uint16_t RADIOLIB_SX126X_REG_DIOX_OUT_ENABLE = 0x0580;
constexpr uint16_t RADIOLIB_SX126X_REG_DIOX_DRIVE_STRENGTH = 0x0582;
constexpr uint16_t RADIOLIB_SX126X_REG_DIOX_IN_ENABLE = 0x0583;
constexpr uint16_t RADIOLIB_SX126X_REG_DIOX_PULL_UP_CTRL = 0x0584;
constexpr uint16_t RADIOLIB_SX126X_REG_DIOX_PULL_DOWN_CTRL = 0x0585;
constexpr uint16_t RADIOLIB_SX126X_REG_TX_BITBANG_ENABLE_0 = 0x0587;
constexpr uint16_t RADIOLIB_SX126X_REG_PATCH_UPDATE_ENABLE = 0x0610;
constexpr uint16_t RADIOLIB_SX126X_REG_TX_BITBANG_ENABLE_1 = 0x0680;
constexpr uint16_t RADIOLIB_SX126X_REG_WHITENING_INITIAL_MSB = 0x06B8;
constexpr uint16_t RADIOLIB_SX126X_REG_WHITENING_INITIAL_LSB = 0x06B9;
constexpr uint16_t RADIOLIB_SX126X_REG_RX_TX_PLD_LEN = 0x06BB;
constexpr uint16_t RADIOLIB_SX126X_REG_CRC_INITIAL_MSB = 0x06BC;
constexpr uint16_t RADIOLIB_SX126X_REG_CRC_INITIAL_LSB = 0x06BD;
constexpr uint16_t RADIOLIB_SX126X_REG_CRC_POLYNOMIAL_MSB = 0x06BE;
constexpr uint16_t RADIOLIB_SX126X_REG_CRC_POLYNOMIAL_LSB = 0x06BF;
constexpr uint16_t RADIOLIB_SX126X_REG_SYNC_WORD_0 = 0x06C0;
constexpr uint16_t RADIOLIB_SX126X_REG_SYNC_WORD_1 = 0x06C1;
constexpr uint16_t RADIOLIB_SX126X_REG_SYNC_WORD_2 = 0x06C2;
constexpr uint16_t RADIOLIB_SX126X_REG_SYNC_WORD_3 = 0x06C3;
constexpr uint16_t RADIOLIB_SX126X_REG_SYNC_WORD_4 = 0x06C4;
constexpr uint16_t RADIOLIB_SX126X_REG_SYNC_WORD_5 = 0x06C5;
constexpr uint16_t RADIOLIB_SX126X_REG_SYNC_WORD_6 = 0x06C6;
constexpr uint16_t RADIOLIB_SX126X_REG_SYNC_WORD_7 = 0x06C7;
constexpr uint16_t RADIOLIB_SX126X_REG_NODE_ADDRESS = 0x06CD;
constexpr uint16_t RADIOLIB_SX126X_REG_BROADCAST_ADDRESS = 0x06CE;
constexpr uint16_t RADIOLIB_SX126X_REG_PAYLOAD_LENGTH = 0x0702;
constexpr uint16_t RADIOLIB_SX126X_REG_PACKET_PARAMS = 0x0704;
constexpr uint16_t RADIOLIB_SX126X_REG_LORA_SYNC_TIMEOUT = 0x0706;
constexpr uint16_t RADIOLIB_SX126X_REG_IQ_CONFIG = 0x0736;
constexpr uint16_t RADIOLIB_SX126X_REG_LORA_SYNC_WORD_MSB = 0x0740;
constexpr uint16_t RADIOLIB_SX126X_REG_LORA_SYNC_WORD_LSB = 0x0741;
constexpr uint16_t RADIOLIB_SX126X_REG_FREQ_ERROR = 0x076B;
constexpr uint16_t RADIOLIB_SX126X_REG_SPECTRAL_SCAN_STATUS = 0x07CD;
constexpr uint16_t RADIOLIB_SX126X_REG_RX_ADDR_PTR = 0x0803;
constexpr uint16_t RADIOLIB_SX126X_REG_RANDOM_NUMBER_0 = 0x0819;
constexpr uint16_t RADIOLIB_SX126X_REG_RANDOM_NUMBER_1 = 0x081A;
constexpr uint16_t RADIOLIB_SX126X_REG_RANDOM_NUMBER_2 = 0x081B;
constexpr uint16_t RADIOLIB_SX126X_REG_RANDOM_NUMBER_3 = 0x081C;
constexpr uint16_t RADIOLIB_SX126X_REG_SENSITIVITY_CONFIG = 0x0889; // SX1268 datasheet v1.1, section 15.1
constexpr uint16_t RADIOLIB_SX126X_REG_RF_FREQUENCY_0 = 0x088B;
constexpr uint16_t RADIOLIB_SX126X_REG_RF_FREQUENCY_1 = 0x088C;
constexpr uint16_t RADIOLIB_SX126X_REG_RF_FREQUENCY_2 = 0x088D;
constexpr uint16_t RADIOLIB_SX126X_REG_RF_FREQUENCY_3 = 0x088E;
constexpr uint16_t RADIOLIB_SX126X_REG_RSSI_AVG_WINDOW = 0x089B;
constexpr uint16_t RADIOLIB_SX126X_REG_RX_GAIN = 0x08AC;
constexpr uint16_t RADIOLIB_SX126X_REG_TX_CLAMP_CONFIG = 0x08D8;
constexpr uint16_t RADIOLIB_SX126X_REG_ANA_LNA = 0x08E2;
constexpr uint16_t RADIOLIB_SX126X_REG_LNA_CAP_TUNE_N = 0x08E3;
constexpr uint16_t RADIOLIB_SX126X_REG_LNA_CAP_TUNE_P = 0x08E4;
constexpr uint16_t RADIOLIB_SX126X_REG_ANA_MIXER = 0x08E5;
constexpr uint16_t RADIOLIB_SX126X_REG_OCP_CONFIGURATION = 0x08E7;
constexpr uint16_t RADIOLIB_SX126X_REG_RTC_CTRL = 0x0902;
constexpr uint16_t RADIOLIB_SX126X_REG_XTA_TRIM = 0x0911;
constexpr uint16_t RADIOLIB_SX126X_REG_XTB_TRIM = 0x0912;
constexpr uint16_t RADIOLIB_SX126X_REG_DIO3_OUT_VOLTAGE_CTRL = 0x0920;
constexpr uint16_t RADIOLIB_SX126X_REG_EVENT_MASK = 0x0944;
constexpr uint16_t RADIOLIB_SX126X_REG_PATCH_MEMORY_BASE = 0x8000;
// SX126X SPI command variables
// RADIOLIB_SX126X_CMD_SET_SLEEP MSB LSB DESCRIPTION
#define RADIOLIB_SX126X_SLEEP_START_COLD 0b00000000 // 2 2 sleep mode: cold start, configuration is lost (default)
#define RADIOLIB_SX126X_SLEEP_START_WARM 0b00000100 // 2 2 warm start, configuration is retained
#define RADIOLIB_SX126X_SLEEP_RTC_OFF 0b00000000 // 0 0 wake on RTC timeout: disabled
#define RADIOLIB_SX126X_SLEEP_RTC_ON 0b00000001 // 0 0 enabled
// RADIOLIB_SX126X_CMD_SET_STANDBY
#define RADIOLIB_SX126X_STANDBY_RC 0x00 // 7 0 standby mode: 13 MHz RC oscillator
#define RADIOLIB_SX126X_STANDBY_XOSC 0x01 // 7 0 32 MHz crystal oscillator
// RADIOLIB_SX126X_CMD_SET_RX
#define RADIOLIB_SX126X_RX_TIMEOUT_NONE 0x000000 // 23 0 Rx timeout duration: no timeout (Rx single mode)
#define RADIOLIB_SX126X_RX_TIMEOUT_INF 0xFFFFFF // 23 0 infinite (Rx continuous mode)
// RADIOLIB_SX126X_CMD_SET_TX
#define RADIOLIB_SX126X_TX_TIMEOUT_NONE 0x000000 // 23 0 Tx timeout duration: no timeout (Tx single mode)
// RADIOLIB_SX126X_CMD_STOP_TIMER_ON_PREAMBLE
#define RADIOLIB_SX126X_STOP_ON_PREAMBLE_OFF 0x00 // 7 0 stop timer on: sync word or header (default)
#define RADIOLIB_SX126X_STOP_ON_PREAMBLE_ON 0x01 // 7 0 preamble detection
// RADIOLIB_SX126X_CMD_SET_REGULATOR_MODE
#define RADIOLIB_SX126X_REGULATOR_LDO 0x00 // 7 0 set regulator mode: LDO (default)
#define RADIOLIB_SX126X_REGULATOR_DC_DC 0x01 // 7 0 DC-DC
// RADIOLIB_SX126X_CMD_CALIBRATE
#define RADIOLIB_SX126X_CALIBRATE_IMAGE_OFF 0b00000000 // 6 6 image calibration: disabled
#define RADIOLIB_SX126X_CALIBRATE_IMAGE_ON 0b01000000 // 6 6 enabled
#define RADIOLIB_SX126X_CALIBRATE_ADC_BULK_P_OFF 0b00000000 // 5 5 ADC bulk P calibration: disabled
#define RADIOLIB_SX126X_CALIBRATE_ADC_BULK_P_ON 0b00100000 // 5 5 enabled
#define RADIOLIB_SX126X_CALIBRATE_ADC_BULK_N_OFF 0b00000000 // 4 4 ADC bulk N calibration: disabled
#define RADIOLIB_SX126X_CALIBRATE_ADC_BULK_N_ON 0b00010000 // 4 4 enabled
#define RADIOLIB_SX126X_CALIBRATE_ADC_PULSE_OFF 0b00000000 // 3 3 ADC pulse calibration: disabled
#define RADIOLIB_SX126X_CALIBRATE_ADC_PULSE_ON 0b00001000 // 3 3 enabled
#define RADIOLIB_SX126X_CALIBRATE_PLL_OFF 0b00000000 // 2 2 PLL calibration: disabled
#define RADIOLIB_SX126X_CALIBRATE_PLL_ON 0b00000100 // 2 2 enabled
#define RADIOLIB_SX126X_CALIBRATE_RC13M_OFF 0b00000000 // 1 1 13 MHz RC osc. calibration: disabled
#define RADIOLIB_SX126X_CALIBRATE_RC13M_ON 0b00000010 // 1 1 enabled
#define RADIOLIB_SX126X_CALIBRATE_RC64K_OFF 0b00000000 // 0 0 64 kHz RC osc. calibration: disabled
#define RADIOLIB_SX126X_CALIBRATE_RC64K_ON 0b00000001 // 0 0 enabled
#define RADIOLIB_SX126X_CALIBRATE_ALL 0b01111111 // 6 0 calibrate all blocks
// RADIOLIB_SX126X_CMD_CALIBRATE_IMAGE
#define RADIOLIB_SX126X_CAL_IMG_430_MHZ_1 0x6B
#define RADIOLIB_SX126X_CAL_IMG_430_MHZ_2 0x6F
#define RADIOLIB_SX126X_CAL_IMG_470_MHZ_1 0x75
#define RADIOLIB_SX126X_CAL_IMG_470_MHZ_2 0x81
#define RADIOLIB_SX126X_CAL_IMG_779_MHZ_1 0xC1
#define RADIOLIB_SX126X_CAL_IMG_779_MHZ_2 0xC5
#define RADIOLIB_SX126X_CAL_IMG_863_MHZ_1 0xD7
#define RADIOLIB_SX126X_CAL_IMG_863_MHZ_2 0xDB
#define RADIOLIB_SX126X_CAL_IMG_902_MHZ_1 0xE1
#define RADIOLIB_SX126X_CAL_IMG_902_MHZ_2 0xE9
// RADIOLIB_SX126X_CMD_SET_PA_CONFIG
#define RADIOLIB_SX126X_PA_CONFIG_HP_MAX 0x07
#define RADIOLIB_SX126X_PA_CONFIG_PA_LUT 0x01
#define RADIOLIB_SX126X_PA_CONFIG_SX1262_8 0x00
// RADIOLIB_SX126X_CMD_SET_RX_TX_FALLBACK_MODE
#define RADIOLIB_SX126X_RX_TX_FALLBACK_MODE_FS 0x40 // 7 0 after Rx/Tx go to: FS mode
#define RADIOLIB_SX126X_RX_TX_FALLBACK_MODE_STDBY_XOSC 0x30 // 7 0 standby with crystal oscillator
#define RADIOLIB_SX126X_RX_TX_FALLBACK_MODE_STDBY_RC 0x20 // 7 0 standby with RC oscillator (default)
// RADIOLIB_SX126X_CMD_SET_DIO_IRQ_PARAMS
#define RADIOLIB_SX126X_IRQ_LR_FHSS_HOP 0b0100000000000000 // 14 14 PA ramped up during LR-FHSS hop
#define RADIOLIB_SX126X_IRQ_TIMEOUT 0b0000001000000000 // 9 9 Rx or Tx timeout
#define RADIOLIB_SX126X_IRQ_CAD_DETECTED 0b0000000100000000 // 8 8 channel activity detected
#define RADIOLIB_SX126X_IRQ_CAD_DONE 0b0000000010000000 // 7 7 channel activity detection finished
#define RADIOLIB_SX126X_IRQ_CRC_ERR 0b0000000001000000 // 6 6 wrong CRC received
#define RADIOLIB_SX126X_IRQ_HEADER_ERR 0b0000000000100000 // 5 5 LoRa header CRC error
#define RADIOLIB_SX126X_IRQ_HEADER_VALID 0b0000000000010000 // 4 4 valid LoRa header received
#define RADIOLIB_SX126X_IRQ_SYNC_WORD_VALID 0b0000000000001000 // 3 3 valid sync word detected
#define RADIOLIB_SX126X_IRQ_RADIOLIB_PREAMBLE_DETECTED 0b0000000000000100 // 2 2 preamble detected
#define RADIOLIB_SX126X_IRQ_RX_DONE 0b0000000000000010 // 1 1 packet received
#define RADIOLIB_SX126X_IRQ_TX_DONE 0b0000000000000001 // 0 0 packet transmission completed
#define RADIOLIB_SX126X_IRQ_RX_DEFAULT 0b0000001001100010 // 14 0 default for Rx (RX_DONE, TIMEOUT, CRC_ERR and HEADER_ERR)
#define RADIOLIB_SX126X_IRQ_ALL 0b0100001111111111 // 14 0 all interrupts
#define RADIOLIB_SX126X_IRQ_NONE 0b0000000000000000 // 14 0 no interrupts
// RADIOLIB_SX126X_CMD_SET_DIO2_AS_RF_SWITCH_CTRL
#define RADIOLIB_SX126X_DIO2_AS_IRQ 0x00 // 7 0 DIO2 configuration: IRQ
#define RADIOLIB_SX126X_DIO2_AS_RF_SWITCH 0x01 // 7 0 RF switch control
// RADIOLIB_SX126X_CMD_SET_DIO3_AS_TCXO_CTRL
#define RADIOLIB_SX126X_DIO3_OUTPUT_1_6 0x00 // 7 0 DIO3 voltage output for TCXO: 1.6 V
#define RADIOLIB_SX126X_DIO3_OUTPUT_1_7 0x01 // 7 0 1.7 V
#define RADIOLIB_SX126X_DIO3_OUTPUT_1_8 0x02 // 7 0 1.8 V
#define RADIOLIB_SX126X_DIO3_OUTPUT_2_2 0x03 // 7 0 2.2 V
#define RADIOLIB_SX126X_DIO3_OUTPUT_2_4 0x04 // 7 0 2.4 V
#define RADIOLIB_SX126X_DIO3_OUTPUT_2_7 0x05 // 7 0 2.7 V
#define RADIOLIB_SX126X_DIO3_OUTPUT_3_0 0x06 // 7 0 3.0 V
#define RADIOLIB_SX126X_DIO3_OUTPUT_3_3 0x07 // 7 0 3.3 V
// RADIOLIB_SX126X_CMD_SET_PACKET_TYPE
constexpr uint8_t RADIOLIB_SX126X_PACKET_TYPE_GFSK = 0x00; // 7 0 packet type: GFSK
constexpr uint8_t RADIOLIB_SX126X_PACKET_TYPE_LORA = 0x01; // 7 0 LoRa
constexpr uint8_t RADIOLIB_SX126X_PACKET_TYPE_LR_FHSS = 0x03; // 7 0 LR-FHSS
// RADIOLIB_SX126X_CMD_SET_TX_PARAMS
#define RADIOLIB_SX126X_PA_RAMP_10U 0x00 // 7 0 ramp time: 10 us
#define RADIOLIB_SX126X_PA_RAMP_20U 0x01 // 7 0 20 us
#define RADIOLIB_SX126X_PA_RAMP_40U 0x02 // 7 0 40 us
#define RADIOLIB_SX126X_PA_RAMP_80U 0x03 // 7 0 80 us
#define RADIOLIB_SX126X_PA_RAMP_200U 0x04 // 7 0 200 us
#define RADIOLIB_SX126X_PA_RAMP_800U 0x05 // 7 0 800 us
#define RADIOLIB_SX126X_PA_RAMP_1700U 0x06 // 7 0 1700 us
#define RADIOLIB_SX126X_PA_RAMP_3400U 0x07 // 7 0 3400 us
// RADIOLIB_SX126X_CMD_SET_MODULATION_PARAMS
constexpr uint8_t RADIOLIB_SX126X_GFSK_FILTER_NONE = 0x00; // 7 0 GFSK filter: none
constexpr uint8_t RADIOLIB_SX126X_GFSK_FILTER_GAUSS_0_3 = 0x08; // 7 0 Gaussian, BT = 0.3
constexpr uint8_t RADIOLIB_SX126X_GFSK_FILTER_GAUSS_0_5 = 0x09; // 7 0 Gaussian, BT = 0.5
constexpr uint8_t RADIOLIB_SX126X_GFSK_FILTER_GAUSS_0_7 = 0x0A; // 7 0 Gaussian, BT = 0.7
constexpr uint8_t RADIOLIB_SX126X_GFSK_FILTER_GAUSS_1 = 0x0B; // 7 0 Gaussian, BT = 1
constexpr uint8_t RADIOLIB_SX126X_GFSK_RX_BW_4_8 = 0x1F; // 7 0 GFSK Rx bandwidth: 4.8 kHz
constexpr uint8_t RADIOLIB_SX126X_GFSK_RX_BW_5_8 = 0x17; // 7 0 5.8 kHz
constexpr uint8_t RADIOLIB_SX126X_GFSK_RX_BW_7_3 = 0x0F; // 7 0 7.3 kHz
constexpr uint8_t RADIOLIB_SX126X_GFSK_RX_BW_9_7 = 0x1E; // 7 0 9.7 kHz
constexpr uint8_t RADIOLIB_SX126X_GFSK_RX_BW_11_7 = 0x16; // 7 0 11.7 kHz
constexpr uint8_t RADIOLIB_SX126X_GFSK_RX_BW_14_6 = 0x0E; // 7 0 14.6 kHz
constexpr uint8_t RADIOLIB_SX126X_GFSK_RX_BW_19_5 = 0x1D; // 7 0 19.5 kHz
constexpr uint8_t RADIOLIB_SX126X_GFSK_RX_BW_23_4 = 0x15; // 7 0 23.4 kHz
constexpr uint8_t RADIOLIB_SX126X_GFSK_RX_BW_29_3 = 0x0D; // 7 0 29.3 kHz
constexpr uint8_t RADIOLIB_SX126X_GFSK_RX_BW_39_0 = 0x1C; // 7 0 39.0 kHz
constexpr uint8_t RADIOLIB_SX126X_GFSK_RX_BW_46_9 = 0x14; // 7 0 46.9 kHz
constexpr uint8_t RADIOLIB_SX126X_GFSK_RX_BW_58_6 = 0x0C; // 7 0 58.6 kHz
constexpr uint8_t RADIOLIB_SX126X_GFSK_RX_BW_78_2 = 0x1B; // 7 0 78.2 kHz
constexpr uint8_t RADIOLIB_SX126X_GFSK_RX_BW_93_8 = 0x13; // 7 0 93.8 kHz
constexpr uint8_t RADIOLIB_SX126X_GFSK_RX_BW_117_3 = 0x0B; // 7 0 117.3 kHz
constexpr uint8_t RADIOLIB_SX126X_GFSK_RX_BW_156_2 = 0x1A; // 7 0 156.2 kHz
constexpr uint8_t RADIOLIB_SX126X_GFSK_RX_BW_187_2 = 0x12; // 7 0 187.2 kHz
constexpr uint8_t RADIOLIB_SX126X_GFSK_RX_BW_234_3 = 0x0A; // 7 0 234.3 kHz
constexpr uint8_t RADIOLIB_SX126X_GFSK_RX_BW_312_0 = 0x19; // 7 0 312.0 kHz
constexpr uint8_t RADIOLIB_SX126X_GFSK_RX_BW_373_6 = 0x11; // 7 0 373.6 kHz
constexpr uint8_t RADIOLIB_SX126X_GFSK_RX_BW_467_0 = 0x09; // 7 0 467.0 kHz
constexpr uint8_t RADIOLIB_SX126X_LORA_BW_7_8 = 0x00; // 7 0 LoRa bandwidth: 7.8 kHz
constexpr uint8_t RADIOLIB_SX126X_LORA_BW_10_4 = 0x08; // 7 0 10.4 kHz
constexpr uint8_t RADIOLIB_SX126X_LORA_BW_15_6 = 0x01; // 7 0 15.6 kHz
constexpr uint8_t RADIOLIB_SX126X_LORA_BW_20_8 = 0x09; // 7 0 20.8 kHz
constexpr uint8_t RADIOLIB_SX126X_LORA_BW_31_25 = 0x02; // 7 0 31.25 kHz
constexpr uint8_t RADIOLIB_SX126X_LORA_BW_41_7 = 0x0A; // 7 0 41.7 kHz
constexpr uint8_t RADIOLIB_SX126X_LORA_BW_62_5 = 0x03; // 7 0 62.5 kHz
constexpr uint8_t RADIOLIB_SX126X_LORA_BW_125_0 = 0x04; // 7 0 125.0 kHz
constexpr uint8_t RADIOLIB_SX126X_LORA_BW_250_0 = 0x05; // 7 0 250.0 kHz
constexpr uint8_t RADIOLIB_SX126X_LORA_BW_500_0 = 0x06; // 7 0 500.0 kHz
constexpr uint8_t RADIOLIB_SX126X_LORA_CR_4_5 = 0x01; // 7 0 LoRa coding rate: 4/5
constexpr uint8_t RADIOLIB_SX126X_LORA_CR_4_6 = 0x02; // 7 0 4/6
constexpr uint8_t RADIOLIB_SX126X_LORA_CR_4_7 = 0x03; // 7 0 4/7
constexpr uint8_t RADIOLIB_SX126X_LORA_CR_4_8 = 0x04; // 7 0 4/8
constexpr uint8_t RADIOLIB_SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_OFF = 0x00; // 7 0 LoRa low data rate optimization: disabled
constexpr uint8_t RADIOLIB_SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_ON = 0x01; // 7 0 enabled
// RADIOLIB_SX126X_CMD_SET_PACKET_PARAMS
constexpr uint8_t RADIOLIB_SX126X_GFSK_PREAMBLE_DETECT_OFF = 0x00; // 7 0 GFSK minimum preamble length before reception starts: detector disabled
constexpr uint8_t RADIOLIB_SX126X_GFSK_PREAMBLE_DETECT_8 = 0x04; // 7 0 8 bits
constexpr uint8_t RADIOLIB_SX126X_GFSK_PREAMBLE_DETECT_16 = 0x05; // 7 0 16 bits
constexpr uint8_t RADIOLIB_SX126X_GFSK_PREAMBLE_DETECT_24 = 0x06; // 7 0 24 bits
constexpr uint8_t RADIOLIB_SX126X_GFSK_PREAMBLE_DETECT_32 = 0x07; // 7 0 32 bits
constexpr uint8_t RADIOLIB_SX126X_GFSK_ADDRESS_FILT_OFF = 0x00; // 7 0 GFSK address filtering: disabled
constexpr uint8_t RADIOLIB_SX126X_GFSK_ADDRESS_FILT_NODE = 0x01; // 7 0 node only
constexpr uint8_t RADIOLIB_SX126X_GFSK_ADDRESS_FILT_NODE_BROADCAST = 0x02; // 7 0 node and broadcast
constexpr uint8_t RADIOLIB_SX126X_GFSK_PACKET_FIXED = 0x00; // 7 0 GFSK packet type: fixed (payload length known in advance to both sides)
constexpr uint8_t RADIOLIB_SX126X_GFSK_PACKET_VARIABLE = 0x01; // 7 0 variable (payload length added to packet)
constexpr uint8_t RADIOLIB_SX126X_GFSK_CRC_OFF = 0x01; // 7 0 GFSK packet CRC: disabled
constexpr uint8_t RADIOLIB_SX126X_GFSK_CRC_1_BYTE = 0x00; // 7 0 1 byte
constexpr uint8_t RADIOLIB_SX126X_GFSK_CRC_2_BYTE = 0x02; // 7 0 2 byte
constexpr uint8_t RADIOLIB_SX126X_GFSK_CRC_1_BYTE_INV = 0x04; // 7 0 1 byte, inverted
constexpr uint8_t RADIOLIB_SX126X_GFSK_CRC_2_BYTE_INV = 0x06; // 7 0 2 byte, inverted
constexpr uint8_t RADIOLIB_SX126X_GFSK_WHITENING_OFF = 0x00; // 7 0 GFSK data whitening: disabled
constexpr uint8_t RADIOLIB_SX126X_GFSK_WHITENING_ON = 0x01; // 7 0 enabled
constexpr uint8_t RADIOLIB_SX126X_LORA_HEADER_EXPLICIT = 0x00; // 7 0 LoRa header mode: explicit
constexpr uint8_t RADIOLIB_SX126X_LORA_HEADER_IMPLICIT = 0x01; // 7 0 implicit
constexpr uint8_t RADIOLIB_SX126X_LORA_CRC_OFF = 0x00; // 7 0 LoRa CRC mode: disabled
constexpr uint8_t RADIOLIB_SX126X_LORA_CRC_ON = 0x01; // 7 0 enabled
constexpr uint8_t RADIOLIB_SX126X_LORA_IQ_STANDARD = 0x00; // 7 0 LoRa IQ setup: standard
constexpr uint8_t RADIOLIB_SX126X_LORA_IQ_INVERTED = 0x01; // 7 0 inverted
// RADIOLIB_SX126X_CMD_SET_CAD_PARAMS
#define RADIOLIB_SX126X_CAD_ON_1_SYMB 0x00 // 7 0 number of symbols used for CAD: 1
#define RADIOLIB_SX126X_CAD_ON_2_SYMB 0x01 // 7 0 2
#define RADIOLIB_SX126X_CAD_ON_4_SYMB 0x02 // 7 0 4
#define RADIOLIB_SX126X_CAD_ON_8_SYMB 0x03 // 7 0 8
#define RADIOLIB_SX126X_CAD_ON_16_SYMB 0x04 // 7 0 16
#define RADIOLIB_SX126X_CAD_GOTO_STDBY 0x00 // 7 0 after CAD is done, always go to STDBY_RC mode
#define RADIOLIB_SX126X_CAD_GOTO_RX 0x01 // 7 0 after CAD is done, go to Rx mode if activity is detected
#define RADIOLIB_SX126X_CAD_PARAM_DEFAULT 0xFF // 7 0 used by the CAD methods to specify default parameter value
#define RADIOLIB_SX126X_CAD_PARAM_DET_MIN 10 // 7 0 default detMin CAD parameter
// RADIOLIB_SX126X_CMD_GET_STATUS
#define RADIOLIB_SX126X_STATUS_MODE_STDBY_RC 0b00100000 // 6 4 current chip mode: STDBY_RC
#define RADIOLIB_SX126X_STATUS_MODE_STDBY_XOSC 0b00110000 // 6 4 STDBY_XOSC
#define RADIOLIB_SX126X_STATUS_MODE_FS 0b01000000 // 6 4 FS
#define RADIOLIB_SX126X_STATUS_MODE_RX 0b01010000 // 6 4 RX
#define RADIOLIB_SX126X_STATUS_MODE_TX 0b01100000 // 6 4 TX
#define RADIOLIB_SX126X_STATUS_DATA_AVAILABLE 0b00000100 // 3 1 command status: packet received and data can be retrieved
#define RADIOLIB_SX126X_STATUS_CMD_TIMEOUT 0b00000110 // 3 1 SPI command timed out
#define RADIOLIB_SX126X_STATUS_CMD_INVALID 0b00001000 // 3 1 invalid SPI command
#define RADIOLIB_SX126X_STATUS_CMD_FAILED 0b00001010 // 3 1 SPI command failed to execute
#define RADIOLIB_SX126X_STATUS_TX_DONE 0b00001100 // 3 1 packet transmission done
#define RADIOLIB_SX126X_STATUS_SPI_FAILED 0b11111111 // 7 0 SPI transaction failed
// RADIOLIB_SX126X_CMD_GET_PACKET_STATUS
#define RADIOLIB_SX126X_GFSK_RX_STATUS_PREAMBLE_ERR 0b10000000 // 7 7 GFSK Rx status: preamble error
#define RADIOLIB_SX126X_GFSK_RX_STATUS_SYNC_ERR 0b01000000 // 6 6 sync word error
#define RADIOLIB_SX126X_GFSK_RX_STATUS_ADRS_ERR 0b00100000 // 5 5 address error
#define RADIOLIB_SX126X_GFSK_RX_STATUS_CRC_ERR 0b00010000 // 4 4 CRC error
#define RADIOLIB_SX126X_GFSK_RX_STATUS_LENGTH_ERR 0b00001000 // 3 3 length error
#define RADIOLIB_SX126X_GFSK_RX_STATUS_ABORT_ERR 0b00000100 // 2 2 abort error
#define RADIOLIB_SX126X_GFSK_RX_STATUS_PACKET_RECEIVED 0b00000010 // 2 2 packet received
#define RADIOLIB_SX126X_GFSK_RX_STATUS_PACKET_SENT 0b00000001 // 2 2 packet sent
// RADIOLIB_SX126X_CMD_GET_DEVICE_ERRORS
#define RADIOLIB_SX126X_PA_RAMP_ERR 0b100000000 // 8 8 device errors: PA ramping failed
#define RADIOLIB_SX126X_PLL_LOCK_ERR 0b001000000 // 6 6 PLL failed to lock
#define RADIOLIB_SX126X_XOSC_START_ERR 0b000100000 // 5 5 crystal oscillator failed to start
#define RADIOLIB_SX126X_IMG_CALIB_ERR 0b000010000 // 4 4 image calibration failed
#define RADIOLIB_SX126X_ADC_CALIB_ERR 0b000001000 // 3 3 ADC calibration failed
#define RADIOLIB_SX126X_PLL_CALIB_ERR 0b000000100 // 2 2 PLL calibration failed
#define RADIOLIB_SX126X_RC13M_CALIB_ERR 0b000000010 // 1 1 RC13M calibration failed
#define RADIOLIB_SX126X_RC64K_CALIB_ERR 0b000000001 // 0 0 RC64K calibration failed
// RADIOLIB_SX126X_CMD_SET_LBT_SCAN_PARAMS + RADIOLIB_SX126X_CMD_SET_SPECTR_SCAN_PARAMS
#define RADIOLIB_SX126X_SCAN_INTERVAL_7_68_US 10 // 7 0 RSSI reading interval: 7.68 us
#define RADIOLIB_SX126X_SCAN_INTERVAL_8_20_US 11 // 7 0 8.20 us
#define RADIOLIB_SX126X_SCAN_INTERVAL_8_68_US 12 // 7 0 8.68 us
// SX126X SPI register variables
// RADIOLIB_SX126X_REG_HOPPING_ENABLE
#define RADIOLIB_SX126X_HOPPING_ENABLED 0b00000001 // 0 0 intra-packet hopping for LR-FHSS: enabled
#define RADIOLIB_SX126X_HOPPING_DISABLED 0b00000000 // 0 0 (disabled)
// RADIOLIB_SX126X_REG_LORA_SYNC_WORD_MSB + LSB
constexpr uint8_t RADIOLIB_SX126X_SYNC_WORD_PUBLIC = 0x34; // actually 0x3444 NOTE: The low nibbles in each byte (0x_4_4) are masked out since apparently, they're reserved.
constexpr uint8_t RADIOLIB_SX126X_SYNC_WORD_PRIVATE = 0x12; // actually 0x1424 You couldn't make this up if you tried.
// RADIOLIB_SX126X_REG_TX_BITBANG_ENABLE_1
#define RADIOLIB_SX126X_TX_BITBANG_1_DISABLED 0b00000000 // 6 4 Tx bitbang: disabled (default)
#define RADIOLIB_SX126X_TX_BITBANG_1_ENABLED 0b00010000 // 6 4 enabled
// RADIOLIB_SX126X_REG_TX_BITBANG_ENABLE_0
#define RADIOLIB_SX126X_TX_BITBANG_0_DISABLED 0b00000000 // 3 0 Tx bitbang: disabled (default)
#define RADIOLIB_SX126X_TX_BITBANG_0_ENABLED 0b00001100 // 3 0 enabled
// RADIOLIB_SX126X_REG_DIOX_OUT_ENABLE
#define RADIOLIB_SX126X_DIO1_OUT_DISABLED 0b00000010 // 1 1 DIO1 output: disabled
#define RADIOLIB_SX126X_DIO1_OUT_ENABLED 0b00000000 // 1 1 enabled
#define RADIOLIB_SX126X_DIO2_OUT_DISABLED 0b00000100 // 2 2 DIO2 output: disabled
#define RADIOLIB_SX126X_DIO2_OUT_ENABLED 0b00000000 // 2 2 enabled
#define RADIOLIB_SX126X_DIO3_OUT_DISABLED 0b00001000 // 3 3 DIO3 output: disabled
#define RADIOLIB_SX126X_DIO3_OUT_ENABLED 0b00000000 // 3 3 enabled
// RADIOLIB_SX126X_REG_DIOX_IN_ENABLE
#define RADIOLIB_SX126X_DIO1_IN_DISABLED 0b00000000 // 1 1 DIO1 input: disabled
#define RADIOLIB_SX126X_DIO1_IN_ENABLED 0b00000010 // 1 1 enabled
#define RADIOLIB_SX126X_DIO2_IN_DISABLED 0b00000000 // 2 2 DIO2 input: disabled
#define RADIOLIB_SX126X_DIO2_IN_ENABLED 0b00000100 // 2 2 enabled
#define RADIOLIB_SX126X_DIO3_IN_DISABLED 0b00000000 // 3 3 DIO3 input: disabled
#define RADIOLIB_SX126X_DIO3_IN_ENABLED 0b00001000 // 3 3 enabled
// RADIOLIB_SX126X_REG_RX_GAIN
#define RADIOLIB_SX126X_RX_GAIN_BOOSTED 0x96 // 7 0 Rx gain: boosted
#define RADIOLIB_SX126X_RX_GAIN_POWER_SAVING 0x94 // 7 0 power saving
#define RADIOLIB_SX126X_RX_GAIN_SPECTRAL_SCAN 0xCB // 7 0 spectral scan
// RADIOLIB_SX126X_REG_PATCH_UPDATE_ENABLE
#define RADIOLIB_SX126X_PATCH_UPDATE_DISABLED 0b00000000 // 4 4 patch update: disabled
#define RADIOLIB_SX126X_PATCH_UPDATE_ENABLED 0b00010000 // 4 4 enabled
// RADIOLIB_SX126X_REG_SPECTRAL_SCAN_STATUS
#define RADIOLIB_SX126X_SPECTRAL_SCAN_NONE 0x00 // 7 0 spectral scan status: none
#define RADIOLIB_SX126X_SPECTRAL_SCAN_ONGOING 0x0F // 7 0 ongoing
#define RADIOLIB_SX126X_SPECTRAL_SCAN_ABORTED 0xF0 // 7 0 aborted
#define RADIOLIB_SX126X_SPECTRAL_SCAN_COMPLETED 0xFF // 7 0 completed
// RADIOLIB_SX126X_REG_RSSI_AVG_WINDOW
#define RADIOLIB_SX126X_SPECTRAL_SCAN_WINDOW_DEFAULT (0x05 << 2) // 7 0 default RSSI average window
// RADIOLIB_SX126X_REG_ANA_LNA
#define RADIOLIB_SX126X_LNA_RNG_DISABLED 0b00000001 // 0 0 random number: disabled
#define RADIOLIB_SX126X_LNA_RNG_ENABLED 0b00000000 // 0 0 enabled
// RADIOLIB_SX126X_REG_ANA_MIXER
#define RADIOLIB_SX126X_MIXER_RNG_DISABLED 0b00000001 // 7 7 random number: disabled
#define RADIOLIB_SX126X_MIXER_RNG_ENABLED 0b00000000 // 7 7 enabled
// size of the spectral scan result
#define RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE (33)
// RADIOLIB_SX126X_CMD_SET_PA_CONFIG
#define RADIOLIB_SX126X_PA_CONFIG_SX1262 0x00
#endif /* ADC87656_291B_49BB_A611_52533D666023 */

View File

@ -0,0 +1,31 @@
#ifndef B0CD865F_D860_44B7_B289_4F512C770D2B
#define B0CD865F_D860_44B7_B289_4F512C770D2B
/** a hack prevent `clangd` to complain about the error
* while prevent this file from being included by mistake.
*
* one needs to define `__CLANGD__` manually, you could
* do that with `.clangd` configuration file
*/
#ifndef __CLANGD__
#error "this file is a template; copy it to `app_const_llcc68.hpp` and modify the values; then remove this error"
#endif
#include <driver/gpio.h>
namespace app::driver::llcc68 {
/// @brief special pin number for no connection
constexpr auto NC_PIN = GPIO_NUM_NC;
constexpr auto MOSI_PIN = GPIO_NUM_NC;
constexpr auto MISO_PIN = GPIO_NUM_NC;
constexpr auto SCLK_PIN = GPIO_NUM_NC;
constexpr auto BUSY_PIN = GPIO_NUM_NC;
constexpr auto CS_PIN = GPIO_NUM_NC;
constexpr auto RST_PIN = NC_PIN;
constexpr auto DIO1_PIN = GPIO_NUM_NC;
constexpr auto DIO2_PIN = NC_PIN;
constexpr auto DIO3_PIN = NC_PIN;
}
#endif /* B0CD865F_D860_44B7_B289_4F512C770D2B */

413
src/hal_spi.cpp Normal file
View File

@ -0,0 +1,413 @@
//
// Created by Kurosu Chan on 2024/1/18.
//
#include <cstdint>
#include <driver/spi_master.h>
#include <esp_log.h>
#include "esp_err.h"
#include "hal_error.hpp"
#include "hal_gpio.hpp"
#include "hal_spi.hpp"
#include "utils/app_utils.hpp"
#include "utils/app_instant.hpp"
// https://docs.espressif.com/projects/esp-idf/en/v5.3.2/esp32h2/api-reference/peripherals/spi_master.html
// https://github.com/espressif/esp-idf/blob/v5.3.2/examples/peripherals/spi_master/lcd/main/spi_master_example_main.c
namespace app::driver::hal::spi {
constexpr auto SPI_CMD_BIT_SIZE = 8;
constexpr auto SPI_BUFFER_OFFSET_BIT_SIZE = 8;
constexpr auto SPI_REGISTER_ADDR_BIT_SIZE = 16;
constexpr auto MAX_BUFFER_SIZE = 256;
namespace details {
static bool is_initialized = false;
static spi_device_handle_t spi_device;
/// should satisfy most of the control register write/read
/// without overlap with the data buffer
constexpr auto DATA_BUFFER_OFFSET = 16;
static_assert(DATA_BUFFER_OFFSET < MAX_BUFFER_SIZE,
"DATA_BUFFER_OFFSET must be less than MAX_BUFFER_SIZE");
static uint8_t _shared_buffer[MAX_BUFFER_SIZE];
/**
* @brief used in generic stream io
*/
static std::span<uint8_t> shared_buffer = std::span{_shared_buffer};
/**
* @brief only used in read/write buffer io
*/
static std::span<uint8_t> data_buffer = std::span{_shared_buffer + DATA_BUFFER_OFFSET, MAX_BUFFER_SIZE - DATA_BUFFER_OFFSET};
}
static constexpr auto verify_statuses = [](std::span<const uint8_t> statuses) {
uint8_t i = 0;
for (const auto st : statuses) {
if (const auto err = status_to_err(st); err != error::OK) {
ESP_LOGE(TAG, "failed to verify status 0x%02x at byte %u", st, i);
return err;
}
i += 1;
}
return error::OK;
};
std::span<uint8_t> mut_shared_buffer() {
return std::span{details::shared_buffer};
}
void init() {
if (details::is_initialized) {
return;
}
// Configure SPI bus
spi_bus_config_t bus_config = {
.mosi_io_num = MOSI_PIN,
.miso_io_num = MISO_PIN,
.sclk_io_num = SCLK_PIN,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.data4_io_num = -1,
.data5_io_num = -1,
.data6_io_num = -1,
.data7_io_num = -1,
.max_transfer_sz = 0,
.flags = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_GPIO_PINS,
.intr_flags = 0,
};
// Configure SPI device
// https://github.com/nopnop2002/esp-idf-sx126x/blob/main/components/ra01s/ra01s.c
//
// SPI_DEVICE_HALFDUPLEX flag will turn the device into half-duplex
// See also the phase
// https://docs.espressif.com/projects/esp-idf/en/v5.3.2/esp32/api-reference/peripherals/spi_master.html#spi-transactions
spi_device_interface_config_t dev_config = {
.command_bits = 8,
.address_bits = 0,
.dummy_bits = 0,
.mode = 0,
.duty_cycle_pos = 128,
.clock_speed_hz = 10'000'000,
.spics_io_num = CS_PIN,
.flags = SPI_DEVICE_NO_DUMMY,
.queue_size = 1,
};
ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &bus_config, SPI_DMA_CH_AUTO));
ESP_ERROR_CHECK(spi_bus_add_device(SPI2_HOST, &dev_config, &details::spi_device));
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << BUSY_PIN),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
ESP_ERROR_CHECK(gpio_config(&io_conf));
details::is_initialized = true;
}
// https://docs.espressif.com/projects/esp-idf/en/release-v5.3/esp32/api-reference/peripherals/spi_master.html
// https://github.com/espressif/arduino-esp32/blob/5ba4c21a990f46b61ae8c7913b81e15064b2a8ef/cores/esp32/esp32-hal-spi.c#L836-L843
// https://github.com/espressif/esp-idf/issues/5223
/*!
\brief wait for busy pin to go down
\return true if the pin goes down, return false if timeout
\note The LLCC68 uses the pin BUSY to indicate the status of the chip and
its ability (or not) to receive another command while internal processing
occurs. Prior to executing one of the generic functions, it is thus
necessary to check the status of BUSY to make sure the chip is in a state
where it can process another function.
*/
inline bool wait_for_not_busy(const size_t timeout_ms) {
if constexpr (BUSY_PIN == GPIO_NUM_NC) {
app::utils::delay_ms(timeout_ms);
return true;
} else {
const auto io_inst = app::utils::Instant<>{};
while (hal::gpio::digital_read(BUSY_PIN) == hal::gpio::HIGH) {
if (io_inst.elapsed_ms() > timeout_ms) {
return false;
}
}
return true;
}
}
// The communication with the LLCC68 is organized around generic functions which
// allow the user to control the device behavior. Each function is based on an
// Operational Command (refer throughout this document as “Opcode”), which is
// then followed by a set of parameters. The LLCC68 use the BUSY pin to indicate
// the status of the chip. In the following chapters, it is assumed that host
// micro-controller has an SPI and access to it via spi.write(data). Data is an
// 8-bit word. The SPI chip select is defined by NSS, active low.
// Through the SPI interface, the host can issue commands to the chip or access
// the data memory space to directly retrieve or write data. In normal
// operation, a reduced number of direct data write operations is required
// except when accessing the data buffer. The user interacts with the circuit
// through an API (instruction set).
/*! READ */
Result<Unit, error_t>
read_stream(uint8_t cmd, std::span<uint8_t> data, const size_t timeout_ms) {
if (not wait_for_not_busy(timeout_ms)) {
return ue_t{error::TIMEOUT};
}
if (data.size() > MAX_BUFFER_SIZE - 1) {
return ue_t{error::INVALID_SIZE};
}
auto rx_buffer = details::shared_buffer.subspan(0, data.size() + 1);
spi_transaction_ext_t transaction;
transaction = spi_transaction_ext_t{
.base = {
.flags = SPI_TRANS_VARIABLE_CMD,
.cmd = cmd,
.length = static_cast<size_t>(rx_buffer.size() * 8),
.rxlength = static_cast<size_t>(rx_buffer.size() * 8),
.tx_buffer = nullptr,
.rx_buffer = rx_buffer.data(),
},
.command_bits = static_cast<uint8_t>(SPI_CMD_BIT_SIZE),
.address_bits = 0,
.dummy_bits = 0,
};
esp_err_t err = spi_device_transmit(details::spi_device, &transaction.base);
if (err != ESP_OK) {
ESP_LOGE(TAG, "failed to transfer %s (%d)", esp_err_to_name(err), err);
return ue_t{error::FAILED};
}
auto status = rx_buffer[0];
if (const auto err = status_to_err(status); err != error::OK) {
return ue_t{err};
}
std::copy(rx_buffer.begin() + 1, rx_buffer.end(), data.begin());
return {};
}
/*! WRITE */
Result<Unit, error_t>
write_stream(uint8_t cmd, std::span<const uint8_t> data, const size_t timeout_ms) {
if (not wait_for_not_busy(timeout_ms)) {
return ue_t{error::TIMEOUT};
}
if (data.size() > MAX_BUFFER_SIZE) {
return ue_t{error::INVALID_SIZE};
}
const auto rx_buffer = details::shared_buffer.subspan(0, data.size());
spi_transaction_ext_t transaction;
transaction = spi_transaction_ext_t{
.base = {
.flags = SPI_TRANS_VARIABLE_CMD,
.cmd = cmd,
.length = static_cast<size_t>(data.size() * 8),
.rxlength = static_cast<size_t>(rx_buffer.size() * 8),
.tx_buffer = data.data(),
.rx_buffer = rx_buffer.data(),
},
.command_bits = static_cast<uint8_t>(SPI_CMD_BIT_SIZE),
.address_bits = 0,
.dummy_bits = 0,
};
esp_err_t err = spi_device_transmit(details::spi_device, &transaction.base);
if (err != ESP_OK) {
ESP_LOGE(TAG, "failed to transfer %s (%d)", esp_err_to_name(err), err);
return ue_t{error::FAILED};
}
if (const auto err = verify_statuses(rx_buffer); err != error::OK) {
return ue_t{err};
}
return {};
}
/*! READ REGISTER */
Result<Unit, error_t>
read_register(uint16_t reg, std::span<uint8_t> data, const size_t timeout_ms) {
if (not wait_for_not_busy(timeout_ms)) {
return ue_t{error::TIMEOUT};
}
if (data.size() > MAX_BUFFER_SIZE - 1) {
return ue_t{error::INVALID_SIZE};
}
// Note that the host has to send an NOP after sending the 2
// bytes of address to start receiving data bytes on the next NOP sent.
//
// NOTE: I'm not sure if ESP32 would send 0x00 (NOP) after MOSI phase
// if the tx_buffer is nullptr.
spi_transaction_ext_t transaction;
auto rx_buffer = details::shared_buffer.subspan(0, data.size() + 1);
transaction = spi_transaction_ext_t{
.base = {
.flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR,
.cmd = SPI_READ_COMMAND,
.addr = reg,
.length = static_cast<size_t>(rx_buffer.size() * 8),
.rxlength = static_cast<size_t>(rx_buffer.size() * 8),
.tx_buffer = nullptr,
.rx_buffer = rx_buffer.data(),
},
.command_bits = static_cast<uint8_t>(SPI_CMD_BIT_SIZE),
.address_bits = static_cast<uint8_t>(SPI_REGISTER_ADDR_BIT_SIZE),
.dummy_bits = 0,
};
esp_err_t err = spi_device_transmit(details::spi_device, &transaction.base);
if (err != ESP_OK) {
ESP_LOGE(TAG, "failed to transfer %s (%d)", esp_err_to_name(err), err);
return ue_t{error::FAILED};
}
auto status = rx_buffer[0];
if (const auto err = status_to_err(status); err != error::OK) {
return ue_t{err};
}
std::copy(rx_buffer.begin() + 1, rx_buffer.end(), data.begin());
return {};
}
/*! WRITE REGISTER */
Result<Unit, error_t>
write_register(uint16_t offset, std::span<const uint8_t> data, const size_t timeout_ms) {
if (not wait_for_not_busy(timeout_ms)) {
return ue_t{error::TIMEOUT};
}
if (data.size() > MAX_BUFFER_SIZE) {
return ue_t{error::INVALID_SIZE};
}
auto rx_buffer = details::shared_buffer.subspan(0, data.size());
spi_transaction_ext_t transaction;
transaction = spi_transaction_ext_t{
.base = {
.flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR,
.cmd = SPI_WRITE_COMMAND,
.addr = offset,
.length = static_cast<size_t>(data.size() * 8),
.rxlength = static_cast<size_t>(rx_buffer.size() * 8),
.tx_buffer = data.data(),
.rx_buffer = rx_buffer.data(),
},
.command_bits = static_cast<uint8_t>(SPI_CMD_BIT_SIZE),
.address_bits = static_cast<uint8_t>(SPI_REGISTER_ADDR_BIT_SIZE),
.dummy_bits = 0,
};
esp_err_t err = spi_device_transmit(details::spi_device, &transaction.base);
if (err != ESP_OK) {
ESP_LOGE(TAG, "failed to transfer %s (%d)", esp_err_to_name(err), err);
return ue_t{error::FAILED};
}
if (const auto err = verify_statuses(rx_buffer); err != error::OK) {
return ue_t{err};
}
return {};
}
/*! READ BUFFER */
Result<std::span<const uint8_t>, error_t>
read_buffer(uint8_t offset, uint8_t size, const size_t timeout_ms) {
const auto size_required = static_cast<size_t>(size) + 1;
if (size_required > details::data_buffer.size()) {
return ue_t{error::INVALID_SIZE};
}
auto rx_buffer = details::data_buffer.subspan(0, size_required);
spi_transaction_ext_t transaction;
transaction = spi_transaction_ext_t{
.base = {
.flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR,
.cmd = SPI_READ_BUFFER_CMD,
.addr = offset,
.length = static_cast<size_t>(rx_buffer.size() * 8),
.rxlength = static_cast<size_t>(rx_buffer.size() * 8),
.tx_buffer = nullptr,
.rx_buffer = rx_buffer.data(),
},
.command_bits = static_cast<uint8_t>(SPI_CMD_BIT_SIZE),
.address_bits = static_cast<uint8_t>(SPI_BUFFER_OFFSET_BIT_SIZE),
.dummy_bits = 0,
};
esp_err_t err = spi_device_polling_transmit(details::spi_device, &transaction.base);
if (err != ESP_OK) {
ESP_LOGE(TAG, "failed to transfer %s (%d)", esp_err_to_name(err), err);
return ue_t{error::FAILED};
}
const auto status = rx_buffer[0];
if (const auto err = status_to_err(status); err != error::OK) {
return ue_t{err};
}
return std::span{rx_buffer.begin() + 1, rx_buffer.end()};
};
/*! WRITE BUFFER */
Result<Unit, error_t>
write_buffer(uint8_t offset, std::span<const uint8_t> data_from_host, const size_t timeout_ms) {
if (not wait_for_not_busy(timeout_ms)) {
return ue_t{error::TIMEOUT};
}
if (data_from_host.size() > details::data_buffer.size()) {
return ue_t{error::INVALID_SIZE};
}
auto rx_buffer = details::data_buffer.subspan(0, data_from_host.size());
spi_transaction_ext_t transaction;
transaction = spi_transaction_ext_t{
.base = {
.flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR,
.cmd = SPI_WRITE_BUFFER_CMD,
.addr = offset,
.length = static_cast<size_t>(data_from_host.size() * 8),
.rxlength = static_cast<size_t>(rx_buffer.size() * 8),
.tx_buffer = data_from_host.data(),
.rx_buffer = rx_buffer.data(),
},
.command_bits = static_cast<uint8_t>(SPI_CMD_BIT_SIZE),
.address_bits = static_cast<uint8_t>(SPI_BUFFER_OFFSET_BIT_SIZE),
.dummy_bits = 0,
};
esp_err_t err = spi_device_transmit(details::spi_device, &transaction.base);
if (err != ESP_OK) {
ESP_LOGE(TAG, "failed to transfer %s (%d)", esp_err_to_name(err), err);
return ue_t{error::FAILED};
}
if (const auto err = verify_statuses(rx_buffer); err != error::OK) {
return ue_t{err};
}
return {};
}
}

36
src/llcc68.cpp Normal file
View File

@ -0,0 +1,36 @@
//
// Created by Kurosu Chan on 2024/1/17.
//
#include "llcc68.hpp"
#include "driver/gpio.h"
#include "app_const_llcc68.hpp"
constexpr auto ESP_INTR_FLAG_DEFAULT = 0;
namespace app::driver::llcc68 {
namespace details {
uint32_t __tcxo_delay__;
transmit_state_t __tx_state__;
// TODO: use freeRTOS queue to handle received data
bool __dio_flag__ = false;
}
void init_exti() {
constexpr auto isr = [](void *param) {
details::__dio_flag__ = true;
};
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << DIO2_PIN),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_POSEDGE,
};
// https://github.com/espressif/esp-idf/blob/v5.3.2/examples/peripherals/gpio/generic_gpio/main/gpio_example_main.c
ESP_ERROR_CHECK(gpio_config(&io_conf));
gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
gpio_isr_handler_add(DIO2_PIN, isr, nullptr);
}
}