
This was introduced when STM32WL support was added. Using constexpr for the END_OF_MODE_TABLE constant allows it to be initialized in the class declaration, but this needs C++11. This moves the initialization out of the class declaration to the .cpp file, which does not need constexpr. It also lets STM32WLx::END_OF_MODE_TABLE define its value directly (instead of aliasing Module::END_OF_MODE_TABLE) to prevent reduce runtime overhead (see below). The downside of this change is that the value of the END_OF_MODE_TABLE is no longer visible in other compilation units and thus cannot be inlined into the rfswitch_table (if used). For example, on STM32, this means that instead of having a pre-cooked rfswitch_table that lives in the .rodata section (so can be read directly from flash), the table lives in RAM and is initialized at runtime (the actual modes and pins are copied from flash to RAM by the standard startup loop that copies all of the .data section, and the END_OF_MODE_TABLE value is copied by a bit of new generated code). This means a little more runtime overhead, but the cost is mostly in RAM size (80 bytes for the SMT32WL sketches, 16 per mode plus 16 for the END_OF_MODE_TABLE). In a first attempt at this commit, STM32WLx::END_OF_MODE_TABLE was still initialized using the Module::END_OF_MODE_TABLE value, but since the latter is also not available at compiletime, this meant initialization of the former also needed to happen at runtime, adding even more code overhead (and possibly leading to ordering issues as well). To avoid this, the STM32WLx::END_OF_MODE_TABLE initialization now just duplicates that of Module::END_OF_MODE_TABLE. On AVR, the impact is not so much: Since AVR cannot address flash directly, the table was already copied from flash to RAM at startup, so the extra RAM usage is just 4 bytes because END_OF_MODE_TABLE is now also present in RAM, to be copied into rfswitch_table at startup. Options for avoiding this overhead (not implemented in this commit) could be (in no particular order): 1. Use a macro instead of a constant. Downside is that these cannot be scoped inside the Module/STM32WLx classes like now, so this requires changes to sketches that use a rfswitch_table (and reduced scoping and using macros adds more opportunity for conflicts and weird errors). 2. Apply the change in this commit only when C++11 is not available. Downside is that the initialization value of these constants must be duplicated in the .h and .cpp file for C++ and older versions respectively. 3. Let sketches just use `{Module::MODE_END_OF_TABLE, {}}` explicitly instead of `Module::END_OF_MODE_TABLE`. Downside of this is that this requires sketches to be modified and that it lets the sketch encode more of the table structure, potentially making future API changes harder (but it probably does not really matter in practice). 4. Turn END_OF_MODE_TABLE into a static method, which *can* then be defined in the class declaration and inlined. The method can then be conditionally marked as constexpr, which allows C++11 compilers to completely resolve the rfswitch_table value at compiletime, producing a binary identical to before this commit. When constexpr is omitted (e.g. on older compilers), some runtime overhead is added (pretty much the same as the result from this commit). Downside is that sketches must be modified, and the `END_OF_MODE_TABLE` "constant" must now be called, e.g. `END_OF_MODE_TABLE()` which might be a bit unexpected syntax.
108 lines
3.6 KiB
C++
108 lines
3.6 KiB
C++
/*
|
|
|
|
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)
|
|
|
|
#include <SubGhz.h>
|
|
|
|
const Module::RfSwitchMode_t STM32WLx::END_OF_MODE_TABLE = {Module::MODE_END_OF_TABLE, {}};
|
|
|
|
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);
|
|
|
|
// Apply workaround for HP only
|
|
state = SX126x::fixPaClamping(use_hp);
|
|
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));
|
|
}
|
|
|
|
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)
|