From 16b325607324b772d8bd838eacbd20433e29b6df Mon Sep 17 00:00:00 2001 From: jgromes Date: Fri, 10 May 2019 15:47:10 +0200 Subject: [PATCH] [SX126x] Added core configuration methods --- keywords.txt | 1 + src/RadioLib.h | 1 + src/modules/SX1262.cpp | 56 ++++++ src/modules/SX1262.h | 29 ++++ src/modules/SX126x.cpp | 360 +++++++++++++++++++++++++++++++++++++++ src/modules/SX126x.h | 375 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 822 insertions(+) create mode 100644 src/modules/SX1262.cpp create mode 100644 src/modules/SX1262.h create mode 100644 src/modules/SX126x.cpp create mode 100644 src/modules/SX126x.h diff --git a/keywords.txt b/keywords.txt index 93d4afbc..53bfc893 100644 --- a/keywords.txt +++ b/keywords.txt @@ -20,6 +20,7 @@ RFM96 KEYWORD1 RFM97 KEYWORD1 RFM98 KEYWORD1 SX1231 KEYWORD1 +SX1262 KEYWORD1 SX1272 KEYWORD1 SX1273 KEYWORD1 SX1276 KEYWORD1 diff --git a/src/RadioLib.h b/src/RadioLib.h index 0b432443..0de12de2 100644 --- a/src/RadioLib.h +++ b/src/RadioLib.h @@ -13,6 +13,7 @@ #include "modules/RFM96.h" #include "modules/RFM97.h" #include "modules/SX1231.h" +#include "modules/SX1262.h" #include "modules/SX1272.h" #include "modules/SX1273.h" #include "modules/SX1276.h" diff --git a/src/modules/SX1262.cpp b/src/modules/SX1262.cpp new file mode 100644 index 00000000..0e8a6377 --- /dev/null +++ b/src/modules/SX1262.cpp @@ -0,0 +1,56 @@ +#include "SX1262.h" + +SX1262::SX1262(Module* mod) : SX126x(mod) { + +} + +int16_t SX1262::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint16_t syncWord, int8_t power, float currentLimit, uint16_t preambleLength) { + // execute common part + int16_t state = SX126x::begin(bw, sf, cr, syncWord, currentLimit, preambleLength); + if(state != ERR_NONE) { + return(state); + } + + // configure publicly accessible settings + state = setFrequency(freq); + if(state != ERR_NONE) { + return(state); + } + + state = setOutputPower(power); + if(state != ERR_NONE) { + return(state); + } + + return(state); +} + +int16_t SX1262::setFrequency(float freq) { + // check frequency range + if((freq < 150.0) || (freq > 960.0)) { + return(ERR_INVALID_FREQUENCY); + } + + // set frequency + return(SX126x::setFrequencyRaw(freq)); +} + +int16_t SX1262::setOutputPower(int8_t power) { + // check allowed power range + if(!((power >= -17) && (power <= 22))) { + return(ERR_INVALID_OUTPUT_POWER); + } + + // enable high power PA for output power higher than 14 dBm + if(power > 14) { + SX126x::setPaConfig(0x04, SX126X_PA_CONFIG_SX1262); + } else { + SX126x::setPaConfig(0x04, SX126X_PA_CONFIG_SX1261); + } + + // set output power + // TODO power ramp time configuration + SX126x::setTxParams(power); + return(ERR_NONE); +} + diff --git a/src/modules/SX1262.h b/src/modules/SX1262.h new file mode 100644 index 00000000..f740dc37 --- /dev/null +++ b/src/modules/SX1262.h @@ -0,0 +1,29 @@ +#ifndef _RADIOLIB_SX1262_H +#define _RADIOLIB_SX1262_H + +#include "TypeDef.h" +#include "Module.h" +#include "SX126x.h" + +//SX126X_CMD_SET_PA_CONFIG +#define SX126X_PA_CONFIG_SX1261 0x01 +#define SX126X_PA_CONFIG_SX1262 0x00 + +class SX1262: public SX126x { + public: + // constructor + SX1262(Module* mod); + + // basic methods + int16_t begin(float freq = 434.0, float bw = 125.0, uint8_t sf = 9, uint8_t cr = 7, uint16_t syncWord = SX126X_SYNC_WORD_PRIVATE, int8_t power = 14, float currentLimit = 60.0, uint16_t preambleLength = 8); + + // configuration methods + int16_t setFrequency(float freq); + int16_t setOutputPower(int8_t power); + + private: + + +}; + +#endif diff --git a/src/modules/SX126x.cpp b/src/modules/SX126x.cpp new file mode 100644 index 00000000..4de399b8 --- /dev/null +++ b/src/modules/SX126x.cpp @@ -0,0 +1,360 @@ +#include "SX126x.h" + +SX126x::SX126x(Module* mod) : PhysicalLayer(SX126X_CRYSTAL_FREQ, SX126X_DIV_EXPONENT) { + _mod = mod; + _bw = SX126X_LORA_BW_125_0; + _bwKhz = 125.0; + _sf = 9; + _cr = SX126X_LORA_CR_4_7; + _ldro = 0x00; +} + +int16_t SX126x::begin(float bw, uint8_t sf, uint8_t cr, uint16_t syncWord, float currentLimit, uint16_t preambleLength) { + // set module properties + _mod->init(USE_SPI, INT_BOTH); + pinMode(_mod->getRx(), INPUT); + + // set mode to standby + standby(); + + // configure settings not accessible by API + config(); + + // configure publicly accessible settings + int16_t state = setBandwidth(bw); + if(state != ERR_NONE) { + return(state); + } + + state = setSpreadingFactor(sf); + if(state != ERR_NONE) { + return(state); + } + + state = setCodingRate(cr); + if(state != ERR_NONE) { + return(state); + } + + state = setSyncWord(syncWord); + if(state != ERR_NONE) { + return(state); + } + + state = setCurrentLimit(currentLimit); + if(state != ERR_NONE) { + return(state); + } + + state = setPreambleLength(preambleLength); + if(state != ERR_NONE) { + return(state); + } + + return(state); +} + +int16_t SX126x::transmit(uint8_t* data, size_t len, uint8_t addr) { + +} + +int16_t SX126x::receive(uint8_t* data, size_t len) { + +} + +int16_t SX126x::transmitDirect(uint32_t frf) { + +} + +int16_t SX126x::receiveDirect() { + +} + +int16_t SX126x::sleep() { + uint8_t data[] = {SX126X_SLEEP_START_COLD | SX126X_SLEEP_RTC_OFF}; + SPIwriteCommand(SX126X_CMD_SET_SLEEP, data, 1); + + // wait for SX126x to safely enter sleep mode + delayMicroseconds(500); + + return(ERR_NONE); +} + +int16_t SX126x::standby(uint8_t mode) { + uint8_t data[] = {mode}; + SPIwriteCommand(SX126X_CMD_SET_STANDBY, data, 1); + return(ERR_NONE); +} + +int16_t SX126x::setBandwidth(float bw) { + // check active modem + if(getPacketType() != SX126X_PACKET_TYPE_LORA) { + return(ERR_WRONG_MODEM); + } + + // check alowed bandwidth values + if(abs(bw - 7.8) <= 0.001) { + _bw = SX126X_LORA_BW_7_8; + } else if(abs(bw - 10.4) <= 0.001) { + _bw = SX126X_LORA_BW_10_4; + } else if(abs(bw - 15.6) <= 0.001) { + _bw = SX126X_LORA_BW_15_6; + } else if(abs(bw - 20.8) <= 0.001) { + _bw = SX126X_LORA_BW_20_8; + } else if(abs(bw - 31.25) <= 0.001) { + _bw = SX126X_LORA_BW_31_25; + } else if(abs(bw - 41.7) <= 0.001) { + _bw = SX126X_LORA_BW_41_7; + } else if(abs(bw - 62.5) <= 0.001) { + _bw = SX126X_LORA_BW_62_5; + } else if(abs(bw - 125.0) <= 0.001) { + _bw = SX126X_LORA_BW_125_0; + } else if(abs(bw - 250.0) <= 0.001) { + _bw = SX126X_LORA_BW_250_0; + } else if(abs(bw - 500.0) <= 0.001) { + _bw = SX126X_LORA_BW_500_0; + } else { + return(ERR_INVALID_BANDWIDTH); + } + + // update modulation parameters + _bwKhz = bw; + setModulationParams(_bw, _sf, _cr); + return(ERR_NONE); +} + +int16_t SX126x::setSpreadingFactor(uint8_t sf) { + // check active modem + if(getPacketType() != SX126X_PACKET_TYPE_LORA) { + return(ERR_WRONG_MODEM); + } + + // check allowed spreading factor values + if(!((sf >= 5) && (sf <= 12))) { + return(ERR_INVALID_SPREADING_FACTOR); + } + + // update modulation parameters + _sf = sf; + setModulationParams(_bw, _sf, _cr); + return(ERR_NONE); +} + +int16_t SX126x::setCodingRate(uint8_t cr) { + // check active modem + if(getPacketType() != SX126X_PACKET_TYPE_LORA) { + return(ERR_WRONG_MODEM); + } + + // check allowed spreading factor values + if(!((cr >= 5) && (cr <= 8))) { + return(ERR_INVALID_CODING_RATE); + } + + // update modulation parameters + _cr = cr - 4; + setModulationParams(_bw, _sf, _cr); + return(ERR_NONE); +} + +int16_t SX126x::setSyncWord(uint16_t syncWord) { + // check active modem + if(getPacketType() != SX126X_PACKET_TYPE_LORA) { + return(ERR_WRONG_MODEM); + } + + // update register + uint8_t data[2] = {(uint8_t)((syncWord >> 8) & 0xFF), (uint8_t)(syncWord & 0xFF)}; + writeRegister(SX126X_REG_LORA_SYNC_WORD_MSB, data, 2); + return(ERR_NONE); +} + +int16_t SX126x::setCurrentLimit(float currentLimit) { + // calculate raw value + uint8_t rawLimit = (uint8_t)(currentLimit / 2.5); + + // update register + writeRegister(SX126X_REG_OCP_CONFIGURATION, &rawLimit, 1); + return(ERR_NONE); +} + +int16_t SX126x::setPreambleLength(uint16_t preambleLength) { + // update packet parameters + _preambleLength = preambleLength; + setPacketParams((uint8_t)((_preambleLength >> 8) & 0xFF), (uint8_t)(_preambleLength & 0xFF), _payloadLength, _crcType); + return(ERR_NONE); +} + +float SX126x::getDataRate() { + +} + +int16_t SX126x::setFrequencyDeviation(float freqDev) { + +} + +void SX126x::setTx(uint32_t timeout) { + uint8_t data[3] = {(uint8_t)((timeout >> 16) & 0xFF), (uint8_t)((timeout >> 8) & 0xFF), (uint8_t)(timeout & 0xFF)}; + SPIwriteCommand(SX126X_CMD_SET_TX, data, 3); +} + +void SX126x::setRx(uint32_t timeout) { + uint8_t data[3] = {(uint8_t)((timeout >> 16) & 0xFF), (uint8_t)((timeout >> 8) & 0xFF), (uint8_t)(timeout & 0xFF)}; + SPIwriteCommand(SX126X_CMD_SET_RX, data, 3); +} + +void SX126x::setCad() { + SPIwriteCommand(SX126X_CMD_SET_CAD, NULL, 0); +} + +void SX126x::setPaConfig(uint8_t paDutyCycle, uint8_t deviceSel, uint8_t hpMax, uint8_t paLut) { + uint8_t data[4] = {paDutyCycle, deviceSel, hpMax, paLut}; + SPIwriteCommand(SX126X_CMD_SET_TX_PARAMS, data, 4); +} + +void SX126x::writeRegister(uint16_t addr, uint8_t* data, uint8_t numBytes) { + uint8_t* dat = new uint8_t[2 + numBytes]; + dat[0] = (uint8_t)((addr >> 8) & 0xFF); + dat[1] = (uint8_t)(addr & 0xFF); + memcpy(dat + 2, data, numBytes); + SPIwriteCommand(SX126X_CMD_WRITE_REGISTER, dat, 2 + numBytes); + delete[] dat; +} + +void SX126x::setDioIrqParams(uint16_t irqMask, uint16_t dio1Mask, uint16_t dio2Mask, uint16_t dio3Mask) { + uint8_t data[8] = {(uint8_t)((irqMask >> 8) & 0xFF), (uint8_t)(irqMask & 0xFF), + (uint8_t)((dio1Mask >> 8) & 0xFF), (uint8_t)(dio1Mask & 0xFF), + (uint8_t)((dio2Mask >> 8) & 0xFF), (uint8_t)(dio2Mask & 0xFF), + (uint8_t)((dio3Mask >> 8) & 0xFF), (uint8_t)(dio3Mask & 0xFF)}; + SPIwriteCommand(SX126X_CMD_SET_DIO_IRQ_PARAMS, data, 8); +} + +void SX126x::clearIrqStatus(uint16_t clearIrqParams) { + uint8_t data[2] = {(uint8_t)((clearIrqParams >> 8) & 0xFF), (uint8_t)(clearIrqParams & 0xFF)}; + SPIwriteCommand(SX126X_CMD_CLEAR_IRQ_STATUS, data, 2); +} + +void SX126x::setRfFrequency(uint32_t frf) { + uint8_t data[4] = {(uint8_t)((frf >> 24) & 0xFF), (uint8_t)((frf >> 16) & 0xFF), (uint8_t)((frf >> 8) & 0xFF), (uint8_t)(frf & 0xFF)}; + SPIwriteCommand(SX126X_CMD_SET_RF_FREQUENCY, data, 4); +} + +uint8_t SX126x::getPacketType() { + uint8_t data[1]; + SPIreadCommand(SX126X_CMD_GET_PACKET_TYPE, data, 1); + return(data[0]); +} + +void SX126x::setTxParams(uint8_t power, uint8_t rampTime) { + uint8_t data[2] = {power, rampTime}; + SPIwriteCommand(SX126X_CMD_SET_TX_PARAMS, data, 2); +} + +void SX126x::setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, uint8_t ldro) { + // calculate symbol length and enable low data rate optimization, if needed + if(ldro == 0xFF) { + float symbolLength = (float)(uint32_t(1) << _sf) / (float)_bwKhz; + if(symbolLength >= 16.0) { + _ldro = SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_ON; + } else { + _ldro = SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_OFF; + } + } else { + _ldro = ldro; + } + + uint8_t data[4] = {sf, bw, cr, _ldro}; + SPIwriteCommand(SX126X_CMD_SET_MODULATION_PARAMS, data, 4); +} + +void SX126x::setPacketParams(uint16_t preambleLength, uint8_t headerType, uint8_t payloadLength, uint8_t crcType, uint8_t invertIQ) { + uint8_t data[7] = {(uint8_t)((preambleLength >> 8) & 0xFF), (uint8_t)(preambleLength & 0xFF), headerType, payloadLength, crcType, invertIQ}; + SPIwriteCommand(SX126X_CMD_SET_PACKET_PARAMS, data, 7); +} + +uint8_t SX126x::getRssiInt() { + uint8_t data[1]; + SPIreadCommand(SX126X_CMD_GET_RSSI_INST, data, 1); + return(data[0]); +} + +int16_t SX126x::setFrequencyRaw(float freq) { + // calculate raw value + uint32_t frf = (freq * (uint32_t(1) << SX126X_DIV_EXPONENT)) / SX126X_CRYSTAL_FREQ; + setRfFrequency(frf); + return(ERR_NONE); +} + +int16_t SX126x::config() { + // set LoRa mode + uint8_t* data = new uint8_t[1]; + data[0] = SX126X_PACKET_TYPE_LORA; + SPIwriteCommand(SX126X_CMD_SET_PACKET_TYPE, data, 1); + + // set Rx/Tx fallback mode to STDBY_RC + data[0] = SX126X_RX_TX_FALLBACK_MODE_STDBY_RC; + SPIwriteCommand(SX126X_CMD_SET_RX_TX_FALLBACK_MODE, data, 1); + + // set CAD parameters + delete[] data; + data = new uint8_t[7]; + data[0] = SX126X_CAD_ON_8_SYMB; + data[1] = _sf + 13; + data[2] = 10; + data[3] = SX126X_CAD_GOTO_STDBY; + data[4] = 0x00; + data[5] = 0x00; + data[6] = 0x00; + SPIwriteCommand(SX126X_CMD_SET_CAD_PARAMS, data, 7); + delete[] data; + + return(ERR_NONE); +} + +void SX126x::SPIwriteCommand(uint8_t cmd, uint8_t* data, uint8_t numBytes, bool waitForBusy) { + SX126x::SPItransfer(cmd, true, data, NULL, numBytes, waitForBusy); +} + +void SX126x::SPIreadCommand(uint8_t cmd, uint8_t* data, uint8_t numBytes, bool waitForBusy) { + SX126x::SPItransfer(cmd, false, NULL, data, numBytes, waitForBusy); +} + +void SX126x::SPItransfer(uint8_t cmd, bool write, uint8_t* dataOut, uint8_t* dataIn, uint8_t numBytes, bool waitForBusy) { + // get pointer to used SPI interface + SPIClass* spi = _mod->getSpi(); + + // ensure BUSY is low (state meachine ready) + // TODO timeout + while(digitalRead(_mod->getRx())); + + // start transfer + digitalWrite(_mod->getCs(), LOW); + spi->beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0)); + + // send command byte + spi->transfer(cmd); + + // send/receive all bytes + if(write) { + for(uint8_t n = 0; n < numBytes; n++) { + spi->transfer(dataOut[n]); + } + } else { + // skip the first byte for read-type commands (status-only) + spi->transfer(SX126X_CMD_NOP); + for(uint8_t n = 0; n < numBytes; n++) { + dataIn[n] = spi->transfer(SX126X_CMD_NOP); + } + } + + // stop transfer + spi->endTransaction(); + digitalWrite(_mod->getCs(), HIGH); + + // wait for BUSY to go high and then low + // TODO timeout + if(waitForBusy) { + delayMicroseconds(1); + while(digitalRead(_mod->getRx())); + } +} diff --git a/src/modules/SX126x.h b/src/modules/SX126x.h new file mode 100644 index 00000000..d5ccac0e --- /dev/null +++ b/src/modules/SX126x.h @@ -0,0 +1,375 @@ +#ifndef _RADIOLIB_SX126X_H +#define _RADIOLIB_SX126X_H + +#include "TypeDef.h" +#include "Module.h" + +#include "../protocols/PhysicalLayer.h" + +// SX126X physical layer properties +#define SX126X_CRYSTAL_FREQ 32.0 +#define SX126X_DIV_EXPONENT 25 + +// SX126X SPI commands +// operational modes commands +#define SX126X_CMD_NOP 0x00 +#define SX126X_CMD_SET_SLEEP 0x84 +#define SX126X_CMD_SET_STANDBY 0x80 +#define SX126X_CMD_SET_FS 0xC1 +#define SX126X_CMD_SET_TX 0x83 +#define SX126X_CMD_SET_RX 0x82 +#define SX126X_CMD_STOP_TIMER_ON_PREAMBLE 0x9F +#define SX126X_CMD_SET_RX_DUTY_CYCLE 0x94 +#define SX126X_CMD_SET_CAD 0xC5 +#define SX126X_CMD_SET_TX_CONTINUOUS_WAVE 0xD1 +#define SX126X_CMD_SET_TX_INFINITE_PREAMBLE 0xD2 +#define SX126X_CMD_SET_REGULATOR_MODE 0x96 +#define SX126X_CMD_CALIBRATE 0x89 +#define SX126X_CMD_CALIBRATE_IMAGE 0x98 +#define SX126X_CMD_SET_PA_CONFIG 0x95 +#define SX126X_CMD_SET_RX_TX_FALLBACK_MODE 0x93 + +// register and buffer access commands +#define SX126X_CMD_WRITE_REGISTER 0x0D +#define SX126X_CMD_READ_REGISTER 0x1D +#define SX126X_CMD_WRITE_BUFFER 0x0E +#define SX126X_CMD_READ_BUFFER 0x1E + +// DIO and IRQ control +#define SX126X_CMD_SET_DIO_IRQ_PARAMS 0x08 +#define SX126X_CMD_GET_IRQ_STATUS 0x12 +#define SX126X_CMD_CLEAR_IRQ_STATUS 0x02 +#define SX126X_CMD_SET_DIO2_AS_RF_SWITCH_CTRL 0x9D +#define SX126X_CMD_SET_DIO3_AS_TCXO_CTRL 0x97 + +// RF, modulation and packet commands +#define SX126X_CMD_SET_RF_FREQUENCY 0x86 +#define SX126X_CMD_SET_PACKET_TYPE 0x8A +#define SX126X_CMD_GET_PACKET_TYPE 0x11 +#define SX126X_CMD_SET_TX_PARAMS 0x8E +#define SX126X_CMD_SET_MODULATION_PARAMS 0x8B +#define SX126X_CMD_SET_PACKET_PARAMS 0x8C +#define SX126X_CMD_SET_CAD_PARAMS 0x88 +#define SX126X_CMD_SET_BUFFER_BASE_ADDRESS 0x8F +#define SX126X_CMD_SET_LORA_SYMB_NUM_TIMEOUT 0x0A + +// status commands +#define SX126X_CMD_GET_STATUS 0xC0 +#define SX126X_CMD_GET_RSSI_INST 0x15 +#define SX126X_CMD_GET_RX_BUFFER_STATUS 0x13 +#define SX126X_CMD_GET_PACKET_STATUS 0x14 +#define SX126X_CMD_GET_DEVICE_ERRORS 0x17 +#define SX126X_CMD_CLEAR_DEVICE_ERRORS 0x07 +#define SX126X_CMD_GET_STATS 0x10 +#define SX126X_CMD_RESET_STATS 0x00 + + +// SX126X register map +#define SX126X_REG_WHITENING_INITIAL_MSB 0x06B8 +#define SX126X_REG_WHITENING_INITIAL_LSB 0x06B9 +#define SX126X_REG_CRC_INITIAL_MSB 0x06BC +#define SX126X_REG_CRC_INITIAL_LSB 0x06BD +#define SX126X_REG_CRC_POLYNOMIAL_MSB 0x06BE +#define SX126X_REG_CRC_POLYNOMIAL_LSB 0x06BF +#define SX126X_REG_SYNC_WORD_0 0x06C0 +#define SX126X_REG_SYNC_WORD_1 0x06C1 +#define SX126X_REG_SYNC_WORD_2 0x06C2 +#define SX126X_REG_SYNC_WORD_3 0x06C3 +#define SX126X_REG_SYNC_WORD_4 0x06C4 +#define SX126X_REG_SYNC_WORD_5 0x06C5 +#define SX126X_REG_SYNC_WORD_6 0x06C6 +#define SX126X_REG_SYNC_WORD_7 0x06C7 +#define SX126X_REG_NODE_ADDRESS 0x06CD +#define SX126X_REG_BROADCAST_ADDRESS 0x06CE +#define SX126X_REG_LORA_SYNC_WORD_MSB 0x0740 +#define SX126X_REG_LORA_SYNC_WORD_LSB 0x0741 +#define SX126X_REG_RANDOM_NUMBER_0 0x0819 +#define SX126X_REG_RANDOM_NUMBER_1 0x081A +#define SX126X_REG_RANDOM_NUMBER_2 0x081B +#define SX126X_REG_RANDOM_NUMBER_3 0x081C +#define SX126X_REG_RX_GAIN 0x08AC +#define SX126X_REG_OCP_CONFIGURATION 0x08E7 +#define SX126X_REG_XTA_TRIM 0x0911 +#define SX126X_REG_XTB_TRIM 0x0912 + + +// SX126X SPI command variables +//SX126X_CMD_SET_SLEEP +#define SX126X_SLEEP_START_COLD 0b00000000 // 2 2 sleep mode: cold start, configuration is lost (default) +#define SX126X_SLEEP_START_WARM 0b00000100 // 2 2 warm start, configuration is retained +#define SX126X_SLEEP_RTC_OFF 0b00000000 // 0 0 wake on RTC timeout: disabled +#define SX126X_SLEEP_RTC_ON 0b00000001 // 0 0 enabled + +//SX126X_CMD_SET_STANDBY +#define SX126X_STANDBY_RC 0x00 // 7 0 standby mode: 13 MHz RC oscillator +#define SX126X_STANDBY_XOSC 0x01 // 7 0 32 MHz crystal oscillator + +//SX126X_CMD_SET_RX +#define SX126X_RX_TIMEOUT_NONE 0x000000 // 23 0 Rx timeout duration: no timeout (Rx single mode) +#define SX126X_RX_TIMEOUT_INF 0xFFFFFF // 23 0 infinite (Rx continuous mode) + +//SX126X_CMD_STOP_TIMER_ON_PREAMBLE +#define SX126X_STOP_ON_PREAMBLE_OFF 0x00 // 7 0 stop timer on: sync word or header (default) +#define SX126X_STOP_ON_PREAMBLE_ON 0x01 // 7 0 preamble detection + +//SX126X_CMD_SET_REGULATOR_MODE +#define SX126X_REGULATOR_LDO 0x00 // 7 0 set regulator mode: LDO (default) +#define SX126X_REGULATOR_DC_DC 0x01 // 7 0 DC-DC + +//SX126X_CMD_CALIBRATE +#define SX126X_CALIBRATE_IMAGE_OFF 0b00000000 // 6 6 image calibration: disabled +#define SX126X_CALIBRATE_IMAGE_ON 0b01000000 // 6 6 enabled +#define SX126X_CALIBRATE_ADC_BULK_P_OFF 0b00000000 // 5 5 ADC bulk P calibration: disabled +#define SX126X_CALIBRATE_ADC_BULK_P_ON 0b00100000 // 5 5 enabled +#define SX126X_CALIBRATE_ADC_BULK_N_OFF 0b00000000 // 4 4 ADC bulk N calibration: disabled +#define SX126X_CALIBRATE_ADC_BULK_N_ON 0b00010000 // 4 4 enabled +#define SX126X_CALIBRATE_ADC_PULSE_OFF 0b00000000 // 3 3 ADC pulse calibration: disabled +#define SX126X_CALIBRATE_ADC_PULSE_ON 0b00001000 // 3 3 enabled +#define SX126X_CALIBRATE_PLL_OFF 0b00000000 // 2 2 PLL calibration: disabled +#define SX126X_CALIBRATE_PLL_ON 0b00000100 // 2 2 enabled +#define SX126X_CALIBRATE_RC13M_OFF 0b00000000 // 1 1 13 MHz RC osc. calibration: disabled +#define SX126X_CALIBRATE_RC13M_ON 0b00000010 // 1 1 enabled +#define SX126X_CALIBRATE_RC64K_OFF 0b00000000 // 0 0 64 kHz RC osc. calibration: disabled +#define SX126X_CALIBRATE_RC64K_ON 0b00000001 // 0 0 enabled + +//SX126X_CMD_CALIBRATE_IMAGE +#define SX126X_CAL_IMG_430_MHZ_1 0x6B +#define SX126X_CAL_IMG_430_MHZ_2 0x6F +#define SX126X_CAL_IMG_470_MHZ_1 0x75 +#define SX126X_CAL_IMG_470_MHZ_2 0x81 +#define SX126X_CAL_IMG_779_MHZ_1 0xC1 +#define SX126X_CAL_IMG_779_MHZ_2 0xC5 +#define SX126X_CAL_IMG_863_MHZ_1 0xD7 +#define SX126X_CAL_IMG_863_MHZ_2 0xDB +#define SX126X_CAL_IMG_902_MHZ_1 0xE1 +#define SX126X_CAL_IMG_902_MHZ_2 0xE9 + +//SX126X_CMD_SET_PA_CONFIG +#define SX126X_PA_CONFIG_HP_MAX 0x07 +#define SX126X_PA_CONFIG_SX1268 0x01 +#define SX126X_PA_CONFIG_PA_LUT 0x01 + +//SX126X_CMD_SET_RX_TX_FALLBACK_MODE +#define SX126X_RX_TX_FALLBACK_MODE_FS 0x40 // 7 0 after Rx/Tx go to: FS mode +#define SX126X_RX_TX_FALLBACK_MODE_STDBY_XOSC 0x30 // 7 0 standby with crystal oscillator +#define SX126X_RX_TX_FALLBACK_MODE_STDBY_RC 0x20 // 7 0 standby with RC oscillator (default) + +//SX126X_CMD_SET_DIO_IRQ_PARAMS +#define SX126X_IRQ_TIMEOUT 0b1000000000 // 9 9 Rx or Tx timeout +#define SX126X_IRQ_CAD_DETECTED 0b0100000000 // 8 8 channel activity detected +#define SX126X_IRQ_CAD_DONE 0b0010000000 // 7 7 channel activity detection finished +#define SX126X_IRQ_CRC_ERR 0b0001000000 // 6 6 wrong CRC received +#define SX126X_IRQ_HEADER_ERR 0b0000100000 // 5 5 LoRa header CRC error +#define SX126X_IRQ_HEADER_VALID 0b0000010000 // 4 4 valid LoRa header received +#define SX126X_IRQ_SYNC_WORD_VALID 0b0000001000 // 3 3 valid sync word detected +#define SX126X_IRQ_PREAMBLE_DETECTED 0b0000000100 // 2 2 preamble detected +#define SX126X_IRQ_RX_DONE 0b0000000010 // 1 1 packet received +#define SX126X_IRQ_TX_DONE 0b0000000001 // 0 0 packet transmission completed + +//SX126X_CMD_SET_DIO2_AS_RF_SWITCH_CTRL +#define SX126X_DIO2_AS_IRQ 0x00 // 7 0 DIO2 configuration: IRQ +#define SX126X_DIO2_AS_RF_SWITCH 0x01 // 7 0 RF switch control + +//SX126X_CMD_SET_DIO3_AS_TCXO_CTRL +#define SX126X_DIO3_OUTPUT_1_6 0x00 // 7 0 DIO3 voltage output for TCXO: 1.6 V +#define SX126X_DIO3_OUTPUT_1_7 0x01 // 7 0 1.7 V +#define SX126X_DIO3_OUTPUT_1_8 0x02 // 7 0 1.8 V +#define SX126X_DIO3_OUTPUT_2_2 0x03 // 7 0 2.2 V +#define SX126X_DIO3_OUTPUT_2_4 0x04 // 7 0 2.4 V +#define SX126X_DIO3_OUTPUT_2_7 0x05 // 7 0 2.7 V +#define SX126X_DIO3_OUTPUT_3_0 0x06 // 7 0 3.0 V +#define SX126X_DIO3_OUTPUT_3_3 0x07 // 7 0 3.3 V + +//SX126X_CMD_SET_PACKET_TYPE +#define SX126X_PACKET_TYPE_GFSK 0x00 // 7 0 packet type: GFSK +#define SX126X_PACKET_TYPE_LORA 0x01 // 7 0 LoRa + +//SX126X_CMD_SET_TX_PARAMS +#define SX126X_PA_RAMP_10U 0x00 // 7 0 ramp time: 10 us +#define SX126X_PA_RAMP_20U 0x01 // 7 0 20 us +#define SX126X_PA_RAMP_40U 0x02 // 7 0 40 us +#define SX126X_PA_RAMP_80U 0x03 // 7 0 80 us +#define SX126X_PA_RAMP_200U 0x04 // 7 0 200 us +#define SX126X_PA_RAMP_800U 0x05 // 7 0 800 us +#define SX126X_PA_RAMP_1700U 0x06 // 7 0 1700 us +#define SX126X_PA_RAMP_3400U 0x07 // 7 0 3400 us + +//SX126X_CMD_SET_MODULATION_PARAMS +#define SX126X_GFSK_FILTER_NONE 0x00 // 7 0 GFSK filter: none +#define SX126X_GFSK_FILTER_GAUSS_0_3 0x08 // 7 0 Gaussian, BT = 0.3 +#define SX126X_GFSK_FILTER_GAUSS_0_5 0x09 // 7 0 Gaussian, BT = 0.5 +#define SX126X_GFSK_FILTER_GAUSS_0_7 0x0A // 7 0 Gaussian, BT = 0.7 +#define SX126X_GFSK_FILTER_GAUSS_1 0x0B // 7 0 Gaussian, BT = 1 +#define SX126X_GFSK_RX_BW_4_8 0x1F // 7 0 GFSK Rx bandwidth: 4.8 kHz +#define SX126X_GFSK_RX_BW_5_8 0x17 // 7 0 5.8 kHz +#define SX126X_GFSK_RX_BW_7_3 0x0F // 7 0 7.3 kHz +#define SX126X_GFSK_RX_BW_9_7 0x1E // 7 0 9.7 kHz +#define SX126X_GFSK_RX_BW_11_7 0x16 // 7 0 11.7 kHz +#define SX126X_GFSK_RX_BW_14_6 0x0E // 7 0 14.6 kHz +#define SX126X_GFSK_RX_BW_19_5 0x1D // 7 0 19.5 kHz +#define SX126X_GFSK_RX_BW_23_4 0x15 // 7 0 23.4 kHz +#define SX126X_GFSK_RX_BW_29_3 0x0D // 7 0 29.3 kHz +#define SX126X_GFSK_RX_BW_39_0 0x1C // 7 0 39.0 kHz +#define SX126X_GFSK_RX_BW_46_9 0x14 // 7 0 46.9 kHz +#define SX126X_GFSK_RX_BW_58_6 0x0C // 7 0 58.6 kHz +#define SX126X_GFSK_RX_BW_78_2 0x1B // 7 0 78.2 kHz +#define SX126X_GFSK_RX_BW_93_8 0x13 // 7 0 93.8 kHz +#define SX126X_GFSK_RX_BW_117_3 0x0B // 7 0 117.3 kHz +#define SX126X_GFSK_RX_BW_156_2 0x1A // 7 0 156.2 kHz +#define SX126X_GFSK_RX_BW_187_2 0x12 // 7 0 187.2 kHz +#define SX126X_GFSK_RX_BW_232_3 0x0A // 7 0 232.3 kHz +#define SX126X_GFSK_RX_BW_312_0 0x19 // 7 0 312.0 kHz +#define SX126X_GFSK_RX_BW_373_6 0x11 // 7 0 373.6 kHz +#define SX126X_GFSK_RX_BW_467_0 0x09 // 7 0 467.0 kHz +#define SX126X_LORA_BW_7_8 0x00 // 7 0 LoRa bandwidth: 7.8 kHz +#define SX126X_LORA_BW_10_4 0x08 // 7 0 10.4 kHz +#define SX126X_LORA_BW_15_6 0x01 // 7 0 15.6 kHz +#define SX126X_LORA_BW_20_8 0x09 // 7 0 20.8 kHz +#define SX126X_LORA_BW_31_25 0x02 // 7 0 31.25 kHz +#define SX126X_LORA_BW_41_7 0x0A // 7 0 41.7 kHz +#define SX126X_LORA_BW_62_5 0x03 // 7 0 62.5 kHz +#define SX126X_LORA_BW_125_0 0x04 // 7 0 125.0 kHz +#define SX126X_LORA_BW_250_0 0x05 // 7 0 250.0 kHz +#define SX126X_LORA_BW_500_0 0x06 // 7 0 500.0 kHz +#define SX126X_LORA_CR_4_5 0x01 // 7 0 LoRa coding rate: 4/5 +#define SX126X_LORA_CR_4_6 0x02 // 7 0 4/6 +#define SX126X_LORA_CR_4_7 0x03 // 7 0 4/7 +#define SX126X_LORA_CR_4_8 0x04 // 7 0 4/8 +#define SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_OFF 0x00 // 7 0 LoRa low data rate optimization: disabled +#define SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_ON 0x01 // 7 0 enabled + +//SX126X_CMD_SET_PACKET_PARAMS +#define SX126X_GFSK_PREAMBLE_DETECT_OFF 0x00 // 7 0 GFSK minimum preamble length before reception starts: detector disabled +#define SX126X_GFSK_PREAMBLE_DETECT_8 0x04 // 7 0 8 bits +#define SX126X_GFSK_PREAMBLE_DETECT_16 0x05 // 7 0 16 bits +#define SX126X_GFSK_PREAMBLE_DETECT_24 0x06 // 7 0 24 bits +#define SX126X_GFSK_PREAMBLE_DETECT_32 0x07 // 7 0 32 bits +#define SX126X_GFSK_ADDRESS_FILT_OFF 0x00 // 7 0 GFSK address filtering: disabled +#define SX126X_GFSK_ADDRESS_FILT_NODE 0x01 // 7 0 node only +#define SX126X_GFSK_ADDRESS_FILT_NODE_BROADCAST 0x02 // 7 0 node and broadcast +#define SX126X_GFSK_PACKET_FIXED 0x00 // 7 0 GFSK packet type: fixed (payload length known in advance to both sides) +#define SX126X_GFSK_PACKET_VARIABLE 0x01 // 7 0 variable (payload length added to packet) +#define SX126X_GFSK_CRC_OFF 0x01 // 7 0 GFSK packet CRC: disabled +#define SX126X_GFSK_CRC_1_BYTE 0x00 // 7 0 1 byte +#define SX126X_GFSK_CRC_2_BYTE 0x02 // 7 0 2 byte +#define SX126X_GFSK_CRC_1_BYTE_INV 0x04 // 7 0 1 byte, inverted +#define SX126X_GFSK_CRC_2_BYTE_INV 0x06 // 7 0 2 byte, inverted +#define SX126X_GFSK_WHITENING_OFF 0x00 // 7 0 GFSK data whitening: disabled +#define SX126X_GFSK_WHITENING_ON 0x01 // 7 0 enabled +#define SX126X_LORA_HEADER_EXPLICIT 0x00 // 7 0 LoRa header mode: explicit +#define SX126X_LORA_HEADER_IMPLICIT 0x01 // 7 0 implicit +#define SX126X_LORA_CRC_OFF 0x00 // 7 0 LoRa CRC mode: disabled +#define SX126X_LORA_CRC_ON 0x01 // 7 0 enabled +#define SX126X_LORA_IQ_STANDARD 0x00 // 7 0 LoRa IQ setup: standard +#define SX126X_LORA_IQ_INVERTED 0x01 // 7 0 inverted + +//SX126X_CMD_SET_CAD_PARAMS +#define SX126X_CAD_ON_1_SYMB 0x00 // 7 0 number of symbols used for CAD: 1 +#define SX126X_CAD_ON_2_SYMB 0x01 // 7 0 2 +#define SX126X_CAD_ON_4_SYMB 0x02 // 7 0 4 +#define SX126X_CAD_ON_8_SYMB 0x03 // 7 0 8 +#define SX126X_CAD_ON_16_SYMB 0x04 // 7 0 16 +#define SX126X_CAD_GOTO_STDBY 0x00 // 7 0 after CAD is done, always go to STDBY_RC mode +#define SX126X_CAD_GOTO_RX 0x01 // 7 0 after CAD is done, go to Rx mode if activity is detected + +//SX126X_CMD_GET_STATUS +#define SX126X_STATUS_MODE_STDBY_RC 0b00100000 // 6 4 current chip mode: STDBY_RC +#define SX126X_STATUS_MODE_STDBY_XOSC 0b00110000 // 6 4 STDBY_XOSC +#define SX126X_STATUS_MODE_FS 0b01000000 // 6 4 FS +#define SX126X_STATUS_MODE_RX 0b01010000 // 6 4 RX +#define SX126X_STATUS_MODE_TX 0b01100000 // 6 4 TX +#define SX126X_STATUS_DATA_AVAILABLE 0b00000100 // 3 1 command status: packet received and data can be retrieved +#define SX126X_STATUS_CMD_TIMEOUT 0b00000110 // 3 1 SPI command timed out +#define SX126X_STATUS_CMD_INVALID 0b00001000 // 3 1 invalid SPI command +#define SX126X_STATUS_CMD_FAILED 0b00001010 // 3 1 SPI command failed to execute +#define SX126X_STATUS_TX_DONE 0b00001100 // 3 1 packet transmission done + +//SX126X_CMD_GET_PACKET_STATUS +#define SX126X_GFSK_RX_STATUS_PREAMBLE_ERR 0b10000000 // 7 7 GFSK Rx status: preamble error +#define SX126X_GFSK_RX_STATUS_SYNC_ERR 0b01000000 // 6 6 sync word error +#define SX126X_GFSK_RX_STATUS_ADRS_ERR 0b00100000 // 5 5 address error +#define SX126X_GFSK_RX_STATUS_CRC_ERR 0b00010000 // 4 4 CRC error +#define SX126X_GFSK_RX_STATUS_LENGTH_ERR 0b00001000 // 3 3 length error +#define SX126X_GFSK_RX_STATUS_ABORT_ERR 0b00000100 // 2 2 abort error +#define SX126X_GFSK_RX_STATUS_PACKET_RECEIVED 0b00000010 // 2 2 packet received +#define SX126X_GFSK_RX_STATUS_PACKET_SENT 0b00000001 // 2 2 packet sent + +//SX126X_CMD_GET_DEVICE_ERRORS +#define SX126X_PA_RAMP_ERR 0b100000000 // 8 8 device errors: PA ramping failed +#define SX126X_PLL_LOCK_ERR 0b001000000 // 6 6 PLL failed to lock +#define SX126X_XOSC_START_ERR 0b000100000 // 5 5 crystal oscillator failed to start +#define SX126X_IMG_CALIB_ERR 0b000010000 // 4 4 image calibration failed +#define SX126X_ADC_CALIB_ERR 0b000001000 // 3 3 ADC calibration failed +#define SX126X_PLL_CALIB_ERR 0b000000100 // 2 2 PLL calibration failed +#define SX126X_RC13M_CALIB_ERR 0b000000010 // 1 1 RC13M calibration failed +#define SX126X_RC64K_CALIB_ERR 0b000000001 // 0 0 RC64K calibration failed + + +// SX126X SPI register variables +//SX126X_REG_LORA_SYNC_WORD_MSB + LSB +#define SX126X_SYNC_WORD_PUBLIC 0x3444 +#define SX126X_SYNC_WORD_PRIVATE 0x1424 + + +class SX126x: public PhysicalLayer { + public: + // introduce PhysicalLayer overloads + using PhysicalLayer::transmit; + using PhysicalLayer::receive; + + // constructor + SX126x(Module* mod); + + // basic methods + int16_t begin(float bw, uint8_t sf, uint8_t cr, uint16_t syncWord, float currentLimit, uint16_t preambleLength); + int16_t transmit(uint8_t* data, size_t len, uint8_t addr = 0); + int16_t receive(uint8_t* data, size_t len); + int16_t transmitDirect(uint32_t frf = 0); + int16_t receiveDirect(); + int16_t sleep(); + int16_t standby(uint8_t mode = SX126X_STANDBY_RC); + + // configuration methods + int16_t setBandwidth(float bw); + int16_t setSpreadingFactor(uint8_t sf); + int16_t setCodingRate(uint8_t cr); + int16_t setSyncWord(uint16_t syncWord); + int16_t setCurrentLimit(float currentLimit); + int16_t setPreambleLength(uint16_t preambleLength); + float getDataRate(); + int16_t setFrequencyDeviation(float freqDev); + + protected: + // SX1276x SPI command implementations + void setTx(uint32_t timeout); + void setRx(uint32_t timeout); + void setCad(); + void setPaConfig(uint8_t paDutyCycle, uint8_t deviceSel, uint8_t hpMax = SX126X_PA_CONFIG_HP_MAX, uint8_t paLut = SX126X_PA_CONFIG_PA_LUT); + void writeRegister(uint16_t addr, uint8_t* data, uint8_t numBytes); + void setDioIrqParams(uint16_t irqMask, uint16_t dio1Mask, uint16_t dio2Mask, uint16_t dio3Mask = 0x0000); + void clearIrqStatus(uint16_t clearIrqParams = 0xFFFF); + void setRfFrequency(uint32_t frf); + uint8_t getPacketType(); + void setTxParams(uint8_t power, uint8_t rampTime = SX126X_PA_RAMP_80U); + void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, uint8_t ldro = 0xFF); + void setPacketParams(uint16_t preambleLength, uint8_t payloadLength, uint8_t crcType, uint8_t headerType = SX126X_LORA_HEADER_EXPLICIT, uint8_t invertIQ = SX126X_LORA_IQ_STANDARD); + uint8_t getRssiInt(); + + int16_t setFrequencyRaw(float freq); + + private: + Module* _mod; + + uint8_t _bw, _sf, _cr, _ldro, _payloadLength, _crcType; + uint16_t _preambleLength; + float _bwKhz; + + int16_t config(); + + // common low-level SPI interface + void SPIwriteCommand(uint8_t cmd, uint8_t* data, uint8_t numBytes, bool waitForBusy = true); + void SPIreadCommand(uint8_t cmd, uint8_t* data, uint8_t numBytes, bool waitForBusy = true); + void SPItransfer(uint8_t cmd, bool write, uint8_t* dataOut, uint8_t* dataIn, uint8_t numBytes, bool waitForBusy); +}; + +#endif