diff --git a/src/modules/SX126x/SX126x.cpp b/src/modules/SX126x/SX126x.cpp index fe2b09ea..e34f1bc6 100644 --- a/src/modules/SX126x/SX126x.cpp +++ b/src/modules/SX126x/SX126x.cpp @@ -251,11 +251,18 @@ int16_t SX126x::receive(uint8_t* data, size_t len) { uint32_t start = micros(); while(!digitalRead(_mod->getInt0())) { if(micros() - start > timeout) { + fixImplicitTimeout(); clearIrqStatus(); return(ERR_RX_TIMEOUT); } } + // timeout fix is recommended after any reception with active timeout + state = fixImplicitTimeout(); + if(state != ERR_NONE) { + return(state); + } + // read the received data return(readData(data, len)); } @@ -364,6 +371,11 @@ int16_t SX126x::startTransmit(uint8_t* data, size_t len, uint8_t addr) { return(ERR_PACKET_TOO_LONG); } + // maximum packet length is decreased by 1 when address filtering is active + if((_addrComp != SX126X_GFSK_ADDRESS_FILT_OFF) && (len > SX126X_MAX_PACKET_LENGTH - 1)) { + return(ERR_PACKET_TOO_LONG); + } + // set packet Length int16_t state = ERR_NONE; uint8_t modem = getPacketType(); @@ -402,6 +414,12 @@ int16_t SX126x::startTransmit(uint8_t* data, size_t len, uint8_t addr) { return(state); } + // fix sensitivity + state = fixSensitivity(); + if(state != ERR_NONE) { + return(state); + } + // start transmission state = setTx(SX126X_TX_TIMEOUT_NONE); if(state != ERR_NONE) { @@ -968,10 +986,15 @@ uint32_t SX126x::getTimeOnAir(size_t len) { } } -int16_t SX126x::setTCXO(float voltage, uint32_t timeout) { +int16_t SX126x::setTCXO(float voltage, uint32_t delay) { // set mode to standby standby(); + // check SX126X_XOSC_START_ERR flag and clear it + if(getDeviceErrors() & SX126X_XOSC_START_ERR) { + clearDeviceErrors(); + } + // check alowed voltage values uint8_t data[4]; if(abs(voltage - 1.6) <= 0.001) { @@ -994,11 +1017,11 @@ int16_t SX126x::setTCXO(float voltage, uint32_t timeout) { return(ERR_INVALID_TCXO_VOLTAGE); } - // calculate timeout - uint32_t timeoutValue = (float)timeout / 15.625; - data[1] = (uint8_t)((timeoutValue >> 16) & 0xFF); - data[2] = (uint8_t)((timeoutValue >> 8) & 0xFF); - data[3] = (uint8_t)(timeoutValue & 0xFF); + // calculate delay + uint32_t delayValue = (float)delay / 15.625; + data[1] = (uint8_t)((delayValue >> 16) & 0xFF); + data[2] = (uint8_t)((delayValue >> 8) & 0xFF); + data[3] = (uint8_t)(delayValue & 0xFF); // enable TCXO control on DIO3 SPIwriteCommand(SX126X_CMD_SET_DIO3_AS_TCXO_CTRL, data, 4); @@ -1098,12 +1121,10 @@ 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) -{ +int16_t SX126x::setOptimalHiPowerPaConfig(int8_t * inOutPower) { + // set PA config for optimal consumption as described in section 13-21 of SX1268 datasheet v1.1 // 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 + // is actually scaled depending on the parameters of setPaConfig int16_t state; if (*inOutPower >= 21) { state = SX126x::setPaConfig(0x04, SX126X_PA_CONFIG_SX1262_8, SX126X_PA_CONFIG_HP_MAX/*0x07*/); @@ -1152,6 +1173,7 @@ int16_t SX126x::setModulationParamsFSK(uint32_t br, uint8_t pulseShape, uint8_t } int16_t SX126x::setPacketParams(uint16_t preambleLength, uint8_t crcType, uint8_t payloadLength, uint8_t headerType, uint8_t invertIQ) { + fixInvertedIQ(invertIQ); uint8_t data[6] = {(uint8_t)((preambleLength >> 8) & 0xFF), (uint8_t)(preambleLength & 0xFF), headerType, payloadLength, crcType, invertIQ}; return(SPIwriteCommand(SX126X_CMD_SET_PACKET_PARAMS, data, 6)); } @@ -1188,8 +1210,8 @@ uint16_t SX126x::getDeviceErrors() { } int16_t SX126x::clearDeviceErrors() { - uint8_t data[1] = {SX126X_CMD_NOP}; - return(SPIwriteCommand(SX126X_CMD_CLEAR_DEVICE_ERRORS, data, 1)); + uint8_t data[2] = {SX126X_CMD_NOP, SX126X_CMD_NOP}; + return(SPIwriteCommand(SX126X_CMD_CLEAR_DEVICE_ERRORS, data, 2)); } int16_t SX126x::setFrequencyRaw(float freq) { @@ -1199,14 +1221,85 @@ int16_t SX126x::setFrequencyRaw(float freq) { return(ERR_NONE); } +int16_t SX126x::fixSensitivity() { + // fix receiver sensitivity for 500 kHz LoRa + // see SX1262/SX1268 datasheet, chapter 15 Known Limitations, section 15.1 for details + + // read current sensitivity configuration + uint8_t sensitivityConfig = 0; + int16_t state = readRegister(SX126X_REG_SENSITIVITY_CONFIG, &sensitivityConfig, 1); + if(state != ERR_NONE) { + return(state); + } + + // fix the value for LoRa with 500 kHz bandwidth + if((getPacketType() == SX126X_PACKET_TYPE_LORA) && (abs(_bwKhz - 500.0) <= 0.001)) { + sensitivityConfig &= 0xFB; + } else { + sensitivityConfig |= 0x04; + } + return(writeRegister(SX126X_REG_SENSITIVITY_CONFIG, &sensitivityConfig, 1)); +} + int16_t SX126x::fixPaClamping() { - uint8_t clampConfig; - uint16_t state = readRegister(SX126X_REG_TX_CLAMP_CONFIG, &clampConfig, 1); + // fixes overly eager PA clamping + // see SX1262/SX1268 datasheet, chapter 15 Known Limitations, section 15.2 for details + + // read current clamping configuration + uint8_t clampConfig = 0; + int16_t state = readRegister(SX126X_REG_TX_CLAMP_CONFIG, &clampConfig, 1); if (state != ERR_NONE) { return state; } + + // update with the new value clampConfig |= 0x1E; - return writeRegister(SX126X_REG_TX_CLAMP_CONFIG, &clampConfig, 1); + return(writeRegister(SX126X_REG_TX_CLAMP_CONFIG, &clampConfig, 1)); +} + +int16_t SX126x::fixImplicitTimeout() { + // fixes timeout in implicit header mode + // see SX1262/SX1268 datasheet, chapter 15 Known Limitations, section 15.3 for details + + // stop RTC counter + uint8_t rtcStop = 0x00; + int16_t state = writeRegister(SX126X_REG_RTC_STOP, &rtcStop, 1); + if(state != ERR_NONE) { + return(state); + } + + // read currently active event + uint8_t rtcEvent = 0; + state = readRegister(SX126X_REG_RTC_EVENT, &rtcEvent, 1); + if(state != ERR_NONE) { + return(state); + } + + // clear events + rtcEvent |= 0x02; + return(writeRegister(SX126X_REG_RTC_EVENT, &rtcEvent, 1)); +} + +int16_t SX126x::fixInvertedIQ(uint8_t iqConfig) { + // fixes IQ configuration for inverted IQ + // see SX1262/SX1268 datasheet, chapter 15 Known Limitations, section 15.4 for details + + // read current IQ configuration + uint8_t iqConfigCurrent = 0; + int16_t state = readRegister(SX126X_REG_IQ_CONFIG, &iqConfigCurrent, 1); + if(state != ERR_NONE) { + return(state); + } + + // set correct IQ configuration + if(iqConfig == SX126X_LORA_IQ_STANDARD) { + iqConfigCurrent &= 0xFB; + } else { + iqConfigCurrent |= 0x04; + } + + // update with the new value + return(writeRegister(SX126X_REG_IQ_CONFIG, &iqConfigCurrent, 1)); } int16_t SX126x::config(uint8_t modem) { diff --git a/src/modules/SX126x/SX126x.h b/src/modules/SX126x/SX126x.h index 4d4efaf7..99668cfb 100644 --- a/src/modules/SX126x/SX126x.h +++ b/src/modules/SX126x/SX126x.h @@ -92,7 +92,16 @@ #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 + +// undocumented registers +#define SX126X_REG_SENSITIVITY_CONFIG 0x0889 // SX1268 datasheet v1.1, section 15.1 +#define SX126X_REG_TX_CLAMP_CONFIG 0x08D8 // SX1268 datasheet v1.1, section 15.2 +#define SX126X_REG_RTC_STOP 0x0920 // SX1268 datasheet v1.1, section 15.3 +#define SX126X_REG_RTC_EVENT 0x0944 // SX1268 datasheet v1.1, section 15.3 +#define SX126X_REG_IQ_CONFIG 0x0736 // SX1268 datasheet v1.1, section 15.4 +#define SX126X_REG_RX_GAIN_RETENTION_0 0x029F // SX1268 datasheet v1.1, section 9.6 +#define SX126X_REG_RX_GAIN_RETENTION_1 0x02A0 // SX1268 datasheet v1.1, section 9.6 +#define SX126X_REG_RX_GAIN_RETENTION_2 0x02A1 // SX1268 datasheet v1.1, section 9.6 // SX126X SPI command variables @@ -676,7 +685,7 @@ class SX126x: public PhysicalLayer { \param TCXO timeout in us. Defaults to 5000 us. */ - int16_t setTCXO(float voltage, uint32_t timeout = 5000); + int16_t setTCXO(float voltage, uint32_t delay = 5000); /*! \brief Set DIO2 to function as RF switch (default in Semtech example designs). @@ -743,7 +752,6 @@ 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); @@ -755,11 +763,13 @@ class SX126x: public PhysicalLayer { int16_t clearDeviceErrors(); int16_t setFrequencyRaw(float freq); + int16_t setOptimalHiPowerPaConfig(int8_t* inOutPower); - /*! - \brief Fixes overly eager PA clamping on SX1262 / SX1268, as described in section 15.2 of the datasheet - */ + // fixes to errata + int16_t fixSensitivity(); int16_t fixPaClamping(); + int16_t fixImplicitTimeout(); + int16_t fixInvertedIQ(uint8_t iqConfig); #ifndef RADIOLIB_GODMODE private: