diff --git a/README.md b/README.md
index ec66e03826..fc4f82adb6 100644
--- a/README.md
+++ b/README.md
@@ -802,17 +802,18 @@ your specific needs.
STTS22H |
STUSB4500 |
SX1276 |
+SX128X |
TCS3414 |
TCS3472 |
-TLC594x |
+TLC594x |
TMP102 |
TMP12x |
TMP175 |
TOUCH2046 |
VL53L0 |
-VL6180 |
+VL6180 |
WS2812 |
diff --git a/src/modm/driver/radio/sx128x.hpp b/src/modm/driver/radio/sx128x.hpp
new file mode 100644
index 0000000000..4e81862a13
--- /dev/null
+++ b/src/modm/driver/radio/sx128x.hpp
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2023, Lasse Alexander Jensen
+ * Copyright (c) 2023, Rasmus Kleist Hørlyck Sørensen
+ *
+ * This file is part of the modm project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+// ----------------------------------------------------------------------------
+
+#ifndef MODM_SX128X_HPP
+#define MODM_SX128X_HPP
+
+#include "sx128x_definitions.hpp"
+#include "sx128x_transport.hpp"
+
+#include
+
+namespace modm
+{
+
+/**
+ * @tparam Transport Transpot Layer for SX128x
+ *
+ * @ingroup modm_driver_sx128x
+ * @author Lasse Alexander Jensen
+ * @author Rasmus Kleist Hørlyck Sørensen
+ */
+template< class Transport, class Reset, class Busy >
+class Sx128x : public sx128x, public Transport
+{
+ using Command = Transport::Command;
+
+public:
+ Sx128x() = default;
+
+ /// Determine if radio is busy
+ bool
+ isBusy();
+
+ /// Reset the transceiver
+ /// @attention It is necessary to reset each radio on a shared bus prior to starting the first SPI communicatio
+ modm::ResumableResult
+ reset();
+
+public:
+ /// Get the transceiver status directly.
+ /// @attention this command can be issued at any time as long as it is not the very first command send over the interface
+ modm::ResumableResult
+ getStatus(Status *status);
+
+ /// Writes a single byte in a data memory space at the specified address
+ modm::ResumableResult
+ writeRegister(Register reg, uint8_t data);
+
+ /// Writes a block of bytes in a data memory space starting at a specific address
+ modm::ResumableResult
+ writeRegister(Register reg, std::span data);
+
+ /// Read a single byte of data at the given address
+ modm::ResumableResult
+ readRegister(Register reg, uint8_t *data);
+
+ /// Read a block of data starting at a given address
+ modm::ResumableResult
+ readRegister(Register reg, std::span data);
+
+ /// This function is used to write the data payload to be transmitted
+ /// @attention When the address exceeds 255 it wraps back to 0 due to the circular nature of data buffer
+ modm::ResumableResult
+ writeBuffer(uint8_t offset, std::span data);
+
+ /// This function is used to read the received data payload
+ modm::ResumableResult
+ readBuffer(uint8_t offset, std::span data);
+
+ /// Set the transceiver to Sleep mode with the lowest current consumption possible
+ /// @warning Depending on the specified level of memory retention not all instruction will be retained
+ modm::ResumableResult
+ setSleep(SleepConfig_t sleepConfig);
+
+ /// Set the device in either STDBY_RC or STDBY_XOSC mode which are intermediate levels of power consumption
+ /// By default in this state, the system is clocked by the 13 MHz RC oscillator to reduce power consumption.
+ /// However if the application is time critical, the XOSC block can be turned or left ON.
+ /// @attention In this Standby mode, the transceiver may be configured for future RF operations
+ modm::ResumableResult
+ setStandby(StandbyConfig standbyConfig = StandbyConfig::StdbyRc);
+
+ /// Set the device in Frequency Synthesizer mode where the PLL is locked to the carrier frequency
+ /// @attention In normal operation of the transceiver, the user does not normally need to use FS mode
+ modm::ResumableResult
+ setFs();
+
+ /// Set the device in Transmit mode
+ /// @warning The IRQ status must be cleared before using this command
+ modm::ResumableResult
+ setTx(PeriodBase periodBase, uint16_t periodBaseCount);
+
+ /// Set the device in Receiver mode
+ /// @warning The IRQ status must be cleared before using this command
+ /// @attention Setting periodBaseCount = 0 puts the transceiver in RX Single Mode
+ /// @attention Setting periodBaseCount = 0xFFFF puts the transceiver in Rx Continuous mode
+ modm::ResumableResult
+ setRx(PeriodBase periodBase, uint16_t periodBaseCount);
+
+ /// Set the transceiver in sniff mode, so that it regularly looks for new packets
+ /// @warning RxDone interrupt must be configured prior to enter Duty cycled operations
+ /// @warning SetLongPreamble must be issued prior to setRxDutyCycle
+ modm::ResumableResult
+ setRxDutyCycle(PeriodBase periodBase, uint16_t rxPeriodBaseCount, uint16_t sleepPeriodBaseCount);
+
+ /// Set the transceiver to use CAD (Channel Activity Detection) mode
+ /// @warning The Channel Activity Detection is a LoRa specific mode of operation
+ modm::ResumableResult
+ setCad();
+
+ /// Set the transceiver to generate a Continuous Wave (RF tone) at a selected frequency and output power
+ /// @attention The device remains in Tx Continuous Wave until the host sends a mode confiquration command.
+ modm::ResumableResult
+ setTxContinuousWave();
+
+ /// Set the transceiver to generate an infinite sequence of alternating 'O's and '1's in GFSK modulation and symbol 0 in LoRa
+ /// @attention The device remains in transmit until the host sends a mode confiquration command
+ modm::ResumableResult
+ setTxContinuousPreamble();
+
+ /// Set the transceiver radio frame out of a choice of 5 different packet types
+ /// @attention the packet type must be set first in the radio configuration sequence
+ modm::ResumableResult
+ setPacketType(PacketType packetType);
+
+ /// Get the current operating packet type of the radio
+ modm::ResumableResult
+ getPacketType(PacketType *packetType);
+
+ /// Set the frequency of the RF frequency mode.
+ /// @warning This must be called after SetPacket type.
+ /// @attention The LSB of rfFrequency is equal to the PLL step i.e. 52e6/2^18 Hz, where 52e6 is the crystal frequency in Hz
+ modm::ResumableResult
+ setRfFrequency(uint32_t rfFrequency);
+
+ /// Set the Tx output power and the Tx ramp time
+ /// @attention The output power (Pout) is defined by the parameter power. Pout = -18 + power
+ /// @attention PoutMin = -18 dBm (power = 0) and PoutMax = 13 dBm (power = 31)
+ modm::ResumableResult
+ setTxParams(uint8_t power, RampTime rampTime);
+
+ /// Set the number of symbols on which which Channel Activity Detected (CAD) operates
+ modm::ResumableResult
+ setCadParams(CadSymbolNumber cadSymbolNumber);
+
+ /// Set the base address for the packet handling operation in Tx and Rx mode for all packet types
+ modm::ResumableResult
+ setBufferBaseAddress(uint8_t txBaseAddress, uint8_t rxBaseAddress);
+
+ /// Set the modulation parameters of the radio
+ /// @attention The modulation parameters will be interpreted depending on the frame type
+ modm::ResumableResult
+ setModulationParams(ModulationParams modulationParams);
+
+ /// Set the parameters of the packet handling block
+ /// @warning Interpretation by the transceiver of the packet parameters depends upon the chosen packet type
+ modm::ResumableResult
+ setPacketParams(PacketParams packetParams);
+
+ /// Get length of the last received packet and the address of the first byte received
+ modm::ResumableResult
+ getRxBufferStatus(RxBufferStatus *rxBufferStatus);
+
+ /// Retrieve information about the last received packet
+ /// @attention The returned parameters are frame-dependent
+ /// @attention Actual signal power is -(rssiSync) / 2 (dBm)
+ /// @attention Actual SNR is (snr) / 4 (dBm)
+ modm::ResumableResult
+ getPacketStatus(PacketStatus *packetStatus);
+
+ /// Get the instantaneous RSSI value during reception of the packet
+ /// @attention Actual signal power is -(rssiSync) / 2 (dBm)
+ modm::ResumableResult
+ getRssiInst(uint8_t *rssiInst);
+
+ /// Enable IRQs and to route IRQs to DIO pins.
+ modm::ResumableResult
+ setDioIrqParams(Irq_t irqMask, Irq_t dio1Mask = Irq_t(), Irq_t dio2Mask = Irq_t(), Irq_t dio3Mask = Irq_t());
+
+ /// Get the value of the IRQ register
+ modm::ResumableResult
+ getIrqStatus(Irq_t *irqStatus);
+
+ /// Clear IRQ flags in the IRQ register
+ modm::ResumableResult
+ clearIrqStatus(Irq_t irqMask);
+
+ /// Specify if DC-DC or LDO is used for power regulation
+ modm::ResumableResult
+ setRegulatorMode(RegulatorMode regModeParam);
+
+ /// Save the present context of the radio register values to the Data RAM
+ modm::ResumableResult
+ setSaveContext();
+
+ /// Set the chip so that the state following a Rx or Tx operation is FS and not STDBY
+ /// This feature is to be used to reduce the switching time between consecutive Rx and/or Tx operations
+ modm::ResumableResult
+ setAutoFs(bool enable = true);
+
+ /// Set the device to be able to send back a response 150 us after a packet reception
+ /// @attention The delay between the packet reception end and the next packet transmission start is defined by TxDelay = time + 33 us
+ modm::ResumableResult
+ setAutoTx(uint16_t time);
+
+ /// Set the transceiver into Long Preamble mode
+ /// @warning Long Preamble mode can only be used with either LoRa mode and GFSK mode
+ modm::ResumableResult
+ setLongPreamble(bool enable = true);
+
+ /// Set UART speed
+ /// @warning UART only
+ /// @attention UART communication must be initiated with 115.2 kbps
+ /// @attention The UartDividerRatio is given by UartDividerRatio = (Baud Rate * 2^20) / fClk
+ modm::ResumableResult
+ setUartSpeed(UartDividerRatio uartDividerRatio);
+
+ /// Set the role of the radio in ranging operation
+ modm::ResumableResult
+ setRangingRole(RangingRole rangingRole);
+
+ /// Enable advanced ranging
+ modm::ResumableResult
+ setAdvancedRanging(bool enable = true);
+
+public:
+ // The LSB of rfFrequency is equal to the PLL step i.e. 52e6/2^18 Hz, where 52e6 is the crystal frequency in Hz
+ static constexpr float frequencyLsb = float(52_MHz) / 262144.f;
+
+private:
+ ShortPreciseTimeout timeout;
+ uint8_t buffer[8];
+};
+
+}
+
+#include "sx128x_impl.hpp"
+
+#endif
\ No newline at end of file
diff --git a/src/modm/driver/radio/sx128x.lb b/src/modm/driver/radio/sx128x.lb
new file mode 100644
index 0000000000..f54c22ba18
--- /dev/null
+++ b/src/modm/driver/radio/sx128x.lb
@@ -0,0 +1,40 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2023, Rasmus Kleist Hørlyck Sørensen
+#
+# This file is part of the modm project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+# -----------------------------------------------------------------------------
+
+
+def init(module):
+ module.name = ":driver:sx128x"
+ module.description = """\
+Semtech SX1280/SX1281 Driver
+
+Long Range, Low Power, 2.4 GHz Transceiver with Ranging Capability
+
+!!! warning "The SX128x driver UART transport layer is untested"
+"""
+
+def prepare(module, options):
+ module.depends(
+ ":architecture:gpio",
+ ":architecture:spi.device",
+ ":architecture:uart.device",
+ ":math:utils",
+ ":processing:resumable",
+ ":processing:timer")
+ return True
+
+def build(env):
+ env.outbasepath = "modm/src/modm/driver/radio"
+ env.copy("sx128x.hpp")
+ env.copy("sx128x_impl.hpp")
+ env.copy("sx128x_definitions.hpp")
+ env.copy("sx128x_transport.hpp")
+ env.copy("sx128x_transport_impl.hpp")
diff --git a/src/modm/driver/radio/sx128x_definitions.hpp b/src/modm/driver/radio/sx128x_definitions.hpp
new file mode 100644
index 0000000000..2673d6a35e
--- /dev/null
+++ b/src/modm/driver/radio/sx128x_definitions.hpp
@@ -0,0 +1,1002 @@
+/*
+ * Copyright (c) 2023, Lasse Alexander Jensen
+ * Copyright (c) 2023, Rasmus Kleist Hørlyck Sørensen
+ *
+ * This file is part of the modm project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+// ----------------------------------------------------------------------------
+
+#ifndef MODM_SX128X_DEFINITIONS_HPP
+#define MODM_SX128X_DEFINITIONS_HPP
+
+#include
+#include
+#include
+
+namespace modm
+{
+
+/// @ingroup modm_driver_sx128x
+struct sx128x
+{
+ enum class
+ Opcode : uint8_t
+ {
+ // Status
+ GetStatus = 0xC0,
+
+ // Register Access Operations
+ WriteRegister = 0x18,
+ ReadRegister = 0x19,
+
+ // Data buffer Operations
+ WriteBuffer = 0x1A,
+ ReadBuffer = 0x1B,
+
+ // Radio Operation Modes
+ SetSleep = 0x84,
+ SetStandby = 0x80,
+ SetFs = 0xC1,
+ SetTx = 0x83,
+ SetRx = 0x82,
+ SetRxDutyCycle = 0x94,
+ SetCad = 0xC5,
+ SetTxContinuousWave = 0xD1,
+ SetTxContinuousPreamble = 0xD2,
+
+ // Radio Configuration
+ SetPacketType = 0x8A,
+ GetPacketType = 0x03,
+ SetRfFrequency = 0x86,
+ SetTxParams = 0x8E,
+ SetCadParams = 0x88,
+ SetBufferBaseAddress = 0x8F,
+ SetModulationParams = 0x8B,
+ SetPacketParams = 0x8C,
+
+ // Communication Status Information
+ GetRxBufferStatus = 0x17,
+ GetPacketStatus = 0x1D,
+ GetRssiInst = 0x1F,
+
+ // IRQ Handling
+ SetDioIrqParams = 0x8D,
+ GetIrqStatus = 0x15,
+ ClrIrqStatus = 0x97,
+
+ // Settings functions
+ SetRegulatorMode = 0x96,
+ SetSaveContext = 0xD5,
+ SetAutoFs = 0x9E,
+ SetAutoTx = 0x98,
+ SetLongPreamble = 0x9B,
+ SetUartSpeed = 0x9D,
+ SetRangingRole = 0xA3,
+ SetAdvancedRanging = 0x9A,
+ };
+
+ enum class
+ Register : uint16_t
+ {
+ RxGain = 0x891,
+ ManualGainSetting = 0x895,
+ LnaGainValue = 0x89E,
+ LnaGainControl = 0x89F,
+ SynchPeakAttenuation = 0x8C2,
+ PayloadLength = 0x901,
+ LoRaHeaderMode = 0x903,
+
+ RangingRequestAddressByte3 = 0x912,
+ RangingRequestAddressByte2 = 0x913,
+ RangingRequestAddressByte1 = 0x914,
+ RangingRequestAddressByte0 = 0x915,
+
+ RangingDeviceAdressByte3 = 0x916,
+ RangingDeviceAdressByte2 = 0x917,
+ RangingDeviceAdressByte1 = 0x918,
+ RangingDeviceAdressByte0 = 0x919,
+
+ RangingFilterWindowSize = 0x91E,
+ ResetRangingFilter = 0x923,
+ RangingResultMux = 0x924,
+
+ SfAdditionalConfiguration = 0x925,
+
+ RangingCalibrationByte2 = 0x92B,
+ RangingCalibrationByte1 = 0x92C,
+ RangingCalibrationByte0 = 0x92D,
+
+ RangingIdCheckLength = 0x931,
+ FrequencyErrorCorrection = 0x93C,
+
+ LoRaSynchWordByte1 = 0x944,
+ LoRaSynchWordByte0 = 0x945,
+
+ FeiByte2 = 0x954,
+ FeiByte1 = 0x955,
+ FeiByte0 = 0x956,
+
+ RangingResultByte2 = 0x961,
+ RangingResultByte1 = 0x962,
+ RangingResultByte0 = 0x963,
+
+ RangingRssi = 0x964,
+ FreezeRangingResult = 0x97F,
+ PacketPreambleSettings = 0x9C1,
+ WhiteningInitialValue = 0x9C5,
+
+ CrcPolynomialDefMsb = 0x9C6,
+ CrcPolynomialDefLsb = 0x9C7,
+
+ CrcPolynomialSeedByte2 = 0x9C7,
+ CrcPolynomialSeedByte1 = 0x9C8,
+ CrcPolynomialSeedByte0 = 0x9C9,
+
+ CrcMsbInitialValue = 0x9C8,
+ CrcLsbInitialValue = 0x9C9,
+
+ SynchAddressControl = 0x9CD,
+
+ SyncAddress1Byte4 = 0x9CE,
+ SyncAddress1Byte3 = 0x9CF,
+ SyncAddress1Byte2 = 0x9D0,
+ SyncAddress1Byte1 = 0x9D1,
+ SyncAddress1Byte0 = 0x9D2,
+
+ SyncAddress2Byte4 = 0x9D3,
+ SyncAddress2Byte3 = 0x9D4,
+ SyncAddress2Byte2 = 0x9D5,
+ SyncAddress2Byte1 = 0x9D6,
+ SyncAddress2Byte0 = 0x9D7,
+
+ SyncAddress3Byte4 = 0x9D8,
+ SyncAddress3Byte3 = 0x9D9,
+ SyncAddress3Byte2 = 0x9DA,
+ SyncAddress3Byte1 = 0x9DB,
+ SyncAddress3Byte0 = 0x9DC
+ };
+
+public:
+
+ struct modm_packed
+ Status
+ {
+ enum class
+ CircuitMode : uint8_t
+ {
+ StdbyRc = 0x2,
+ StdbyXosc = 0x3,
+ Fs = 0x4,
+ Rx = 0x5,
+ Tx = 0x6,
+ };
+
+ enum class
+ CommandStatus : uint8_t
+ {
+ Processed = 0x1, /// Transceiver has successfully processed the command
+ DataAvailable = 0x2, /// Data are available to host
+ Timeout = 0x3, /// Command time-out
+ ProcessingError = 0x4, /// Command processing error
+ ExecutionFailure = 0x5, /// Failure to execute command
+ TxDone = 0x6, /// Command Tx done
+ };
+
+ uint8_t: 2;
+ CommandStatus commandStatus: 3;
+ CircuitMode circuitMode: 3;
+ };
+
+ enum class
+ SleepConfig : uint8_t
+ {
+ DataRamFlushed = Bit0,
+ DataRamRetention = Bit1,
+ DataBufferRetention = Bit2,
+ };
+ MODM_FLAGS8(SleepConfig);
+
+ enum class
+ StandbyConfig : uint8_t
+ {
+ StdbyRc = 0, /// Device running on RC 13MHz, set STDBY_RC mode
+ StdbyXosc = 1, /// Device running on XTAL 52MHz, set STDBY_XOSC mode
+ };
+
+ enum class
+ PeriodBase : uint8_t
+ {
+ us15_625 = 0x00,
+ us62_5 = 0x01,
+ ms1 = 0x02,
+ ms4 = 0x03
+ };
+
+ enum class
+ PacketType : uint8_t
+ {
+ Gfsk = 0x00,
+ LoRa = 0x01,
+ Ranging = 0x02,
+ Flrc = 0x03,
+ Ble = 0x04,
+ };
+
+ enum class
+ RampTime : uint8_t
+ {
+ us2 = 0x00,
+ us4 = 0x20,
+ us6 = 0x40,
+ us8 = 0x60,
+ us10 = 0x80,
+ us12 = 0xA0,
+ us16 = 0xC0,
+ us20 = 0xE0
+ };
+
+ enum class
+ CadSymbolNumber : uint8_t
+ {
+ One = 0x00,
+ Two = 0x20,
+ Four = 0x40,
+ Eight = 0x60,
+ Sixteen = 0x80
+ };
+
+ enum class
+ RegulatorMode : uint8_t
+ {
+ Ldo = 0x00,
+ DcDc = 0x01,
+ };
+
+ enum class
+ Irq : uint16_t
+ {
+ TxDone = Bit0,
+ RxDone = Bit1,
+ SyncWordValid = Bit2,
+ SyncWordError = Bit3,
+ HeaderValid = Bit4,
+ HeaderError = Bit5,
+ CrcError = Bit6,
+ RangingSlaveResponseDone = Bit7,
+ RangingSlaveRequestDiscard = Bit8,
+ RangingMasterResultValid = Bit9,
+ RangingMasterTimeout = Bit10,
+ RangingMasterRequestTimeout = Bit11,
+ CadDone = Bit12,
+ CadDetected = Bit13,
+ RxTxTimeout = Bit14,
+ PreambleDetected = Bit15,
+ };
+ MODM_FLAGS16(Irq);
+
+ struct RxBufferStatus
+ {
+ uint8_t rxPayloadLength;
+ uint8_t rxStartBufferPointer;
+ };
+
+ enum class
+ UartDividerRatio : uint16_t
+ {
+ Bd2400 = 0x00C2,
+ Bd4800 = 0x0183,
+ Bd9600 = 0x0306,
+ Bd14400 = 0x0489,
+ Bd19200 = 0x060D,
+ Bd38400 = 0x0C19,
+ Bd57600 = 0x1226,
+ Bd115200 = 0x244C,
+ Bd460600 = 0x9120,
+ Bd812490 = 0xFFFF,
+ };
+
+ enum class
+ RangingRole : uint8_t
+ {
+ Master = 0x01,
+ Slave = 0x00,
+ };
+
+ struct Gfsk
+ {
+ struct modm_packed
+ PacketStatus
+ {
+ enum class
+ Status : uint8_t
+ {
+ RxNoAck = Bit5,
+ PktSent = Bit0,
+ };
+ MODM_FLAGS8(Status);
+
+ enum class
+ Error : uint8_t
+ {
+ SyncError = Bit6,
+ LengthError = Bit5,
+ CrcError = Bit4,
+ AbortError = Bit3,
+ HeaderReceived = Bit2,
+ PacketCtrlBusy = Bit1,
+ };
+ MODM_FLAGS8(Error);
+
+ enum class
+ Sync : uint8_t
+ {
+ SyncAddrsCode2 = Bit2,
+ SyncAddrsCode1 = Bit1,
+ SyncAddrsCode0 = Bit0,
+ };
+ MODM_FLAGS8(Sync);
+
+ uint8_t rfu;
+ uint8_t rssiSync;
+ Error_t error;
+ Status_t status;
+ Sync_t sync;
+ };
+
+ struct modm_packed
+ PacketParams
+ {
+ enum class
+ PreambleLength : uint8_t
+ {
+ Bits4 = 0x00,
+ Bits8 = 0x10,
+ Bits12 = 0x20,
+ Bits16 = 0x30,
+ Bits20 = 0x40,
+ Bits24 = 0x50,
+ Bits28 = 0x60,
+ Bits32 = 0x70,
+ };
+
+ enum class
+ SyncWordLength : uint8_t
+ {
+ Byte1 = 0x00,
+ Byte2 = 0x02,
+ Byte3 = 0x04,
+ Byte4 = 0x06,
+ Byte5 = 0x08,
+ };
+
+ enum class
+ SyncWordMatch : uint8_t
+ {
+ Disable = 0x00,
+ One = 0x10,
+ Two = 0x20,
+ OneTwo = 0x30,
+ Three = 0x40,
+ OneThree = 0x50,
+ TwoThree = 0x60,
+ OneTwoThree = 0x70,
+ };
+
+ enum class
+ HeaderType : uint8_t
+ {
+ FixedLength = 0x00,
+ VariableLength = 0x20,
+ };
+
+ enum class
+ CrcLength : uint8_t
+ {
+ Off = 0x00,
+ Byte2 = 0x10,
+ Byte3 = 0x20,
+ Byte4 = 0x30,
+ };
+
+ enum class
+ Whitening : uint8_t
+ {
+ Enable = 0x00,
+ Disable = 0x08,
+ };
+
+ PreambleLength preambleLength;
+ SyncWordLength syncWordLength;
+ SyncWordMatch syncWordMatch;
+ HeaderType headerType;
+ uint8_t payloadLength;
+ CrcLength crcLength;
+ Whitening whitening;
+ };
+
+ struct modm_packed
+ ModulationParams
+ {
+ enum class
+ BitrateBandwidth : uint8_t
+ {
+ Br2000Bw2400 = 0x04,
+ Br1600Bw2400 = 0x28,
+ Br1000Bw2400 = 0x4C,
+ Br1000Bw1200 = 0x45,
+ Br800Bw2400 = 0x70,
+ Br800Bw1200 = 0x69,
+ Br500Bw1200 = 0x8D,
+ Br500Bw600 = 0x86,
+ Br400Bw1200 = 0xB1,
+ Br400Bw600 = 0xAA,
+ Br250Bw600 = 0xCE,
+ Br250Bw300 = 0xC7,
+ Br124Bw300 = 0xEF,
+ };
+
+ enum class
+ ModulationIndex : uint8_t
+ {
+ Ind0_35 = 0x00,
+ Ind0_50 = 0x01,
+ Ind0_75 = 0x02,
+ Ind1_00 = 0x03,
+ Ind1_25 = 0x04,
+ Ind1_50 = 0x05,
+ Ind1_75 = 0x06,
+ Ind2_00 = 0x07,
+ Ind2_25 = 0x08,
+ Ind2_50 = 0x09,
+ Ind2_75 = 0x0A,
+ Ind3_00 = 0x0B,
+ Ind3_25 = 0x0C,
+ Ind3_50 = 0x0D,
+ Ind3_75 = 0x0E,
+ Ind4_00 = 0x0F,
+ };
+
+ enum class
+ ModulationShaping : uint8_t
+ {
+ BtOff = 0x00,
+ Bt1_0 = 0x10,
+ Bt0_5 = 0x20,
+ };
+
+ BitrateBandwidth bitrateBandwidth;
+ ModulationIndex modulationIndex;
+ ModulationShaping modulationShaping;
+ };
+ };
+
+ struct Ble
+ {
+ struct modm_packed
+ PacketStatus
+ {
+ enum class
+ Status : uint8_t
+ {
+ RxNoAck = Bit5,
+ PktSent = Bit0,
+ };
+ MODM_FLAGS8(Status);
+
+ enum class
+ Error : uint8_t
+ {
+ SyncError = Bit6,
+ LengthError = Bit5,
+ CrcError = Bit4,
+ AbortError = Bit3,
+ HeaderReceived = Bit2,
+ PacketCtrlBusy = Bit1,
+ };
+ MODM_FLAGS8(Error);
+
+ enum class
+ Sync : uint8_t
+ {
+ SyncAddrsCode2 = Bit2,
+ SyncAddrsCode1 = Bit1,
+ SyncAddrsCode0 = Bit0,
+ };
+ MODM_FLAGS8(Sync);
+
+ uint8_t rfu;
+ uint8_t rssiSync;
+ Error_t error;
+ Status_t status;
+ Sync_t sync;
+ };
+
+ struct modm_packed
+ PacketParams
+ {
+ enum class
+ ConnectionState : uint8_t
+ {
+ PayloadLengthMax31Bytes = 0x00,
+ PayloadLengthMax37Bytes = 0x20,
+ TxTestMode = 0x40,
+ PayloadLengthMax355Bytes = 0x80,
+ };
+
+ enum class
+ CrcLength : uint8_t
+ {
+ Off = 0x00,
+ Byte3 = 0x10,
+ };
+
+ enum class
+ TestPayload : uint8_t
+ {
+ Prbs9 = 0x00,
+ Eyelong10 = 0x04,
+ Eyeshort10 = 0x08,
+ Prbs15 = 0x0C,
+ All1 = 0x10,
+ All0 = 0x14,
+ Eyelong01 = 0x18,
+ Eyeshort01 = 0x1c,
+ };
+
+ enum class
+ Whitening : uint8_t
+ {
+ Enable = 0x00,
+ Disable = 0x08,
+ };
+
+ ConnectionState connectionState;
+ CrcLength crcLength;
+ TestPayload testPayload;
+ Whitening whitening;
+ };
+
+ struct modm_packed
+ ModulationParams
+ {
+ enum class
+ BitrateBandwidth : uint8_t
+ {
+ Br2000Bw2400 = 0x04,
+ Br1600Bw2400 = 0x28,
+ Br1000Bw2400 = 0x4C,
+ Br1000Bw1200 = 0x45,
+ Br800Bw2400 = 0x70,
+ Br800Bw1200 = 0x69,
+ Br500Bw1200 = 0x8D,
+ Br500Bw600 = 0x86,
+ Br400Bw1200 = 0xB1,
+ Br400Bw600 = 0xAA,
+ Br250Bw600 = 0xCE,
+ Br250Bw300 = 0xC7,
+ Br124Bw300 = 0xEF,
+ };
+
+ enum class
+ ModulationIndex : uint8_t
+ {
+ Ind0_35 = 0x00,
+ Ind0_50 = 0x01,
+ Ind0_75 = 0x02,
+ Ind1_00 = 0x03,
+ Ind1_25 = 0x04,
+ Ind1_50 = 0x05,
+ Ind1_75 = 0x06,
+ Ind2_00 = 0x07,
+ Ind2_25 = 0x08,
+ Ind2_50 = 0x09,
+ Ind2_75 = 0x0A,
+ Ind3_00 = 0x0B,
+ Ind3_25 = 0x0C,
+ Ind3_50 = 0x0D,
+ Ind3_75 = 0x0E,
+ Ind4_00 = 0x0F,
+ };
+
+ enum class
+ ModulationShaping : uint8_t
+ {
+ BtOff = 0x00,
+ Bt1_0 = 0x10,
+ Bt0_5 = 0x20,
+ };
+
+ BitrateBandwidth bitrateBandwidth;
+ ModulationIndex modulationIndex;
+ ModulationShaping modulationShaping;
+ };
+ };
+
+ struct Flrc
+ {
+ struct modm_packed
+ PacketStatus
+ {
+ enum class
+ Status : uint8_t
+ {
+ RxNoAck = Bit5,
+ PktSent = Bit0,
+ };
+ MODM_FLAGS8(Status);
+
+ enum class
+ Error : uint8_t
+ {
+ SyncError = Bit6,
+ LengthError = Bit5,
+ CrcError = Bit4,
+ AbortError = Bit3,
+ HeaderReceived = Bit2,
+ PacketCtrlBusy = Bit1,
+ };
+ MODM_FLAGS8(Error);
+
+ enum class
+ Sync : uint8_t
+ {
+ SyncAddrsCode2 = Bit2,
+ SyncAddrsCode1 = Bit1,
+ SyncAddrsCode0 = Bit0,
+ };
+ MODM_FLAGS8(Sync);
+
+ uint8_t rfu;
+ uint8_t rssiSync;
+ Error_t error;
+ Status_t status;
+ Sync_t sync;
+ };
+
+ struct modm_packed
+ PacketParams
+ {
+ enum class
+ PreambleLength : uint8_t
+ {
+ Bits4 = 0x00,
+ Bits8 = 0x10,
+ Bits12 = 0x20,
+ Bits16 = 0x30,
+ Bits20 = 0x40,
+ Bits24 = 0x50,
+ Bits28 = 0x60,
+ Bits32 = 0x70,
+ };
+
+ enum class
+ SyncWordLength : uint8_t
+ {
+ NoSync = 0x00,
+ Bits32 = 0x02,
+ };
+
+ enum class
+ SyncWordMatch : uint8_t
+ {
+ Disable = 0x00,
+ One = 0x10,
+ Two = 0x20,
+ OneTwo = 0x30,
+ Three = 0x40,
+ OneThree = 0x50,
+ TwoThree = 0x60,
+ OneTwoThree = 0x70,
+ };
+
+ enum class
+ PacketType : uint8_t
+ {
+ FixedLength = 0x00,
+ VariableLength = 0x20,
+ };
+
+ enum class
+ CrcLength : uint8_t
+ {
+ Off = 0x00,
+ Byte1 = 0x10,
+ Byte2 = 0x20,
+ };
+
+ enum class
+ Whitening : uint8_t
+ {
+ Disable = 0x08,
+ };
+
+ PreambleLength preambleLength;
+ SyncWordLength syncWordLength;
+ SyncWordMatch syncWordMatch;
+ PacketType packetType;
+ uint8_t payloadLength;
+ CrcLength crcLength;
+ Whitening whitening;
+ };
+
+ struct modm_packed
+ ModulationParams
+ {
+ enum class
+ BitrateBandwidth : uint8_t
+ {
+ Br1300Bw1200 = 0x45,
+ Br1000Bw1200 = 0x69,
+ Br650Bw600 = 0x86,
+ Br520Bw600 = 0xAA,
+ Br325Bw300 = 0xC7,
+ Br260Bw300 = 0xEB,
+ };
+
+ enum class
+ CodingRate : uint8_t
+ {
+ Cr1_2 = 0x00,
+ Cr3_4 = 0x02,
+ Cr1_1 = 0x04,
+ };
+
+ enum class
+ ModulationShaping : uint8_t
+ {
+ BtDis = 0x00,
+ Bt1_0 = 0x10,
+ Bt0_5 = 0x20,
+ };
+
+ BitrateBandwidth bitrateBandwidth;
+ CodingRate codingRate;
+ ModulationShaping modulationShaping;
+ };
+ };
+
+ struct LoRa
+ {
+ struct modm_packed
+ PacketStatus
+ {
+ uint8_t rssiSync;
+ uint8_t snr;
+ };
+
+ struct modm_packed
+ PacketParams
+ {
+ enum class
+ HeaderType : uint8_t
+ {
+ Explicit = 0x00,
+ Implicit = 0x80,
+ };
+
+ enum class
+ Crc : uint8_t
+ {
+ Enable = 0x20,
+ Disable = 0x00,
+ };
+
+ enum class
+ InvertIq : uint8_t
+ {
+ Inverted = 0x00,
+ Standard = 0x40,
+ };
+
+ uint8_t preambleLength;
+ HeaderType headerType;
+ uint8_t payloadLength;
+ Crc crc;
+ InvertIq invertIq;
+ };
+
+ struct modm_packed
+ ModulationParams
+ {
+ enum class
+ SpreadingFactor : uint8_t
+ {
+ Sf5 = 0x50,
+ Sf6 = 0x60,
+ Sf7 = 0x70,
+ Sf8 = 0x80,
+ Sf9 = 0x90,
+ Sf10 = 0xA0,
+ Sf11 = 0xB0,
+ Sf12 = 0xC0,
+ };
+
+ enum class
+ Bandwidth : uint8_t
+ {
+ Bw1600 = 0x0A,
+ Bw800 = 0x18,
+ Bw400 = 0x26,
+ Bw200 = 0x34,
+ };
+
+ enum class
+ CodingRate : uint8_t
+ {
+ Cr4_5 = 0x01,
+ Cr4_6 = 0x02,
+ Cr4_7 = 0x03,
+ Cr4_8 = 0x04,
+ Cr_Li_4_5 = 0x05,
+ Cr_Li_4_6 = 0x06,
+ Cr_Li_4_7 = 0x07,
+ };
+
+ SpreadingFactor spreadingFactor;
+ Bandwidth bandwidth;
+ CodingRate codingRate;
+ };
+ };
+
+ struct Ranging
+ {
+ struct modm_packed
+ PacketStatus
+ {
+ uint8_t rssiSync;
+ uint8_t sni;
+ };
+
+ struct modm_packed
+ PacketParams
+ {
+ enum class
+ HeaderType : uint8_t
+ {
+ Explicit = 0x00,
+ Implicit = 0x80,
+ };
+
+ enum class
+ Crc : uint8_t
+ {
+ Enable = 0x20,
+ Disable = 0x00,
+ };
+
+ enum class
+ InvertIq : uint8_t
+ {
+ Inverted = 0x00,
+ Standard = 0x40,
+ };
+
+ uint8_t preambleLength;
+ HeaderType headerType;
+ uint8_t payloadLength;
+ Crc crc;
+ InvertIq invertIq;
+ };
+
+ struct modm_packed
+ ModulationParams
+ {
+ enum class
+ SpreadingFactor : uint8_t
+ {
+ Sf5 = 0x50,
+ Sf6 = 0x60,
+ Sf7 = 0x70,
+ Sf8 = 0x80,
+ Sf9 = 0x90,
+ Sf10 = 0xA0,
+ };
+
+ enum class
+ Bandwidth : uint8_t
+ {
+ Bw1600 = 0x0A,
+ Bw800 = 0x18,
+ Bw400 = 0x26,
+ };
+
+ enum class
+ CodingRate : uint8_t
+ {
+ Cr4_5 = 0x01,
+ Cr4_6 = 0x02,
+ Cr4_7 = 0x03,
+ Cr4_8 = 0x04,
+ Cr_Li_4_5 = 0x05,
+ Cr_Li_4_6 = 0x06,
+ Cr_Li_4_7 = 0x07,
+ };
+
+ SpreadingFactor spreadingFactor;
+ Bandwidth bandwidth;
+ CodingRate codingRate;
+ };
+ };
+
+ union PacketParams
+ {
+ Gfsk::PacketParams gfsk;
+ Flrc::PacketParams flrc;
+ Ble::PacketParams ble;
+ LoRa::PacketParams lora;
+ Ranging::PacketParams ranging;
+
+ PacketParams() {}
+ PacketParams(Gfsk::PacketParams packetParams) : gfsk(packetParams) {}
+ PacketParams(Flrc::PacketParams packetParams) : flrc(packetParams) {}
+ PacketParams(Ble::PacketParams packetParams) : ble(packetParams) {}
+ PacketParams(LoRa::PacketParams packetParams) : lora(packetParams) {}
+ PacketParams(Ranging::PacketParams packetParams) : ranging(packetParams) {}
+ };
+
+ union PacketStatus
+ {
+ Gfsk::PacketStatus gfsk;
+ Flrc::PacketStatus flrc;
+ Ble::PacketStatus ble;
+ LoRa::PacketStatus lora;
+ Ranging::PacketStatus ranging;
+
+ PacketStatus() {}
+ PacketStatus(Gfsk::PacketStatus packetStatus) : gfsk(packetStatus) {}
+ PacketStatus(Flrc::PacketStatus packetStatus) : flrc(packetStatus) {}
+ PacketStatus(Ble::PacketStatus packetStatus) : ble(packetStatus) {}
+ PacketStatus(LoRa::PacketStatus packetStatus) : lora(packetStatus) {}
+ PacketStatus(Ranging::PacketStatus packetStatus) : ranging(packetStatus) {}
+ };
+
+ union ModulationParams
+ {
+ Gfsk::ModulationParams gfsk;
+ Flrc::ModulationParams flrc;
+ Ble::ModulationParams ble;
+ LoRa::ModulationParams lora;
+ Ranging::ModulationParams ranging;
+
+ ModulationParams() {}
+ ModulationParams(Gfsk::ModulationParams modulationParams) : gfsk(modulationParams) {}
+ ModulationParams(Flrc::ModulationParams modulationParams) : flrc(modulationParams) {}
+ ModulationParams(Ble::ModulationParams modulationParams) : ble(modulationParams) {}
+ ModulationParams(LoRa::ModulationParams modulationParams) : lora(modulationParams) {}
+ ModulationParams(Ranging::ModulationParams modulationParams) : ranging(modulationParams) {}
+ };
+
+protected:
+ /// @cond
+ static constexpr uint16_t
+ i(Register reg) { return uint16_t(reg); }
+ static constexpr uint8_t
+ i(Opcode opcode) { return uint8_t(opcode); }
+ static constexpr uint8_t
+ i(StandbyConfig standbyConfig) { return uint8_t(standbyConfig); }
+ static constexpr uint8_t
+ i(PeriodBase periodBase) { return uint8_t(periodBase); }
+ static constexpr uint8_t
+ i(PacketType packetType) { return uint8_t(packetType); }
+ static constexpr uint8_t
+ i(RampTime rampTime) { return uint8_t(rampTime); }
+ static constexpr uint8_t
+ i(CadSymbolNumber cadSymbolNumber) { return uint8_t(cadSymbolNumber); }
+ static constexpr uint8_t
+ i(RegulatorMode regulatorMode) { return uint8_t(regulatorMode); }
+ static constexpr uint16_t
+ i(UartDividerRatio uartDividerRatio) { return uint16_t(uartDividerRatio); }
+ static constexpr uint8_t
+ i(RangingRole rangingRole) { return uint8_t(rangingRole); }
+ /// @endcond
+};
+
+}
+
+
+
+#endif
\ No newline at end of file
diff --git a/src/modm/driver/radio/sx128x_impl.hpp b/src/modm/driver/radio/sx128x_impl.hpp
new file mode 100644
index 0000000000..251041eea8
--- /dev/null
+++ b/src/modm/driver/radio/sx128x_impl.hpp
@@ -0,0 +1,608 @@
+/*
+ * Copyright (c) 2023, Lasse Alexander Jensen
+ * Copyright (c) 2023, Rasmus Kleist Hørlyck Sørensen
+ *
+ * This file is part of the modm project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+// ----------------------------------------------------------------------------
+
+#ifndef MODM_SX128X_HPP
+# error "Don't include this file directly, use 'sx128x.hpp' instead!"
+#endif
+
+#include
+
+namespace modm
+{
+
+// ----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+bool
+Sx128x< Transport, Reset, Busy >::isBusy()
+{
+ return Busy::read();
+}
+
+// ----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::reset()
+{
+ RF_BEGIN();
+
+ Reset::setOutput(modm::Gpio::Low);
+ timeout.restart(std::chrono::milliseconds(50));
+ RF_WAIT_UNTIL(timeout.isExpired());
+
+ Reset::setOutput(modm::Gpio::High);
+ timeout.restart(std::chrono::milliseconds(20));
+ RF_WAIT_UNTIL(timeout.isExpired());
+
+ RF_END();
+}
+
+// ----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::getStatus(sx128x::Status *status)
+{
+ // The GetStatus command can be issued at any time
+ return this->writeCommandSingleData(Opcode::GetStatus, std::span{status, 1});
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::writeRegister(Register reg, uint8_t data)
+{
+ buffer[0] = data;
+ return this->writeRegister(reg, std::span{buffer, 1});
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::writeRegister(Register reg, std::span data)
+{
+ RF_BEGIN();
+
+ buffer[0] = (i(reg) >> 8) & 0xFF;
+ buffer[1] = i(reg) & 0xFF;
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->writeCommand(Command(Opcode::WriteRegister, std::span{buffer, 2}), data));
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::readRegister(Register reg, uint8_t *value)
+{
+ return this->readRegister(reg, std::span{value, 1});
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::readRegister(Register reg, std::span data)
+{
+ RF_BEGIN();
+
+ buffer[0] = (i(reg) >> 8) & 0xFF;
+ buffer[1] = i(reg) & 0xFF;
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->readCommand(Command(Opcode::ReadRegister, std::span{buffer, 2}), data));
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::writeBuffer(uint8_t offset, std::span data)
+{
+ RF_BEGIN();
+
+ buffer[0] = offset;
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->writeCommand(Command(Opcode::WriteBuffer, std::span{buffer, 1}), data));
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::readBuffer(uint8_t offset, std::span data)
+{
+ RF_BEGIN();
+
+ buffer[0] = offset;
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->readCommand(Command(Opcode::ReadBuffer, std::span{buffer, 1}), data));
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::setSleep(SleepConfig_t sleepConfig)
+{
+ RF_BEGIN();
+
+ buffer[0] = sleepConfig.value;
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->writeCommand(Opcode::SetSleep, std::span{buffer, 1}));
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::setStandby(StandbyConfig standbyConfig)
+{
+ RF_BEGIN();
+
+ buffer[0] = i(standbyConfig);
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->writeCommand(Opcode::SetStandby, std::span{buffer, 1}));
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::setFs()
+{
+ RF_BEGIN();
+
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->writeCommandSingleData(Opcode::SetFs));
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::setTx(PeriodBase periodBase, uint16_t periodBaseCount)
+{
+ RF_BEGIN();
+
+ buffer[0] = i(periodBase);
+ buffer[1] = (periodBaseCount >> 8) & 0xFF;
+ buffer[2] = periodBaseCount & 0xFF;
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->writeCommand(Opcode::SetTx, std::span{buffer, 3}));
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::setRx(PeriodBase periodBase, uint16_t periodBaseCount)
+{
+ RF_BEGIN();
+
+ buffer[0] = i(periodBase);
+ buffer[1] = (periodBaseCount >> 8) & 0xFF;
+ buffer[2] = periodBaseCount & 0xFF;
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->writeCommand(Opcode::SetRx, std::span{buffer, 3}) );
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::setRxDutyCycle(PeriodBase periodBase, uint16_t rxPeriodBaseCount, uint16_t sleepPeriodBaseCount)
+{
+ RF_BEGIN();
+
+ buffer[0] = i(periodBase);
+ buffer[1] = (rxPeriodBaseCount >> 8) & 0xFF;
+ buffer[2] = rxPeriodBaseCount & 0xFF;
+ buffer[3] = (sleepPeriodBaseCount >> 8) & 0xFF;
+ buffer[4] = sleepPeriodBaseCount & 0xFF;
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->writeCommand(Opcode::SetRxDutyCycle, std::span{buffer, 5}));
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::setCad()
+{
+ RF_BEGIN();
+
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->writeCommandSingleData(Opcode::SetCad));
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::setTxContinuousWave()
+{
+ RF_BEGIN();
+
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->writeCommandSingleData(Opcode::SetTxContinuousWave));
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::setTxContinuousPreamble()
+{
+ RF_BEGIN();
+
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->writeCommandSingleData(Opcode::SetTxContinuousPreamble));
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::setPacketType(PacketType packetType)
+{
+ RF_BEGIN();
+
+ buffer[0] = i(packetType);
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->writeCommand(Opcode::SetPacketType, std::span{buffer, 1}));
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::getPacketType(PacketType *packetType)
+{
+ RF_BEGIN();
+
+ RF_WAIT_WHILE(isBusy());
+ if (RF_CALL(this->readCommand(Opcode::GetPacketType, std::span{(uint8_t *) packetType, 1})))
+ {
+ *packetType = PacketType(buffer[0]);
+ RF_RETURN(true);
+ }
+
+ RF_END_RETURN(false);
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::setRfFrequency(uint32_t rfFrequency)
+{
+ RF_BEGIN();
+
+ buffer[0] = (rfFrequency >> 16) & 0xFF;
+ buffer[1] = (rfFrequency >> 8) & 0xFF;
+ buffer[2] = rfFrequency & 0xFF;
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->writeCommand(Opcode::SetRfFrequency, std::span{buffer, 3}));
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::setTxParams(uint8_t power, RampTime rampTime)
+{
+ RF_BEGIN();
+
+ buffer[0] = power;
+ buffer[1] = i(rampTime);
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->writeCommand(Opcode::SetTxParams, std::span{buffer, 2}));
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::setCadParams(CadSymbolNumber cadSymbolNumber)
+{
+ RF_BEGIN();
+
+ buffer[0] = uint8_t(cadSymbolNumber);
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->writeCommand(Opcode::SetCadParams, std::span{buffer, 1}));
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::setBufferBaseAddress(uint8_t txBaseAddress, uint8_t rxBaseAddress)
+{
+ RF_BEGIN();
+
+ buffer[0] = txBaseAddress;
+ buffer[1] = rxBaseAddress;
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->writeCommand(Opcode::SetBufferBaseAddress, std::span{buffer, 2}));
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::setModulationParams(ModulationParams modulationParams)
+{
+ RF_BEGIN();
+
+ std::memcpy(buffer, (uint8_t *) &modulationParams, 3);
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->writeCommand(Opcode::SetModulationParams, std::span{buffer, 3}));
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::setPacketParams(PacketParams packetParams)
+{
+ RF_BEGIN();
+
+ std::memcpy(buffer, (uint8_t *) &packetParams, 7);
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->writeCommand(Opcode::SetPacketParams, std::span{buffer, 7}));
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::getRxBufferStatus(RxBufferStatus *rxBufferStatus)
+{
+ RF_BEGIN();
+
+ RF_WAIT_WHILE(isBusy());
+ if (RF_CALL(this->readCommand(Opcode::GetRxBufferStatus, std::span{buffer, 2})))
+ {
+ std::memcpy((uint8_t *) rxBufferStatus, buffer, 2);
+ RF_RETURN(true);
+ }
+
+ RF_END_RETURN(false);
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::getPacketStatus(PacketStatus *packetStatus)
+{
+ RF_BEGIN();
+
+ RF_WAIT_WHILE(isBusy());
+ if (RF_CALL(this->readCommand(Opcode::GetPacketStatus, std::span{buffer, 5})))
+ {
+ std::memcpy((uint8_t *) packetStatus, buffer, 5);
+ RF_RETURN(true);
+ }
+
+ RF_END_RETURN(false);
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::getRssiInst(uint8_t *rssiInst)
+{
+ RF_BEGIN();
+
+ RF_WAIT_WHILE(isBusy());
+ if (RF_CALL(this->readCommand(Opcode::GetRssiInst, std::span{buffer, 1})))
+ {
+ *rssiInst = buffer[0];
+ RF_RETURN(true);
+ }
+
+ RF_END_RETURN(false);
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::setDioIrqParams(Irq_t irqMask, Irq_t dio1Mask, Irq_t dio2Mask, Irq_t dio3Mask)
+{
+ RF_BEGIN();
+
+ buffer[0] = (irqMask.value >> 8) & 0xFF;
+ buffer[1] = irqMask.value & 0xFF;
+ buffer[2] = (dio1Mask.value >> 8) & 0xFF;
+ buffer[3] = dio1Mask.value & 0xFF;
+ buffer[4] = (dio2Mask.value >> 8) & 0xFF;
+ buffer[5] = dio2Mask.value & 0xFF;
+ buffer[6] = (dio3Mask.value >> 8) & 0xFF;
+ buffer[7] = dio3Mask.value & 0xFF;
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->writeCommand(Opcode::SetDioIrqParams, std::span{buffer, 8}));
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::getIrqStatus(Irq_t *irqStatus)
+{
+ RF_BEGIN();
+
+ RF_WAIT_WHILE(isBusy());
+ if (RF_CALL(this->readCommand(Opcode::GetIrqStatus, std::span{buffer, 2})))
+ {
+ *irqStatus = Irq_t((buffer[0] << 8) | buffer[1]);
+ RF_RETURN(true);
+ }
+
+ RF_END_RETURN(false);
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::clearIrqStatus(Irq_t irqMask)
+{
+ RF_BEGIN();
+
+ buffer[0] = (irqMask.value >> 8) & 0xFF;
+ buffer[1] = irqMask.value & 0xFF;
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->writeCommand(Opcode::ClrIrqStatus, std::span{buffer, 2}));
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::setRegulatorMode(RegulatorMode regModeParam)
+{
+ RF_BEGIN();
+
+ buffer[0] = i(regModeParam);
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->writeCommand(Opcode::SetRegulatorMode, std::span{buffer, 1}));
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::setSaveContext()
+{
+ RF_BEGIN();
+
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->writeCommandSingleData(Opcode::SetSaveContext));
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::setAutoFs(bool enable)
+{
+ RF_BEGIN();
+
+ buffer[0] = enable ? 0x01 : 0x00;
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->writeCommand(Opcode::SetAutoFs, std::span{buffer, 1}));
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::setAutoTx(uint16_t time)
+{
+ RF_BEGIN();
+
+ buffer[0] = (time >> 8) & 0xFF;
+ buffer[1] = time & 0xFF;
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->writeCommand(Opcode::SetAutoTx, std::span{buffer, 2}));
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::setLongPreamble(bool enable)
+{
+ RF_BEGIN();
+
+ buffer[0] = enable ? 0x01 : 0x00;
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->writeCommand(Opcode::SetLongPreamble, std::span{buffer, 1}));
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::setUartSpeed(UartDividerRatio uartDividerRatio)
+{
+ RF_BEGIN();
+
+ buffer[0] = (i(uartDividerRatio) >> 8) & 0xFF;
+ buffer[1] = i(uartDividerRatio) & 0xFF;
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->writeCommand(Opcode::SetLongPreamble, std::span{buffer, 1}));
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::setRangingRole(RangingRole rangingRole)
+{
+ RF_BEGIN();
+
+ buffer[0] = i(rangingRole);
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->writeCommand(Opcode::SetLongPreamble, std::span{buffer, 1}));
+}
+
+// -----------------------------------------------------------------------------
+
+template < class Transport, class Reset, class Busy >
+modm::ResumableResult
+Sx128x< Transport, Reset, Busy >::setAdvancedRanging(bool enable)
+{
+ RF_BEGIN();
+
+ buffer[0] = enable ? 0x01 : 0x00;
+ RF_WAIT_WHILE(isBusy());
+
+ RF_END_RETURN_CALL(this->writeCommand(Opcode::SetAdvancedRanging, std::span{buffer, 1}));
+}
+
+}
\ No newline at end of file
diff --git a/src/modm/driver/radio/sx128x_transport.hpp b/src/modm/driver/radio/sx128x_transport.hpp
new file mode 100644
index 0000000000..8cc98bcf2c
--- /dev/null
+++ b/src/modm/driver/radio/sx128x_transport.hpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2023, Rasmus Kleist Hørlyck Sørensen
+ *
+ * This file is part of the modm project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+// ----------------------------------------------------------------------------
+
+#ifndef MODM_SX128X_TRANSPORT_HPP
+#define MODM_SX128X_TRANSPORT_HPP
+
+#include "sx128x_definitions.hpp"
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+namespace modm
+{
+
+/**
+ * SX128X Transport Layer Interface
+ *
+ * @ingroup modm_driver_sx128x_transport
+ * @author Rasmus Kleist Hørlyck Sørensen
+ */
+class Sx128xTransport
+{
+public:
+ struct Command : sx128x
+ {
+ Command(Opcode opcode) : opcode(opcode) {}
+ Command(Opcode opcode, std::span vargs) : opcode(opcode), vargs(vargs) {}
+
+ // DATA ACCESS
+ ///@{
+ ///
+ Opcode inline
+ getOpcode() const
+ { return opcode; }
+
+ void inline
+ getOpcode(Opcode *opcode) const
+ { *opcode = getOpcode(); }
+
+ void inline
+ getOpcode(uint8_t *opcode) const
+ { *opcode = i(getOpcode()); }
+
+ std::span inline
+ getVargs() const
+ { return vargs; }
+ ///@}
+
+ bool inline
+ hasVargs() const
+ { return !vargs.empty(); }
+
+ std::size_t inline
+ getVargsCount() const
+ { return vargs.size(); }
+
+ private:
+ Opcode opcode;
+ std::span vargs;
+ };
+};
+
+/**
+ * SX128X SPI Transport Layer.
+ *
+ * @tparam SpiMaster SpiMaster interface
+ * @tparam Cs Chip-select pin
+ *
+ * @ingroup modm_driver_sx128x_transport
+ * @author Rasmus Kleist Hørlyck Sørensen
+ */
+template < class SpiMaster, class Cs >
+class Sx128xTransportSpi : public Sx128xTransport, public SpiDevice< SpiMaster >, protected NestedResumable<2>
+{
+public:
+ Sx128xTransportSpi() = default;
+
+protected:
+ modm::ResumableResult
+ writeCommandSingleData(Command command, uint8_t *data = nullptr);
+
+ modm::ResumableResult
+ writeCommand(Command command, std::span data);
+
+ modm::ResumableResult
+ readCommand(Command command, std::span data);
+
+private:
+ uint8_t commandBuffer[4];
+};
+
+/**
+ * SX128X UART Transport Layer.
+ *
+ * @tparam Uart UART interface
+ *
+ * @ingroup modm_driver_sx128x_transport
+ * @author Rasmus Kleist Hørlyck Sørensen
+ */
+template < class Uart >
+class Sx128xTransportUart : public Sx128xTransport, public UartDevice< Uart, 2 >
+{
+public:
+ Sx128xTransportUart() = default;
+
+protected:
+ modm::ResumableResult
+ writeCommandSingleData(Command command, uint8_t *data = nullptr);
+
+ modm::ResumableResult
+ writeCommand(Command command, std::span data);
+
+ modm::ResumableResult
+ readCommand(Command command, std::span data);
+
+private:
+ uint8_t commandBuffer[4];
+};
+
+} // namespace modm
+
+#include "sx128x_transport_impl.hpp"
+
+#endif // MODM_SX128X_TRANSPORT_HPP
diff --git a/src/modm/driver/radio/sx128x_transport_impl.hpp b/src/modm/driver/radio/sx128x_transport_impl.hpp
new file mode 100644
index 0000000000..86967b1f46
--- /dev/null
+++ b/src/modm/driver/radio/sx128x_transport_impl.hpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2023, Rasmus Kleist Hørlyck Sørensen
+ *
+ * This file is part of the modm project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+// ----------------------------------------------------------------------------
+
+#ifndef MODM_SX128X_TRANSPORT_HPP
+# error "Don't include this file directly, use 'sx128x_transport.hpp' instead!"
+#endif
+
+#include
+
+namespace modm
+{
+
+template < class SpiMaster, class Cs >
+modm::ResumableResult
+Sx128xTransportSpi::writeCommandSingleData(Command command, uint8_t *data)
+{
+ RF_BEGIN();
+
+ RF_WAIT_UNTIL(this->acquireMaster());
+ Cs::reset();
+
+ command.getOpcode(commandBuffer);
+ RF_CALL(SpiMaster::transfer(commandBuffer, data, 1));
+
+ if (this->releaseMaster())
+ Cs::set();
+
+ RF_END_RETURN(true);
+}
+
+// ----------------------------------------------------------------------------
+
+template < class SpiMaster, class Cs >
+modm::ResumableResult
+Sx128xTransportSpi::writeCommand(Command command, std::span data)
+{
+ RF_BEGIN();
+
+ RF_WAIT_UNTIL(this->acquireMaster());
+ Cs::reset();
+
+ command.getOpcode(commandBuffer);
+ if (command.hasVargs()) {
+ auto vargs = command.getVargs();
+ std::memcpy(commandBuffer + 1, vargs.data(), vargs.size());
+ }
+
+ RF_CALL(SpiMaster::transfer(commandBuffer, nullptr, command.getVargsCount() + 1));
+ RF_CALL(SpiMaster::transfer(&data[0], nullptr, data.size()));
+
+ if (this->releaseMaster())
+ Cs::set();
+
+ RF_END_RETURN(true);
+}
+
+// ----------------------------------------------------------------------------
+
+template < class SpiMaster, class Cs >
+modm::ResumableResult
+Sx128xTransportSpi::readCommand(Command command, std::span data)
+{
+ RF_BEGIN();
+
+ RF_WAIT_UNTIL(this->acquireMaster());
+ Cs::reset();
+
+ command.getOpcode(commandBuffer);
+ if (command.hasVargs()) {
+ auto vargs = command.getVargs();
+ std::memcpy(commandBuffer + 1, vargs.data(), vargs.size());
+ commandBuffer[1 + vargs.size()] = 0x00;
+ } else {
+ commandBuffer[1] = 0x00;
+ }
+
+ RF_CALL(SpiMaster::transfer(commandBuffer, nullptr, command.getVargsCount() + 2));
+ RF_CALL(SpiMaster::transfer(nullptr, &data[0], data.size()));
+
+ if (this->releaseMaster())
+ Cs::set();
+
+ RF_END_RETURN(true);
+}
+
+// ----------------------------------------------------------------------------
+
+template < class Uart >
+modm::ResumableResult
+Sx128xTransportUart::writeCommandSingleData(Command command, uint8_t *data)
+{
+ RF_BEGIN();
+
+ command.getOpcode(commandBuffer);
+ if (RF_CALL(this->write(commandBuffer[0]))) {
+ if (data != nullptr) {
+ RF_RETURN_CALL(this->read(data));
+ } else {
+ RF_RETURN(true);
+ }
+ }
+
+ RF_END_RETURN(false);
+}
+
+// ----------------------------------------------------------------------------
+
+template < class Uart >
+modm::ResumableResult
+Sx128xTransportUart::writeCommand(Command command, std::span data)
+{
+ RF_BEGIN();
+
+ command.getOpcode(commandBuffer);
+ if (command.hasVargs()) {
+ auto vargs = command.getVargs();
+ std::memcpy(commandBuffer + 1, vargs.data(), vargs.size());
+ commandBuffer[1 + vargs.size()] = data.size();
+ } else {
+ commandBuffer[1] = data.size();
+ }
+
+ if (RF_CALL(this->write(commandBuffer, 2 + command.getVargsCount()))) {
+ RF_RETURN_CALL(this->write(&data[0], data.size()));
+ }
+
+ RF_END_RETURN(false);
+}
+
+// ----------------------------------------------------------------------------
+
+template < class Uart >
+modm::ResumableResult
+Sx128xTransportUart::readCommand(Command command, std::span data)
+{
+ RF_BEGIN();
+
+ command.getOpcode(commandBuffer);
+ if (command.hasVargs()) {
+ auto vargs = command.getVargs();
+ std::memcpy(commandBuffer + 1, vargs.data(), vargs.size());
+ commandBuffer[1 + vargs.size()] = data.size();
+ } else {
+ commandBuffer[1] = data.size();
+ }
+
+ if (RF_CALL(this->write(commandBuffer, 2 + command.getVargsCount()))) {
+ RF_RETURN_CALL(this->read(&data[0], data.size()));
+ }
+
+ RF_END_RETURN(false);
+}
+
+} // namespace modm
\ No newline at end of file