From 90b28d77223ecdd1699c8d3807f3477623bc5cea Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 6 Dec 2022 17:41:30 +0100 Subject: [PATCH 01/13] Remove duplicated setRfSwitchPins documentation All radios that support RfSwitch define this method that simply forwards to the `Module::setRfSwitchPins()` method. Previously, all these methods duplicated the documentation as well, but this uses the doxygen \copydoc to remove this duplication. --- src/modules/CC1101/CC1101.h | 9 +-------- src/modules/RF69/RF69.h | 9 +-------- src/modules/SX126x/SX126x.h | 9 +-------- src/modules/SX127x/SX127x.h | 9 +-------- src/modules/SX128x/SX128x.h | 9 +-------- src/modules/Si443x/Si443x.h | 9 +-------- 6 files changed, 6 insertions(+), 48 deletions(-) diff --git a/src/modules/CC1101/CC1101.h b/src/modules/CC1101/CC1101.h index bb245cbe..558519c2 100644 --- a/src/modules/CC1101/CC1101.h +++ b/src/modules/CC1101/CC1101.h @@ -928,14 +928,7 @@ class CC1101: public PhysicalLayer { */ int16_t setEncoding(uint8_t encoding) override; - /*! - \brief Some modules contain external RF switch controlled by two pins. This function gives RadioLib control over those two pins to automatically switch Rx and Tx state. - When using automatic RF switch control, DO NOT change the pin mode of rxEn or txEn from Arduino sketch! - - \param rxEn RX enable pin. - - \param txEn TX enable pin. - */ + /*! \copydoc Module::setRfSwitchPins */ void setRfSwitchPins(RADIOLIB_PIN_TYPE rxEn, RADIOLIB_PIN_TYPE txEn); /*! diff --git a/src/modules/RF69/RF69.h b/src/modules/RF69/RF69.h index 979f4845..e799b716 100644 --- a/src/modules/RF69/RF69.h +++ b/src/modules/RF69/RF69.h @@ -1010,14 +1010,7 @@ class RF69: public PhysicalLayer { */ int16_t setRSSIThreshold(float dbm); - /*! - \brief Some modules contain external RF switch controlled by two pins. This function gives RadioLib control over those two pins to automatically switch Rx and Tx state. - When using automatic RF switch control, DO NOT change the pin mode of rxEn or txEn from Arduino sketch! - - \param rxEn RX enable pin. - - \param txEn TX enable pin. - */ + /*! \copydoc Module::setRfSwitchPins */ void setRfSwitchPins(RADIOLIB_PIN_TYPE rxEn, RADIOLIB_PIN_TYPE txEn); /*! diff --git a/src/modules/SX126x/SX126x.h b/src/modules/SX126x/SX126x.h index b56bcb29..0c8d1756 100644 --- a/src/modules/SX126x/SX126x.h +++ b/src/modules/SX126x/SX126x.h @@ -914,14 +914,7 @@ class SX126x: public PhysicalLayer { */ int16_t setEncoding(uint8_t encoding) override; - /*! - \brief Some modules contain external RF switch controlled by two pins. This function gives RadioLib control over those two pins to automatically switch Rx and Tx state. - When using automatic RF switch control, DO NOT change the pin mode of rxEn or txEn from Arduino sketch! - - \param rxEn RX enable pin. - - \param txEn TX enable pin. - */ + /*! \copydoc Module::setRfSwitchPins */ void setRfSwitchPins(RADIOLIB_PIN_TYPE rxEn, RADIOLIB_PIN_TYPE txEn); /*! diff --git a/src/modules/SX127x/SX127x.h b/src/modules/SX127x/SX127x.h index 302fabf4..4d68a2fa 100644 --- a/src/modules/SX127x/SX127x.h +++ b/src/modules/SX127x/SX127x.h @@ -1141,14 +1141,7 @@ class SX127x: public PhysicalLayer { */ int8_t getTempRaw(); - /*! - \brief Some modules contain external RF switch controlled by two pins. This function gives RadioLib control over those two pins to automatically switch Rx and Tx state. - When using automatic RF switch control, DO NOT change the pin mode of rxEn or txEn from Arduino sketch! - - \param rxEn RX enable pin. - - \param txEn TX enable pin. - */ + /*! \copydoc Module::setRfSwitchPins */ void setRfSwitchPins(RADIOLIB_PIN_TYPE rxEn, RADIOLIB_PIN_TYPE txEn); /*! diff --git a/src/modules/SX128x/SX128x.h b/src/modules/SX128x/SX128x.h index c367013f..854ef06a 100644 --- a/src/modules/SX128x/SX128x.h +++ b/src/modules/SX128x/SX128x.h @@ -809,14 +809,7 @@ class SX128x: public PhysicalLayer { */ int16_t setEncoding(uint8_t encoding) override; - /*! - \brief Some modules contain external RF switch controlled by two pins. This function gives RadioLib control over those two pins to automatically switch Rx and Tx state. - When using automatic RF switch control, DO NOT change the pin mode of rxEn or txEn from Arduino sketch! - - \param rxEn RX enable pin. - - \param txEn TX enable pin. - */ + /*! \copydoc Module::setRfSwitchPins */ void setRfSwitchPins(RADIOLIB_PIN_TYPE rxEn, RADIOLIB_PIN_TYPE txEn); /*! diff --git a/src/modules/Si443x/Si443x.h b/src/modules/Si443x/Si443x.h index cf4f689b..e3218c68 100644 --- a/src/modules/Si443x/Si443x.h +++ b/src/modules/Si443x/Si443x.h @@ -785,14 +785,7 @@ class Si443x: public PhysicalLayer { */ int16_t setDataShaping(uint8_t sh) override; - /*! - \brief Some modules contain external RF switch controlled by two pins. This function gives RadioLib control over those two pins to automatically switch Rx and Tx state. - When using automatic RF switch control, DO NOT change the pin mode of rxEn or txEn from Arduino sketch! - - \param rxEn RX enable pin. - - \param txEn TX enable pin. - */ + /*! \copydoc Module::setRfSwitchPins */ void setRfSwitchPins(RADIOLIB_PIN_TYPE rxEn, RADIOLIB_PIN_TYPE txEn); /*! From 83ff964b6613beba695ac084b366f58f9b41bb8f Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 6 Dec 2022 17:52:18 +0100 Subject: [PATCH 02/13] [MOD] Generalize rfswitch pin handling This defines operation modes (IDLE, RX and TX) and allows defining up to to three pins to be controlled. For each mode a value can be specified for each pin a table. Compared to the previous handling, this: - Allows up to three pins instead of only two. - Gives more control over output pin values (e.g. to simply change polarity or support more complex control logic). In addition, the modes are treated as opaque by the Module code, allowing radio classes to define their own modes if needed. Some notes regarding the implementation: - The number of pins is limited at three, since most boards seem to need only two pins and only the Nucleo STM32WL55 board needs three. If more pins are needed in the future, the setRfSwitchTable() can be overloaded to accept either a 3-element or e.g. 4-element pins array, to allow new and old code to work as-is. Note that there is a RFSWITCH_MAX_PINS constant defined, but it is not recommended for sketches to use this constant when defining a rfswitch pins array, to prevent issues when this value is ever increased and such an array gets extra zero elements (that will be interpreted as pin 0). Note that this is not a problem for the RfSwitchMode_t values array, since any extra values in there will only be used if a valid pin was set in the pins array. - The pins array is passed by reference, so the compiler complains if the array passed is not the expected size. Since a reference to an array without a length is not supported (at least not by the gcc 7 used by the AVR core - gcc 10 for STM32 seems to accept it), the table array is passed as a pointer instead (but because arrays and pointers are reasonably interchangeable, the caller does not see the difference). - The existing setRfSwitchPins() method is still supported as before. Internally it creates a table with the right values and pins and passes those to setRfSwitchTable. - For easier review, this commit does not modify all calls to setRfSwitchState() in all radio modules yet, but has a compatibility wrapper to delay this change until the next commit. Similarly, the setRfSwitchTable() method is now defined on Module only, a wrapper for it will be defined in all radios that already have the setRfSwitchPins() wrapper in another commit. - To allow future radios to define any number of modes, the modes table does not have a fixed length, but instead is terminated by a special value. This is a bit fragile (if the terminator is omitted, the code will read past the end of the array), but rather flexible. One alternative to this approach would be to make setRfSwitchTable a template that deduces the array size from a template argument and then stores the size explicitly, but using templates probably reduces code clarity. --- src/Module.cpp | 50 ++++++++++++---- src/Module.h | 151 +++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 180 insertions(+), 21 deletions(-) diff --git a/src/Module.cpp b/src/Module.cpp index ba0674c0..5046da63 100644 --- a/src/Module.cpp +++ b/src/Module.cpp @@ -551,20 +551,50 @@ void Module::regdump(uint8_t start, uint8_t len) { } void Module::setRfSwitchPins(RADIOLIB_PIN_TYPE rxEn, RADIOLIB_PIN_TYPE txEn) { - _useRfSwitch = true; - _rxEn = rxEn; - _txEn = txEn; - this->pinMode(rxEn, OUTPUT); - this->pinMode(txEn, OUTPUT); + // This can be on the stack, setRfSwitchTable copies the contents + const RADIOLIB_PIN_TYPE pins[] = { + rxEn, txEn, RADIOLIB_NC, + }; + // This must be static, since setRfSwitchTable stores a reference. + static constexpr RfSwitchMode_t table[] = { + {MODE_IDLE, {LOW, LOW}}, + {MODE_RX, {HIGH, LOW}}, + {MODE_TX, {LOW, HIGH}}, + END_OF_MODE_TABLE, + }; + setRfSwitchTable(pins, table); } -void Module::setRfSwitchState(RADIOLIB_PIN_STATUS rxPinState, RADIOLIB_PIN_STATUS txPinState) { - // check RF switch control is enabled - if(!_useRfSwitch) { +void Module::setRfSwitchTable(const RADIOLIB_PIN_TYPE (&pins)[3], const RfSwitchMode_t table[]) { + memcpy(_rfSwitchPins, pins, sizeof(_rfSwitchPins)); + _rfSwitchTable = table; + for(size_t i = 0; i < RFSWITCH_MAX_PINS; i++) + this->pinMode(pins[i], OUTPUT); +} + +const Module::RfSwitchMode_t *Module::findRfSwitchMode(uint8_t mode) const { + const RfSwitchMode_t *row = _rfSwitchTable; + while (row && row->mode != MODE_END_OF_TABLE) { + if (row->mode == mode) + return row; + ++row; + } + return nullptr; +} + +void Module::setRfSwitchState(uint8_t mode) { + const RfSwitchMode_t *row = findRfSwitchMode(mode); + if(!row) { + // RF switch control is disabled or does not have this mode return; } // set pins - this->digitalWrite(_rxEn, rxPinState); - this->digitalWrite(_txEn, txPinState); + const RADIOLIB_PIN_STATUS *value = &row->values[0]; + for(size_t i = 0; i < RFSWITCH_MAX_PINS; i++) { + RADIOLIB_PIN_TYPE pin = _rfSwitchPins[i]; + if (pin != RADIOLIB_NC) + this->digitalWrite(pin, *value); + ++value; + } } diff --git a/src/Module.h b/src/Module.h index f699d5fe..d566b560 100644 --- a/src/Module.h +++ b/src/Module.h @@ -15,6 +15,49 @@ */ class Module { public: + /*! + * \brief The maximum number of pins supported by the RF switch + * code. + * + * Note: It is not recommended to use this constant in your sketch + * when defining a rfswitch pins array, to prevent issues when this + * value is ever increased and such an array gets extra zero + * elements (that will be interpreted as pin 0). + */ + static const size_t RFSWITCH_MAX_PINS = 3; + + /*! + * Description of RF switch pin states for a single mode. + * + * See setRfSwitchTable() for details. + */ + struct RfSwitchMode_t { + uint8_t mode; + RADIOLIB_PIN_STATUS values[RFSWITCH_MAX_PINS]; + }; + + /*! + * Constants to use in a mode table set be setRfSwitchTable. These + * constants work for most radios, but some radios define their own + * constants to be used instead. + * + * See setRfSwitchTable() for details. + */ + enum OpMode_t { + // Table end marker is zero to ensure zero-initialized mode ends the table + MODE_END_OF_TABLE = 0, + MODE_IDLE, + MODE_RX, + MODE_TX, + }; + + /*! + * Value to use as the last element in a mode table to indicate the + * end of the table. + * + * See setRfSwitchTable() for details. + */ + static constexpr RfSwitchMode_t END_OF_MODE_TABLE = {MODE_END_OF_TABLE, {}}; #if defined(RADIOLIB_BUILD_ARDUINO) @@ -242,23 +285,110 @@ class Module { RADIOLIB_PIN_TYPE getGpio() const { return(_gpio); } /*! - \brief Some modules contain external RF switch controlled by two pins. This function gives RadioLib control over those two pins to automatically switch Rx and Tx state. - When using automatic RF switch control, DO NOT change the pin mode of rxEn or txEn from Arduino sketch! + \brief Some modules contain external RF switch controlled by pins. + This function gives RadioLib control over those pins to + automatically switch between various modes: When idle both pins + will be LOW, during TX the `txEn` pin will be HIGH, during RX the + `rxPin` will be HIGH. + + Radiolib will automatically set the pin mode and value of these + pins, so do not control them from the sketch. + + When more than two pins or more control over the output values are + needed, use the setRfSwitchTable() function. \param rxEn RX enable pin. - \param txEn TX enable pin. */ void setRfSwitchPins(RADIOLIB_PIN_TYPE rxEn, RADIOLIB_PIN_TYPE txEn); /*! - \brief Set RF switch state. + \brief Some modules contain external RF switch controlled by pins. + This function gives RadioLib control over those pins to + automatically switch between various modes. - \param rxPinState Pin state to set on Tx enable pin (usually high to transmit). + Radiolib will automatically set the pin mode and value of these + pins, so do not control them from the sketch. - \param txPinState Pin state to set on Rx enable pin (usually high to receive). + + \param pins A reference to an array of pins to control. This + should always be an array of 3 elements. If you need less pins, + use RADIOLIB_NC for the unused elements. + + \param table A reference to an array of pin values to use for each + supported mode. Each element is an RfSwitchMode_T struct that + lists the mode for which it applies and the values for each of the + pins passed in the pins argument respectively. + + The `pins` array will be copied into the Module object, so the + original array can be deallocated after this call. However, + a reference to the `table` array will be stored, so that array + must remain valid as long RadioLib is being used. + + The `mode` field in each table row should normally use any of the + `MODE_*` constants from the Module::OpMode_t enum. However, some + radios support additional modes and will define their own OpMode_t + enum. + + The length of the table is variable (to support radios that add + additional modes), so the table must always be terminated with the + special END_OF_MODE_TABLE value. + + Normally all modes should be listed in the table, but for some + radios, modes can be omitted to indicate they are not supported + (e.g. when a radio has a high power and low power TX mode but + external circuitry only supports low power). If applicable, this + is documented in the radio class itself. + + #### Example + For example, on a board that has an RF switch with an enable pin + connected to PA0 and a TX/RX select pin connected to PA1: + + \code + // In global scope, define the pin array and mode table + static const RADIOLIB_PIN_TYPE rfswitch_pins[] = + {PA0, PA1, RADIOLIB_NC}; + static const Module::RfSwitchMode_t rfswitch_table[] = { + {Module::MODE_IDLE, {LOW, LOW}}, + {Module::MODE_RX, {HIGH, LOW}}, + {Module::MODE_TX, {HIGH, HIGH}}, + Module::END_OF_MODE_TABLE, + }; + + void setup() { + ... + // Then somewhere in setup, pass them to radiolib + radio.setRfSwitchTable(rfswitch_pins, rfswitch_table); + ... + } + \endcode */ - void setRfSwitchState(RADIOLIB_PIN_STATUS rxPinState, RADIOLIB_PIN_STATUS txPinState); + + void setRfSwitchTable(const RADIOLIB_PIN_TYPE (&pins)[RFSWITCH_MAX_PINS], const RfSwitchMode_t table[]); + + /*! + * \brief Find a mode in the RfSwitchTable. + * + * \param The mode to find. + * + * \returns A pointer to the RfSwitchMode_t struct in the table that + * matches the passed mode. Returns nullptr if no rfswitch pins are + * configured, or the passed mode is not listed in the table. + */ + const RfSwitchMode_t *findRfSwitchMode(uint8_t mode) const; + + /*! + \brief Set RF switch state. + \param mode The mode to set. This must be one of the MODE_ constants, or a radio-specific constant. + */ + void setRfSwitchState(uint8_t mode); + + /*! Temporary compatibility wrapper */ + void setRfSwitchState(RADIOLIB_PIN_STATUS rxPinState, RADIOLIB_PIN_STATUS txPinState) { + if (rxPinState) setRfSwitchState(MODE_RX); + else if (txPinState) setRfSwitchState(MODE_TX); + else setRfSwitchState(MODE_IDLE); + } /*! \brief Wait for time to elapse, either using the microsecond timer, or the TimerFlag. @@ -446,10 +576,9 @@ class Module { bool _initInterface = false; #endif - // RF switch presence and pins - bool _useRfSwitch = false; - RADIOLIB_PIN_TYPE _rxEn = RADIOLIB_NC; - RADIOLIB_PIN_TYPE _txEn = RADIOLIB_NC; + // RF switch pins and table + RADIOLIB_PIN_TYPE _rfSwitchPins[RFSWITCH_MAX_PINS] = { RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC }; + const RfSwitchMode_t *_rfSwitchTable = nullptr; #if defined(RADIOLIB_INTERRUPT_TIMING) uint32_t _prevTimingLen = 0; From 3779faf6007fdc29d296aa0dad671cb33b4c3c9a Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Mon, 9 Jan 2023 09:40:27 +0100 Subject: [PATCH 03/13] Add setRfSwitchTable() wrapper methods This gives all radios that use an rfswitch (i.e. have a setRfSwitchPins() wrapper already) a wrapper method for setRfSwitchTable() too. This wrapper just calls the same method on Module, to make it easier for sketches to use it. --- src/modules/CC1101/CC1101.cpp | 4 ++++ src/modules/CC1101/CC1101.h | 3 +++ src/modules/RF69/RF69.cpp | 4 ++++ src/modules/RF69/RF69.h | 3 +++ src/modules/SX126x/SX126x.cpp | 4 ++++ src/modules/SX126x/SX126x.h | 3 +++ src/modules/SX127x/SX127x.cpp | 4 ++++ src/modules/SX127x/SX127x.h | 3 +++ src/modules/SX128x/SX128x.cpp | 4 ++++ src/modules/SX128x/SX128x.h | 3 +++ src/modules/Si443x/Si443x.cpp | 4 ++++ src/modules/Si443x/Si443x.h | 3 +++ 12 files changed, 42 insertions(+) diff --git a/src/modules/CC1101/CC1101.cpp b/src/modules/CC1101/CC1101.cpp index f943a470..b3cb622c 100644 --- a/src/modules/CC1101/CC1101.cpp +++ b/src/modules/CC1101/CC1101.cpp @@ -902,6 +902,10 @@ void CC1101::setRfSwitchPins(RADIOLIB_PIN_TYPE rxEn, RADIOLIB_PIN_TYPE txEn) { _mod->setRfSwitchPins(rxEn, txEn); } +void CC1101::setRfSwitchTable(const RADIOLIB_PIN_TYPE (&pins)[Module::RFSWITCH_MAX_PINS], const Module::RfSwitchMode_t table[]) { + _mod->setRfSwitchTable(pins, table); +} + uint8_t CC1101::randomByte() { // set mode to Rx SPIsendCommand(RADIOLIB_CC1101_CMD_RX); diff --git a/src/modules/CC1101/CC1101.h b/src/modules/CC1101/CC1101.h index 558519c2..eb12b0be 100644 --- a/src/modules/CC1101/CC1101.h +++ b/src/modules/CC1101/CC1101.h @@ -931,6 +931,9 @@ class CC1101: public PhysicalLayer { /*! \copydoc Module::setRfSwitchPins */ void setRfSwitchPins(RADIOLIB_PIN_TYPE rxEn, RADIOLIB_PIN_TYPE txEn); + /*! \copydoc Module::setRfSwitchTable */ + void setRfSwitchTable(const RADIOLIB_PIN_TYPE (&pins)[Module::RFSWITCH_MAX_PINS], const Module::RfSwitchMode_t table[]); + /*! \brief Get one truly random byte from RSSI noise. diff --git a/src/modules/RF69/RF69.cpp b/src/modules/RF69/RF69.cpp index ec1f3c42..b1ef42e3 100644 --- a/src/modules/RF69/RF69.cpp +++ b/src/modules/RF69/RF69.cpp @@ -916,6 +916,10 @@ void RF69::setRfSwitchPins(RADIOLIB_PIN_TYPE rxEn, RADIOLIB_PIN_TYPE txEn) { _mod->setRfSwitchPins(rxEn, txEn); } +void RF69::setRfSwitchTable(const RADIOLIB_PIN_TYPE (&pins)[Module::RFSWITCH_MAX_PINS], const Module::RfSwitchMode_t table[]) { + _mod->setRfSwitchTable(pins, table); +} + uint8_t RF69::randomByte() { // set mode to Rx setMode(RADIOLIB_RF69_RX); diff --git a/src/modules/RF69/RF69.h b/src/modules/RF69/RF69.h index e799b716..d4767329 100644 --- a/src/modules/RF69/RF69.h +++ b/src/modules/RF69/RF69.h @@ -1013,6 +1013,9 @@ class RF69: public PhysicalLayer { /*! \copydoc Module::setRfSwitchPins */ void setRfSwitchPins(RADIOLIB_PIN_TYPE rxEn, RADIOLIB_PIN_TYPE txEn); + /*! \copydoc Module::setRfSwitchTable */ + void setRfSwitchTable(const RADIOLIB_PIN_TYPE (&pins)[Module::RFSWITCH_MAX_PINS], const Module::RfSwitchMode_t table[]); + /*! \brief Get one truly random byte from RSSI noise. diff --git a/src/modules/SX126x/SX126x.cpp b/src/modules/SX126x/SX126x.cpp index f5de00b3..8d9f67aa 100644 --- a/src/modules/SX126x/SX126x.cpp +++ b/src/modules/SX126x/SX126x.cpp @@ -1199,6 +1199,10 @@ void SX126x::setRfSwitchPins(RADIOLIB_PIN_TYPE rxEn, RADIOLIB_PIN_TYPE txEn) { _mod->setRfSwitchPins(rxEn, txEn); } +void SX126x::setRfSwitchTable(const RADIOLIB_PIN_TYPE (&pins)[Module::RFSWITCH_MAX_PINS], const Module::RfSwitchMode_t table[]) { + _mod->setRfSwitchTable(pins, table); +} + int16_t SX126x::forceLDRO(bool enable) { // check active modem if(getPacketType() != RADIOLIB_SX126X_PACKET_TYPE_LORA) { diff --git a/src/modules/SX126x/SX126x.h b/src/modules/SX126x/SX126x.h index 0c8d1756..47a9cb37 100644 --- a/src/modules/SX126x/SX126x.h +++ b/src/modules/SX126x/SX126x.h @@ -917,6 +917,9 @@ class SX126x: public PhysicalLayer { /*! \copydoc Module::setRfSwitchPins */ void setRfSwitchPins(RADIOLIB_PIN_TYPE rxEn, RADIOLIB_PIN_TYPE txEn); + /*! \copydoc Module::setRfSwitchTable */ + void setRfSwitchTable(const RADIOLIB_PIN_TYPE (&pins)[Module::RFSWITCH_MAX_PINS], const Module::RfSwitchMode_t table[]); + /*! \brief Forces LoRa low data rate optimization. Only available in LoRa mode. After calling this method, LDRO will always be set to the provided value, regardless of symbol length. To re-enable automatic LDRO configuration, call SX126x::autoLDRO() diff --git a/src/modules/SX127x/SX127x.cpp b/src/modules/SX127x/SX127x.cpp index 9930841e..6d1b77e4 100644 --- a/src/modules/SX127x/SX127x.cpp +++ b/src/modules/SX127x/SX127x.cpp @@ -1258,6 +1258,10 @@ void SX127x::setRfSwitchPins(RADIOLIB_PIN_TYPE rxEn, RADIOLIB_PIN_TYPE txEn) { _mod->setRfSwitchPins(rxEn, txEn); } +void SX127x::setRfSwitchTable(const RADIOLIB_PIN_TYPE (&pins)[Module::RFSWITCH_MAX_PINS], const Module::RfSwitchMode_t table[]) { + _mod->setRfSwitchTable(pins, table); +} + uint8_t SX127x::randomByte() { // check active modem uint8_t rssiValueReg = RADIOLIB_SX127X_REG_RSSI_WIDEBAND; diff --git a/src/modules/SX127x/SX127x.h b/src/modules/SX127x/SX127x.h index 4d68a2fa..e7502c15 100644 --- a/src/modules/SX127x/SX127x.h +++ b/src/modules/SX127x/SX127x.h @@ -1144,6 +1144,9 @@ class SX127x: public PhysicalLayer { /*! \copydoc Module::setRfSwitchPins */ void setRfSwitchPins(RADIOLIB_PIN_TYPE rxEn, RADIOLIB_PIN_TYPE txEn); + /*! \copydoc Module::setRfSwitchTable */ + void setRfSwitchTable(const RADIOLIB_PIN_TYPE (&pins)[Module::RFSWITCH_MAX_PINS], const Module::RfSwitchMode_t table[]); + /*! \brief Get one truly random byte from RSSI noise. diff --git a/src/modules/SX128x/SX128x.cpp b/src/modules/SX128x/SX128x.cpp index 32a2970e..14313759 100644 --- a/src/modules/SX128x/SX128x.cpp +++ b/src/modules/SX128x/SX128x.cpp @@ -1243,6 +1243,10 @@ void SX128x::setRfSwitchPins(RADIOLIB_PIN_TYPE rxEn, RADIOLIB_PIN_TYPE txEn) { _mod->setRfSwitchPins(rxEn, txEn); } +void SX128x::setRfSwitchTable(const RADIOLIB_PIN_TYPE (&pins)[Module::RFSWITCH_MAX_PINS], const Module::RfSwitchMode_t table[]) { + _mod->setRfSwitchTable(pins, table); +} + uint8_t SX128x::randomByte() { // it's unclear whether SX128x can measure RSSI while not receiving a packet // this method is implemented only for PhysicalLayer compatibility diff --git a/src/modules/SX128x/SX128x.h b/src/modules/SX128x/SX128x.h index 854ef06a..98bd7252 100644 --- a/src/modules/SX128x/SX128x.h +++ b/src/modules/SX128x/SX128x.h @@ -812,6 +812,9 @@ class SX128x: public PhysicalLayer { /*! \copydoc Module::setRfSwitchPins */ void setRfSwitchPins(RADIOLIB_PIN_TYPE rxEn, RADIOLIB_PIN_TYPE txEn); + /*! \copydoc Module::setRfSwitchTable */ + void setRfSwitchTable(const RADIOLIB_PIN_TYPE (&pins)[Module::RFSWITCH_MAX_PINS], const Module::RfSwitchMode_t table[]); + /*! \brief Dummy random method, to ensure PhysicalLayer compatibility. diff --git a/src/modules/Si443x/Si443x.cpp b/src/modules/Si443x/Si443x.cpp index f7e81576..7cfca65b 100644 --- a/src/modules/Si443x/Si443x.cpp +++ b/src/modules/Si443x/Si443x.cpp @@ -568,6 +568,10 @@ void Si443x::setRfSwitchPins(RADIOLIB_PIN_TYPE rxEn, RADIOLIB_PIN_TYPE txEn) { _mod->setRfSwitchPins(rxEn, txEn); } +void Si443x::setRfSwitchTable(const RADIOLIB_PIN_TYPE (&pins)[Module::RFSWITCH_MAX_PINS], const Module::RfSwitchMode_t table[]) { + _mod->setRfSwitchTable(pins, table); +} + uint8_t Si443x::randomByte() { // set mode to Rx _mod->SPIwriteRegister(RADIOLIB_SI443X_REG_OP_FUNC_CONTROL_1, RADIOLIB_SI443X_RX_ON | RADIOLIB_SI443X_XTAL_ON); diff --git a/src/modules/Si443x/Si443x.h b/src/modules/Si443x/Si443x.h index e3218c68..6945e510 100644 --- a/src/modules/Si443x/Si443x.h +++ b/src/modules/Si443x/Si443x.h @@ -788,6 +788,9 @@ class Si443x: public PhysicalLayer { /*! \copydoc Module::setRfSwitchPins */ void setRfSwitchPins(RADIOLIB_PIN_TYPE rxEn, RADIOLIB_PIN_TYPE txEn); + /*! \copydoc Module::setRfSwitchTable */ + void setRfSwitchTable(const RADIOLIB_PIN_TYPE (&pins)[Module::RFSWITCH_MAX_PINS], const Module::RfSwitchMode_t table[]); + /*! \brief Get one truly random byte from RSSI noise. From 52ec165643fd697ae2b8e0f469e306a13de92a6a Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 6 Dec 2022 13:27:19 +0100 Subject: [PATCH 04/13] Update radios to use new setRfSwitchState This removes the compatibility wrapper and applies the following replacements: sed -i 's/setRfSwitchState(LOW, LOW)/setRfSwitchState(Module::MODE_IDLE)/' src/modules/*/*.cpp sed -i 's/setRfSwitchState(HIGH, LOW)/setRfSwitchState(Module::MODE_RX)/' src/modules/*/*.cpp sed -i 's/setRfSwitchState(LOW, HIGH)/setRfSwitchState(Module::MODE_TX)/' src/modules/*/*.cpp --- src/Module.h | 7 ------- src/modules/CC1101/CC1101.cpp | 10 +++++----- src/modules/RF69/RF69.cpp | 12 ++++++------ src/modules/SX126x/SX126x.cpp | 14 +++++++------- src/modules/SX127x/SX127x.cpp | 16 ++++++++-------- src/modules/SX128x/SX128x.cpp | 14 +++++++------- src/modules/Si443x/Si443x.cpp | 12 ++++++------ 7 files changed, 39 insertions(+), 46 deletions(-) diff --git a/src/Module.h b/src/Module.h index d566b560..9958a30f 100644 --- a/src/Module.h +++ b/src/Module.h @@ -383,13 +383,6 @@ class Module { */ void setRfSwitchState(uint8_t mode); - /*! Temporary compatibility wrapper */ - void setRfSwitchState(RADIOLIB_PIN_STATUS rxPinState, RADIOLIB_PIN_STATUS txPinState) { - if (rxPinState) setRfSwitchState(MODE_RX); - else if (txPinState) setRfSwitchState(MODE_TX); - else setRfSwitchState(MODE_IDLE); - } - /*! \brief Wait for time to elapse, either using the microsecond timer, or the TimerFlag. Note that in interrupt timing mode, it is up to the user to set up the timing interrupt! diff --git a/src/modules/CC1101/CC1101.cpp b/src/modules/CC1101/CC1101.cpp index b3cb622c..8ad06742 100644 --- a/src/modules/CC1101/CC1101.cpp +++ b/src/modules/CC1101/CC1101.cpp @@ -162,7 +162,7 @@ int16_t CC1101::standby() { SPIsendCommand(RADIOLIB_CC1101_CMD_IDLE); // set RF switch (if present) - _mod->setRfSwitchState(LOW, LOW); + _mod->setRfSwitchState(Module::MODE_IDLE); return(RADIOLIB_ERR_NONE); } @@ -176,7 +176,7 @@ int16_t CC1101::transmitDirectAsync(uint32_t frf) { int16_t CC1101::transmitDirect(bool sync, uint32_t frf) { // set RF switch (if present) - _mod->setRfSwitchState(LOW, HIGH); + _mod->setRfSwitchState(Module::MODE_TX); // user requested to start transmitting immediately (required for RTTY) if(frf != 0) { @@ -206,7 +206,7 @@ int16_t CC1101::receiveDirectAsync() { int16_t CC1101::receiveDirect(bool sync) { // set RF switch (if present) - _mod->setRfSwitchState(HIGH, LOW); + _mod->setRfSwitchState(Module::MODE_RX); // activate direct mode int16_t state = directMode(sync); @@ -291,7 +291,7 @@ int16_t CC1101::startTransmit(uint8_t* data, size_t len, uint8_t addr) { dataSent += initialWrite; // set RF switch (if present) - _mod->setRfSwitchState(LOW, HIGH); + _mod->setRfSwitchState(Module::MODE_TX); // set mode to transmit SPIsendCommand(RADIOLIB_CC1101_CMD_TX); @@ -344,7 +344,7 @@ int16_t CC1101::startReceive() { RADIOLIB_ASSERT(state); // set RF switch (if present) - _mod->setRfSwitchState(HIGH, LOW); + _mod->setRfSwitchState(Module::MODE_RX); // set mode to receive SPIsendCommand(RADIOLIB_CC1101_CMD_RX); diff --git a/src/modules/RF69/RF69.cpp b/src/modules/RF69/RF69.cpp index b1ef42e3..ff6306da 100644 --- a/src/modules/RF69/RF69.cpp +++ b/src/modules/RF69/RF69.cpp @@ -159,7 +159,7 @@ int16_t RF69::receive(uint8_t* data, size_t len) { int16_t RF69::sleep() { // set RF switch (if present) - _mod->setRfSwitchState(LOW, LOW); + _mod->setRfSwitchState(Module::MODE_IDLE); // set module to sleep return(setMode(RADIOLIB_RF69_SLEEP)); @@ -167,7 +167,7 @@ int16_t RF69::sleep() { int16_t RF69::standby() { // set RF switch (if present) - _mod->setRfSwitchState(LOW, LOW); + _mod->setRfSwitchState(Module::MODE_IDLE); // set module to standby return(setMode(RADIOLIB_RF69_STANDBY)); @@ -175,7 +175,7 @@ int16_t RF69::standby() { int16_t RF69::transmitDirect(uint32_t frf) { // set RF switch (if present) - _mod->setRfSwitchState(LOW, HIGH); + _mod->setRfSwitchState(Module::MODE_TX); // user requested to start transmitting immediately (required for RTTY) if(frf != 0) { @@ -196,7 +196,7 @@ int16_t RF69::transmitDirect(uint32_t frf) { int16_t RF69::receiveDirect() { // set RF switch (if present) - _mod->setRfSwitchState(HIGH, LOW); + _mod->setRfSwitchState(Module::MODE_RX); // activate direct mode int16_t state = directMode(); @@ -254,7 +254,7 @@ int16_t RF69::startReceive() { clearIRQFlags(); // set RF switch (if present) - _mod->setRfSwitchState(HIGH, LOW); + _mod->setRfSwitchState(Module::MODE_RX); // set mode to receive state = _mod->SPIsetRegValue(RADIOLIB_RF69_REG_OCP, RADIOLIB_RF69_OCP_ON | RADIOLIB_RF69_OCP_TRIM); @@ -414,7 +414,7 @@ int16_t RF69::startTransmit(uint8_t* data, size_t len, uint8_t addr) { } // set RF switch (if present) - _mod->setRfSwitchState(LOW, HIGH); + _mod->setRfSwitchState(Module::MODE_TX); // set mode to transmit state = setMode(RADIOLIB_RF69_TX); diff --git a/src/modules/SX126x/SX126x.cpp b/src/modules/SX126x/SX126x.cpp index 8d9f67aa..6fcf956c 100644 --- a/src/modules/SX126x/SX126x.cpp +++ b/src/modules/SX126x/SX126x.cpp @@ -299,7 +299,7 @@ int16_t SX126x::receive(uint8_t* data, size_t len) { int16_t SX126x::transmitDirect(uint32_t frf) { // set RF switch (if present) - _mod->setRfSwitchState(LOW, HIGH); + _mod->setRfSwitchState(Module::MODE_TX); // user requested to start transmitting immediately (required for RTTY) int16_t state = RADIOLIB_ERR_NONE; @@ -315,7 +315,7 @@ int16_t SX126x::transmitDirect(uint32_t frf) { int16_t SX126x::receiveDirect() { // set RF switch (if present) - _mod->setRfSwitchState(HIGH, LOW); + _mod->setRfSwitchState(Module::MODE_RX); // SX126x is unable to output received data directly return(RADIOLIB_ERR_UNKNOWN); @@ -337,7 +337,7 @@ int16_t SX126x::scanChannel(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin) int16_t SX126x::sleep(bool retainConfig) { // set RF switch (if present) - _mod->setRfSwitchState(LOW, LOW); + _mod->setRfSwitchState(Module::MODE_IDLE); uint8_t sleepMode = RADIOLIB_SX126X_SLEEP_START_WARM | RADIOLIB_SX126X_SLEEP_RTC_OFF; if(!retainConfig) { @@ -357,7 +357,7 @@ int16_t SX126x::standby() { int16_t SX126x::standby(uint8_t mode) { // set RF switch (if present) - _mod->setRfSwitchState(LOW, LOW); + _mod->setRfSwitchState(Module::MODE_IDLE); uint8_t data[] = {mode}; return(SPIwriteCommand(RADIOLIB_SX126X_CMD_SET_STANDBY, data, 1)); @@ -418,7 +418,7 @@ int16_t SX126x::startTransmit(uint8_t* data, size_t len, uint8_t addr) { RADIOLIB_ASSERT(state); // set RF switch (if present) - _mod->setRfSwitchState(LOW, HIGH); + _mod->setRfSwitchState(Module::MODE_TX); // start transmission state = setTx(RADIOLIB_SX126X_TX_TIMEOUT_NONE); @@ -445,7 +445,7 @@ int16_t SX126x::startReceive(uint32_t timeout) { RADIOLIB_ASSERT(state); // set RF switch (if present) - _mod->setRfSwitchState(HIGH, LOW); + _mod->setRfSwitchState(Module::MODE_RX); // set mode to receive state = setRx(timeout); @@ -600,7 +600,7 @@ int16_t SX126x::startChannelScan(uint8_t symbolNum, uint8_t detPeak, uint8_t det RADIOLIB_ASSERT(state); // set RF switch (if present) - _mod->setRfSwitchState(HIGH, LOW); + _mod->setRfSwitchState(Module::MODE_RX); // set DIO pin mapping state = setDioIrqParams(RADIOLIB_SX126X_IRQ_CAD_DETECTED | RADIOLIB_SX126X_IRQ_CAD_DONE, RADIOLIB_SX126X_IRQ_CAD_DETECTED | RADIOLIB_SX126X_IRQ_CAD_DONE); diff --git a/src/modules/SX127x/SX127x.cpp b/src/modules/SX127x/SX127x.cpp index 6d1b77e4..b6e4441a 100644 --- a/src/modules/SX127x/SX127x.cpp +++ b/src/modules/SX127x/SX127x.cpp @@ -275,7 +275,7 @@ int16_t SX127x::scanChannel() { int16_t SX127x::sleep() { // set RF switch (if present) - _mod->setRfSwitchState(LOW, LOW); + _mod->setRfSwitchState(Module::MODE_IDLE); // set mode to sleep return(setMode(RADIOLIB_SX127X_SLEEP)); @@ -283,7 +283,7 @@ int16_t SX127x::sleep() { int16_t SX127x::standby() { // set RF switch (if present) - _mod->setRfSwitchState(LOW, LOW); + _mod->setRfSwitchState(Module::MODE_IDLE); // set mode to standby return(setMode(RADIOLIB_SX127X_STANDBY)); @@ -296,7 +296,7 @@ int16_t SX127x::transmitDirect(uint32_t frf) { } // set RF switch (if present) - _mod->setRfSwitchState(LOW, HIGH); + _mod->setRfSwitchState(Module::MODE_TX); // user requested to start transmitting immediately (required for RTTY) if(frf != 0) { @@ -325,7 +325,7 @@ int16_t SX127x::receiveDirect() { } // set RF switch (if present) - _mod->setRfSwitchState(HIGH, LOW); + _mod->setRfSwitchState(Module::MODE_RX); // activate direct mode int16_t state = directMode(); @@ -405,13 +405,13 @@ int16_t SX127x::startReceive(uint8_t len, uint8_t mode) { // FSK modem does not distinguish between Rx single and continuous if(mode == RADIOLIB_SX127X_RXCONTINUOUS) { // set RF switch (if present) - _mod->setRfSwitchState(HIGH, LOW); + _mod->setRfSwitchState(Module::MODE_RX); return(setMode(RADIOLIB_SX127X_RX)); } } // set RF switch (if present) - _mod->setRfSwitchState(HIGH, LOW); + _mod->setRfSwitchState(Module::MODE_RX); // set mode to receive return(setMode(mode)); @@ -570,7 +570,7 @@ int16_t SX127x::startTransmit(uint8_t* data, size_t len, uint8_t addr) { _mod->SPIwriteRegisterBurst(RADIOLIB_SX127X_REG_FIFO, data, packetLen); // set RF switch (if present) - _mod->setRfSwitchState(LOW, HIGH); + _mod->setRfSwitchState(Module::MODE_TX); // start transmission state |= setMode(RADIOLIB_SX127X_TX); @@ -659,7 +659,7 @@ int16_t SX127x::startChannelScan() { RADIOLIB_ASSERT(state); // set RF switch (if present) - _mod->setRfSwitchState(HIGH, LOW); + _mod->setRfSwitchState(Module::MODE_RX); // set mode to CAD state = setMode(RADIOLIB_SX127X_CAD); diff --git a/src/modules/SX128x/SX128x.cpp b/src/modules/SX128x/SX128x.cpp index 14313759..531ebd0e 100644 --- a/src/modules/SX128x/SX128x.cpp +++ b/src/modules/SX128x/SX128x.cpp @@ -348,7 +348,7 @@ int16_t SX128x::receive(uint8_t* data, size_t len) { int16_t SX128x::transmitDirect(uint32_t frf) { // set RF switch (if present) - _mod->setRfSwitchState(LOW, HIGH); + _mod->setRfSwitchState(Module::MODE_TX); // user requested to start transmitting immediately (required for RTTY) int16_t state = RADIOLIB_ERR_NONE; @@ -363,7 +363,7 @@ int16_t SX128x::transmitDirect(uint32_t frf) { int16_t SX128x::receiveDirect() { // set RF switch (if present) - _mod->setRfSwitchState(HIGH, LOW); + _mod->setRfSwitchState(Module::MODE_RX); // SX128x is unable to output received data directly return(RADIOLIB_ERR_UNKNOWN); @@ -388,7 +388,7 @@ int16_t SX128x::scanChannel() { RADIOLIB_ASSERT(state); // set RF switch (if present) - _mod->setRfSwitchState(HIGH, LOW); + _mod->setRfSwitchState(Module::MODE_RX); // set mode to CAD state = setCad(); @@ -416,7 +416,7 @@ int16_t SX128x::scanChannel() { int16_t SX128x::sleep(bool retainConfig) { // set RF switch (if present) - _mod->setRfSwitchState(LOW, LOW); + _mod->setRfSwitchState(Module::MODE_IDLE); uint8_t sleepConfig = RADIOLIB_SX128X_SLEEP_DATA_BUFFER_RETAIN | RADIOLIB_SX128X_SLEEP_DATA_RAM_RETAIN; if(!retainConfig) { @@ -436,7 +436,7 @@ int16_t SX128x::standby() { int16_t SX128x::standby(uint8_t mode) { // set RF switch (if present) - _mod->setRfSwitchState(LOW, LOW); + _mod->setRfSwitchState(Module::MODE_IDLE); uint8_t data[] = { mode }; return(SPIwriteCommand(RADIOLIB_SX128X_CMD_SET_STANDBY, data, 1)); @@ -500,7 +500,7 @@ int16_t SX128x::startTransmit(uint8_t* data, size_t len, uint8_t addr) { RADIOLIB_ASSERT(state); // set RF switch (if present) - _mod->setRfSwitchState(LOW, HIGH); + _mod->setRfSwitchState(Module::MODE_TX); // start transmission state = setTx(RADIOLIB_SX128X_TX_TIMEOUT_NONE); @@ -553,7 +553,7 @@ int16_t SX128x::startReceive(uint16_t timeout) { } // set RF switch (if present) - _mod->setRfSwitchState(HIGH, LOW); + _mod->setRfSwitchState(Module::MODE_RX); // set mode to receive state = setRx(timeout); diff --git a/src/modules/Si443x/Si443x.cpp b/src/modules/Si443x/Si443x.cpp index 7cfca65b..b8641aee 100644 --- a/src/modules/Si443x/Si443x.cpp +++ b/src/modules/Si443x/Si443x.cpp @@ -119,7 +119,7 @@ int16_t Si443x::receive(uint8_t* data, size_t len) { int16_t Si443x::sleep() { // set RF switch (if present) - _mod->setRfSwitchState(LOW, LOW); + _mod->setRfSwitchState(Module::MODE_IDLE); // disable wakeup timer interrupt int16_t state = _mod->SPIsetRegValue(RADIOLIB_SI443X_REG_INTERRUPT_ENABLE_1, 0x00); @@ -135,14 +135,14 @@ int16_t Si443x::sleep() { int16_t Si443x::standby() { // set RF switch (if present) - _mod->setRfSwitchState(LOW, LOW); + _mod->setRfSwitchState(Module::MODE_IDLE); return(_mod->SPIsetRegValue(RADIOLIB_SI443X_REG_OP_FUNC_CONTROL_1, RADIOLIB_SI443X_XTAL_ON, 7, 0, 10)); } int16_t Si443x::transmitDirect(uint32_t frf) { // set RF switch (if present) - _mod->setRfSwitchState(LOW, HIGH); + _mod->setRfSwitchState(Module::MODE_TX); // user requested to start transmitting immediately (required for RTTY) if(frf != 0) { @@ -184,7 +184,7 @@ int16_t Si443x::transmitDirect(uint32_t frf) { int16_t Si443x::receiveDirect() { // set RF switch (if present) - _mod->setRfSwitchState(HIGH, LOW); + _mod->setRfSwitchState(Module::MODE_RX); // activate direct mode int16_t state = directMode(); @@ -239,7 +239,7 @@ int16_t Si443x::startTransmit(uint8_t* data, size_t len, uint8_t addr) { _mod->SPIwriteRegisterBurst(RADIOLIB_SI443X_REG_FIFO_ACCESS, data, len); // set RF switch (if present) - _mod->setRfSwitchState(LOW, HIGH); + _mod->setRfSwitchState(Module::MODE_TX); // set interrupt mapping _mod->SPIwriteRegister(RADIOLIB_SI443X_REG_INTERRUPT_ENABLE_1, RADIOLIB_SI443X_PACKET_SENT_ENABLED); @@ -272,7 +272,7 @@ int16_t Si443x::startReceive() { clearIRQFlags(); // set RF switch (if present) - _mod->setRfSwitchState(HIGH, LOW); + _mod->setRfSwitchState(Module::MODE_RX); // set interrupt mapping _mod->SPIwriteRegister(RADIOLIB_SI443X_REG_INTERRUPT_ENABLE_1, RADIOLIB_SI443X_VALID_PACKET_RECEIVED_ENABLED | RADIOLIB_SI443X_CRC_ERROR_ENABLED); From da6c3f6a6ba190bdaaae0d7101952393c0940f2a Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 6 Dec 2022 21:21:09 +0100 Subject: [PATCH 05/13] [SX126x] Allow subclasses to change the TX mode used This prepares for adding a STM32WLx subclass later. --- src/modules/SX126x/SX126x.cpp | 4 ++-- src/modules/SX126x/SX126x.h | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/modules/SX126x/SX126x.cpp b/src/modules/SX126x/SX126x.cpp index 6fcf956c..73e8a4df 100644 --- a/src/modules/SX126x/SX126x.cpp +++ b/src/modules/SX126x/SX126x.cpp @@ -299,7 +299,7 @@ int16_t SX126x::receive(uint8_t* data, size_t len) { int16_t SX126x::transmitDirect(uint32_t frf) { // set RF switch (if present) - _mod->setRfSwitchState(Module::MODE_TX); + _mod->setRfSwitchState(_tx_mode); // user requested to start transmitting immediately (required for RTTY) int16_t state = RADIOLIB_ERR_NONE; @@ -418,7 +418,7 @@ int16_t SX126x::startTransmit(uint8_t* data, size_t len, uint8_t addr) { RADIOLIB_ASSERT(state); // set RF switch (if present) - _mod->setRfSwitchState(Module::MODE_TX); + _mod->setRfSwitchState(_tx_mode); // start transmission state = setTx(RADIOLIB_SX126X_TX_TIMEOUT_NONE); diff --git a/src/modules/SX126x/SX126x.h b/src/modules/SX126x/SX126x.h index 47a9cb37..ae479859 100644 --- a/src/modules/SX126x/SX126x.h +++ b/src/modules/SX126x/SX126x.h @@ -1043,6 +1043,9 @@ class SX126x: public PhysicalLayer { int16_t _lastError = RADIOLIB_ERR_NONE; + // Allow subclasses to define different TX modes + uint8_t _tx_mode = Module::MODE_TX; + int16_t config(uint8_t modem); int16_t checkCommandResult(); }; From 5e47d944188642ceba765a733db4dead681bf0a9 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 3 Nov 2022 10:45:07 +0100 Subject: [PATCH 06/13] [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. --- src/BuildOpt.h | 1 + src/Module.h | 7 +- src/RadioLib.h | 1 + src/modules/SX126x/STM32WLx.cpp | 76 +++++++++++++++ src/modules/SX126x/STM32WLx.h | 122 +++++++++++++++++++++++++ src/modules/SX126x/STM32WLx_Module.cpp | 99 ++++++++++++++++++++ src/modules/SX126x/STM32WLx_Module.h | 49 ++++++++++ 7 files changed, 354 insertions(+), 1 deletion(-) create mode 100644 src/modules/SX126x/STM32WLx.cpp create mode 100644 src/modules/SX126x/STM32WLx.h create mode 100644 src/modules/SX126x/STM32WLx_Module.cpp create mode 100644 src/modules/SX126x/STM32WLx_Module.h diff --git a/src/BuildOpt.h b/src/BuildOpt.h index e28e7a5c..2e2e8835 100644 --- a/src/BuildOpt.h +++ b/src/BuildOpt.h @@ -96,6 +96,7 @@ //#define RADIOLIB_EXCLUDE_SX127X //#define RADIOLIB_EXCLUDE_RFM9X // dependent on RADIOLIB_EXCLUDE_SX127X //#define RADIOLIB_EXCLUDE_SX126X + //#define RADIOLIB_EXCLUDE_STM32WLX // dependent on RADIOLIB_EXCLUDE_SX126X //#define RADIOLIB_EXCLUDE_SX128X //#define RADIOLIB_EXCLUDE_AFSK //#define RADIOLIB_EXCLUDE_AX25 diff --git a/src/Module.h b/src/Module.h index 9958a30f..78793d0a 100644 --- a/src/Module.h +++ b/src/Module.h @@ -44,10 +44,15 @@ class Module { * See setRfSwitchTable() for details. */ 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, + /*! Idle mode */ MODE_IDLE, + /*! Receive mode */ MODE_RX, + /*! Transmission mode */ MODE_TX, }; diff --git a/src/RadioLib.h b/src/RadioLib.h index be496dd6..419e0bea 100644 --- a/src/RadioLib.h +++ b/src/RadioLib.h @@ -78,6 +78,7 @@ #include "modules/SX126x/SX1261.h" #include "modules/SX126x/SX1262.h" #include "modules/SX126x/SX1268.h" +#include "modules/SX126x/STM32WLx.h" #include "modules/SX127x/SX1272.h" #include "modules/SX127x/SX1273.h" #include "modules/SX127x/SX1276.h" diff --git a/src/modules/SX126x/STM32WLx.cpp b/src/modules/SX126x/STM32WLx.cpp new file mode 100644 index 00000000..a1ec8a58 --- /dev/null +++ b/src/modules/SX126x/STM32WLx.cpp @@ -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) diff --git a/src/modules/SX126x/STM32WLx.h b/src/modules/SX126x/STM32WLx.h new file mode 100644 index 00000000..d90f1450 --- /dev/null +++ b/src/modules/SX126x/STM32WLx.h @@ -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 diff --git a/src/modules/SX126x/STM32WLx_Module.cpp b/src/modules/SX126x/STM32WLx_Module.cpp new file mode 100644 index 00000000..1fa4725a --- /dev/null +++ b/src/modules/SX126x/STM32WLx_Module.cpp @@ -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 + +// 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) diff --git a/src/modules/SX126x/STM32WLx_Module.h b/src/modules/SX126x/STM32WLx_Module.h new file mode 100644 index 00000000..05f2b2fe --- /dev/null +++ b/src/modules/SX126x/STM32WLx_Module.h @@ -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 From 9252cf53d3e154756aec94b9924eda0b4346ce3c Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 7 Dec 2022 09:17:44 +0100 Subject: [PATCH 07/13] [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(); From cd138dd6c7b099124ff7b82686c2e17f80be34bc Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 7 Dec 2022 09:50:31 +0100 Subject: [PATCH 08/13] [STM32WLx] Apply PA clamp workaround for HP only This modifies SX1262::begin and beginFSK to call setOutputPower after applying the workaround, so that can undo the workaround if needed. --- src/modules/SX126x/STM32WLx.cpp | 4 ++++ src/modules/SX126x/SX1262.cpp | 8 ++++---- src/modules/SX126x/SX126x.cpp | 10 +++++++--- src/modules/SX126x/SX126x.h | 2 +- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/modules/SX126x/STM32WLx.cpp b/src/modules/SX126x/STM32WLx.cpp index bad38dc9..744f88c3 100644 --- a/src/modules/SX126x/STM32WLx.cpp +++ b/src/modules/SX126x/STM32WLx.cpp @@ -64,6 +64,10 @@ int16_t STM32WLx::setOutputPower(int8_t power) { } 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); diff --git a/src/modules/SX126x/SX1262.cpp b/src/modules/SX126x/SX1262.cpp index 1a2a60d3..a667525e 100644 --- a/src/modules/SX126x/SX1262.cpp +++ b/src/modules/SX126x/SX1262.cpp @@ -20,10 +20,10 @@ int16_t SX1262::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t sync state = setFrequency(freq); RADIOLIB_ASSERT(state); - state = setOutputPower(power); + state = SX126x::fixPaClamping(); RADIOLIB_ASSERT(state); - state = SX126x::fixPaClamping(); + state = setOutputPower(power); RADIOLIB_ASSERT(state); return(state); @@ -38,10 +38,10 @@ int16_t SX1262::beginFSK(float freq, float br, float freqDev, float rxBw, int8_t state = setFrequency(freq); RADIOLIB_ASSERT(state); - state = setOutputPower(power); + state = SX126x::fixPaClamping(); RADIOLIB_ASSERT(state); - state = SX126x::fixPaClamping(); + state = setOutputPower(power); RADIOLIB_ASSERT(state); return(state); diff --git a/src/modules/SX126x/SX126x.cpp b/src/modules/SX126x/SX126x.cpp index 73e8a4df..2e95d778 100644 --- a/src/modules/SX126x/SX126x.cpp +++ b/src/modules/SX126x/SX126x.cpp @@ -1575,7 +1575,7 @@ int16_t SX126x::fixSensitivity() { return(writeRegister(RADIOLIB_SX126X_REG_SENSITIVITY_CONFIG, &sensitivityConfig, 1)); } -int16_t SX126x::fixPaClamping() { +int16_t SX126x::fixPaClamping(bool enable) { // fixes overly eager PA clamping // see SX1262/SX1268 datasheet, chapter 15 Known Limitations, section 15.2 for details @@ -1584,8 +1584,12 @@ int16_t SX126x::fixPaClamping() { int16_t state = readRegister(RADIOLIB_SX126X_REG_TX_CLAMP_CONFIG, &clampConfig, 1); RADIOLIB_ASSERT(state); - // update with the new value - clampConfig |= 0x1E; + // apply or undo workaround + if (enable) + clampConfig |= 0x1E; + else + clampConfig = (clampConfig & ~0x1E) | 0x08; + return(writeRegister(RADIOLIB_SX126X_REG_TX_CLAMP_CONFIG, &clampConfig, 1)); } diff --git a/src/modules/SX126x/SX126x.h b/src/modules/SX126x/SX126x.h index e1b603ba..97cb77e1 100644 --- a/src/modules/SX126x/SX126x.h +++ b/src/modules/SX126x/SX126x.h @@ -1005,7 +1005,7 @@ class SX126x: public PhysicalLayer { // fixes to errata int16_t fixSensitivity(); - int16_t fixPaClamping(); + int16_t fixPaClamping(bool enable = true); int16_t fixImplicitTimeout(); int16_t fixInvertedIQ(uint8_t iqConfig); From 3e2810cfbb07a559ae4fda9c57ff6b317a6dfdba Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 7 Dec 2022 10:52:17 +0100 Subject: [PATCH 09/13] [STM32WLx] Make reading IRQ flag more reliable Now that clearIrqStatus also clears the pending flag, reading the IRQ flag no longer has to, making this more reliable. --- src/modules/SX126x/STM32WLx_Module.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/modules/SX126x/STM32WLx_Module.cpp b/src/modules/SX126x/STM32WLx_Module.cpp index 1fa4725a..c824200a 100644 --- a/src/modules/SX126x/STM32WLx_Module.cpp +++ b/src/modules/SX126x/STM32WLx_Module.cpp @@ -79,10 +79,19 @@ int STM32WLx_Module::virtualDigitalRead(uint32_t ulPin) { 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(); + // We cannot use the radio IRQ output directly, but since: + // - the pending flag will be set whenever the IRQ output is set, + // and + // - the pending flag will be cleared (by + // STM32WLx::clearIrqStatus()) whenever the radio IRQ output is + // cleared, + // the pending flag should always reflect the current radio IRQ + // output. There is one exception: when the ISR starts the pending + // flag is cleared by hardware and not set again until after the + // ISR finishes, so the value is incorrect *inside* the ISR, but + // running RadioLib code inside the ISR (especially code that + // polls the IRQ flag) is not supported and probably broken in + // other ways too. return(SubGhz.isInterruptPending() ? HIGH : LOW); case RADIOLIB_STM32WLx_VIRTUAL_PIN_NSS: From e52ffb0a69264dc5e3a661cb7209b9991f500f0e Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 20 Dec 2022 16:21:48 +0100 Subject: [PATCH 10/13] [STM32WLx] Only build on compatible STM32 boards This checks for some system macros to be set, but also includes the doxygen build to generate documentation. This reuses the RADIOLIB_EXCLUDE_STM32WLX that the code already checks for to prevent having to duplicate this macro check in four places. To detect running inside doxygen, this configures doxygen to predefine a DOXYGEN macro (it seems no such macros are defined by default by doxygen). --- Doxyfile | 3 ++- src/BuildOpt.h | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Doxyfile b/Doxyfile index 3c1d2d18..db6d223b 100644 --- a/Doxyfile +++ b/Doxyfile @@ -2172,7 +2172,8 @@ INCLUDE_FILE_PATTERNS = # recursively expanded use the := operator instead of the = operator. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -PREDEFINED = protected=private +PREDEFINED = protected=private \ + DOXYGEN # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The diff --git a/src/BuildOpt.h b/src/BuildOpt.h index 2e2e8835..5e6ca965 100644 --- a/src/BuildOpt.h +++ b/src/BuildOpt.h @@ -1010,6 +1010,13 @@ #define RADIOLIB_STATIC_ARRAY_SIZE (256) #endif + +// This only compiles on STM32 boards with SUBGHZ module, but also +// include when generating docs +#if (!defined(ARDUINO_ARCH_STM32) || !defined(SUBGHZSPI_BASE)) && !defined(DOXYGEN) + #define RADIOLIB_EXCLUDE_STM32WLX +#endif + #if defined(RADIOLIB_DEBUG) #if defined(RADIOLIB_BUILD_ARDUINO) #define RADIOLIB_DEBUG_PRINT(...) { RADIOLIB_DEBUG_PORT.print(__VA_ARGS__); } From e116a20d89ae7f76eba72f4419cde34f8129ff0c Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 20 Dec 2022 21:31:43 +0100 Subject: [PATCH 11/13] [STM32WLx] Add examples Co-Authored-By: jgromes --- .../STM32WLx_Channel_Activity_Detection.ino | 61 +++++++ ...x_Channel_Activity_Detection_Interrupt.ino | 98 +++++++++++ .../STM32WLx_Receive/STM32WLx_Receive.ino | 124 ++++++++++++++ .../STM32WLx_Receive_Interrupt.ino | 158 ++++++++++++++++++ .../STM32WLx_Transmit/STM32WLx_Transmit.ino | 112 +++++++++++++ .../STM32WLx_Transmit_Interrupt.ino | 140 ++++++++++++++++ 6 files changed, 693 insertions(+) create mode 100644 examples/STM32WLx/STM32WLx_Channel_Activity_Detection/STM32WLx_Channel_Activity_Detection.ino create mode 100644 examples/STM32WLx/STM32WLx_Channel_Activity_Detection_Interrupt/STM32WLx_Channel_Activity_Detection_Interrupt.ino create mode 100644 examples/STM32WLx/STM32WLx_Receive/STM32WLx_Receive.ino create mode 100644 examples/STM32WLx/STM32WLx_Receive_Interrupt/STM32WLx_Receive_Interrupt.ino create mode 100644 examples/STM32WLx/STM32WLx_Transmit/STM32WLx_Transmit.ino create mode 100644 examples/STM32WLx/STM32WLx_Transmit_Interrupt/STM32WLx_Transmit_Interrupt.ino diff --git a/examples/STM32WLx/STM32WLx_Channel_Activity_Detection/STM32WLx_Channel_Activity_Detection.ino b/examples/STM32WLx/STM32WLx_Channel_Activity_Detection/STM32WLx_Channel_Activity_Detection.ino new file mode 100644 index 00000000..ef511dc8 --- /dev/null +++ b/examples/STM32WLx/STM32WLx_Channel_Activity_Detection/STM32WLx_Channel_Activity_Detection.ino @@ -0,0 +1,61 @@ +/* + RadioLib STM32WLx Channel Activity Detection Example + + This example uses STM32WLx to scan the current LoRa + channel and detect ongoing LoRa transmissions. + Unlike SX127x CAD, SX126x/STM32WLx can detect any part + of LoRa transmission, not just the preamble. + + For default module settings, see the wiki page + https://github.com/jgromes/RadioLib/wiki/Default-configuration#sx126x---lora-modem + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// no need to configure pins, signals are routed to the radio internally +STM32WLx radio = new STM32WLx_Module(); + + +void setup() { + Serial.begin(9600); + + // initialize STM32WLx with default settings + Serial.print(F("[STM32WLx] Initializing ... ")); + int state = radio.begin(868.0); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } +} + +void loop() { + Serial.print(F("[STM32WLx] Scanning channel for LoRa transmission ... ")); + + // start scanning current channel + int state = radio.scanChannel(); + + if (state == RADIOLIB_LORA_DETECTED) { + // LoRa preamble was detected + Serial.println(F("detected!")); + + } else if (state == RADIOLIB_CHANNEL_FREE) { + // no preamble was detected, channel is free + Serial.println(F("channel is free!")); + + } else { + // some other error occurred + Serial.print(F("failed, code ")); + Serial.println(state); + + } + + // wait 100 ms before new scan + delay(100); +} diff --git a/examples/STM32WLx/STM32WLx_Channel_Activity_Detection_Interrupt/STM32WLx_Channel_Activity_Detection_Interrupt.ino b/examples/STM32WLx/STM32WLx_Channel_Activity_Detection_Interrupt/STM32WLx_Channel_Activity_Detection_Interrupt.ino new file mode 100644 index 00000000..374a41f1 --- /dev/null +++ b/examples/STM32WLx/STM32WLx_Channel_Activity_Detection_Interrupt/STM32WLx_Channel_Activity_Detection_Interrupt.ino @@ -0,0 +1,98 @@ +/* + RadioLib STM32WLx Channel Activity Detection Example + + This example uses STM32WLx to scan the current LoRa + channel and detect ongoing LoRa transmissions. + Unlike SX127x CAD, SX126x/STM32WLx can detect any part + of LoRa transmission, not just the preamble. + + For default module settings, see the wiki page + https://github.com/jgromes/RadioLib/wiki/Default-configuration#sx126x---lora-modem + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// no need to configure pins, signals are routed to the radio internally +STM32WLx radio = new STM32WLx_Module(); + + +void setup() { + Serial.begin(9600); + + // initialize STM32WLx with default settings + Serial.print(F("[STM32WLx] Initializing ... ")); + int state = radio.begin(868.0); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } + + // set the function that will be called + // when LoRa packet or timeout is detected + radio.setDio1Action(setFlag); + + // start scanning the channel + Serial.print(F("[STM32WLx] Starting scan for LoRa preamble ... ")); + state = radio.startChannelScan(); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + } +} + +// flag to indicate that a packet was detected or CAD timed out +volatile bool scanFlag = false; + +// this function is called when a complete packet +// is received by the module +// IMPORTANT: this function MUST be 'void' type +// and MUST NOT have any arguments! +void setFlag(void) { + // something happened, set the flag + scanFlag = true; +} + +void loop() { + // check if the flag is set + if(scanFlag) { + // reset flag + scanFlag = false; + + // check CAD result + int state = radio.getChannelScanResult(); + + if (state == RADIOLIB_LORA_DETECTED) { + // LoRa packet was detected + Serial.println(F("[STM32WLx] Packet detected!")); + + } else if (state == RADIOLIB_CHANNEL_FREE) { + // channel is free + Serial.println(F("[STM32WLx] Channel is free!")); + + } else { + // some other error occurred + Serial.print(F("[STM32WLx] Failed, code ")); + Serial.println(state); + + } + + // start scanning the channel again + Serial.print(F("[STM32WLx] Starting scan for LoRa preamble ... ")); + state = radio.startChannelScan(); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + } + } +} diff --git a/examples/STM32WLx/STM32WLx_Receive/STM32WLx_Receive.ino b/examples/STM32WLx/STM32WLx_Receive/STM32WLx_Receive.ino new file mode 100644 index 00000000..b645affe --- /dev/null +++ b/examples/STM32WLx/STM32WLx_Receive/STM32WLx_Receive.ino @@ -0,0 +1,124 @@ +/* + RadioLib STM32WLx Receive Example + + This example listens for LoRa transmissions using STM32WL MCU with + integrated (SX126x) LoRa radio. + + To successfully receive data, the following settings have to be the same + on both transmitter and receiver: + - carrier frequency + - bandwidth + - spreading factor + - coding rate + - sync word + - preamble length + + This example assumes Nucleo WL55JC1 is used. For other Nucleo boards + or standalone STM32WL, some configuration such as TCXO voltage and + RF switch control may have to be adjusted. + + For default module settings, see the wiki page + https://github.com/jgromes/RadioLib/wiki/Default-configuration#sx126x---lora-modem + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// no need to configure pins, signals are routed to the radio internally +STM32WLx radio = new STM32WLx_Module(); + +// set RF switch configuration for Nucleo WL55JC1 +// NOTE: other boards may be different! +static const RADIOLIB_PIN_TYPE rfswitch_pins[] = + {PC3, PC4, PC5}; +static const Module::RfSwitchMode_t rfswitch_table[] = { + {STM32WLx::MODE_IDLE, {LOW, LOW, LOW}}, + {STM32WLx::MODE_RX, {HIGH, HIGH, LOW}}, + {STM32WLx::MODE_TX_LP, {HIGH, HIGH, HIGH}}, + {STM32WLx::MODE_TX_HP, {HIGH, LOW, HIGH}}, + STM32WLx::END_OF_MODE_TABLE, +}; + +void setup() { + Serial.begin(9600); + + // set RF switch control configuration + // this has to be done prior to calling begin() + radio.setRfSwitchTable(rfswitch_pins, rfswitch_table); + + // initialize STM32WL with default settings, except frequency + Serial.print(F("[STM32WL] Initializing ... ")); + int state = radio.begin(868.0); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } + + // set appropriate TCXO voltage for Nucleo WL55JC1 + state = radio.setTCXO(1.7); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } +} + +void loop() { + Serial.print(F("[STM32WL] Waiting for incoming transmission ... ")); + + // you can receive data as an Arduino String + // NOTE: receive() is a blocking method! + // See example ReceiveInterrupt for details + // on non-blocking reception method. + String str; + int state = radio.receive(str); + + // you can also receive data as byte array + /* + byte byteArr[8]; + int state = radio.receive(byteArr, 8); + */ + + if (state == RADIOLIB_ERR_NONE) { + // packet was successfully received + Serial.println(F("success!")); + + // print the data of the packet + Serial.print(F("[STM32WL] Data:\t\t")); + Serial.println(str); + + // print the RSSI (Received Signal Strength Indicator) + // of the last received packet + Serial.print(F("[STM32WL] RSSI:\t\t")); + Serial.print(radio.getRSSI()); + Serial.println(F(" dBm")); + + // print the SNR (Signal-to-Noise Ratio) + // of the last received packet + Serial.print(F("[STM32WL] SNR:\t\t")); + Serial.print(radio.getSNR()); + Serial.println(F(" dB")); + + } else if (state == RADIOLIB_ERR_RX_TIMEOUT) { + // timeout occurred while waiting for a packet + Serial.println(F("timeout!")); + + } else if (state == RADIOLIB_ERR_CRC_MISMATCH) { + // packet was received, but is malformed + Serial.println(F("CRC error!")); + + } else { + // some other error occurred + Serial.print(F("failed, code ")); + Serial.println(state); + + } +} diff --git a/examples/STM32WLx/STM32WLx_Receive_Interrupt/STM32WLx_Receive_Interrupt.ino b/examples/STM32WLx/STM32WLx_Receive_Interrupt/STM32WLx_Receive_Interrupt.ino new file mode 100644 index 00000000..57563001 --- /dev/null +++ b/examples/STM32WLx/STM32WLx_Receive_Interrupt/STM32WLx_Receive_Interrupt.ino @@ -0,0 +1,158 @@ +/* + RadioLib STM32WLx Receive with Interrupts Example + + This example listens for LoRa transmissions and tries to + receive them. Once a packet is received, an interrupt is + triggered. To successfully receive data, the following + settings have to be the same on both transmitter + and receiver: + - carrier frequency + - bandwidth + - spreading factor + - coding rate + - sync word + + This example assumes Nucleo WL55JC1 is used. For other Nucleo boards + or standalone STM32WL, some configuration such as TCXO voltage and + RF switch control may have to be adjusted. + + For default module settings, see the wiki page + https://github.com/jgromes/RadioLib/wiki/Default-configuration#sx126x---lora-modem + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// no need to configure pins, signals are routed to the radio internally +STM32WLx radio = new STM32WLx_Module(); + +// set RF switch configuration for Nucleo WL55JC1 +// NOTE: other boards may be different! +static const RADIOLIB_PIN_TYPE rfswitch_pins[] = + {PC3, PC4, PC5}; +static const Module::RfSwitchMode_t rfswitch_table[] = { + {STM32WLx::MODE_IDLE, {LOW, LOW, LOW}}, + {STM32WLx::MODE_RX, {HIGH, HIGH, LOW}}, + {STM32WLx::MODE_TX_LP, {HIGH, HIGH, HIGH}}, + {STM32WLx::MODE_TX_HP, {HIGH, LOW, HIGH}}, + STM32WLx::END_OF_MODE_TABLE, +}; + +void setup() { + Serial.begin(9600); + + // set RF switch control configuration + // this has to be done prior to calling begin() + radio.setRfSwitchTable(rfswitch_pins, rfswitch_table); + + // initialize STM32WL with default settings, except frequency + Serial.print(F("[STM32WL] Initializing ... ")); + int state = radio.begin(868.0); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } + + // set appropriate TCXO voltage for Nucleo WL55JC1 + state = radio.setTCXO(1.7); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } + + // set the function that will be called + // when new packet is received + radio.setDio1Action(setFlag); + + // start listening for LoRa packets + Serial.print(F("[STM32WL] Starting to listen ... ")); + state = radio.startReceive(); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } + + // if needed, 'listen' mode can be disabled by calling + // any of the following methods: + // + // radio.standby() + // radio.sleep() + // radio.transmit(); + // radio.receive(); + // radio.readData(); + // radio.scanChannel(); +} + +// flag to indicate that a packet was received +volatile bool receivedFlag = false; + +// this function is called when a complete packet +// is received by the module +// IMPORTANT: this function MUST be 'void' type +// and MUST NOT have any arguments! +void setFlag(void) { + // we got a packet, set the flag + receivedFlag = true; +} + +void loop() { + // check if the flag is set + if(receivedFlag) { + // reset flag + receivedFlag = false; + + // you can read received data as an Arduino String + String str; + int state = radio.readData(str); + + // you can also read received data as byte array + /* + byte byteArr[8]; + int state = radio.readData(byteArr, 8); + */ + + if (state == RADIOLIB_ERR_NONE) { + // packet was successfully received + Serial.println(F("[STM32WL] Received packet!")); + + // print data of the packet + Serial.print(F("[STM32WL] Data:\t\t")); + Serial.println(str); + + // print RSSI (Received Signal Strength Indicator) + Serial.print(F("[STM32WL] RSSI:\t\t")); + Serial.print(radio.getRSSI()); + Serial.println(F(" dBm")); + + // print SNR (Signal-to-Noise Ratio) + Serial.print(F("[STM32WL] SNR:\t\t")); + Serial.print(radio.getSNR()); + Serial.println(F(" dB")); + + } else if (state == RADIOLIB_ERR_CRC_MISMATCH) { + // packet was received, but is malformed + Serial.println(F("CRC error!")); + + } else { + // some other error occurred + Serial.print(F("failed, code ")); + Serial.println(state); + + } + + // put module back to listen mode + radio.startReceive(); + } +} diff --git a/examples/STM32WLx/STM32WLx_Transmit/STM32WLx_Transmit.ino b/examples/STM32WLx/STM32WLx_Transmit/STM32WLx_Transmit.ino new file mode 100644 index 00000000..fb6dab58 --- /dev/null +++ b/examples/STM32WLx/STM32WLx_Transmit/STM32WLx_Transmit.ino @@ -0,0 +1,112 @@ +/* + RadioLib STM32WLx Transmit Example + + This example transmits packets using STM32WL MCU with integrated + (SX126x) LoRa radio. + + Each packet contains up to 256 bytes of data, in the form of: + - Arduino String + - null-terminated char array (C-string) + - arbitrary binary data (byte array) + + This example assumes Nucleo WL55JC1 is used. For other Nucleo boards + or standalone STM32WL, some configuration such as TCXO voltage and + RF switch control may have to be adjusted. + + For default module settings, see the wiki page + https://github.com/jgromes/RadioLib/wiki/Default-configuration#sx126x---lora-modem + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// no need to configure pins, signals are routed to the radio internally +STM32WLx radio = new STM32WLx_Module(); + +// set RF switch configuration for Nucleo WL55JC1 +// NOTE: other boards may be different! +static const RADIOLIB_PIN_TYPE rfswitch_pins[] = + {PC3, PC4, PC5}; +static const Module::RfSwitchMode_t rfswitch_table[] = { + {STM32WLx::MODE_IDLE, {LOW, LOW, LOW}}, + {STM32WLx::MODE_RX, {HIGH, HIGH, LOW}}, + {STM32WLx::MODE_TX_LP, {HIGH, HIGH, HIGH}}, + {STM32WLx::MODE_TX_HP, {HIGH, LOW, HIGH}}, + STM32WLx::END_OF_MODE_TABLE, +}; + +void setup() { + Serial.begin(9600); + + // set RF switch control configuration + // this has to be done prior to calling begin() + radio.setRfSwitchTable(rfswitch_pins, rfswitch_table); + + // initialize STM32WL with default settings, except frequency + Serial.print(F("[STM32WL] Initializing ... ")); + int state = radio.begin(868.0); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } + + // set appropriate TCXO voltage for Nucleo WL55JC1 + state = radio.setTCXO(1.7); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } +} + +void loop() { + Serial.print(F("[STM32WL] Transmitting packet ... ")); + + // you can transmit C-string or Arduino string up to + // 256 characters long + // NOTE: transmit() is a blocking method! + // See example STM32WLx_Transmit_Interrupt for details + // on non-blocking transmission method. + int state = radio.transmit("Hello World!"); + + // you can also transmit byte array up to 256 bytes long + /* + byte byteArr[] = {0x01, 0x23, 0x45, 0x56, 0x78, 0xAB, 0xCD, 0xEF}; + int state = radio.transmit(byteArr, 8); + */ + + if (state == RADIOLIB_ERR_NONE) { + // the packet was successfully transmitted + Serial.println(F("success!")); + + // print measured data rate + Serial.print(F("[STM32WL] Datarate:\t")); + Serial.print(radio.getDataRate()); + Serial.println(F(" bps")); + + } else if (state == RADIOLIB_ERR_PACKET_TOO_LONG) { + // the supplied packet was longer than 256 bytes + Serial.println(F("too long!")); + + } else if (state == RADIOLIB_ERR_TX_TIMEOUT) { + // timeout occured while transmitting packet + Serial.println(F("timeout!")); + + } else { + // some other error occurred + Serial.print(F("failed, code ")); + Serial.println(state); + + } + + // wait for a second before transmitting again + delay(1000); +} diff --git a/examples/STM32WLx/STM32WLx_Transmit_Interrupt/STM32WLx_Transmit_Interrupt.ino b/examples/STM32WLx/STM32WLx_Transmit_Interrupt/STM32WLx_Transmit_Interrupt.ino new file mode 100644 index 00000000..ea17838a --- /dev/null +++ b/examples/STM32WLx/STM32WLx_Transmit_Interrupt/STM32WLx_Transmit_Interrupt.ino @@ -0,0 +1,140 @@ +/* + RadioLib STM32WLx Transmit with Interrupts Example + + This example transmits LoRa packets with one second delays + between them. Each packet contains up to 256 bytes + of data, in the form of: + - Arduino String + - null-terminated char array (C-string) + - arbitrary binary data (byte array) + + For default module settings, see the wiki page + https://github.com/jgromes/RadioLib/wiki/Default-configuration#sx126x---lora-modem + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// no need to configure pins, signals are routed to the radio internally +STM32WLx radio = new STM32WLx_Module(); + +// set RF switch configuration for Nucleo WL55JC1 +// NOTE: other boards may be different! +static const RADIOLIB_PIN_TYPE rfswitch_pins[] = + {PC3, PC4, PC5}; +static const Module::RfSwitchMode_t rfswitch_table[] = { + {STM32WLx::MODE_IDLE, {LOW, LOW, LOW}}, + {STM32WLx::MODE_RX, {HIGH, HIGH, LOW}}, + {STM32WLx::MODE_TX_LP, {HIGH, HIGH, HIGH}}, + {STM32WLx::MODE_TX_HP, {HIGH, LOW, HIGH}}, + STM32WLx::END_OF_MODE_TABLE, +}; + +// save transmission state between loops +int transmissionState = RADIOLIB_ERR_NONE; + +void setup() { + Serial.begin(9600); + + // set RF switch control configuration + // this has to be done prior to calling begin() + radio.setRfSwitchTable(rfswitch_pins, rfswitch_table); + + // initialize STM32WL with default settings, except frequency + Serial.print(F("[STM32WL] Initializing ... ")); + int state = radio.begin(868.0); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } + + // set appropriate TCXO voltage for Nucleo WL55JC1 + state = radio.setTCXO(1.7); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } + + // set the function that will be called + // when packet transmission is finished + radio.setDio1Action(setFlag); + + // start transmitting the first packet + Serial.print(F("[STM32WL] Sending first packet ... ")); + + // you can transmit C-string or Arduino string up to + // 256 characters long + transmissionState = radio.startTransmit("Hello World!"); + + // you can also transmit byte array up to 256 bytes long + /* + byte byteArr[] = {0x01, 0x23, 0x45, 0x67, + 0x89, 0xAB, 0xCD, 0xEF}; + state = radio.startTransmit(byteArr, 8); + */ +} + +// flag to indicate that a packet was sent +volatile bool transmittedFlag = false; + +// this function is called when a complete packet +// is transmitted by the module +// IMPORTANT: this function MUST be 'void' type +// and MUST NOT have any arguments! +void setFlag(void) { + // we sent a packet, set the flag + transmittedFlag = true; +} + +void loop() { + // check if the previous transmission finished + if(transmittedFlag) { + // reset flag + transmittedFlag = false; + + if (transmissionState == RADIOLIB_ERR_NONE) { + // packet was successfully sent + Serial.println(F("transmission finished!")); + + // NOTE: when using interrupt-driven transmit method, + // it is not possible to automatically measure + // transmission data rate using getDataRate() + + } else { + Serial.print(F("failed, code ")); + Serial.println(transmissionState); + + } + + // clean up after transmission is finished + // this will ensure transmitter is disabled, + // RF switch is powered down etc. + radio.finishTransmit(); + + // wait a second before transmitting again + delay(1000); + + // send another one + Serial.print(F("[STM32WL] Sending another packet ... ")); + + // you can transmit C-string or Arduino string up to + // 256 characters long + transmissionState = radio.startTransmit("Hello World!"); + + // you can also transmit byte array up to 256 bytes long + /* + byte byteArr[] = {0x01, 0x23, 0x45, 0x67, + 0x89, 0xAB, 0xCD, 0xEF}; + int state = radio.startTransmit(byteArr, 8); + */ + } +} From 6531ce7400cf2994ccc3cfdd9319c72014a4cf90 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Wed, 21 Dec 2022 18:16:07 +0100 Subject: [PATCH 12/13] [CI]: Set up CI for STM32WLx (CI_BUILD_ALL) This adds a build for the Nucleo WL55JC1 board, and ensures that the STM32WLx examples are skipped on all other boards. To slightly generalize the list of boards, this pushes the pnum option for the black pill into the boards list, instead of setting it into the options variable (even though it is technically a board option and not the board itself, conceptually it is part of the selected board and these values are just concatenated, so it can go in either place). Co-Authored-By: jgromes --- .github/workflows/main.yml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 688fe19c..05a897f4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -27,7 +27,8 @@ jobs: - esp8266:esp8266:generic - Intel:arc32:arduino_101 - SparkFun:apollo3:sfe_artemis - - STMicroelectronics:stm32:GenF3 + - STMicroelectronics:stm32:GenF3:pnum=BLACKPILL_F303CC + - STMicroelectronics:stm32:Nucleo_64:pnum=NUCLEO_WL55JC1 - stm32duino:STM32F1:mapleMini - MegaCoreX:megaavr:4809 - arduino:mbed_rp2040:pico @@ -63,9 +64,9 @@ jobs: id: prep run: | - # common settings - no extra options, skip nothing, all warnings + # common settings - no extra options, skip STM32WL examples, all warnings echo "::set-output name=options::" - echo "::set-output name=skip-pattern::''" + echo "::set-output name=skip-pattern::'STM32WL'" echo "::set-output name=warnings::'all'" # platform-dependent settings - extra board options, board index URLs, skip patterns etc. @@ -109,9 +110,13 @@ jobs: elif [[ "${{ contains(matrix.board, 'STMicroelectronics:stm32') }}" == "true" ]]; then # STM32 (official core) - echo "::set-output name=options:::pnum=BLACKPILL_F303CC" echo "::set-output name=index-url::--additional-urls https://raw.githubusercontent.com/stm32duino/BoardManagerFiles/main/package_stmicroelectronics_index.json" + if [[ "${{ contains(matrix.board, 'NUCLEO_WL55') }}" == "true" ]]; then + # Do *not* skip STM32WL examples + echo "::set-output name=skip-pattern::''" + fi + elif [[ "${{ contains(matrix.board, 'stm32duino:STM32F1') }}" == "true" ]]; then # STM32 (unofficial core) echo "::set-output name=options:::bootloader_version=original,cpu_speed=speed_72mhz" From ba05317b7231bcf75ae8afb6071fabce201a18bc Mon Sep 17 00:00:00 2001 From: jgromes Date: Thu, 22 Dec 2022 12:40:46 +0100 Subject: [PATCH 13/13] Added new keywords --- keywords.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/keywords.txt b/keywords.txt index 7ad8dbbd..550790d2 100644 --- a/keywords.txt +++ b/keywords.txt @@ -38,6 +38,8 @@ SX1279 KEYWORD1 SX1280 KEYWORD1 SX1281 KEYWORD1 SX1282 KEYWORD1 +STM32WLx KEYWORD1 +STM32WLx_Module KEYWORD1 # protocols RTTYClient KEYWORD1 @@ -70,7 +72,7 @@ PasokonP7 KEYWORD1 # RadioLib ModuleA KEYWORD2 ModuleB KEYWORD2 -Module KEYWORD2 +setRfSwitchTable KEYWORD2 # SX127x/RFM9x + RF69 + CC1101 begin KEYWORD2 @@ -256,6 +258,7 @@ setInterruptSetup KEYWORD2 RADIOLIB_NC LITERAL1 RADIOLIB_VERSION LITERAL1 +RADIOLIB_PIN_TYPE LITERAL1 RADIOLIB_SHAPING_NONE LITERAL1 RADIOLIB_SHAPING_0_3 LITERAL1