From 9252cf53d3e154756aec94b9924eda0b4346ce3c Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 7 Dec 2022 09:17:44 +0100 Subject: [PATCH] [STM32WLx] Implement setDio1Action / interrupts Because this interrupt is not connected to a GPIO or even the EXTI unit, but directly to the MCU NVIC, the regular attachInterrupt flow cannot be used. Instead, this lets STM32WLx override the setDio1Action() and clearDio1Action() methods to register the radio IRQ directly. Note that it would have been nicer to implement this by overriding attachInterrupt in STM32WLx_Module, but that is never called for virtual pins (because the STM32Duino digitalPinToInterrupt() macro does not work for virtual pins). In addition, because the NVIC is level-sensitive and the code expects edge-sensitive interrupts (for GPIO, the EXTI module in front of the NVIC makes the interrupts edge-sensitive), this adds some additional handling around the user-registered interrupt callback. To prevent the ISR from triggering over and over again (and also to not require SPI transactions in the ISR to clear the IRQ flags inside the radio), the interrupt is disabled in the NVIC whenever it is trigered. Then, whenever the IRQ flags *are* cleared in the radio, the IRQ is enabled in the NVIC again. This requires making the SX126x::clearIrqStatus() method virtual so STM32WLx can override it. --- src/modules/SX126x/STM32WLx.cpp | 26 ++++++++++++++++++++++++++ src/modules/SX126x/STM32WLx.h | 17 +++++++++++++++++ src/modules/SX126x/SX126x.h | 2 +- 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/modules/SX126x/STM32WLx.cpp b/src/modules/SX126x/STM32WLx.cpp index a1ec8a58..bad38dc9 100644 --- a/src/modules/SX126x/STM32WLx.cpp +++ b/src/modules/SX126x/STM32WLx.cpp @@ -9,6 +9,7 @@ This file is licensed under the MIT License: https://opensource.org/licenses/MIT #include "STM32WLx.h" #if !defined(RADIOLIB_EXCLUDE_STM32WLX) +#include STM32WLx::STM32WLx(STM32WLx_Module* mod) : SX1262(mod) { } @@ -72,5 +73,30 @@ int16_t STM32WLx::setOutputPower(int8_t power) { return(writeRegister(RADIOLIB_SX126X_REG_OCP_CONFIGURATION, &ocp, 1)); } +int16_t STM32WLx::clearIrqStatus(uint16_t clearIrqParams) { + int16_t res = SX126x::clearIrqStatus(clearIrqParams); + // The NVIC interrupt is level-sensitive, so clear away any pending + // flag that is only set because the radio IRQ status was not cleared + // in the interrupt (to prevent each IRQ triggering twice and allow + // reading the irq status through the pending flag). + SubGhz.clearPendingInterrupt(); + if(SubGhz.hasInterrupt()) + SubGhz.enableInterrupt(); + return(res); +} + +void STM32WLx::setDio1Action(void (*func)(void)) { + SubGhz.attachInterrupt([func]() { + // Because the interrupt is level-triggered, we disable it in the + // NVIC (otherwise we would need an SPI command to clear the IRQ in + // the radio, or it would trigger over and over again). + SubGhz.disableInterrupt(); + func(); + }); +} + +void STM32WLx::clearDio1Action() { + SubGhz.detachInterrupt(); +} #endif // !defined(RADIOLIB_EXCLUDE_STM32WLX) diff --git a/src/modules/SX126x/STM32WLx.h b/src/modules/SX126x/STM32WLx.h index d90f1450..5778dfb1 100644 --- a/src/modules/SX126x/STM32WLx.h +++ b/src/modules/SX126x/STM32WLx.h @@ -112,6 +112,23 @@ class STM32WLx : public SX1262 { // Note: This explicitly inherits this method only to override docs using SX126x::setRfSwitchTable; + /*! + \brief Sets interrupt service routine to call when DIO1/2/3 activates. + + \param func ISR to call. + */ + void setDio1Action(void (*func)(void)); + + /*! + \brief Clears interrupt service routine to call when DIO1/2/3 activates. + */ + void clearDio1Action(); + +#if !defined(RADIOLIB_GODMODE) + protected: +#endif + virtual int16_t clearIrqStatus(uint16_t clearIrqParams) override; + #if !defined(RADIOLIB_GODMODE) private: #endif diff --git a/src/modules/SX126x/SX126x.h b/src/modules/SX126x/SX126x.h index ae479859..e1b603ba 100644 --- a/src/modules/SX126x/SX126x.h +++ b/src/modules/SX126x/SX126x.h @@ -982,7 +982,7 @@ class SX126x: public PhysicalLayer { int16_t writeBuffer(uint8_t* data, uint8_t numBytes, uint8_t offset = 0x00); int16_t readBuffer(uint8_t* data, uint8_t numBytes); int16_t setDioIrqParams(uint16_t irqMask, uint16_t dio1Mask, uint16_t dio2Mask = RADIOLIB_SX126X_IRQ_NONE, uint16_t dio3Mask = RADIOLIB_SX126X_IRQ_NONE); - int16_t clearIrqStatus(uint16_t clearIrqParams = RADIOLIB_SX126X_IRQ_ALL); + virtual int16_t clearIrqStatus(uint16_t clearIrqParams = RADIOLIB_SX126X_IRQ_ALL); int16_t setRfFrequency(uint32_t frf); int16_t calibrateImage(uint8_t* data); uint8_t getPacketType();