The STM32WL series of microcontrollers includes an embedded sub-GHz radio based on the Semtech SX1261 and SX1262 transceivers. Just like the SX126x devices, the sub-GHz radio system is controlled by a SPI interface, a BUSY line, and three interrupt (IRQ) lines. The only difference is, as the radio is integrated into the STM32WL device, these signals (highlighted in Figure 1) are internally connected to the rest of the SoC.
As such, the interrupt scheme of the STM32WL sub-GHz radio system is the same as that of the Semtech SX126x transceivers. That is, there are ten interrupt sources that can be enabled/disabled and mapped to any of the three IRQ lines. A description of these interrupt sources is provided in Table 1 below.
Table 1: Sub-GHz radio system interrupt sources (derived from Table 37 in RM0453)
Bit | Source | Description | Packet Type | Operation |
---|---|---|---|---|
0 | TxDone | Packet transmission finished | LoRa and GFSK | TX |
1 | RxDone | Packet reception finished | LoRa and GFSK | RX |
2 | PreambleDetected | Preamble detected | LoRa and GFSK | RX |
3 | SyncDetected | Synchronization word valid | GFSK | RX |
4 | HeaderValid | Header valid | LoRa | RX |
5 | HeaderErr | Header CRC error | LoRa | RX |
6 | Err | Preamble, syncword, address, CRC, or length error | GFSK | RX |
6 | CrcErr | CRC error | LoRa | RX |
7 | CadDone | Channel activity detection finished | LoRa | CAD |
8 | CadDetected | Channel activity detected | LoRa | CAD |
9 | Timeout | RX or TX timeout | LoRa and GFSK | RX & TX |
15:10 | N/A | Reserved | N/A | N/A |
The low-level driver of the Sub-GHz Phy Middleware will be utilized in this article to interface with the STM32WL. The provided code could also be applied to an application utilizing the complete Middleware (including the high-level driver), but doing so will likely not be necessary as most of the implementation details are abstracted by the higher layer. For more information about utilizing the low-level sub-GHz driver in an STM32WL project, please read Using the Low-Level Sub-GHz Radio Driver for the STM32WL Series.
The following code snippets demonstrate the basic procedure for utilizing STM32WL radio interrupts. The first step, shown in Listing 1, is to pass a pointer to a callback function as an argument to the driver initialization routine. This callback function will be executed every time an interrupt is issued by the sub-GHz radio system. To determine the source of the interrupt (see Table 1), this function must include a single parameter of type RadioIrqMasks_t
, allowing a switch statement (or other conditional logic) to handle each individual interrupt source.
Listing 1: Template for setting and implementing the sub-GHz radio callback function
// Initialize the hardware (SPI bus, TCXO control, RF switch)
SUBGRF_Init(RadioOnDioIrq);
.
.
.
// Callback function - executed once for each (enabled) interrupt issued from sub-GHz radio
void RadioOnDioIrq(RadioIrqMasks_t radioIrq)
{
switch (radioIrq)
{
case IRQ_TX_DONE:
// do something
break;
case IRQ_RX_DONE:
// do something
break;
case IRQ_RX_TX_TIMEOUT:
// do something
break;
case IRQ_CRC_ERROR:
// do something
break;
default:
break;
}
}
For an interrupt request to be generated by the sub-GHz radio subsystem, one or more interrupt sources must be globally enabled and mapped to any of the three IRQ lines. As shown in Listing 2, this is done by calling the SUBGRF_SetDioIrqParams()
function. The first parameter of this function is a global interrupt enable mask, which enables/disabled the indicated interrupt source(s). The last three parameters are masks for the individual IRQ lines, which map the interrupt source(s) to the corresponding line. For example, in Listing 2 the TxDone, Timeout, and RxDone interrupts are globally enabled by the first argument. The second argument indicates that the IRQ1 line should transition from low to high when either the TxDone or Timeout interrupts are issued. Likewise, the IRQ2 line should transition from low to high when the RxDone interrupt is issued. There is no interrupt source mapped to the IRQ3 line in this example.
Listing 2: Enabling interrupts from the sub-GHz radio system
SUBGRF_SetDioIrqParams( IRQ_TX_DONE | IRQ_RX_TX_TIMEOUT | IRQ_RX_DONE,
IRQ_TX_DONE | IRQ_RX_TX_TIMEOUT,
IRQ_RX_DONE,
IRQ_RADIO_NONE );
NOTE: In the case that several interrupt sources mapped to the same IRQ line are triggered simultaneously, the callback function will be called multiple times to handle each source event.
Unfortunately, short of directly modifying the Sub-GHz Phy Middleware, there is no way to assign separate callback functions to the individual IRQ lines. That is, the same callback function will be executed regardless of which IRQ line a triggered interrupt is mapped to. It is for this reason that most applications typically only utilize the IRQ1 line. Should the situation arise that an application must determine which IRQ line was triggered, the code provided in Listing 3 may be used. It employs a workaround in which the internal IRQ lines are made external, allowing the GPIO IDR (Input Data Register) to be polled to get the status of each line. For more details about making the internal sub-GHz radio signals external, see Mapping the Internal STM32WL Sub-GHz Radio Interface Signals to GPIO Pins.
Listing 3: Route the IRQ[0:2] lines to GPIO pins and check their current state.
#define GPIO_PIN_RF_IRQ1 GPIO_PIN_3
#define GPIO_PIN_RF_IRQ2 GPIO_PIN_5
#define GPIO_PIN_RF_IRQ3 GPIO_PIN_8
/**************************** Initialization Code *****************************/
// Enable GPIOB Clock
__HAL_RCC_GPIOB_CLK_ENABLE();
// Configure RF_{IRQ0, IRQ1, IRQ2} pins
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_RF_IRQ1 | GPIO_PIN_RF_IRQ2 | GPIO_PIN_RF_IRQ3;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF6_RF_BUSY;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/************ Conditional logic to be placed in callback function *************/
// Check status of each IRQ line
if (READ_BIT(GPIOB->IDR, GPIO_PIN_RF_IRQ1))
{
// IRQ1 line triggered
}
else if (READ_BIT(GPIOB->IDR, GPIO_PIN_RF_IRQ2))
{
// IRQ2 line triggered
}
else if (READ_BIT(GPIOB->IDR, GPIO_PIN_RF_IRQ3))
{
// IRQ3 line triggered
}
For the most part, managing interrupts from the sub-GHz radio system on the STM32WL line of devices is identical to interfacing with an external RF transceiver. There is only a slight loss in flexibility stemming from the internalization of the radio control signals. There are several complete example applications which utilize the radio interrupts available from the STM32CubeWL MCU Package .