Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
53893a6fe1
13 changed files with 472 additions and 79 deletions
|
@ -90,6 +90,10 @@ setDataShapingOOK KEYWORD2
|
|||
setCRC KEYWORD2
|
||||
variablePacketLengthMode KEYWORD2
|
||||
fixedPacketLengthMode KEYWORD2
|
||||
setCrcFiltering KEYWORD2
|
||||
enableSyncWordFiltering KEYWORD2
|
||||
disableSyncWordFiltering KEYWORD2
|
||||
setPromiscuous KEYWORD2
|
||||
|
||||
# RF69-specific
|
||||
setAESKey KEYWORD2
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
50
src/modules/SX1261.cpp
Normal file
50
src/modules/SX1261.cpp
Normal file
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue