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" 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/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); + */ + } +} 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 diff --git a/src/BuildOpt.h b/src/BuildOpt.h index e28e7a5c..5e6ca965 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 @@ -1009,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__); } 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..78793d0a 100644 --- a/src/Module.h +++ b/src/Module.h @@ -15,6 +15,54 @@ */ 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 { + /*! 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, + }; + + /*! + * 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 +290,103 @@ 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); /*! \brief Wait for time to elapse, either using the microsecond timer, or the TimerFlag. @@ -446,10 +574,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; 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/CC1101/CC1101.cpp b/src/modules/CC1101/CC1101.cpp index f943a470..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); @@ -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 bb245cbe..eb12b0be 100644 --- a/src/modules/CC1101/CC1101.h +++ b/src/modules/CC1101/CC1101.h @@ -928,16 +928,12 @@ 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); + /*! \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..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); @@ -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 979f4845..d4767329 100644 --- a/src/modules/RF69/RF69.h +++ b/src/modules/RF69/RF69.h @@ -1010,16 +1010,12 @@ 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); + /*! \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/STM32WLx.cpp b/src/modules/SX126x/STM32WLx.cpp new file mode 100644 index 00000000..744f88c3 --- /dev/null +++ b/src/modules/SX126x/STM32WLx.cpp @@ -0,0 +1,106 @@ +/* + +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 + +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) diff --git a/src/modules/SX126x/STM32WLx.h b/src/modules/SX126x/STM32WLx.h new file mode 100644 index 00000000..5778dfb1 --- /dev/null +++ b/src/modules/SX126x/STM32WLx.h @@ -0,0 +1,139 @@ +/* + +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; + + /*! + \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 +}; + +#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..c824200a --- /dev/null +++ b/src/modules/SX126x/STM32WLx_Module.cpp @@ -0,0 +1,108 @@ +/* + +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: + // 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: + 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 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 f5de00b3..2e95d778 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(_tx_mode); // 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(_tx_mode); // 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); @@ -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) { @@ -1571,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 @@ -1580,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 b56bcb29..97cb77e1 100644 --- a/src/modules/SX126x/SX126x.h +++ b/src/modules/SX126x/SX126x.h @@ -914,16 +914,12 @@ 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); + /*! \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() @@ -986,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(); @@ -1009,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); @@ -1047,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(); }; diff --git a/src/modules/SX127x/SX127x.cpp b/src/modules/SX127x/SX127x.cpp index 9930841e..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); @@ -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 302fabf4..e7502c15 100644 --- a/src/modules/SX127x/SX127x.h +++ b/src/modules/SX127x/SX127x.h @@ -1141,16 +1141,12 @@ 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); + /*! \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..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); @@ -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 c367013f..98bd7252 100644 --- a/src/modules/SX128x/SX128x.h +++ b/src/modules/SX128x/SX128x.h @@ -809,16 +809,12 @@ 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); + /*! \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..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); @@ -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 cf4f689b..6945e510 100644 --- a/src/modules/Si443x/Si443x.h +++ b/src/modules/Si443x/Si443x.h @@ -785,16 +785,12 @@ 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); + /*! \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.