[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.
This commit is contained in:
Matthijs Kooijman 2022-12-07 09:17:44 +01:00
parent 5e47d94418
commit 9252cf53d3
3 changed files with 44 additions and 1 deletions

View file

@ -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 <SubGhz.h>
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)

View file

@ -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

View file

@ -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();