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