diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e2635a8b..063f259e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -46,7 +46,7 @@ jobs: # platform-dependent settings - extra board options, board index URLs, skip patterns etc. include: - id: arduino:avr:uno - run: echo "skip-pattern=(STM32WL|SSTV|LoRaWAN|LR11x0_Firmware_Update|Pager)" >> $GITHUB_OUTPUT + run: echo "skip-pattern=(STM32WL|SSTV|LoRaWAN|LR11x0_Firmware_Update|Pager|APRS|Morse)" >> $GITHUB_OUTPUT - id: arduino:avr:mega run: | echo "options=':cpu=atmega2560'" >> $GITHUB_OUTPUT diff --git a/examples/SX127x/SX127x_Channel_Activity_Detection_Receive/SX127x_Channel_Activity_Detection_Receive.ino b/examples/SX127x/SX127x_Channel_Activity_Detection_Receive/SX127x_Channel_Activity_Detection_Receive.ino index 35f9d40d..f78d747a 100644 --- a/examples/SX127x/SX127x_Channel_Activity_Detection_Receive/SX127x_Channel_Activity_Detection_Receive.ino +++ b/examples/SX127x/SX127x_Channel_Activity_Detection_Receive/SX127x_Channel_Activity_Detection_Receive.ino @@ -168,9 +168,9 @@ void loop() { // check if we got a preamble if(detectedFlag) { - // LoRa preamble was detected + // LoRa preamble was detected, start reception with timeout of 100 LoRa symbols Serial.print(F("[SX1278] Preamble detected, starting reception ... ")); - state = radio.startReceive(0, RADIOLIB_SX127X_RXSINGLE); + state = radio.startReceive(100); if (state == RADIOLIB_ERR_NONE) { Serial.println(F("success!")); } else { diff --git a/src/TypeDef.h b/src/TypeDef.h index 55434cf1..de6e06b6 100644 --- a/src/TypeDef.h +++ b/src/TypeDef.h @@ -615,6 +615,12 @@ */ typedef unsigned long RadioLibTime_t; +/*! + \brief Type used for radio-agnostic IRQ flags. IRQ to enable corresponds to the bit index (RadioLibIrq_t). + For example, if bit 0 is set, the module will enable its RADIOLIB_IRQ_TX_DONE (if it is supported). +*/ +typedef uint32_t RadioLibIrqFlags_t; + /*! \} */ diff --git a/src/modules/LR11x0/LR11x0.cpp b/src/modules/LR11x0/LR11x0.cpp index b1ebbc44..1a0ac5a2 100644 --- a/src/modules/LR11x0/LR11x0.cpp +++ b/src/modules/LR11x0/LR11x0.cpp @@ -11,6 +11,16 @@ LR11x0::LR11x0(Module* mod) : PhysicalLayer(RADIOLIB_LR11X0_FREQUENCY_STEP_SIZE, RADIOLIB_LR11X0_MAX_PACKET_LENGTH) { this->mod = mod; this->XTAL = false; + this->irqMap[RADIOLIB_IRQ_TX_DONE] = RADIOLIB_LR11X0_IRQ_TX_DONE; + this->irqMap[RADIOLIB_IRQ_RX_DONE] = RADIOLIB_LR11X0_IRQ_RX_DONE; + this->irqMap[RADIOLIB_IRQ_PREAMBLE_DETECTED] = RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED; + this->irqMap[RADIOLIB_IRQ_SYNC_WORD_VALID] = RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID; + this->irqMap[RADIOLIB_IRQ_HEADER_VALID] = RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID; + this->irqMap[RADIOLIB_IRQ_HEADER_ERR] = RADIOLIB_LR11X0_IRQ_HEADER_ERR; + this->irqMap[RADIOLIB_IRQ_CRC_ERR] = RADIOLIB_LR11X0_IRQ_CRC_ERR; + this->irqMap[RADIOLIB_IRQ_CAD_DONE] = RADIOLIB_LR11X0_IRQ_CAD_DONE; + this->irqMap[RADIOLIB_IRQ_CAD_DETECTED] = RADIOLIB_LR11X0_IRQ_CAD_DETECTED; + this->irqMap[RADIOLIB_IRQ_TIMEOUT] = RADIOLIB_LR11X0_IRQ_TIMEOUT; } int16_t LR11x0::begin(float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, uint16_t preambleLength, float tcxoVoltage) { @@ -278,12 +288,23 @@ int16_t LR11x0::receiveDirect() { } int16_t LR11x0::scanChannel() { - return(this->scanChannel(RADIOLIB_LR11X0_CAD_PARAM_DEFAULT, RADIOLIB_LR11X0_CAD_PARAM_DEFAULT, RADIOLIB_LR11X0_CAD_PARAM_DEFAULT)); + ChannelScanConfig_t config = { + .cad = { + .symNum = RADIOLIB_LR11X0_CAD_PARAM_DEFAULT, + .detPeak = RADIOLIB_LR11X0_CAD_PARAM_DEFAULT, + .detMin = RADIOLIB_LR11X0_CAD_PARAM_DEFAULT, + .exitMode = RADIOLIB_LR11X0_CAD_PARAM_DEFAULT, + .timeout = 0, + .irqFlags = RADIOLIB_IRQ_CAD_DEFAULT_FLAGS, + .irqMask = RADIOLIB_IRQ_CAD_DEFAULT_MASK, + }, + }; + return(this->scanChannel(config)); } -int16_t LR11x0::scanChannel(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin) { +int16_t LR11x0::scanChannel(ChannelScanConfig_t config) { // set mode to CAD - int state = startChannelScan(symbolNum, detPeak, detMin); + int state = startChannelScan(config); RADIOLIB_ASSERT(state); // wait for channel activity detected or timeout @@ -460,10 +481,9 @@ int16_t LR11x0::startReceive(uint32_t timeout, uint32_t irqFlags, uint32_t irqMa // set DIO mapping uint32_t irq = irqFlags; if(timeout != RADIOLIB_LR11X0_RX_TIMEOUT_INF) { - irq |= RADIOLIB_LR11X0_IRQ_TIMEOUT; + irq |= (1UL << RADIOLIB_IRQ_TIMEOUT); } - - state = setDioIrqParams(irq); + state = setDioIrqParams(getIrqMapped(irq)); RADIOLIB_ASSERT(state); // clear interrupt flags @@ -541,10 +561,21 @@ int16_t LR11x0::readData(uint8_t* data, size_t len) { } int16_t LR11x0::startChannelScan() { - return(this->startChannelScan(RADIOLIB_LR11X0_CAD_PARAM_DEFAULT, RADIOLIB_LR11X0_CAD_PARAM_DEFAULT, RADIOLIB_LR11X0_CAD_PARAM_DEFAULT)); + ChannelScanConfig_t config = { + .cad = { + .symNum = RADIOLIB_LR11X0_CAD_PARAM_DEFAULT, + .detPeak = RADIOLIB_LR11X0_CAD_PARAM_DEFAULT, + .detMin = RADIOLIB_LR11X0_CAD_PARAM_DEFAULT, + .exitMode = RADIOLIB_LR11X0_CAD_PARAM_DEFAULT, + .timeout = 0, + .irqFlags = RADIOLIB_IRQ_CAD_DEFAULT_FLAGS, + .irqMask = RADIOLIB_IRQ_CAD_DEFAULT_MASK, + }, + }; + return(this->startChannelScan(config)); } -int16_t LR11x0::startChannelScan(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin) { +int16_t LR11x0::startChannelScan(const ChannelScanConfig_t &config) { // check active modem int16_t state = RADIOLIB_ERR_NONE; uint8_t modem = RADIOLIB_LR11X0_PACKET_TYPE_NONE; @@ -562,7 +593,8 @@ int16_t LR11x0::startChannelScan(uint8_t symbolNum, uint8_t detPeak, uint8_t det this->mod->setRfSwitchState(Module::MODE_RX); // set DIO pin mapping - state = setDioIrqParams(RADIOLIB_LR11X0_IRQ_CAD_DETECTED | RADIOLIB_LR11X0_IRQ_CAD_DONE); + uint16_t irqFlags = (config.cad.irqFlags == RADIOLIB_IRQ_NOT_SUPPORTED) ? RADIOLIB_LR11X0_IRQ_CAD_DETECTED | RADIOLIB_LR11X0_IRQ_CAD_DONE : config.cad.irqFlags; + state = setDioIrqParams(irqFlags, irqFlags); RADIOLIB_ASSERT(state); // clear interrupt flags @@ -570,7 +602,7 @@ int16_t LR11x0::startChannelScan(uint8_t symbolNum, uint8_t detPeak, uint8_t det RADIOLIB_ASSERT(state); // set mode to CAD - return(startCad(symbolNum, detPeak, detMin)); + return(startCad(config.cad.symNum, config.cad.detPeak, config.cad.detMin, config.cad.exitMode, config.cad.timeout)); } int16_t LR11x0::getChannelScanResult() { @@ -1322,39 +1354,16 @@ RadioLibTime_t LR11x0::calculateRxTimeout(RadioLibTime_t timeoutUs) { return(timeout); } -int16_t LR11x0::irqRxDoneRxTimeout(uint32_t &irqFlags, uint32_t &irqMask) { - irqFlags = RADIOLIB_LR11X0_IRQ_RX_DONE | RADIOLIB_LR11X0_IRQ_TIMEOUT; // flags that can appear in the IRQ register - irqMask = irqFlags; // on LR11x0, these are the same - return(RADIOLIB_ERR_NONE); +uint32_t LR11x0::getIrqFlags() { + return((uint32_t)this->getIrqStatus()); } -int16_t LR11x0::checkIrq(uint8_t irq) { - uint16_t flags = getIrqStatus(); - switch(irq) { - case RADIOLIB_IRQ_TX_DONE: - return(flags & RADIOLIB_LR11X0_IRQ_TX_DONE); - case RADIOLIB_IRQ_RX_DONE: - return(flags & RADIOLIB_LR11X0_IRQ_RX_DONE); - case RADIOLIB_IRQ_PREAMBLE_DETECTED: - return(flags & RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED); - case RADIOLIB_IRQ_SYNC_WORD_VALID: - return(flags & RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID); - case RADIOLIB_IRQ_HEADER_VALID: - return(flags & RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID); - case RADIOLIB_IRQ_HEADER_ERR: - return(flags & RADIOLIB_LR11X0_IRQ_HEADER_ERR); - case RADIOLIB_IRQ_CRC_ERR: - return(flags & RADIOLIB_LR11X0_IRQ_CRC_ERR); - case RADIOLIB_IRQ_CAD_DONE: - return(flags & RADIOLIB_LR11X0_IRQ_CAD_DONE); - case RADIOLIB_IRQ_CAD_DETECTED: - return(flags & RADIOLIB_LR11X0_IRQ_CAD_DETECTED); - case RADIOLIB_IRQ_TIMEOUT: - return(flags & RADIOLIB_LR11X0_IRQ_TIMEOUT); - default: - return(RADIOLIB_ERR_UNSUPPORTED); - } - return(RADIOLIB_ERR_UNSUPPORTED); +int16_t LR11x0::setIrqFlags(uint32_t irq) { + return(this->setDioIrqParams(irq, irq)); +} + +int16_t LR11x0::clearIrqFlags(uint32_t irq) { + return(this->clearIrq(irq)); } uint8_t LR11x0::randomByte() { @@ -2008,7 +2017,7 @@ int16_t LR11x0::setPacketMode(uint8_t mode, uint8_t len) { return(state); } -int16_t LR11x0::startCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin) { +int16_t LR11x0::startCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin, uint8_t exitMode, RadioLibTime_t timeout) { // check active modem uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; int16_t state = getPacketType(&type); @@ -2035,9 +2044,16 @@ int16_t LR11x0::startCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin) { min = 10; } + uint8_t mode = exitMode; + if(mode == RADIOLIB_LR11X0_CAD_PARAM_DEFAULT) { + mode = RADIOLIB_LR11X0_CAD_EXIT_MODE_STBY_RC; + } + + uint32_t timeout_raw = (float)timeout / 30.52f; + // set CAD parameters // TODO add configurable exit mode and timeout - state = setCadParams(num, peak, min, RADIOLIB_LR11X0_CAD_EXIT_MODE_STBY_RC, 0); + state = setCadParams(num, peak, min, mode, timeout_raw); RADIOLIB_ASSERT(state); // start CAD diff --git a/src/modules/LR11x0/LR11x0.h b/src/modules/LR11x0/LR11x0.h index dffac085..7c5737bd 100644 --- a/src/modules/LR11x0/LR11x0.h +++ b/src/modules/LR11x0/LR11x0.h @@ -844,12 +844,10 @@ class LR11x0: public PhysicalLayer { /*! \brief Performs scan for LoRa transmission in the current channel. Detects both preamble and payload. - \param symbolNum Number of symbols for CAD detection. - \param detPeak Peak value for CAD detection. - \param detMin Minimum value for CAD detection. + \param config CAD configuration structure. \returns \ref status_codes */ - int16_t scanChannel(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin); + int16_t scanChannel(ChannelScanConfig_t config) override; /*! \brief Sets the module to standby mode (overload for PhysicalLayer compatibility, uses 13 MHz RC oscillator). @@ -979,14 +977,12 @@ class LR11x0: public PhysicalLayer { int16_t startChannelScan() override; /*! - \brief Interrupt-driven channel activity detection method. IRQ1 will be activated + \brief Interrupt-driven channel activity detection method. IRQ pin will be activated when LoRa preamble is detected, or upon timeout. - \param symbolNum Number of symbols for CAD detection. - \param detPeak Peak value for CAD detection. - \param detMin Minimum value for CAD detection. + \param config CAD configuration structure. \returns \ref status_codes */ - int16_t startChannelScan(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin); + int16_t startChannelScan(const ChannelScanConfig_t &config) override; /*! \brief Read the channel scan result @@ -1221,18 +1217,24 @@ class LR11x0: public PhysicalLayer { RadioLibTime_t calculateRxTimeout(RadioLibTime_t timeoutUs) override; /*! - \brief Create the flags that make up RxDone and RxTimeout used for receiving downlinks - \param irqFlags The flags for which IRQs must be triggered - \param irqMask Mask indicating which IRQ triggers a DIO - \returns \ref status_codes + \brief Read currently active IRQ flags. + \returns IRQ flags. */ - int16_t irqRxDoneRxTimeout(uint32_t &irqFlags, uint32_t &irqMask) override; + uint32_t getIrqFlags() override; /*! - \brief Check whether a specific IRQ bit is set (e.g. RxTimeout, CadDone). - \returns Whether requested IRQ is set. + \brief Set interrupt on IRQ pin to be sent on a specific IRQ bit (e.g. RxTimeout, CadDone). + \param irq Module-specific IRQ flags. + \returns \ref status_codes */ - int16_t checkIrq(uint8_t irq) override; + int16_t setIrqFlags(uint32_t irq) override; + + /*! + \brief Clear interrupt on a specific IRQ bit (e.g. RxTimeout, CadDone). + \param irq Module-specific IRQ flags. + \returns \ref status_codes + */ + int16_t clearIrqFlags(uint32_t irq) override; /*! \brief Get one truly random byte from RSSI noise. @@ -1618,7 +1620,7 @@ class LR11x0: public PhysicalLayer { bool findChip(uint8_t ver); int16_t config(uint8_t modem); int16_t setPacketMode(uint8_t mode, uint8_t len); - int16_t startCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin); + int16_t startCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin, uint8_t exitMode, RadioLibTime_t timeout); int16_t setHeaderType(uint8_t hdrType, size_t len = 0xFF); // common methods to avoid some copy-paste diff --git a/src/modules/SX126x/SX126x.cpp b/src/modules/SX126x/SX126x.cpp index c6185e05..be314f6a 100644 --- a/src/modules/SX126x/SX126x.cpp +++ b/src/modules/SX126x/SX126x.cpp @@ -7,6 +7,16 @@ SX126x::SX126x(Module* mod) : PhysicalLayer(RADIOLIB_SX126X_FREQUENCY_STEP_SIZE, this->mod = mod; this->XTAL = false; this->standbyXOSC = false; + this->irqMap[RADIOLIB_IRQ_TX_DONE] = RADIOLIB_SX126X_IRQ_TX_DONE; + this->irqMap[RADIOLIB_IRQ_RX_DONE] = RADIOLIB_SX126X_IRQ_RX_DONE; + this->irqMap[RADIOLIB_IRQ_PREAMBLE_DETECTED] = RADIOLIB_SX126X_IRQ_PREAMBLE_DETECTED; + this->irqMap[RADIOLIB_IRQ_SYNC_WORD_VALID] = RADIOLIB_SX126X_IRQ_SYNC_WORD_VALID; + this->irqMap[RADIOLIB_IRQ_HEADER_VALID] = RADIOLIB_SX126X_IRQ_HEADER_VALID; + this->irqMap[RADIOLIB_IRQ_HEADER_ERR] = RADIOLIB_SX126X_IRQ_HEADER_ERR; + this->irqMap[RADIOLIB_IRQ_CRC_ERR] = RADIOLIB_SX126X_IRQ_CRC_ERR; + this->irqMap[RADIOLIB_IRQ_CAD_DONE] = RADIOLIB_SX126X_IRQ_CAD_DONE; + this->irqMap[RADIOLIB_IRQ_CAD_DETECTED] = RADIOLIB_SX126X_IRQ_CAD_DETECTED; + this->irqMap[RADIOLIB_IRQ_TIMEOUT] = RADIOLIB_SX126X_IRQ_TIMEOUT; } int16_t SX126x::begin(uint8_t cr, uint8_t syncWord, uint16_t preambleLength, float tcxoVoltage, bool useRegulatorLDO) { @@ -317,7 +327,7 @@ int16_t SX126x::receive(uint8_t* data, size_t len) { } // check whether this was a timeout or not - if((getIrqStatus() & RADIOLIB_SX126X_IRQ_TIMEOUT) || softTimeout) { + if((getIrqFlags() & RADIOLIB_SX126X_IRQ_TIMEOUT) || softTimeout) { standby(); fixImplicitTimeout(); clearIrqStatus(); @@ -425,12 +435,23 @@ int16_t SX126x::packetMode() { } int16_t SX126x::scanChannel() { - return(this->scanChannel(RADIOLIB_SX126X_CAD_PARAM_DEFAULT, RADIOLIB_SX126X_CAD_PARAM_DEFAULT, RADIOLIB_SX126X_CAD_PARAM_DEFAULT)); + ChannelScanConfig_t config = { + .cad = { + .symNum = RADIOLIB_SX126X_CAD_PARAM_DEFAULT, + .detPeak = RADIOLIB_SX126X_CAD_PARAM_DEFAULT, + .detMin = RADIOLIB_SX126X_CAD_PARAM_DEFAULT, + .exitMode = RADIOLIB_SX126X_CAD_PARAM_DEFAULT, + .timeout = 0, + .irqFlags = RADIOLIB_IRQ_CAD_DEFAULT_FLAGS, + .irqMask = RADIOLIB_IRQ_CAD_DEFAULT_MASK, + }, + }; + return(this->scanChannel(config)); } -int16_t SX126x::scanChannel(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin) { +int16_t SX126x::scanChannel(ChannelScanConfig_t config) { // set mode to CAD - int state = startChannelScan(symbolNum, detPeak, detMin); + int state = startChannelScan(config); RADIOLIB_ASSERT(state); // wait for channel activity detected or timeout @@ -442,7 +463,6 @@ int16_t SX126x::scanChannel(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin) return(getChannelScanResult()); } - int16_t SX126x::sleep() { return(SX126x::sleep(true)); } @@ -582,10 +602,10 @@ int16_t SX126x::finishTransmit() { } int16_t SX126x::startReceive() { - return(this->startReceive(RADIOLIB_SX126X_RX_TIMEOUT_INF, RADIOLIB_SX126X_IRQ_RX_DEFAULT, RADIOLIB_SX126X_IRQ_RX_DONE, 0)); + return(this->startReceive(RADIOLIB_SX126X_RX_TIMEOUT_INF, RADIOLIB_IRQ_RX_DEFAULT_FLAGS, RADIOLIB_IRQ_RX_DEFAULT_MASK, 0)); } -int16_t SX126x::startReceive(uint32_t timeout, uint32_t irqFlags, uint32_t irqMask, size_t len) { +int16_t SX126x::startReceive(uint32_t timeout, RadioLibIrqFlags_t irqFlags, RadioLibIrqFlags_t irqMask, size_t len) { (void)len; int16_t state = startReceiveCommon(timeout, irqFlags, irqMask); RADIOLIB_ASSERT(state); @@ -599,7 +619,7 @@ int16_t SX126x::startReceive(uint32_t timeout, uint32_t irqFlags, uint32_t irqMa return(state); } -int16_t SX126x::startReceiveDutyCycle(uint32_t rxPeriod, uint32_t sleepPeriod, uint16_t irqFlags, uint16_t irqMask) { +int16_t SX126x::startReceiveDutyCycle(uint32_t rxPeriod, uint32_t sleepPeriod, RadioLibIrqFlags_t irqFlags, RadioLibIrqFlags_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 = this->tcxoDelay + 1000; sleepPeriod -= transitionTime; @@ -626,7 +646,7 @@ int16_t SX126x::startReceiveDutyCycle(uint32_t rxPeriod, uint32_t sleepPeriod, u return(this->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) { +int16_t SX126x::startReceiveDutyCycleAuto(uint16_t senderPreambleLength, uint16_t minSymbols, RadioLibIrqFlags_t irqFlags, RadioLibIrqFlags_t irqMask) { if(senderPreambleLength == 0) { senderPreambleLength = this->preambleLengthLoRa; } @@ -664,12 +684,12 @@ int16_t SX126x::startReceiveDutyCycleAuto(uint16_t senderPreambleLength, uint16_ return(startReceiveDutyCycle(wakePeriod, sleepPeriod, irqFlags, irqMask)); } -int16_t SX126x::startReceiveCommon(uint32_t timeout, uint16_t irqFlags, uint16_t irqMask) { +int16_t SX126x::startReceiveCommon(uint32_t timeout, RadioLibIrqFlags_t irqFlags, RadioLibIrqFlags_t irqMask) { // set DIO mapping if(timeout != RADIOLIB_SX126X_RX_TIMEOUT_INF) { - irqMask |= RADIOLIB_SX126X_IRQ_TIMEOUT; + irqMask |= (1UL << RADIOLIB_IRQ_TIMEOUT); } - int16_t state = setDioIrqParams(irqFlags, irqMask); + int16_t state = setDioIrqParams(getIrqMapped(irqFlags), getIrqMapped(irqMask)); RADIOLIB_ASSERT(state); // set buffer pointers @@ -697,14 +717,14 @@ int16_t SX126x::readData(uint8_t* data, size_t len) { // 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 = this->mod->SPIcheckStream(); - if((state == RADIOLIB_ERR_SPI_CMD_TIMEOUT) && (getIrqStatus() & RADIOLIB_SX126X_IRQ_TIMEOUT)) { + if((state == RADIOLIB_ERR_SPI_CMD_TIMEOUT) && (getIrqFlags() & RADIOLIB_SX126X_IRQ_TIMEOUT)) { // this is definitely Rx timeout return(RADIOLIB_ERR_RX_TIMEOUT); } RADIOLIB_ASSERT(state); // check integrity CRC - uint16_t irq = getIrqStatus(); + uint16_t irq = getIrqFlags(); int16_t crcState = RADIOLIB_ERR_NONE; if((irq & RADIOLIB_SX126X_IRQ_CRC_ERR) || (irq & RADIOLIB_SX126X_IRQ_HEADER_ERR)) { crcState = RADIOLIB_ERR_CRC_MISMATCH; @@ -732,10 +752,21 @@ int16_t SX126x::readData(uint8_t* data, size_t len) { } int16_t SX126x::startChannelScan() { - return(this->startChannelScan(RADIOLIB_SX126X_CAD_PARAM_DEFAULT, RADIOLIB_SX126X_CAD_PARAM_DEFAULT, RADIOLIB_SX126X_CAD_PARAM_DEFAULT)); + ChannelScanConfig_t config = { + .cad = { + .symNum = RADIOLIB_SX126X_CAD_PARAM_DEFAULT, + .detPeak = RADIOLIB_SX126X_CAD_PARAM_DEFAULT, + .detMin = RADIOLIB_SX126X_CAD_PARAM_DEFAULT, + .exitMode = RADIOLIB_SX126X_CAD_PARAM_DEFAULT, + .timeout = 0, + .irqFlags = RADIOLIB_IRQ_CAD_DEFAULT_FLAGS, + .irqMask = RADIOLIB_IRQ_CAD_DEFAULT_MASK, + }, + }; + return(this->startChannelScan(config)); } -int16_t SX126x::startChannelScan(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin) { +int16_t SX126x::startChannelScan(const ChannelScanConfig_t &config) { // check active modem if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_LORA) { return(RADIOLIB_ERR_WRONG_MODEM); @@ -749,7 +780,7 @@ int16_t SX126x::startChannelScan(uint8_t symbolNum, uint8_t detPeak, uint8_t det this->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); + state = setDioIrqParams(getIrqMapped(config.cad.irqFlags), getIrqMapped(config.cad.irqMask)); RADIOLIB_ASSERT(state); // clear interrupt flags @@ -757,7 +788,7 @@ int16_t SX126x::startChannelScan(uint8_t symbolNum, uint8_t detPeak, uint8_t det RADIOLIB_ASSERT(state); // set mode to CAD - state = setCad(symbolNum, detPeak, detMin); + state = setCad(config.cad.symNum, config.cad.detPeak, config.cad.detMin, config.cad.exitMode, config.cad.timeout); return(state); } @@ -768,7 +799,7 @@ int16_t SX126x::getChannelScanResult() { } // check CAD result - uint16_t cadResult = getIrqStatus(); + uint16_t cadResult = getIrqFlags(); if(cadResult & RADIOLIB_SX126X_IRQ_CAD_DETECTED) { // detected some LoRa activity return(RADIOLIB_LORA_DETECTED); @@ -1460,39 +1491,18 @@ RadioLibTime_t SX126x::calculateRxTimeout(RadioLibTime_t timeoutUs) { return(timeout); } -int16_t SX126x::irqRxDoneRxTimeout(uint32_t &irqFlags, uint32_t &irqMask) { - irqFlags = RADIOLIB_SX126X_IRQ_RX_DEFAULT; // flags that can appear in the IRQ register - irqMask = RADIOLIB_SX126X_IRQ_RX_DONE | RADIOLIB_SX126X_IRQ_TIMEOUT; // flags that will trigger DIO0 - return(RADIOLIB_ERR_NONE); +uint32_t SX126x::getIrqFlags() { + uint8_t data[] = { 0x00, 0x00 }; + this->mod->SPIreadStream(RADIOLIB_SX126X_CMD_GET_IRQ_STATUS, data, 2); + return(((uint32_t)(data[0]) << 8) | data[1]); } -int16_t SX126x::checkIrq(uint8_t irq) { - uint16_t flags = getIrqStatus(); - switch(irq) { - case RADIOLIB_IRQ_TX_DONE: - return(flags & RADIOLIB_SX126X_IRQ_TX_DONE); - case RADIOLIB_IRQ_RX_DONE: - return(flags & RADIOLIB_SX126X_IRQ_RX_DONE); - case RADIOLIB_IRQ_PREAMBLE_DETECTED: - return(flags & RADIOLIB_SX126X_IRQ_PREAMBLE_DETECTED); - case RADIOLIB_IRQ_SYNC_WORD_VALID: - return(flags & RADIOLIB_SX126X_IRQ_SYNC_WORD_VALID); - case RADIOLIB_IRQ_HEADER_VALID: - return(flags & RADIOLIB_SX126X_IRQ_HEADER_VALID); - case RADIOLIB_IRQ_HEADER_ERR: - return(flags & RADIOLIB_SX126X_IRQ_HEADER_ERR); - case RADIOLIB_IRQ_CRC_ERR: - return(flags & RADIOLIB_SX126X_IRQ_CRC_ERR); - case RADIOLIB_IRQ_CAD_DONE: - return(flags & RADIOLIB_SX126X_IRQ_CAD_DONE); - case RADIOLIB_IRQ_CAD_DETECTED: - return(flags & RADIOLIB_SX126X_IRQ_CAD_DETECTED); - case RADIOLIB_IRQ_TIMEOUT: - return(flags & RADIOLIB_SX126X_IRQ_TIMEOUT); - default: - return(RADIOLIB_ERR_UNSUPPORTED); - } - return(RADIOLIB_ERR_UNSUPPORTED); +int16_t SX126x::setIrqFlags(uint32_t irq) { + return(this->setDioIrqParams(irq, irq)); +} + +int16_t SX126x::clearIrqFlags(uint32_t irq) { + return(this->clearIrqStatus(irq)); } int16_t SX126x::implicitHeader(size_t len) { @@ -1761,8 +1771,7 @@ int16_t SX126x::setRx(uint32_t timeout) { return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_RX, data, 3, true, false)); } - -int16_t SX126x::setCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin) { +int16_t SX126x::setCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin, uint8_t exitMode, RadioLibTime_t timeout) { // default CAD parameters are shown in Semtech AN1200.48, page 41. const uint8_t detPeakValues[6] = { 22, 22, 24, 25, 26, 30}; @@ -1773,29 +1782,17 @@ int16_t SX126x::setCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin) { this->spreadingFactor = 12; } - // build the packet + // build the packet with default configuration uint8_t data[7]; data[0] = RADIOLIB_SX126X_CAD_ON_2_SYMB; data[1] = detPeakValues[this->spreadingFactor - 7]; data[2] = RADIOLIB_SX126X_CAD_PARAM_DET_MIN; data[3] = RADIOLIB_SX126X_CAD_GOTO_STDBY; - data[4] = 0x00; - data[5] = 0x00; - data[6] = 0x00; + uint32_t timeout_raw = (float)timeout / 15.625f; + data[4] = (uint8_t)((timeout_raw >> 16) & 0xFF); + data[5] = (uint8_t)((timeout_raw >> 8) & 0xFF); + data[6] = (uint8_t)(timeout_raw & 0xFF); - - /* - CAD Configuration Note: - The default CAD configuration applied by `scanChannel` overrides the optimal SF-specific configurations, leading to suboptimal detection. - I.e., anything that is not RADIOLIB_SX126X_CAD_PARAM_DEFAULT is overridden. But CAD settings are SF specific. - To address this, the user override has been commented out, ensuring consistent application of the optimal CAD settings as - per Semtech's Application Note AN1200.48 (page 41) for the 125KHz setting. This approach significantly reduces false CAD occurrences. - Testing has shown that there is no reason for a user to change CAD settings for anything other than most optimal ones described in AN1200.48 . - However, this change does not respect CAD configs from the LoRaWAN layer. Future considerations or use cases might require revisiting this decision. - Hence this note. -*/ - -/* // set user-provided values if(symbolNum != RADIOLIB_SX126X_CAD_PARAM_DEFAULT) { data[0] = symbolNum; @@ -1809,10 +1806,9 @@ int16_t SX126x::setCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin) { data[2] = detMin; } -*/ - (void)symbolNum; - (void)detPeak; - (void)detMin; + if(exitMode != RADIOLIB_SX126X_CAD_PARAM_DEFAULT) { + data[3] = exitMode; + } // configure parameters int16_t state = this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_CAD_PARAMS, data, 7); @@ -1859,12 +1855,6 @@ int16_t SX126x::setDioIrqParams(uint16_t irqMask, uint16_t dio1Mask, uint16_t di return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_DIO_IRQ_PARAMS, data, 8)); } -uint16_t SX126x::getIrqStatus() { - uint8_t data[] = { 0x00, 0x00 }; - this->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(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_CLEAR_IRQ_STATUS, data, 2)); diff --git a/src/modules/SX126x/SX126x.h b/src/modules/SX126x/SX126x.h index 68598650..143a25e0 100644 --- a/src/modules/SX126x/SX126x.h +++ b/src/modules/SX126x/SX126x.h @@ -222,7 +222,6 @@ #define RADIOLIB_SX126X_IRQ_PREAMBLE_DETECTED 0b0000000000000100 // 2 2 preamble detected #define RADIOLIB_SX126X_IRQ_RX_DONE 0b0000000000000010 // 1 1 packet received #define RADIOLIB_SX126X_IRQ_TX_DONE 0b0000000000000001 // 0 0 packet transmission completed -#define RADIOLIB_SX126X_IRQ_RX_DEFAULT 0b0000001001100010 // 14 0 default for Rx (RX_DONE, TIMEOUT, CRC_ERR and HEADER_ERR) #define RADIOLIB_SX126X_IRQ_ALL 0b0100001111111111 // 14 0 all interrupts #define RADIOLIB_SX126X_IRQ_NONE 0b0000000000000000 // 14 0 no interrupts @@ -533,18 +532,17 @@ class SX126x: public PhysicalLayer { /*! \brief Performs scan for LoRa transmission in the current channel. Detects both preamble and payload. + Configuration defaults to the values recommended by AN1200.48. \returns \ref status_codes */ int16_t scanChannel() override; /*! \brief Performs scan for LoRa transmission in the current channel. Detects both preamble and payload. - \param symbolNum Number of symbols for CAD detection. Defaults to the value recommended by AN1200.48. - \param detPeak Peak value for CAD detection. Defaults to the value recommended by AN1200.48. - \param detMin Minimum value for CAD detection. Defaults to the value recommended by AN1200.48. + \param config CAD configuration structure. \returns \ref status_codes */ - int16_t scanChannel(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin); + int16_t scanChannel(ChannelScanConfig_t config) override; /*! \brief Sets the module to sleep mode. To wake the device up, call standby(). @@ -656,24 +654,26 @@ class SX126x: public PhysicalLayer { For any other value, timeout will be applied and signal will be generated on DIO1 for conditions defined by irqFlags and irqMask. - \param irqFlags Sets the IRQ flags, defaults to RADIOLIB_SX126X_IRQ_RX_DEFAULT. - \param irqMask Sets the mask of IRQ flags that will trigger DIO1, defaults to RADIOLIB_SX126X_IRQ_RX_DONE. + \param irqFlags Sets the IRQ flags, defaults to RX done, RX timeout, CRC error and header error. + \param irqMask Sets the mask of IRQ flags that will trigger DIO1, defaults to RX done. \param len Only for PhysicalLayer compatibility, not used. \returns \ref status_codes */ - int16_t startReceive(uint32_t timeout, uint32_t irqFlags = RADIOLIB_SX126X_IRQ_RX_DEFAULT, uint32_t irqMask = RADIOLIB_SX126X_IRQ_RX_DONE, size_t len = 0); + int16_t startReceive(uint32_t timeout, RadioLibIrqFlags_t irqFlags = RADIOLIB_IRQ_RX_DEFAULT_FLAGS, RadioLibIrqFlags_t irqMask = RADIOLIB_IRQ_RX_DEFAULT_MASK, size_t len = 0); /*! \brief Interrupt-driven receive method where the device mostly sleeps and periodically wakes to listen. Note that this function assumes the unit will take 500us + TCXO_delay to change state. See datasheet section 13.1.7, version 1.2. + \param rxPeriod The duration the receiver will be in Rx mode, in microseconds. \param sleepPeriod The duration the receiver will not be in Rx mode, in microseconds. - \param irqFlags Sets the IRQ flags, defaults to RADIOLIB_SX126X_IRQ_RX_DEFAULT. - \param irqMask Sets the mask of IRQ flags that will trigger DIO1, defaults to RADIOLIB_SX126X_IRQ_RX_DONE. + + \param irqFlags Sets the IRQ flags, defaults to RX done, RX timeout, CRC error and header error. + \param irqMask Sets the mask of IRQ flags that will trigger DIO1, defaults to RX done. \returns \ref status_codes */ - int16_t startReceiveDutyCycle(uint32_t rxPeriod, uint32_t sleepPeriod, uint16_t irqFlags = RADIOLIB_SX126X_IRQ_RX_DEFAULT, uint16_t irqMask = RADIOLIB_SX126X_IRQ_RX_DONE); + int16_t startReceiveDutyCycle(uint32_t rxPeriod, uint32_t sleepPeriod, RadioLibIrqFlags_t irqFlags = RADIOLIB_IRQ_RX_DEFAULT_FLAGS, RadioLibIrqFlags_t irqMask = RADIOLIB_IRQ_RX_DEFAULT_MASK); /*! \brief Calls \ref startReceiveDutyCycle with rxPeriod and sleepPeriod set so the unit shouldn't miss any messages. @@ -685,17 +685,11 @@ class SX126x: public PhysicalLayer { According to Semtech, receiver requires 8 symbols to reliably latch a preamble. This makes this method redundant when transmitter preamble length is less than 17 (2*minSymbols + 1). - \param irqFlags Sets the IRQ flags, defaults to RADIOLIB_SX126X_IRQ_RX_DEFAULT. - \param irqMask Sets the mask of IRQ flags that will trigger DIO1, defaults to RADIOLIB_SX126X_IRQ_RX_DONE. + \param irqFlags Sets the IRQ flags, defaults to RX done, RX timeout, CRC error and header error. + \param irqMask Sets the mask of IRQ flags that will trigger DIO1, defaults to RX done. \returns \ref status_codes */ - int16_t startReceiveDutyCycleAuto(uint16_t senderPreambleLength = 0, uint16_t minSymbols = 8, uint16_t irqFlags = RADIOLIB_SX126X_IRQ_RX_DEFAULT, uint16_t irqMask = RADIOLIB_SX126X_IRQ_RX_DONE); - - /*! - \brief Reads the current IRQ status. - \returns IRQ status bits - */ - uint16_t getIrqStatus(); + int16_t startReceiveDutyCycleAuto(uint16_t senderPreambleLength = 0, uint16_t minSymbols = 8, RadioLibIrqFlags_t irqFlags = RADIOLIB_IRQ_RX_DEFAULT_FLAGS, RadioLibIrqFlags_t irqMask = RADIOLIB_IRQ_RX_DEFAULT_MASK); /*! \brief Reads data received after calling startReceive method. When the packet length is not known in advance, @@ -717,12 +711,10 @@ class SX126x: public PhysicalLayer { /*! \brief Interrupt-driven channel activity detection method. DIO1 will be activated when LoRa preamble is detected, or upon timeout. - \param symbolNum Number of symbols for CAD detection. - \param detPeak Peak value for CAD detection. - \param detMin Minimum value for CAD detection. + \param config CAD configuration structure. \returns \ref status_codes */ - int16_t startChannelScan(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin); + int16_t startChannelScan(const ChannelScanConfig_t &config) override; /*! \brief Read the channel scan result @@ -989,18 +981,24 @@ class SX126x: public PhysicalLayer { RadioLibTime_t calculateRxTimeout(RadioLibTime_t timeoutUs) override; /*! - \brief Create the flags that make up RxDone and RxTimeout used for receiving downlinks - \param irqFlags The flags for which IRQs must be triggered - \param irqMask Mask indicating which IRQ triggers a DIO - \returns \ref status_codes + \brief Read currently active IRQ flags. + \returns IRQ flags. */ - int16_t irqRxDoneRxTimeout(uint32_t &irqFlags, uint32_t &irqMask) override; + uint32_t getIrqFlags() override; /*! - \brief Check whether a specific IRQ bit is set (e.g. RxTimeout, CadDone). - \returns Whether requested IRQ is set. + \brief Set interrupt on DIO1 to be sent on a specific IRQ bit (e.g. RxTimeout, CadDone). + \param irq Module-specific IRQ flags. + \returns \ref status_codes */ - int16_t checkIrq(uint8_t irq) override; + int16_t setIrqFlags(uint32_t irq) override; + + /*! + \brief Clear interrupt on a specific IRQ bit (e.g. RxTimeout, CadDone). + \param irq Module-specific IRQ flags. + \returns \ref status_codes + */ + int16_t clearIrqFlags(uint32_t irq) override; /*! \brief Set implicit header mode for future reception/transmission. @@ -1160,7 +1158,7 @@ class SX126x: public PhysicalLayer { int16_t setFs(); int16_t setTx(uint32_t timeout = 0); int16_t setRx(uint32_t timeout); - int16_t setCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin); + int16_t setCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin, uint8_t exitMode, RadioLibTime_t timeout); int16_t writeRegister(uint16_t addr, uint8_t* data, uint8_t numBytes); int16_t readRegister(uint16_t addr, uint8_t* data, uint8_t numBytes); int16_t writeBuffer(uint8_t* data, uint8_t numBytes, uint8_t offset = 0x00); @@ -1222,7 +1220,7 @@ class SX126x: public PhysicalLayer { int16_t config(uint8_t modem); bool findChip(const char* verStr); - int16_t startReceiveCommon(uint32_t timeout = RADIOLIB_SX126X_RX_TIMEOUT_INF, uint16_t irqFlags = RADIOLIB_SX126X_IRQ_RX_DEFAULT, uint16_t irqMask = RADIOLIB_SX126X_IRQ_RX_DONE); + int16_t startReceiveCommon(uint32_t timeout = RADIOLIB_SX126X_RX_TIMEOUT_INF, RadioLibIrqFlags_t irqFlags = RADIOLIB_IRQ_RX_DEFAULT_FLAGS, RadioLibIrqFlags_t irqMask = RADIOLIB_IRQ_RX_DEFAULT_MASK); int16_t setPacketMode(uint8_t mode, uint8_t len); int16_t setHeaderType(uint8_t hdrType, size_t len = 0xFF); int16_t directMode(); diff --git a/src/modules/SX127x/SX127x.cpp b/src/modules/SX127x/SX127x.cpp index 778dc9e9..b43bf6f3 100644 --- a/src/modules/SX127x/SX127x.cpp +++ b/src/modules/SX127x/SX127x.cpp @@ -12,6 +12,18 @@ int16_t SX127x::begin(uint8_t* chipVersions, uint8_t numVersions, uint8_t syncWo this->mod->hal->pinMode(this->mod->getIrq(), this->mod->hal->GpioModeInput); this->mod->hal->pinMode(this->mod->getGpio(), this->mod->hal->GpioModeInput); + // set IRQ mapping - it is different for LoRa and FSK mode + this->irqMap[RADIOLIB_IRQ_TX_DONE] = RADIOLIB_SX127X_CLEAR_IRQ_FLAG_TX_DONE; + this->irqMap[RADIOLIB_IRQ_RX_DONE] = RADIOLIB_SX127X_CLEAR_IRQ_FLAG_RX_DONE; + this->irqMap[RADIOLIB_IRQ_PREAMBLE_DETECTED] = RADIOLIB_IRQ_NOT_SUPPORTED; + this->irqMap[RADIOLIB_IRQ_SYNC_WORD_VALID] = RADIOLIB_IRQ_NOT_SUPPORTED; + this->irqMap[RADIOLIB_IRQ_HEADER_VALID] = RADIOLIB_SX127X_CLEAR_IRQ_FLAG_VALID_HEADER; + this->irqMap[RADIOLIB_IRQ_HEADER_ERR] = RADIOLIB_IRQ_NOT_SUPPORTED; + this->irqMap[RADIOLIB_IRQ_CRC_ERR] = RADIOLIB_SX127X_CLEAR_IRQ_FLAG_PAYLOAD_CRC_ERROR; + this->irqMap[RADIOLIB_IRQ_CAD_DONE] = RADIOLIB_SX127X_CLEAR_IRQ_FLAG_CAD_DONE; + this->irqMap[RADIOLIB_IRQ_CAD_DETECTED] = RADIOLIB_SX127X_CLEAR_IRQ_FLAG_CAD_DETECTED; + this->irqMap[RADIOLIB_IRQ_TIMEOUT] = RADIOLIB_SX127X_CLEAR_IRQ_FLAG_RX_TIMEOUT; + // try to find the SX127x chip if(!SX127x::findChip(chipVersions, numVersions)) { RADIOLIB_DEBUG_BASIC_PRINTLN("No SX127x found!"); @@ -63,6 +75,18 @@ int16_t SX127x::beginFSK(uint8_t* chipVersions, uint8_t numVersions, float freqD this->mod->hal->pinMode(this->mod->getIrq(), this->mod->hal->GpioModeInput); this->mod->hal->pinMode(this->mod->getGpio(), this->mod->hal->GpioModeInput); + // set IRQ mapping - it is different for LoRa and FSK mode + this->irqMap[RADIOLIB_IRQ_TX_DONE] = RADIOLIB_SX127X_FLAG_PACKET_SENT << 8; + this->irqMap[RADIOLIB_IRQ_RX_DONE] = RADIOLIB_SX127X_FLAG_PAYLOAD_READY << 8; + this->irqMap[RADIOLIB_IRQ_PREAMBLE_DETECTED] = RADIOLIB_SX127X_FLAG_PREAMBLE_DETECT << 0; + this->irqMap[RADIOLIB_IRQ_SYNC_WORD_VALID] = RADIOLIB_SX127X_FLAG_SYNC_ADDRESS_MATCH << 0; + this->irqMap[RADIOLIB_IRQ_HEADER_VALID] = RADIOLIB_IRQ_NOT_SUPPORTED; + this->irqMap[RADIOLIB_IRQ_HEADER_ERR] = RADIOLIB_IRQ_NOT_SUPPORTED; + this->irqMap[RADIOLIB_IRQ_CRC_ERR] = RADIOLIB_IRQ_NOT_SUPPORTED; + this->irqMap[RADIOLIB_IRQ_CAD_DONE] = RADIOLIB_IRQ_NOT_SUPPORTED; + this->irqMap[RADIOLIB_IRQ_CAD_DETECTED] = RADIOLIB_IRQ_NOT_SUPPORTED; + this->irqMap[RADIOLIB_IRQ_TIMEOUT] = RADIOLIB_SX127X_FLAG_TIMEOUT << 0; + // try to find the SX127x chip if(!SX127x::findChip(chipVersions, numVersions)) { RADIOLIB_DEBUG_BASIC_PRINTLN("No SX127x found!"); @@ -193,7 +217,7 @@ int16_t SX127x::receive(uint8_t* data, size_t len) { int16_t modem = getActiveModem(); if(modem == RADIOLIB_SX127X_LORA) { // set mode to receive - state = startReceive(len, RADIOLIB_SX127X_RXSINGLE); + state = startReceive(100, RADIOLIB_IRQ_RX_DEFAULT_FLAGS, RADIOLIB_IRQ_RX_DEFAULT_MASK, len); RADIOLIB_ASSERT(state); // if no DIO1 is provided, use software timeout (100 LoRa symbols, same as hardware timeout) @@ -211,13 +235,13 @@ int16_t SX127x::receive(uint8_t* data, size_t len) { if(this->mod->getGpio() == RADIOLIB_NC) { // no GPIO pin provided, use software timeout if(this->mod->hal->millis() - start > timeout) { - clearIRQFlags(); + clearIrqFlags(RADIOLIB_SX127X_FLAGS_ALL); return(RADIOLIB_ERR_RX_TIMEOUT); } } else { // GPIO provided, use that if(this->mod->hal->digitalRead(this->mod->getGpio())) { - clearIRQFlags(); + clearIrqFlags(RADIOLIB_SX127X_FLAGS_ALL); return(RADIOLIB_ERR_RX_TIMEOUT); } } @@ -229,7 +253,7 @@ int16_t SX127x::receive(uint8_t* data, size_t len) { RadioLibTime_t timeout = (getTimeOnAir(len) * 5) / 1000; // set mode to receive - state = startReceive(len, RADIOLIB_SX127X_RX); + state = startReceive(0, RADIOLIB_IRQ_RX_DEFAULT_FLAGS, RADIOLIB_IRQ_RX_DEFAULT_MASK, len); RADIOLIB_ASSERT(state); // wait for packet reception or timeout @@ -237,7 +261,7 @@ int16_t SX127x::receive(uint8_t* data, size_t len) { while(!this->mod->hal->digitalRead(this->mod->getIrq())) { this->mod->hal->yield(); if(this->mod->hal->millis() - start > timeout) { - clearIRQFlags(); + clearIrqFlags(RADIOLIB_SX127X_FLAGS_ALL); return(RADIOLIB_ERR_RX_TIMEOUT); } } @@ -362,21 +386,35 @@ int16_t SX127x::packetMode() { } int16_t SX127x::startReceive() { - return(this->startReceive(0, RADIOLIB_SX127X_RXCONTINUOUS)); + return(this->startReceive(0, RADIOLIB_IRQ_RX_DEFAULT_FLAGS, RADIOLIB_IRQ_RX_DEFAULT_MASK, 0)); } -int16_t SX127x::startReceive(uint8_t len, uint8_t mode) { +int16_t SX127x::startReceive(uint32_t timeout, RadioLibIrqFlags_t irqFlags, RadioLibIrqFlags_t irqMask, size_t len) { + uint8_t mode = RADIOLIB_SX127X_RXCONTINUOUS; + // set mode to standby int16_t state = setMode(RADIOLIB_SX127X_STANDBY); RADIOLIB_ASSERT(state); + // set DIO pin mapping + state = this->setIrqFlags(getIrqMapped(irqFlags & irqMask)); + RADIOLIB_ASSERT(state); + int16_t modem = getActiveModem(); if(modem == RADIOLIB_SX127X_LORA) { - // set DIO pin mapping + if(timeout != 0) { + // for non-zero timeout value, change mode to Rx single and set the timeout + mode = RADIOLIB_SX127X_RXSINGLE; + uint8_t msb_sym = (timeout > 0x3FF) ? 0x3 : (uint8_t)(timeout >> 8); + uint8_t lsb_sym = (timeout > 0x3FF) ? 0xFF : (uint8_t)(timeout & 0xFF); + state = this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_MODEM_CONFIG_2, msb_sym, 1, 0); + state |= this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_SYMB_TIMEOUT_LSB, lsb_sym); + RADIOLIB_ASSERT(state); + } + + // in FHSS mode, enable channel change interrupt if(this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_HOP_PERIOD) > RADIOLIB_SX127X_HOP_PERIOD_OFF) { - state = this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO0_LORA_RX_DONE | RADIOLIB_SX127X_DIO1_LORA_FHSS_CHANGE_CHANNEL, 7, 4); - } else { - state = this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO0_LORA_RX_DONE | RADIOLIB_SX127X_DIO1_LORA_RX_TIMEOUT, 7, 4); + state = this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO1_LORA_FHSS_CHANGE_CHANNEL, 5, 4); } // set expected packet length for SF6 @@ -389,7 +427,7 @@ int16_t SX127x::startReceive(uint8_t len, uint8_t mode) { RADIOLIB_ERRATA_SX127X(true); // clear interrupt flags - clearIRQFlags(); + clearIrqFlags(RADIOLIB_SX127X_FLAGS_ALL); // set FIFO pointers state |= this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_FIFO_RX_BASE_ADDR, RADIOLIB_SX127X_FIFO_RX_BASE_ADDR_MAX); @@ -397,19 +435,11 @@ int16_t SX127x::startReceive(uint8_t len, uint8_t mode) { RADIOLIB_ASSERT(state); } else if(modem == RADIOLIB_SX127X_FSK_OOK) { - // set DIO pin mapping - state = this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO0_PACK_PAYLOAD_READY, 7, 6); - RADIOLIB_ASSERT(state); - // clear interrupt flags - clearIRQFlags(); + clearIrqFlags(RADIOLIB_SX127X_FLAGS_ALL); // FSK modem does not distinguish between Rx single and continuous - if(mode == RADIOLIB_SX127X_RXCONTINUOUS) { - // set RF switch (if present) - this->mod->setRfSwitchState(Module::MODE_RX); - return(setMode(RADIOLIB_SX127X_RX)); - } + mode = RADIOLIB_SX127X_RX; } // set RF switch (if present) @@ -419,22 +449,6 @@ int16_t SX127x::startReceive(uint8_t len, uint8_t mode) { return(setMode(mode)); } -int16_t SX127x::startReceive(uint32_t timeout, uint32_t irqFlags, uint32_t irqMask, size_t len) { - (void)irqFlags; - (void)irqMask; - uint8_t mode = RADIOLIB_SX127X_RXCONTINUOUS; - if(timeout != 0) { - // for non-zero timeout value, change mode to Rx single and set the timeout - mode = RADIOLIB_SX127X_RXSINGLE; - uint8_t msb_sym = (timeout > 0x3FF) ? 0x3 : (uint8_t)(timeout >> 8); - uint8_t lsb_sym = (timeout > 0x3FF) ? 0xFF : (uint8_t)(timeout & 0xFF); - int16_t state = this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_MODEM_CONFIG_2, msb_sym, 1, 0); - state |= this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_SYMB_TIMEOUT_LSB, lsb_sym); - RADIOLIB_ASSERT(state); - } - return(startReceive((uint8_t)len, mode)); -} - void SX127x::setDio0Action(void (*func)(void), uint32_t dir) { this->mod->hal->attachInterrupt(this->mod->hal->pinToInterrupt(this->mod->getIrq()), func, dir); } @@ -571,7 +585,7 @@ int16_t SX127x::startTransmit(const uint8_t* data, size_t len, uint8_t addr) { RADIOLIB_ERRATA_SX127X(false); // clear interrupt flags - clearIRQFlags(); + clearIrqFlags(RADIOLIB_SX127X_FLAGS_ALL); // set packet length state |= this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_PAYLOAD_LENGTH, len); @@ -582,7 +596,7 @@ int16_t SX127x::startTransmit(const uint8_t* data, size_t len, uint8_t addr) { } else if(modem == RADIOLIB_SX127X_FSK_OOK) { // clear interrupt flags - clearIRQFlags(); + clearIrqFlags(RADIOLIB_SX127X_FLAGS_ALL); // set DIO mapping if(len > RADIOLIB_SX127X_MAX_PACKET_LENGTH_FSK) { @@ -628,7 +642,7 @@ int16_t SX127x::finishTransmit() { mod->hal->delayMicroseconds(1000000/1200); // clear interrupt flags - clearIRQFlags(); + clearIrqFlags(RADIOLIB_SX127X_FLAGS_ALL); // set mode to standby to disable transmitter/RF switch return(standby()); @@ -686,7 +700,7 @@ int16_t SX127x::readData(uint8_t* data, size_t len) { this->packetLengthQueried = false; // clear interrupt flags - clearIRQFlags(); + clearIrqFlags(RADIOLIB_SX127X_FLAGS_ALL); return(state); } @@ -702,7 +716,7 @@ int16_t SX127x::startChannelScan() { RADIOLIB_ASSERT(state); // clear interrupt flags - clearIRQFlags(); + clearIrqFlags(RADIOLIB_SX127X_FLAGS_ALL); // set DIO pin mapping state = this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO0_LORA_CAD_DONE | RADIOLIB_SX127X_DIO1_LORA_CAD_DETECTED, 7, 4); @@ -1295,36 +1309,66 @@ RadioLibTime_t SX127x::calculateRxTimeout(RadioLibTime_t timeoutUs) { return(numSymbols); } -int16_t SX127x::irqRxDoneRxTimeout(uint32_t &irqFlags, uint32_t &irqMask) { - // IRQ flags/masks are inverted to what seems logical for SX127x (0 being activated, 1 being deactivated) - irqFlags = RADIOLIB_SX127X_MASK_IRQ_FLAG_RX_DEFAULT; - irqMask = RADIOLIB_SX127X_MASK_IRQ_FLAG_RX_DONE & RADIOLIB_SX127X_MASK_IRQ_FLAG_RX_TIMEOUT; - return(RADIOLIB_ERR_NONE); +uint32_t SX127x::getIrqFlags() { + return((uint32_t)this->getIRQFlags()); } -int16_t SX127x::checkIrq(uint8_t irq) { - uint16_t flags = getIRQFlags(); - switch(irq) { - case RADIOLIB_IRQ_TX_DONE: - return(flags & RADIOLIB_SX127X_CLEAR_IRQ_FLAG_TX_DONE); - case RADIOLIB_IRQ_RX_DONE: - return(flags & RADIOLIB_SX127X_CLEAR_IRQ_FLAG_RX_DONE); - case RADIOLIB_IRQ_HEADER_VALID: - return(flags & RADIOLIB_SX127X_CLEAR_IRQ_FLAG_VALID_HEADER); - case RADIOLIB_IRQ_CRC_ERR: - return(flags & RADIOLIB_SX127X_CLEAR_IRQ_FLAG_PAYLOAD_CRC_ERROR); - case RADIOLIB_IRQ_CAD_DONE: - return(flags & RADIOLIB_SX127X_CLEAR_IRQ_FLAG_CAD_DONE); - case RADIOLIB_IRQ_CAD_DETECTED: - return(flags & RADIOLIB_SX127X_CLEAR_IRQ_FLAG_CAD_DETECTED); - case RADIOLIB_IRQ_TIMEOUT: - return(flags & RADIOLIB_SX127X_CLEAR_IRQ_FLAG_RX_TIMEOUT); - default: - return(RADIOLIB_ERR_UNSUPPORTED); +int16_t SX127x::setIrqFlags(uint32_t irq) { + // this is a bit convoluted, but unfortunately SX127x IRQ flags are not used to enable/disable that IRQ ... + int16_t modem = getActiveModem(); + if(modem == RADIOLIB_SX127X_LORA) { + switch(irq) { + case(RADIOLIB_SX127X_CLEAR_IRQ_FLAG_TX_DONE): + return(this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO0_PACK_PACKET_SENT, 7, 6)); + case(RADIOLIB_SX127X_CLEAR_IRQ_FLAG_RX_DONE): + return(this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO0_LORA_RX_DONE, 7, 6)); + case(RADIOLIB_SX127X_CLEAR_IRQ_FLAG_VALID_HEADER): + return(this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO3_LORA_VALID_HEADER, 1, 0)); + case(RADIOLIB_SX127X_CLEAR_IRQ_FLAG_PAYLOAD_CRC_ERROR): + return(this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO3_LORA_PAYLOAD_CRC_ERROR, 1, 0)); + case(RADIOLIB_SX127X_CLEAR_IRQ_FLAG_CAD_DONE): + return(this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO0_LORA_CAD_DONE, 7, 6)); + case(RADIOLIB_SX127X_CLEAR_IRQ_FLAG_CAD_DETECTED): + return(this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO1_LORA_CAD_DETECTED, 5, 4)); + case(RADIOLIB_SX127X_CLEAR_IRQ_FLAG_RX_TIMEOUT): + return(this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO1_LORA_RX_TIMEOUT, 5, 4)); + } + return(RADIOLIB_ERR_UNSUPPORTED); + + } else if(modem == RADIOLIB_SX127X_FSK_OOK) { + switch(irq) { + case(RADIOLIB_SX127X_FLAG_PACKET_SENT << 8): + return(this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO0_PACK_PACKET_SENT, 7, 6)); + case(RADIOLIB_SX127X_FLAG_PAYLOAD_READY << 8): + return(this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO0_PACK_PAYLOAD_READY, 7, 6)); + case(RADIOLIB_SX127X_FLAG_PREAMBLE_DETECT << 0): + return(this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_2, RADIOLIB_SX127X_DIO4_PACK_RSSI_PREAMBLE_DETECT, 7, 6)); + case(RADIOLIB_SX127X_FLAG_SYNC_ADDRESS_MATCH << 0): + return(this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO2_PACK_SYNC_ADDRESS, 3, 2)); + case(RADIOLIB_SX127X_FLAG_TIMEOUT << 0): + return(this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO2_PACK_TIMEOUT, 3, 2)); + } + return(RADIOLIB_ERR_UNSUPPORTED); } + return(RADIOLIB_ERR_UNSUPPORTED); } +int16_t SX127x::clearIrqFlags(uint32_t irq) { + int16_t modem = getActiveModem(); + if(modem == RADIOLIB_SX127X_LORA) { + this->mod->SPIwriteRegister(RADIOLIB_SX127X_REG_IRQ_FLAGS, (uint8_t)irq); + return(RADIOLIB_ERR_NONE); + + } else if(modem == RADIOLIB_SX127X_FSK_OOK) { + this->mod->SPIwriteRegister(RADIOLIB_SX127X_REG_IRQ_FLAGS_1, (uint8_t)irq); + this->mod->SPIwriteRegister(RADIOLIB_SX127X_REG_IRQ_FLAGS_2, (uint8_t)(irq >> 8)); + return(RADIOLIB_ERR_NONE); + } + + return(RADIOLIB_ERR_UNKNOWN); +} + int16_t SX127x::setCrcFiltering(bool enable) { this->crcOn = enable; @@ -1619,16 +1663,6 @@ int16_t SX127x::setActiveModem(uint8_t modem) { return(state); } -void SX127x::clearIRQFlags() { - int16_t modem = getActiveModem(); - if(modem == RADIOLIB_SX127X_LORA) { - this->mod->SPIwriteRegister(RADIOLIB_SX127X_REG_IRQ_FLAGS, 0b11111111); - } else if(modem == RADIOLIB_SX127X_FSK_OOK) { - this->mod->SPIwriteRegister(RADIOLIB_SX127X_REG_IRQ_FLAGS_1, 0b11111111); - this->mod->SPIwriteRegister(RADIOLIB_SX127X_REG_IRQ_FLAGS_2, 0b11111111); - } -} - void SX127x::clearFIFO(size_t count) { while(count) { this->mod->SPIreadRegister(RADIOLIB_SX127X_REG_FIFO); diff --git a/src/modules/SX127x/SX127x.h b/src/modules/SX127x/SX127x.h index c00cd42f..94db00ab 100644 --- a/src/modules/SX127x/SX127x.h +++ b/src/modules/SX127x/SX127x.h @@ -160,7 +160,6 @@ #define RADIOLIB_SX127X_MASK_IRQ_FLAG_CAD_DONE 0b11111011 // 2 2 CAD complete #define RADIOLIB_SX127X_MASK_IRQ_FLAG_FHSS_CHANGE_CHANNEL 0b11111101 // 1 1 FHSS change channel #define RADIOLIB_SX127X_MASK_IRQ_FLAG_CAD_DETECTED 0b11111110 // 0 0 valid LoRa signal detected during CAD operation -#define RADIOLIB_SX127X_MASK_IRQ_FLAG_RX_DEFAULT 0b00011111 // 7 0 default for Rx (RX_TIMEOUT, RX_DONE, CRC_ERR) // RADIOLIB_SX127X_REG_FIFO_TX_BASE_ADDR #define RADIOLIB_SX127X_FIFO_TX_BASE_ADDR_MAX 0b00000000 // 7 0 allocate the entire FIFO buffer for TX only @@ -500,6 +499,7 @@ #define RADIOLIB_SX127X_FLAG_PAYLOAD_READY 0b00000100 // 2 2 packet was successfully received #define RADIOLIB_SX127X_FLAG_CRC_OK 0b00000010 // 1 1 CRC check passed #define RADIOLIB_SX127X_FLAG_LOW_BAT 0b00000001 // 0 0 battery voltage dropped below threshold +#define RADIOLIB_SX127X_FLAGS_ALL 0xFFFF // RADIOLIB_SX127X_REG_DIO_MAPPING_1 #define RADIOLIB_SX127X_DIO0_LORA_RX_DONE 0b00000000 // 7 6 @@ -812,27 +812,18 @@ class SX127x: public PhysicalLayer { */ int16_t startReceive() override; - /*! - \brief Interrupt-driven receive method. DIO0 will be activated when full valid packet is received. - \param len Expected length of packet to be received, or 0 when unused. - Defaults to 0, non-zero required for LoRa spreading factor 6. - \param mode Receive mode to be used. Defaults to RxContinuous. - \returns \ref status_codes - */ - int16_t startReceive(uint8_t len, uint8_t mode = RADIOLIB_SX127X_RXCONTINUOUS); - /*! \brief Interrupt-driven receive method, implemented for compatibility with PhysicalLayer. \param timeout Receive mode type and/or raw timeout value in symbols. When set to 0, the timeout will be infinite and the device will remain in Rx mode until explicitly commanded to stop (Rx continuous mode). When non-zero (maximum 1023), the device will be set to Rx single mode and timeout will be set. - \param irqFlags Ignored. - \param irqMask Ignored. + \param irqFlags Sets the IRQ flags, defaults to RX done, RX timeout, CRC error and header error. + \param irqMask Sets the mask of IRQ flags that will trigger DIO1, defaults to RX done. \param len Expected length of packet to be received. Required for LoRa spreading factor 6. \returns \ref status_codes */ - int16_t startReceive(uint32_t timeout, uint32_t irqFlags, uint32_t irqMask, size_t len) override; + int16_t startReceive(uint32_t timeout, RadioLibIrqFlags_t irqFlags = RADIOLIB_IRQ_RX_DEFAULT_FLAGS, RadioLibIrqFlags_t irqMask = RADIOLIB_IRQ_RX_DEFAULT_MASK, size_t len = 0) override; /*! \brief Reads data that was received after calling startReceive method. When the packet length is not known in advance, @@ -1065,18 +1056,24 @@ class SX127x: public PhysicalLayer { RadioLibTime_t calculateRxTimeout(RadioLibTime_t timeoutUs) override; /*! - \brief Create the flags that make up RxDone and RxTimeout used for receiving downlinks - \param irqFlags The flags for which IRQs must be triggered - \param irqMask Mask indicating which IRQ triggers a DIO - \returns \ref status_codes + \brief Read currently active IRQ flags. + \returns IRQ flags. */ - int16_t irqRxDoneRxTimeout(uint32_t &irqFlags, uint32_t &irqMask) override; + uint32_t getIrqFlags() override; /*! - \brief Check whether a specific IRQ bit is set (e.g. RxTimeout, CadDone). - \returns Whether requested IRQ is set. + \brief Set interrupt on DIO1 to be sent on a specific IRQ bit (e.g. RxTimeout, CadDone). + \param irq Module-specific IRQ flags. + \returns \ref status_codes */ - int16_t checkIrq(uint8_t irq) override; + int16_t setIrqFlags(uint32_t irq) override; + + /*! + \brief Clear interrupt on a specific IRQ bit (e.g. RxTimeout, CadDone). + \param irq Module-specific IRQ flags. + \returns \ref status_codes + */ + int16_t clearIrqFlags(uint32_t irq) override; /*! \brief Enable CRC filtering and generation. @@ -1257,7 +1254,6 @@ class SX127x: public PhysicalLayer { bool findChip(const uint8_t* vers, uint8_t num); int16_t setMode(uint8_t mode); int16_t setActiveModem(uint8_t modem); - void clearIRQFlags(); void clearFIFO(size_t count); // used mostly to clear remaining bytes in FIFO after a packet read /*! diff --git a/src/modules/SX128x/SX128x.cpp b/src/modules/SX128x/SX128x.cpp index 088644cc..6ee188eb 100644 --- a/src/modules/SX128x/SX128x.cpp +++ b/src/modules/SX128x/SX128x.cpp @@ -4,6 +4,16 @@ SX128x::SX128x(Module* mod) : PhysicalLayer(RADIOLIB_SX128X_FREQUENCY_STEP_SIZE, RADIOLIB_SX128X_MAX_PACKET_LENGTH) { this->mod = mod; + this->irqMap[RADIOLIB_IRQ_TX_DONE] = RADIOLIB_SX128X_IRQ_TX_DONE; + this->irqMap[RADIOLIB_IRQ_RX_DONE] = RADIOLIB_SX128X_IRQ_RX_DONE; + this->irqMap[RADIOLIB_IRQ_PREAMBLE_DETECTED] = RADIOLIB_SX128X_IRQ_PREAMBLE_DETECTED; + this->irqMap[RADIOLIB_IRQ_SYNC_WORD_VALID] = RADIOLIB_SX128X_IRQ_SYNC_WORD_VALID; + this->irqMap[RADIOLIB_IRQ_HEADER_VALID] = RADIOLIB_SX128X_IRQ_HEADER_VALID; + this->irqMap[RADIOLIB_IRQ_HEADER_ERR] = RADIOLIB_SX128X_IRQ_HEADER_ERROR; + this->irqMap[RADIOLIB_IRQ_CRC_ERR] = RADIOLIB_SX128X_IRQ_CRC_ERROR; + this->irqMap[RADIOLIB_IRQ_CAD_DONE] = RADIOLIB_SX128X_IRQ_CAD_DONE; + this->irqMap[RADIOLIB_IRQ_CAD_DETECTED] = RADIOLIB_SX128X_IRQ_CAD_DETECTED; + this->irqMap[RADIOLIB_IRQ_TIMEOUT] = RADIOLIB_SX128X_IRQ_RX_TX_TIMEOUT; } int16_t SX128x::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t pwr, uint16_t preambleLength) { @@ -411,8 +421,23 @@ int16_t SX128x::receiveDirect() { } int16_t SX128x::scanChannel() { + ChannelScanConfig_t config = { + .cad = { + .symNum = RADIOLIB_SX128X_CAD_PARAM_DEFAULT, + .detPeak = 0, + .detMin = 0, + .exitMode = 0, + .timeout = 0, + .irqFlags = RADIOLIB_IRQ_CAD_DEFAULT_FLAGS, + .irqMask = RADIOLIB_IRQ_CAD_DEFAULT_MASK, + }, + }; + return(this->scanChannel(config)); +} + +int16_t SX128x::scanChannel(ChannelScanConfig_t config) { // set mode to CAD - int16_t state = startChannelScan(); + int16_t state = startChannelScan(config); RADIOLIB_ASSERT(state); // wait for channel activity detected or timeout @@ -558,10 +583,10 @@ int16_t SX128x::finishTransmit() { } int16_t SX128x::startReceive() { - return(this->startReceive(RADIOLIB_SX128X_RX_TIMEOUT_INF, RADIOLIB_SX128X_IRQ_RX_DEFAULT, RADIOLIB_SX128X_IRQ_RX_DONE, 0)); + return(this->startReceive(RADIOLIB_SX128X_RX_TIMEOUT_INF, RADIOLIB_IRQ_RX_DEFAULT_FLAGS, RADIOLIB_IRQ_RX_DEFAULT_MASK, 0)); } -int16_t SX128x::startReceive(uint16_t timeout, uint32_t irqFlags, uint32_t irqMask, size_t len) { +int16_t SX128x::startReceive(uint16_t timeout, RadioLibIrqFlags_t irqFlags, RadioLibIrqFlags_t irqMask, size_t len) { (void)len; // check active modem @@ -571,10 +596,10 @@ int16_t SX128x::startReceive(uint16_t timeout, uint32_t irqFlags, uint32_t irqMa // set DIO mapping if(timeout != RADIOLIB_SX128X_RX_TIMEOUT_INF) { - irqMask |= RADIOLIB_SX128X_IRQ_RX_TX_TIMEOUT; + irqMask |= (1UL << RADIOLIB_IRQ_TIMEOUT); } - int16_t state = setDioIrqParams(irqFlags, irqMask); + int16_t state = setDioIrqParams(getIrqMapped(irqFlags), getIrqMapped(irqMask)); RADIOLIB_ASSERT(state); // set buffer pointers @@ -638,36 +663,34 @@ int16_t SX128x::readData(uint8_t* data, size_t len) { return(state); } -int16_t SX128x::checkIrq(uint8_t irq) { - uint16_t flags = getIrqStatus(); - switch(irq) { - case RADIOLIB_IRQ_TX_DONE: - return(flags & RADIOLIB_SX128X_IRQ_TX_DONE); - case RADIOLIB_IRQ_RX_DONE: - return(flags & RADIOLIB_SX128X_IRQ_RX_DONE); - case RADIOLIB_IRQ_PREAMBLE_DETECTED: - return(flags & RADIOLIB_SX128X_IRQ_PREAMBLE_DETECTED); - case RADIOLIB_IRQ_SYNC_WORD_VALID: - return(flags & RADIOLIB_SX128X_IRQ_SYNC_WORD_VALID); - case RADIOLIB_IRQ_HEADER_VALID: - return(flags & RADIOLIB_SX128X_IRQ_HEADER_VALID); - case RADIOLIB_IRQ_HEADER_ERR: - return(flags & RADIOLIB_SX128X_IRQ_HEADER_ERROR); - case RADIOLIB_IRQ_CRC_ERR: - return(flags & RADIOLIB_SX128X_IRQ_CRC_ERROR); - case RADIOLIB_IRQ_CAD_DONE: - return(flags & RADIOLIB_SX128X_IRQ_CAD_DONE); - case RADIOLIB_IRQ_CAD_DETECTED: - return(flags & RADIOLIB_SX128X_IRQ_CAD_DETECTED); - case RADIOLIB_IRQ_TIMEOUT: - return(flags & RADIOLIB_SX128X_IRQ_RX_TX_TIMEOUT); - default: - return(RADIOLIB_ERR_UNSUPPORTED); - } - return(RADIOLIB_ERR_UNSUPPORTED); +uint32_t SX128x::getIrqFlags() { + return((uint32_t)this->getIrqStatus()); +} + +int16_t SX128x::setIrqFlags(uint32_t irq) { + return(this->setDioIrqParams(irq, irq)); +} + +int16_t SX128x::clearIrqFlags(uint32_t irq) { + return(this->clearIrqStatus(irq)); } int16_t SX128x::startChannelScan() { + ChannelScanConfig_t config = { + .cad = { + .symNum = RADIOLIB_SX128X_CAD_PARAM_DEFAULT, + .detPeak = 0, + .detMin = 0, + .exitMode = 0, + .timeout = 0, + .irqFlags = RADIOLIB_IRQ_CAD_DEFAULT_FLAGS, + .irqMask = RADIOLIB_IRQ_CAD_DEFAULT_MASK, + }, + }; + return(this->startChannelScan(config)); +} + +int16_t SX128x::startChannelScan(const ChannelScanConfig_t &config) { // check active modem if(getPacketType() != RADIOLIB_SX128X_PACKET_TYPE_LORA) { return(RADIOLIB_ERR_WRONG_MODEM); @@ -678,7 +701,7 @@ int16_t SX128x::startChannelScan() { RADIOLIB_ASSERT(state); // set DIO pin mapping - state = setDioIrqParams(RADIOLIB_SX128X_IRQ_CAD_DETECTED | RADIOLIB_SX128X_IRQ_CAD_DONE, RADIOLIB_SX128X_IRQ_CAD_DETECTED | RADIOLIB_SX128X_IRQ_CAD_DONE); + state = setDioIrqParams(getIrqMapped(config.cad.irqFlags), getIrqMapped(config.cad.irqMask)); RADIOLIB_ASSERT(state); // clear interrupt flags @@ -689,7 +712,7 @@ int16_t SX128x::startChannelScan() { this->mod->setRfSwitchState(Module::MODE_RX); // set mode to CAD - return(setCad()); + return(setCad(config.cad.symNum)); } int16_t SX128x::getChannelScanResult() { @@ -1464,7 +1487,12 @@ int16_t SX128x::setRx(uint16_t periodBaseCount, uint8_t periodBase) { return(this->mod->SPIwriteStream(RADIOLIB_SX128X_CMD_SET_RX, data, 3)); } -int16_t SX128x::setCad() { +int16_t SX128x::setCad(uint8_t symbolNum) { + // configure parameters + int16_t state = this->mod->SPIwriteStream(RADIOLIB_SX128X_CMD_SET_CAD_PARAMS, &symbolNum, 1); + RADIOLIB_ASSERT(state); + + // start CAD return(this->mod->SPIwriteStream(RADIOLIB_SX128X_CMD_SET_CAD, NULL, 0)); } diff --git a/src/modules/SX128x/SX128x.h b/src/modules/SX128x/SX128x.h index 43c1f412..1e1dea1f 100644 --- a/src/modules/SX128x/SX128x.h +++ b/src/modules/SX128x/SX128x.h @@ -186,6 +186,7 @@ #define RADIOLIB_SX128X_CAD_ON_4_SYMB 0x40 // 7 0 4 #define RADIOLIB_SX128X_CAD_ON_8_SYMB 0x60 // 7 0 8 #define RADIOLIB_SX128X_CAD_ON_16_SYMB 0x80 // 7 0 16 +#define RADIOLIB_SX128X_CAD_PARAM_DEFAULT RADIOLIB_SX128X_CAD_ON_8_SYMB //RADIOLIB_SX128X_CMD_SET_MODULATION_PARAMS #define RADIOLIB_SX128X_BLE_GFSK_BR_2_000_BW_2_4 0x04 // 7 0 GFSK/BLE bit rate and bandwidth setting: 2.0 Mbps 2.4 MHz @@ -327,7 +328,6 @@ #define RADIOLIB_SX128X_IRQ_SYNC_WORD_VALID 0x0004 // 2 2 sync word valid #define RADIOLIB_SX128X_IRQ_RX_DONE 0x0002 // 1 1 Rx done #define RADIOLIB_SX128X_IRQ_TX_DONE 0x0001 // 0 0 Tx done -#define RADIOLIB_SX128X_IRQ_RX_DEFAULT 0x4062 // 15 0 default for Rx (RX_DONE, RX_TX_TIMEOUT, CRC_ERROR and HEADER_ERROR) #define RADIOLIB_SX128X_IRQ_NONE 0x0000 // 15 0 none #define RADIOLIB_SX128X_IRQ_ALL 0xFFFF // 15 0 all @@ -457,6 +457,13 @@ class SX128x: public PhysicalLayer { */ int16_t scanChannel() override; + /*! + \brief Performs scan for LoRa transmission in the current channel. Detects both preamble and payload. + \param config CAD configuration structure. + \returns \ref status_codes + */ + int16_t scanChannel(ChannelScanConfig_t config) override; + /*! \brief Sets the module to sleep mode. To wake the device up, call standby(). Overload for PhysicalLayer compatibility. @@ -553,12 +560,12 @@ class SX128x: public PhysicalLayer { set to RADIOLIB_SX128X_RX_TIMEOUT_NONE for no timeout (Rx single mode). If timeout other than infinite is set, signal will be generated on DIO1. - \param irqFlags Sets the IRQ flags, defaults to RADIOLIB_SX128X_IRQ_RX_DEFAULT. - \param irqMask Sets the mask of IRQ flags that will trigger DIO1, defaults to RADIOLIB_SX128X_IRQ_RX_DONE. + \param irqFlags Sets the IRQ flags, defaults to RX done, RX timeout, CRC error and header error. + \param irqMask Sets the mask of IRQ flags that will trigger DIO1, defaults to RX done. \param len Only for PhysicalLayer compatibility, not used. \returns \ref status_codes */ - int16_t startReceive(uint16_t timeout, uint32_t irqFlags = RADIOLIB_SX128X_IRQ_RX_DEFAULT, uint32_t irqMask = RADIOLIB_SX128X_IRQ_RX_DONE, size_t len = 0); + int16_t startReceive(uint16_t timeout, RadioLibIrqFlags_t irqFlags = RADIOLIB_IRQ_RX_DEFAULT_FLAGS, RadioLibIrqFlags_t irqMask = RADIOLIB_IRQ_RX_DEFAULT_MASK, size_t len = 0); /*! \brief Reads the current IRQ status. @@ -577,18 +584,40 @@ class SX128x: public PhysicalLayer { int16_t readData(uint8_t* data, size_t len) override; /*! - \brief Check whether a specific IRQ bit is set (e.g. RxTimeout, CadDone). - \returns Whether requested IRQ is set. + \brief Read currently active IRQ flags. + \returns IRQ flags. */ - int16_t checkIrq(uint8_t irq) override; + uint32_t getIrqFlags() override; + + /*! + \brief Set interrupt on DIO1 to be sent on a specific IRQ bit (e.g. RxTimeout, CadDone). + \param irq Module-specific IRQ flags. + \returns \ref status_codes + */ + int16_t setIrqFlags(uint32_t irq) override; + + /*! + \brief Clear interrupt on a specific IRQ bit (e.g. RxTimeout, CadDone). + \param irq Module-specific IRQ flags. + \returns \ref status_codes + */ + int16_t clearIrqFlags(uint32_t irq) override; /*! \brief Interrupt-driven channel activity detection method. DIO1 will be activated - when LoRa preamble is detected, or upon timeout. Defaults to CAD parameter values recommended by AN1200.48. + when LoRa preamble is detected, or upon timeout. \returns \ref status_codes */ int16_t startChannelScan() override; + /*! + \brief Interrupt-driven channel activity detection method. DIO1 will be activated + when LoRa preamble is detected, or upon timeout. + \param config CAD configuration structure. + \returns \ref status_codes + */ + int16_t startChannelScan(const ChannelScanConfig_t &config) override; + /*! \brief Read the channel scan result \returns \ref status_codes @@ -837,7 +866,7 @@ class SX128x: public PhysicalLayer { int16_t readBuffer(uint8_t* data, uint8_t numBytes, uint8_t offset = 0x00); int16_t setTx(uint16_t periodBaseCount = RADIOLIB_SX128X_TX_TIMEOUT_NONE, uint8_t periodBase = RADIOLIB_SX128X_PERIOD_BASE_15_625_US); int16_t setRx(uint16_t periodBaseCount, uint8_t periodBase = RADIOLIB_SX128X_PERIOD_BASE_15_625_US); - int16_t setCad(); + int16_t setCad(uint8_t symbolNum); uint8_t getPacketType(); int16_t setRfFrequency(uint32_t frf); int16_t setTxParams(uint8_t pwr, uint8_t rampTime = RADIOLIB_SX128X_PA_RAMP_10_US); diff --git a/src/protocols/LoRaWAN/LoRaWAN.cpp b/src/protocols/LoRaWAN/LoRaWAN.cpp index c27feaa4..a1f63a7d 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.cpp +++ b/src/protocols/LoRaWAN/LoRaWAN.cpp @@ -1190,9 +1190,8 @@ int16_t LoRaWANNode::downlinkCommon() { RADIOLIB_ASSERT(state); // create the masks that are required for receiving downlinks - uint32_t irqFlags = 0; - uint32_t irqMask = 0; - this->phyLayer->irqRxDoneRxTimeout(irqFlags, irqMask); + RadioLibIrqFlags_t irqFlags = (1UL << RADIOLIB_IRQ_RX_DONE) | (1UL << RADIOLIB_IRQ_TIMEOUT); + RadioLibIrqFlags_t irqMask = RADIOLIB_IRQ_RX_DEFAULT_MASK; this->phyLayer->setPacketReceivedAction(LoRaWANNodeOnDownlinkAction); diff --git a/src/protocols/PhysicalLayer/PhysicalLayer.cpp b/src/protocols/PhysicalLayer/PhysicalLayer.cpp index 6953447c..1a2c19f9 100644 --- a/src/protocols/PhysicalLayer/PhysicalLayer.cpp +++ b/src/protocols/PhysicalLayer/PhysicalLayer.cpp @@ -132,7 +132,7 @@ int16_t PhysicalLayer::startReceive() { return(RADIOLIB_ERR_UNSUPPORTED); } -int16_t PhysicalLayer::startReceive(uint32_t timeout, uint32_t irqFlags, uint32_t irqMask, size_t len) { +int16_t PhysicalLayer::startReceive(uint32_t timeout, RadioLibIrqFlags_t irqFlags, RadioLibIrqFlags_t irqMask, size_t len) { (void)timeout; (void)irqFlags; (void)irqMask; @@ -310,13 +310,44 @@ RadioLibTime_t PhysicalLayer::calculateRxTimeout(RadioLibTime_t timeoutUs) { return(0); } -int16_t PhysicalLayer::irqRxDoneRxTimeout(uint32_t &irqFlags, uint32_t &irqMask) { - (void)irqFlags; - (void)irqMask; +uint32_t PhysicalLayer::getIrqMapped(RadioLibIrqFlags_t irq) { + // iterate over all set bits and build the module-specific flags + uint32_t irqRaw = 0; + for(uint8_t i = 0; i < 8*(sizeof(RadioLibIrqFlags_t)); i++) { + if((irq & (uint32_t)(1UL << i)) && (this->irqMap[i] != RADIOLIB_IRQ_NOT_SUPPORTED)) { + irqRaw |= this->irqMap[i]; + } + } + + return(irqRaw); +} + +int16_t PhysicalLayer::checkIrq(RadioLibIrqType_t irq) { + if((irq > RADIOLIB_IRQ_TIMEOUT) || (this->irqMap[irq] == RADIOLIB_IRQ_NOT_SUPPORTED)) { + return(RADIOLIB_ERR_UNSUPPORTED); + } + + return(getIrqFlags() & this->irqMap[irq]); +} + +int16_t PhysicalLayer::setIrq(RadioLibIrqFlags_t irq) { + return(setIrqFlags(getIrqMapped(irq))); +} + +int16_t PhysicalLayer::clearIrq(RadioLibIrqFlags_t irq) { + return(clearIrqFlags(getIrqMapped(irq))); +} + +uint32_t PhysicalLayer::getIrqFlags() { return(RADIOLIB_ERR_UNSUPPORTED); } -int16_t PhysicalLayer::checkIrq(uint8_t irq) { +int16_t PhysicalLayer::setIrqFlags(uint32_t irq) { + (void)irq; + return(RADIOLIB_ERR_UNSUPPORTED); +} + +int16_t PhysicalLayer::clearIrqFlags(uint32_t irq) { (void)irq; return(RADIOLIB_ERR_UNSUPPORTED); } @@ -325,6 +356,11 @@ int16_t PhysicalLayer::startChannelScan() { return(RADIOLIB_ERR_UNSUPPORTED); } +int16_t PhysicalLayer::startChannelScan(const ChannelScanConfig_t &config) { + (void)config; + return(RADIOLIB_ERR_UNSUPPORTED); +} + int16_t PhysicalLayer::getChannelScanResult() { return(RADIOLIB_ERR_UNSUPPORTED); } @@ -333,6 +369,11 @@ int16_t PhysicalLayer::scanChannel() { return(RADIOLIB_ERR_UNSUPPORTED); } +int16_t PhysicalLayer::scanChannel(ChannelScanConfig_t config) { + (void)config; + return(RADIOLIB_ERR_UNSUPPORTED); +} + int32_t PhysicalLayer::random(int32_t max) { if(max == 0) { return(0); diff --git a/src/protocols/PhysicalLayer/PhysicalLayer.h b/src/protocols/PhysicalLayer/PhysicalLayer.h index 408408ae..0920076a 100644 --- a/src/protocols/PhysicalLayer/PhysicalLayer.h +++ b/src/protocols/PhysicalLayer/PhysicalLayer.h @@ -4,17 +4,26 @@ #include "../../TypeDef.h" #include "../../Module.h" -// common IRQ flags -#define RADIOLIB_IRQ_TX_DONE 0x00 -#define RADIOLIB_IRQ_RX_DONE 0x01 -#define RADIOLIB_IRQ_PREAMBLE_DETECTED 0x02 -#define RADIOLIB_IRQ_SYNC_WORD_VALID 0x03 -#define RADIOLIB_IRQ_HEADER_VALID 0x04 -#define RADIOLIB_IRQ_HEADER_ERR 0x05 -#define RADIOLIB_IRQ_CRC_ERR 0x06 -#define RADIOLIB_IRQ_CAD_DONE 0x07 -#define RADIOLIB_IRQ_CAD_DETECTED 0x08 -#define RADIOLIB_IRQ_TIMEOUT 0x09 +// common IRQ values - the IRQ flags in RadioLibIrqFlags_t arguments are offset by this value +enum RadioLibIrqType_t { + RADIOLIB_IRQ_TX_DONE = 0x00, + RADIOLIB_IRQ_RX_DONE = 0x01, + RADIOLIB_IRQ_PREAMBLE_DETECTED = 0x02, + RADIOLIB_IRQ_SYNC_WORD_VALID = 0x03, + RADIOLIB_IRQ_HEADER_VALID = 0x04, + RADIOLIB_IRQ_HEADER_ERR = 0x05, + RADIOLIB_IRQ_CRC_ERR = 0x06, + RADIOLIB_IRQ_CAD_DONE = 0x07, + RADIOLIB_IRQ_CAD_DETECTED = 0x08, + RADIOLIB_IRQ_TIMEOUT = 0x09, + RADIOLIB_IRQ_NOT_SUPPORTED = 0x1F, // this must be the last value, intentionally set to 31 +}; + +// some commonly used default values - defined here to ensure all modules have the same default behavior +#define RADIOLIB_IRQ_RX_DEFAULT_FLAGS ((1UL << RADIOLIB_IRQ_RX_DONE) | (1UL << RADIOLIB_IRQ_TIMEOUT) | (1UL << RADIOLIB_IRQ_CRC_ERR) | (1UL << RADIOLIB_IRQ_HEADER_ERR)) +#define RADIOLIB_IRQ_RX_DEFAULT_MASK ((1UL << RADIOLIB_IRQ_RX_DONE)) +#define RADIOLIB_IRQ_CAD_DEFAULT_FLAGS ((1UL << RADIOLIB_IRQ_CAD_DETECTED) | (1UL << RADIOLIB_IRQ_CAD_DONE)) +#define RADIOLIB_IRQ_CAD_DEFAULT_MASK ((1UL << RADIOLIB_IRQ_CAD_DETECTED) | (1UL << RADIOLIB_IRQ_CAD_DONE)) /*! \struct LoRaRate_t @@ -39,7 +48,7 @@ struct FSKRate_t { /*! \brief FSK bit rate in kbps */ float bitRate; - /*! \brief FS frequency deviation in kHz*/ + /*! \brief FSK frequency deviation in kHz */ float freqDev; }; @@ -55,6 +64,54 @@ union DataRate_t { FSKRate_t fsk; }; +/*! + \struct CADScanConfig_t + \brief Channel scan configuration interpretation in case LoRa CAD is used +*/ +struct CADScanConfig_t { + /*! \brief Number of symbols to consider signal present */ + uint8_t symNum; + + /*! \brief Number of peak detection symbols */ + uint8_t detPeak; + + /*! \brief Number of minimum detection symbols */ + uint8_t detMin; + + /*! \brief Exit mode after signal detection is complete - module-specific value */ + uint8_t exitMode; + + /*! \brief Timeout in microseconds */ + RadioLibTime_t timeout; + + /*! \brief Optional IRQ flags to set, bits offset by the value of RADIOLIB_IRQ_ */ + RadioLibIrqFlags_t irqFlags; + + /*! \brief Optional IRQ mask to set, bits offset by the value of RADIOLIB_IRQ_ */ + RadioLibIrqFlags_t irqMask; +}; + +/*! + \struct RSSIScanConfig_t + \brief Channel scan configuration interpretation in case RSSI threshold is used +*/ +struct RSSIScanConfig_t { + /*! \brief RSSI limit in dBm */ + float limit; +}; + +/*! + \union ChannelScanConfig_t + \brief Common channel scan configuration structure +*/ +union ChannelScanConfig_t { + /*! \brief Interpretation for modems that use CAD (usually LoRa modems)*/ + CADScanConfig_t cad; + + /*! \brief Interpretation for modems that use RSSI threshold*/ + RSSIScanConfig_t rssi; +}; + /*! \class PhysicalLayer @@ -152,16 +209,16 @@ class PhysicalLayer { \param timeout Raw timeout value. Some modules use this argument to specify operation mode (single vs. continuous receive). \param irqFlags Sets the IRQ flags. - \param irqMask Sets the mask of IRQ flags that will trigger the DIO pin. + \param irqMask Sets the mask of IRQ flags that will trigger the radio interrupt pin. \param len Packet length, needed for some modules under special circumstances (e.g. LoRa implicit header mode). \returns \ref status_codes */ - virtual int16_t startReceive(uint32_t timeout, uint32_t irqFlags, uint32_t irqMask, size_t len); + virtual int16_t startReceive(uint32_t timeout, RadioLibIrqFlags_t irqFlags, RadioLibIrqFlags_t irqMask, size_t len); /*! \brief Binary receive method. Must be implemented in module class. \param data Pointer to array to save the received binary data. - \param len Number of bytes that will be received. Must be known in advance for binary transmissions. + \param len Packet length, needed for some modules under special circumstances (e.g. LoRa implicit header mode). \returns \ref status_codes */ virtual int16_t receive(uint8_t* data, size_t len); @@ -366,18 +423,59 @@ class PhysicalLayer { virtual RadioLibTime_t calculateRxTimeout(RadioLibTime_t timeoutUs); /*! - \brief Create the flags that make up RxDone and RxTimeout used for receiving downlinks. - \param irqFlags The flags for which IRQs must be triggered. - \param irqMask Mask indicating which IRQ triggers a DIO. - \returns \ref status_codes + \brief Convert from radio-agnostic IRQ flags to radio-specific flags. + \param irq Radio-agnostic IRQ flags. + \returns Flags for a specific radio module. */ - virtual int16_t irqRxDoneRxTimeout(uint32_t &irqFlags, uint32_t &irqMask); + uint32_t getIrqMapped(RadioLibIrqFlags_t irq); /*! \brief Check whether a specific IRQ bit is set (e.g. RxTimeout, CadDone). - \returns Whether requested IRQ is set. + \param irq IRQ type to check, one of RADIOLIB_IRQ_*. + \returns 1 when requested IRQ is set, 0 when it is not or RADIOLIB_ERR_UNSUPPORTED if the IRQ is not supported. */ - virtual int16_t checkIrq(uint8_t irq); + int16_t checkIrq(RadioLibIrqType_t irq); + + /*! + \brief Set interrupt on specific IRQ bit(s) (e.g. RxTimeout, CadDone). + Keep in mind that not all radio modules support all RADIOLIB_IRQ_ flags! + \param irq Flags to set, multiple bits may be enabled. IRQ to enable corresponds to the bit index (RadioLibIrq_t). + For example, if bit 0 is enabled, the module will enable its RADIOLIB_IRQ_TX_DONE (if it is supported). + \returns \ref status_codes + */ + int16_t setIrq(RadioLibIrqFlags_t irq); + + /*! + \brief Clear interrupt on a specific IRQ bit (e.g. RxTimeout, CadDone). + Keep in mind that not all radio modules support all RADIOLIB_IRQ_ flags! + \param irq Flags to set, multiple bits may be enabled. IRQ to enable corresponds to the bit index (RadioLibIrq_t). + For example, if bit 0 is enabled, the module will enable its RADIOLIB_IRQ_TX_DONE (if it is supported). + \returns \ref status_codes + */ + int16_t clearIrq(RadioLibIrqFlags_t irq); + + /*! + \brief Read currently active IRQ flags. + Must be implemented in module class. + \returns IRQ flags. + */ + virtual uint32_t getIrqFlags(); + + /*! + \brief Set interrupt on DIO1 to be sent on a specific IRQ bit (e.g. RxTimeout, CadDone). + Must be implemented in module class. + \param irq Module-specific IRQ flags. + \returns \ref status_codes + */ + virtual int16_t setIrqFlags(uint32_t irq); + + /*! + \brief Clear interrupt on a specific IRQ bit (e.g. RxTimeout, CadDone). + Must be implemented in module class. + \param irq Module-specific IRQ flags. + \returns \ref status_codes + */ + virtual int16_t clearIrqFlags(uint32_t irq); /*! \brief Interrupt-driven channel activity detection method. Interrupt will be activated @@ -386,6 +484,14 @@ class PhysicalLayer { */ virtual int16_t startChannelScan(); + /*! + \brief Interrupt-driven channel activity detection method. interrupt will be activated + when packet is detected. Must be implemented in module class. + \param config Scan configuration structure. Interpretation depends on currently active modem. + \returns \ref status_codes + */ + virtual int16_t startChannelScan(const ChannelScanConfig_t &config); + /*! \brief Read the channel scan result \returns \ref status_codes @@ -400,6 +506,15 @@ class PhysicalLayer { */ virtual int16_t scanChannel(); + /*! + \brief Check whether the current communication channel is free or occupied. Performs CAD for LoRa modules, + or RSSI measurement for FSK modules. + \param config Scan configuration structure. Interpretation depends on currently active modem. + \returns RADIOLIB_CHANNEL_FREE when channel is free, + RADIOLIB_PREAMBLE_DETECTEDwhen occupied or other \ref status_codes. + */ + virtual int16_t scanChannel(ChannelScanConfig_t config); + /*! \brief Get truly random number in range 0 - max. \param max The maximum value of the random number (non-inclusive). @@ -531,6 +646,8 @@ class PhysicalLayer { #if !RADIOLIB_GODMODE protected: #endif + uint32_t irqMap[10] = { 0 }; + #if !RADIOLIB_EXCLUDE_DIRECT_RECEIVE void updateDirectBuffer(uint8_t bit); #endif