/* * Author: Jon Trulson * Copyright (c) 2015 Intel Corporation. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include #ifdef JAVACALLBACK #undef JAVACALLBACK #endif #include "sx1276.h" using namespace upm; using namespace std; // // FSK bandwidth to register definition structs // typedef struct { uint32_t bandwidth; uint8_t RegValue; } FskBandwidth_t; static const FskBandwidth_t FskBandwidths[] = { { 2600 , 0x17 }, { 3100 , 0x0F }, { 3900 , 0x07 }, { 5200 , 0x16 }, { 6300 , 0x0E }, { 7800 , 0x06 }, { 10400 , 0x15 }, { 12500 , 0x0D }, { 15600 , 0x05 }, { 20800 , 0x14 }, { 25000 , 0x0C }, { 31300 , 0x04 }, { 41700 , 0x13 }, { 50000 , 0x0B }, { 62500 , 0x03 }, { 83333 , 0x12 }, { 100000, 0x0A }, { 125000, 0x02 }, { 166700, 0x11 }, { 200000, 0x09 }, { 250000, 0x01 }, { 300000, 0x00 }, // Invalid Badwidth }; SX1276::SX1276(uint8_t chipRev, int bus, int cs, int resetPin, int dio0, int dio1, int dio2, int dio3, int dio4, int dio5) : m_spi(bus), m_gpioCS(cs), m_gpioReset(resetPin), m_gpioDIO0(dio0), m_gpioDIO1(dio1), m_gpioDIO2(dio2), m_gpioDIO3(dio3), m_gpioDIO4(dio4), m_gpioDIO5(dio5) { m_spi.mode(mraa::SPI_MODE0); m_spi.frequency(10000000); // 10Mhz, if supported m_gpioCS.dir(mraa::DIR_OUT); m_gpioCS.useMmap(true); csOff(); m_gpioReset.dir(mraa::DIR_IN); // 10ms for POR usleep(10000); // setup the interrupt handlers. All 6 of them. m_gpioDIO0.dir(mraa::DIR_IN); if (m_gpioDIO0.isr(mraa::EDGE_RISING, onDio0Irq, this)) throw std::runtime_error(string(__FUNCTION__) + ": Gpio.isr(dio0) failed"); m_gpioDIO1.dir(mraa::DIR_IN); if (m_gpioDIO1.isr(mraa::EDGE_RISING, onDio1Irq, this)) throw std::runtime_error(string(__FUNCTION__) + ": Gpio.isr(dio1) failed"); m_gpioDIO2.dir(mraa::DIR_IN); if (m_gpioDIO2.isr(mraa::EDGE_RISING, onDio2Irq, this)) throw std::runtime_error(string(__FUNCTION__) + ": Gpio.isr(dio2) failed"); m_gpioDIO3.dir(mraa::DIR_IN); if (m_gpioDIO3.isr(mraa::EDGE_RISING, onDio3Irq, this)) throw std::runtime_error(string(__FUNCTION__) + ": Gpio.isr(dio3) failed"); m_gpioDIO4.dir(mraa::DIR_IN); if (m_gpioDIO4.isr(mraa::EDGE_RISING, onDio4Irq, this)) throw std::runtime_error(string(__FUNCTION__) + ": Gpio.isr(dio4) failed"); // this one isn't as vital, so no need to fail if this one can't be // setup. m_gpioDIO5.dir(mraa::DIR_IN); if (m_gpioDIO5.isr(mraa::EDGE_RISING, onDio5Irq, this)) cerr << __FUNCTION__ << ": Gpio.isr(dio5) failed" << endl; initClock(); m_radioEvent = REVENT_DONE; m_settings.state = STATE_IDLE; memset(m_rxBuffer, 0, FIFO_SIZE); m_rxSNR = 0; m_rxRSSI = 0; // check the chip revision (to make sure we can read the regs properly) uint8_t cRev = getChipVersion(); if (cRev != chipRev) { std::ostringstream str; std::ostringstream str2; str << hex << (int)cRev << dec; str2 << hex << (int)chipRev << dec; throw std::runtime_error(string(__FUNCTION__) + ": Incorrect Chip Revision. Expected 0x" + str2.str() + ", got 0x" + str.str()); } pthread_mutexattr_t mutexAttrib; pthread_mutexattr_init(&mutexAttrib); // pthread_mutexattr_settype(&mutexAttrib, PTHREAD_MUTEX_RECURSIVE); if (pthread_mutex_init(&m_intrLock, &mutexAttrib)) { throw std::runtime_error(std::string(__FUNCTION__) + ": pthread_mutex_init(intrLock) failed"); } pthread_mutexattr_destroy(&mutexAttrib); init(); } SX1276::~SX1276() { pthread_mutex_destroy(&m_intrLock); } uint8_t SX1276::readReg(uint8_t reg) { uint8_t pkt[2] = {(reg & 0x7f), 0}; csOn(); if (m_spi.transfer(pkt, pkt, 2)) { csOff(); throw std::runtime_error(string(__FUNCTION__) + ": Spi.transfer() failed"); return 0; } csOff(); return pkt[1]; } bool SX1276::writeReg(uint8_t reg, uint8_t val) { uint8_t pkt[2] = {reg | m_writeMode, val}; csOn(); if (m_spi.transfer(pkt, NULL, 2)) { csOff(); throw std::runtime_error(string(__FUNCTION__) + ": Spi.transfer() failed"); return false; } csOff(); return true; } void SX1276::readFifo(uint8_t *buffer, int len) { // can't read more than 256 bytes if (len > FIFO_SIZE) { throw std::length_error(string(__FUNCTION__) + ": cannot read more than 256 bytes from FIFO"); return; } uint8_t pkt = 0; csOn(); if (m_spi.transfer(&pkt, NULL, 1)) { csOff(); throw std::runtime_error(string(__FUNCTION__) + ": Spi.transfer(0) failed"); return; } if (m_spi.transfer(NULL, buffer, len)) { csOff(); throw std::runtime_error(string(__FUNCTION__) + ": Spi.transfer(buf) failed"); return; } csOff(); } void SX1276::writeFifo(uint8_t *buffer, int len) { // can't write more than 256 bytes if (len > FIFO_SIZE) { throw std::length_error(string(__FUNCTION__) + ": cannot write more than 256 bytes to FIFO"); return; } uint8_t pkt = (0 | m_writeMode); csOn(); if (m_spi.transfer(&pkt, NULL, 1)) { csOff(); throw std::runtime_error(string(__FUNCTION__) + ": Spi.transfer(0) failed"); return; } if (m_spi.transfer(buffer, NULL, len)) { csOff(); throw std::runtime_error(string(__FUNCTION__) + ": Spi.transfer(buf) failed"); return; } csOff(); } uint8_t SX1276::getChipVersion() { return readReg(COM_RegVersion); } void SX1276::reset() { m_gpioReset.dir(mraa::DIR_OUT); usleep(1000); // 1ms m_gpioReset.dir(mraa::DIR_IN); usleep(10000); // 10ms } void SX1276::init() { typedef struct { RADIO_MODEM_T Modem; uint8_t Addr; uint8_t Value; } radioRegisters_t; // some initial setup static const radioRegisters_t radioRegsInit[] = { { MODEM_FSK , COM_RegLna , 0x23 }, { MODEM_FSK , FSK_RegRxConfig , 0x1E }, { MODEM_FSK , FSK_RegRssiConfg , 0xD2 }, { MODEM_FSK , FSK_RegPreambleDetect , 0xAA }, { MODEM_FSK , FSK_RegOsc , 0x07 }, { MODEM_FSK , FSK_RegSyncConfig , 0x12 }, { MODEM_FSK , FSK_RegSyncValue1 , 0xC1 }, { MODEM_FSK , FSK_RegSyncValue2 , 0x94 }, { MODEM_FSK , FSK_RegSyncValue3 , 0xC1 }, { MODEM_FSK , FSK_RegPacketConfig1 , 0xD8 }, { MODEM_FSK , FSK_RegFifoThresh , 0x8F }, { MODEM_FSK , FSK_RegImageCal , 0x02 }, { MODEM_FSK , COM_RegDioMapping1 , 0x00 }, { MODEM_FSK , COM_RegDioMapping2 , 0x30 }, { MODEM_LORA, LOR_RegMaxPayloadLength, 0x40 } }; reset(); rxChainCalibration(); setOpMode(MODE_Sleep); for (int i = 0; i < sizeof(radioRegsInit) / sizeof(radioRegisters_t); i++ ) { setModem(radioRegsInit[i].Modem); writeReg(radioRegsInit[i].Addr, radioRegsInit[i].Value); } setModem(MODEM_FSK); m_settings.state = STATE_IDLE; } void SX1276::rxChainCalibration() { uint8_t regPaConfigInitVal; uint32_t initialFreq; uint8_t reg; // this function should only be called in init() (after reset()), as // the device is configured for FSK mode, LF at that time. // Save context regPaConfigInitVal = readReg(COM_RegPaConfig); initialFreq = (uint32_t) ( ((double) (((uint32_t)readReg(COM_RegFrfMsb) << 16) | ((uint32_t)readReg(COM_RegFrfMid) << 8) | ((uint32_t)readReg(COM_RegFrfLsb)) ) ) * FXOSC_STEP); // Cut the PA just in case, RFO output, power = -1 dBm writeReg(COM_RegPaConfig, 0x00); // Launch Rx chain calibration for LF band reg = readReg(FSK_RegImageCal); writeReg(FSK_RegImageCal, reg | IMAGECAL_ImageCalStart); // spin until complete while (readReg(FSK_RegImageCal) & IMAGECAL_ImageCalRunning) usleep(1); // cerr << __FUNCTION__ << ": Imagecal LF complete" << endl; // Set a Frequency in HF band setChannel(868000000); // Launch Rx chain calibration for HF band reg = readReg(FSK_RegImageCal); writeReg(FSK_RegImageCal, reg | IMAGECAL_ImageCalStart); // spin until complete while (readReg(FSK_RegImageCal) & IMAGECAL_ImageCalRunning) usleep(1); // cerr << __FUNCTION__ << ": Imagecal HF complete" << endl; // Restore context writeReg(COM_RegPaConfig, regPaConfigInitVal); setChannel(initialFreq); } void SX1276::setChannel(uint32_t freq) { m_settings.channel = freq; freq = ( uint32_t )( ( double )freq / FXOSC_STEP ); writeReg(COM_RegFrfMsb, ( uint8_t )( ( freq >> 16 ) & 0xff ) ); writeReg(COM_RegFrfMid, ( uint8_t )( ( freq >> 8 ) & 0xff ) ); writeReg(COM_RegFrfLsb, ( uint8_t )( freq & 0xff ) ); } void SX1276::setOpMode(MODE_T opMode) { static uint8_t opModePrev = MODE_Standby; if(opMode != opModePrev) { opModePrev = opMode; uint8_t reg = readReg(COM_RegOpMode) & ~(_OPMODE_Mode_MASK << _OPMODE_Mode_SHIFT); writeReg(COM_RegOpMode, (reg | (opMode << _OPMODE_Mode_SHIFT)) ); } } void SX1276::setModem(RADIO_MODEM_T modem) { if (m_settings.modem == modem ) { return; } m_settings.modem = modem; uint8_t reg = 0; switch (m_settings.modem) { default: case MODEM_FSK: setOpMode(MODE_Sleep); // turn off lora reg = (readReg(COM_RegOpMode) & ~OPMODE_LongRangeMode); writeReg(COM_RegOpMode, reg); writeReg(COM_RegDioMapping1, 0x00); writeReg(COM_RegDioMapping2, 0x30); // DIO5=ModeReady break; case MODEM_LORA: setOpMode(MODE_Sleep); // turn lora on reg = (readReg(COM_RegOpMode) | OPMODE_LongRangeMode); writeReg(COM_RegOpMode, reg); writeReg(COM_RegDioMapping1, 0x00); writeReg(COM_RegDioMapping2, 0x00); break; } } bool SX1276::isChannelFree(RADIO_MODEM_T modem, uint32_t freq, int16_t rssiThresh) { int16_t rssi = 0; setModem(modem); setChannel(freq); setOpMode(MODE_FSK_RxMode); usleep(1000); rssi = getRSSI(modem); setSleep(); if (rssi > rssiThresh) { return false; } return true; } int16_t SX1276::getRSSI(RADIO_MODEM_T modem) { int16_t rssi = 0; switch (modem) { case MODEM_FSK: // devide by 2 rssi = -(readReg(FSK_RegRssiValue) >> 1); break; case MODEM_LORA: { uint8_t reg = readReg(LOR_RegRssiValue); if (m_settings.channel > RF_MID_BAND_THRESH ) { rssi = LOR_RSSI_OFFSET_HF + reg; } else { rssi = LOR_RSSI_OFFSET_LF + reg; } } break; default: rssi = -1; break; } return rssi; } void SX1276::setSleep() { setOpMode(MODE_Sleep); m_settings.state = STATE_IDLE; } void SX1276::setStandby() { setOpMode(MODE_Standby); m_settings.state = STATE_IDLE; } uint8_t SX1276::lookupFSKBandWidth(uint32_t bw) { // This function looks up values in the fsk_bw_lookup_table and // returns the approprite register value to use for either the // FSK_RxBw or the FSK_RegAfcBw registers // See Table 40 in the datasheet for (int i=0; i<(sizeof(FskBandwidths)/sizeof(FskBandwidth_t)) - 1; i++) { if ( (bw >= FskBandwidths[i].bandwidth) && (bw < FskBandwidths[i + 1].bandwidth) ) { return FskBandwidths[i].RegValue; } } // shouldn't happen, but the universe is vast and indifferent... throw std::range_error(std::string(__FUNCTION__) + ": Unable to find bandwidth in lookup table. " "Bandwidth must be between 2600 and 250000 for FSK"); return 0; } SX1276::RADIO_EVENT_T SX1276::sendStr(string buffer, int timeout) { if (buffer.size() > (FIFO_SIZE - 1)) throw std::range_error(string(__FUNCTION__) + ": buffer size must be less than 256"); // for LORA/FSK modem, there seems to be a 64 byte requirement, // (LOR_RegRxNbBytes on the receiver) never seems to be anything // other than 64. Same seems to go for the FSK modem. So, if the // packet is less than 64, pad it out to 64 bytes. This requires // investigation. while (buffer.size() < 64) buffer.push_back(0); return send((uint8_t *)buffer.c_str(), buffer.size(), timeout); } SX1276::RADIO_EVENT_T SX1276::send(uint8_t *buffer, uint8_t size, int txTimeout) { switch (m_settings.modem) { case MODEM_FSK: { m_settings.fskPacketHandler.NbBytes = 0; m_settings.fskPacketHandler.Size = size; if (m_settings.fskSettings.FixLen == false) { writeFifo((uint8_t *)&size, 1); } else { writeReg(FSK_RegPayloadLength, size ); } if ( (size > 0) && (size <= 64) ) { m_settings.fskPacketHandler.ChunkSize = size; } else { m_settings.fskPacketHandler.ChunkSize = 32; } // Write payload buffer writeFifo(buffer, m_settings.fskPacketHandler.ChunkSize); m_settings.fskPacketHandler.NbBytes += m_settings.fskPacketHandler.ChunkSize; } break; case MODEM_LORA: { if (m_settings.loraSettings.IqInverted == true) { uint8_t reg = readReg(LOR_RegInvertIQ); reg &= ~(INVERTIQ_InvertIQTxOff | INVERTIQ_InvertIQRx); writeReg(LOR_RegInvertIQ, reg); // warning, hardcoded undocumented magic number into // undocumented register writeReg(LOR_RegInvertIQ2, 0x19); } else { uint8_t reg = readReg(LOR_RegInvertIQ); reg &= ~(INVERTIQ_InvertIQTxOff | INVERTIQ_InvertIQRx); reg |= INVERTIQ_InvertIQTxOff; // 'active' off. writeReg(LOR_RegInvertIQ, reg); // warning, hardcoded undocumented magic number into // undocumented register writeReg(LOR_RegInvertIQ2, 0x1d); } m_settings.loraPacketHandler.Size = size; // cerr << "PAYLOAD SIZE " << (int)size << endl; // Initializes the payload size writeReg(LOR_RegPayloadLength, size); // Full buffer used for Tx writeReg(LOR_RegFifoTxBaseAddr, 0); writeReg(LOR_RegFifoAddrPtr, 0 ); // FIFO operations can not take place in Sleep mode if ((readReg(COM_RegOpMode) & _OPMODE_Mode_MASK) == MODE_Sleep) { setStandby(); usleep(1000); // 1ms } // Write payload buffer writeFifo(buffer, size); } break; } return setTx(txTimeout); } void SX1276::setRxConfig(RADIO_MODEM_T modem, uint32_t bandwidth, uint32_t datarate, uint8_t coderate, uint32_t bandwidthAfc, uint16_t preambleLen, uint16_t symbTimeout, bool fixLen, uint8_t payloadLen, bool crcOn, bool freqHopOn, uint8_t hopPeriod, bool iqInverted, bool rxContinuous) { setModem( modem ); uint8_t reg; switch (modem) { case MODEM_FSK: { m_settings.fskSettings.Bandwidth = bandwidth; m_settings.fskSettings.Datarate = datarate; m_settings.fskSettings.BandwidthAfc = bandwidthAfc; m_settings.fskSettings.FixLen = fixLen; m_settings.fskSettings.PayloadLen = payloadLen; m_settings.fskSettings.CrcOn = crcOn; m_settings.fskSettings.IqInverted = iqInverted; m_settings.fskSettings.RxContinuous = rxContinuous; m_settings.fskSettings.PreambleLen = preambleLen; datarate = (uint16_t)(FXOSC_FREQ / (double)datarate); writeReg(FSK_RegBitrateMsb, (uint8_t)(datarate >> 8)); writeReg(FSK_RegBitrateLsb, (uint8_t)(datarate & 0xff)); writeReg(FSK_RegRxBw, lookupFSKBandWidth(bandwidth)); writeReg(FSK_RegAfcBw, lookupFSKBandWidth(bandwidthAfc)); writeReg(FSK_RegPreambleMsb, (uint8_t)((preambleLen >> 8) & 0xff)); writeReg(FSK_RegPreambleLsb, (uint8_t)(preambleLen & 0xff)); if (fixLen) { writeReg(FSK_RegPayloadLength, payloadLen); } reg = readReg(FSK_RegPacketConfig1); reg &= ~(PACKETCONFIG1_CrcOn | PACKETCONFIG1_PacketFormat); if (!fixLen) reg |= PACKETCONFIG1_PacketFormat; // variable len if (crcOn) reg |= PACKETCONFIG1_CrcOn; writeReg(FSK_RegPacketConfig1, reg); } break; case MODEM_LORA: { // convert the supplied (legal) LORA bandwidths into something // the chip can handle. switch (bandwidth) { case 125000: bandwidth = BW_125; break; case 250000: bandwidth = BW_250; break; case 500000: bandwidth = BW_500; break; default: throw std::runtime_error(std::string(__FUNCTION__) + ": LORA bandwidth must be 125000, 250000, " "or 500000"); } m_settings.loraSettings.Bandwidth = bandwidth; m_settings.loraSettings.Datarate = datarate; m_settings.loraSettings.Coderate = coderate; m_settings.loraSettings.FixLen = fixLen; m_settings.loraSettings.PayloadLen = payloadLen; m_settings.loraSettings.CrcOn = crcOn; m_settings.loraSettings.FreqHopOn = freqHopOn; m_settings.loraSettings.HopPeriod = hopPeriod; m_settings.loraSettings.IqInverted = iqInverted; m_settings.loraSettings.RxContinuous = rxContinuous; // datarate is really LORA SPREADING_FACTOR_* if (datarate > 12) { datarate = 12; } else if (datarate < 6) { datarate = 6; } if ( ((bandwidth == BW_125) && ((datarate == 11) || (datarate == 12))) || ((bandwidth == BW_250) && (datarate == 12)) ) { m_settings.loraSettings.LowDatarateOptimize = true; } else { m_settings.loraSettings.LowDatarateOptimize = false; } reg = readReg(LOR_RegModemConfig1); reg &= ~((_MODEMCONFIG1_CodingRate_MASK << _MODEMCONFIG1_CodingRate_SHIFT) | (_MODEMCONFIG1_Bw_MASK << _MODEMCONFIG1_Bw_SHIFT) | MODEMCONFIG1_ImplicitHeaderModeOn); if (fixLen) reg |= MODEMCONFIG1_ImplicitHeaderModeOn; reg |= ((bandwidth & _MODEMCONFIG1_Bw_MASK) << _MODEMCONFIG1_Bw_SHIFT); reg |= ((coderate & _MODEMCONFIG1_CodingRate_MASK) << _MODEMCONFIG1_CodingRate_SHIFT); writeReg(LOR_RegModemConfig1, reg); reg = readReg(LOR_RegModemConfig2); reg &= ~((_MODEMCONFIG2_SpreadingFactor_MASK << _MODEMCONFIG2_SpreadingFactor_SHIFT) | MODEMCONFIG2_RxPayloadCrcOn | (_MODEMCONFIG2_SymbTimeoutMsb_MASK << _MODEMCONFIG2_SymbTimeoutMsb_SHIFT)); if (crcOn) reg |= MODEMCONFIG2_RxPayloadCrcOn; reg |= ((datarate & _MODEMCONFIG2_SpreadingFactor_MASK) << _MODEMCONFIG2_SpreadingFactor_SHIFT); // mask symbTimeOut (MSB) for safety reg |= ( ((symbTimeout >> 8) & _MODEMCONFIG2_SymbTimeoutMsb_MASK) << _MODEMCONFIG2_SymbTimeoutMsb_SHIFT); writeReg(LOR_RegModemConfig2, reg); reg = readReg(LOR_RegModemConfig3); reg &= ~MODEMCONFIG3_LowDataRateOptimize; if (m_settings.loraSettings.LowDatarateOptimize) reg |= MODEMCONFIG3_LowDataRateOptimize; writeReg(LOR_RegModemConfig3, reg); writeReg(LOR_RegSymbTimeoutLsb, (uint8_t)(symbTimeout & 0xff)); writeReg(LOR_RegPreambleMsb, (uint8_t)((preambleLen >> 8) & 0xff)); writeReg(LOR_RegPreambleLsb, (uint8_t)(preambleLen & 0xff)); if (fixLen == 1) writeReg(LOR_RegPayloadLength, payloadLen); // The datasheet says this is only valid in FSK mode, but // Semtech code indicates it is only available in LORA // mode... So which is it? // Lets assume for now that the code is correct, as there // is a HopPeriod register for LoRa, and no such registers // exist for FSK. if (m_settings.loraSettings.FreqHopOn) { reg = readReg(LOR_RegPllHop); reg &= ~PLLHOP_FastHopOn; reg |= PLLHOP_FastHopOn; writeReg(LOR_RegPllHop, reg); writeReg(LOR_RegHopPeriod, m_settings.loraSettings.HopPeriod); } else { reg = readReg(LOR_RegPllHop); reg &= ~PLLHOP_FastHopOn; writeReg(LOR_RegPllHop, reg); } // errata checks - writing magic numbers into undocumented, // reserved registers :) The Semtech code was broken in this // logic. if ( (bandwidth == BW_500) && (m_settings.channel > RF_MID_BAND_THRESH) ) { // ERRATA 2.1 - Sensitivity Optimization with a 500 kHz // Bandwidth (HF) writeReg(LOR_Reserved36, 0x02); writeReg(LOR_Reserved3a, 0x64); } else if (bandwidth == BW_500 && (m_settings.channel >= 410000000)) { // ERRATA 2.1 - Sensitivity Optimization with a 500 kHz // Bandwidth (LF above 410Mhz) writeReg(LOR_Reserved36, 0x02); writeReg(LOR_Reserved3a, 0x7f); } else { // ERRATA 2.1 - Sensitivity Optimization with a 500 kHz // Bandwidth (everything else) writeReg(LOR_Reserved36, 0x03); } // datarate is really LORA spreading factor if (datarate == 6) { // datarate == SPREADINGFACTOR_64 reg = readReg(LOR_RegDetectOptimize); reg &= ~(_DETECTOPTIMIZE_DetectionOptimize_MASK << _DETECTOPTIMIZE_DetectionOptimize_SHIFT); reg |= (DETECTIONOPTIMIZE_SF6 << _DETECTOPTIMIZE_DetectionOptimize_SHIFT); writeReg(LOR_RegDetectOptimize, reg); // see page 27 in the datasheet writeReg(LOR_RegDetectionThreshold, LOR_DetectionThreshold_SF6); } else { reg = readReg(LOR_RegDetectOptimize); reg &= ~(_DETECTOPTIMIZE_DetectionOptimize_MASK << _DETECTOPTIMIZE_DetectionOptimize_SHIFT); reg |= (DETECTIONOPTIMIZE_SF7_SF12 << _DETECTOPTIMIZE_DetectionOptimize_SHIFT); writeReg(LOR_RegDetectOptimize, reg); // see page 27 in the datasheet writeReg(LOR_RegDetectionThreshold, LOR_DetectionThreshold_SF7_SF12); } } break; } } void SX1276::setTxConfig(RADIO_MODEM_T modem, int8_t power, uint32_t fdev, uint32_t bandwidth, uint32_t datarate, uint8_t coderate, uint16_t preambleLen, bool fixLen, bool crcOn, bool freqHopOn, uint8_t hopPeriod, bool iqInverted) { uint8_t paConfig = 0; uint8_t paDac = 0; setModem(modem); paConfig = readReg(COM_RegPaConfig); paDac = readReg(COM_RegPaDac); uint8_t paSelect = 0x00; // default, +14dBm if (m_settings.channel < RF_MID_BAND_THRESH) paSelect = PACONFIG_PaSelect; // PA_BOOST, +20dBm paConfig &= ~PACONFIG_PaSelect; paConfig |= paSelect; paConfig &= ~(_PACONFIG_MaxPower_MASK << _PACONFIG_MaxPower_SHIFT); paConfig |= (7 << _PACONFIG_MaxPower_SHIFT); // PACONFIG_MaxPower = 7 if ((paConfig & PACONFIG_PaSelect)) { if (power > 17) { paDac &= ~(_PADAC_PaDac_MASK << _PADAC_PaDac_SHIFT); paDac |= (PADAC_BOOST << _PADAC_PaDac_SHIFT); } else { paDac &= ~(_PADAC_PaDac_MASK << _PADAC_PaDac_SHIFT); paDac |= (PADAC_DEFAULT << _PADAC_PaDac_SHIFT); } if ((paDac & PADAC_BOOST) == PADAC_BOOST) { if (power < 5) { power = 5; } if (power > 20) { power = 20; } paConfig = ~(_PACONFIG_OutputPower_MASK & _PACONFIG_OutputPower_SHIFT); paConfig |= ( ((uint8_t)(power - 5) & _PACONFIG_OutputPower_MASK) << _PACONFIG_OutputPower_SHIFT ); } else { if (power < 2) { power = 2; } if (power > 17) { power = 17; } paConfig = ~(_PACONFIG_OutputPower_MASK & _PACONFIG_OutputPower_SHIFT); paConfig |= ( ((uint8_t)(power - 2) & _PACONFIG_OutputPower_MASK) << _PACONFIG_OutputPower_SHIFT ); } } else { if (power < -1) { power = -1; } if (power > 14) { power = 14; } paConfig = ~(_PACONFIG_OutputPower_MASK & _PACONFIG_OutputPower_SHIFT); paConfig |= ( ((uint8_t)(power + 1) & _PACONFIG_OutputPower_MASK) << _PACONFIG_OutputPower_SHIFT ); } writeReg(COM_RegPaConfig, paConfig); writeReg(COM_RegPaDac, paDac); uint8_t reg; switch (modem) { case MODEM_FSK: { m_settings.fskSettings.Power = power; m_settings.fskSettings.Fdev = fdev; m_settings.fskSettings.Bandwidth = bandwidth; m_settings.fskSettings.Datarate = datarate; m_settings.fskSettings.PreambleLen = preambleLen; m_settings.fskSettings.FixLen = fixLen; m_settings.fskSettings.CrcOn = crcOn; m_settings.fskSettings.IqInverted = iqInverted; fdev = (uint16_t)((double)fdev / FXOSC_STEP); writeReg(FSK_RegFdevMsb, (uint8_t)(fdev >> 8)); writeReg(FSK_RegFdevLsb, (uint8_t)(fdev & 0xFF)); datarate = (uint16_t)(FXOSC_FREQ / (double)datarate); writeReg(FSK_RegBitrateMsb, (uint8_t)(datarate >> 8)); writeReg(FSK_RegBitrateLsb, (uint8_t)(datarate & 0xff)); writeReg(FSK_RegPreambleMsb, (uint8_t)(preambleLen >> 8)); writeReg(FSK_RegPreambleLsb, (uint8_t)(preambleLen & 0xff)); reg = readReg(FSK_RegPacketConfig1); reg &= ~(PACKETCONFIG1_CrcOn | PACKETCONFIG1_PacketFormat); if (!fixLen) reg |= PACKETCONFIG1_PacketFormat; // variable len if (crcOn) reg |= PACKETCONFIG1_CrcOn; writeReg(FSK_RegPacketConfig1, reg); } break; case MODEM_LORA: { m_settings.loraSettings.Power = power; // we convert bandwidth into appropriate BW_* constants for LORA switch (bandwidth) { case 125000: bandwidth = BW_125; break; case 250000: bandwidth = BW_250; break; case 500000: bandwidth = BW_500; break; default: throw std::runtime_error(std::string(__FUNCTION__) + ": LORA bandwidth must be 125000, 250000, " "or 500000"); } m_settings.loraSettings.Bandwidth = bandwidth; m_settings.loraSettings.Datarate = datarate; m_settings.loraSettings.Coderate = coderate; m_settings.loraSettings.PreambleLen = preambleLen; m_settings.loraSettings.FixLen = fixLen; m_settings.loraSettings.FreqHopOn = freqHopOn; m_settings.loraSettings.HopPeriod = hopPeriod; m_settings.loraSettings.CrcOn = crcOn; m_settings.loraSettings.IqInverted = iqInverted; // datarate is really SPREADINGFACTOR_* for LoRa if (datarate > 12) { datarate = 12; } else if (datarate < 6) { datarate = 6; } if ( ((bandwidth == BW_125) && ((datarate == 11) || (datarate == 12))) || ((bandwidth == BW_250) && (datarate == 12)) ) { m_settings.loraSettings.LowDatarateOptimize = true; } else { m_settings.loraSettings.LowDatarateOptimize = false; } // datasheet says this is only valid in FSK mode, but Semtech // code indicates it is only available in LORA mode... So // which is it? // Lets assume for now that the code is correct, as there // is a HopPeriod register for LoRa, and no such registers // exist for FSK. if (m_settings.loraSettings.FreqHopOn == true) { reg = readReg(LOR_RegPllHop); reg &= ~PLLHOP_FastHopOn; reg |= PLLHOP_FastHopOn; writeReg(LOR_RegPllHop, reg); writeReg(LOR_RegHopPeriod, m_settings.loraSettings.HopPeriod); } else { reg = readReg(LOR_RegPllHop); reg &= ~PLLHOP_FastHopOn; writeReg(LOR_RegPllHop, reg); } reg = readReg(LOR_RegModemConfig1); reg &= ~((_MODEMCONFIG1_CodingRate_MASK << _MODEMCONFIG1_CodingRate_SHIFT) | (_MODEMCONFIG1_Bw_MASK << _MODEMCONFIG1_Bw_SHIFT) | MODEMCONFIG1_ImplicitHeaderModeOn); if (fixLen) reg |= MODEMCONFIG1_ImplicitHeaderModeOn; reg |= ((bandwidth & _MODEMCONFIG1_Bw_MASK) << _MODEMCONFIG1_Bw_SHIFT); reg |= ((coderate & _MODEMCONFIG1_CodingRate_MASK) << _MODEMCONFIG1_CodingRate_SHIFT); writeReg(LOR_RegModemConfig1, reg); reg = readReg(LOR_RegModemConfig2); reg &= ~((_MODEMCONFIG2_SpreadingFactor_MASK << _MODEMCONFIG2_SpreadingFactor_SHIFT) | MODEMCONFIG2_RxPayloadCrcOn); if (crcOn) reg |= MODEMCONFIG2_RxPayloadCrcOn; reg |= ((datarate & _MODEMCONFIG2_SpreadingFactor_MASK) << _MODEMCONFIG2_SpreadingFactor_SHIFT); writeReg(LOR_RegModemConfig2, reg); reg = readReg(LOR_RegModemConfig3); reg &= ~MODEMCONFIG3_LowDataRateOptimize; if (m_settings.loraSettings.LowDatarateOptimize) reg |= MODEMCONFIG3_LowDataRateOptimize; writeReg(LOR_RegModemConfig3, reg); writeReg(LOR_RegPreambleMsb, (uint8_t)((preambleLen >> 8) & 0xff)); writeReg(LOR_RegPreambleLsb, (uint8_t)(preambleLen & 0xff)); // datarate is SPREADINGFACTOR_* if (datarate == 6) { reg = readReg(LOR_RegDetectOptimize); reg &= ~(_DETECTOPTIMIZE_DetectionOptimize_MASK << _DETECTOPTIMIZE_DetectionOptimize_SHIFT); reg |= (DETECTIONOPTIMIZE_SF6 << _DETECTOPTIMIZE_DetectionOptimize_SHIFT); writeReg(LOR_RegDetectOptimize, reg); // see page 27 in the datasheet writeReg(LOR_RegDetectionThreshold, LOR_DetectionThreshold_SF6); } else { reg = readReg(LOR_RegDetectOptimize); reg &= ~(_DETECTOPTIMIZE_DetectionOptimize_MASK << _DETECTOPTIMIZE_DetectionOptimize_SHIFT); reg |= (DETECTIONOPTIMIZE_SF7_SF12 << _DETECTOPTIMIZE_DetectionOptimize_SHIFT); writeReg(LOR_RegDetectOptimize, reg); // see page 27 in the datasheet writeReg(LOR_RegDetectionThreshold, LOR_DetectionThreshold_SF7_SF12); } } break; } } SX1276::RADIO_EVENT_T SX1276::setTx(int timeout) { uint8_t reg = 0; switch (m_settings.modem) { case MODEM_FSK: { // DIO0=PacketSent // DIO1=FifoLevel // DIO2=FifoFull // DIO3=FifoEmpty // DIO4=LowBat // DIO5=ModeReady reg = readReg(COM_RegDioMapping1); reg &= ~( (DOIMAPPING1_Dio0Mapping_MASK << DOIMAPPING1_Dio0Mapping_SHIFT) | (DOIMAPPING1_Dio2Mapping_MASK << DOIMAPPING1_Dio2Mapping_SHIFT) ); writeReg(COM_RegDioMapping1, reg); reg = readReg(COM_RegDioMapping2); reg &= ~( (DOIMAPPING2_Dio4Mapping_MASK << DOIMAPPING2_Dio4Mapping_SHIFT) | (DOIMAPPING2_Dio5Mapping_MASK << DOIMAPPING2_Dio5Mapping_SHIFT) ); writeReg(COM_RegDioMapping2, reg); m_settings.fskPacketHandler.FifoThresh = (readReg(FSK_RegFifoThresh) & (_FIFOTHRESH_FifoThreshold_MASK << _FIFOTHRESH_FifoThreshold_SHIFT)); } break; case MODEM_LORA: { if (m_settings.loraSettings.FreqHopOn == true ) { // mask out all except TxDone and FhssChangeChannel writeReg(LOR_RegIrqFlagsMask, LOR_IRQFLAG_RxTimeout | LOR_IRQFLAG_RxDone | LOR_IRQFLAG_PayloadCrcError | LOR_IRQFLAG_ValidHeader | // LOR_IRQFLAG_TxDone | LOR_IRQFLAG_CadDone | // LOR_IRQFLAG_FhssChangeChannel | LOR_IRQFLAG_CadDetected); // DIO0=TxDone, DIO2=FhssChangeChannel reg = readReg(COM_RegDioMapping1); reg &= ~( (DOIMAPPING1_Dio0Mapping_MASK << DOIMAPPING1_Dio0Mapping_SHIFT) | (DOIMAPPING1_Dio2Mapping_MASK << DOIMAPPING1_Dio2Mapping_SHIFT) ); reg |= ( (DIOMAPPING_01 << DOIMAPPING1_Dio0Mapping_SHIFT) | (DIOMAPPING_00 << DOIMAPPING1_Dio2Mapping_SHIFT) ); writeReg(COM_RegDioMapping1, reg); } else { // mask out all except TxDone writeReg(LOR_RegIrqFlagsMask, LOR_IRQFLAG_RxTimeout | LOR_IRQFLAG_RxDone | LOR_IRQFLAG_PayloadCrcError | LOR_IRQFLAG_ValidHeader | // LOR_IRQFLAG_TxDone | LOR_IRQFLAG_CadDone | LOR_IRQFLAG_FhssChangeChannel | LOR_IRQFLAG_CadDetected); // DIO0=TxDone reg = readReg(COM_RegDioMapping1); reg &= ~( (DOIMAPPING1_Dio0Mapping_MASK << DOIMAPPING1_Dio0Mapping_SHIFT) ); reg |= (DIOMAPPING_01 << DOIMAPPING1_Dio0Mapping_SHIFT); writeReg(COM_RegDioMapping1, reg); } } break; } m_settings.state = STATE_TX_RUNNING; m_radioEvent = REVENT_EXEC; setOpMode(MODE_TxMode); initClock(); while ((getMillis() < timeout) && m_radioEvent == REVENT_EXEC) usleep(100); if (m_radioEvent == REVENT_EXEC) { // timeout m_radioEvent = REVENT_TIMEOUT; } return m_radioEvent; } SX1276::RADIO_EVENT_T SX1276::setRx(uint32_t timeout) { bool rxContinuous = false; uint8_t reg = 0; switch (m_settings.modem) { case MODEM_FSK: { rxContinuous = m_settings.fskSettings.RxContinuous; // DIO0=PayloadReady // DIO1=FifoLevel // DIO2=SyncAddr // DIO3=FifoEmpty // DIO4=Preamble // DIO5=ModeReady reg = readReg(COM_RegDioMapping1); reg &= ~( (DOIMAPPING1_Dio0Mapping_MASK << DOIMAPPING1_Dio0Mapping_SHIFT) | (DOIMAPPING1_Dio2Mapping_MASK << DOIMAPPING1_Dio2Mapping_SHIFT) ); reg |= ( (DIOMAPPING_00 << DOIMAPPING1_Dio0Mapping_SHIFT) | (DIOMAPPING_11 << DOIMAPPING1_Dio2Mapping_SHIFT) ); writeReg(COM_RegDioMapping1, reg); reg = readReg(COM_RegDioMapping2); reg &= ~( (DOIMAPPING2_Dio4Mapping_MASK << DOIMAPPING2_Dio4Mapping_SHIFT) | (DOIMAPPING2_Dio5Mapping_MASK << DOIMAPPING2_Dio5Mapping_SHIFT) ); reg |= (DIOMAPPING_11 << DOIMAPPING2_Dio4Mapping_SHIFT) | DOIMAPPING2_MapPreambleDetect; writeReg(COM_RegDioMapping2, reg); m_settings.fskPacketHandler.FifoThresh = (readReg(FSK_RegFifoThresh) & _FIFOTHRESH_FifoThreshold_MASK); m_settings.fskPacketHandler.PreambleDetected = false; m_settings.fskPacketHandler.SyncWordDetected = false; m_settings.fskPacketHandler.NbBytes = 0; m_settings.fskPacketHandler.Size = 0; } break; case MODEM_LORA: { // The datasheet does not mention anything other than an // InvertIQ bit (0x40) in RegInvertIQ register (0x33). Here, // we seem to have two bits in RegInvertIQ (existing one for // RX), and a 'new' one for TXOff (0x01). In addition, // INVERTIQ2 (0x3b) does not exist in the datasheet, it is // marked as reserved. We will assume that the datasheet is // out of date. if (m_settings.loraSettings.IqInverted == true) { reg = readReg(LOR_RegInvertIQ); reg &= ~(INVERTIQ_InvertIQTxOff | INVERTIQ_InvertIQRx); reg |= INVERTIQ_InvertIQRx; writeReg(LOR_RegInvertIQ, reg); // warning, hardcoded undocumented magic number into // undocumented register writeReg(LOR_RegInvertIQ2, 0x19); } else { reg = readReg(LOR_RegInvertIQ); reg &= ~(INVERTIQ_InvertIQTxOff | INVERTIQ_InvertIQRx); reg |= INVERTIQ_InvertIQTxOff; // 'active' off. writeReg(LOR_RegInvertIQ, reg); // warning, hardcoded undocumented magic number into // undocumented register writeReg(LOR_RegInvertIQ2, 0x1d); } // ERRATA 2.3 - Receiver Spurious Reception of a LoRa Signal if (m_settings.loraSettings.Bandwidth < 9) { reg = readReg(LOR_RegDetectOptimize); reg &= 0x7f; // clear undocumented bit 7 writeReg(LOR_RegDetectOptimize, reg); // warning, writing magic numbers into undocumented // registers switch (m_settings.loraSettings.Bandwidth) { case 0: // 7.8 kHz writeReg(LOR_Reserved2f, 0x48); setChannel(m_settings.channel + 7.81e3); break; case 1: // 10.4 kHz writeReg(LOR_Reserved2f, 0x44); setChannel(m_settings.channel + 10.42e3); break; case 2: // 15.6 kHz writeReg(LOR_Reserved2f, 0x44); setChannel(m_settings.channel + 15.62e3); break; case 3: // 20.8 kHz writeReg(LOR_Reserved2f, 0x44); setChannel(m_settings.channel + 20.83e3); break; case 4: // 31.2 kHz writeReg(LOR_Reserved2f, 0x44); setChannel(m_settings.channel + 31.25e3); break; case 5: // 41.4 kHz writeReg(LOR_Reserved2f, 0x44); setChannel(m_settings.channel + 41.67e3); break; case 6: // 62.5 kHz writeReg(LOR_Reserved2f, 0x40); break; case 7: // 125 kHz writeReg(LOR_Reserved2f, 0x40); break; case 8: // 250 kHz writeReg(LOR_Reserved2f, 0x40); break; } } else { reg = readReg(LOR_RegDetectOptimize); reg |= 0x80; // set undocumented bit 7 writeReg(LOR_RegDetectOptimize, reg); } rxContinuous = m_settings.loraSettings.RxContinuous; if (m_settings.loraSettings.FreqHopOn == true) { // mask out all except RxDone, RxTimeout, PayloadCrCError, // and FhssChangeChannel writeReg(LOR_RegIrqFlagsMask, // LOR_IRQFLAG_RxTimeout | // LOR_IRQFLAG_RxDone | // LOR_IRQFLAG_PayloadCrcError | LOR_IRQFLAG_ValidHeader | LOR_IRQFLAG_TxDone | LOR_IRQFLAG_CadDone | // LOR_IRQFLAG_FhssChangeChannel | LOR_IRQFLAG_CadDetected); // DIO0=RxDone, DIO2=FhssChangeChannel reg = readReg(COM_RegDioMapping1); reg &= ~( (DOIMAPPING1_Dio0Mapping_MASK << DOIMAPPING1_Dio0Mapping_SHIFT) | (DOIMAPPING1_Dio2Mapping_MASK << DOIMAPPING1_Dio2Mapping_SHIFT) ); reg |= ( (DIOMAPPING_00 << DOIMAPPING1_Dio0Mapping_SHIFT) | (DIOMAPPING_00 << DOIMAPPING1_Dio2Mapping_SHIFT) ); writeReg(COM_RegDioMapping1, reg); } else { // mask out all except RxDone, RxTimeout, and PayloadCrCError writeReg(LOR_RegIrqFlagsMask, // LOR_IRQFLAG_RxTimeout | // LOR_IRQFLAG_RxDone | // LOR_IRQFLAG_PayloadCrcError | LOR_IRQFLAG_ValidHeader | LOR_IRQFLAG_TxDone | LOR_IRQFLAG_CadDone | LOR_IRQFLAG_FhssChangeChannel | LOR_IRQFLAG_CadDetected); // DIO0=RxDone reg = readReg(COM_RegDioMapping1); reg &= ~(DOIMAPPING1_Dio0Mapping_MASK << DOIMAPPING1_Dio0Mapping_SHIFT); reg |= (DIOMAPPING_00 << DOIMAPPING1_Dio0Mapping_SHIFT); writeReg(COM_RegDioMapping1, reg); } writeReg(LOR_RegFifoRxBaseAddr, 0); writeReg(LOR_RegFifoAddrPtr, 0); } break; } memset(m_rxBuffer, 0, FIFO_SIZE); m_settings.state = STATE_RX_RUNNING; m_radioEvent = REVENT_EXEC; if (m_settings.modem == MODEM_FSK) { setOpMode(MODE_FSK_RxMode); if (rxContinuous == false) { // timer..? #if 0 TimerSetValue( &RxTimeoutSyncWord, ( 8.0 * ( m_settings.fsk.PreambleLen + ( ( SX1276Read( REG_SYNCCONFIG ) & ~RF_SYNCCONFIG_SYNCSIZE_MASK ) + 1.0 ) + 10.0 ) / ( double )m_settings.fsk.Datarate ) * 1e6 ); TimerStart( &RxTimeoutSyncWord ); #endif } } else { // LoRa if (rxContinuous == true) { setOpMode(MODE_LOR_RxContinuous); } else { setOpMode(MODE_LOR_RxSingle); } } initClock(); while ((getMillis() < timeout) && m_radioEvent == REVENT_EXEC) usleep(100); if (m_radioEvent == REVENT_EXEC) { // timeout m_radioEvent = REVENT_TIMEOUT; } return m_radioEvent; } void SX1276::startCAD() { switch (m_settings.modem) { case MODEM_LORA: { // mask out all except CadDone and CadDetected writeReg(LOR_RegIrqFlagsMask, LOR_IRQFLAG_RxTimeout | LOR_IRQFLAG_RxDone | LOR_IRQFLAG_PayloadCrcError | LOR_IRQFLAG_ValidHeader | LOR_IRQFLAG_TxDone | // LOR_IRQFLAG_CadDone | LOR_IRQFLAG_FhssChangeChannel //| // LOR_IRQFLAG_CadDetected ); // DIO3=CADDone uint8_t reg; reg = readReg(COM_RegDioMapping1); reg &= ~(DOIMAPPING1_Dio3Mapping_MASK << DOIMAPPING1_Dio3Mapping_SHIFT); reg |= (DIOMAPPING_00 << DOIMAPPING1_Dio3Mapping_SHIFT); writeReg(COM_RegDioMapping1, reg); m_settings.state = STATE_CAD; setOpMode(MODE_LOR_CAD); } break; case MODEM_FSK: default: break; } } void SX1276::setMaxPayloadLength(RADIO_MODEM_T modem, uint8_t max) { setModem(modem); switch (modem) { case MODEM_FSK: if (m_settings.fskSettings.FixLen == false) { writeReg(FSK_RegPayloadLength, max); } break; case MODEM_LORA: writeReg(LOR_RegMaxPayloadLength, max); break; } } void SX1276::onDio0Irq(void *ctx) { upm::SX1276 *This = (upm::SX1276 *)ctx; This->lockIntrs(); volatile uint8_t irqFlags = 0; // cerr << __FUNCTION__ << ": Enter" << endl; switch (This->m_settings.state) { case STATE_RX_RUNNING: // RxDone interrupt switch (This->m_settings.modem) { case MODEM_FSK: if (This->m_settings.fskSettings.CrcOn == true ) { irqFlags = This->readReg(FSK_RegIrqFlags2); if (!(irqFlags & IRQFLAGS2_CrcOk)) { // Clear Irqs This->writeReg(FSK_RegIrqFlags1, IRQFLAGS1_Rssi | IRQFLAGS1_PreambleDetect | IRQFLAGS1_SyncAddressMatch); This->writeReg(FSK_RegIrqFlags2, IRQFLAGS2_FifoOverrun); if (This->m_settings.fskSettings.RxContinuous == false ) { This->m_settings.state = STATE_IDLE; } else { // Continuous mode restart Rx chain This->writeReg(FSK_RegRxConfig, This->readReg(FSK_RegRxConfig) | RXCONFIG_RestartRxWithoutPllLock); } // RxError radio event // cerr << __FUNCTION__ << ": RxError crc/sync timeout" << endl; This->m_radioEvent = REVENT_ERROR; This->m_settings.fskPacketHandler.PreambleDetected = false; This->m_settings.fskPacketHandler.SyncWordDetected = false; This->m_settings.fskPacketHandler.NbBytes = 0; This->m_settings.fskPacketHandler.Size = 0; break; } } // Read received packet size if ( (This->m_settings.fskPacketHandler.Size == 0) && (This->m_settings.fskPacketHandler.NbBytes == 0) ) { if (This->m_settings.fskSettings.FixLen == false ) { This->readFifo((uint8_t*)&(This->m_settings.fskPacketHandler.Size), 1); } else { This->m_settings.fskPacketHandler.Size = This->readReg(FSK_RegPayloadLength); } This->readFifo(This->m_rxBuffer + This->m_settings.fskPacketHandler.NbBytes, This->m_settings.fskPacketHandler.Size - This->m_settings.fskPacketHandler.NbBytes); This->m_settings.fskPacketHandler.NbBytes += (This->m_settings.fskPacketHandler.Size - This->m_settings.fskPacketHandler.NbBytes); } else { This->readFifo(This->m_rxBuffer + This->m_settings.fskPacketHandler.NbBytes, (This->m_settings.fskPacketHandler.Size - This->m_settings.fskPacketHandler.NbBytes)); This->m_settings.fskPacketHandler.NbBytes += (This->m_settings.fskPacketHandler.Size - This->m_settings.fskPacketHandler.NbBytes); } if (This->m_settings.fskSettings.RxContinuous == false) { This->m_settings.state = STATE_IDLE; } else { // Continuous mode restart Rx chain This->writeReg(FSK_RegRxConfig, This->readReg(FSK_RegRxConfig) | RXCONFIG_RestartRxWithoutPllLock); } // RxDone radio event This->m_rxRSSI = This->m_settings.fskPacketHandler.RssiValue; This->m_rxLen = This->m_settings.fskPacketHandler.Size; This->m_radioEvent = REVENT_DONE; // cerr << __FUNCTION__ << ": FSK RxDone" << endl; // fprintf(stderr, "### %s: RX(%d): %s\n", // __FUNCTION__, // This->m_settings.fskPacketHandler.Size, // This->m_rxBuffer); This->m_settings.fskPacketHandler.PreambleDetected = false; This->m_settings.fskPacketHandler.SyncWordDetected = false; This->m_settings.fskPacketHandler.NbBytes = 0; This->m_settings.fskPacketHandler.Size = 0; break; case MODEM_LORA: { int8_t snr = 0; // Clear Irq This->writeReg(LOR_RegIrqFlags, LOR_IRQFLAG_RxDone); irqFlags = This->readReg(LOR_RegIrqFlags); // cerr << "LORA PayloadCRC on = " // << hex << (int)This->readReg(LOR_RegHopChannel) << dec << endl; if (irqFlags & LOR_IRQFLAG_PayloadCrcError) { // Clear Irq This->writeReg(LOR_RegIrqFlags, LOR_IRQFLAG_PayloadCrcError); if (This->m_settings.loraSettings.RxContinuous == false) { This->m_settings.state = STATE_IDLE; } // RxError radio event // cerr << __FUNCTION__ << ": RxError (payload crc error)" << endl; This->m_radioEvent = REVENT_ERROR; break; } This->m_settings.loraPacketHandler.SnrValue = This->readReg(LOR_RegPktSnrValue); if (This->m_settings.loraPacketHandler.SnrValue & 0x80) { // The SNR sign bit is 1 // Invert and divide by 4 snr = ( (~(This->m_settings.loraPacketHandler.SnrValue) + 1 ) & 0xff) >> 2; snr = -snr; } else { // Divide by 4 snr = (This->m_settings.loraPacketHandler.SnrValue & 0xff) >> 2; } int16_t rssi = This->readReg(LOR_RegPktRssiValue); if (snr < 0) { if (This->m_settings.channel > RF_MID_BAND_THRESH) { This->m_settings.loraPacketHandler.RssiValue = LOR_RSSI_OFFSET_HF + rssi + ( rssi >> 4 ) + snr; } else { This->m_settings.loraPacketHandler.RssiValue = LOR_RSSI_OFFSET_LF + rssi + ( rssi >> 4 ) + snr; } } else { if (This->m_settings.channel > RF_MID_BAND_THRESH) { This->m_settings.loraPacketHandler.RssiValue = LOR_RSSI_OFFSET_HF + rssi + (rssi >> 4); } else { This->m_settings.loraPacketHandler.RssiValue = LOR_RSSI_OFFSET_LF + rssi + (rssi >> 4); } } This->m_settings.loraPacketHandler.Size = This->readReg(LOR_RegRxNbBytes); // cerr << "LORA HANDLER SIZE = " // << (int)This->m_settings.loraPacketHandler.Size << endl; // cerr << "LORA MAXPAYLOAD = " // << (int)This->readReg(LOR_RegMaxPayloadLength) << endl; This->readFifo(This->m_rxBuffer, This->m_settings.loraPacketHandler.Size); if (This->m_settings.loraSettings.RxContinuous == false) { This->m_settings.state = STATE_IDLE; } // RxDone radio event // The returned size (from LOR_RegRxNbBytes) is always 64 // bytes regardless of the packet size I sent. Something // is wrong here. // cerr << __FUNCTION__ << ": RxDone (LORA)" << endl; This->m_rxRSSI = (int)rssi; This->m_rxSNR = (int)snr; This->m_rxLen = This->m_settings.loraPacketHandler.Size; This->m_radioEvent = REVENT_DONE; // if (This->m_settings.state == STATE_RX_RUNNING) // fprintf(stderr, "### %s: snr = %d rssi = %d RX(%d): %s\n", // __FUNCTION__, // (int)snr, (int)rssi, // This->m_settings.loraPacketHandler.Size, // This->m_rxBuffer); // else // fprintf(stderr, "### %s: snr = %d rssi = %d RX: INV BUFFER (crc)\n", __FUNCTION__, // (int)snr, (int)rssi); } break; default: break; } break; case STATE_TX_RUNNING: // TxDone interrupt switch (This->m_settings.modem) { case MODEM_LORA: // Clear Irq This->writeReg(LOR_RegIrqFlags, LOR_IRQFLAG_TxDone); // fprintf(stderr, "%s: LORA IrqFlags = %02x\n", __FUNCTION__, // This->readReg(LOR_RegIrqFlags)); // Intentional fall through case MODEM_FSK: default: This->m_settings.state = STATE_IDLE; // TxDone radio event This->m_radioEvent = REVENT_DONE; // cerr << __FUNCTION__ << ": TxDone" << endl; break; } break; default: break; } This->unlockIntrs(); } void SX1276::onDio1Irq(void *ctx) { upm::SX1276 *This = (upm::SX1276 *)ctx; This->lockIntrs(); // cerr << __FUNCTION__ << ": Enter" << endl; switch (This->m_settings.state) { case STATE_RX_RUNNING: switch (This->m_settings.modem) { case MODEM_FSK: // FifoLevel interrupt // Read received packet size if ( (This->m_settings.fskPacketHandler.Size == 0 ) && (This->m_settings.fskPacketHandler.NbBytes == 0) ) { if (This->m_settings.fskSettings.FixLen == false) { This->readFifo((uint8_t*)&(This->m_settings.fskPacketHandler.Size), 1); } else { This->m_settings.fskPacketHandler.Size = This->readReg(FSK_RegPayloadLength); } } if ( (This->m_settings.fskPacketHandler.Size - This->m_settings.fskPacketHandler.NbBytes) > This->m_settings.fskPacketHandler.FifoThresh) { This->readFifo((This->m_rxBuffer + This->m_settings.fskPacketHandler.NbBytes), This->m_settings.fskPacketHandler.FifoThresh); This->m_settings.fskPacketHandler.NbBytes += This->m_settings.fskPacketHandler.FifoThresh; } else { This->readFifo((This->m_rxBuffer + This->m_settings.fskPacketHandler.NbBytes), This->m_settings.fskPacketHandler.Size - This->m_settings.fskPacketHandler.NbBytes); This->m_settings.fskPacketHandler.NbBytes += (This->m_settings.fskPacketHandler.Size - This->m_settings.fskPacketHandler.NbBytes); } break; case MODEM_LORA: // Sync time out This->m_settings.state = STATE_IDLE; // RxError (LORA timeout) radio events // cerr << __FUNCTION__ << ": RxTimeout (LORA)" << endl; This->m_radioEvent = REVENT_TIMEOUT; break; default: break; } break; case STATE_TX_RUNNING: switch (This->m_settings.modem ) { case MODEM_FSK: // FifoLevel interrupt if ( (This->m_settings.fskPacketHandler.Size - This->m_settings.fskPacketHandler.NbBytes) > This->m_settings.fskPacketHandler.ChunkSize) { This->writeFifo((This->m_rxBuffer + This->m_settings.fskPacketHandler.NbBytes), This->m_settings.fskPacketHandler.ChunkSize); This->m_settings.fskPacketHandler.NbBytes += This->m_settings.fskPacketHandler.ChunkSize; } else { // Write the last chunk of data This->writeFifo((This->m_rxBuffer + This->m_settings.fskPacketHandler.NbBytes), This->m_settings.fskPacketHandler.Size - This->m_settings.fskPacketHandler.NbBytes); This->m_settings.fskPacketHandler.NbBytes += (This->m_settings.fskPacketHandler.Size - This->m_settings.fskPacketHandler.NbBytes); } break; case MODEM_LORA: break; default: break; } break; default: break; } This->unlockIntrs(); } void SX1276::onDio2Irq(void *ctx) { upm::SX1276 *This = (upm::SX1276 *)ctx; This->lockIntrs(); // cerr << __FUNCTION__ << ": Enter" << endl; switch (This->m_settings.state) { case STATE_RX_RUNNING: switch (This->m_settings.modem) { case MODEM_FSK: if ( (This->m_settings.fskPacketHandler.PreambleDetected == true ) && (This->m_settings.fskPacketHandler.SyncWordDetected == false) ) { This->m_settings.fskPacketHandler.SyncWordDetected = true; This->m_settings.fskPacketHandler.RssiValue = -(This->readReg(FSK_RegRssiValue) >> 1 ); This->m_settings.fskPacketHandler.AfcValue = (int32_t)(double)( ((uint16_t)This->readReg(FSK_RegAfcMsb) << 8 ) | (uint16_t)This->readReg(FSK_RegAfcLsb) ) * FXOSC_STEP; This->m_settings.fskPacketHandler.RxGain = (This->readReg(COM_RegLna) >> 5) & 0x07; } break; case MODEM_LORA: if (This->m_settings.loraSettings.FreqHopOn == true) { // Clear Irq This->writeReg(LOR_RegIrqFlags, LOR_IRQFLAG_FhssChangeChannel); // Fhss radio event (unsupported currently) // FhssChangeChannel( (readReg( LOR_RegHopChannel) & // ~_HOPCHANNEL_FhssPresentChannel_MASK) ); //cerr << __FUNCTION__ << ": Fhss Change Channel (LORA, RX running)" << endl; } break; default: break; } break; case STATE_TX_RUNNING: switch (This->m_settings.modem) { case MODEM_FSK: break; case MODEM_LORA: if (This->m_settings.loraSettings.FreqHopOn == true) { // Clear Irq This->writeReg(LOR_RegIrqFlags, LOR_IRQFLAG_FhssChangeChannel); // Fhss radio event (unsupported currently) // FhssChangeChannel( (readReg( LOR_RegHopChannel) & // ~_HOPCHANNEL_FhssPresentChannel_MASK) ); //cerr << __FUNCTION__ << ": Fhss Change Channel (LORA, TX running)" << endl; } break; default: break; } break; default: break; } This->unlockIntrs(); } void SX1276::onDio3Irq(void *ctx) { upm::SX1276 *This = (upm::SX1276 *)ctx; This->lockIntrs(); // cerr << __FUNCTION__ << ": Enter" << endl; switch (This->m_settings.modem) { case MODEM_FSK: break; case MODEM_LORA: if (This->readReg(LOR_RegIrqFlags) & LOR_IRQFLAG_CadDetected) { // Clear Irq This->writeReg(LOR_RegIrqFlags, (LOR_IRQFLAG_CadDetected | LOR_IRQFLAG_CadDone)); // CADDetected radio event (true) // cerr << __FUNCTION__ << ": CadDetected (LORA)" << endl; } else { // Clear Irq This->writeReg(LOR_RegIrqFlags, LOR_IRQFLAG_CadDone); // CADDetected radio event (false) //cerr << __FUNCTION__ << ": CadDone (LORA)" << endl; } break; default: break; } This->unlockIntrs(); } void SX1276::onDio4Irq(void *ctx) { upm::SX1276 *This = (upm::SX1276 *)ctx; This->lockIntrs(); // cerr << __FUNCTION__ << ": Enter" << endl; switch (This->m_settings.modem) { case MODEM_FSK: { if (This->m_settings.fskPacketHandler.PreambleDetected == false) { This->m_settings.fskPacketHandler.PreambleDetected = true; } } break; case MODEM_LORA: break; default: break; } This->unlockIntrs(); } void SX1276::onDio5Irq(void *ctx) { upm::SX1276 *This = (upm::SX1276 *)ctx; This->lockIntrs(); // cerr << __FUNCTION__ << ": Enter" << endl; switch (This->m_settings.modem) { case MODEM_FSK: break; case MODEM_LORA: // fprintf(stderr, "%s: LORA IrqFlags = %02x\n", __FUNCTION__, // This->readReg(LOR_RegIrqFlags)); break; default: break; } This->unlockIntrs(); } void SX1276::initClock() { gettimeofday(&m_startTime, NULL); } uint32_t SX1276::getMillis() { struct timeval elapsed, now; uint32_t elapse; // get current time gettimeofday(&now, NULL); // compute the delta since m_startTime if( (elapsed.tv_usec = now.tv_usec - m_startTime.tv_usec) < 0 ) { elapsed.tv_usec += 1000000; elapsed.tv_sec = now.tv_sec - m_startTime.tv_sec - 1; } else { elapsed.tv_sec = now.tv_sec - m_startTime.tv_sec; } elapse = (uint32_t)((elapsed.tv_sec * 1000) + (elapsed.tv_usec / 1000)); // never return 0 if (elapse == 0) elapse = 1; return elapse; }