2023 lines
64 KiB
C++
2023 lines
64 KiB
C++
#include "SX126x.h"
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#if !defined(RADIOLIB_EXCLUDE_SX126X)
|
|
|
|
SX126x::SX126x(Module* mod) : PhysicalLayer(RADIOLIB_SX126X_FREQUENCY_STEP_SIZE, RADIOLIB_SX126X_MAX_PACKET_LENGTH) {
|
|
_mod = mod;
|
|
this->XTAL = false;
|
|
}
|
|
|
|
Module* SX126x::getMod() {
|
|
return(_mod);
|
|
}
|
|
|
|
int16_t SX126x::begin(uint8_t cr, uint8_t syncWord, uint16_t preambleLength, float tcxoVoltage, bool useRegulatorLDO) {
|
|
// set module properties
|
|
_mod->init();
|
|
_mod->hal->pinMode(_mod->getIrq(), _mod->hal->GpioModeInput);
|
|
_mod->hal->pinMode(_mod->getGpio(), _mod->hal->GpioModeInput);
|
|
_mod->SPIreadCommand = RADIOLIB_SX126X_CMD_READ_REGISTER;
|
|
_mod->SPIwriteCommand = RADIOLIB_SX126X_CMD_WRITE_REGISTER;
|
|
_mod->SPInopCommand = RADIOLIB_SX126X_CMD_NOP;
|
|
_mod->SPIstatusCommand = RADIOLIB_SX126X_CMD_GET_STATUS;
|
|
_mod->SPIstreamType = true;
|
|
_mod->SPIparseStatusCb = SPIparseStatus;
|
|
|
|
// try to find the SX126x chip
|
|
if(!SX126x::findChip(_chipType)) {
|
|
RADIOLIB_DEBUG_PRINTLN("No SX126x found!");
|
|
_mod->term();
|
|
return(RADIOLIB_ERR_CHIP_NOT_FOUND);
|
|
}
|
|
RADIOLIB_DEBUG_PRINTLN("M\tSX126x");
|
|
|
|
// BW in kHz and SF are required in order to calculate LDRO for setModulationParams
|
|
// set the defaults, this will get overwritten later anyway
|
|
_bwKhz = 500.0;
|
|
_sf = 9;
|
|
|
|
// initialize configuration variables (will be overwritten during public settings configuration)
|
|
_bw = RADIOLIB_SX126X_LORA_BW_500_0; // initialized to 500 kHz, since lower valeus will interfere with LLCC68
|
|
_cr = RADIOLIB_SX126X_LORA_CR_4_7;
|
|
_ldro = 0x00;
|
|
_crcType = RADIOLIB_SX126X_LORA_CRC_ON;
|
|
_preambleLength = preambleLength;
|
|
_tcxoDelay = 0;
|
|
_headerType = RADIOLIB_SX126X_LORA_HEADER_EXPLICIT;
|
|
_implicitLen = 0xFF;
|
|
|
|
// reset the module and verify startup
|
|
int16_t state = reset();
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// set mode to standby
|
|
state = standby();
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// set TCXO control, if requested
|
|
if(!this->XTAL && tcxoVoltage > 0.0) {
|
|
state = setTCXO(tcxoVoltage);
|
|
RADIOLIB_ASSERT(state);
|
|
}
|
|
|
|
// configure settings not accessible by API
|
|
state = config(RADIOLIB_SX126X_PACKET_TYPE_LORA);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// configure publicly accessible settings
|
|
state = setCodingRate(cr);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
state = setSyncWord(syncWord);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
state = setPreambleLength(preambleLength);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
if (useRegulatorLDO) {
|
|
state = setRegulatorLDO();
|
|
} else {
|
|
state = setRegulatorDCDC();
|
|
}
|
|
|
|
// set publicly accessible settings that are not a part of begin method
|
|
state = setCurrentLimit(60.0);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
state = setDio2AsRfSwitch(true);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
state = setCRC(2);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
return(state);
|
|
}
|
|
|
|
int16_t SX126x::beginFSK(float br, float freqDev, float rxBw, uint16_t preambleLength, float tcxoVoltage, bool useRegulatorLDO) {
|
|
// set module properties
|
|
_mod->init();
|
|
_mod->hal->pinMode(_mod->getIrq(), _mod->hal->GpioModeInput);
|
|
_mod->hal->pinMode(_mod->getGpio(), _mod->hal->GpioModeInput);
|
|
_mod->SPIreadCommand = RADIOLIB_SX126X_CMD_READ_REGISTER;
|
|
_mod->SPIwriteCommand = RADIOLIB_SX126X_CMD_WRITE_REGISTER;
|
|
_mod->SPInopCommand = RADIOLIB_SX126X_CMD_NOP;
|
|
_mod->SPIstatusCommand = RADIOLIB_SX126X_CMD_GET_STATUS;
|
|
_mod->SPIstreamType = true;
|
|
_mod->SPIparseStatusCb = SPIparseStatus;
|
|
|
|
// try to find the SX126x chip
|
|
if(!SX126x::findChip(_chipType)) {
|
|
RADIOLIB_DEBUG_PRINTLN("No SX126x found!");
|
|
_mod->term();
|
|
return(RADIOLIB_ERR_CHIP_NOT_FOUND);
|
|
}
|
|
RADIOLIB_DEBUG_PRINTLN("M\tSX126x");
|
|
|
|
// initialize configuration variables (will be overwritten during public settings configuration)
|
|
_br = 21333; // 48.0 kbps
|
|
_freqDev = 52428; // 50.0 kHz
|
|
_rxBw = RADIOLIB_SX126X_GFSK_RX_BW_156_2;
|
|
_rxBwKhz = 156.2;
|
|
_pulseShape = RADIOLIB_SX126X_GFSK_FILTER_GAUSS_0_5;
|
|
_crcTypeFSK = RADIOLIB_SX126X_GFSK_CRC_2_BYTE_INV; // CCIT CRC configuration
|
|
_preambleLengthFSK = preambleLength;
|
|
_addrComp = RADIOLIB_SX126X_GFSK_ADDRESS_FILT_OFF;
|
|
|
|
// reset the module and verify startup
|
|
int16_t state = reset();
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// set mode to standby
|
|
state = standby();
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// set TCXO control, if requested
|
|
if(!this->XTAL && tcxoVoltage > 0.0) {
|
|
state = setTCXO(tcxoVoltage);
|
|
RADIOLIB_ASSERT(state);
|
|
}
|
|
|
|
// configure settings not accessible by API
|
|
state = config(RADIOLIB_SX126X_PACKET_TYPE_GFSK);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// configure publicly accessible settings
|
|
state = setBitRate(br);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
state = setFrequencyDeviation(freqDev);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
state = setRxBandwidth(rxBw);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
state = setCurrentLimit(60.0);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
state = setPreambleLength(preambleLength);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
if(useRegulatorLDO) {
|
|
state = setRegulatorLDO();
|
|
} else {
|
|
state = setRegulatorDCDC();
|
|
}
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// set publicly accessible settings that are not a part of begin method
|
|
uint8_t sync[] = {0x12, 0xAD};
|
|
state = setSyncWord(sync, 2);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
state = setDataShaping(RADIOLIB_SHAPING_NONE);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
state = setEncoding(RADIOLIB_ENCODING_NRZ);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
state = variablePacketLengthMode(RADIOLIB_SX126X_MAX_PACKET_LENGTH);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
state = setCRC(2);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
state = setDio2AsRfSwitch(true);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
return(state);
|
|
}
|
|
|
|
int16_t SX126x::reset(bool verify) {
|
|
// run the reset sequence
|
|
_mod->hal->pinMode(_mod->getRst(), _mod->hal->GpioModeOutput);
|
|
_mod->hal->digitalWrite(_mod->getRst(), _mod->hal->GpioLevelLow);
|
|
_mod->hal->delay(1);
|
|
_mod->hal->digitalWrite(_mod->getRst(), _mod->hal->GpioLevelHigh);
|
|
|
|
// return immediately when verification is disabled
|
|
if(!verify) {
|
|
return(RADIOLIB_ERR_NONE);
|
|
}
|
|
|
|
// set mode to standby - SX126x often refuses first few commands after reset
|
|
uint32_t start = _mod->hal->millis();
|
|
while(true) {
|
|
// try to set mode to standby
|
|
int16_t state = standby();
|
|
if(state == RADIOLIB_ERR_NONE) {
|
|
// standby command successful
|
|
return(RADIOLIB_ERR_NONE);
|
|
}
|
|
|
|
// standby command failed, check timeout and try again
|
|
if(_mod->hal->millis() - start >= 1000) {
|
|
// timed out, possibly incorrect wiring
|
|
return(state);
|
|
}
|
|
|
|
// wait a bit to not spam the module
|
|
_mod->hal->delay(100);
|
|
}
|
|
}
|
|
|
|
int16_t SX126x::transmit(uint8_t* data, size_t len, uint8_t addr) {
|
|
// set mode to standby
|
|
int16_t state = standby();
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// check packet length
|
|
if(len > RADIOLIB_SX126X_MAX_PACKET_LENGTH) {
|
|
return(RADIOLIB_ERR_PACKET_TOO_LONG);
|
|
}
|
|
|
|
uint32_t timeout = 0;
|
|
|
|
// get currently active modem
|
|
uint8_t modem = getPacketType();
|
|
if(modem == RADIOLIB_SX126X_PACKET_TYPE_LORA) {
|
|
// calculate timeout (150% of expected time-on-air)
|
|
timeout = (getTimeOnAir(len) * 3) / 2;
|
|
|
|
} else if(modem == RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
|
|
// calculate timeout (500% of expected time-on-air)
|
|
timeout = getTimeOnAir(len) * 5;
|
|
|
|
} else {
|
|
return(RADIOLIB_ERR_UNKNOWN);
|
|
}
|
|
|
|
RADIOLIB_DEBUG_PRINTLN("Timeout in %d us", timeout);
|
|
|
|
// start transmission
|
|
state = startTransmit(data, len, addr);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// wait for packet transmission or timeout
|
|
uint32_t start = _mod->hal->micros();
|
|
while(!_mod->hal->digitalRead(_mod->getIrq())) {
|
|
_mod->hal->yield();
|
|
if(_mod->hal->micros() - start > timeout) {
|
|
finishTransmit();
|
|
return(RADIOLIB_ERR_TX_TIMEOUT);
|
|
}
|
|
}
|
|
uint32_t elapsed = _mod->hal->micros() - start;
|
|
|
|
// update data rate
|
|
_dataRate = (len*8.0)/((float)elapsed/1000000.0);
|
|
|
|
return(finishTransmit());
|
|
}
|
|
|
|
int16_t SX126x::receive(uint8_t* data, size_t len) {
|
|
// set mode to standby
|
|
int16_t state = standby();
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
uint32_t timeout = 0;
|
|
|
|
// get currently active modem
|
|
uint8_t modem = getPacketType();
|
|
if(modem == RADIOLIB_SX126X_PACKET_TYPE_LORA) {
|
|
// calculate timeout (100 LoRa symbols, the default for SX127x series)
|
|
float symbolLength = (float)(uint32_t(1) << _sf) / (float)_bwKhz;
|
|
timeout = (uint32_t)(symbolLength * 100.0 * 1000.0);
|
|
} else if(modem == RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
|
|
// calculate timeout (500 % of expected time-one-air)
|
|
size_t maxLen = len;
|
|
if(len == 0) {
|
|
maxLen = 0xFF;
|
|
}
|
|
float brBps = ((float)(RADIOLIB_SX126X_CRYSTAL_FREQ) * 1000000.0 * 32.0) / (float)_br;
|
|
timeout = (uint32_t)(((maxLen * 8.0) / brBps) * 1000000.0 * 5.0);
|
|
|
|
} else {
|
|
return(RADIOLIB_ERR_UNKNOWN);
|
|
}
|
|
|
|
RADIOLIB_DEBUG_PRINTLN("Timeout in %d us", timeout);
|
|
|
|
// start reception
|
|
uint32_t timeoutValue = (uint32_t)((float)timeout / 15.625);
|
|
state = startReceive(timeoutValue);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// wait for packet reception or timeout
|
|
uint32_t start = _mod->hal->micros();
|
|
while(!_mod->hal->digitalRead(_mod->getIrq())) {
|
|
_mod->hal->yield();
|
|
if(_mod->hal->micros() - start > timeout) {
|
|
fixImplicitTimeout();
|
|
clearIrqStatus();
|
|
standby();
|
|
return(RADIOLIB_ERR_RX_TIMEOUT);
|
|
}
|
|
}
|
|
|
|
// fix timeout in implicit LoRa mode
|
|
if(((_headerType == RADIOLIB_SX126X_LORA_HEADER_IMPLICIT) && (getPacketType() == RADIOLIB_SX126X_PACKET_TYPE_LORA))) {
|
|
state = fixImplicitTimeout();
|
|
RADIOLIB_ASSERT(state);
|
|
}
|
|
|
|
// read the received data
|
|
return(readData(data, len));
|
|
}
|
|
|
|
int16_t SX126x::transmitDirect(uint32_t frf) {
|
|
// set RF switch (if present)
|
|
_mod->setRfSwitchState(_tx_mode);
|
|
|
|
// user requested to start transmitting immediately (required for RTTY)
|
|
int16_t state = RADIOLIB_ERR_NONE;
|
|
if(frf != 0) {
|
|
state = setRfFrequency(frf);
|
|
}
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// start transmitting
|
|
uint8_t data[] = {RADIOLIB_SX126X_CMD_NOP};
|
|
return(_mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_TX_CONTINUOUS_WAVE, data, 1));
|
|
}
|
|
|
|
int16_t SX126x::receiveDirect() {
|
|
// set RF switch (if present)
|
|
_mod->setRfSwitchState(Module::MODE_RX);
|
|
|
|
// SX126x is unable to output received data directly
|
|
return(RADIOLIB_ERR_UNKNOWN);
|
|
}
|
|
|
|
int16_t SX126x::directMode() {
|
|
// check modem
|
|
if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
|
|
return(RADIOLIB_ERR_WRONG_MODEM);
|
|
}
|
|
|
|
// set mode to standby
|
|
int16_t state = standby();
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// disable DIO2 RF switch
|
|
state = setDio2AsRfSwitch(false);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// set DIO2 to clock output and DIO3 to data input
|
|
// this is done exclusively by writing magic values to even more magic registers
|
|
state = _mod->SPIsetRegValue(RADIOLIB_SX126X_REG_TX_BITBANG_ENABLE_1, RADIOLIB_SX126X_TX_BITBANG_1_ENABLED, 6, 4);
|
|
RADIOLIB_ASSERT(state);
|
|
state = _mod->SPIsetRegValue(RADIOLIB_SX126X_REG_TX_BITBANG_ENABLE_0, RADIOLIB_SX126X_TX_BITBANG_0_ENABLED, 3, 0);
|
|
RADIOLIB_ASSERT(state);
|
|
state = _mod->SPIsetRegValue(RADIOLIB_SX126X_REG_DIOX_OUT_ENABLE, RADIOLIB_SX126X_DIO3_OUT_DISABLED, 3, 3);
|
|
RADIOLIB_ASSERT(state);
|
|
state = _mod->SPIsetRegValue(RADIOLIB_SX126X_REG_DIOX_IN_ENABLE, RADIOLIB_SX126X_DIO3_IN_ENABLED, 3, 3);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// enable TxDone interrupt
|
|
state = setDioIrqParams(RADIOLIB_SX126X_IRQ_TX_DONE, RADIOLIB_SX126X_IRQ_TX_DONE);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// set preamble length to the maximum to prevent SX126x from exiting Tx mode for a while
|
|
state = setPreambleLength(0xFFFF);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
return(state);
|
|
}
|
|
|
|
int16_t SX126x::packetMode() {
|
|
// set mode to standby
|
|
int16_t state = standby();
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// set preamble length to the default
|
|
state = setPreambleLength(16);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// disable TxDone interrupt
|
|
state = setDioIrqParams(RADIOLIB_SX126X_IRQ_NONE, RADIOLIB_SX126X_IRQ_NONE);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// restore the magic registers
|
|
state = _mod->SPIsetRegValue(RADIOLIB_SX126X_REG_DIOX_IN_ENABLE, RADIOLIB_SX126X_DIO3_IN_DISABLED, 3, 3);
|
|
RADIOLIB_ASSERT(state);
|
|
state = _mod->SPIsetRegValue(RADIOLIB_SX126X_REG_DIOX_OUT_ENABLE, RADIOLIB_SX126X_DIO3_OUT_ENABLED, 3, 3);
|
|
RADIOLIB_ASSERT(state);
|
|
state = _mod->SPIsetRegValue(RADIOLIB_SX126X_REG_TX_BITBANG_ENABLE_0, RADIOLIB_SX126X_TX_BITBANG_0_DISABLED, 3, 0);
|
|
RADIOLIB_ASSERT(state);
|
|
state = _mod->SPIsetRegValue(RADIOLIB_SX126X_REG_TX_BITBANG_ENABLE_1, RADIOLIB_SX126X_TX_BITBANG_1_DISABLED, 6, 4);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// enable DIO2 RF switch
|
|
state = setDio2AsRfSwitch(true);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
return(state);
|
|
}
|
|
|
|
int16_t SX126x::scanChannel(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin) {
|
|
// set mode to CAD
|
|
int state = startChannelScan(symbolNum, detPeak, detMin);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// wait for channel activity detected or timeout
|
|
while(!_mod->hal->digitalRead(_mod->getIrq())) {
|
|
_mod->hal->yield();
|
|
}
|
|
|
|
// check CAD result
|
|
return(getChannelScanResult());
|
|
}
|
|
|
|
int16_t SX126x::sleep(bool retainConfig) {
|
|
// set RF switch (if present)
|
|
_mod->setRfSwitchState(Module::MODE_IDLE);
|
|
|
|
uint8_t sleepMode = RADIOLIB_SX126X_SLEEP_START_WARM | RADIOLIB_SX126X_SLEEP_RTC_OFF;
|
|
if(!retainConfig) {
|
|
sleepMode = RADIOLIB_SX126X_SLEEP_START_COLD | RADIOLIB_SX126X_SLEEP_RTC_OFF;
|
|
}
|
|
int16_t state = _mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_SLEEP, &sleepMode, 1, false, false);
|
|
|
|
// wait for SX126x to safely enter sleep mode
|
|
_mod->hal->delay(1);
|
|
|
|
return(state);
|
|
}
|
|
|
|
int16_t SX126x::standby() {
|
|
return(SX126x::standby(RADIOLIB_SX126X_STANDBY_RC));
|
|
}
|
|
|
|
int16_t SX126x::standby(uint8_t mode) {
|
|
// set RF switch (if present)
|
|
_mod->setRfSwitchState(Module::MODE_IDLE);
|
|
|
|
uint8_t data[] = {mode};
|
|
return(_mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_STANDBY, data, 1));
|
|
}
|
|
|
|
void SX126x::setDio1Action(void (*func)(void)) {
|
|
_mod->hal->attachInterrupt(_mod->hal->pinToInterrupt(_mod->getIrq()), func, _mod->hal->GpioInterruptRising);
|
|
}
|
|
|
|
void SX126x::clearDio1Action() {
|
|
_mod->hal->detachInterrupt(_mod->hal->pinToInterrupt(_mod->getIrq()));
|
|
}
|
|
|
|
int16_t SX126x::startTransmit(uint8_t* data, size_t len, uint8_t addr) {
|
|
// suppress unused variable warning
|
|
(void)addr;
|
|
|
|
// check packet length
|
|
if(len > RADIOLIB_SX126X_MAX_PACKET_LENGTH) {
|
|
return(RADIOLIB_ERR_PACKET_TOO_LONG);
|
|
}
|
|
|
|
// maximum packet length is decreased by 1 when address filtering is active
|
|
if((_addrComp != RADIOLIB_SX126X_GFSK_ADDRESS_FILT_OFF) && (len > RADIOLIB_SX126X_MAX_PACKET_LENGTH - 1)) {
|
|
return(RADIOLIB_ERR_PACKET_TOO_LONG);
|
|
}
|
|
|
|
// set packet Length
|
|
int16_t state = RADIOLIB_ERR_NONE;
|
|
uint8_t modem = getPacketType();
|
|
if(modem == RADIOLIB_SX126X_PACKET_TYPE_LORA) {
|
|
state = setPacketParams(_preambleLength, _crcType, len, _headerType);
|
|
} else if(modem == RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
|
|
state = setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength, _addrComp, _whitening, _packetType, len);
|
|
} else {
|
|
return(RADIOLIB_ERR_UNKNOWN);
|
|
}
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// set DIO mapping
|
|
state = setDioIrqParams(RADIOLIB_SX126X_IRQ_TX_DONE | RADIOLIB_SX126X_IRQ_TIMEOUT, RADIOLIB_SX126X_IRQ_TX_DONE);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// set buffer pointers
|
|
state = setBufferBaseAddress();
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// write packet to buffer
|
|
state = writeBuffer(data, len);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// clear interrupt flags
|
|
state = clearIrqStatus();
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// fix sensitivity
|
|
state = fixSensitivity();
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// set RF switch (if present)
|
|
_mod->setRfSwitchState(_tx_mode);
|
|
|
|
// start transmission
|
|
state = setTx(RADIOLIB_SX126X_TX_TIMEOUT_NONE);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// wait for BUSY to go low (= PA ramp up done)
|
|
while(_mod->hal->digitalRead(_mod->getGpio())) {
|
|
_mod->hal->yield();
|
|
}
|
|
|
|
return(state);
|
|
}
|
|
|
|
int16_t SX126x::finishTransmit() {
|
|
// clear interrupt flags
|
|
clearIrqStatus();
|
|
|
|
// set mode to standby to disable transmitter/RF switch
|
|
return(standby());
|
|
}
|
|
|
|
int16_t SX126x::startReceive(uint32_t timeout, uint16_t irqFlags, uint16_t irqMask, size_t len) {
|
|
(void)len;
|
|
int16_t state = startReceiveCommon(timeout, irqFlags, irqMask);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// set RF switch (if present)
|
|
_mod->setRfSwitchState(Module::MODE_RX);
|
|
|
|
// set mode to receive
|
|
state = setRx(timeout);
|
|
|
|
return(state);
|
|
}
|
|
|
|
int16_t SX126x::startReceiveDutyCycle(uint32_t rxPeriod, uint32_t sleepPeriod, uint16_t irqFlags, uint16_t irqMask) {
|
|
// datasheet claims time to go to sleep is ~500us, same to wake up, compensate for that with 1 ms + TCXO delay
|
|
uint32_t transitionTime = _tcxoDelay + 1000;
|
|
sleepPeriod -= transitionTime;
|
|
|
|
// divide by 15.625
|
|
uint32_t rxPeriodRaw = (rxPeriod * 8) / 125;
|
|
uint32_t sleepPeriodRaw = (sleepPeriod * 8) / 125;
|
|
|
|
// check 24 bit limit and zero value (likely not intended)
|
|
if((rxPeriodRaw & 0xFF000000) || (rxPeriodRaw == 0)) {
|
|
return(RADIOLIB_ERR_INVALID_RX_PERIOD);
|
|
}
|
|
|
|
// this check of the high byte also catches underflow when we subtracted transitionTime
|
|
if((sleepPeriodRaw & 0xFF000000) || (sleepPeriodRaw == 0)) {
|
|
return(RADIOLIB_ERR_INVALID_SLEEP_PERIOD);
|
|
}
|
|
|
|
int16_t state = startReceiveCommon(RADIOLIB_SX126X_RX_TIMEOUT_INF, irqFlags, irqMask);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
uint8_t data[6] = {(uint8_t)((rxPeriodRaw >> 16) & 0xFF), (uint8_t)((rxPeriodRaw >> 8) & 0xFF), (uint8_t)(rxPeriodRaw & 0xFF),
|
|
(uint8_t)((sleepPeriodRaw >> 16) & 0xFF), (uint8_t)((sleepPeriodRaw >> 8) & 0xFF), (uint8_t)(sleepPeriodRaw & 0xFF)};
|
|
return(_mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_RX_DUTY_CYCLE, data, 6));
|
|
}
|
|
|
|
int16_t SX126x::startReceiveDutyCycleAuto(uint16_t senderPreambleLength, uint16_t minSymbols, uint16_t irqFlags, uint16_t irqMask) {
|
|
if(senderPreambleLength == 0) {
|
|
senderPreambleLength = _preambleLength;
|
|
}
|
|
|
|
// worst case is that the sender starts transmitting when we're just less than minSymbols from going back to sleep.
|
|
// in this case, we don't catch minSymbols before going to sleep,
|
|
// so we must be awake for at least that long before the sender stops transmitting.
|
|
uint16_t sleepSymbols = senderPreambleLength - 2 * minSymbols;
|
|
|
|
// if we're not to sleep at all, just use the standard startReceive.
|
|
if(2 * minSymbols > senderPreambleLength) {
|
|
return(startReceive(RADIOLIB_SX126X_RX_TIMEOUT_INF, irqFlags, irqMask));
|
|
}
|
|
|
|
uint32_t symbolLength = ((uint32_t)(10 * 1000) << _sf) / (10 * _bwKhz);
|
|
uint32_t sleepPeriod = symbolLength * sleepSymbols;
|
|
RADIOLIB_DEBUG_PRINTLN("Auto sleep period: %d", sleepPeriod);
|
|
|
|
// when the unit detects a preamble, it starts a timer that will timeout if it doesn't receive a header in time.
|
|
// the duration is sleepPeriod + 2 * wakePeriod.
|
|
// The sleepPeriod doesn't take into account shutdown and startup time for the unit (~1ms)
|
|
// We need to ensure that the timeout is longer than senderPreambleLength.
|
|
// So we must satisfy: wakePeriod > (preamblePeriod - (sleepPeriod - 1000)) / 2. (A)
|
|
// we also need to ensure the unit is awake to see at least minSymbols. (B)
|
|
uint32_t wakePeriod = max(
|
|
(symbolLength * (senderPreambleLength + 1) - (sleepPeriod - 1000)) / 2, // (A)
|
|
symbolLength * (minSymbols + 1)); //(B)
|
|
RADIOLIB_DEBUG_PRINTLN("Auto wake period: ", wakePeriod);
|
|
|
|
// If our sleep period is shorter than our transition time, just use the standard startReceive
|
|
if(sleepPeriod < _tcxoDelay + 1016) {
|
|
return(startReceive(RADIOLIB_SX126X_RX_TIMEOUT_INF, irqFlags, irqMask));
|
|
}
|
|
|
|
return(startReceiveDutyCycle(wakePeriod, sleepPeriod, irqFlags, irqMask));
|
|
}
|
|
|
|
int16_t SX126x::startReceiveCommon(uint32_t timeout, uint16_t irqFlags, uint16_t irqMask) {
|
|
// set DIO mapping
|
|
if(timeout != RADIOLIB_SX126X_RX_TIMEOUT_INF) {
|
|
irqMask |= RADIOLIB_SX126X_IRQ_TIMEOUT;
|
|
}
|
|
int16_t state = setDioIrqParams(irqFlags, irqMask);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// set buffer pointers
|
|
state = setBufferBaseAddress();
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// clear interrupt flags
|
|
state = clearIrqStatus();
|
|
|
|
// restore original packet length
|
|
uint8_t modem = getPacketType();
|
|
if(modem == RADIOLIB_SX126X_PACKET_TYPE_LORA) {
|
|
state = setPacketParams(_preambleLength, _crcType, _implicitLen, _headerType);
|
|
} else if(modem == RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
|
|
state = setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength, _addrComp, _whitening, _packetType);
|
|
} else {
|
|
return(RADIOLIB_ERR_UNKNOWN);
|
|
}
|
|
|
|
return(state);
|
|
}
|
|
|
|
int16_t SX126x::readData(uint8_t* data, size_t len) {
|
|
// this method may get called from receive() after Rx timeout
|
|
// if that's the case, the first call will return "SPI command timeout error"
|
|
// check the IRQ to be sure this really originated from timeout event
|
|
int16_t state = _mod->SPIcheckStream();
|
|
if((state == RADIOLIB_ERR_SPI_CMD_TIMEOUT) && (getIrqStatus() & RADIOLIB_SX126X_IRQ_TIMEOUT)) {
|
|
// this is definitely Rx timeout
|
|
return(RADIOLIB_ERR_RX_TIMEOUT);
|
|
}
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// check integrity CRC
|
|
uint16_t irq = getIrqStatus();
|
|
int16_t crcState = RADIOLIB_ERR_NONE;
|
|
if((irq & RADIOLIB_SX126X_IRQ_CRC_ERR) || (irq & RADIOLIB_SX126X_IRQ_HEADER_ERR)) {
|
|
crcState = RADIOLIB_ERR_CRC_MISMATCH;
|
|
}
|
|
|
|
// get packet length
|
|
size_t length = getPacketLength();
|
|
if((len != 0) && (len < length)) {
|
|
// user requested less data than we got, only return what was requested
|
|
length = len;
|
|
}
|
|
|
|
// read packet data
|
|
state = readBuffer(data, length);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// reset the base addresses
|
|
state = setBufferBaseAddress();
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// clear interrupt flags
|
|
state = clearIrqStatus();
|
|
|
|
// check if CRC failed - this is done after reading data to give user the option to keep them
|
|
RADIOLIB_ASSERT(crcState);
|
|
|
|
return(state);
|
|
}
|
|
|
|
int16_t SX126x::startChannelScan(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin) {
|
|
// check active modem
|
|
if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_LORA) {
|
|
return(RADIOLIB_ERR_WRONG_MODEM);
|
|
}
|
|
|
|
// set mode to standby
|
|
int16_t state = standby();
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// set RF switch (if present)
|
|
_mod->setRfSwitchState(Module::MODE_RX);
|
|
|
|
// set DIO pin mapping
|
|
state = setDioIrqParams(RADIOLIB_SX126X_IRQ_CAD_DETECTED | RADIOLIB_SX126X_IRQ_CAD_DONE, RADIOLIB_SX126X_IRQ_CAD_DETECTED | RADIOLIB_SX126X_IRQ_CAD_DONE);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// clear interrupt flags
|
|
state = clearIrqStatus();
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// set mode to CAD
|
|
state = setCad(symbolNum, detPeak, detMin);
|
|
return(state);
|
|
}
|
|
|
|
int16_t SX126x::getChannelScanResult() {
|
|
// check active modem
|
|
if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_LORA) {
|
|
return(RADIOLIB_ERR_WRONG_MODEM);
|
|
}
|
|
|
|
// check CAD result
|
|
uint16_t cadResult = getIrqStatus();
|
|
if(cadResult & RADIOLIB_SX126X_IRQ_CAD_DETECTED) {
|
|
// detected some LoRa activity
|
|
clearIrqStatus();
|
|
return(RADIOLIB_LORA_DETECTED);
|
|
} else if(cadResult & RADIOLIB_SX126X_IRQ_CAD_DONE) {
|
|
// channel is free
|
|
clearIrqStatus();
|
|
return(RADIOLIB_CHANNEL_FREE);
|
|
}
|
|
|
|
return(RADIOLIB_ERR_UNKNOWN);
|
|
}
|
|
|
|
int16_t SX126x::setBandwidth(float bw) {
|
|
// check active modem
|
|
if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_LORA) {
|
|
return(RADIOLIB_ERR_WRONG_MODEM);
|
|
}
|
|
|
|
// ensure byte conversion doesn't overflow
|
|
RADIOLIB_CHECK_RANGE(bw, 0.0, 510.0, RADIOLIB_ERR_INVALID_BANDWIDTH);
|
|
|
|
// check allowed bandwidth values
|
|
uint8_t bw_div2 = bw / 2 + 0.01;
|
|
switch (bw_div2) {
|
|
case 3: // 7.8:
|
|
_bw = RADIOLIB_SX126X_LORA_BW_7_8;
|
|
break;
|
|
case 5: // 10.4:
|
|
_bw = RADIOLIB_SX126X_LORA_BW_10_4;
|
|
break;
|
|
case 7: // 15.6:
|
|
_bw = RADIOLIB_SX126X_LORA_BW_15_6;
|
|
break;
|
|
case 10: // 20.8:
|
|
_bw = RADIOLIB_SX126X_LORA_BW_20_8;
|
|
break;
|
|
case 15: // 31.25:
|
|
_bw = RADIOLIB_SX126X_LORA_BW_31_25;
|
|
break;
|
|
case 20: // 41.7:
|
|
_bw = RADIOLIB_SX126X_LORA_BW_41_7;
|
|
break;
|
|
case 31: // 62.5:
|
|
_bw = RADIOLIB_SX126X_LORA_BW_62_5;
|
|
break;
|
|
case 62: // 125.0:
|
|
_bw = RADIOLIB_SX126X_LORA_BW_125_0;
|
|
break;
|
|
case 125: // 250.0
|
|
_bw = RADIOLIB_SX126X_LORA_BW_250_0;
|
|
break;
|
|
case 250: // 500.0
|
|
_bw = RADIOLIB_SX126X_LORA_BW_500_0;
|
|
break;
|
|
default:
|
|
return(RADIOLIB_ERR_INVALID_BANDWIDTH);
|
|
}
|
|
|
|
// update modulation parameters
|
|
_bwKhz = bw;
|
|
return(setModulationParams(_sf, _bw, _cr, _ldro));
|
|
}
|
|
|
|
int16_t SX126x::setSpreadingFactor(uint8_t sf) {
|
|
// check active modem
|
|
if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_LORA) {
|
|
return(RADIOLIB_ERR_WRONG_MODEM);
|
|
}
|
|
|
|
RADIOLIB_CHECK_RANGE(sf, 5, 12, RADIOLIB_ERR_INVALID_SPREADING_FACTOR);
|
|
|
|
// update modulation parameters
|
|
_sf = sf;
|
|
return(setModulationParams(_sf, _bw, _cr, _ldro));
|
|
}
|
|
|
|
int16_t SX126x::setCodingRate(uint8_t cr) {
|
|
// check active modem
|
|
if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_LORA) {
|
|
return(RADIOLIB_ERR_WRONG_MODEM);
|
|
}
|
|
|
|
RADIOLIB_CHECK_RANGE(cr, 5, 8, RADIOLIB_ERR_INVALID_CODING_RATE);
|
|
|
|
// update modulation parameters
|
|
_cr = cr - 4;
|
|
return(setModulationParams(_sf, _bw, _cr, _ldro));
|
|
}
|
|
|
|
int16_t SX126x::setSyncWord(uint8_t syncWord, uint8_t controlBits) {
|
|
// check active modem
|
|
if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_LORA) {
|
|
return(RADIOLIB_ERR_WRONG_MODEM);
|
|
}
|
|
|
|
// update register
|
|
uint8_t data[2] = {(uint8_t)((syncWord & 0xF0) | ((controlBits & 0xF0) >> 4)), (uint8_t)(((syncWord & 0x0F) << 4) | (controlBits & 0x0F))};
|
|
return(writeRegister(RADIOLIB_SX126X_REG_LORA_SYNC_WORD_MSB, data, 2));
|
|
}
|
|
|
|
int16_t SX126x::setCurrentLimit(float currentLimit) {
|
|
// check allowed range
|
|
if(!((currentLimit >= 0) && (currentLimit <= 140))) {
|
|
return(RADIOLIB_ERR_INVALID_CURRENT_LIMIT);
|
|
}
|
|
|
|
// calculate raw value
|
|
uint8_t rawLimit = (uint8_t)(currentLimit / 2.5);
|
|
|
|
// update register
|
|
return(writeRegister(RADIOLIB_SX126X_REG_OCP_CONFIGURATION, &rawLimit, 1));
|
|
}
|
|
|
|
float SX126x::getCurrentLimit() {
|
|
// get the raw value
|
|
uint8_t ocp = 0;
|
|
readRegister(RADIOLIB_SX126X_REG_OCP_CONFIGURATION, &ocp, 1);
|
|
|
|
// return the actual value
|
|
return((float)ocp * 2.5);
|
|
}
|
|
|
|
int16_t SX126x::setPreambleLength(uint16_t preambleLength) {
|
|
uint8_t modem = getPacketType();
|
|
if(modem == RADIOLIB_SX126X_PACKET_TYPE_LORA) {
|
|
_preambleLength = preambleLength;
|
|
return(setPacketParams(_preambleLength, _crcType, _implicitLen, _headerType));
|
|
} else if(modem == RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
|
|
_preambleLengthFSK = preambleLength;
|
|
return(setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength, _addrComp, _whitening, _packetType));
|
|
}
|
|
|
|
return(RADIOLIB_ERR_UNKNOWN);
|
|
}
|
|
|
|
int16_t SX126x::setFrequencyDeviation(float freqDev) {
|
|
// check active modem
|
|
if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
|
|
return(RADIOLIB_ERR_WRONG_MODEM);
|
|
}
|
|
|
|
// set frequency deviation to lowest available setting (required for digimodes)
|
|
float newFreqDev = freqDev;
|
|
if(freqDev < 0.0) {
|
|
newFreqDev = 0.6;
|
|
}
|
|
|
|
RADIOLIB_CHECK_RANGE(newFreqDev, 0.6, 200.0, RADIOLIB_ERR_INVALID_FREQUENCY_DEVIATION);
|
|
|
|
// calculate raw frequency deviation value
|
|
uint32_t freqDevRaw = (uint32_t)(((newFreqDev * 1000.0) * (float)((uint32_t)(1) << 25)) / (RADIOLIB_SX126X_CRYSTAL_FREQ * 1000000.0));
|
|
|
|
// check modulation parameters
|
|
_freqDev = freqDevRaw;
|
|
|
|
// update modulation parameters
|
|
return(setModulationParamsFSK(_br, _pulseShape, _rxBw, _freqDev));
|
|
}
|
|
|
|
int16_t SX126x::setBitRate(float br) {
|
|
// check active modem
|
|
if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
|
|
return(RADIOLIB_ERR_WRONG_MODEM);
|
|
}
|
|
|
|
RADIOLIB_CHECK_RANGE(br, 0.6, 300.0, RADIOLIB_ERR_INVALID_BIT_RATE);
|
|
|
|
// calculate raw bit rate value
|
|
uint32_t brRaw = (uint32_t)((RADIOLIB_SX126X_CRYSTAL_FREQ * 1000000.0 * 32.0) / (br * 1000.0));
|
|
|
|
// check modulation parameters
|
|
_br = brRaw;
|
|
|
|
// update modulation parameters
|
|
return(setModulationParamsFSK(_br, _pulseShape, _rxBw, _freqDev));
|
|
}
|
|
|
|
int16_t SX126x::setRxBandwidth(float rxBw) {
|
|
// check active modem
|
|
if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
|
|
return(RADIOLIB_ERR_WRONG_MODEM);
|
|
}
|
|
|
|
// check modulation parameters
|
|
/*if(2 * _freqDev + _br > rxBw * 1000.0) {
|
|
return(RADIOLIB_ERR_INVALID_MODULATION_PARAMETERS);
|
|
}*/
|
|
_rxBwKhz = rxBw;
|
|
|
|
// check allowed receiver bandwidth values
|
|
if(fabs(rxBw - 4.8) <= 0.001) {
|
|
_rxBw = RADIOLIB_SX126X_GFSK_RX_BW_4_8;
|
|
} else if(fabs(rxBw - 5.8) <= 0.001) {
|
|
_rxBw = RADIOLIB_SX126X_GFSK_RX_BW_5_8;
|
|
} else if(fabs(rxBw - 7.3) <= 0.001) {
|
|
_rxBw = RADIOLIB_SX126X_GFSK_RX_BW_7_3;
|
|
} else if(fabs(rxBw - 9.7) <= 0.001) {
|
|
_rxBw = RADIOLIB_SX126X_GFSK_RX_BW_9_7;
|
|
} else if(fabs(rxBw - 11.7) <= 0.001) {
|
|
_rxBw = RADIOLIB_SX126X_GFSK_RX_BW_11_7;
|
|
} else if(fabs(rxBw - 14.6) <= 0.001) {
|
|
_rxBw = RADIOLIB_SX126X_GFSK_RX_BW_14_6;
|
|
} else if(fabs(rxBw - 19.5) <= 0.001) {
|
|
_rxBw = RADIOLIB_SX126X_GFSK_RX_BW_19_5;
|
|
} else if(fabs(rxBw - 23.4) <= 0.001) {
|
|
_rxBw = RADIOLIB_SX126X_GFSK_RX_BW_23_4;
|
|
} else if(fabs(rxBw - 29.3) <= 0.001) {
|
|
_rxBw = RADIOLIB_SX126X_GFSK_RX_BW_29_3;
|
|
} else if(fabs(rxBw - 39.0) <= 0.001) {
|
|
_rxBw = RADIOLIB_SX126X_GFSK_RX_BW_39_0;
|
|
} else if(fabs(rxBw - 46.9) <= 0.001) {
|
|
_rxBw = RADIOLIB_SX126X_GFSK_RX_BW_46_9;
|
|
} else if(fabs(rxBw - 58.6) <= 0.001) {
|
|
_rxBw = RADIOLIB_SX126X_GFSK_RX_BW_58_6;
|
|
} else if(fabs(rxBw - 78.2) <= 0.001) {
|
|
_rxBw = RADIOLIB_SX126X_GFSK_RX_BW_78_2;
|
|
} else if(fabs(rxBw - 93.8) <= 0.001) {
|
|
_rxBw = RADIOLIB_SX126X_GFSK_RX_BW_93_8;
|
|
} else if(fabs(rxBw - 117.3) <= 0.001) {
|
|
_rxBw = RADIOLIB_SX126X_GFSK_RX_BW_117_3;
|
|
} else if(fabs(rxBw - 156.2) <= 0.001) {
|
|
_rxBw = RADIOLIB_SX126X_GFSK_RX_BW_156_2;
|
|
} else if(fabs(rxBw - 187.2) <= 0.001) {
|
|
_rxBw = RADIOLIB_SX126X_GFSK_RX_BW_187_2;
|
|
} else if(fabs(rxBw - 234.3) <= 0.001) {
|
|
_rxBw = RADIOLIB_SX126X_GFSK_RX_BW_234_3;
|
|
} else if(fabs(rxBw - 312.0) <= 0.001) {
|
|
_rxBw = RADIOLIB_SX126X_GFSK_RX_BW_312_0;
|
|
} else if(fabs(rxBw - 373.6) <= 0.001) {
|
|
_rxBw = RADIOLIB_SX126X_GFSK_RX_BW_373_6;
|
|
} else if(fabs(rxBw - 467.0) <= 0.001) {
|
|
_rxBw = RADIOLIB_SX126X_GFSK_RX_BW_467_0;
|
|
} else {
|
|
return(RADIOLIB_ERR_INVALID_RX_BANDWIDTH);
|
|
}
|
|
|
|
// update modulation parameters
|
|
return(setModulationParamsFSK(_br, _pulseShape, _rxBw, _freqDev));
|
|
}
|
|
|
|
int16_t SX126x::setRxBoostedGainMode(bool rxbgm, bool persist) {
|
|
// read the current register value
|
|
uint8_t rxGain = 0;
|
|
int16_t state = readRegister(RADIOLIB_SX126X_REG_RX_GAIN, &rxGain, 1);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// gain mode register value (SX1261/2 datasheet v2.1 section 9.6)
|
|
if(rxbgm) {
|
|
rxGain = RADIOLIB_SX126X_RX_GAIN_BOOSTED;
|
|
} else {
|
|
rxGain = RADIOLIB_SX126X_RX_GAIN_POWER_SAVING;
|
|
}
|
|
|
|
// update RX gain setting register
|
|
state = writeRegister(RADIOLIB_SX126X_REG_RX_GAIN, &rxGain, 1);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// add Rx Gain register to retention memory if requested
|
|
if(persist) {
|
|
// values and registers below are specified in SX126x datasheet v2.1 section 9.6, just below table 9-3
|
|
uint8_t value0 = 0x01;
|
|
uint8_t value1 = 0x08;
|
|
uint8_t value2 = 0xAC;
|
|
|
|
state = writeRegister(RADIOLIB_SX126X_REG_RX_GAIN_RETENTION_0, &value0, 1);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
state = writeRegister(RADIOLIB_SX126X_REG_RX_GAIN_RETENTION_1, &value1, 1);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
state = writeRegister(RADIOLIB_SX126X_REG_RX_GAIN_RETENTION_2, &value2, 1);
|
|
RADIOLIB_ASSERT(state);
|
|
}
|
|
|
|
return(state);
|
|
}
|
|
|
|
int16_t SX126x::setDataShaping(uint8_t sh) {
|
|
// check active modem
|
|
if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
|
|
return(RADIOLIB_ERR_WRONG_MODEM);
|
|
}
|
|
|
|
// set data shaping
|
|
switch(sh) {
|
|
case RADIOLIB_SHAPING_NONE:
|
|
_pulseShape = RADIOLIB_SX126X_GFSK_FILTER_NONE;
|
|
break;
|
|
case RADIOLIB_SHAPING_0_3:
|
|
_pulseShape = RADIOLIB_SX126X_GFSK_FILTER_GAUSS_0_3;
|
|
break;
|
|
case RADIOLIB_SHAPING_0_5:
|
|
_pulseShape = RADIOLIB_SX126X_GFSK_FILTER_GAUSS_0_5;
|
|
break;
|
|
case RADIOLIB_SHAPING_0_7:
|
|
_pulseShape = RADIOLIB_SX126X_GFSK_FILTER_GAUSS_0_7;
|
|
break;
|
|
case RADIOLIB_SHAPING_1_0:
|
|
_pulseShape = RADIOLIB_SX126X_GFSK_FILTER_GAUSS_1;
|
|
break;
|
|
default:
|
|
return(RADIOLIB_ERR_INVALID_DATA_SHAPING);
|
|
}
|
|
|
|
// update modulation parameters
|
|
return(setModulationParamsFSK(_br, _pulseShape, _rxBw, _freqDev));
|
|
}
|
|
|
|
int16_t SX126x::setSyncWord(uint8_t* syncWord, uint8_t len) {
|
|
// check active modem
|
|
if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
|
|
return(RADIOLIB_ERR_WRONG_MODEM);
|
|
}
|
|
|
|
// check sync word Length
|
|
if(len > 8) {
|
|
return(RADIOLIB_ERR_INVALID_SYNC_WORD);
|
|
}
|
|
|
|
// write sync word
|
|
int16_t state = writeRegister(RADIOLIB_SX126X_REG_SYNC_WORD_0, syncWord, len);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// update packet parameters
|
|
_syncWordLength = len * 8;
|
|
state = setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength, _addrComp, _whitening, _packetType);
|
|
|
|
return(state);
|
|
}
|
|
|
|
int16_t SX126x::setSyncBits(uint8_t *syncWord, uint8_t bitsLen) {
|
|
// check active modem
|
|
if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
|
|
return(RADIOLIB_ERR_WRONG_MODEM);
|
|
}
|
|
|
|
// check sync word Length
|
|
if(bitsLen > 0x40) {
|
|
return(RADIOLIB_ERR_INVALID_SYNC_WORD);
|
|
}
|
|
|
|
uint8_t bytesLen = bitsLen / 8;
|
|
if ((bitsLen % 8) != 0) {
|
|
bytesLen++;
|
|
}
|
|
|
|
// write sync word
|
|
int16_t state = writeRegister(RADIOLIB_SX126X_REG_SYNC_WORD_0, syncWord, bytesLen);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// update packet parameters
|
|
_syncWordLength = bitsLen;
|
|
state = setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength, _addrComp, _whitening, _packetType);
|
|
|
|
return(state);
|
|
}
|
|
|
|
int16_t SX126x::setNodeAddress(uint8_t nodeAddr) {
|
|
// check active modem
|
|
if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
|
|
return(RADIOLIB_ERR_WRONG_MODEM);
|
|
}
|
|
|
|
// enable address filtering (node only)
|
|
_addrComp = RADIOLIB_SX126X_GFSK_ADDRESS_FILT_NODE;
|
|
int16_t state = setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength, _addrComp, _whitening, _packetType);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// set node address
|
|
state = writeRegister(RADIOLIB_SX126X_REG_NODE_ADDRESS, &nodeAddr, 1);
|
|
|
|
return(state);
|
|
}
|
|
|
|
int16_t SX126x::setBroadcastAddress(uint8_t broadAddr) {
|
|
// check active modem
|
|
if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
|
|
return(RADIOLIB_ERR_WRONG_MODEM);
|
|
}
|
|
|
|
// enable address filtering (node and broadcast)
|
|
_addrComp = RADIOLIB_SX126X_GFSK_ADDRESS_FILT_NODE_BROADCAST;
|
|
int16_t state = setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength, _addrComp, _whitening, _packetType);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// set broadcast address
|
|
state = writeRegister(RADIOLIB_SX126X_REG_BROADCAST_ADDRESS, &broadAddr, 1);
|
|
|
|
return(state);
|
|
}
|
|
|
|
int16_t SX126x::disableAddressFiltering() {
|
|
// check active modem
|
|
if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
|
|
return(RADIOLIB_ERR_WRONG_MODEM);
|
|
}
|
|
|
|
// disable address filtering
|
|
_addrComp = RADIOLIB_SX126X_GFSK_ADDRESS_FILT_OFF;
|
|
return(setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength, _addrComp, _whitening));
|
|
}
|
|
|
|
int16_t SX126x::setCRC(uint8_t len, uint16_t initial, uint16_t polynomial, bool inverted) {
|
|
// check active modem
|
|
uint8_t modem = getPacketType();
|
|
|
|
if(modem == RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
|
|
// update packet parameters
|
|
switch(len) {
|
|
case 0:
|
|
_crcTypeFSK = RADIOLIB_SX126X_GFSK_CRC_OFF;
|
|
break;
|
|
case 1:
|
|
if(inverted) {
|
|
_crcTypeFSK = RADIOLIB_SX126X_GFSK_CRC_1_BYTE_INV;
|
|
} else {
|
|
_crcTypeFSK = RADIOLIB_SX126X_GFSK_CRC_1_BYTE;
|
|
}
|
|
break;
|
|
case 2:
|
|
if(inverted) {
|
|
_crcTypeFSK = RADIOLIB_SX126X_GFSK_CRC_2_BYTE_INV;
|
|
} else {
|
|
_crcTypeFSK = RADIOLIB_SX126X_GFSK_CRC_2_BYTE;
|
|
}
|
|
break;
|
|
default:
|
|
return(RADIOLIB_ERR_INVALID_CRC_CONFIGURATION);
|
|
}
|
|
|
|
int16_t state = setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength, _addrComp, _whitening, _packetType);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// write initial CRC value
|
|
uint8_t data[2] = {(uint8_t)((initial >> 8) & 0xFF), (uint8_t)(initial & 0xFF)};
|
|
state = writeRegister(RADIOLIB_SX126X_REG_CRC_INITIAL_MSB, data, 2);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// write CRC polynomial value
|
|
data[0] = (uint8_t)((polynomial >> 8) & 0xFF);
|
|
data[1] = (uint8_t)(polynomial & 0xFF);
|
|
state = writeRegister(RADIOLIB_SX126X_REG_CRC_POLYNOMIAL_MSB, data, 2);
|
|
|
|
return(state);
|
|
|
|
} else if(modem == RADIOLIB_SX126X_PACKET_TYPE_LORA) {
|
|
// LoRa CRC doesn't allow to set CRC polynomial, initial value, or inversion
|
|
|
|
// update packet parameters
|
|
if(len) {
|
|
_crcType = RADIOLIB_SX126X_LORA_CRC_ON;
|
|
} else {
|
|
_crcType = RADIOLIB_SX126X_LORA_CRC_OFF;
|
|
}
|
|
|
|
return(setPacketParams(_preambleLength, _crcType, _implicitLen, _headerType));
|
|
}
|
|
|
|
return(RADIOLIB_ERR_UNKNOWN);
|
|
}
|
|
|
|
int16_t SX126x::setWhitening(bool enabled, uint16_t initial) {
|
|
// check active modem
|
|
if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
|
|
return(RADIOLIB_ERR_WRONG_MODEM);
|
|
}
|
|
|
|
int16_t state = RADIOLIB_ERR_NONE;
|
|
if(!enabled) {
|
|
// disable whitening
|
|
_whitening = RADIOLIB_SX126X_GFSK_WHITENING_OFF;
|
|
|
|
state = setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength, _addrComp, _whitening, _packetType);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
} else {
|
|
// enable whitening
|
|
_whitening = RADIOLIB_SX126X_GFSK_WHITENING_ON;
|
|
|
|
// write initial whitening value
|
|
// as per note on pg. 65 of datasheet v1.2: "The user should not change the value of the 7 MSB's of this register"
|
|
uint8_t data[2];
|
|
// first read the actual value and mask 7 MSB which we can not change
|
|
// if different value is written in 7 MSB, the Rx won't even work (tested on HW)
|
|
state = readRegister(RADIOLIB_SX126X_REG_WHITENING_INITIAL_MSB, data, 1);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
data[0] = (data[0] & 0xFE) | (uint8_t)((initial >> 8) & 0x01);
|
|
data[1] = (uint8_t)(initial & 0xFF);
|
|
state = writeRegister(RADIOLIB_SX126X_REG_WHITENING_INITIAL_MSB, data, 2);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
state = setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength, _addrComp, _whitening, _packetType);
|
|
RADIOLIB_ASSERT(state);
|
|
}
|
|
return(state);
|
|
}
|
|
|
|
float SX126x::getDataRate() const {
|
|
return(_dataRate);
|
|
}
|
|
|
|
float SX126x::getRSSI(bool packet) {
|
|
if(packet) {
|
|
// get last packet RSSI from packet status
|
|
uint32_t packetStatus = getPacketStatus();
|
|
uint8_t rssiPkt = packetStatus & 0xFF;
|
|
return(-1.0 * rssiPkt/2.0);
|
|
} else {
|
|
// get instantaneous RSSI value
|
|
uint8_t data[3] = {0, 0, 0}; // RssiInst, Status, RFU
|
|
_mod->SPIreadStream(RADIOLIB_SX126X_CMD_GET_RSSI_INST, data, 3);
|
|
return((float)data[0] / (-2.0));
|
|
}
|
|
}
|
|
|
|
float SX126x::getSNR() {
|
|
// check active modem
|
|
if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_LORA) {
|
|
return(RADIOLIB_ERR_WRONG_MODEM);
|
|
}
|
|
|
|
// get last packet SNR from packet status
|
|
uint32_t packetStatus = getPacketStatus();
|
|
uint8_t snrPkt = (packetStatus >> 8) & 0xFF;
|
|
if(snrPkt < 128) {
|
|
return(snrPkt/4.0);
|
|
} else {
|
|
return((snrPkt - 256)/4.0);
|
|
}
|
|
}
|
|
|
|
float SX126x::getFrequencyError() {
|
|
// check active modem
|
|
uint8_t modem = getPacketType();
|
|
if(modem != RADIOLIB_SX126X_PACKET_TYPE_LORA) {
|
|
return(0.0);
|
|
}
|
|
|
|
// read the raw frequency error register values
|
|
uint8_t efeRaw[3] = {0};
|
|
int16_t state = readRegister(RADIOLIB_SX126X_REG_FREQ_ERROR, &efeRaw[0], 1);
|
|
RADIOLIB_ASSERT(state);
|
|
state = readRegister(RADIOLIB_SX126X_REG_FREQ_ERROR + 1, &efeRaw[1], 1);
|
|
RADIOLIB_ASSERT(state);
|
|
state = readRegister(RADIOLIB_SX126X_REG_FREQ_ERROR + 2, &efeRaw[2], 1);
|
|
RADIOLIB_ASSERT(state);
|
|
uint32_t efe = ((uint32_t) efeRaw[0] << 16) | ((uint32_t) efeRaw[1] << 8) | efeRaw[2];
|
|
efe &= 0x0FFFFF;
|
|
|
|
float error = 0;
|
|
|
|
// check the first bit
|
|
if (efe & 0x80000) {
|
|
// frequency error is negative
|
|
efe |= (uint32_t) 0xFFF00000;
|
|
efe = ~efe + 1;
|
|
error = 1.55 * (float) efe / (1600.0 / (float) _bwKhz) * -1.0;
|
|
} else {
|
|
error = 1.55 * (float) efe / (1600.0 / (float) _bwKhz);
|
|
}
|
|
|
|
return(error);
|
|
}
|
|
|
|
size_t SX126x::getPacketLength(bool update) {
|
|
(void)update;
|
|
|
|
// in implicit mode, return the cached value
|
|
if((getPacketType() == RADIOLIB_SX126X_PACKET_TYPE_LORA) && (_headerType == RADIOLIB_SX126X_LORA_HEADER_IMPLICIT)) {
|
|
return(_implicitLen);
|
|
}
|
|
|
|
uint8_t rxBufStatus[2] = {0, 0};
|
|
_mod->SPIreadStream(RADIOLIB_SX126X_CMD_GET_RX_BUFFER_STATUS, rxBufStatus, 2);
|
|
return((size_t)rxBufStatus[0]);
|
|
}
|
|
|
|
int16_t SX126x::fixedPacketLengthMode(uint8_t len) {
|
|
return(setPacketMode(RADIOLIB_SX126X_GFSK_PACKET_FIXED, len));
|
|
}
|
|
|
|
int16_t SX126x::variablePacketLengthMode(uint8_t maxLen) {
|
|
return(setPacketMode(RADIOLIB_SX126X_GFSK_PACKET_VARIABLE, maxLen));
|
|
}
|
|
|
|
uint32_t SX126x::getTimeOnAir(size_t len) {
|
|
// everything is in microseconds to allow integer arithmetic
|
|
// some constants have .25, these are multiplied by 4, and have _x4 postfix to indicate that fact
|
|
if(getPacketType() == RADIOLIB_SX126X_PACKET_TYPE_LORA) {
|
|
uint32_t symbolLength_us = ((uint32_t)(1000 * 10) << _sf) / (_bwKhz * 10) ;
|
|
uint8_t sfCoeff1_x4 = 17; // (4.25 * 4)
|
|
uint8_t sfCoeff2 = 8;
|
|
if(_sf == 5 || _sf == 6) {
|
|
sfCoeff1_x4 = 25; // 6.25 * 4
|
|
sfCoeff2 = 0;
|
|
}
|
|
uint8_t sfDivisor = 4*_sf;
|
|
if(symbolLength_us >= 16000) {
|
|
sfDivisor = 4*(_sf - 2);
|
|
}
|
|
const int8_t bitsPerCrc = 16;
|
|
const int8_t N_symbol_header = _headerType == RADIOLIB_SX126X_LORA_HEADER_EXPLICIT ? 20 : 0;
|
|
|
|
// numerator of equation in section 6.1.4 of SX1268 datasheet v1.1 (might not actually be bitcount, but it has len * 8)
|
|
int16_t bitCount = (int16_t) 8 * len + _crcType * bitsPerCrc - 4 * _sf + sfCoeff2 + N_symbol_header;
|
|
if(bitCount < 0) {
|
|
bitCount = 0;
|
|
}
|
|
// add (sfDivisor) - 1 to the numerator to give integer CEIL(...)
|
|
uint16_t nPreCodedSymbols = (bitCount + (sfDivisor - 1)) / (sfDivisor);
|
|
|
|
// preamble can be 65k, therefore nSymbol_x4 needs to be 32 bit
|
|
uint32_t nSymbol_x4 = (_preambleLength + 8) * 4 + sfCoeff1_x4 + nPreCodedSymbols * (_cr + 4) * 4;
|
|
|
|
return((symbolLength_us * nSymbol_x4) / 4);
|
|
} else {
|
|
return((len * 8 * _br) / (RADIOLIB_SX126X_CRYSTAL_FREQ * 32));
|
|
}
|
|
}
|
|
|
|
int16_t SX126x::implicitHeader(size_t len) {
|
|
return(setHeaderType(RADIOLIB_SX126X_LORA_HEADER_IMPLICIT, len));
|
|
}
|
|
|
|
int16_t SX126x::explicitHeader() {
|
|
return(setHeaderType(RADIOLIB_SX126X_LORA_HEADER_EXPLICIT));
|
|
}
|
|
|
|
int16_t SX126x::setRegulatorLDO() {
|
|
return(setRegulatorMode(RADIOLIB_SX126X_REGULATOR_LDO));
|
|
}
|
|
|
|
int16_t SX126x::setRegulatorDCDC() {
|
|
return(setRegulatorMode(RADIOLIB_SX126X_REGULATOR_DC_DC));
|
|
}
|
|
|
|
int16_t SX126x::setEncoding(uint8_t encoding) {
|
|
return(setWhitening(encoding));
|
|
}
|
|
|
|
void SX126x::setRfSwitchPins(uint8_t rxEn, uint8_t txEn) {
|
|
_mod->setRfSwitchPins(rxEn, txEn);
|
|
}
|
|
|
|
void SX126x::setRfSwitchTable(const uint8_t (&pins)[Module::RFSWITCH_MAX_PINS], const Module::RfSwitchMode_t table[]) {
|
|
_mod->setRfSwitchTable(pins, table);
|
|
}
|
|
|
|
int16_t SX126x::forceLDRO(bool enable) {
|
|
// check active modem
|
|
if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_LORA) {
|
|
return(RADIOLIB_ERR_WRONG_MODEM);
|
|
}
|
|
|
|
// update modulation parameters
|
|
_ldroAuto = false;
|
|
_ldro = (uint8_t)enable;
|
|
return(setModulationParams(_sf, _bw, _cr, _ldro));
|
|
}
|
|
|
|
int16_t SX126x::autoLDRO() {
|
|
if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_LORA) {
|
|
return(RADIOLIB_ERR_WRONG_MODEM);
|
|
}
|
|
|
|
_ldroAuto = true;
|
|
return(RADIOLIB_ERR_NONE);
|
|
}
|
|
|
|
uint8_t SX126x::randomByte() {
|
|
// set some magic registers
|
|
_mod->SPIsetRegValue(RADIOLIB_SX126X_REG_ANA_LNA, RADIOLIB_SX126X_LNA_RNG_ENABLED, 0, 0);
|
|
_mod->SPIsetRegValue(RADIOLIB_SX126X_REG_ANA_MIXER, RADIOLIB_SX126X_MIXER_RNG_ENABLED, 0, 0);
|
|
|
|
// set mode to Rx
|
|
setRx(RADIOLIB_SX126X_RX_TIMEOUT_INF);
|
|
|
|
// wait a bit for the RSSI reading to stabilise
|
|
_mod->hal->delay(10);
|
|
|
|
// read RSSI value 8 times, always keep just the least significant bit
|
|
uint8_t randByte = 0x00;
|
|
for(uint8_t i = 0; i < 8; i++) {
|
|
uint8_t val = 0x00;
|
|
readRegister(RADIOLIB_SX126X_REG_RANDOM_NUMBER_0, &val, sizeof(uint8_t));
|
|
randByte |= ((val & 0x01) << i);
|
|
}
|
|
|
|
// set mode to standby
|
|
standby();
|
|
|
|
// restore the magic registers
|
|
_mod->SPIsetRegValue(RADIOLIB_SX126X_REG_ANA_LNA, RADIOLIB_SX126X_LNA_RNG_DISABLED, 0, 0);
|
|
_mod->SPIsetRegValue(RADIOLIB_SX126X_REG_ANA_MIXER, RADIOLIB_SX126X_MIXER_RNG_DISABLED, 0, 0);
|
|
|
|
return(randByte);
|
|
}
|
|
|
|
int16_t SX126x::invertIQ(bool invertIQ) {
|
|
if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_LORA) {
|
|
return(RADIOLIB_ERR_WRONG_MODEM);
|
|
}
|
|
|
|
uint8_t invert = RADIOLIB_SX126X_LORA_IQ_STANDARD;
|
|
if(invertIQ) {
|
|
invert = RADIOLIB_SX126X_LORA_IQ_INVERTED;
|
|
}
|
|
|
|
return(setPacketParams(_preambleLength, _crcType, _implicitLen, _headerType, invert));
|
|
}
|
|
|
|
#if !defined(RADIOLIB_EXCLUDE_DIRECT_RECEIVE)
|
|
void SX126x::setDirectAction(void (*func)(void)) {
|
|
setDio1Action(func);
|
|
}
|
|
|
|
void SX126x::readBit(uint8_t pin) {
|
|
updateDirectBuffer((uint8_t)_mod->hal->digitalRead(pin));
|
|
}
|
|
#endif
|
|
|
|
int16_t SX126x::uploadPatch(const uint32_t* patch, size_t len, bool nonvolatile) {
|
|
// set to standby RC mode
|
|
int16_t state = standby(RADIOLIB_SX126X_STANDBY_RC);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// check the version
|
|
#if defined(RADIOLIB_DEBUG)
|
|
char ver_pre[16];
|
|
_mod->SPIreadRegisterBurst(RADIOLIB_SX126X_REG_VERSION_STRING, 16, (uint8_t*)ver_pre);
|
|
RADIOLIB_DEBUG_PRINTLN("Pre-update version string: %d", ver_pre);
|
|
#endif
|
|
|
|
// enable patch update
|
|
_mod->SPIwriteRegister(RADIOLIB_SX126X_REG_PATCH_UPDATE_ENABLE, RADIOLIB_SX126X_PATCH_UPDATE_ENABLED);
|
|
|
|
// upload the patch
|
|
uint8_t data[4];
|
|
for(uint32_t i = 0; i < len / sizeof(uint32_t); i++) {
|
|
uint32_t bin = 0;
|
|
if(nonvolatile) {
|
|
bin = RADIOLIB_NONVOLATILE_READ_DWORD(patch + i);
|
|
} else {
|
|
bin = patch[i];
|
|
}
|
|
data[0] = (bin >> 24) & 0xFF;
|
|
data[1] = (bin >> 16) & 0xFF;
|
|
data[2] = (bin >> 8) & 0xFF;
|
|
data[3] = bin & 0xFF;
|
|
_mod->SPIwriteRegisterBurst(RADIOLIB_SX126X_REG_PATCH_MEMORY_BASE + i*sizeof(uint32_t), data, sizeof(uint32_t));
|
|
}
|
|
|
|
// disable patch update
|
|
_mod->SPIwriteRegister(RADIOLIB_SX126X_REG_PATCH_UPDATE_ENABLE, RADIOLIB_SX126X_PATCH_UPDATE_DISABLED);
|
|
|
|
// update
|
|
_mod->SPIwriteStream(RADIOLIB_SX126X_CMD_PRAM_UPDATE, NULL, 0);
|
|
|
|
// check the version again
|
|
#if defined(RADIOLIB_DEBUG)
|
|
char ver_post[16];
|
|
_mod->SPIreadRegisterBurst(RADIOLIB_SX126X_REG_VERSION_STRING, 16, (uint8_t*)ver_post);
|
|
RADIOLIB_DEBUG_PRINTLN("Post-update version string: %d", ver_post);
|
|
#endif
|
|
|
|
return(state);
|
|
}
|
|
|
|
int16_t SX126x::spectralScanStart(uint16_t numSamples, uint8_t window, uint8_t interval) {
|
|
// abort first - not sure if this is strictly needed, but the example code does this
|
|
spectralScanAbort();
|
|
|
|
// set the RSSI window size
|
|
_mod->SPIwriteRegister(RADIOLIB_SX126X_REG_RSSI_AVG_WINDOW, window);
|
|
|
|
// start Rx with infinite timeout
|
|
int16_t state = setRx(RADIOLIB_SX126X_RX_TIMEOUT_INF);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// now set the actual spectral scan parameters
|
|
uint8_t data[3] = { (uint8_t)((numSamples >> 8) & 0xFF), (uint8_t)(numSamples & 0xFF), interval };
|
|
return(_mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_SPECTR_SCAN_PARAMS, data, 3));
|
|
}
|
|
|
|
void SX126x::spectralScanAbort() {
|
|
_mod->SPIwriteRegister(RADIOLIB_SX126X_REG_RSSI_AVG_WINDOW, 0x00);
|
|
}
|
|
|
|
int16_t SX126x::spectralScanGetStatus() {
|
|
uint8_t status = _mod->SPIreadRegister(RADIOLIB_SX126X_REG_SPECTRAL_SCAN_STATUS);
|
|
if(status == RADIOLIB_SX126X_SPECTRAL_SCAN_COMPLETED) {
|
|
return(RADIOLIB_ERR_NONE);
|
|
}
|
|
return(RADIOLIB_ERR_RANGING_TIMEOUT);
|
|
}
|
|
|
|
int16_t SX126x::spectralScanGetResult(uint16_t* results) {
|
|
// read the raw results
|
|
uint8_t data[2*RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE];
|
|
_mod->SPIreadRegisterBurst(RADIOLIB_SX126X_REG_SPECTRAL_SCAN_RESULT, 2*RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE, data);
|
|
|
|
// convert it
|
|
for(uint8_t i = 0; i < RADIOLIB_SX126X_SPECTRAL_SCAN_RES_SIZE; i++) {
|
|
results[i] = ((uint16_t)data[i*2] << 8) | ((uint16_t)data[i*2 + 1]);
|
|
}
|
|
return(RADIOLIB_ERR_NONE);
|
|
}
|
|
|
|
int16_t SX126x::setTCXO(float voltage, uint32_t delay) {
|
|
// check if TCXO is enabled at all
|
|
if(this->XTAL) {
|
|
return(RADIOLIB_ERR_INVALID_TCXO_VOLTAGE);
|
|
}
|
|
|
|
// set mode to standby
|
|
standby();
|
|
|
|
// check RADIOLIB_SX126X_XOSC_START_ERR flag and clear it
|
|
if(getDeviceErrors() & RADIOLIB_SX126X_XOSC_START_ERR) {
|
|
clearDeviceErrors();
|
|
}
|
|
|
|
// check 0 V disable
|
|
if(fabs(voltage - 0.0) <= 0.001) {
|
|
return(reset(true));
|
|
}
|
|
|
|
// check alowed voltage values
|
|
uint8_t data[4];
|
|
if(fabs(voltage - 1.6) <= 0.001) {
|
|
data[0] = RADIOLIB_SX126X_DIO3_OUTPUT_1_6;
|
|
} else if(fabs(voltage - 1.7) <= 0.001) {
|
|
data[0] = RADIOLIB_SX126X_DIO3_OUTPUT_1_7;
|
|
} else if(fabs(voltage - 1.8) <= 0.001) {
|
|
data[0] = RADIOLIB_SX126X_DIO3_OUTPUT_1_8;
|
|
} else if(fabs(voltage - 2.2) <= 0.001) {
|
|
data[0] = RADIOLIB_SX126X_DIO3_OUTPUT_2_2;
|
|
} else if(fabs(voltage - 2.4) <= 0.001) {
|
|
data[0] = RADIOLIB_SX126X_DIO3_OUTPUT_2_4;
|
|
} else if(fabs(voltage - 2.7) <= 0.001) {
|
|
data[0] = RADIOLIB_SX126X_DIO3_OUTPUT_2_7;
|
|
} else if(fabs(voltage - 3.0) <= 0.001) {
|
|
data[0] = RADIOLIB_SX126X_DIO3_OUTPUT_3_0;
|
|
} else if(fabs(voltage - 3.3) <= 0.001) {
|
|
data[0] = RADIOLIB_SX126X_DIO3_OUTPUT_3_3;
|
|
} else {
|
|
return(RADIOLIB_ERR_INVALID_TCXO_VOLTAGE);
|
|
}
|
|
|
|
// 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);
|
|
|
|
_tcxoDelay = delay;
|
|
|
|
// enable TCXO control on DIO3
|
|
return(_mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_DIO3_AS_TCXO_CTRL, data, 4));
|
|
}
|
|
|
|
int16_t SX126x::setDio2AsRfSwitch(bool enable) {
|
|
uint8_t data = 0;
|
|
if(enable) {
|
|
data = RADIOLIB_SX126X_DIO2_AS_RF_SWITCH;
|
|
} else {
|
|
data = RADIOLIB_SX126X_DIO2_AS_IRQ;
|
|
}
|
|
return(_mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_DIO2_AS_RF_SWITCH_CTRL, &data, 1));
|
|
}
|
|
|
|
int16_t SX126x::setFs() {
|
|
return(_mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_FS, NULL, 0));
|
|
}
|
|
|
|
int16_t SX126x::setTx(uint32_t timeout) {
|
|
uint8_t data[] = { (uint8_t)((timeout >> 16) & 0xFF), (uint8_t)((timeout >> 8) & 0xFF), (uint8_t)(timeout & 0xFF)} ;
|
|
return(_mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_TX, data, 3));
|
|
}
|
|
|
|
int16_t SX126x::setRx(uint32_t timeout) {
|
|
uint8_t data[] = { (uint8_t)((timeout >> 16) & 0xFF), (uint8_t)((timeout >> 8) & 0xFF), (uint8_t)(timeout & 0xFF) };
|
|
return(_mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_RX, data, 3, true, false));
|
|
}
|
|
|
|
int16_t SX126x::setCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin) {
|
|
// default CAD parameters for assigned SF as per Semtech AN1200.48, Rev 2.1, Page 50
|
|
uint8_t detPeakValues[8] = { 22, 22, 22, 22, 23, 24, 25, 28};
|
|
uint8_t symbolNumValues[8] = { RADIOLIB_SX126X_CAD_ON_2_SYMB,
|
|
RADIOLIB_SX126X_CAD_ON_2_SYMB,
|
|
RADIOLIB_SX126X_CAD_ON_2_SYMB,
|
|
RADIOLIB_SX126X_CAD_ON_2_SYMB,
|
|
RADIOLIB_SX126X_CAD_ON_4_SYMB,
|
|
RADIOLIB_SX126X_CAD_ON_4_SYMB,
|
|
RADIOLIB_SX126X_CAD_ON_4_SYMB,
|
|
RADIOLIB_SX126X_CAD_ON_4_SYMB };
|
|
// build the packet
|
|
uint8_t data[7];
|
|
data[0] = symbolNumValues[_sf - 5];
|
|
data[1] = detPeakValues[_sf - 5];
|
|
data[2] = RADIOLIB_SX126X_CAD_PARAM_DET_MIN;
|
|
data[3] = RADIOLIB_SX126X_CAD_GOTO_STDBY;
|
|
data[4] = 0x00;
|
|
data[5] = 0x00;
|
|
data[6] = 0x00;
|
|
|
|
// set user-provided values
|
|
if(symbolNum != RADIOLIB_SX126X_CAD_PARAM_DEFAULT) {
|
|
data[0] = symbolNum;
|
|
}
|
|
|
|
if(detPeak != RADIOLIB_SX126X_CAD_PARAM_DEFAULT) {
|
|
data[1] = detPeak;
|
|
}
|
|
|
|
if(detMin != RADIOLIB_SX126X_CAD_PARAM_DEFAULT) {
|
|
data[2] = detMin;
|
|
}
|
|
|
|
// configure paramaters
|
|
int16_t state = _mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_CAD_PARAMS, data, 7);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// start CAD
|
|
return(_mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_CAD, NULL, 0));
|
|
}
|
|
|
|
int16_t SX126x::setPaConfig(uint8_t paDutyCycle, uint8_t deviceSel, uint8_t hpMax, uint8_t paLut) {
|
|
uint8_t data[] = { paDutyCycle, hpMax, deviceSel, paLut };
|
|
return(_mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_PA_CONFIG, data, 4));
|
|
}
|
|
|
|
int16_t SX126x::writeRegister(uint16_t addr, uint8_t* data, uint8_t numBytes) {
|
|
_mod->SPIwriteRegisterBurst(addr, data, numBytes);
|
|
return(RADIOLIB_ERR_NONE);
|
|
}
|
|
|
|
int16_t SX126x::readRegister(uint16_t addr, uint8_t* data, uint8_t numBytes) {
|
|
// send the command
|
|
_mod->SPIreadRegisterBurst(addr, numBytes, data);
|
|
|
|
// check the status
|
|
int16_t state = _mod->SPIcheckStream();
|
|
return(state);
|
|
}
|
|
|
|
int16_t SX126x::writeBuffer(uint8_t* data, uint8_t numBytes, uint8_t offset) {
|
|
uint8_t cmd[] = { RADIOLIB_SX126X_CMD_WRITE_BUFFER, offset };
|
|
return(_mod->SPIwriteStream(cmd, 2, data, numBytes));
|
|
}
|
|
|
|
int16_t SX126x::readBuffer(uint8_t* data, uint8_t numBytes, uint8_t offset) {
|
|
uint8_t cmd[] = { RADIOLIB_SX126X_CMD_READ_BUFFER, offset };
|
|
return(_mod->SPIreadStream(cmd, 2, data, numBytes));
|
|
}
|
|
|
|
int16_t 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)};
|
|
return(_mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_DIO_IRQ_PARAMS, data, 8));
|
|
}
|
|
|
|
uint16_t SX126x::getIrqStatus() {
|
|
uint8_t data[] = { 0x00, 0x00 };
|
|
_mod->SPIreadStream(RADIOLIB_SX126X_CMD_GET_IRQ_STATUS, data, 2);
|
|
return(((uint16_t)(data[0]) << 8) | data[1]);
|
|
}
|
|
|
|
int16_t SX126x::clearIrqStatus(uint16_t clearIrqParams) {
|
|
uint8_t data[] = { (uint8_t)((clearIrqParams >> 8) & 0xFF), (uint8_t)(clearIrqParams & 0xFF) };
|
|
return(_mod->SPIwriteStream(RADIOLIB_SX126X_CMD_CLEAR_IRQ_STATUS, data, 2));
|
|
}
|
|
|
|
int16_t SX126x::setRfFrequency(uint32_t frf) {
|
|
uint8_t data[] = { (uint8_t)((frf >> 24) & 0xFF), (uint8_t)((frf >> 16) & 0xFF), (uint8_t)((frf >> 8) & 0xFF), (uint8_t)(frf & 0xFF) };
|
|
return(_mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_RF_FREQUENCY, data, 4));
|
|
}
|
|
|
|
int16_t SX126x::calibrateImage(uint8_t* data) {
|
|
int16_t state = _mod->SPIwriteStream(RADIOLIB_SX126X_CMD_CALIBRATE_IMAGE, data, 2);
|
|
|
|
// if something failed, show the device errors
|
|
#if defined(RADIOLIB_DEBUG)
|
|
if(state != RADIOLIB_ERR_NONE) {
|
|
// unless mode is forced to standby, device errors will be 0
|
|
standby();
|
|
uint16_t errors = getDeviceErrors();
|
|
RADIOLIB_DEBUG_PRINTLN("Calibration failed, device errors: 0x%X", errors);
|
|
}
|
|
#endif
|
|
return(state);
|
|
}
|
|
|
|
uint8_t SX126x::getPacketType() {
|
|
uint8_t data = 0xFF;
|
|
_mod->SPIreadStream(RADIOLIB_SX126X_CMD_GET_PACKET_TYPE, &data, 1);
|
|
return(data);
|
|
}
|
|
|
|
int16_t SX126x::setTxParams(uint8_t power, uint8_t rampTime) {
|
|
uint8_t data[] = { power, rampTime };
|
|
return(_mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_TX_PARAMS, data, 2));
|
|
}
|
|
|
|
int16_t SX126x::setPacketMode(uint8_t mode, uint8_t len) {
|
|
// check active modem
|
|
if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_GFSK) {
|
|
return(RADIOLIB_ERR_WRONG_MODEM);
|
|
}
|
|
|
|
// set requested packet mode
|
|
int16_t state = setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength, _addrComp, _whitening, mode, len);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// update cached value
|
|
_packetType = mode;
|
|
return(state);
|
|
}
|
|
|
|
int16_t SX126x::setHeaderType(uint8_t headerType, size_t len) {
|
|
// check active modem
|
|
if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_LORA) {
|
|
return(RADIOLIB_ERR_WRONG_MODEM);
|
|
}
|
|
|
|
// set requested packet mode
|
|
int16_t state = setPacketParams(_preambleLength, _crcType, len, headerType);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// update cached value
|
|
_headerType = headerType;
|
|
_implicitLen = len;
|
|
|
|
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 auto-configuration is enabled
|
|
if(_ldroAuto) {
|
|
float symbolLength = (float)(uint32_t(1) << _sf) / (float)_bwKhz;
|
|
RADIOLIB_DEBUG_PRINTLN("Symbol length: %d ms", symbolLength);
|
|
if(symbolLength >= 16.0) {
|
|
_ldro = RADIOLIB_SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_ON;
|
|
} else {
|
|
_ldro = RADIOLIB_SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_OFF;
|
|
}
|
|
} else {
|
|
_ldro = ldro;
|
|
}
|
|
// 500/9/8 - 0x09 0x04 0x03 0x00 - SF9, BW125, 4/8
|
|
// 500/11/8 - 0x0B 0x04 0x03 0x00 - SF11 BW125, 4/7
|
|
uint8_t data[4] = {sf, bw, cr, _ldro};
|
|
return(_mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_MODULATION_PARAMS, data, 4));
|
|
}
|
|
|
|
int16_t SX126x::setModulationParamsFSK(uint32_t br, uint8_t pulseShape, uint8_t rxBw, uint32_t freqDev) {
|
|
uint8_t data[8] = {(uint8_t)((br >> 16) & 0xFF), (uint8_t)((br >> 8) & 0xFF), (uint8_t)(br & 0xFF),
|
|
pulseShape, rxBw,
|
|
(uint8_t)((freqDev >> 16) & 0xFF), (uint8_t)((freqDev >> 8) & 0xFF), (uint8_t)(freqDev & 0xFF)};
|
|
return(_mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_MODULATION_PARAMS, data, 8));
|
|
}
|
|
|
|
int16_t SX126x::setPacketParams(uint16_t preambleLength, uint8_t crcType, uint8_t payloadLength, uint8_t headerType, uint8_t invertIQ) {
|
|
int16_t state = fixInvertedIQ(invertIQ);
|
|
RADIOLIB_ASSERT(state);
|
|
uint8_t data[6] = {(uint8_t)((preambleLength >> 8) & 0xFF), (uint8_t)(preambleLength & 0xFF), headerType, payloadLength, crcType, invertIQ};
|
|
return(_mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_PACKET_PARAMS, data, 6));
|
|
}
|
|
|
|
int16_t SX126x::setPacketParamsFSK(uint16_t preambleLength, uint8_t crcType, uint8_t syncWordLength, uint8_t addrComp, uint8_t whitening, uint8_t packetType, uint8_t payloadLength, uint8_t preambleDetectorLength) {
|
|
uint8_t data[9] = {(uint8_t)((preambleLength >> 8) & 0xFF), (uint8_t)(preambleLength & 0xFF),
|
|
preambleDetectorLength, syncWordLength, addrComp,
|
|
packetType, payloadLength, crcType, whitening};
|
|
return(_mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_PACKET_PARAMS, data, 9));
|
|
}
|
|
|
|
int16_t SX126x::setBufferBaseAddress(uint8_t txBaseAddress, uint8_t rxBaseAddress) {
|
|
uint8_t data[2] = {txBaseAddress, rxBaseAddress};
|
|
return(_mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_BUFFER_BASE_ADDRESS, data, 2));
|
|
}
|
|
|
|
int16_t SX126x::setRegulatorMode(uint8_t mode) {
|
|
uint8_t data[1] = {mode};
|
|
return(_mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_REGULATOR_MODE, data, 1));
|
|
}
|
|
|
|
uint8_t SX126x::getStatus() {
|
|
uint8_t data = 0;
|
|
_mod->SPIreadStream(RADIOLIB_SX126X_CMD_GET_STATUS, &data, 1);
|
|
return(data);
|
|
}
|
|
|
|
uint32_t SX126x::getPacketStatus() {
|
|
uint8_t data[3] = {0, 0, 0};
|
|
_mod->SPIreadStream(RADIOLIB_SX126X_CMD_GET_PACKET_STATUS, data, 3);
|
|
return((((uint32_t)data[0]) << 16) | (((uint32_t)data[1]) << 8) | (uint32_t)data[2]);
|
|
}
|
|
|
|
uint16_t SX126x::getDeviceErrors() {
|
|
uint8_t data[2] = {0, 0};
|
|
_mod->SPIreadStream(RADIOLIB_SX126X_CMD_GET_DEVICE_ERRORS, data, 2);
|
|
uint16_t opError = (((uint16_t)data[0] & 0xFF) << 8) | ((uint16_t)data[1]);
|
|
return(opError);
|
|
}
|
|
|
|
int16_t SX126x::clearDeviceErrors() {
|
|
uint8_t data[2] = {RADIOLIB_SX126X_CMD_NOP, RADIOLIB_SX126X_CMD_NOP};
|
|
return(_mod->SPIwriteStream(RADIOLIB_SX126X_CMD_CLEAR_DEVICE_ERRORS, data, 2));
|
|
}
|
|
|
|
int16_t SX126x::setFrequencyRaw(float freq) {
|
|
// calculate raw value
|
|
uint32_t frf = (freq * (uint32_t(1) << RADIOLIB_SX126X_DIV_EXPONENT)) / RADIOLIB_SX126X_CRYSTAL_FREQ;
|
|
return(setRfFrequency(frf));
|
|
}
|
|
|
|
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(RADIOLIB_SX126X_REG_SENSITIVITY_CONFIG, &sensitivityConfig, 1);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// fix the value for LoRa with 500 kHz bandwidth
|
|
if((getPacketType() == RADIOLIB_SX126X_PACKET_TYPE_LORA) && (fabs(_bwKhz - 500.0) <= 0.001)) {
|
|
sensitivityConfig &= 0xFB;
|
|
} else {
|
|
sensitivityConfig |= 0x04;
|
|
}
|
|
return(writeRegister(RADIOLIB_SX126X_REG_SENSITIVITY_CONFIG, &sensitivityConfig, 1));
|
|
}
|
|
|
|
int16_t SX126x::fixPaClamping(bool enable) {
|
|
// 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(RADIOLIB_SX126X_REG_TX_CLAMP_CONFIG, &clampConfig, 1);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// apply or undo workaround
|
|
if (enable)
|
|
clampConfig |= 0x1E;
|
|
else
|
|
clampConfig = (clampConfig & ~0x1E) | 0x08;
|
|
|
|
return(writeRegister(RADIOLIB_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
|
|
|
|
//check if we're in implicit LoRa mode
|
|
if(!((_headerType == RADIOLIB_SX126X_LORA_HEADER_IMPLICIT) && (getPacketType() == RADIOLIB_SX126X_PACKET_TYPE_LORA))) {
|
|
return(RADIOLIB_ERR_WRONG_MODEM);
|
|
}
|
|
|
|
// stop RTC counter
|
|
uint8_t rtcStop = 0x00;
|
|
int16_t state = writeRegister(RADIOLIB_SX126X_REG_RTC_CTRL, &rtcStop, 1);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// read currently active event
|
|
uint8_t rtcEvent = 0;
|
|
state = readRegister(RADIOLIB_SX126X_REG_EVENT_MASK, &rtcEvent, 1);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// clear events
|
|
rtcEvent |= 0x02;
|
|
return(writeRegister(RADIOLIB_SX126X_REG_EVENT_MASK, &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(RADIOLIB_SX126X_REG_IQ_CONFIG, &iqConfigCurrent, 1);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// set correct IQ configuration
|
|
if(iqConfig == RADIOLIB_SX126X_LORA_IQ_INVERTED) {
|
|
iqConfigCurrent &= 0xFB;
|
|
} else {
|
|
iqConfigCurrent |= 0x04;
|
|
}
|
|
|
|
// update with the new value
|
|
return(writeRegister(RADIOLIB_SX126X_REG_IQ_CONFIG, &iqConfigCurrent, 1));
|
|
}
|
|
|
|
int16_t SX126x::config(uint8_t modem) {
|
|
// reset buffer base address
|
|
int16_t state = setBufferBaseAddress();
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// set modem
|
|
uint8_t data[7];
|
|
data[0] = modem;
|
|
state = _mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_PACKET_TYPE, data, 1);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// set Rx/Tx fallback mode to STDBY_RC
|
|
data[0] = RADIOLIB_SX126X_RX_TX_FALLBACK_MODE_STDBY_RC;
|
|
state = _mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_RX_TX_FALLBACK_MODE, data, 1);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// set some CAD parameters - will be overwritten whel calling CAD anyway
|
|
data[0] = RADIOLIB_SX126X_CAD_ON_8_SYMB;
|
|
data[1] = _sf + 13;
|
|
data[2] = RADIOLIB_SX126X_CAD_PARAM_DET_MIN;
|
|
data[3] = RADIOLIB_SX126X_CAD_GOTO_STDBY;
|
|
data[4] = 0x00;
|
|
data[5] = 0x00;
|
|
data[6] = 0x00;
|
|
state = _mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_CAD_PARAMS, data, 7);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// clear IRQ
|
|
state = clearIrqStatus();
|
|
state |= setDioIrqParams(RADIOLIB_SX126X_IRQ_NONE, RADIOLIB_SX126X_IRQ_NONE);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// calibrate all blocks
|
|
data[0] = RADIOLIB_SX126X_CALIBRATE_ALL;
|
|
state = _mod->SPIwriteStream(RADIOLIB_SX126X_CMD_CALIBRATE, data, 1, true, false);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// wait for calibration completion
|
|
_mod->hal->delay(5);
|
|
while(_mod->hal->digitalRead(_mod->getGpio())) {
|
|
_mod->hal->yield();
|
|
}
|
|
|
|
// check calibration result
|
|
state = _mod->SPIcheckStream();
|
|
|
|
// if something failed, show the device errors
|
|
#if defined(RADIOLIB_DEBUG)
|
|
if(state != RADIOLIB_ERR_NONE) {
|
|
// unless mode is forced to standby, device errors will be 0
|
|
standby();
|
|
uint16_t errors = getDeviceErrors();
|
|
RADIOLIB_DEBUG_PRINTLN("Calibration failed, device errors: 0x%X", errors);
|
|
}
|
|
#endif
|
|
|
|
return(state);
|
|
}
|
|
|
|
int16_t SX126x::SPIparseStatus(uint8_t in) {
|
|
if((in & 0b00001110) == RADIOLIB_SX126X_STATUS_CMD_TIMEOUT) {
|
|
return(RADIOLIB_ERR_SPI_CMD_TIMEOUT);
|
|
} else if((in & 0b00001110) == RADIOLIB_SX126X_STATUS_CMD_INVALID) {
|
|
return(RADIOLIB_ERR_SPI_CMD_INVALID);
|
|
} else if((in & 0b00001110) == RADIOLIB_SX126X_STATUS_CMD_FAILED) {
|
|
return(RADIOLIB_ERR_SPI_CMD_FAILED);
|
|
} else if((in == 0x00) || (in == 0xFF)) {
|
|
return(RADIOLIB_ERR_CHIP_NOT_FOUND);
|
|
}
|
|
return(RADIOLIB_ERR_NONE);
|
|
}
|
|
|
|
bool SX126x::findChip(const char* verStr) {
|
|
uint8_t i = 0;
|
|
bool flagFound = false;
|
|
while((i < 10) && !flagFound) {
|
|
// reset the module
|
|
reset();
|
|
|
|
// read the version string
|
|
char version[16];
|
|
_mod->SPIreadRegisterBurst(RADIOLIB_SX126X_REG_VERSION_STRING, 16, (uint8_t*)version);
|
|
|
|
// check version register
|
|
if(strncmp(verStr, version, 6) == 0) {
|
|
RADIOLIB_DEBUG_PRINTLN("Found SX126x: RADIOLIB_SX126X_REG_VERSION_STRING:");
|
|
_mod->hexdump((uint8_t*)version, 16, RADIOLIB_SX126X_REG_VERSION_STRING);
|
|
RADIOLIB_DEBUG_PRINTLN();
|
|
flagFound = true;
|
|
} else {
|
|
#if defined(RADIOLIB_DEBUG)
|
|
RADIOLIB_DEBUG_PRINTLN("SX126x not found! (%d of 10 tries) RADIOLIB_SX126X_REG_VERSION_STRING:", i + 1);
|
|
_mod->hexdump((uint8_t*)version, 16, RADIOLIB_SX126X_REG_VERSION_STRING);
|
|
RADIOLIB_DEBUG_PRINTLN("Expected string: %s", verStr);
|
|
#endif
|
|
_mod->hal->delay(10);
|
|
i++;
|
|
}
|
|
}
|
|
|
|
return(flagFound);
|
|
}
|
|
|
|
#endif
|