diff --git a/keywords.txt b/keywords.txt index dcec71c7..1bb6c6f8 100644 --- a/keywords.txt +++ b/keywords.txt @@ -90,6 +90,10 @@ setDataShapingOOK KEYWORD2 setCRC KEYWORD2 variablePacketLengthMode KEYWORD2 fixedPacketLengthMode KEYWORD2 +setCrcFiltering KEYWORD2 +enableSyncWordFiltering KEYWORD2 +disableSyncWordFiltering KEYWORD2 +setPromiscuous KEYWORD2 # RF69-specific setAESKey KEYWORD2 diff --git a/src/modules/CC1101.cpp b/src/modules/CC1101.cpp index 8c1a5ab1..f841fbd5 100644 --- a/src/modules/CC1101.cpp +++ b/src/modules/CC1101.cpp @@ -4,6 +4,8 @@ CC1101::CC1101(Module* module) : PhysicalLayer(CC1101_CRYSTAL_FREQ, CC1101_DIV_E _mod = module; _packetLengthQueried = false; _packetLengthConfig = CC1101_LENGTH_CONFIG_VARIABLE; + + _syncWordLength = CC1101_DEFAULT_SYNC_WORD_LENGTH; } int16_t CC1101::begin(float freq, float br, float rxBw, float freqDev, int8_t power) { @@ -441,11 +443,35 @@ int16_t CC1101::setOutputPower(int8_t power) { return(SPIsetRegValue(CC1101_REG_PATABLE, powerRaw)); } -int16_t CC1101::setSyncWord(uint8_t syncH, uint8_t syncL) { - // set sync word - int16_t state = SPIsetRegValue(CC1101_REG_SYNC1, syncH); - state |= SPIsetRegValue(CC1101_REG_SYNC0, syncL); - return(state); +int16_t CC1101::setSyncWord(uint8_t* syncWord, uint8_t len, uint8_t maxErrBits) { + if((maxErrBits > 1) || (len > CC1101_MAX_SYNC_WORD_LENGTH)) { + return(ERR_INVALID_SYNC_WORD); + } + + // sync word must not contain value 0x00 + for(uint8_t i = 0; i < len; i++) { + if(syncWord[i] == 0x00) { + return(ERR_INVALID_SYNC_WORD); + } + } + + _syncWordLength = len; + + // enable sync word filtering + int16_t state = enableSyncWordFiltering(maxErrBits); + if (state != ERR_NONE) { + return(state); + } + + // set sync word register + _mod->SPIwriteRegisterBurst(CC1101_REG_SYNC1, syncWord, len); + + return(ERR_NONE); +} + +int16_t CC1101::setSyncWord(uint8_t syncH, uint8_t syncL, uint8_t maxErrBits) { + uint8_t syncWord[] = { syncH, syncL }; + return(setSyncWord(syncWord, sizeof(syncWord), maxErrBits)); } int16_t CC1101::setPreambleLength(uint8_t preambleLength) { @@ -584,6 +610,76 @@ int16_t CC1101::variablePacketLengthMode(uint8_t maxLen) { return(state); } +int16_t CC1101::enableSyncWordFiltering(uint8_t maxErrBits) { + if (maxErrBits > 1) { + return(ERR_INVALID_SYNC_WORD); + } + + if (maxErrBits == 0) { + if (_syncWordLength == 1) { + // in 16 bit sync word, expect all 16 bits + return(SPIsetRegValue(CC1101_REG_MDMCFG2, CC1101_SYNC_MODE_16_16, 2, 0)); + } else { + // there's no 32 of 32 case, so we resort to 30 of 32 bits required + return(SPIsetRegValue(CC1101_REG_MDMCFG2, CC1101_SYNC_MODE_30_32, 2, 0)); + } + } + + if (maxErrBits == 1) { + if (_syncWordLength == 1) { + // in 16 bit sync word, expect at least 15 bits + return(SPIsetRegValue(CC1101_REG_MDMCFG2, CC1101_SYNC_MODE_15_16, 2, 0)); + } else { + // in 32 bits sync word (16 + 16), expect 30 of 32 to match + return(SPIsetRegValue(CC1101_REG_MDMCFG2, CC1101_SYNC_MODE_30_32, 2, 0)); + } + } + + return(ERR_UNKNOWN); +} + +int16_t CC1101::disableSyncWordFiltering() { + return(SPIsetRegValue(CC1101_REG_MDMCFG2, CC1101_SYNC_MODE_NONE, 2, 0)); +} + +int16_t CC1101::setCrcFiltering(bool crcOn) { + if (crcOn == true) { + return(SPIsetRegValue(CC1101_REG_PKTCTRL0, CC1101_CRC_ON, 2, 2)); + } else { + return(SPIsetRegValue(CC1101_REG_PKTCTRL0, CC1101_CRC_OFF, 2, 2)); + } +} + +int16_t CC1101::setPromiscuousMode(bool promiscuous) { + int16_t state = ERR_NONE; + + if (_promiscuous == promiscuous) { + return(state); + } + + if (promiscuous == true) { + // disable preamble and sync word filtering and insertion + state = disableSyncWordFiltering(); + if (state != ERR_NONE) { + return(state); + } + + // disable CRC filtering + state = setCrcFiltering(false); + } else { + // enable preamble and sync word filtering and insertion + state = enableSyncWordFiltering(); + if (state != ERR_NONE) { + return(state); + } + + // enable CRC filtering + state = setCrcFiltering(true); + } + + return(state); +} + int16_t CC1101::config() { // enable automatic frequency synthesizer calibration int16_t state = SPIsetRegValue(CC1101_REG_MCSM0, CC1101_FS_AUTOCAL_IDLE_TO_RXTX, 5, 4); diff --git a/src/modules/CC1101.h b/src/modules/CC1101.h index 16b23a6e..5c8ef657 100644 --- a/src/modules/CC1101.h +++ b/src/modules/CC1101.h @@ -10,6 +10,9 @@ #define CC1101_CRYSTAL_FREQ 26.0 #define CC1101_DIV_EXPONENT 16 #define CC1101_MAX_PACKET_LENGTH 63 +#define CC1101_MAX_SYNC_WORD_LENGTH 2 +#define CC1101_DEFAULT_SYNC_WORD_LENGTH 2 +#define CC1101_DEFAULT_SYNC_WORD { 0xD3, 0x91 } // CC1101 SPI commands #define CC1101_CMD_READ 0b10000000 @@ -237,7 +240,7 @@ #define CC1101_MOD_FORMAT_MFSK 0b01110000 // 6 4 MFSK - only for data rates above 26 kBaud #define CC1101_MANCHESTER_EN_OFF 0b00000000 // 3 3 Manchester encoding: disabled (default) #define CC1101_MANCHESTER_EN_ON 0b00001000 // 3 3 enabled -#define CC1101_SYNC_MODE_NONE 0b00000000 // 2 0 synchronization: no preamble sync +#define CC1101_SYNC_MODE_NONE 0b00000000 // 2 0 synchronization: no preamble/sync #define CC1101_SYNC_MODE_15_16 0b00000001 // 2 0 15/16 sync word bits #define CC1101_SYNC_MODE_16_16 0b00000010 // 2 0 16/16 sync word bits (default) #define CC1101_SYNC_MODE_30_32 0b00000011 // 2 0 30/32 sync word bits @@ -694,9 +697,24 @@ class CC1101: public PhysicalLayer { \param syncL LSB of the sync word. + \param maxErrBits Maximum allowed number of bit errors in received sync word. Defaults to 0. + \returns \ref status_codes */ - int16_t setSyncWord(uint8_t syncH, uint8_t syncL); + int16_t setSyncWord(uint8_t syncH, uint8_t syncL, uint8_t maxErrBits = 0); + + /*! + \brief Sets 1 or 2 bytes of sync word. + + \param syncWord Pointer to the array of sync word bytes. + + \param len Sync word length in bytes. + + \param maxErrBits Maximum allowed number of bit errors in received sync word. Defaults to 0. + + \returns \ref status_codes + */ + int16_t setSyncWord(uint8_t* syncWord, uint8_t len, uint8_t maxErrBits = 0); /*! \brief Sets preamble length. @@ -766,6 +784,40 @@ class CC1101: public PhysicalLayer { */ int16_t variablePacketLengthMode(uint8_t maxLen = CC1101_MAX_PACKET_LENGTH); + /*! + \brief Enable sync word filtering and generation. + + \param numBits Sync word length in bits. + + \returns \ref status_codes + */ + int16_t enableSyncWordFiltering(uint8_t maxErrBits = 0); + + /*! + \brief Disable preamble and sync word filtering and generation. + + \returns \ref status_codes + */ + int16_t disableSyncWordFiltering(); + + /*! + \brief Enable CRC filtering and generation. + + \param crcOn Set or unset promiscuous mode. + + \returns \ref status_codes + */ + int16_t setCrcFiltering(bool crcOn = true); + + /*! + \brief Set modem in "sniff" mode: no packet filtering (e.g., no preamble, sync word, address, CRC). + + \param promiscuous Set or unset promiscuous mode. + + \returns \ref status_codes + */ + int16_t setPromiscuousMode(bool promiscuous = true); + private: Module* _mod; @@ -777,6 +829,10 @@ class CC1101: public PhysicalLayer { bool _packetLengthQueried; uint8_t _packetLengthConfig; + bool _promiscuous; + + uint8_t _syncWordLength; + int16_t config(); int16_t directMode(); void getExpMant(float target, uint16_t mantOffset, uint8_t divExp, uint8_t expMax, uint8_t& exp, uint8_t& mant); diff --git a/src/modules/RF69.cpp b/src/modules/RF69.cpp index a0febe19..882ca15d 100644 --- a/src/modules/RF69.cpp +++ b/src/modules/RF69.cpp @@ -3,8 +3,13 @@ RF69::RF69(Module* module) : PhysicalLayer(RF69_CRYSTAL_FREQ, RF69_DIV_EXPONENT, RF69_MAX_PACKET_LENGTH) { _mod = module; _tempOffset = 0; + _packetLengthQueried = false; _packetLengthConfig = RF69_PACKET_FORMAT_VARIABLE; + + _promiscuous = false; + + _syncWordLength = RF69_DEFAULT_SYNC_WORD_LENGTH; } int16_t RF69::begin(float freq, float br, float rxBw, float freqDev, int8_t power) { @@ -87,8 +92,8 @@ int16_t RF69::begin(float freq, float br, float rxBw, float freqDev, int8_t powe } // default sync word values 0x2D01 is the same as the default in LowPowerLab RFM69 library - uint8_t syncWord[] = {0x2D, 0x01}; - state = setSyncWord(syncWord, 2); + uint8_t syncWord[] = RF69_DEFAULT_SYNC_WORD; + state = setSyncWord(syncWord, sizeof(syncWord)); if(state != ERR_NONE) { return(state); } @@ -495,7 +500,7 @@ int16_t RF69::setOutputPower(int8_t power) { int16_t RF69::setSyncWord(uint8_t* syncWord, size_t len, uint8_t maxErrBits) { // check constraints - if((maxErrBits > 7) || (len > 8)) { + if((maxErrBits > 7) || (len > RF69_MAX_SYNC_WORD_LENGTH)) { return(ERR_INVALID_SYNC_WORD); } @@ -506,13 +511,14 @@ int16_t RF69::setSyncWord(uint8_t* syncWord, size_t len, uint8_t maxErrBits) { } } - // enable sync word recognition - int16_t state = _mod->SPIsetRegValue(RF69_REG_SYNC_CONFIG, RF69_SYNC_ON | RF69_FIFO_FILL_CONDITION_SYNC | (len - 1) << 3 | maxErrBits, 7, 0); - if(state != ERR_NONE) { + _syncWordLength = len; + + int16_t state = enableSyncWordFiltering(maxErrBits); + if (state != ERR_NONE) { return(state); } - // set sync word + // set sync word register _mod->SPIwriteRegisterBurst(RF69_REG_SYNC_VALUE_1, syncWord, len); return(ERR_NONE); } @@ -636,6 +642,71 @@ int16_t RF69::variablePacketLengthMode(uint8_t maxLen) { return(state); } +int16_t RF69::enableSyncWordFiltering(uint8_t maxErrBits) { + // enable sync word recognition + int16_t state = _mod->SPIsetRegValue(RF69_REG_SYNC_CONFIG, RF69_SYNC_ON | RF69_FIFO_FILL_CONDITION_SYNC | (_syncWordLength - 1) << 3 | maxErrBits, 7, 0); + if(state != ERR_NONE) { + return(state); + } + + return(state); +} + +int16_t RF69::disableSyncWordFiltering() { + // disable preamble detection and generation + int16_t state = _mod->SPIsetRegValue(RF69_REG_PREAMBLE_LSB, 0, 7, 0); + state |= _mod->SPIsetRegValue(RF69_REG_PREAMBLE_MSB, 0, 7, 0); + if (state != ERR_NONE) { + return(state); + } + + // disable sync word detection and generation + state = _mod->SPIsetRegValue(RF69_REG_SYNC_CONFIG, RF69_SYNC_OFF | RF69_FIFO_FILL_CONDITION, 7, 6); + if (state != ERR_NONE) { + return(state); + } + + return(state); +} + +int16_t RF69::setCrcFiltering(bool crcOn) { + if (crcOn == true) { + return(_mod->SPIsetRegValue(RF69_REG_PACKET_CONFIG_1, RF69_CRC_ON, 4, 4)); + } else { + return(_mod->SPIsetRegValue(RF69_REG_PACKET_CONFIG_1, RF69_CRC_OFF, 4, 4)); + } +} + +int16_t RF69::setPromiscuousMode(bool promiscuous) { + int16_t state = ERR_NONE; + + if (_promiscuous == promiscuous) { + return(state); + } + + if (promiscuous == true) { + // disable preamble and sync word filtering and insertion + state = disableSyncWordFiltering(); + if (state != ERR_NONE) { + return(state); + } + + // disable CRC filtering + state = setCrcFiltering(false); + } else { + // enable preamble and sync word filtering and insertion + state = enableSyncWordFiltering(); + if (state != ERR_NONE) { + return(state); + } + + // enable CRC filtering + state = setCrcFiltering(true); + } + + return(state); +} + int16_t RF69::config() { int16_t state = ERR_NONE; diff --git a/src/modules/RF69.h b/src/modules/RF69.h index 9820d539..5318b7e8 100644 --- a/src/modules/RF69.h +++ b/src/modules/RF69.h @@ -10,6 +10,10 @@ #define RF69_CRYSTAL_FREQ 32.0 #define RF69_DIV_EXPONENT 19 #define RF69_MAX_PACKET_LENGTH 64 +#define RF69_MAX_PREAMBLE_LENGTH 4 +#define RF69_MAX_SYNC_WORD_LENGTH 8 +#define RF69_DEFAULT_SYNC_WORD_LENGTH 2 +#define RF69_DEFAULT_SYNC_WORD { 0x2D, 0x01 } // RF69 register map #define RF69_REG_FIFO 0x00 @@ -646,7 +650,7 @@ class RF69: public PhysicalLayer { int16_t setOutputPower(int8_t power); /*! - \brief Sets sync word. Up to 8 bytes can be set as snyc word. + \brief Sets sync word. Up to 8 bytes can be set as sync word. \param syncWord Pointer to the array of sync word bytes. @@ -724,6 +728,40 @@ class RF69: public PhysicalLayer { */ int16_t variablePacketLengthMode(uint8_t maxLen = RF69_MAX_PACKET_LENGTH); + /*! + \brief Enable sync word filtering and generation. + + \param numBits Sync word length in bits. + + \returns \ref status_codes + */ + int16_t enableSyncWordFiltering(uint8_t maxErrBits = 0); + + /*! + \brief Disable preamble and sync word filtering and generation. + + \returns \ref status_codes + */ + int16_t disableSyncWordFiltering(); + + /*! + \brief Enable CRC filtering and generation. + + \param crcOn Set or unset promiscuous mode. + + \returns \ref status_codes + */ + int16_t setCrcFiltering(bool crcOn = true); + + /*! + \brief Set modem in "sniff" mode: no packet filtering (e.g., no preamble, sync word, address, CRC). + + \param promiscuous Set or unset promiscuous mode. + + \returns \ref status_codes + */ + int16_t setPromiscuousMode(bool promiscuous = true); + protected: Module* _mod; @@ -735,6 +773,10 @@ class RF69: public PhysicalLayer { bool _packetLengthQueried; uint8_t _packetLengthConfig; + bool _promiscuous; + + uint8_t _syncWordLength; + int16_t config(); int16_t directMode(); diff --git a/src/modules/SX1261.cpp b/src/modules/SX1261.cpp new file mode 100644 index 00000000..06e37d73 --- /dev/null +++ b/src/modules/SX1261.cpp @@ -0,0 +1,50 @@ +#include "SX1261.h" + +SX1261::SX1261(Module* mod) + : SX1262(mod) { + +} + +int16_t SX1261::setOutputPower(int8_t power) { + // check allowed power range + if (!((power >= -17) && (power <= 14))) { + return(ERR_INVALID_OUTPUT_POWER); + } + + // get current OCP configuration + uint8_t ocp = 0; + int16_t state = readRegister(SX126X_REG_OCP_CONFIGURATION, &ocp, 1); + if (state != ERR_NONE) { + return(state); + } + + state = setOptimalLowPowerPaConfig(&power); + if (state != ERR_NONE) { + return(state); + } + + // set output power + // TODO power ramp time configuration + state = SX126x::setTxParams(power); + if (state != ERR_NONE) { + return(state); + } + + // restore OCP configuration + return writeRegister(SX126X_REG_OCP_CONFIGURATION, &ocp, 1); +} + +int16_t SX1261::setOptimalLowPowerPaConfig(int8_t* inOutPower) +{ + int16_t state; + if (*inOutPower > 10) { + state = SX126x::setPaConfig(0x04, SX126X_PA_CONFIG_SX1261, 0x00); + } + else { + state = SX126x::setPaConfig(0x01, SX126X_PA_CONFIG_SX1261, 0x00); + // changing the PaConfig means output power is now scaled so we get 3 dB less than requested. + // see datasheet table 13-21 and comments in setOptimalHiPowerPaConfig. + *inOutPower -= 3; + } + return state; +} diff --git a/src/modules/SX1261.h b/src/modules/SX1261.h index cb8a5758..22c7cadd 100644 --- a/src/modules/SX1261.h +++ b/src/modules/SX1261.h @@ -8,9 +8,33 @@ //SX126X_CMD_SET_PA_CONFIG #define SX126X_PA_CONFIG_SX1261 0x01 -#define SX126X_PA_CONFIG_SX1262 0x00 -// TODO: implement SX1261 class -using SX1261 = SX1262; +/*! + \class SX1261 + + \brief Derived class for %SX1261 modules. +*/ +class SX1261 : public SX1262 { + public: + /*! + \brief Default constructor. + + \param mod Instance of Module that will be used to communicate with the radio. + */ + SX1261(Module* mod); + + /*! + \brief Sets output power. Allowed values are in range from -17 to 14 dBm. + + \param power Output power to be set in dBm. + + \returns \ref status_codes + */ + int16_t setOutputPower(int8_t power); + + private: + int16_t setOptimalLowPowerPaConfig(int8_t* inOutPower); +}; + #endif diff --git a/src/modules/SX1262.cpp b/src/modules/SX1262.cpp index 0bcce47d..345c7db6 100644 --- a/src/modules/SX1262.cpp +++ b/src/modules/SX1262.cpp @@ -22,6 +22,11 @@ int16_t SX1262::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint16_t syn return(state); } + state = SX126x::fixPaClamping(); + if (state != ERR_NONE) { + return state; + } + return(state); } @@ -43,6 +48,11 @@ int16_t SX1262::beginFSK(float freq, float br, float freqDev, float rxBw, int8_t return(state); } + state = SX126x::fixPaClamping(); + if (state != ERR_NONE) { + return state; + } + return(state); } @@ -85,28 +95,32 @@ int16_t SX1262::setFrequency(float freq, bool calibrate) { int16_t SX1262::setOutputPower(int8_t power) { // check allowed power range - if(!((power >= -17) && (power <= 22))) { + if (!((power >= -17) && (power <= 22))) { return(ERR_INVALID_OUTPUT_POWER); } // get current OCP configuration uint8_t ocp = 0; int16_t state = readRegister(SX126X_REG_OCP_CONFIGURATION, &ocp, 1); - if(state != ERR_NONE) { + if (state != ERR_NONE) { return(state); } - // enable high power PA for output power higher than 14 dBm - if(power > 13) { - SX126x::setPaConfig(0x04, SX126X_PA_CONFIG_SX1262); - } else { - SX126x::setPaConfig(0x04, SX126X_PA_CONFIG_SX1261); + // this function sets the optimal PA settings + // and adjusts power based on the PA settings chosen + // so that output power matches requested power. + state = SX126x::setOptimalHiPowerPaConfig(&power); + if (state != ERR_NONE) { + return(state); } // set output power // TODO power ramp time configuration - SX126x::setTxParams(power); + state = SX126x::setTxParams(power); + if (state != ERR_NONE) { + return(state); + } // restore OCP configuration - return(writeRegister(SX126X_REG_OCP_CONFIGURATION, &ocp, 1)); + return writeRegister(SX126X_REG_OCP_CONFIGURATION, &ocp, 1); } diff --git a/src/modules/SX1262.h b/src/modules/SX1262.h index 88a49576..38180464 100644 --- a/src/modules/SX1262.h +++ b/src/modules/SX1262.h @@ -5,10 +5,6 @@ #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 @@ -94,8 +90,6 @@ class SX1262: public SX126x { int16_t setOutputPower(int8_t power); private: - - }; #endif diff --git a/src/modules/SX1268.cpp b/src/modules/SX1268.cpp index c0b01a80..44a671a7 100644 --- a/src/modules/SX1268.cpp +++ b/src/modules/SX1268.cpp @@ -22,6 +22,11 @@ int16_t SX1268::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint16_t syn return(state); } + state = SX126x::fixPaClamping(); + if (state != ERR_NONE) { + return state; + } + return(state); } int16_t SX1268::beginFSK(float freq, float br, float freqDev, float rxBw, int8_t power, float currentLimit, uint16_t preambleLength, float dataShaping) { @@ -42,6 +47,11 @@ int16_t SX1268::beginFSK(float freq, float br, float freqDev, float rxBw, int8_t return(state); } + state = SX126x::fixPaClamping(); + if (state != ERR_NONE) { + return state; + } + return(state); } @@ -89,13 +99,19 @@ int16_t SX1268::setOutputPower(int8_t power) { return(state); } - // enable high power PA - SX126x::setPaConfig(0x04, SX126X_PA_CONFIG_SX1268); + // enable optimal PA - this changes the value of power. + state = SX126x::setOptimalHiPowerPaConfig(&power); + if (state != ERR_NONE) { + return(state); + } // set output power // TODO power ramp time configuration - SX126x::setTxParams(power); + state = SX126x::setTxParams(power); + if (state != ERR_NONE) { + return(state); + } // restore OCP configuration - return(writeRegister(SX126X_REG_OCP_CONFIGURATION, &ocp, 1)); + return writeRegister(SX126X_REG_OCP_CONFIGURATION, &ocp, 1); } diff --git a/src/modules/SX126x.cpp b/src/modules/SX126x.cpp index e47ad31c..5603de76 100644 --- a/src/modules/SX126x.cpp +++ b/src/modules/SX126x.cpp @@ -1036,18 +1036,8 @@ int16_t SX126x::setPaConfig(uint8_t paDutyCycle, uint8_t deviceSel, uint8_t hpMa } int16_t SX126x::writeRegister(uint16_t addr, uint8_t* data, uint8_t numBytes) { - #ifdef STATIC_ONLY - uint8_t dat[STATIC_ARRAY_SIZE + 2]; - #else - uint8_t* dat = new uint8_t[2 + numBytes]; - #endif - dat[0] = (uint8_t)((addr >> 8) & 0xFF); - dat[1] = (uint8_t)(addr & 0xFF); - memcpy(dat + 2, data, numBytes); - int16_t state = SPIwriteCommand(SX126X_CMD_WRITE_REGISTER, dat, 2 + numBytes); - #ifndef STATIC_ONLY - delete[] dat; - #endif + uint8_t cmd[] = { SX126X_CMD_WRITE_REGISTER, (uint8_t)((addr >> 8) & 0xFF), (uint8_t)(addr & 0xFF) }; + int16_t state = SPIwriteCommand(cmd, 3, data, numBytes); return(state); } @@ -1057,34 +1047,15 @@ int16_t SX126x::readRegister(uint16_t addr, uint8_t* data, uint8_t numBytes) { } int16_t SX126x::writeBuffer(uint8_t* data, uint8_t numBytes, uint8_t offset) { - #ifdef STATIC_ONLY - uint8_t dat[STATIC_ARRAY_SIZE + 1]; - #else - uint8_t* dat = new uint8_t[1 + numBytes]; - #endif - dat[0] = offset; - memcpy(dat + 1, data, numBytes); - int16_t state = SPIwriteCommand(SX126X_CMD_WRITE_BUFFER, dat, 1 + numBytes); - #ifndef STATIC_ONLY - delete[] dat; - #endif + uint8_t cmd[] = { SX126X_CMD_WRITE_BUFFER, offset }; + int16_t state = SPIwriteCommand(cmd, 2, data, numBytes); + return(state); } int16_t SX126x::readBuffer(uint8_t* data, uint8_t numBytes) { - // offset will be always set to 0 (one extra NOP is sent) - #ifdef STATIC_ONLY - uint8_t dat[STATIC_ARRAY_SIZE + 1]; - #else - uint8_t* dat = new uint8_t[1 + numBytes]; - #endif - dat[0] = SX126X_CMD_NOP; - memcpy(dat + 1, data, numBytes); - int16_t state = SPIreadCommand(SX126X_CMD_READ_BUFFER, dat, 1 + numBytes); - memcpy(data, dat + 1, numBytes); - #ifndef STATIC_ONLY - delete[] dat; - #endif + uint8_t cmd[] = { SX126X_CMD_READ_BUFFER, SX126X_CMD_NOP }; + int16_t state = SPIreadCommand(cmd, 2, data, numBytes); return(state); } @@ -1127,6 +1098,32 @@ int16_t SX126x::setTxParams(uint8_t power, uint8_t rampTime) { return(SPIwriteCommand(SX126X_CMD_SET_TX_PARAMS, data, 2)); } +// set PA config for optimal consumption as described in section 13-21 of the datasheet. +int16_t SX126x::setOptimalHiPowerPaConfig(int8_t * inOutPower) +{ + // the final column of Table 13-21 suggests that the value passed in SetTxParams + // is actually scaled depending on the parameters of setPaConfig. + // Testing confirms this is approximately right + int16_t state; + if (*inOutPower >= 21) { + state = SX126x::setPaConfig(0x04, SX126X_PA_CONFIG_SX1262_8, SX126X_PA_CONFIG_HP_MAX/*0x07*/); + } + else if (*inOutPower >= 18) { + state = SX126x::setPaConfig(0x03, SX126X_PA_CONFIG_SX1262_8, 0x05); + // datasheet instructs request 22 dBm for 20 dBm actual output power + *inOutPower += 2; + } else if (*inOutPower >= 15) { + state = SX126x::setPaConfig(0x02, SX126X_PA_CONFIG_SX1262_8, 0x03); + // datasheet instructs request 22 dBm for 17 dBm actual output power + *inOutPower += 5; + } else { + state = SX126x::setPaConfig(0x02, SX126X_PA_CONFIG_SX1262_8, 0x02); + // datasheet instructs request 22 dBm for 14 dBm actual output power. + *inOutPower += 8; + } + return state; +} + int16_t 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) { @@ -1202,6 +1199,16 @@ int16_t SX126x::setFrequencyRaw(float freq) { return(ERR_NONE); } +int16_t SX126x::fixPaClamping() { + uint8_t clampConfig; + uint16_t state = readRegister(SX126X_REG_TX_CLAMP_CONFIG, &clampConfig, 1); + if (state != ERR_NONE) { + return state; + } + clampConfig |= 0x1E; + return writeRegister(SX126X_REG_TX_CLAMP_CONFIG, &clampConfig, 1); +} + int16_t SX126x::config(uint8_t modem) { // set regulator mode uint8_t data[7]; @@ -1265,14 +1272,20 @@ int16_t SX126x::config(uint8_t modem) { return(ERR_NONE); } +int16_t SX126x::SPIwriteCommand(uint8_t* cmd, uint8_t cmdLen, uint8_t* data, uint8_t numBytes, bool waitForBusy) { + return(SX126x::SPItransfer(cmd, cmdLen, true, data, NULL, numBytes, waitForBusy)); +} + int16_t SX126x::SPIwriteCommand(uint8_t cmd, uint8_t* data, uint8_t numBytes, bool waitForBusy) { - uint8_t cmdBuffer[] = {cmd}; - return(SX126x::SPItransfer(cmdBuffer, 1, true, data, NULL, numBytes, waitForBusy)); + return(SX126x::SPItransfer(&cmd, 1, true, data, NULL, numBytes, waitForBusy)); +} + +int16_t SX126x::SPIreadCommand(uint8_t* cmd, uint8_t cmdLen, uint8_t* data, uint8_t numBytes, bool waitForBusy) { + return(SX126x::SPItransfer(cmd, cmdLen, false, NULL, data, numBytes, waitForBusy)); } int16_t SX126x::SPIreadCommand(uint8_t cmd, uint8_t* data, uint8_t numBytes, bool waitForBusy) { - uint8_t cmdBuffer[] = {cmd}; - return(SX126x::SPItransfer(cmdBuffer, 1, false, NULL, data, numBytes, waitForBusy)); + return(SX126x::SPItransfer(&cmd, 1, false, NULL, data, numBytes, waitForBusy)); } int16_t SX126x::SPItransfer(uint8_t* cmd, uint8_t cmdLen, bool write, uint8_t* dataOut, uint8_t* dataIn, uint8_t numBytes, bool waitForBusy, uint32_t timeout) { @@ -1284,6 +1297,9 @@ int16_t SX126x::SPItransfer(uint8_t* cmd, uint8_t cmdLen, bool write, uint8_t* d uint8_t debugBuff[256]; #endif + // pull NSS low + digitalWrite(_mod->getCs(), LOW); + // ensure BUSY is low (state meachine ready) RADIOLIB_VERBOSE_PRINTLN(F("Wait for BUSY ... ")); uint32_t start = millis(); @@ -1294,7 +1310,6 @@ int16_t SX126x::SPItransfer(uint8_t* cmd, uint8_t cmdLen, bool write, uint8_t* d } // start transfer - digitalWrite(_mod->getCs(), LOW); spi->beginTransaction(spiSettings); // send command byte(s) diff --git a/src/modules/SX126x.h b/src/modules/SX126x.h index 641d6445..d82721c5 100644 --- a/src/modules/SX126x.h +++ b/src/modules/SX126x.h @@ -92,6 +92,7 @@ #define SX126X_REG_OCP_CONFIGURATION 0x08E7 #define SX126X_REG_XTA_TRIM 0x0911 #define SX126X_REG_XTB_TRIM 0x0912 +#define SX126X_REG_TX_CLAMP_CONFIG 0x08D8 //Datasheet 15.2 // SX126X SPI command variables @@ -152,6 +153,7 @@ //SX126X_CMD_SET_PA_CONFIG #define SX126X_PA_CONFIG_HP_MAX 0x07 #define SX126X_PA_CONFIG_PA_LUT 0x01 +#define SX126X_PA_CONFIG_SX1262_8 0x00 //SX126X_CMD_SET_RX_TX_FALLBACK_MODE #define SX126X_RX_TX_FALLBACK_MODE_FS 0x40 // 7 0 after Rx/Tx go to: FS mode @@ -739,6 +741,7 @@ class SX126x: public PhysicalLayer { int16_t calibrateImage(uint8_t* data); uint8_t getPacketType(); int16_t setTxParams(uint8_t power, uint8_t rampTime = SX126X_PA_RAMP_200U); + int16_t setOptimalHiPowerPaConfig(int8_t* inOutPower); int16_t setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, uint8_t ldro = 0xFF); int16_t setModulationParamsFSK(uint32_t br, uint8_t pulseShape, uint8_t rxBw, uint32_t freqDev); int16_t setPacketParams(uint16_t preambleLength, uint8_t crcType, uint8_t payloadLength = 0xFF, uint8_t headerType = SX126X_LORA_HEADER_EXPLICIT, uint8_t invertIQ = SX126X_LORA_IQ_STANDARD); @@ -751,6 +754,10 @@ class SX126x: public PhysicalLayer { int16_t setFrequencyRaw(float freq); + /*! + \brief Fixes overly eager PA clamping on SX1262 / SX1268, as described in section 15.2 of the datasheet + */ + int16_t fixPaClamping(); private: Module* _mod; @@ -767,9 +774,13 @@ class SX126x: public PhysicalLayer { int16_t config(uint8_t modem); + + // common low-level SPI interface int16_t SPIwriteCommand(uint8_t cmd, uint8_t* data, uint8_t numBytes, bool waitForBusy = true); + int16_t SPIwriteCommand(uint8_t* cmd, uint8_t cmdLen, uint8_t* data, uint8_t numBytes, bool waitForBusy = true); int16_t SPIreadCommand(uint8_t cmd, uint8_t* data, uint8_t numBytes, bool waitForBusy = true); + int16_t SPIreadCommand(uint8_t* cmd, uint8_t cmdLen, uint8_t* data, uint8_t numBytes, bool waitForBusy = true); int16_t SPItransfer(uint8_t* cmd, uint8_t cmdLen, bool write, uint8_t* dataOut, uint8_t* dataIn, uint8_t numBytes, bool waitForBusy, uint32_t timeout = 5000); }; diff --git a/src/protocols/RTTY.h b/src/protocols/RTTY.h index 06aeac55..60270429 100644 --- a/src/protocols/RTTY.h +++ b/src/protocols/RTTY.h @@ -147,7 +147,7 @@ class RTTYClient { uint8_t _encoding; uint32_t _base; uint32_t _shift; - uint16_t _bitDuration; + uint32_t _bitDuration; uint8_t _dataBits; uint8_t _stopBits;