#ifndef RF_SX1276 #define RF_SX1276 /* // registers #define REG_FIFO 0x00 #define REG_OP_MODE 0x01 #define REG_FRF_MSB 0x06 #define REG_FRF_MID 0x07 #define REG_FRF_LSB 0x08 #define REG_PA_CONFIG 0x09 #define REG_OCP 0x0b #define REG_LNA 0x0c #define REG_FIFO_ADDR_PTR 0x0d #define REG_FIFO_TX_BASE_ADDR 0x0e #define REG_FIFO_RX_BASE_ADDR 0x0f #define REG_FIFO_RX_CURRENT_ADDR 0x10 #define REG_IRQ_FLAGS 0x12 #define REG_RX_NB_BYTES 0x13 #define REG_PKT_SNR_VALUE 0x19 #define REG_PKT_RSSI_VALUE 0x1a #define REG_MODEM_CONFIG_1 0x1d #define REG_MODEM_CONFIG_2 0x1e #define REG_PREAMBLE_MSB 0x20 #define REG_PREAMBLE_LSB 0x21 #define REG_PAYLOAD_LENGTH 0x22 #define REG_MODEM_CONFIG_3 0x26 #define REG_FREQ_ERROR_MSB 0x28 #define REG_FREQ_ERROR_MID 0x29 #define REG_FREQ_ERROR_LSB 0x2a #define REG_RSSI_WIDEBAND 0x2c #define REG_DETECTION_OPTIMIZE 0x31 #define REG_INVERTIQ 0x33 #define REG_DETECTION_THRESHOLD 0x37 #define REG_SYNC_WORD 0x39 #define REG_INVERTIQ2 0x3b #define REG_DIO_MAPPING_1 0x40 #define REG_VERSION 0x42 #define REG_PA_DAC 0x4d // modes #define MODE_LONG_RANGE_MODE 0x80 #define MODE_SLEEP 0x00 #define MODE_STDBY 0x01 #define MODE_TX 0x03 #define MODE_RX_CONTINUOUS 0x05 #define MODE_RX_SINGLE 0x06 // PA config #define PA_BOOST 0x80 // IRQ masks #define IRQ_TX_DONE_MASK 0x08 #define IRQ_PAYLOAD_CRC_ERROR_MASK 0x20 #define IRQ_RX_DONE_MASK 0x40 #define MAX_PKT_LENGTH 255 #if (ESP8266 || ESP32) #define ISR_PREFIX ICACHE_RAM_ATTR #else #define ISR_PREFIX #endif */ #include "../../Debug.h" template class SX1276 { SPI& spi; static constexpr const uint8_t REG_FIFO = 0x00; static constexpr const uint8_t REG_OP_MODE = 0x01; static constexpr const uint8_t REG_FRF_MSB = 0x06; static constexpr const uint8_t REG_FRF_MID = 0x07; static constexpr const uint8_t REG_FRF_LSB = 0x08; static constexpr const uint8_t REG_PA_CONFIG = 0x09; static constexpr const uint8_t REG_OCP = 0x0b; static constexpr const uint8_t REG_LNA = 0x0c; static constexpr const uint8_t REG_FIFO_ADDR_PTR = 0x0d; static constexpr const uint8_t REG_FIFO_TX_BASE_ADDR = 0x0e; static constexpr const uint8_t REG_FIFO_RX_BASE_ADDR = 0x0f; static constexpr const uint8_t REG_FIFO_RX_CURRENT_ADDR = 0x10; static constexpr const uint8_t REG_IRQ_FLAGS = 0x12; static constexpr const uint8_t REG_RX_NB_BYTES = 0x13; static constexpr const uint8_t REG_PKT_SNR_VALUE = 0x19; static constexpr const uint8_t REG_PKT_RSSI_VALUE = 0x1a; static constexpr const uint8_t REG_MODEM_CONFIG_1 = 0x1d; static constexpr const uint8_t REG_MODEM_CONFIG_2 = 0x1e; static constexpr const uint8_t REG_PAYLOAD_LENGTH = 0x22; static constexpr const uint8_t REG_MODEM_CONFIG_3 = 0x26; static constexpr const uint8_t REG_DETECTION_OPTIMIZE = 0x31; static constexpr const uint8_t REG_DETECTION_THRESHOLD =0x37; static constexpr const uint8_t REG_VERSION = 0x42; static constexpr const uint8_t REG_PA_DAC = 0x4d; // modes static constexpr const uint8_t MODE_LONG_RANGE_MODE = 0x80; static constexpr const uint8_t MODE_SLEEP = 0x00; static constexpr const uint8_t MODE_STDBY = 0x01; static constexpr const uint8_t MODE_TX = 0x03; static constexpr const uint8_t MODE_RX_CONTINUOUS = 0x05; static constexpr const uint8_t MODE_RX_SINGLE = 0x06; // PA config static constexpr const uint8_t PA_BOOST = 0x80; static constexpr const uint8_t PA_OUTPUT_RFO_PIN = 0; static constexpr const uint8_t PA_OUTPUT_PA_BOOST_PIN = 1; // IRQ marks static constexpr const uint8_t IRQ_TX_DONE_MASK = 0x08; static constexpr const uint8_t IRQ_PAYLOAD_CRC_ERROR_MASK = 0x20; static constexpr const uint8_t IRQ_RX_DONE_MASK = 0x40; static constexpr const uint8_t MAX_PKT_LENGTH = 255; static constexpr const char* NAME = "LoRa"; uint32_t curFreq = 0; public: struct PacketDetails { int8_t rssi; float snr; }; public: SX1276(SPI& spi) : spi(spi) { MyGPIO::setOutput(PIN_SLAVE_SEL); MyGPIO::setOutput(PIN_RESET); reset(); check(); init(); } void init() { debugMod(NAME, "init()"); // sleep mode sleep(); // set the RF frequency setFrequency(866E6); // set base address writeRegister(REG_FIFO_TX_BASE_ADDR, 0); writeRegister(REG_FIFO_RX_BASE_ADDR, 0); // set LNA boost writeRegister(REG_LNA, readRegister(REG_LNA) | 0x03); // set auto AGC writeRegister(REG_MODEM_CONFIG_3, 0x04); // set output power to 17 dBm setTxPower(17); // put in standby mode idle(); debugMod(NAME, "init complete"); } // void setUseCRC(bool use) { // if (use) { // writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) | 0x04); // } else { // writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) & 0xfb); // } // } /** get the currently configured spreading factor */ int getSpreadingFactor() { return readRegister(REG_MODEM_CONFIG_2) >> 4; } /** * set the spreading factor to use * BEWARE!! BOTH sides must agree on the same spreading factor or the transmission will fail!! * 6 requires implicit header mode (you are responsible for sending the header) * 7 = lowest quality, fastest * 12 = highest quality, slowest */ void setSpreadingFactor(int sf) { if (sf < 6 || sf > 12) {debugMod1(NAME, "setSpreadingFactor(%d) out of range", sf); return;} if (sf == 6) { writeRegister(REG_DETECTION_OPTIMIZE, 0xc5); writeRegister(REG_DETECTION_THRESHOLD, 0x0c); } else { writeRegister(REG_DETECTION_OPTIMIZE, 0xc3); writeRegister(REG_DETECTION_THRESHOLD, 0x0a); } writeRegister(REG_MODEM_CONFIG_2, (readRegister(REG_MODEM_CONFIG_2) & 0x0f) | ((sf << 4) & 0xf0)); setLdoFlag(); } /** get the signal's transmit bandwidth (in Hz) */ uint32_t getSignalBandwidth() { const uint8_t bw = (readRegister(REG_MODEM_CONFIG_1) >> 4); switch (bw) { case 0: return 7.8E3; case 1: return 10.4E3; case 2: return 15.6E3; case 3: return 20.8E3; case 4: return 31.25E3; case 5: return 41.7E3; case 6: return 62.5E3; case 7: return 125E3; case 8: return 250E3; case 9: return 500E3; default: return -1; } } /** * set the FEC (Forward Error Correction) (see Table14) * available values are: 4/5, 4/6, 4/7, 4/8 * the receiver is not required to know the FEC of the sender, as this value is transmitted within the header, which is always sent with 4/8 */ void setCodingRate4(int denominator) { if (denominator < 5 || denominator > 8) {debugMod1(NAME, "setCodingRate4(%d) out of range", denominator); return;} const int cr = denominator - 4; writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0xf1) | (cr << 1)); } /** get the current FEC denominator 4/x */ int getCodingRate4() { return ((readRegister(REG_MODEM_CONFIG_1) & 0b1110) >> 1) + 4; } /** calculate the current data-rate based on spreading-factor and bandwidth */ int16_t getDataRate() { // https://blog.dbrgn.ch/2017/6/23/lorawan-data-rates/ const int sf = getSpreadingFactor(); const int bw = getSignalBandwidth(); if (bw == 125000) { if (sf == 7) {return 5470;} if (sf == 8) {return 3125;} if (sf == 9) {return 1760;} if (sf == 10) {return 980;} if (sf == 11) {return 440;} if (sf == 12) {return 250;} } return -1; } /** switch to idle mode */ void idle() { debugMod(NAME, "idle()"); writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_STDBY); } /** switch to sleep mode */ void sleep() { debugMod(NAME, "sleep()"); writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_SLEEP); } bool isTransmitting() { if ((readRegister(REG_OP_MODE) & MODE_TX) == MODE_TX) {return true;} if (readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) {writeRegister(REG_IRQ_FLAGS, IRQ_TX_DONE_MASK);} // clear TX-done IRQ flag return false; } /** send the given data */ int send(const uint8_t* data, uint8_t len, bool async) { debugMod1(NAME, "send(%d bytes)", len); //if (len > MAX_PKT_LENGTH) {return -1;} if (isTransmitting()) {return 0;} // put in standby mode idle(); //if (implicitHeader) { // implicitHeaderMode(); //} else { explicitHeaderMode(); //} // reset FIFO address and payload length writeRegister(REG_FIFO_ADDR_PTR, 0); writeRegister(REG_PAYLOAD_LENGTH, 0); // write data into FIFO debugMod(NAME, "writing FIFO"); for (uint16_t i = 0; i < len; ++i) {writeRegister(REG_FIFO, data[i]);} writeRegister(REG_PAYLOAD_LENGTH, len); //if ((async) && (_onTxDone)) writeRegister(REG_DIO_MAPPING_1, 0x40); // DIO0 => TXDONE // put in TX mode debugMod(NAME, "starting TX"); writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_TX); if (!async) { debugMod(NAME, "waiting"); while ((readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) == 0) {yield();} // wait for TX done writeRegister(REG_IRQ_FLAGS, IRQ_TX_DONE_MASK); // clear IRQ TX-done flag debugMod(NAME, "done"); } return len; } /** switch to RX-mode. either for a single frame or permanent */ void setRX(bool single) { // reset FIFO address writeRegister(REG_FIFO_ADDR_PTR, 0); // put in single RX mode if (single) { writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_SINGLE); } else { writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_CONTINUOUS); } } /** fetch one received packet, if any is available. requires setRX(..) beforehand to actually receive something! */ int read(uint8_t* dst, PacketDetails* det) { uint8_t irqFlags = readRegister(REG_IRQ_FLAGS); explicitHeaderMode(); // clear all current IRQ flags writeRegister(REG_IRQ_FLAGS, irqFlags); // got something? if ((irqFlags & IRQ_RX_DONE_MASK) && (irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) { // read packet length uint8_t packetLength = readRegister(REG_RX_NB_BYTES); // set FIFO address to current RX address writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR)); // read the packet for (uint16_t i = 0; i < packetLength; ++i) { dst[i] = readRegister(REG_FIFO); } // read the details? if (det) { det->rssi = (readRegister(REG_PKT_RSSI_VALUE) - (curFreq < 868E6 ? 164 : 157)); det->snr = ((int8_t)readRegister(REG_PKT_SNR_VALUE)) * 0.25f; } // put in standby mode //idle(); return packetLength; } // nothing available return 0; } /** debug output */ void debugDump() { printf("SX1276: spreadingFactor: %d, bandwidth: %d Hz, dataRate: %d bit/s FEC: 4/%d \n", getSpreadingFactor(), getSignalBandwidth(), getDataRate(), getCodingRate4()); } private: void yield() { usleep(5000); } void explicitHeaderMode() { writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) & 0xfe); } void implicitHeaderMode() { writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) | 0x01); } // uint8_t getMode() { // const uint8_t mode = readRegister(REG_OP_MODE); // debugMod1(NAME, "mode: %d", mode); // return mode; // } /** set the RF frequency to use (in Hz) */ void setFrequency(uint32_t hz) { this->curFreq = hz; debugMod1(NAME, "setFrequency(%d Hz)", (int)hz); const uint64_t frf = ((uint64_t)hz << 19) / 32000000; writeRegister(REG_FRF_MSB, (uint8_t)(frf >> 16)); writeRegister(REG_FRF_MID, (uint8_t)(frf >> 8)); writeRegister(REG_FRF_LSB, (uint8_t)(frf >> 0)); } /** configure the TX power (in dB). but what is the pin for??? */ void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN) { debugMod1(NAME, "setTx(%d dB)", level); if (PA_OUTPUT_RFO_PIN == outputPin) { // RFO if (level < 0) {level = 0;} if (level > 14) {level = 14;} writeRegister(REG_PA_CONFIG, 0x70 | level); } else { // PA BOOST if (level > 17) { if (level > 20) {level = 20;} // subtract 3 from level, so 18 - 20 maps to 15 - 17 level -= 3; // High Power +20 dBm Operation (Semtech SX1276/77/78/79 5.4.3.) writeRegister(REG_PA_DAC, 0x87); setOCP(140); } else { if (level < 2) {level = 2;} //Default value PA_HF/LF or +17dBm writeRegister(REG_PA_DAC, 0x84); setOCP(100); } writeRegister(REG_PA_CONFIG, PA_BOOST | (level - 2)); } } /** perform hard reset */ void reset() { debugMod(NAME, "reset"); MyGPIO::set(PIN_RESET); usleep(50*1000); MyGPIO::clear(PIN_RESET); // perform reset usleep(50*1000); MyGPIO::set(PIN_RESET); usleep(50*1000); debugMod(NAME, "reset done"); } /** check the chip's version */ void check() { idle(); uint8_t version = readRegister(REG_VERSION); debugMod1(NAME, "version %d", version); if (version != 0x12) {debugMod(NAME, "!! unsupported chip version detected");} sleep(); } /** what exactly is this? */ void setOCP(uint8_t mA) { uint8_t ocpTrim = 27; if (mA <= 120) {ocpTrim = (mA - 45) / 5;} else if (mA <= 240) {ocpTrim = (mA + 30) / 10;} writeRegister(REG_OCP, 0x20 | (0x1F & ocpTrim)); } /** what exactly is this? Low-DataRate-Optimizer??? */ void setLdoFlag() { // Section 4.1.1.5 uint64_t symbolDuration = 1000 / ( getSignalBandwidth() / (1L << getSpreadingFactor()) ) ; // Section 4.1.1.6 bool ldoOn = symbolDuration > 16; uint8_t config3 = readRegister(REG_MODEM_CONFIG_3); if (ldoOn) {config3 = config3 | (1<<3);} else {config3 = config3 & ~(1<<3);} //bitWrite(config3, 3, ldoOn); writeRegister(REG_MODEM_CONFIG_3, config3); } private: /** write value to register */ void writeRegister(uint8_t address, uint8_t value) { transfer(address | 0x80, value); } /** read value from register */ uint8_t readRegister(uint8_t address) { return transfer(address & 0x7f, 0x00); } uint8_t transfer(uint8_t address, uint8_t value) { MyGPIO::clear(PIN_SLAVE_SEL); // select, active LOW spi.writeByte(address); const uint8_t res = spi.readWriteByte(value); MyGPIO::set(PIN_SLAVE_SEL); // de-select return res; } }; #endif