Compare commits
26 Commits
dbeab21efa
...
bcbc7c6776
| Author | SHA1 | Date | |
|---|---|---|---|
| bcbc7c6776 | |||
| 536214241c | |||
| 3bcc5bf273 | |||
| 70a51cdb5c | |||
| 42e6c4d213 | |||
| d80eeb7c3f | |||
| 5d629415f6 | |||
| cecca5b711 | |||
| 59ba0f81d8 | |||
| fc9b06aec0 | |||
| 333d2f5874 | |||
| 92130d4a7a | |||
| 0bdd03aedc | |||
| 1237e5ea48 | |||
| edcdc779d9 | |||
| 059070e146 | |||
| 7637906efe | |||
| 72299b62ce | |||
| c4a61d2708 | |||
| 249c125447 | |||
| ffcaba10fd | |||
| 6269bb99c4 | |||
| 1d31faef01 | |||
| 04efb1e40b | |||
| 0653fc2e3d | |||
| fca2e8cc47 |
@ -9,8 +9,12 @@ idf_component_register(
|
|||||||
app_constant
|
app_constant
|
||||||
)
|
)
|
||||||
|
|
||||||
# This option would make LLCC68 ignore `SPI_CMD_INVALID` error.
|
# Map ESP-IDF Kconfig options to existing compile-time macros used in the codebase.
|
||||||
option(APP_SPI_DISABLE_INVALID_STATUS_CHECK "Disable invalid status check" ON)
|
if (CONFIG_LLCC68_SPI_IGNORE_INVALID_STATUS_CHECK)
|
||||||
if (APP_SPI_DISABLE_INVALID_STATUS_CHECK)
|
|
||||||
target_compile_definitions(${COMPONENT_LIB} PUBLIC APP_SPI_DISABLE_INVALID_STATUS_CHECK)
|
target_compile_definitions(${COMPONENT_LIB} PUBLIC APP_SPI_DISABLE_INVALID_STATUS_CHECK)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (CONFIG_LLCC68_RADIO_DISABLE_CALIBRATION)
|
||||||
|
target_compile_definitions(${COMPONENT_LIB} PUBLIC APP_RADIO_DISABLE_CALIBRATION)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|||||||
19
Kconfig
Normal file
19
Kconfig
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
menu "LLCC68 options"
|
||||||
|
|
||||||
|
config LLCC68_SPI_IGNORE_INVALID_STATUS_CHECK
|
||||||
|
bool "Ignore SPI_CMD_INVALID status in SPI driver"
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
When enabled, the LLCC68 SPI HAL will ignore the SPI_CMD_INVALID
|
||||||
|
status returned from the device. This maps to the compile definition
|
||||||
|
APP_SPI_DISABLE_INVALID_STATUS_CHECK used in the component sources.
|
||||||
|
|
||||||
|
config LLCC68_RADIO_DISABLE_CALIBRATION
|
||||||
|
bool "Disable radio calibration"
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
When enabled, radio calibration routines are disabled to save time
|
||||||
|
or avoid issues on certain hardware. This maps to the compile
|
||||||
|
definition APP_RADIO_DISABLE_CALIBRATION used in the component.
|
||||||
|
|
||||||
|
endmenu
|
||||||
@ -7,6 +7,15 @@ Internal buffer, C++ 20 features is used.
|
|||||||
|
|
||||||
See [app_const_llcc68_template.hpp](inc/template/app_const_llcc68_template.hpp)
|
See [app_const_llcc68_template.hpp](inc/template/app_const_llcc68_template.hpp)
|
||||||
|
|
||||||
|
Configuration via menuconfig:
|
||||||
|
|
||||||
|
- LLCC68 options → Ignore SPI_CMD_INVALID status in SPI driver
|
||||||
|
- Maps to APP_SPI_DISABLE_INVALID_STATUS_CHECK
|
||||||
|
- Default: disabled (N)
|
||||||
|
- LLCC68 options → Disable radio calibration
|
||||||
|
- Maps to APP_RADIO_DISABLE_CALIBRATION
|
||||||
|
- Default: enabled (Y)
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
- [ ] Long-Range Frequency Hopping Spread Spectrum (LR-FHSS)
|
- [ ] Long-Range Frequency Hopping Spread Spectrum (LR-FHSS)
|
||||||
|
|||||||
BIN
docs/DS_LLCC68 V1-1.pdf
LFS
Normal file
BIN
docs/DS_LLCC68 V1-1.pdf
LFS
Normal file
Binary file not shown.
BIN
docs/DS_LLCC68_V1.0.pdf
LFS
BIN
docs/DS_LLCC68_V1.0.pdf
LFS
Binary file not shown.
@ -3,7 +3,8 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
#define APP_ERR_TBL_IT(err) {err, #err}
|
#define APP_ERR_TBL_IT(err) \
|
||||||
|
{ err, #err }
|
||||||
|
|
||||||
namespace app::driver::hal::error {
|
namespace app::driver::hal::error {
|
||||||
using t = int;
|
using t = int;
|
||||||
@ -31,16 +32,18 @@ constexpr t GENERIC_ERR_BASE = 0x120;
|
|||||||
constexpr t AGAIN = GENERIC_ERR_BASE + 1; /*!< Operation failed, retry */
|
constexpr t AGAIN = GENERIC_ERR_BASE + 1; /*!< Operation failed, retry */
|
||||||
constexpr t BUSY = GENERIC_ERR_BASE + 2; /*!< Busy */
|
constexpr t BUSY = GENERIC_ERR_BASE + 2; /*!< Busy */
|
||||||
|
|
||||||
constexpr t SPI_ERR_BASE = 0x1'2000;
|
constexpr t RADIO_TRANS_ERR_BASE = 0x1'2000;
|
||||||
// A transaction from host took too long to complete and triggered an internal watchdog.
|
// 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.
|
// 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;
|
constexpr t RADIO_TRANS_TIMEOUT = RADIO_TRANS_ERR_BASE + 2;
|
||||||
// Processor was unable to process command either because of an invalid opcode or
|
// Processor was unable to process command either because of an invalid opcode or
|
||||||
// because an incorrect number of parameters has been provided.
|
// because an incorrect number of parameters has been provided.
|
||||||
constexpr t SPI_CMD_INVALID = SPI_ERR_BASE + 3;
|
constexpr t RADIO_TRANS_CMD_PROC_ERR = RADIO_TRANS_ERR_BASE + 3;
|
||||||
// The command was successfully processed, however the chip could not execute the command;
|
// 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
|
// 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_TRANS_FAIL_TO_EXE = RADIO_TRANS_ERR_BASE + 4;
|
||||||
|
/*!< Radio is in an expected state (not necessary an error) */
|
||||||
|
constexpr t RADIO_TRANS_INVALID_RADIO_STATE = RADIO_TRANS_ERR_BASE + 5;
|
||||||
|
|
||||||
constexpr t RADIO_ERR_BASE = 0x1'3000;
|
constexpr t RADIO_ERR_BASE = 0x1'3000;
|
||||||
constexpr t RADIO_CHIP_NOT_FOUND = RADIO_ERR_BASE + 1;
|
constexpr t RADIO_CHIP_NOT_FOUND = RADIO_ERR_BASE + 1;
|
||||||
@ -73,9 +76,10 @@ constexpr auto error_table = std::to_array<std::tuple<t, const char *>>(
|
|||||||
APP_ERR_TBL_IT(AGAIN),
|
APP_ERR_TBL_IT(AGAIN),
|
||||||
APP_ERR_TBL_IT(BUSY),
|
APP_ERR_TBL_IT(BUSY),
|
||||||
|
|
||||||
APP_ERR_TBL_IT(SPI_TIMEOUT),
|
APP_ERR_TBL_IT(RADIO_TRANS_TIMEOUT),
|
||||||
APP_ERR_TBL_IT(SPI_CMD_INVALID),
|
APP_ERR_TBL_IT(RADIO_TRANS_CMD_PROC_ERR),
|
||||||
APP_ERR_TBL_IT(SPI_CMD_FAILED),
|
APP_ERR_TBL_IT(RADIO_TRANS_FAIL_TO_EXE),
|
||||||
|
APP_ERR_TBL_IT(RADIO_TRANS_INVALID_RADIO_STATE),
|
||||||
|
|
||||||
APP_ERR_TBL_IT(RADIO_CHIP_NOT_FOUND),
|
APP_ERR_TBL_IT(RADIO_CHIP_NOT_FOUND),
|
||||||
APP_ERR_TBL_IT(RADIO_INVALID_TCXO_VOLTAGE),
|
APP_ERR_TBL_IT(RADIO_INVALID_TCXO_VOLTAGE),
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
#define B7431E31_4075_4B09_B4B3_EAD0234EFB40
|
#define B7431E31_4075_4B09_B4B3_EAD0234EFB40
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <span>
|
#include <span>
|
||||||
|
#include "hal/spi_types.h"
|
||||||
#include "radiolib_definitions.hpp"
|
#include "radiolib_definitions.hpp"
|
||||||
#include "llcc68_definitions.hpp"
|
#include "llcc68_definitions.hpp"
|
||||||
#include "utils/app_result.hpp"
|
#include "utils/app_result.hpp"
|
||||||
@ -16,7 +17,7 @@ namespace app::driver::hal::spi {
|
|||||||
template <typename T, typename E>
|
template <typename T, typename E>
|
||||||
using Result = app::utils::Result<T, E>;
|
using Result = app::utils::Result<T, E>;
|
||||||
using Unit = app::utils::Unit;
|
using Unit = app::utils::Unit;
|
||||||
constexpr auto TAG = "spi";
|
constexpr auto TAG = "app_spi";
|
||||||
|
|
||||||
constexpr auto MOSI_PIN = llcc68::MOSI_PIN;
|
constexpr auto MOSI_PIN = llcc68::MOSI_PIN;
|
||||||
constexpr auto MISO_PIN = llcc68::MISO_PIN;
|
constexpr auto MISO_PIN = llcc68::MISO_PIN;
|
||||||
@ -34,30 +35,49 @@ constexpr uint8_t SPI_READ_BUFFER_CMD = RADIOLIB_SX126X_CMD_READ_BUFFER;
|
|||||||
|
|
||||||
constexpr size_t DEFAULT_TIMEOUT_MS = 1000;
|
constexpr size_t DEFAULT_TIMEOUT_MS = 1000;
|
||||||
|
|
||||||
std::span<uint8_t> mut_shared_buffer();
|
struct spi_config_t {
|
||||||
|
spi_host_device_t host_id;
|
||||||
|
int clock_speed_hz;
|
||||||
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief Initialize the SPI interface.
|
\brief Initialize the SPI interface.
|
||||||
*/
|
*/
|
||||||
void init();
|
void init(spi_config_t config = {SPI2_HOST, 4'000'000});
|
||||||
|
|
||||||
|
|
||||||
inline error_t status_to_err(const uint8_t status) {
|
inline error_t status_to_err(const uint8_t status) {
|
||||||
const auto st = *reinterpret_cast<const llcc68::status_t *>(&status);
|
const auto st = *reinterpret_cast<const llcc68::status_t *>(&status);
|
||||||
switch (st.command_status) {
|
error_t status_ok = [](llcc68::CommandStatus st) {
|
||||||
|
switch (st) {
|
||||||
case llcc68::CommandStatus::COMMAND_TIMEOUT:
|
case llcc68::CommandStatus::COMMAND_TIMEOUT:
|
||||||
return error::SPI_TIMEOUT;
|
return error::RADIO_TRANS_TIMEOUT;
|
||||||
case llcc68::CommandStatus::FAILURE_TO_EXECUTE_COMMAND:
|
case llcc68::CommandStatus::FAILURE_TO_EXECUTE_COMMAND:
|
||||||
return error::SPI_CMD_FAILED;
|
return error::RADIO_TRANS_FAIL_TO_EXE;
|
||||||
case llcc68::CommandStatus::COMMAND_PROCESSING_ERROR:
|
case llcc68::CommandStatus::COMMAND_PROCESSING_ERROR:
|
||||||
#ifdef APP_SPI_DISABLE_INVALID_STATUS_CHECK
|
#ifdef APP_SPI_DISABLE_INVALID_STATUS_CHECK
|
||||||
return error::OK;
|
return error::OK;
|
||||||
#else
|
#else
|
||||||
return error::SPI_CMD_INVALID;
|
return error::RADIO_TRANS_CMD_PROC_ERR;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
return error::OK;
|
return error::OK;
|
||||||
}
|
}
|
||||||
|
}(st.command_status);
|
||||||
|
if (status_ok != error::OK) {
|
||||||
|
return status_ok;
|
||||||
|
}
|
||||||
|
error_t chip_mode_usual = [](llcc68::ChipMode mode) {
|
||||||
|
switch (mode) {
|
||||||
|
case llcc68::ChipMode::TX:
|
||||||
|
case llcc68::ChipMode::RX:
|
||||||
|
case llcc68::ChipMode::STBY_RC:
|
||||||
|
return error::OK;
|
||||||
|
default:
|
||||||
|
return error::RADIO_TRANS_INVALID_RADIO_STATE;
|
||||||
|
}
|
||||||
|
}(st.chip_mode);
|
||||||
|
return chip_mode_usual;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|||||||
592
inc/llcc68.hpp
592
inc/llcc68.hpp
File diff suppressed because it is too large
Load Diff
@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
#ifndef LLCC68_DEFINITIONS_H
|
#ifndef LLCC68_DEFINITIONS_H
|
||||||
#define LLCC68_DEFINITIONS_H
|
#define LLCC68_DEFINITIONS_H
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <format>
|
#include <format>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
@ -28,7 +28,32 @@ enum LoRaBandwidth : uint8_t {
|
|||||||
BW_500_0 = 0x06,
|
BW_500_0 = 0x06,
|
||||||
};
|
};
|
||||||
|
|
||||||
inline const char *bw_to_string(const LoRaBandwidth &bw) {
|
// - RampTime Value RampTime (µs)
|
||||||
|
// - SET_RAMP_10U 0x00 10
|
||||||
|
// - SET_RAMP_20U 0x01 20
|
||||||
|
// - SET_RAMP_ 40U 0x02 40
|
||||||
|
// - SET_RAMP_80U 0x03 80
|
||||||
|
// - SET_RAMP_200U 0x04 200
|
||||||
|
// - SET_RAMP_800U 0x05 800
|
||||||
|
// - SET_RAMP_1700U 0x06 1700
|
||||||
|
// - SET_RAMP_3400U 0x07 3400
|
||||||
|
enum TxRampTime : uint8_t {
|
||||||
|
SET_RAMP_10U = 0x00,
|
||||||
|
SET_RAMP_20U = 0x01,
|
||||||
|
SET_RAMP_40U = 0x02,
|
||||||
|
SET_RAMP_80U = 0x03,
|
||||||
|
SET_RAMP_200U = 0x04,
|
||||||
|
SET_RAMP_800U = 0x05,
|
||||||
|
SET_RAMP_1700U = 0x06,
|
||||||
|
SET_RAMP_3400U = 0x07,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum RegulatorMode : uint8_t {
|
||||||
|
REGULATOR_LDO = RADIOLIB_SX126X_REGULATOR_LDO,
|
||||||
|
REGULATOR_DC_DC = RADIOLIB_SX126X_REGULATOR_DC_DC,
|
||||||
|
};
|
||||||
|
|
||||||
|
inline const char *to_str(LoRaBandwidth bw) {
|
||||||
switch (bw) {
|
switch (bw) {
|
||||||
case LoRaBandwidth::BW_7_8:
|
case LoRaBandwidth::BW_7_8:
|
||||||
return "7.8kHz";
|
return "7.8kHz";
|
||||||
@ -62,7 +87,7 @@ enum LoRaCodingRate : uint8_t {
|
|||||||
CR_4_8 = 0x04,
|
CR_4_8 = 0x04,
|
||||||
};
|
};
|
||||||
|
|
||||||
inline const char *cr_to_string(const LoRaCodingRate &cr) {
|
inline const char *to_str(LoRaCodingRate cr) {
|
||||||
switch (cr) {
|
switch (cr) {
|
||||||
case LoRaCodingRate::CR_4_5:
|
case LoRaCodingRate::CR_4_5:
|
||||||
return "4/5";
|
return "4/5";
|
||||||
@ -112,11 +137,12 @@ struct modulation_params_t {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string to_string() const {
|
[[nodiscard]]
|
||||||
|
auto to_string() const -> std::string {
|
||||||
return std::format("mod_params: bw={}, sf={}, cr={}, ldr_optimize={}",
|
return std::format("mod_params: bw={}, sf={}, cr={}, ldr_optimize={}",
|
||||||
bw_to_string(static_cast<LoRaBandwidth>(bw)),
|
to_str(static_cast<LoRaBandwidth>(bw)),
|
||||||
sf,
|
sf,
|
||||||
cr_to_string(static_cast<LoRaCodingRate>(cr)),
|
to_str(static_cast<LoRaCodingRate>(cr)),
|
||||||
ldr_optimize ? "ON" : "OFF");
|
ldr_optimize ? "ON" : "OFF");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -137,9 +163,94 @@ struct packet_params_t {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct pa_setting_t {
|
||||||
|
uint8_t pa_duty_cycle;
|
||||||
|
uint8_t hp_max;
|
||||||
|
uint8_t device_sel;
|
||||||
|
|
||||||
|
static constexpr pa_setting_t Default22dBm() {
|
||||||
|
return {0x04, 0x07, 0x00};
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @note SetTxParams should be set to +22dBm
|
||||||
|
*/
|
||||||
|
static constexpr pa_setting_t Default20dBm() {
|
||||||
|
return {0x03, 0x05, 0x00};
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @note SetTxParams should be set to +22dBm
|
||||||
|
*/
|
||||||
|
static constexpr pa_setting_t Default17dBm() {
|
||||||
|
return {0x02, 0x03, 0x00};
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @note SetTxParams should be set to +14dBm
|
||||||
|
*/
|
||||||
|
static constexpr pa_setting_t Default14dBm() {
|
||||||
|
return {0x02, 0x02, 0x00};
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr pa_setting_t Default() {
|
||||||
|
return Default22dBm();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline const char *to_str(llcc68::TxRampTime ramp_time) {
|
||||||
|
switch (ramp_time) {
|
||||||
|
case llcc68::SET_RAMP_10U:
|
||||||
|
return "10us";
|
||||||
|
case llcc68::SET_RAMP_20U:
|
||||||
|
return "20us";
|
||||||
|
case llcc68::SET_RAMP_40U:
|
||||||
|
return "40us";
|
||||||
|
case llcc68::SET_RAMP_80U:
|
||||||
|
return "80us";
|
||||||
|
case llcc68::SET_RAMP_200U:
|
||||||
|
return "200us";
|
||||||
|
case llcc68::SET_RAMP_800U:
|
||||||
|
return "800us";
|
||||||
|
case llcc68::SET_RAMP_1700U:
|
||||||
|
return "1700us";
|
||||||
|
case llcc68::SET_RAMP_3400U:
|
||||||
|
return "3400us";
|
||||||
|
default:
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct tx_params_t {
|
||||||
|
/**
|
||||||
|
* @brief magic number for automatic max TX power depending on
|
||||||
|
* chip type
|
||||||
|
*/
|
||||||
|
static constexpr auto TX_POWER_AUTO = 0x7f;
|
||||||
|
static constexpr auto MAX_POWER_HIGH_PA = 22;
|
||||||
|
static constexpr auto MAX_POWER_LOW_PA = 14;
|
||||||
|
|
||||||
|
int8_t power;
|
||||||
|
TxRampTime ramp_time;
|
||||||
|
|
||||||
|
static tx_params_t Default() {
|
||||||
|
return tx_params_t{
|
||||||
|
.power = TX_POWER_AUTO,
|
||||||
|
.ramp_time = SET_RAMP_200U,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
[[nodiscard]]
|
||||||
|
std::string to_string() const {
|
||||||
|
if (power == TX_POWER_AUTO) {
|
||||||
|
return "tx_params: power=auto, ramp_time=" + std::string(to_str(ramp_time));
|
||||||
|
}
|
||||||
|
return std::format("tx_params: power={}, ramp_time={}",
|
||||||
|
power,
|
||||||
|
to_str(ramp_time));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct lora_parameters_t {
|
struct lora_parameters_t {
|
||||||
modulation_params_t mod_params;
|
modulation_params_t mod_params;
|
||||||
packet_params_t packet_params;
|
packet_params_t packet_params;
|
||||||
|
tx_params_t tx_params;
|
||||||
freq_t frequency;
|
freq_t frequency;
|
||||||
uint8_t sync_word;
|
uint8_t sync_word;
|
||||||
|
|
||||||
@ -147,6 +258,7 @@ struct lora_parameters_t {
|
|||||||
return lora_parameters_t{
|
return lora_parameters_t{
|
||||||
.mod_params = modulation_params_t::Default(),
|
.mod_params = modulation_params_t::Default(),
|
||||||
.packet_params = packet_params_t::Default(),
|
.packet_params = packet_params_t::Default(),
|
||||||
|
.tx_params = tx_params_t::Default(),
|
||||||
.frequency = DEFAULT_FREQUENCY,
|
.frequency = DEFAULT_FREQUENCY,
|
||||||
.sync_word = DEFAULT_SYNC_WORD,
|
.sync_word = DEFAULT_SYNC_WORD,
|
||||||
};
|
};
|
||||||
@ -225,6 +337,27 @@ enum class CommandStatus : uint8_t {
|
|||||||
COMMAND_TX_DONE = 0x06, // 0b110
|
COMMAND_TX_DONE = 0x06, // 0b110
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline const char *to_str(const CommandStatus &status) {
|
||||||
|
switch (status) {
|
||||||
|
case CommandStatus::RESERVED:
|
||||||
|
return "RESERVED";
|
||||||
|
case CommandStatus::RFU:
|
||||||
|
return "RFU";
|
||||||
|
case CommandStatus::DATA_AVAILABLE:
|
||||||
|
return "DATA_AVAILABLE";
|
||||||
|
case CommandStatus::COMMAND_TIMEOUT:
|
||||||
|
return "COMMAND_TIMEOUT";
|
||||||
|
case CommandStatus::COMMAND_PROCESSING_ERROR:
|
||||||
|
return "COMMAND_PROCESSING_ERROR";
|
||||||
|
case CommandStatus::FAILURE_TO_EXECUTE_COMMAND:
|
||||||
|
return "FAILURE_TO_EXECUTE_COMMAND";
|
||||||
|
case CommandStatus::COMMAND_TX_DONE:
|
||||||
|
return "COMMAND_TX_DONE";
|
||||||
|
default:
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum class ChipMode : uint8_t {
|
enum class ChipMode : uint8_t {
|
||||||
UNUSED = 0x00, // 0b000
|
UNUSED = 0x00, // 0b000
|
||||||
RFU = 0x01, // 0b001
|
RFU = 0x01, // 0b001
|
||||||
@ -235,12 +368,35 @@ enum class ChipMode : uint8_t {
|
|||||||
TX = 0x06, // 0b110
|
TX = 0x06, // 0b110
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline const char *to_str(const ChipMode &mode) {
|
||||||
|
switch (mode) {
|
||||||
|
case ChipMode::UNUSED:
|
||||||
|
return "UNUSED";
|
||||||
|
case ChipMode::RFU:
|
||||||
|
return "RFU";
|
||||||
|
case ChipMode::STBY_RC:
|
||||||
|
return "STBY_RC";
|
||||||
|
case ChipMode::STBY_XOSC:
|
||||||
|
return "STBY_XOSC";
|
||||||
|
case ChipMode::FS:
|
||||||
|
return "FS";
|
||||||
|
case ChipMode::RX:
|
||||||
|
return "RX";
|
||||||
|
case ChipMode::TX:
|
||||||
|
return "TX";
|
||||||
|
default:
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
struct __attribute__((packed)) status_t {
|
struct __attribute__((packed)) status_t {
|
||||||
|
// LSB
|
||||||
uint8_t reserved_1 : 1;
|
uint8_t reserved_1 : 1;
|
||||||
CommandStatus command_status : 3;
|
CommandStatus command_status : 3;
|
||||||
ChipMode chip_mode : 3;
|
ChipMode chip_mode : 3;
|
||||||
uint8_t reserved_2 : 1;
|
uint8_t reserved_2 : 1;
|
||||||
|
// MSB
|
||||||
};
|
};
|
||||||
static_assert(sizeof(status_t) == 1);
|
static_assert(sizeof(status_t) == 1);
|
||||||
|
|
||||||
@ -251,21 +407,24 @@ struct __attribute__((packed)) lora_packet_status_t {
|
|||||||
uint8_t rssi_pkt;
|
uint8_t rssi_pkt;
|
||||||
// Estimation of SNR on last packet received in two’s compliment format multiplied by 4.
|
// Estimation of SNR on last packet received in two’s compliment format multiplied by 4.
|
||||||
// Actual SNR in dB =SnrPkt/4
|
// Actual SNR in dB =SnrPkt/4
|
||||||
uint8_t snr_pkt;
|
int8_t snr_pkt;
|
||||||
// Estimation of RSSI of the LoRa® signal (after despreading) on last packet received.
|
// Estimation of RSSI of the LoRa® signal (after despreading) on last packet received.
|
||||||
// Actual Rssi in dB = -SignalRssiPkt/2
|
// Actual Rssi in dB = -SignalRssiPkt/2
|
||||||
uint8_t signal_rssi_pkt;
|
uint8_t signal_rssi_pkt;
|
||||||
|
|
||||||
float rssi_pkt_dbm() const {
|
[[nodiscard]]
|
||||||
return -rssi_pkt / 2.0f;
|
auto rssi_pkt_dbm() const -> float {
|
||||||
|
return -rssi_pkt / 2.0F;
|
||||||
}
|
}
|
||||||
|
|
||||||
float snr_pkt_db() const {
|
[[nodiscard]]
|
||||||
return snr_pkt / 4.0f;
|
auto snr_pkt_db() const -> float {
|
||||||
|
return snr_pkt / 4.0F;
|
||||||
}
|
}
|
||||||
|
|
||||||
float signal_rssi_pkt_dbm() const {
|
[[nodiscard]]
|
||||||
return -signal_rssi_pkt / 2.0f;
|
auto signal_rssi_pkt_dbm() const -> float {
|
||||||
|
return -signal_rssi_pkt / 2.0F;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -296,20 +455,61 @@ union packet_status_t {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct __attribute__((packed)) op_error_t {
|
struct __attribute__((packed)) DeviceErrors {
|
||||||
bool RC64K_CALIB_ERR : 1; // RC64k calibration failed
|
// LSB
|
||||||
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);
|
bool RC64K_CALIB_ERR : 1; // [0] RC64k calibration failed
|
||||||
|
bool RC13M_CALIB_ERR : 1; // [1] RkC13M calibration failed
|
||||||
|
bool PLL_CALIB_ERR : 1; // [2] PLL calibration failed
|
||||||
|
bool ADC_CALIB_ERR : 1; // [3] ADC calibration failed
|
||||||
|
bool IMG_CALIB_ERR : 1; // [4] IMG calibration failed
|
||||||
|
bool XOSC_START_ERR : 1; // [5] XOSC failed to start
|
||||||
|
bool PLL_LOCK_ERR : 1; // [6] PLL failed to lock
|
||||||
|
uint8_t rfu_1 : 1; // [7] RFU
|
||||||
|
bool PA_RAMP_ERR : 1; // [8] PA ramping failed
|
||||||
|
uint8_t rfu_2 : 7; // [15:9] RFU
|
||||||
|
|
||||||
|
// MSB
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::string to_string() const {
|
||||||
|
auto ss = std::stringstream();
|
||||||
|
ss << "DeviceErrors{";
|
||||||
|
if (RC64K_CALIB_ERR) {
|
||||||
|
ss << "RC64K_CALIB_ERR(0) ";
|
||||||
|
}
|
||||||
|
if (RC13M_CALIB_ERR) {
|
||||||
|
ss << "RC13M_CALIB_ERR(1) ";
|
||||||
|
}
|
||||||
|
if (PLL_CALIB_ERR) {
|
||||||
|
ss << "PLL_CALIB_ERR(2) ";
|
||||||
|
}
|
||||||
|
if (ADC_CALIB_ERR) {
|
||||||
|
ss << "ADC_CALIB_ERR(3) ";
|
||||||
|
}
|
||||||
|
if (IMG_CALIB_ERR) {
|
||||||
|
ss << "IMG_CALIB_ERR(4) ";
|
||||||
|
}
|
||||||
|
if (XOSC_START_ERR) {
|
||||||
|
ss << "XOSC_START_ERR(5) ";
|
||||||
|
}
|
||||||
|
if (PLL_LOCK_ERR) {
|
||||||
|
ss << "PLL_LOCK_ERR(6) ";
|
||||||
|
}
|
||||||
|
if (PA_RAMP_ERR) {
|
||||||
|
ss << "PA_RAMP_ERR(8) ";
|
||||||
|
}
|
||||||
|
ss << "}";
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
bool has_any_error() const {
|
||||||
|
return RC64K_CALIB_ERR || RC13M_CALIB_ERR || PLL_CALIB_ERR || ADC_CALIB_ERR ||
|
||||||
|
IMG_CALIB_ERR || XOSC_START_ERR || PLL_LOCK_ERR || PA_RAMP_ERR;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static_assert(sizeof(DeviceErrors) == 2);
|
||||||
|
|
||||||
constexpr bool in_range(const auto v, const auto min, const auto max) {
|
constexpr bool in_range(const auto v, const auto min, const auto max) {
|
||||||
return v >= min && v <= max;
|
return v >= min && v <= max;
|
||||||
@ -344,10 +544,7 @@ cr_to_ratio(const uint8_t cr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constexpr bool valid_ldr_optimize(const uint8_t ldr_optimize) {
|
constexpr bool valid_ldr_optimize(const uint8_t ldr_optimize) {
|
||||||
if (ldr_optimize > RADIOLIB_SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_ON) {
|
return static_cast<bool>(ldr_optimize <= RADIOLIB_SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_ON);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr bool valid_bw(const uint8_t bw) {
|
constexpr bool valid_bw(const uint8_t bw) {
|
||||||
@ -372,25 +569,25 @@ using bw_t = float;
|
|||||||
constexpr bw_t bw_khz(const uint8_t bw) {
|
constexpr bw_t bw_khz(const uint8_t bw) {
|
||||||
switch (bw) {
|
switch (bw) {
|
||||||
case RADIOLIB_SX126X_LORA_BW_7_8:
|
case RADIOLIB_SX126X_LORA_BW_7_8:
|
||||||
return bw_t{7.8f};
|
return bw_t{7.8F};
|
||||||
case RADIOLIB_SX126X_LORA_BW_10_4:
|
case RADIOLIB_SX126X_LORA_BW_10_4:
|
||||||
return bw_t{10.4f};
|
return bw_t{10.4F};
|
||||||
case RADIOLIB_SX126X_LORA_BW_15_6:
|
case RADIOLIB_SX126X_LORA_BW_15_6:
|
||||||
return bw_t{15.6f};
|
return bw_t{15.6F};
|
||||||
case RADIOLIB_SX126X_LORA_BW_20_8:
|
case RADIOLIB_SX126X_LORA_BW_20_8:
|
||||||
return bw_t{20.8f};
|
return bw_t{20.8F};
|
||||||
case RADIOLIB_SX126X_LORA_BW_31_25:
|
case RADIOLIB_SX126X_LORA_BW_31_25:
|
||||||
return bw_t{31.25f};
|
return bw_t{31.25F};
|
||||||
case RADIOLIB_SX126X_LORA_BW_41_7:
|
case RADIOLIB_SX126X_LORA_BW_41_7:
|
||||||
return bw_t{41.7f};
|
return bw_t{41.7F};
|
||||||
case RADIOLIB_SX126X_LORA_BW_62_5:
|
case RADIOLIB_SX126X_LORA_BW_62_5:
|
||||||
return bw_t{62.5f};
|
return bw_t{62.5F};
|
||||||
case RADIOLIB_SX126X_LORA_BW_125_0:
|
case RADIOLIB_SX126X_LORA_BW_125_0:
|
||||||
return bw_t{125.0f};
|
return bw_t{125.0F};
|
||||||
case RADIOLIB_SX126X_LORA_BW_250_0:
|
case RADIOLIB_SX126X_LORA_BW_250_0:
|
||||||
return bw_t{250.0f};
|
return bw_t{250.0F};
|
||||||
case RADIOLIB_SX126X_LORA_BW_500_0:
|
case RADIOLIB_SX126X_LORA_BW_500_0:
|
||||||
return bw_t{500.0f};
|
return bw_t{500.0F};
|
||||||
default:
|
default:
|
||||||
return bw_t{0};
|
return bw_t{0};
|
||||||
}
|
}
|
||||||
@ -412,6 +609,38 @@ constexpr bool valid_sf(const uint8_t bw, const uint8_t sf) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr bool valid_tx_power(int8_t power) {
|
||||||
|
if (power == tx_params_t::TX_POWER_AUTO) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (power >= -9 && power <= 22) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (power >= -17 && power <= 14) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool valid_ramp_time(const llcc68::TxRampTime ramp_time) {
|
||||||
|
switch (ramp_time) {
|
||||||
|
case llcc68::SET_RAMP_10U:
|
||||||
|
case llcc68::SET_RAMP_20U:
|
||||||
|
case llcc68::SET_RAMP_40U:
|
||||||
|
case llcc68::SET_RAMP_80U:
|
||||||
|
case llcc68::SET_RAMP_200U:
|
||||||
|
case llcc68::SET_RAMP_800U:
|
||||||
|
case llcc68::SET_RAMP_1700U:
|
||||||
|
case llcc68::SET_RAMP_3400U:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
constexpr bool valid_freq(const freq_t freq) {
|
constexpr bool valid_freq(const freq_t freq) {
|
||||||
return in_range(freq, freq_t{150.0}, freq_t{960.0});
|
return in_range(freq, freq_t{150.0}, freq_t{960.0});
|
||||||
}
|
}
|
||||||
|
|||||||
175
src/hal_spi.cpp
175
src/hal_spi.cpp
@ -10,41 +10,40 @@
|
|||||||
#include "hal_gpio.hpp"
|
#include "hal_gpio.hpp"
|
||||||
#include "hal_spi.hpp"
|
#include "hal_spi.hpp"
|
||||||
#include "utils/app_utils.hpp"
|
#include "utils/app_utils.hpp"
|
||||||
#include "utils/app_instant.hpp"
|
#include "utils/app_clock_instant.hpp"
|
||||||
// https://docs.espressif.com/projects/esp-idf/en/v5.3.2/esp32h2/api-reference/peripherals/spi_master.html
|
// 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
|
// 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 {
|
namespace app::driver::hal::spi {
|
||||||
constexpr auto SPI_CMD_BIT_SIZE = 8;
|
static constexpr uint8_t CMD_BIT_SIZE = 8;
|
||||||
constexpr auto SPI_BUFFER_OFFSET_BIT_SIZE = 8;
|
static constexpr uint8_t OFFSET_BIT_SIZE = 8;
|
||||||
constexpr auto SPI_REGISTER_ADDR_BIT_SIZE = 16;
|
static constexpr uint8_t REG_ADDR_BIT_SIZE = 16;
|
||||||
constexpr auto MAX_BUFFER_SIZE = 256;
|
|
||||||
|
static constexpr auto MAX_BUFFER_SIZE = 256;
|
||||||
|
|
||||||
namespace details {
|
namespace details {
|
||||||
static bool is_initialized = false;
|
static bool is_initialized = false;
|
||||||
static spi_device_handle_t spi_device;
|
static spi_device_handle_t spi_device;
|
||||||
|
|
||||||
/// should satisfy most of the control register write/read
|
static uint8_t _data_buffer[MAX_BUFFER_SIZE];
|
||||||
/// 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
|
* @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 std::span<uint8_t> data_buffer = std::span{_data_buffer};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a traditional way to complete an synchronous SPI transaction
|
||||||
|
*
|
||||||
|
* @sa https://github.com/nopnop2002/esp-idf-sx126x/blob/bd26fd20b118c6d57d35b0554d8de80378d614a8/components/ra01s/ra01s.c#L123-L161
|
||||||
|
* @sa https://github.com/IanBurwell/DynamicLRS/blob/c26f7f8dcca0c1b70af0aa6aee3aba3a1652aba6/components/DLRS_LoRadio/radiolib_esp32s3_hal.hpp#L193-L201
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
static constexpr auto verify_statuses = [](std::span<const uint8_t> statuses) {
|
static constexpr auto verify_statuses = [](std::span<const uint8_t> statuses) {
|
||||||
uint8_t i = 0;
|
uint8_t i = 0;
|
||||||
for (const auto st : statuses) {
|
for (const auto st : statuses) {
|
||||||
if (const auto err = status_to_err(st); err != error::OK) {
|
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);
|
ESP_LOGE(TAG, "s[%u]=0x%02x", i, st);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
i += 1;
|
i += 1;
|
||||||
@ -52,11 +51,7 @@ static constexpr auto verify_statuses = [](std::span<const uint8_t> statuses) {
|
|||||||
return error::OK;
|
return error::OK;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::span<uint8_t> mut_shared_buffer() {
|
void init(spi_config_t config) {
|
||||||
return std::span{details::shared_buffer};
|
|
||||||
}
|
|
||||||
|
|
||||||
void init() {
|
|
||||||
if (details::is_initialized) {
|
if (details::is_initialized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -84,19 +79,19 @@ void init() {
|
|||||||
// See also the phase
|
// See also the phase
|
||||||
// https://docs.espressif.com/projects/esp-idf/en/v5.3.2/esp32/api-reference/peripherals/spi_master.html#spi-transactions
|
// 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 = {
|
spi_device_interface_config_t dev_config = {
|
||||||
.command_bits = 8,
|
.command_bits = 0,
|
||||||
.address_bits = 0,
|
.address_bits = 0,
|
||||||
.dummy_bits = 0,
|
.dummy_bits = 0,
|
||||||
.mode = 0,
|
.mode = 0,
|
||||||
.duty_cycle_pos = 128,
|
.duty_cycle_pos = 128,
|
||||||
.clock_speed_hz = 10'000'000,
|
.clock_speed_hz = config.clock_speed_hz,
|
||||||
.spics_io_num = CS_PIN,
|
.spics_io_num = CS_PIN,
|
||||||
.flags = SPI_DEVICE_NO_DUMMY,
|
.flags = SPI_DEVICE_NO_DUMMY,
|
||||||
.queue_size = 1,
|
.queue_size = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &bus_config, SPI_DMA_CH_AUTO));
|
ESP_ERROR_CHECK(spi_bus_initialize(config.host_id, &bus_config, SPI_DMA_CH_AUTO));
|
||||||
ESP_ERROR_CHECK(spi_bus_add_device(SPI2_HOST, &dev_config, &details::spi_device));
|
ESP_ERROR_CHECK(spi_bus_add_device(config.host_id, &dev_config, &details::spi_device));
|
||||||
|
|
||||||
|
|
||||||
gpio_config_t io_conf = {
|
gpio_config_t io_conf = {
|
||||||
@ -128,7 +123,7 @@ inline bool wait_for_not_busy(const size_t timeout_ms) {
|
|||||||
app::utils::delay_ms(timeout_ms);
|
app::utils::delay_ms(timeout_ms);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
const auto io_inst = app::utils::Instant<>{};
|
const auto io_inst = app::utils::Instant{};
|
||||||
while (hal::gpio::digital_read(BUSY_PIN) == hal::gpio::HIGH) {
|
while (hal::gpio::digital_read(BUSY_PIN) == hal::gpio::HIGH) {
|
||||||
if (io_inst.elapsed_ms() > timeout_ms) {
|
if (io_inst.elapsed_ms() > timeout_ms) {
|
||||||
return false;
|
return false;
|
||||||
@ -155,44 +150,58 @@ inline bool wait_for_not_busy(const size_t timeout_ms) {
|
|||||||
|
|
||||||
/*! READ */
|
/*! READ */
|
||||||
|
|
||||||
|
constexpr auto STACK_BUFFER_SIZE = 32;
|
||||||
|
constexpr auto PADDING = 0xFE;
|
||||||
|
|
||||||
Result<Unit, error_t>
|
Result<Unit, error_t>
|
||||||
read_stream(uint8_t cmd, std::span<uint8_t> data, const size_t timeout_ms) {
|
read_stream(uint8_t cmd, std::span<uint8_t> data, const size_t timeout_ms) {
|
||||||
|
std::array<uint8_t, STACK_BUFFER_SIZE> _rx_buf;
|
||||||
|
std::array<uint8_t, STACK_BUFFER_SIZE> _tx_buf;
|
||||||
|
|
||||||
|
if (data.empty() or data.size() > STACK_BUFFER_SIZE - 1) {
|
||||||
|
return ue_t{error::INVALID_SIZE};
|
||||||
|
}
|
||||||
|
|
||||||
if (not wait_for_not_busy(timeout_ms)) {
|
if (not wait_for_not_busy(timeout_ms)) {
|
||||||
return ue_t{error::TIMEOUT};
|
return ue_t{error::TIMEOUT};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.size() > MAX_BUFFER_SIZE - 1) {
|
auto rx_buffer = std::span{_rx_buf}.subspan(0, data.size() + 1);
|
||||||
return ue_t{error::INVALID_SIZE};
|
auto tx_buffer = std::span{_tx_buf}.subspan(0, rx_buffer.size());
|
||||||
}
|
std::ranges::fill(tx_buffer, PADDING);
|
||||||
|
|
||||||
auto rx_buffer = details::shared_buffer.subspan(0, data.size() + 1);
|
spi_transaction_ext_t transaction{};
|
||||||
|
// during the Command and Address phases, the members spi_transaction_t::cmd
|
||||||
spi_transaction_ext_t transaction;
|
// and spi_transaction_t::addr are sent to the bus, nothing is read at this
|
||||||
|
// time.
|
||||||
transaction = spi_transaction_ext_t{
|
transaction = spi_transaction_ext_t{
|
||||||
.base = {
|
.base = {
|
||||||
.flags = SPI_TRANS_VARIABLE_CMD,
|
.flags = SPI_TRANS_VARIABLE_CMD,
|
||||||
.cmd = cmd,
|
.cmd = cmd,
|
||||||
|
// total data length, in bits
|
||||||
.length = static_cast<size_t>(rx_buffer.size() * 8),
|
.length = static_cast<size_t>(rx_buffer.size() * 8),
|
||||||
|
// total data length received, should be not greater than length in
|
||||||
|
// full-duplex mode (0 defaults this to the value of length).
|
||||||
.rxlength = static_cast<size_t>(rx_buffer.size() * 8),
|
.rxlength = static_cast<size_t>(rx_buffer.size() * 8),
|
||||||
.tx_buffer = nullptr,
|
.tx_buffer = tx_buffer.data(),
|
||||||
.rx_buffer = rx_buffer.data(),
|
.rx_buffer = rx_buffer.data(),
|
||||||
},
|
},
|
||||||
.command_bits = static_cast<uint8_t>(SPI_CMD_BIT_SIZE),
|
.command_bits = CMD_BIT_SIZE,
|
||||||
.address_bits = 0,
|
.address_bits = 0,
|
||||||
.dummy_bits = 0,
|
.dummy_bits = 0,
|
||||||
};
|
};
|
||||||
esp_err_t err = spi_device_transmit(details::spi_device, &transaction.base);
|
esp_err_t err = spi_device_transmit(details::spi_device, &transaction.base);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "failed to transfer %s (%d)", esp_err_to_name(err), err);
|
ESP_LOGE(TAG, "spi transmit: %s (%d)", esp_err_to_name(err), err);
|
||||||
return ue_t{error::FAILED};
|
return ue_t{error::FAILED};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::copy(rx_buffer.begin() + 1, rx_buffer.end(), data.begin());
|
||||||
|
|
||||||
auto status = rx_buffer[0];
|
auto status = rx_buffer[0];
|
||||||
if (const auto err = status_to_err(status); err != error::OK) {
|
if (const auto err = status_to_err(status); err != error::OK) {
|
||||||
return ue_t{err};
|
return ue_t{err};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::copy(rx_buffer.begin() + 1, rx_buffer.end(), data.begin());
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,16 +210,18 @@ read_stream(uint8_t cmd, std::span<uint8_t> data, const size_t timeout_ms) {
|
|||||||
|
|
||||||
Result<Unit, error_t>
|
Result<Unit, error_t>
|
||||||
write_stream(uint8_t cmd, std::span<const uint8_t> data, const size_t timeout_ms) {
|
write_stream(uint8_t cmd, std::span<const uint8_t> data, const size_t timeout_ms) {
|
||||||
|
std::array<uint8_t, STACK_BUFFER_SIZE> _rx_buf;
|
||||||
|
|
||||||
|
if (data.size() > STACK_BUFFER_SIZE - 1) {
|
||||||
|
return ue_t{error::INVALID_SIZE};
|
||||||
|
}
|
||||||
|
|
||||||
if (not wait_for_not_busy(timeout_ms)) {
|
if (not wait_for_not_busy(timeout_ms)) {
|
||||||
return ue_t{error::TIMEOUT};
|
return ue_t{error::TIMEOUT};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.size() > MAX_BUFFER_SIZE) {
|
const auto rx_buffer = std::span{_rx_buf}.subspan(0, data.size());
|
||||||
return ue_t{error::INVALID_SIZE};
|
spi_transaction_ext_t transaction{};
|
||||||
}
|
|
||||||
|
|
||||||
const auto rx_buffer = details::shared_buffer.subspan(0, data.size());
|
|
||||||
spi_transaction_ext_t transaction;
|
|
||||||
transaction = spi_transaction_ext_t{
|
transaction = spi_transaction_ext_t{
|
||||||
.base = {
|
.base = {
|
||||||
.flags = SPI_TRANS_VARIABLE_CMD,
|
.flags = SPI_TRANS_VARIABLE_CMD,
|
||||||
@ -220,13 +231,13 @@ write_stream(uint8_t cmd, std::span<const uint8_t> data, const size_t timeout_ms
|
|||||||
.tx_buffer = data.data(),
|
.tx_buffer = data.data(),
|
||||||
.rx_buffer = rx_buffer.data(),
|
.rx_buffer = rx_buffer.data(),
|
||||||
},
|
},
|
||||||
.command_bits = static_cast<uint8_t>(SPI_CMD_BIT_SIZE),
|
.command_bits = CMD_BIT_SIZE,
|
||||||
.address_bits = 0,
|
.address_bits = 0,
|
||||||
.dummy_bits = 0,
|
.dummy_bits = 0,
|
||||||
};
|
};
|
||||||
esp_err_t err = spi_device_transmit(details::spi_device, &transaction.base);
|
esp_err_t err = spi_device_transmit(details::spi_device, &transaction.base);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "failed to transfer %s (%d)", esp_err_to_name(err), err);
|
ESP_LOGE(TAG, "%s (%d)", esp_err_to_name(err), err);
|
||||||
return ue_t{error::FAILED};
|
return ue_t{error::FAILED};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,12 +253,15 @@ write_stream(uint8_t cmd, std::span<const uint8_t> data, const size_t timeout_ms
|
|||||||
|
|
||||||
Result<Unit, error_t>
|
Result<Unit, error_t>
|
||||||
read_register(uint16_t reg, std::span<uint8_t> data, const size_t timeout_ms) {
|
read_register(uint16_t reg, std::span<uint8_t> data, const size_t timeout_ms) {
|
||||||
if (not wait_for_not_busy(timeout_ms)) {
|
std::array<uint8_t, STACK_BUFFER_SIZE> _rx_buf;
|
||||||
return ue_t{error::TIMEOUT};
|
std::array<uint8_t, STACK_BUFFER_SIZE> _tx_buf;
|
||||||
|
|
||||||
|
if (data.size() > STACK_BUFFER_SIZE - 1) {
|
||||||
|
return ue_t{error::INVALID_SIZE};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.size() > MAX_BUFFER_SIZE - 1) {
|
if (not wait_for_not_busy(timeout_ms)) {
|
||||||
return ue_t{error::INVALID_SIZE};
|
return ue_t{error::TIMEOUT};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note that the host has to send an NOP after sending the 2
|
// Note that the host has to send an NOP after sending the 2
|
||||||
@ -257,7 +271,10 @@ read_register(uint16_t reg, std::span<uint8_t> data, const size_t timeout_ms) {
|
|||||||
// if the tx_buffer is nullptr.
|
// if the tx_buffer is nullptr.
|
||||||
spi_transaction_ext_t transaction;
|
spi_transaction_ext_t transaction;
|
||||||
|
|
||||||
auto rx_buffer = details::shared_buffer.subspan(0, data.size() + 1);
|
auto rx_buffer = std::span{_rx_buf}.subspan(0, data.size() + 1);
|
||||||
|
auto tx_buffer = std::span{_tx_buf}.subspan(0, rx_buffer.size());
|
||||||
|
std::ranges::fill(tx_buffer, PADDING);
|
||||||
|
|
||||||
transaction = spi_transaction_ext_t{
|
transaction = spi_transaction_ext_t{
|
||||||
.base = {
|
.base = {
|
||||||
.flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR,
|
.flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR,
|
||||||
@ -265,16 +282,16 @@ read_register(uint16_t reg, std::span<uint8_t> data, const size_t timeout_ms) {
|
|||||||
.addr = reg,
|
.addr = reg,
|
||||||
.length = static_cast<size_t>(rx_buffer.size() * 8),
|
.length = static_cast<size_t>(rx_buffer.size() * 8),
|
||||||
.rxlength = static_cast<size_t>(rx_buffer.size() * 8),
|
.rxlength = static_cast<size_t>(rx_buffer.size() * 8),
|
||||||
.tx_buffer = nullptr,
|
.tx_buffer = tx_buffer.data(),
|
||||||
.rx_buffer = rx_buffer.data(),
|
.rx_buffer = rx_buffer.data(),
|
||||||
},
|
},
|
||||||
.command_bits = static_cast<uint8_t>(SPI_CMD_BIT_SIZE),
|
.command_bits = static_cast<uint8_t>(CMD_BIT_SIZE),
|
||||||
.address_bits = static_cast<uint8_t>(SPI_REGISTER_ADDR_BIT_SIZE),
|
.address_bits = static_cast<uint8_t>(REG_ADDR_BIT_SIZE),
|
||||||
.dummy_bits = 0,
|
.dummy_bits = 0,
|
||||||
};
|
};
|
||||||
esp_err_t err = spi_device_transmit(details::spi_device, &transaction.base);
|
esp_err_t err = spi_device_transmit(details::spi_device, &transaction.base);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "failed to transfer %s (%d)", esp_err_to_name(err), err);
|
ESP_LOGE(TAG, "%s (%d)", esp_err_to_name(err), err);
|
||||||
return ue_t{error::FAILED};
|
return ue_t{error::FAILED};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,15 +307,16 @@ read_register(uint16_t reg, std::span<uint8_t> data, const size_t timeout_ms) {
|
|||||||
|
|
||||||
Result<Unit, error_t>
|
Result<Unit, error_t>
|
||||||
write_register(uint16_t offset, std::span<const uint8_t> data, const size_t timeout_ms) {
|
write_register(uint16_t offset, std::span<const uint8_t> data, const size_t timeout_ms) {
|
||||||
|
std::array<uint8_t, STACK_BUFFER_SIZE> _rx_buf;
|
||||||
|
if (data.size() > STACK_BUFFER_SIZE - 1) {
|
||||||
|
return ue_t{error::INVALID_SIZE};
|
||||||
|
}
|
||||||
|
|
||||||
if (not wait_for_not_busy(timeout_ms)) {
|
if (not wait_for_not_busy(timeout_ms)) {
|
||||||
return ue_t{error::TIMEOUT};
|
return ue_t{error::TIMEOUT};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.size() > MAX_BUFFER_SIZE) {
|
auto rx_buffer = std::span{_rx_buf}.subspan(0, data.size());
|
||||||
return ue_t{error::INVALID_SIZE};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto rx_buffer = details::shared_buffer.subspan(0, data.size());
|
|
||||||
|
|
||||||
spi_transaction_ext_t transaction;
|
spi_transaction_ext_t transaction;
|
||||||
transaction = spi_transaction_ext_t{
|
transaction = spi_transaction_ext_t{
|
||||||
@ -311,14 +329,14 @@ write_register(uint16_t offset, std::span<const uint8_t> data, const size_t time
|
|||||||
.tx_buffer = data.data(),
|
.tx_buffer = data.data(),
|
||||||
.rx_buffer = rx_buffer.data(),
|
.rx_buffer = rx_buffer.data(),
|
||||||
},
|
},
|
||||||
.command_bits = static_cast<uint8_t>(SPI_CMD_BIT_SIZE),
|
.command_bits = static_cast<uint8_t>(CMD_BIT_SIZE),
|
||||||
.address_bits = static_cast<uint8_t>(SPI_REGISTER_ADDR_BIT_SIZE),
|
.address_bits = static_cast<uint8_t>(REG_ADDR_BIT_SIZE),
|
||||||
.dummy_bits = 0,
|
.dummy_bits = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
esp_err_t err = spi_device_transmit(details::spi_device, &transaction.base);
|
esp_err_t err = spi_device_transmit(details::spi_device, &transaction.base);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "failed to transfer %s (%d)", esp_err_to_name(err), err);
|
ESP_LOGE(TAG, "spi transmit: %s (%d)", esp_err_to_name(err), err);
|
||||||
return ue_t{error::FAILED};
|
return ue_t{error::FAILED};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -339,6 +357,9 @@ read_buffer(uint8_t offset, uint8_t size, const size_t timeout_ms) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto rx_buffer = details::data_buffer.subspan(0, size_required);
|
auto rx_buffer = details::data_buffer.subspan(0, size_required);
|
||||||
|
auto _tx_buffer = std::make_unique<uint8_t[]>(rx_buffer.size());
|
||||||
|
auto tx_buffer = std::span{_tx_buffer.get(), rx_buffer.size()};
|
||||||
|
std::ranges::fill(tx_buffer, PADDING);
|
||||||
|
|
||||||
spi_transaction_ext_t transaction;
|
spi_transaction_ext_t transaction;
|
||||||
transaction = spi_transaction_ext_t{
|
transaction = spi_transaction_ext_t{
|
||||||
@ -348,21 +369,22 @@ read_buffer(uint8_t offset, uint8_t size, const size_t timeout_ms) {
|
|||||||
.addr = offset,
|
.addr = offset,
|
||||||
.length = static_cast<size_t>(rx_buffer.size() * 8),
|
.length = static_cast<size_t>(rx_buffer.size() * 8),
|
||||||
.rxlength = static_cast<size_t>(rx_buffer.size() * 8),
|
.rxlength = static_cast<size_t>(rx_buffer.size() * 8),
|
||||||
.tx_buffer = nullptr,
|
.tx_buffer = tx_buffer.data(),
|
||||||
.rx_buffer = rx_buffer.data(),
|
.rx_buffer = rx_buffer.data(),
|
||||||
},
|
},
|
||||||
.command_bits = static_cast<uint8_t>(SPI_CMD_BIT_SIZE),
|
.command_bits = static_cast<uint8_t>(CMD_BIT_SIZE),
|
||||||
.address_bits = static_cast<uint8_t>(SPI_BUFFER_OFFSET_BIT_SIZE),
|
.address_bits = static_cast<uint8_t>(OFFSET_BIT_SIZE),
|
||||||
.dummy_bits = 0,
|
.dummy_bits = 0,
|
||||||
};
|
};
|
||||||
esp_err_t err = spi_device_polling_transmit(details::spi_device, &transaction.base);
|
esp_err_t err = spi_device_transmit(details::spi_device, &transaction.base);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "failed to transfer %s (%d)", esp_err_to_name(err), err);
|
ESP_LOGE(TAG, "%s (%d)", esp_err_to_name(err), err);
|
||||||
return ue_t{error::FAILED};
|
return ue_t{error::FAILED};
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto status = rx_buffer[0];
|
const auto status = rx_buffer[0];
|
||||||
if (const auto err = status_to_err(status); err != error::OK) {
|
// FIXME: current hack ignore processing error
|
||||||
|
if (const auto err = status_to_err(status); err != error::OK && err != error::RADIO_TRANS_CMD_PROC_ERR) {
|
||||||
return ue_t{err};
|
return ue_t{err};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -373,13 +395,14 @@ read_buffer(uint8_t offset, uint8_t size, const size_t timeout_ms) {
|
|||||||
|
|
||||||
Result<Unit, error_t>
|
Result<Unit, error_t>
|
||||||
write_buffer(uint8_t offset, std::span<const uint8_t> data_from_host, const size_t timeout_ms) {
|
write_buffer(uint8_t offset, std::span<const uint8_t> data_from_host, const size_t timeout_ms) {
|
||||||
|
if (data_from_host.size() > details::data_buffer.size()) {
|
||||||
|
return ue_t{error::INVALID_SIZE};
|
||||||
|
}
|
||||||
|
|
||||||
if (not wait_for_not_busy(timeout_ms)) {
|
if (not wait_for_not_busy(timeout_ms)) {
|
||||||
return ue_t{error::TIMEOUT};
|
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());
|
auto rx_buffer = details::data_buffer.subspan(0, data_from_host.size());
|
||||||
|
|
||||||
spi_transaction_ext_t transaction;
|
spi_transaction_ext_t transaction;
|
||||||
@ -393,14 +416,14 @@ write_buffer(uint8_t offset, std::span<const uint8_t> data_from_host, const size
|
|||||||
.tx_buffer = data_from_host.data(),
|
.tx_buffer = data_from_host.data(),
|
||||||
.rx_buffer = rx_buffer.data(),
|
.rx_buffer = rx_buffer.data(),
|
||||||
},
|
},
|
||||||
.command_bits = static_cast<uint8_t>(SPI_CMD_BIT_SIZE),
|
.command_bits = static_cast<uint8_t>(CMD_BIT_SIZE),
|
||||||
.address_bits = static_cast<uint8_t>(SPI_BUFFER_OFFSET_BIT_SIZE),
|
.address_bits = static_cast<uint8_t>(OFFSET_BIT_SIZE),
|
||||||
.dummy_bits = 0,
|
.dummy_bits = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
esp_err_t err = spi_device_transmit(details::spi_device, &transaction.base);
|
esp_err_t err = spi_device_transmit(details::spi_device, &transaction.base);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "failed to transfer %s (%d)", esp_err_to_name(err), err);
|
ESP_LOGE(TAG, "spi transmit: %s (%d)", esp_err_to_name(err), err);
|
||||||
return ue_t{error::FAILED};
|
return ue_t{error::FAILED};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user