#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_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); // } // } void idle() { debugMod(NAME, "idle()"); writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_STDBY); } 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; } 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; } 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(20*1000); MyGPIO::clear(PIN_RESET); // perform reset usleep(20*1000); MyGPIO::set(PIN_RESET); usleep(20*1000); debugMod(NAME, "reset done"); } /** check the chip's version */ void check() { uint8_t version = readRegister(REG_VERSION); debugMod1(NAME, "version %d", version); if (version != 0x12) {debugMod(NAME, "!! unsupported chip version detected");} } /** 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)); } /** 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