[STM32WLx] Add module for STM32WL MCUs with integrated radio (#588)
This is a nearly complete implementation, except that the Dio1 interrupt is not yet supported (this will be added in a subsequent commit to simplify review). This fixes #588.
This commit is contained in:
parent
da6c3f6a6b
commit
5e47d94418
7 changed files with 354 additions and 1 deletions
|
@ -96,6 +96,7 @@
|
||||||
//#define RADIOLIB_EXCLUDE_SX127X
|
//#define RADIOLIB_EXCLUDE_SX127X
|
||||||
//#define RADIOLIB_EXCLUDE_RFM9X // dependent on RADIOLIB_EXCLUDE_SX127X
|
//#define RADIOLIB_EXCLUDE_RFM9X // dependent on RADIOLIB_EXCLUDE_SX127X
|
||||||
//#define RADIOLIB_EXCLUDE_SX126X
|
//#define RADIOLIB_EXCLUDE_SX126X
|
||||||
|
//#define RADIOLIB_EXCLUDE_STM32WLX // dependent on RADIOLIB_EXCLUDE_SX126X
|
||||||
//#define RADIOLIB_EXCLUDE_SX128X
|
//#define RADIOLIB_EXCLUDE_SX128X
|
||||||
//#define RADIOLIB_EXCLUDE_AFSK
|
//#define RADIOLIB_EXCLUDE_AFSK
|
||||||
//#define RADIOLIB_EXCLUDE_AX25
|
//#define RADIOLIB_EXCLUDE_AX25
|
||||||
|
|
|
@ -44,10 +44,15 @@ class Module {
|
||||||
* See setRfSwitchTable() for details.
|
* See setRfSwitchTable() for details.
|
||||||
*/
|
*/
|
||||||
enum OpMode_t {
|
enum OpMode_t {
|
||||||
// Table end marker is zero to ensure zero-initialized mode ends the table
|
/*! End of table marker, use \ref END_OF_MODE_TABLE constant
|
||||||
|
* instead. Value is zero to ensure zero-initialized mode ends the
|
||||||
|
* table */
|
||||||
MODE_END_OF_TABLE = 0,
|
MODE_END_OF_TABLE = 0,
|
||||||
|
/*! Idle mode */
|
||||||
MODE_IDLE,
|
MODE_IDLE,
|
||||||
|
/*! Receive mode */
|
||||||
MODE_RX,
|
MODE_RX,
|
||||||
|
/*! Transmission mode */
|
||||||
MODE_TX,
|
MODE_TX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,7 @@
|
||||||
#include "modules/SX126x/SX1261.h"
|
#include "modules/SX126x/SX1261.h"
|
||||||
#include "modules/SX126x/SX1262.h"
|
#include "modules/SX126x/SX1262.h"
|
||||||
#include "modules/SX126x/SX1268.h"
|
#include "modules/SX126x/SX1268.h"
|
||||||
|
#include "modules/SX126x/STM32WLx.h"
|
||||||
#include "modules/SX127x/SX1272.h"
|
#include "modules/SX127x/SX1272.h"
|
||||||
#include "modules/SX127x/SX1273.h"
|
#include "modules/SX127x/SX1273.h"
|
||||||
#include "modules/SX127x/SX1276.h"
|
#include "modules/SX127x/SX1276.h"
|
||||||
|
|
76
src/modules/SX126x/STM32WLx.cpp
Normal file
76
src/modules/SX126x/STM32WLx.cpp
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
Copyright (c) 2018 Jan Gromeš
|
||||||
|
Copyright (c) 2022 STMicroelectronics
|
||||||
|
|
||||||
|
This file is licensed under the MIT License: https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "STM32WLx.h"
|
||||||
|
#if !defined(RADIOLIB_EXCLUDE_STM32WLX)
|
||||||
|
|
||||||
|
|
||||||
|
STM32WLx::STM32WLx(STM32WLx_Module* mod) : SX1262(mod) {
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t STM32WLx::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, uint16_t preambleLength, float tcxoVoltage, bool useRegulatorLDO) {
|
||||||
|
// Execute common part
|
||||||
|
int16_t state = SX1262::begin(freq, bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage, useRegulatorLDO);
|
||||||
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
|
// This overrides the value in SX126x::begin()
|
||||||
|
// On STM32WL, DIO2 is hardwired to the radio IRQ on the MCU, so it
|
||||||
|
// should really not be used as RfSwitch control output.
|
||||||
|
state = setDio2AsRfSwitch(false);
|
||||||
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
|
return(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t STM32WLx::beginFSK(float freq, float br, float freqDev, float rxBw, int8_t power, uint16_t preambleLength, float tcxoVoltage, bool useRegulatorLDO) {
|
||||||
|
// Execute common part
|
||||||
|
int16_t state = SX1262::beginFSK(freq, br, freqDev, rxBw, power, preambleLength, tcxoVoltage, useRegulatorLDO);
|
||||||
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
|
// This overrides the value in SX126x::beginFSK()
|
||||||
|
// On STM32WL, DIO2 is hardwired to the radio IRQ on the MCU, so it
|
||||||
|
// should really not be used as RfSwitch control output.
|
||||||
|
state = setDio2AsRfSwitch(false);
|
||||||
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
|
return(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t STM32WLx::setOutputPower(int8_t power) {
|
||||||
|
// get current OCP configuration
|
||||||
|
uint8_t ocp = 0;
|
||||||
|
int16_t state = readRegister(RADIOLIB_SX126X_REG_OCP_CONFIGURATION, &ocp, 1);
|
||||||
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
|
// Use HP only if available and needed for the requested power
|
||||||
|
bool hp_supported = _mod->findRfSwitchMode(MODE_TX_HP);
|
||||||
|
bool use_hp = power > 14 && hp_supported;
|
||||||
|
|
||||||
|
// set PA config.
|
||||||
|
if(use_hp) {
|
||||||
|
RADIOLIB_CHECK_RANGE(power, -9, 22, RADIOLIB_ERR_INVALID_OUTPUT_POWER);
|
||||||
|
state = SX126x::setPaConfig(0x04, 0x00, 0x07); // HP output up to 22dBm
|
||||||
|
_tx_mode = MODE_TX_HP;
|
||||||
|
} else {
|
||||||
|
RADIOLIB_CHECK_RANGE(power, -17, 14, RADIOLIB_ERR_INVALID_OUTPUT_POWER);
|
||||||
|
state = SX126x::setPaConfig(0x04, 0x01, 0x00); // LP output up to 14dBm
|
||||||
|
_tx_mode = MODE_TX_LP;
|
||||||
|
}
|
||||||
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
|
// set output power
|
||||||
|
/// \todo power ramp time configuration
|
||||||
|
state = SX126x::setTxParams(power);
|
||||||
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
|
// restore OCP configuration
|
||||||
|
return(writeRegister(RADIOLIB_SX126X_REG_OCP_CONFIGURATION, &ocp, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif // !defined(RADIOLIB_EXCLUDE_STM32WLX)
|
122
src/modules/SX126x/STM32WLx.h
Normal file
122
src/modules/SX126x/STM32WLx.h
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
Copyright (c) 2018 Jan Gromeš
|
||||||
|
Copyright (c) 2022 STMicroelectronics
|
||||||
|
|
||||||
|
This file is licensed under the MIT License: https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !defined(_RADIOLIB_STM32WLx_H)
|
||||||
|
#define _RADIOLIB_STM32WLx_H
|
||||||
|
|
||||||
|
#include "../../TypeDef.h"
|
||||||
|
|
||||||
|
#if !defined(RADIOLIB_EXCLUDE_STM32WLX)
|
||||||
|
|
||||||
|
#include "../../Module.h"
|
||||||
|
#include "SX1262.h"
|
||||||
|
#include "STM32WLx_Module.h"
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\class STM32WLx
|
||||||
|
|
||||||
|
\brief Derived class for STM32WL modules.
|
||||||
|
|
||||||
|
The radio integrated into these modules is essentially the same as the
|
||||||
|
Semtech %SX126x external radio chips, so most of the documentation for
|
||||||
|
those also applies here.
|
||||||
|
|
||||||
|
One notable difference with the %SX126x radios is that this radio
|
||||||
|
essentially combines the %SX1261 and %SX1262 by integrating both the
|
||||||
|
low-power (LP) and high-power (HP) amplifier. See setOutputPower() and
|
||||||
|
setRfSwitchTable() for details on how this is handled.
|
||||||
|
*/
|
||||||
|
class STM32WLx : public SX1262 {
|
||||||
|
// NOTE: This class could not be named STM32WL (or STM32WLxx), since
|
||||||
|
// those are macros defined by
|
||||||
|
// system/Drivers/CMSIS/Device/ST/STM32WLxxx/Include/stm32wlxx.h
|
||||||
|
public:
|
||||||
|
/*!
|
||||||
|
\brief Default constructor.
|
||||||
|
|
||||||
|
\param mod Instance of STM32WLx_Module that will be used to communicate with the radio.
|
||||||
|
*/
|
||||||
|
STM32WLx(STM32WLx_Module* mod);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Custom operation modes for STMWLx.
|
||||||
|
*
|
||||||
|
* This splits the TX mode into two modes: Low-power and high-power.
|
||||||
|
* These constants can be used with the setRfSwitchTable() method,
|
||||||
|
* instead of the Module::OpMode_t constants.
|
||||||
|
*/
|
||||||
|
enum OpMode_t {
|
||||||
|
/*! End of table marker, use \ref END_OF_MODE_TABLE constant instead */
|
||||||
|
MODE_END_OF_TABLE = Module::MODE_END_OF_TABLE,
|
||||||
|
/*! Idle mode */
|
||||||
|
MODE_IDLE = Module::MODE_IDLE,
|
||||||
|
/*! Receive mode */
|
||||||
|
MODE_RX = Module::MODE_RX,
|
||||||
|
/*! Low power transmission mode */
|
||||||
|
MODE_TX_LP = Module::MODE_TX,
|
||||||
|
/*! High power transmission mode */
|
||||||
|
MODE_TX_HP,
|
||||||
|
};
|
||||||
|
/*! \copydoc Module::END_OF_MODE_TABLE */
|
||||||
|
static constexpr auto END_OF_MODE_TABLE = Module::END_OF_MODE_TABLE;
|
||||||
|
|
||||||
|
// basic methods
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\copydoc SX1262::begin
|
||||||
|
*/
|
||||||
|
int16_t begin(float freq = 434.0, float bw = 125.0, uint8_t sf = 9, uint8_t cr = 7, uint8_t syncWord = RADIOLIB_SX126X_SYNC_WORD_PRIVATE, int8_t power = 10, uint16_t preambleLength = 8, float tcxoVoltage = 1.6, bool useRegulatorLDO = false);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\copydoc SX1262::beginFSK
|
||||||
|
*/
|
||||||
|
int16_t beginFSK(float freq = 434.0, float br = 4.8, float freqDev = 5.0, float rxBw = 156.2, int8_t power = 10, uint16_t preambleLength = 16, float tcxoVoltage = 1.6, bool useRegulatorLDO = false);
|
||||||
|
|
||||||
|
// configuration methods
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Sets output power. Allowed values are in range from -17 to 22 dBm.
|
||||||
|
|
||||||
|
This automatically switches between the low-power (LP) and high-power (HP) amplifier.
|
||||||
|
|
||||||
|
LP is preferred and supports -17 to +14dBm. When a higher power is
|
||||||
|
requested (or the LP amplifier is marked as unvailable using
|
||||||
|
setRfSwitchTable()), HP is used, which supports -9 to +22dBm.
|
||||||
|
|
||||||
|
\param power Output power to be set in dBm.
|
||||||
|
|
||||||
|
\returns \ref status_codes
|
||||||
|
*/
|
||||||
|
virtual int16_t setOutputPower(int8_t power) override;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\copybrief Module::setRfSwitchTable
|
||||||
|
|
||||||
|
This method works like Module::setRfSwitchTable(), except that you
|
||||||
|
should use STM32WLx::OpMode_t constants for modes, which
|
||||||
|
distinguishes between a low-power (LP) and high-power (HP) TX mode.
|
||||||
|
|
||||||
|
For boards that do not support both modes, just omit the
|
||||||
|
unsupported mode from the table and it will not be used (and the
|
||||||
|
valid power range is adjusted by setOutputPower() accordingly).
|
||||||
|
|
||||||
|
Note that the setRfSwitchTable() method should be called *before* the
|
||||||
|
begin() method, to ensure the radio knows which modes are supported
|
||||||
|
during initialization.
|
||||||
|
*/
|
||||||
|
// Note: This explicitly inherits this method only to override docs
|
||||||
|
using SX126x::setRfSwitchTable;
|
||||||
|
|
||||||
|
#if !defined(RADIOLIB_GODMODE)
|
||||||
|
private:
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // !defined(RADIOLIB_EXCLUDE_SX126X)
|
||||||
|
|
||||||
|
#endif // _RADIOLIB_STM32WLX_MODULE_H
|
99
src/modules/SX126x/STM32WLx_Module.cpp
Normal file
99
src/modules/SX126x/STM32WLx_Module.cpp
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
Copyright (c) 2022 STMicroelectronics
|
||||||
|
|
||||||
|
This file is licensed under the MIT License: https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "STM32WLx_Module.h"
|
||||||
|
|
||||||
|
#if !defined(RADIOLIB_EXCLUDE_STM32WLX)
|
||||||
|
|
||||||
|
#include <SubGhz.h>
|
||||||
|
|
||||||
|
// This defines some dummy pin numbers (starting at NUM_DIGITAL_PINS to
|
||||||
|
// guarantee these are not valid regular pin numbers) that can be passed
|
||||||
|
// to the parent Module class, to be stored here and then passed back to
|
||||||
|
// the overridden callbacks when these are used.
|
||||||
|
enum {
|
||||||
|
RADIOLIB_STM32WLx_VIRTUAL_PIN_NSS = NUM_DIGITAL_PINS,
|
||||||
|
RADIOLIB_STM32WLx_VIRTUAL_PIN_BUSY,
|
||||||
|
RADIOLIB_STM32WLx_VIRTUAL_PIN_IRQ,
|
||||||
|
RADIOLIB_STM32WLx_VIRTUAL_PIN_RESET,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
STM32WLx_Module::STM32WLx_Module():
|
||||||
|
Module(
|
||||||
|
RADIOLIB_STM32WLx_VIRTUAL_PIN_NSS,
|
||||||
|
RADIOLIB_STM32WLx_VIRTUAL_PIN_IRQ,
|
||||||
|
RADIOLIB_STM32WLx_VIRTUAL_PIN_RESET,
|
||||||
|
RADIOLIB_STM32WLx_VIRTUAL_PIN_BUSY,
|
||||||
|
SubGhz.SPI,
|
||||||
|
SubGhz.spi_settings
|
||||||
|
)
|
||||||
|
{
|
||||||
|
setCb_pinMode(virtualPinMode);
|
||||||
|
setCb_digitalWrite(virtualDigitalWrite);
|
||||||
|
setCb_digitalRead(virtualDigitalRead);
|
||||||
|
}
|
||||||
|
|
||||||
|
void STM32WLx_Module::virtualPinMode(uint32_t dwPin, uint32_t dwMode) {
|
||||||
|
switch(dwPin) {
|
||||||
|
case RADIOLIB_STM32WLx_VIRTUAL_PIN_NSS:
|
||||||
|
case RADIOLIB_STM32WLx_VIRTUAL_PIN_BUSY:
|
||||||
|
case RADIOLIB_STM32WLx_VIRTUAL_PIN_IRQ:
|
||||||
|
case RADIOLIB_STM32WLx_VIRTUAL_PIN_RESET:
|
||||||
|
// Nothing to do
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
::pinMode(dwPin, dwMode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void STM32WLx_Module::virtualDigitalWrite(uint32_t dwPin, uint32_t dwVal) {
|
||||||
|
switch (dwPin) {
|
||||||
|
case RADIOLIB_STM32WLx_VIRTUAL_PIN_NSS:
|
||||||
|
SubGhz.setNssActive(dwVal == LOW);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RADIOLIB_STM32WLx_VIRTUAL_PIN_RESET:
|
||||||
|
SubGhz.setResetActive(dwVal == LOW);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RADIOLIB_STM32WLx_VIRTUAL_PIN_BUSY:
|
||||||
|
case RADIOLIB_STM32WLx_VIRTUAL_PIN_IRQ:
|
||||||
|
// Should not (and cannot) be written, just ignore
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
::digitalWrite(dwPin, dwVal);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int STM32WLx_Module::virtualDigitalRead(uint32_t ulPin) {
|
||||||
|
switch (ulPin) {
|
||||||
|
case RADIOLIB_STM32WLx_VIRTUAL_PIN_BUSY:
|
||||||
|
return(SubGhz.isBusy() ? HIGH : LOW);
|
||||||
|
|
||||||
|
case RADIOLIB_STM32WLx_VIRTUAL_PIN_IRQ:
|
||||||
|
// If the IRQ is disabled, we can read the value by clearing and
|
||||||
|
// seeing if it immediately becomes pending again.
|
||||||
|
// TODO: This seems a bit fragile, but it does work.
|
||||||
|
SubGhz.clearPendingInterrupt();
|
||||||
|
return(SubGhz.isInterruptPending() ? HIGH : LOW);
|
||||||
|
|
||||||
|
case RADIOLIB_STM32WLx_VIRTUAL_PIN_NSS:
|
||||||
|
return(SubGhz.isNssActive() ? LOW : HIGH);
|
||||||
|
|
||||||
|
case RADIOLIB_STM32WLx_VIRTUAL_PIN_RESET:
|
||||||
|
return(SubGhz.isResetActive() ? LOW : HIGH);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return(::digitalRead(ulPin));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // !defined(RADIOLIB_EXCLUDE_STM32WLX)
|
49
src/modules/SX126x/STM32WLx_Module.h
Normal file
49
src/modules/SX126x/STM32WLx_Module.h
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
Copyright (c) 2022 STMicroelectronics
|
||||||
|
|
||||||
|
This file is licensed under the MIT License: https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !defined(_RADIOLIB_STM32WLX_MODULE_H)
|
||||||
|
#define _RADIOLIB_STM32WLX_MODULE_H
|
||||||
|
|
||||||
|
#include "../../TypeDef.h"
|
||||||
|
|
||||||
|
#if !defined(RADIOLIB_EXCLUDE_STM32WLX)
|
||||||
|
|
||||||
|
#include "../../Module.h"
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \class STM32WLx_Module
|
||||||
|
*
|
||||||
|
* This is a subclass of Module to be used with the STM32WLx driver.
|
||||||
|
*
|
||||||
|
* It is used to override some callbacks, allowing access to some of the
|
||||||
|
* radio control signals that are wired to internal registers instead of
|
||||||
|
* actual GPIO pins.
|
||||||
|
*/
|
||||||
|
class STM32WLx_Module : public Module {
|
||||||
|
// Note: We cannot easily override any methods here, since most calls
|
||||||
|
// are non-virtual and made through a Module*, so they would not be
|
||||||
|
// calling any overridden methods. This means this class works by
|
||||||
|
// overriding some of the callbacks in its constructor.
|
||||||
|
|
||||||
|
public:
|
||||||
|
STM32WLx_Module();
|
||||||
|
|
||||||
|
#if !defined(RADIOLIB_GODMODE)
|
||||||
|
private:
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Replacement callbacks to handle virtual pins. These are static,
|
||||||
|
// since they replace global functions that cannot take any this
|
||||||
|
// pointer for context.
|
||||||
|
static void virtualPinMode(uint32_t dwPin, uint32_t dwMode);
|
||||||
|
static void virtualDigitalWrite(uint32_t dwPin, uint32_t dwVal);
|
||||||
|
static int virtualDigitalRead(uint32_t ulPin);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // !defined(RADIOLIB_EXCLUDE_STM32WLX)
|
||||||
|
|
||||||
|
#endif // _RADIOLIB_STM32WLX_MODULE_H
|
Loading…
Add table
Reference in a new issue