cleanup debbugging
This commit is contained in:
parent
1d3d25e6ae
commit
3a616b512a
@ -1,146 +1,93 @@
|
||||
// lora_rx.cpp — LR1121 receive test (robust RX loop)
|
||||
// Usage: sudo ./lora_rx [-v] [--433|--868|--24|freq_hz] [--reset] [--tmo=ms]
|
||||
// [--busy-gpio=N] [--reset-gpio=N] [--dio9-gpio=N]
|
||||
// -v verbose step labels (shows exactly where init hangs)
|
||||
// --433 433.05 MHz (default)
|
||||
// --868 868 MHz
|
||||
// --24 2403 MHz
|
||||
// freq_hz any raw frequency in Hz
|
||||
// --reset send Reboot(app) to escape bootloader, print fw before/after, exit
|
||||
//
|
||||
// TX and RX must use identical SF/BW/CR/freq settings.
|
||||
// Requires SPI: sudo raspi-config → Interfaces → SPI → Yes → reboot
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "lr1121_malnus.hpp"
|
||||
|
||||
static void applyPaPreset(lr1121::Config &cfg, bool hp_mode)
|
||||
{
|
||||
cfg.pa_sel = hp_mode ? 0x01 : 0x00;
|
||||
cfg.tx_dbm = hp_mode ? 14 : 10;
|
||||
}
|
||||
|
||||
static bool parseArgs(int argc, char **argv, bool &verbose, bool &do_reset, bool &hp_mode)
|
||||
{
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (std::strcmp(argv[i], "-v") == 0) {
|
||||
verbose = true;
|
||||
continue;
|
||||
}
|
||||
if (std::strcmp(argv[i], "--reset") == 0) {
|
||||
do_reset = true;
|
||||
continue;
|
||||
}
|
||||
if (std::strcmp(argv[i], "--hp") == 0) {
|
||||
hp_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (std::strcmp(argv[i], "--lp") == 0) {
|
||||
hp_mode = false;
|
||||
continue;
|
||||
}
|
||||
std::fprintf(stderr, "Unknown option: %s\nUsage: sudo ./lora_rx [-v] [--reset] [--lp|--hp]\n", argv[i]);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
lr1121::Config cfg;
|
||||
cfg.verbose = false;
|
||||
cfg.pa_sel = 0x01;
|
||||
cfg.tx_dbm = 14;
|
||||
cfg.sf = 0x07; // SF7
|
||||
cfg.bw = 0x04; // 125 kHz
|
||||
cfg.cr = 0x01; // CR 4/5
|
||||
cfg.freq_hz = lr1121::FREQ_433;
|
||||
cfg.busy_gpio = 24;
|
||||
cfg.reset_gpio = 25;
|
||||
cfg.dio9_gpio = 4;
|
||||
cfg.sf = 0x07;
|
||||
cfg.bw = 0x04;
|
||||
cfg.cr = 0x01;
|
||||
constexpr uint32_t rx_timeout_ms = 1000;
|
||||
bool do_reset = false;
|
||||
uint32_t rx_timeout_ms = 1000; // Short timeout keeps RX loop responsive.
|
||||
bool hp_mode = false;
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (std::strcmp(argv[i], "-v") == 0) cfg.verbose = true;
|
||||
else if (std::strcmp(argv[i], "--433") == 0) cfg.freq_hz = lr1121::FREQ_433;
|
||||
else if (std::strcmp(argv[i], "--868") == 0) cfg.freq_hz = lr1121::FREQ_868;
|
||||
else if (std::strcmp(argv[i], "--24") == 0 ||
|
||||
std::strcmp(argv[i], "--2g4") == 0) cfg.freq_hz = lr1121::FREQ_2400;
|
||||
else if (std::strcmp(argv[i], "--reset") == 0) do_reset = true;
|
||||
else if (std::strncmp(argv[i], "--tmo=", 6) == 0) rx_timeout_ms = (uint32_t)std::strtoul(argv[i] + 6, nullptr, 10);
|
||||
else if (std::strncmp(argv[i], "--busy-gpio=", 12) == 0) cfg.busy_gpio = (unsigned)std::strtoul(argv[i] + 12, nullptr, 10);
|
||||
else if (std::strncmp(argv[i], "--reset-gpio=", 13) == 0) cfg.reset_gpio = (unsigned)std::strtoul(argv[i] + 13, nullptr, 10);
|
||||
else if (std::strncmp(argv[i], "--dio9-gpio=", 12) == 0) cfg.dio9_gpio = (unsigned)std::strtoul(argv[i] + 12, nullptr, 10);
|
||||
else cfg.freq_hz = (uint32_t)std::strtoul(argv[i], nullptr, 10);
|
||||
}
|
||||
|
||||
std::printf("lora_rx: %u Hz SF%u BW=0x%02X CR=0x%02X tmo=%ums gpio(busy=%u reset=%u dio9=%u)%s\n",
|
||||
cfg.freq_hz, cfg.sf, cfg.bw,
|
||||
cfg.cr, rx_timeout_ms, cfg.busy_gpio, cfg.reset_gpio, cfg.dio9_gpio,
|
||||
cfg.verbose ? " [verbose]" : "");
|
||||
|
||||
if (do_reset) {
|
||||
cfg.verbose = true;
|
||||
lr1121::Radio radio;
|
||||
if (!radio.beginRaw(cfg)) {
|
||||
std::fprintf(stderr, "ERROR: cannot open SPI/GPIO\n");
|
||||
return 1;
|
||||
}
|
||||
auto v1 = radio.getVersion();
|
||||
std::printf("before reboot: hw=0x%02X type=0x%02X fw=0x%02X%02X\n",
|
||||
v1.hw, v1.type, v1.fw_hi, v1.fw_lo);
|
||||
std::printf("sending Reboot(app)...\n");
|
||||
radio.reboot(false);
|
||||
auto v2 = radio.getVersion();
|
||||
std::printf("after reboot: hw=0x%02X type=0x%02X fw=0x%02X%02X\n",
|
||||
v2.hw, v2.type, v2.fw_hi, v2.fw_lo);
|
||||
if (v2.fw_hi >= 0x02)
|
||||
std::printf("OK — application firmware active (fw >= 0x02xx)\n"
|
||||
"Run without --reset to continue.\n");
|
||||
else
|
||||
std::printf("Still in bootloader (fw=0x%02X%02X).\n",
|
||||
v2.fw_hi, v2.fw_lo);
|
||||
radio.end();
|
||||
return 0;
|
||||
}
|
||||
if (!parseArgs(argc, argv, cfg.verbose, do_reset, hp_mode)) return 2;
|
||||
applyPaPreset(cfg, hp_mode);
|
||||
std::printf("RX %u Hz, tmo=%ums, gpio(busy=%u reset=%u dio9=%u)%s\n",
|
||||
cfg.freq_hz, rx_timeout_ms, cfg.busy_gpio, cfg.reset_gpio, cfg.dio9_gpio, cfg.verbose ? " [v]" : "");
|
||||
|
||||
lr1121::Radio radio;
|
||||
if (!radio.begin(cfg)) {
|
||||
std::fprintf(stderr, "ERROR: radio init failed\n"
|
||||
" Check: SPI enabled? wiring? DIO5/DIO6 connected?\n"
|
||||
" Run with -v for step-by-step output\n");
|
||||
std::fprintf(stderr, "ERROR: radio init failed\n");
|
||||
return 1;
|
||||
}
|
||||
std::printf("Radio OK — listening (Ctrl+C to stop)\n\n");
|
||||
if (do_reset) {
|
||||
std::puts("Applying soft settings reset...");
|
||||
if (!radio.softResetSettings()) {
|
||||
std::fprintf(stderr, "ERROR: soft settings reset failed\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
std::puts("Radio OK - listening...");
|
||||
|
||||
uint8_t buf[256];
|
||||
int pkt = 0;
|
||||
int timeouts_in_row = 0;
|
||||
int crc_in_row = 0;
|
||||
uint32_t timeout_total = 0;
|
||||
uint32_t crc_total = 0;
|
||||
|
||||
for (;;) {
|
||||
lr1121::RxInfo info{};
|
||||
int r = radio.receive(buf, (uint8_t)(sizeof(buf) - 1), rx_timeout_ms, &info);
|
||||
const int r = radio.receive(buf, static_cast<uint8_t>(sizeof(buf) - 1), rx_timeout_ms, &info);
|
||||
|
||||
if (r > 0) {
|
||||
buf[r] = '\0';
|
||||
std::printf("[%4d] %d B rssi=%d dBm snr=%d dB '%s'\n",
|
||||
std::printf("[%04d] %3d B rssi=%4d snr=%3d '%s'\n",
|
||||
++pkt, r, info.rssi_dbm, info.snr_db, buf);
|
||||
timeouts_in_row = 0;
|
||||
crc_in_row = 0;
|
||||
} else if (r == -1) {
|
||||
++timeouts_in_row;
|
||||
++timeout_total;
|
||||
if ((timeout_total % 10u) == 0u) {
|
||||
std::printf(" timeout x%u total (current streak=%d)\n",
|
||||
timeout_total, timeouts_in_row);
|
||||
}
|
||||
|
||||
// If RX keeps stalling, fully reinitialize to recover reliably.
|
||||
if (timeouts_in_row >= 20) {
|
||||
std::printf(" RX stalled (timeouts streak=%d) -> reinitializing radio...\n",
|
||||
timeouts_in_row);
|
||||
radio.end();
|
||||
if (!radio.begin(cfg)) {
|
||||
std::fprintf(stderr, "ERROR: radio reinit failed, retrying in 1s...\n");
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
} else {
|
||||
std::printf(" radio reinitialized, listening...\n");
|
||||
timeouts_in_row = 0;
|
||||
crc_in_row = 0;
|
||||
}
|
||||
}
|
||||
if ((timeout_total % 10u) == 0u) std::printf("timeout x%u\n", timeout_total);
|
||||
} else if (r == -2) {
|
||||
++crc_in_row;
|
||||
++crc_total;
|
||||
std::printf(" CRC error (streak=%d total=%u)\n", crc_in_row, crc_total);
|
||||
|
||||
if (crc_in_row >= 8) {
|
||||
std::printf(" too many CRC errors -> reinitializing radio...\n");
|
||||
radio.end();
|
||||
if (!radio.begin(cfg)) {
|
||||
std::fprintf(stderr, "ERROR: radio reinit failed, retrying in 1s...\n");
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
std::printf("crc error x%u\n", crc_total);
|
||||
} else {
|
||||
std::printf(" radio reinitialized, listening...\n");
|
||||
timeouts_in_row = 0;
|
||||
crc_in_row = 0;
|
||||
std::printf("rx code=%d\n", r);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::printf(" RX unexpected code=%d\n", r);
|
||||
}
|
||||
}
|
||||
|
||||
radio.end();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1,73 +1,80 @@
|
||||
// lora_tx.cpp — LR1121 transmit test
|
||||
// Usage: sudo ./lora_tx [-v] [--433|--868|--24|freq_hz] [--lp|--hp] [--dbm=N]
|
||||
// [--busy-gpio=N] [--reset-gpio=N] [--dio9-gpio=N]
|
||||
// -v verbose step labels (shows exactly where init hangs)
|
||||
// --433 433.05 MHz (default)
|
||||
// --868 868 MHz
|
||||
// --24 2403 MHz
|
||||
// freq_hz any raw frequency in Hz
|
||||
//
|
||||
// Requires SPI: sudo raspi-config → Interfaces → SPI → Yes → reboot
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "lr1121_malnus.hpp"
|
||||
|
||||
static void applyPaPreset(lr1121::Config &cfg, bool hp_mode)
|
||||
{
|
||||
cfg.pa_sel = hp_mode ? 0x01 : 0x00;
|
||||
cfg.tx_dbm = hp_mode ? 14 : 10;
|
||||
}
|
||||
|
||||
static bool parseArgs(int argc, char **argv, bool &verbose, bool &do_reset, bool &hp_mode)
|
||||
{
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (std::strcmp(argv[i], "-v") == 0) {
|
||||
verbose = true;
|
||||
continue;
|
||||
}
|
||||
if (std::strcmp(argv[i], "--reset") == 0) {
|
||||
do_reset = true;
|
||||
continue;
|
||||
}
|
||||
if (std::strcmp(argv[i], "--hp") == 0) {
|
||||
hp_mode = true;
|
||||
continue;
|
||||
}
|
||||
if (std::strcmp(argv[i], "--lp") == 0) {
|
||||
hp_mode = false;
|
||||
continue;
|
||||
}
|
||||
std::fprintf(stderr, "Unknown option: %s\nUsage: sudo ./lora_tx [-v] [--reset] [--lp|--hp]\n", argv[i]);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
lr1121::Config cfg;
|
||||
cfg.verbose = false;
|
||||
cfg.pa_sel = 0x00; // LP default: avoids LBD on weak VBAT rails
|
||||
cfg.tx_dbm = 10;
|
||||
cfg.sf = 0x07; // SF7
|
||||
cfg.bw = 0x04; // 125 kHz
|
||||
cfg.cr = 0x01; // CR 4/5
|
||||
cfg.freq_hz = lr1121::FREQ_433;
|
||||
cfg.busy_gpio = 24;
|
||||
cfg.reset_gpio = 25;
|
||||
cfg.dio9_gpio = 4;
|
||||
cfg.sf = 0x07;
|
||||
cfg.bw = 0x04;
|
||||
cfg.cr = 0x01;
|
||||
bool do_reset = false;
|
||||
bool hp_mode = false;
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (std::strcmp(argv[i], "-v") == 0) cfg.verbose = true;
|
||||
else if (std::strcmp(argv[i], "--433") == 0) cfg.freq_hz = lr1121::FREQ_433;
|
||||
else if (std::strcmp(argv[i], "--868") == 0) cfg.freq_hz = lr1121::FREQ_868;
|
||||
else if (std::strcmp(argv[i], "--24") == 0 ||
|
||||
std::strcmp(argv[i], "--2g4") == 0) cfg.freq_hz = lr1121::FREQ_2400;
|
||||
else if (std::strcmp(argv[i], "--hp") == 0) cfg.pa_sel = 0x01;
|
||||
else if (std::strcmp(argv[i], "--lp") == 0) cfg.pa_sel = 0x00;
|
||||
else if (std::strncmp(argv[i], "--dbm=", 6) == 0) cfg.tx_dbm = (int8_t)std::atoi(argv[i] + 6);
|
||||
else if (std::strncmp(argv[i], "--busy-gpio=", 12) == 0) cfg.busy_gpio = (unsigned)std::strtoul(argv[i] + 12, nullptr, 10);
|
||||
else if (std::strncmp(argv[i], "--reset-gpio=", 13) == 0) cfg.reset_gpio = (unsigned)std::strtoul(argv[i] + 13, nullptr, 10);
|
||||
else if (std::strncmp(argv[i], "--dio9-gpio=", 12) == 0) cfg.dio9_gpio = (unsigned)std::strtoul(argv[i] + 12, nullptr, 10);
|
||||
else cfg.freq_hz = (uint32_t)std::strtoul(argv[i], nullptr, 10);
|
||||
}
|
||||
if (!parseArgs(argc, argv, cfg.verbose, do_reset, hp_mode)) return 2;
|
||||
applyPaPreset(cfg, hp_mode);
|
||||
|
||||
std::printf("lora_tx: %u Hz SF%u BW=0x%02X PA=%s PWR=%d dBm gpio(busy=%u reset=%u dio9=%u)%s\n",
|
||||
cfg.freq_hz, cfg.sf, cfg.bw,
|
||||
cfg.pa_sel ? "HP" : "LP", (int)cfg.tx_dbm,
|
||||
cfg.busy_gpio, cfg.reset_gpio, cfg.dio9_gpio,
|
||||
cfg.verbose ? " [verbose]" : "");
|
||||
std::printf("TX %u Hz, %s PA, %d dBm, gpio(busy=%u reset=%u dio9=%u)%s\n",
|
||||
cfg.freq_hz, cfg.pa_sel ? "HP" : "LP", static_cast<int>(cfg.tx_dbm),
|
||||
cfg.busy_gpio, cfg.reset_gpio, cfg.dio9_gpio, cfg.verbose ? " [v]" : "");
|
||||
|
||||
lr1121::Radio radio;
|
||||
if (!radio.begin(cfg)) {
|
||||
std::fprintf(stderr, "ERROR: radio init failed\n"
|
||||
" Check: SPI enabled? wiring? DIO5/DIO6 connected?\n"
|
||||
" If fw looks like bootloader: sudo ./lora_rx --reset\n"
|
||||
" Run with -v for step-by-step output\n");
|
||||
std::fprintf(stderr, "ERROR: radio init failed\n");
|
||||
return 1;
|
||||
}
|
||||
auto ver = radio.getVersion();
|
||||
if (ver.fw_hi < 0x02)
|
||||
std::fprintf(stderr, "hint: fw=0x%02X%02X — try: sudo ./lora_rx --reset\n",
|
||||
ver.fw_hi, ver.fw_lo);
|
||||
std::printf("Radio OK — sending every second\n");
|
||||
if (do_reset) {
|
||||
std::puts("Applying soft settings reset...");
|
||||
if (!radio.softResetSettings()) {
|
||||
std::fprintf(stderr, "ERROR: soft settings reset failed\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
std::puts("Radio OK - sending...");
|
||||
|
||||
for (int n = 0; ; ++n) {
|
||||
char msg[32];
|
||||
int len = std::snprintf(msg, sizeof(msg), "hello %d", n);
|
||||
bool ok = radio.send((const uint8_t *)msg, (uint8_t)len);
|
||||
std::printf("[%4d] tx '%s' → %s\n", n, msg, ok ? "OK" : "TIMEOUT");
|
||||
const int len = std::snprintf(msg, sizeof(msg), "hello %d", n);
|
||||
const bool ok = radio.send(reinterpret_cast<const uint8_t *>(msg), static_cast<uint8_t>(len));
|
||||
std::printf("[%04d] %s: %s\n", n, msg, ok ? "OK" : "FAIL");
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
|
||||
radio.end();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <thread>
|
||||
@ -37,6 +38,7 @@ constexpr uint16_t OC_CALIBRATE_IMAGE = 0x0111;
|
||||
constexpr uint16_t OC_SET_DIO_AS_RFSW = 0x0112;
|
||||
constexpr uint16_t OC_SET_DIOIRQ = 0x0113;
|
||||
constexpr uint16_t OC_CLEAR_IRQ = 0x0114;
|
||||
constexpr uint16_t OC_GET_IRQ = 0x0115;
|
||||
constexpr uint16_t OC_REBOOT = 0x0118;
|
||||
constexpr uint16_t OC_GET_VBAT = 0x0119;
|
||||
constexpr uint16_t OC_SET_STANDBY = 0x011C;
|
||||
@ -68,6 +70,16 @@ constexpr uint32_t FREQ_433 = 433'050'000u;
|
||||
constexpr uint32_t FREQ_868 = 868'000'000u;
|
||||
constexpr uint32_t FREQ_2400 = 2'403'000'000u;
|
||||
|
||||
constexpr uint8_t LORA_PKT_HEADER_EXPLICIT = 0x00;
|
||||
constexpr uint8_t LORA_PKT_PREAMBLE_LEN = 0x08;
|
||||
constexpr uint8_t LORA_PKT_CRC_ON = 0x01;
|
||||
constexpr uint8_t LORA_PKT_IQ_STD = 0x00;
|
||||
constexpr uint8_t TX_RAMP_48US = 0x02;
|
||||
constexpr uint8_t PKT_TYPE_LORA = 0x02;
|
||||
constexpr uint32_t TX_TIMEOUT_MS = 3000;
|
||||
constexpr uint32_t TX_POLL_GUARD_MS = 500;
|
||||
constexpr int8_t MIN_TX_DBM_FALLBACK = 2;
|
||||
|
||||
struct Config {
|
||||
const char *spi_path = "/dev/spidev0.0";
|
||||
uint32_t spi_hz = 8'000'000;
|
||||
@ -104,6 +116,7 @@ class Radio {
|
||||
public:
|
||||
bool begin(const Config &cfg);
|
||||
bool beginRaw(const Config &cfg);
|
||||
bool softResetSettings();
|
||||
void end();
|
||||
|
||||
bool send(const uint8_t *data, uint8_t n);
|
||||
@ -136,6 +149,7 @@ private:
|
||||
bool clearIrq(uint32_t mask = IRQ_ALL);
|
||||
uint32_t getIrq();
|
||||
uint16_t getErrors();
|
||||
bool applyRadioSettings();
|
||||
|
||||
static void imgCalFreqs(uint32_t hz, uint8_t &f1, uint8_t &f2);
|
||||
static uint8_t computeLdRo(uint8_t sf, uint8_t bw);
|
||||
@ -156,6 +170,7 @@ inline bool Radio::spiTransfer(uint8_t *buf, size_t len)
|
||||
|
||||
inline bool Radio::wcmd(uint16_t op, const uint8_t *params, size_t n)
|
||||
{
|
||||
if (n > 256) return false;
|
||||
if (!waitBusy()) return false;
|
||||
uint8_t cmd[258]{};
|
||||
cmd[0] = static_cast<uint8_t>(op >> 8);
|
||||
@ -167,6 +182,7 @@ inline bool Radio::wcmd(uint16_t op, const uint8_t *params, size_t n)
|
||||
|
||||
inline bool Radio::rcmd(uint16_t op, const uint8_t *params, size_t np, uint8_t *out, size_t nr)
|
||||
{
|
||||
if (np > 30 || nr > 259) return false;
|
||||
if (!waitBusy()) return false;
|
||||
uint8_t cmd[32]{};
|
||||
cmd[0] = static_cast<uint8_t>(op >> 8);
|
||||
@ -203,12 +219,12 @@ inline bool Radio::clearIrq(uint32_t mask)
|
||||
|
||||
inline uint32_t Radio::getIrq()
|
||||
{
|
||||
uint8_t b[6]{};
|
||||
if (!spiTransfer(b, sizeof(b))) return 0;
|
||||
return (static_cast<uint32_t>(b[2]) << 24) |
|
||||
(static_cast<uint32_t>(b[3]) << 16) |
|
||||
(static_cast<uint32_t>(b[4]) << 8) |
|
||||
(static_cast<uint32_t>(b[5]));
|
||||
uint8_t b[4]{};
|
||||
if (!rcmd(OC_GET_IRQ, nullptr, 0, b, sizeof(b))) return 0;
|
||||
return (static_cast<uint32_t>(b[0]) << 24) |
|
||||
(static_cast<uint32_t>(b[1]) << 16) |
|
||||
(static_cast<uint32_t>(b[2]) << 8) |
|
||||
static_cast<uint32_t>(b[3]);
|
||||
}
|
||||
|
||||
inline uint16_t Radio::getErrors()
|
||||
@ -237,9 +253,13 @@ inline bool Radio::openSpi(const Config &cfg)
|
||||
|
||||
uint8_t mode = SPI_MODE_0;
|
||||
uint8_t bits = 8;
|
||||
if (ioctl(spi_fd_, SPI_IOC_WR_MODE, &mode) < 0) return false;
|
||||
if (ioctl(spi_fd_, SPI_IOC_WR_BITS_PER_WORD, &bits) < 0) return false;
|
||||
if (ioctl(spi_fd_, SPI_IOC_WR_MAX_SPEED_HZ, &cfg.spi_hz) < 0) return false;
|
||||
if (ioctl(spi_fd_, SPI_IOC_WR_MODE, &mode) < 0 ||
|
||||
ioctl(spi_fd_, SPI_IOC_WR_BITS_PER_WORD, &bits) < 0 ||
|
||||
ioctl(spi_fd_, SPI_IOC_WR_MAX_SPEED_HZ, &cfg.spi_hz) < 0) {
|
||||
::close(spi_fd_);
|
||||
spi_fd_ = -1;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -355,13 +375,15 @@ inline ChipVersion Radio::getVersion()
|
||||
|
||||
inline bool Radio::beginRaw(const Config &cfg)
|
||||
{
|
||||
end();
|
||||
cfg_ = cfg;
|
||||
if (!openSpi(cfg_)) return false;
|
||||
if (!openGpio(cfg_.reset_gpio, true, reset_fd_)) return false;
|
||||
if (!openGpio(cfg_.busy_gpio, false, busy_fd_)) return false;
|
||||
if (!openGpio(cfg_.dio9_gpio, false, dio9_fd_)) return false;
|
||||
if (!openGpio(cfg_.reset_gpio, true, reset_fd_)) { end(); return false; }
|
||||
if (!openGpio(cfg_.busy_gpio, false, busy_fd_)) { end(); return false; }
|
||||
if (!openGpio(cfg_.dio9_gpio, false, dio9_fd_)) { end(); return false; }
|
||||
hardReset();
|
||||
return waitBusy(500);
|
||||
if (!waitBusy(500)) { end(); return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool Radio::begin(const Config &cfg)
|
||||
@ -377,58 +399,71 @@ inline bool Radio::begin(const Config &cfg)
|
||||
std::fprintf(stderr, "[lr1121] VBAT raw=0x%02X (~%.2fV)\n", raw, vb);
|
||||
}
|
||||
}
|
||||
if (v.type != 0x03) return false;
|
||||
if (v.type != 0x03) { end(); return false; }
|
||||
return applyRadioSettings();
|
||||
}
|
||||
|
||||
if (!setStandbyRc()) return false;
|
||||
{ const uint8_t p[] = {0x01}; if (!wcmd(OC_SET_FALLBACK_MODE, p, sizeof(p))) return false; }
|
||||
if (!clearIrq()) return false;
|
||||
if (!setIrqMask(0, 0)) return false;
|
||||
{ const uint8_t p[] = {CALIB_ALL}; if (!wcmd(OC_CALIBRATE, p, sizeof(p))) return false; }
|
||||
{ const uint8_t p[] = {0x00}; (void)wcmd(OC_CLEAR_ERRORS, p, 0); }
|
||||
inline bool Radio::applyRadioSettings()
|
||||
{
|
||||
if (!setStandbyRc()) { end(); return false; }
|
||||
{ const uint8_t p[] = {0x01}; if (!wcmd(OC_SET_FALLBACK_MODE, p, sizeof(p))) { end(); return false; } }
|
||||
if (!clearIrq()) { end(); return false; }
|
||||
if (!setIrqMask(0, 0)) { end(); return false; }
|
||||
{ const uint8_t p[] = {CALIB_ALL}; if (!wcmd(OC_CALIBRATE, p, sizeof(p))) { end(); return false; } }
|
||||
(void)wcmd(OC_CLEAR_ERRORS, nullptr, 0);
|
||||
|
||||
{ const uint8_t p[] = {static_cast<uint8_t>(cfg_.use_dcdc ? 0x01 : 0x00)}; if (!wcmd(OC_SET_REGMODE, p, sizeof(p))) return false; }
|
||||
{ const uint8_t p[] = {0x02}; if (!wcmd(OC_SET_PKT_TYPE, p, sizeof(p))) return false; }
|
||||
{ const uint8_t p[] = {static_cast<uint8_t>(cfg_.use_dcdc ? 0x01 : 0x00)}; if (!wcmd(OC_SET_REGMODE, p, sizeof(p))) { end(); return false; } }
|
||||
{ const uint8_t p[] = {PKT_TYPE_LORA}; if (!wcmd(OC_SET_PKT_TYPE, p, sizeof(p))) { end(); return false; } }
|
||||
{
|
||||
const uint8_t p[] = {
|
||||
static_cast<uint8_t>(cfg_.freq_hz >> 24), static_cast<uint8_t>(cfg_.freq_hz >> 16),
|
||||
static_cast<uint8_t>(cfg_.freq_hz >> 8), static_cast<uint8_t>(cfg_.freq_hz),
|
||||
};
|
||||
if (!wcmd(OC_SET_RF_FREQ, p, sizeof(p))) return false;
|
||||
if (!wcmd(OC_SET_RF_FREQ, p, sizeof(p))) { end(); return false; }
|
||||
}
|
||||
{
|
||||
uint8_t f1 = 0, f2 = 0;
|
||||
imgCalFreqs(cfg_.freq_hz, f1, f2);
|
||||
const uint8_t p[] = {f1, f2};
|
||||
if (!wcmd(OC_CALIBRATE_IMAGE, p, sizeof(p))) return false;
|
||||
if (!wcmd(OC_CALIBRATE_IMAGE, p, sizeof(p))) { end(); return false; }
|
||||
}
|
||||
{ const uint8_t p[] = {0x00, 0x00}; if (!wcmd(OC_SET_PKT_ADRS, p, sizeof(p))) return false; }
|
||||
{ const uint8_t p[] = {0x00, 0x00}; if (!wcmd(OC_SET_PKT_ADRS, p, sizeof(p))) { end(); return false; } }
|
||||
{
|
||||
const uint8_t p[] = {cfg_.sf, cfg_.bw, cfg_.cr, computeLdRo(cfg_.sf, cfg_.bw)};
|
||||
if (!wcmd(OC_SET_MOD_PARAM, p, sizeof(p))) return false;
|
||||
if (!wcmd(OC_SET_MOD_PARAM, p, sizeof(p))) { end(); return false; }
|
||||
}
|
||||
{ const uint8_t p[] = {0x00, 0x08, 0x00, 0xFF, 0x01, 0x00}; if (!wcmd(OC_SET_PKT_PARAM, p, sizeof(p))) return false; }
|
||||
{ const uint8_t p[] = {LORA_PKT_HEADER_EXPLICIT, LORA_PKT_PREAMBLE_LEN, 0x00, 0xFF, LORA_PKT_CRC_ON, LORA_PKT_IQ_STD}; if (!wcmd(OC_SET_PKT_PARAM, p, sizeof(p))) { end(); return false; } }
|
||||
|
||||
{
|
||||
uint8_t duty = 0, hp_max = 0;
|
||||
computePaConfig(cfg_.pa_sel, cfg_.tx_dbm, duty, hp_max);
|
||||
const uint8_t p[] = {cfg_.pa_sel, cfg_.pa_supply, duty, hp_max};
|
||||
if (!wcmd(OC_SET_PA_CFG, p, sizeof(p))) return false;
|
||||
if (!wcmd(OC_SET_PA_CFG, p, sizeof(p))) { end(); return false; }
|
||||
if (cfg_.verbose) {
|
||||
std::fprintf(stderr, "[lr1121] PA: sel=0x%02X supply=0x%02X duty=%u hp_max=%u dbm=%d\n",
|
||||
cfg_.pa_sel, cfg_.pa_supply, duty, hp_max, static_cast<int>(cfg_.tx_dbm));
|
||||
}
|
||||
}
|
||||
{ const uint8_t p[] = {static_cast<uint8_t>(cfg_.tx_dbm), 0x02}; if (!wcmd(OC_SET_TX_PARAMS, p, sizeof(p))) return false; }
|
||||
{ const uint8_t p[] = {static_cast<uint8_t>(cfg_.lora_wan ? 0x01 : 0x00)}; if (!wcmd(OC_SET_LORA_NET, p, sizeof(p))) return false; }
|
||||
{ const uint8_t p[] = {static_cast<uint8_t>(cfg_.tx_dbm), TX_RAMP_48US}; if (!wcmd(OC_SET_TX_PARAMS, p, sizeof(p))) { end(); return false; } }
|
||||
{ const uint8_t p[] = {static_cast<uint8_t>(cfg_.lora_wan ? 0x01 : 0x00)}; if (!wcmd(OC_SET_LORA_NET, p, sizeof(p))) { end(); return false; } }
|
||||
|
||||
// DIO5/DIO6 RF switch mapping: STBY(0,0), RX(1,0), TX(0,1), TX_HP(0,1)
|
||||
{ const uint8_t p[] = {0x03, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00}; if (!wcmd(OC_SET_DIO_AS_RFSW, p, sizeof(p))) return false; }
|
||||
{ const uint8_t p[] = {0x03, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00}; if (!wcmd(OC_SET_DIO_AS_RFSW, p, sizeof(p))) { end(); return false; } }
|
||||
|
||||
if (!setStandbyXosc()) return false;
|
||||
if (!setStandbyXosc()) { end(); return false; }
|
||||
if (cfg_.verbose) std::fprintf(stderr, "[lr1121] init OK: %u Hz SF%u BW=0x%02X CR=0x%02X\n", cfg_.freq_hz, cfg_.sf, cfg_.bw, cfg_.cr);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool Radio::softResetSettings()
|
||||
{
|
||||
if (spi_fd_ < 0 || busy_fd_ < 0 || reset_fd_ < 0 || dio9_fd_ < 0) return false;
|
||||
if (!setStandbyRc()) return false;
|
||||
(void)wcmd(OC_CLEAR_RXBUF, nullptr, 0);
|
||||
(void)clearIrq();
|
||||
return applyRadioSettings();
|
||||
}
|
||||
|
||||
inline void Radio::reboot(bool stay_in_bootloader)
|
||||
{
|
||||
const uint8_t p[] = { static_cast<uint8_t>(stay_in_bootloader ? 0x01 : 0x00) };
|
||||
@ -447,15 +482,25 @@ inline void Radio::end()
|
||||
inline bool Radio::send(const uint8_t *data, uint8_t n)
|
||||
{
|
||||
if (n == 0) return false;
|
||||
uint8_t pa_sel = cfg_.pa_sel;
|
||||
int8_t tx_dbm = cfg_.tx_dbm;
|
||||
|
||||
for (int attempt = 0; attempt < 6; ++attempt) {
|
||||
if (!setStandbyXosc()) return false;
|
||||
if (!clearIrq()) return false;
|
||||
if (!setIrqMask(IRQ_TX_DONE | IRQ_TIMEOUT | IRQ_LBD, IRQ_TX_DONE | IRQ_TIMEOUT | IRQ_LBD)) return false;
|
||||
|
||||
if (!wcmd(OC_WRITE_BUF8, data, n)) return false;
|
||||
{ const uint8_t p[] = {0x00, 0x08, 0x00, n, 0x01, 0x00}; if (!wcmd(OC_SET_PKT_PARAM, p, sizeof(p))) return false; }
|
||||
|
||||
{
|
||||
const uint32_t tx_steps = timeoutMsToRtcSteps(3000);
|
||||
uint8_t duty = 0, hp_max = 0;
|
||||
computePaConfig(pa_sel, tx_dbm, duty, hp_max);
|
||||
const uint8_t p[] = {pa_sel, cfg_.pa_supply, duty, hp_max};
|
||||
if (!wcmd(OC_SET_PA_CFG, p, sizeof(p))) return false;
|
||||
}
|
||||
{ const uint8_t p[] = {static_cast<uint8_t>(tx_dbm), TX_RAMP_48US}; if (!wcmd(OC_SET_TX_PARAMS, p, sizeof(p))) return false; }
|
||||
if (!wcmd(OC_WRITE_BUF8, data, n)) return false;
|
||||
{ const uint8_t p[] = {LORA_PKT_HEADER_EXPLICIT, LORA_PKT_PREAMBLE_LEN, 0x00, n, LORA_PKT_CRC_ON, LORA_PKT_IQ_STD}; if (!wcmd(OC_SET_PKT_PARAM, p, sizeof(p))) return false; }
|
||||
{
|
||||
const uint32_t tx_steps = timeoutMsToRtcSteps(TX_TIMEOUT_MS);
|
||||
const uint8_t p[] = {
|
||||
static_cast<uint8_t>(tx_steps >> 16),
|
||||
static_cast<uint8_t>(tx_steps >> 8),
|
||||
@ -464,9 +509,8 @@ inline bool Radio::send(const uint8_t *data, uint8_t n)
|
||||
if (!wcmd(OC_SET_TX, p, sizeof(p))) return false;
|
||||
}
|
||||
|
||||
const auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(3500);
|
||||
const auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(TX_TIMEOUT_MS + TX_POLL_GUARD_MS);
|
||||
while (std::chrono::steady_clock::now() < deadline) {
|
||||
// Primary trigger is GPIO DIO9 (actual IRQ pin), then decode IRQ bits.
|
||||
if (getGpioLine(dio9_fd_) == 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
continue;
|
||||
@ -477,22 +521,41 @@ inline bool Radio::send(const uint8_t *data, uint8_t n)
|
||||
(void)setIrqMask(0, 0);
|
||||
return true;
|
||||
}
|
||||
if (irq & (IRQ_TIMEOUT | IRQ_LBD)) {
|
||||
if (cfg_.verbose) {
|
||||
std::fprintf(stderr, "[lr1121] TX fail irq=0x%08X errs=0x%04X\n", irq, getErrors());
|
||||
if (irq & IRQ_LBD) std::fprintf(stderr, "[lr1121] LBD triggered: reduce TX power or use LP PA\n");
|
||||
}
|
||||
if (irq & IRQ_TIMEOUT) {
|
||||
if (cfg_.verbose) std::fprintf(stderr, "[lr1121] TX timeout irq=0x%08X errs=0x%04X\n", irq, getErrors());
|
||||
(void)clearIrq();
|
||||
(void)setIrqMask(0, 0);
|
||||
return false;
|
||||
}
|
||||
if (irq & IRQ_LBD) {
|
||||
(void)clearIrq();
|
||||
(void)setIrqMask(0, 0);
|
||||
if (pa_sel == 0x01) {
|
||||
pa_sel = 0x00;
|
||||
if (tx_dbm > 10) tx_dbm = 10;
|
||||
} else if (tx_dbm > MIN_TX_DBM_FALLBACK) {
|
||||
tx_dbm = static_cast<int8_t>(tx_dbm - 2);
|
||||
} else {
|
||||
if (cfg_.verbose) std::fprintf(stderr, "[lr1121] TX fail: LBD at minimum power (%d dBm)\n", static_cast<int>(tx_dbm));
|
||||
return false;
|
||||
}
|
||||
if (cfg_.verbose) {
|
||||
std::fprintf(stderr, "[lr1121] LBD retry with %s PA %d dBm\n",
|
||||
pa_sel ? "HP" : "LP", static_cast<int>(tx_dbm));
|
||||
}
|
||||
break;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
if (std::chrono::steady_clock::now() >= deadline) {
|
||||
if (cfg_.verbose) std::fprintf(stderr, "[lr1121] TX poll timeout errs=0x%04X\n", getErrors());
|
||||
(void)clearIrq();
|
||||
(void)setIrqMask(0, 0);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline int Radio::receive(uint8_t *buf, uint8_t cap, uint32_t timeout_ms, RxInfo *rx_info)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user