diff --git a/examples/RTTY/RTTY_Transmit/RTTY_Transmit.ino b/examples/RTTY/RTTY_Transmit/RTTY_Transmit.ino index e49a5560..9199858b 100644 --- a/examples/RTTY/RTTY_Transmit/RTTY_Transmit.ino +++ b/examples/RTTY/RTTY_Transmit/RTTY_Transmit.ino @@ -9,6 +9,7 @@ - RF69 - SX1231 - CC1101 + - SX1262/68 */ // include the library @@ -48,6 +49,7 @@ void setup() { // SX127x - 61 Hz // RF69 - 61 Hz // CC1101 - 397 Hz + // SX126x - 1 Hz Serial.print(F("[RTTY] Initializing ... ")); // low ("space") frequency: 434.0 MHz // frequency shift: 183 Hz diff --git a/src/modules/SX1262.cpp b/src/modules/SX1262.cpp index 9aad7281..e46a3431 100644 --- a/src/modules/SX1262.cpp +++ b/src/modules/SX1262.cpp @@ -31,6 +31,33 @@ int16_t SX1262::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint16_t syn return(state); } +int16_t SX1262::beginFSK(float freq, float br, float freqDev, float rxBw, int8_t power, float currentLimit, uint16_t preambleLength, float dataShaping) { + // execute common part + int16_t state = SX126x::beginFSK(br, freqDev, rxBw, preambleLength, dataShaping); + if(state != ERR_NONE) { + return(state); + } + + // configure publicly accessible settings + state = setFrequency(freq); + if(state != ERR_NONE) { + return(state); + } + + state = setOutputPower(power); + if(state != ERR_NONE) { + return(state); + } + + // OCP must be configured after PA + state = SX126x::setCurrentLimit(currentLimit); + if(state != ERR_NONE) { + return(state); + } + + return(state); +} + int16_t SX1262::setFrequency(float freq) { // check frequency range if((freq < 150.0) || (freq > 960.0)) { diff --git a/src/modules/SX1262.h b/src/modules/SX1262.h index eee52084..79321db5 100644 --- a/src/modules/SX1262.h +++ b/src/modules/SX1262.h @@ -16,6 +16,7 @@ class SX1262: public SX126x { // basic methods int16_t begin(float freq = 434.0, float bw = 125.0, uint8_t sf = 9, uint8_t cr = 7, uint16_t syncWord = SX126X_SYNC_WORD_PRIVATE, int8_t power = 14, float currentLimit = 60.0, uint16_t preambleLength = 8); + int16_t beginFSK(float freq = 434.0, float br = 48.0, float freqDev = 50.0, float rxBw = 117.3, int8_t power = 14, float currentLimit = 60.0, uint16_t preambleLength = 8, float dataShaping = 0.5); // configuration methods int16_t setFrequency(float freq); diff --git a/src/modules/SX126x.cpp b/src/modules/SX126x.cpp index a99c6a7a..13e5f6d9 100644 --- a/src/modules/SX126x.cpp +++ b/src/modules/SX126x.cpp @@ -13,7 +13,7 @@ int16_t SX126x::begin(float bw, uint8_t sf, uint8_t cr, uint16_t syncWord, uint1 _bwKhz = bw; _sf = sf; - // initialize dummy configuration variables + // initialize configuration variables (will be overwritten during public settings configuration) _bw = SX126X_LORA_BW_125_0; _cr = SX126X_LORA_CR_4_7; _ldro = 0x00; @@ -59,9 +59,69 @@ int16_t SX126x::begin(float bw, uint8_t sf, uint8_t cr, uint16_t syncWord, uint1 return(state); } +int16_t SX126x::beginFSK(float br, float freqDev, float rxBw, uint16_t preambleLength, float dataShaping) { + // set module properties + _mod->init(USE_SPI, INT_BOTH); + pinMode(_mod->getRx(), INPUT); + + // initialize configuration variables (will be overwritten during public settings configuration) + _br = 21333; + _freqDev = 52429; + _rxBw = SX126X_GFSK_RX_BW_117_3; + _pulseShape = SX126X_GFSK_FILTER_GAUSS_0_5; + _crcTypeFSK = SX126X_GFSK_CRC_1_BYTE; + _preambleLengthFSK = preambleLength; + + // get status and errors + getStatus(); + getDeviceErrors(); + + // set mode to standby + standby(); + + // configure settings not accessible by API + configFSK(); + + // configure publicly accessible settings + int16_t state = setBitRate(br); + if(state != ERR_NONE) { + return(state); + } + + state = setFrequencyDeviation(freqDev); + if(state != ERR_NONE) { + return(state); + } + + state = setRxBandwidth(rxBw); + if(state != ERR_NONE) { + return(state); + } + + state = setDataShaping(dataShaping); + if(state != ERR_NONE) { + return(state); + } + + state = setPreambleLength(preambleLength); + if(state != ERR_NONE) { + return(state); + } + + // set default sync word 0x2D01 - not a beginFSK attribute + uint8_t sync[] = {0x2D, 0x01}; + _syncWordLength = 2; + state = setSyncWord(sync, _syncWordLength); + if(state != ERR_NONE) { + return(state); + } + + return(state); +} + int16_t SX126x::transmit(uint8_t* data, size_t len, uint8_t addr) { // set mode to standby - int16_t state = standby(); + standby(); // check packet length if(len >= 256) { @@ -74,7 +134,7 @@ int16_t SX126x::transmit(uint8_t* data, size_t len, uint8_t addr) { uint8_t modem = getPacketType(); if(modem == SX126X_PACKET_TYPE_LORA) { // calculate timeout (150% of expected time-on-air) - float symbolLength = (float)(uint32_t(1) << _sf) / (float)_bwKhz; + float symbolLength = (float)((uint32_t)(1) << _sf) / (float)_bwKhz; float sfCoeff1 = 4.25; float sfCoeff2 = 8.0; if(_sf == 5 || _sf == 6) { @@ -141,7 +201,7 @@ int16_t SX126x::transmit(uint8_t* data, size_t len, uint8_t addr) { int16_t SX126x::receive(uint8_t* data, size_t len) { // set mode to standby - int16_t state = standby(); + standby(); uint32_t timeout = 0; @@ -340,9 +400,164 @@ int16_t SX126x::setCurrentLimit(float currentLimit) { } int16_t SX126x::setPreambleLength(uint16_t preambleLength) { + uint8_t modem = getPacketType(); + if(modem == SX126X_PACKET_TYPE_LORA) { + _preambleLength = preambleLength; + setPacketParams(_preambleLength, _crcType); + return(ERR_NONE); + } else if(modem == SX126X_PACKET_TYPE_GFSK) { + _preambleLengthFSK = preambleLength; + setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength); + return(ERR_NONE); + } + + return(ERR_UNKNOWN); +} + +int16_t SX126x::setFrequencyDeviation(float freqDev) { + // check active modem + if(getPacketType() != SX126X_PACKET_TYPE_GFSK) { + return(ERR_WRONG_MODEM); + } + + // check alowed frequency deviation values + if(!(freqDev <= 200.0)) { + return(ERR_INVALID_FREQUENCY_DEVIATION); + } + + // calculate raw frequency deviation value + _freqDev = (uint32_t)((freqDev * 1000.0) / ((SX126X_CRYSTAL_FREQ * 1000000.0) / (float)((uint32_t)(1) << 25))); + + // update modulation parameters + setModulationParamsFSK(_br, _pulseShape, _rxBw, _freqDev); + + return(ERR_NONE); +} + +int16_t SX126x::setBitRate(float br) { + // check active modem + if(getPacketType() != SX126X_PACKET_TYPE_GFSK) { + return(ERR_WRONG_MODEM); + } + + // check alowed bit rate values + if(!((br >= 0.6) && (br <= 300.0))) { + return(ERR_INVALID_BIT_RATE); + } + + // calculate raw bit rate value + _br = (uint32_t)((SX126X_CRYSTAL_FREQ * 1000000.0 * 32.0) / (br * 1000.0)); + + // update modulation parameters + setModulationParamsFSK(_br, _pulseShape, _rxBw, _freqDev); + + return(ERR_NONE); +} + +int16_t SX126x::setRxBandwidth(float rxBw) { + // check active modem + if(getPacketType() != SX126X_PACKET_TYPE_GFSK) { + return(ERR_WRONG_MODEM); + } + + // check alowed receiver bandwidth values + if(abs(rxBw - 4.8) <= 0.001) { + _rxBw = SX126X_GFSK_RX_BW_4_8; + } else if(abs(rxBw - 5.8) <= 0.001) { + _rxBw = SX126X_GFSK_RX_BW_5_8; + } else if(abs(rxBw - 7.3) <= 0.001) { + _rxBw = SX126X_GFSK_RX_BW_7_3; + } else if(abs(rxBw - 9.7) <= 0.001) { + _rxBw = SX126X_GFSK_RX_BW_9_7; + } else if(abs(rxBw - 11.7) <= 0.001) { + _rxBw = SX126X_GFSK_RX_BW_11_7; + } else if(abs(rxBw - 14.6) <= 0.001) { + _rxBw = SX126X_GFSK_RX_BW_14_6; + } else if(abs(rxBw - 19.5) <= 0.001) { + _rxBw = SX126X_GFSK_RX_BW_19_5; + } else if(abs(rxBw - 23.4) <= 0.001) { + _rxBw = SX126X_GFSK_RX_BW_23_4; + } else if(abs(rxBw - 29.3) <= 0.001) { + _rxBw = SX126X_GFSK_RX_BW_29_3; + } else if(abs(rxBw - 39.0) <= 0.001) { + _rxBw = SX126X_GFSK_RX_BW_39_0; + } else if(abs(rxBw - 46.9) <= 0.001) { + _rxBw = SX126X_GFSK_RX_BW_46_9; + } else if(abs(rxBw - 58.6) <= 0.001) { + _rxBw = SX126X_GFSK_RX_BW_58_6; + } else if(abs(rxBw - 78.2) <= 0.001) { + _rxBw = SX126X_GFSK_RX_BW_78_2; + } else if(abs(rxBw - 93.8) <= 0.001) { + _rxBw = SX126X_GFSK_RX_BW_93_8; + } else if(abs(rxBw - 117.3) <= 0.001) { + _rxBw = SX126X_GFSK_RX_BW_117_3; + } else if(abs(rxBw - 156.2) <= 0.001) { + _rxBw = SX126X_GFSK_RX_BW_156_2; + } else if(abs(rxBw - 187.2) <= 0.001) { + _rxBw = SX126X_GFSK_RX_BW_187_2; + } else if(abs(rxBw - 234.3) <= 0.001) { + _rxBw = SX126X_GFSK_RX_BW_234_3; + } else if(abs(rxBw - 312.0) <= 0.001) { + _rxBw = SX126X_GFSK_RX_BW_312_0; + } else if(abs(rxBw - 373.6) <= 0.001) { + _rxBw = SX126X_GFSK_RX_BW_373_6; + } else if(abs(rxBw - 467.0) <= 0.001) { + _rxBw = SX126X_GFSK_RX_BW_467_0; + } else { + return(ERR_INVALID_RX_BANDWIDTH); + } + + // update modulation parameters + setModulationParamsFSK(_br, _pulseShape, _rxBw, _freqDev); + + return(ERR_NONE); +} + +int16_t SX126x::setDataShaping(float sh) { + // check active modem + if(getPacketType() != SX126X_PACKET_TYPE_GFSK) { + return(ERR_WRONG_MODEM); + } + + // check allowed values + if(abs(sh - 0.0) <= 0.001) { + _pulseShape = SX126X_GFSK_FILTER_NONE; + } else if(abs(sh - 0.3) <= 0.001) { + _pulseShape = SX126X_GFSK_FILTER_GAUSS_0_3; + } else if(abs(sh - 0.5) <= 0.001) { + _pulseShape = SX126X_GFSK_FILTER_GAUSS_0_5; + } else if(abs(sh - 0.7) <= 0.001) { + _pulseShape = SX126X_GFSK_FILTER_GAUSS_0_7; + } else if(abs(sh - 1.0) <= 0.001) { + _pulseShape = SX126X_GFSK_FILTER_GAUSS_1; + } else { + return(ERR_INVALID_DATA_SHAPING); + } + + // update modulation parameters + setModulationParamsFSK(_br, _pulseShape, _rxBw, _freqDev); + + return(ERR_NONE); +} + +int16_t SX126x::setSyncWord(uint8_t* syncWord, uint8_t len) { + // check active modem + if(getPacketType() != SX126X_PACKET_TYPE_GFSK) { + return(ERR_WRONG_MODEM); + } + + // check sync word Length + if(len > 8) { + return(ERR_INVALID_SYNC_WORD); + } + + // write sync word + writeRegister(SX126X_REG_SYNC_WORD_0, syncWord, len); + // update packet parameters - _preambleLength = preambleLength; - setPacketParams(_preambleLength, _crcType); + _syncWordLength = len; + setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength); + return(ERR_NONE); } @@ -350,11 +565,6 @@ float SX126x::getDataRate() { return(_dataRate); } -int16_t SX126x::setFrequencyDeviation(float freqDev) { - - return(ERR_NONE); -} - float SX126x::getRSSI() { // check active modem if(getPacketType() != SX126X_PACKET_TYPE_LORA) { @@ -477,11 +687,25 @@ void SX126x::setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, uint8_t ldr SPIwriteCommand(SX126X_CMD_SET_MODULATION_PARAMS, data, 4); } +void 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)}; + SPIwriteCommand(SX126X_CMD_SET_MODULATION_PARAMS, data, 8); +} + void SX126x::setPacketParams(uint16_t preambleLength, uint8_t crcType, uint8_t payloadLength, uint8_t headerType, uint8_t invertIQ) { uint8_t data[6] = {(uint8_t)((preambleLength >> 8) & 0xFF), (uint8_t)(preambleLength & 0xFF), headerType, payloadLength, crcType, invertIQ}; SPIwriteCommand(SX126X_CMD_SET_PACKET_PARAMS, data, 6); } +void SX126x::setPacketParamsFSK(uint16_t preambleLength, uint8_t crcType, uint8_t syncWordLength, uint8_t payloadLength, uint8_t packetType, uint8_t addrComp, uint8_t preambleDetectorLength, uint8_t whitening) { + uint8_t data[9] = {(uint8_t)((preambleLength >> 8) & 0xFF), (uint8_t)(preambleLength & 0xFF), + preambleDetectorLength, syncWordLength, addrComp, + packetType, payloadLength, crcType, whitening}; + SPIwriteCommand(SX126X_CMD_SET_MODULATION_PARAMS, data, 9); +} + void SX126x::setBufferBaseAddress(uint8_t txBaseAddress, uint8_t rxBaseAddress) { uint8_t data[2] = {txBaseAddress, rxBaseAddress}; SPIwriteCommand(SX126X_CMD_SET_BUFFER_BASE_ADDRESS, data, 2); @@ -582,6 +806,36 @@ int16_t SX126x::config() { return(ERR_NONE); } +int16_t SX126x::configFSK() { + // set DIO2 as IRQ + uint8_t* data = new uint8_t[1]; + data[0] = SX126X_DIO2_AS_IRQ; + SPIwriteCommand(SX126X_DIO2_AS_RF_SWITCH, data, 1); + + // set regulator mode + data[0] = SX126X_REGULATOR_DC_DC; + SPIwriteCommand(SX126X_CMD_SET_REGULATOR_MODE, data, 1); + + // reset buffer base address + setBufferBaseAddress(); + + // set FSK mode + data[0] = SX126X_PACKET_TYPE_GFSK; + SPIwriteCommand(SX126X_CMD_SET_PACKET_TYPE, data, 1); + + // set Rx/Tx fallback mode to STDBY_RC + data[0] = SX126X_RX_TX_FALLBACK_MODE_STDBY_RC; + SPIwriteCommand(SX126X_CMD_SET_RX_TX_FALLBACK_MODE, data, 1); + + // clear IRQ + clearIrqStatus(); + setDioIrqParams(SX126X_IRQ_NONE, SX126X_IRQ_NONE); + + delete[] data; + + return(ERR_NONE); +} + void SX126x::SPIwriteCommand(uint8_t cmd, uint8_t* data, uint8_t numBytes, bool waitForBusy) { SX126x::SPItransfer(cmd, true, data, NULL, numBytes, waitForBusy); } diff --git a/src/modules/SX126x.h b/src/modules/SX126x.h index 8f50bbdb..38a98dc9 100644 --- a/src/modules/SX126x.h +++ b/src/modules/SX126x.h @@ -219,7 +219,7 @@ #define SX126X_GFSK_RX_BW_117_3 0x0B // 7 0 117.3 kHz #define SX126X_GFSK_RX_BW_156_2 0x1A // 7 0 156.2 kHz #define SX126X_GFSK_RX_BW_187_2 0x12 // 7 0 187.2 kHz -#define SX126X_GFSK_RX_BW_232_3 0x0A // 7 0 232.3 kHz +#define SX126X_GFSK_RX_BW_234_3 0x0A // 7 0 234.3 kHz #define SX126X_GFSK_RX_BW_312_0 0x19 // 7 0 312.0 kHz #define SX126X_GFSK_RX_BW_373_6 0x11 // 7 0 373.6 kHz #define SX126X_GFSK_RX_BW_467_0 0x09 // 7 0 467.0 kHz @@ -324,6 +324,7 @@ class SX126x: public PhysicalLayer { // basic methods int16_t begin(float bw, uint8_t sf, uint8_t cr, uint16_t syncWord, uint16_t preambleLength); + int16_t beginFSK(float br, float freqDev, float rxBw, uint16_t preambleLength, float dataShaping); int16_t transmit(uint8_t* data, size_t len, uint8_t addr = 0); int16_t receive(uint8_t* data, size_t len); int16_t transmitDirect(uint32_t frf = 0); @@ -338,8 +339,12 @@ class SX126x: public PhysicalLayer { int16_t setSyncWord(uint16_t syncWord); int16_t setCurrentLimit(float currentLimit); int16_t setPreambleLength(uint16_t preambleLength); - float getDataRate(); int16_t setFrequencyDeviation(float freqDev); + int16_t setBitRate(float br); + int16_t setRxBandwidth(float rxBw); + int16_t setDataShaping(float sh); + int16_t setSyncWord(uint8_t* syncWord, uint8_t len); + float getDataRate(); float getRSSI(); float getSNR(); @@ -359,7 +364,9 @@ class SX126x: public PhysicalLayer { uint8_t getPacketType(); void setTxParams(uint8_t power, uint8_t rampTime = SX126X_PA_RAMP_200U); void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, uint8_t ldro = 0xFF); + void setModulationParamsFSK(uint32_t br, uint8_t pulseShape, uint8_t rxBw, uint32_t freqDev); void 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); + void setPacketParamsFSK(uint16_t preambleLength, uint8_t crcType, uint8_t syncWordLength, uint8_t payloadLength = 0xFF, uint8_t packetType = SX126X_GFSK_PACKET_VARIABLE, uint8_t addrComp = SX126X_GFSK_ADDRESS_FILT_OFF, uint8_t preambleDetectorLength = SX126X_GFSK_PREAMBLE_DETECT_8, uint8_t whitening = SX126X_GFSK_WHITENING_ON); void setBufferBaseAddress(uint8_t txBaseAddress = 0x00, uint8_t rxBaseAddress = 0x00); uint8_t getStatus(); uint32_t getPacketStatus(); @@ -375,9 +382,14 @@ class SX126x: public PhysicalLayer { uint16_t _preambleLength; float _bwKhz; + uint32_t _br, _freqDev; + uint8_t _rxBw, _pulseShape, _crcTypeFSK, _syncWordLength; + uint16_t _preambleLengthFSK; + float _dataRate; int16_t config(); + int16_t configFSK(); // common low-level SPI interface void SPIwriteCommand(uint8_t cmd, uint8_t* data, uint8_t numBytes, bool waitForBusy = true);