From d214fb71141f8cc263303105a2d17ddcfca505ff Mon Sep 17 00:00:00 2001 From: rasmuskleist Date: Mon, 7 Aug 2023 20:12:45 +0200 Subject: [PATCH 1/3] [architecture] Adding UartDevice for resumable r/w --- .../architecture/interface/uart_device.hpp | 156 ++++++++++++++++++ src/modm/architecture/module.lb | 15 ++ 2 files changed, 171 insertions(+) create mode 100644 src/modm/architecture/interface/uart_device.hpp diff --git a/src/modm/architecture/interface/uart_device.hpp b/src/modm/architecture/interface/uart_device.hpp new file mode 100644 index 0000000000..1be7ff47b6 --- /dev/null +++ b/src/modm/architecture/interface/uart_device.hpp @@ -0,0 +1,156 @@ +/* + * 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_INTERFACE_UART_DEVICE_HPP +#define MODM_INTERFACE_UART_DEVICE_HPP + +#include "uart.hpp" +#include + +namespace modm +{ + +/** + * Base class of an UART Device. + * + * This class provides generic transaction-like semantics + * + * @author Rasmus Kleist Hørlyck Sørensen + * @ingroup modm_architecture_uart_device + */ +template < class Uart, uint8_t NestingLevels = 10 > +class UartDevice : protected modm::NestedResumable< NestingLevels + 1 > +{ +public: + UartDevice() : + txTimeout(std::chrono::microseconds(1000)), + rxTimeout(std::chrono::microseconds(10000)) + { + } + + bool + hasReceived() + { + return Uart::receiveBufferSize() > 0; + } + + void + setTxTimeout(ShortPreciseDuration timeout) + { + txTimeout = timeout; + } + + void + setRxTimeout(ShortPreciseDuration timeout) + { + rxTimeout = timeout; + } + +protected: + modm::ResumableResult + write(uint8_t data) + { + RF_BEGIN(0); + + timeout.restart(txTimeout); + RF_WAIT_UNTIL(Uart::write(data) or timeout.isExpired() or Uart::hasError()); + if (timeout.isExpired() or Uart::hasError()) + { + Uart::discardTransmitBuffer(); + Uart::discardReceiveBuffer(); + Uart::clearError(); + RF_RETURN(false); + } + RF_END_RETURN(true); + } + + modm::ResumableResult + write(const uint8_t *data, std::size_t length) + { + RF_BEGIN(0); + + writeIndex = 0; + timeout.restart(txTimeout); + while (writeIndex < length) + { + if (size_t writeSize = Uart::write(&data[writeIndex], length - writeIndex)) + { + writeIndex += writeSize; + timeout.restart(txTimeout); + } + + if (timeout.isExpired() or Uart::hasError()) + { + Uart::discardReceiveBuffer(); + Uart::clearError(); + RF_RETURN(false); + } + RF_YIELD(); + } + + RF_END_RETURN(true); + } + + modm::ResumableResult + read(uint8_t &data) + { + RF_BEGIN(1); + + timeout.restart(rxTimeout); + RF_WAIT_UNTIL(Uart::read(data) or timeout.isExpired() or Uart::hasError()); + if (timeout.isExpired() or Uart::hasError()) + { + Uart::discardReceiveBuffer(); + Uart::clearError(); + RF_RETURN(false); + } + RF_END_RETURN(true); + } + + modm::ResumableResult + read(uint8_t *buffer, std::size_t length) + { + RF_BEGIN(1); + + readIndex = 0; + timeout.restart(rxTimeout); + while (readIndex < length) + { + if (size_t readSize = Uart::read(&buffer[readIndex], length - readIndex)) + { + readIndex += readSize; + timeout.restart(rxTimeout); + } + + if (timeout.isExpired() or Uart::hasError()) + { + Uart::discardReceiveBuffer(); + Uart::clearError(); + RF_RETURN(false); + } + RF_YIELD(); + } + + RF_END_RETURN(true); + } + +private: + std::size_t readIndex; + std::size_t writeIndex; + + ShortPreciseDuration txTimeout; + ShortPreciseDuration rxTimeout; + ShortPreciseTimeout timeout; +}; + +} // namespace modm + +#endif // MODM_INTERFACE_UART_DEVICE_HPP diff --git a/src/modm/architecture/module.lb b/src/modm/architecture/module.lb index aba5fe10b9..7ce177c87f 100644 --- a/src/modm/architecture/module.lb +++ b/src/modm/architecture/module.lb @@ -339,6 +339,20 @@ class Uart(Module): env.copy("interface/uart.hpp") # ----------------------------------------------------------------------------- +class UartDevice(Module): + def init(self, module): + module.name = "uart.device" + module.description = "UART Devices" + + def prepare(self, module, options): + module.depends(":architecture:uart") + return True + + def build(self, env): + env.outbasepath = "modm/src/modm/architecture" + env.copy("interface/uart_device.hpp") +# ----------------------------------------------------------------------------- + class Unaligned(Module): def init(self, module): module.name = "unaligned" @@ -386,6 +400,7 @@ def prepare(module, options): module.add_submodule(Spi()) module.add_submodule(SpiDevice()) module.add_submodule(Uart()) + module.add_submodule(UartDevice()) module.add_submodule(Unaligned()) return True From 194f3bc266c54efab23b6dcefd4959c3dd8bf241 Mon Sep 17 00:00:00 2001 From: rasmuskleist Date: Mon, 7 Aug 2023 20:14:09 +0200 Subject: [PATCH 2/3] [driver] Adding Semtech SX1280/SX1281 driver --- README.md | 5 +- src/modm/driver/radio/sx128x.hpp | 247 ++++ src/modm/driver/radio/sx128x.lb | 40 + src/modm/driver/radio/sx128x_definitions.hpp | 1002 +++++++++++++++++ src/modm/driver/radio/sx128x_impl.hpp | 608 ++++++++++ src/modm/driver/radio/sx128x_transport.hpp | 138 +++ .../driver/radio/sx128x_transport_impl.hpp | 162 +++ 7 files changed, 2200 insertions(+), 2 deletions(-) create mode 100644 src/modm/driver/radio/sx128x.hpp create mode 100644 src/modm/driver/radio/sx128x.lb create mode 100644 src/modm/driver/radio/sx128x_definitions.hpp create mode 100644 src/modm/driver/radio/sx128x_impl.hpp create mode 100644 src/modm/driver/radio/sx128x_transport.hpp create mode 100644 src/modm/driver/radio/sx128x_transport_impl.hpp 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 From 517bd845748d24f512c3159f9089e9e19b4c9fda Mon Sep 17 00:00:00 2001 From: rasmuskleist Date: Mon, 7 Aug 2023 20:15:01 +0200 Subject: [PATCH 3/3] [example] Adding SX128x LoRa example --- examples/nucleo_g474re/sx128x_lora/main.cpp | 278 ++++++++++++++++++ .../nucleo_g474re/sx128x_lora/project.xml | 15 + 2 files changed, 293 insertions(+) create mode 100644 examples/nucleo_g474re/sx128x_lora/main.cpp create mode 100644 examples/nucleo_g474re/sx128x_lora/project.xml diff --git a/examples/nucleo_g474re/sx128x_lora/main.cpp b/examples/nucleo_g474re/sx128x_lora/main.cpp new file mode 100644 index 0000000000..54b0282ed2 --- /dev/null +++ b/examples/nucleo_g474re/sx128x_lora/main.cpp @@ -0,0 +1,278 @@ +// 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/. + */ +// ---------------------------------------------------------------------------- + +#include +#include + +#include +#include +#include +#include +#include + +using Sck = GpioA5; +using Miso = GpioA6; +using Mosi = GpioA7; +using SpiMaster = modm::platform::SpiMaster1; + +namespace +{ + +namespace rx +{ + std::atomic_bool dio1 = false; + std::atomic_bool dio2 = false; + std::atomic_bool dio3 = false; +} + +namespace tx +{ + std::atomic_bool dio1 = false; + std::atomic_bool dio2 = false; + std::atomic_bool dio3 = false; +} + +static constexpr modm::sx128x::LoRa::ModulationParams modulationParams = { + .spreadingFactor = modm::sx128x::LoRa::ModulationParams::SpreadingFactor::Sf9, + .bandwidth = modm::sx128x::LoRa::ModulationParams::Bandwidth::Bw400, + .codingRate = modm::sx128x::LoRa::ModulationParams::CodingRate::Cr_Li_4_7 +}; + +static constexpr modm::sx128x::LoRa::PacketParams packetParams = { + .preambleLength = 12, + .headerType = modm::sx128x::LoRa::PacketParams::HeaderType::Explicit, + .payloadLength = 4, + .crc = modm::sx128x::LoRa::PacketParams::Crc::Enable, + .invertIq = modm::sx128x::LoRa::PacketParams::InvertIq::Standard +}; + +} + +class RxThread : public modm::sx128x, public modm::pt::Protothread +{ + using Reset = GpioB3; + using Busy = GpioB4; + using Dio1 = GpioB5; + using Dio2 = GpioB6; + using Dio3 = GpioB7; + + using Nss = GpioD2; + using Transport = modm::Sx128xTransportSpi; + +public: + RxThread() {} + + inline bool + run() + { + PT_BEGIN(); + + Nss::setOutput(modm::Gpio::High); + Reset::setOutput(modm::Gpio::Low); + Busy::setInput(modm::platform::Gpio::InputType::PullDown); + + Dio1::setInput(modm::platform::Gpio::InputType::PullDown); + Exti::connect(Exti::Trigger::RisingEdge, [](uint8_t) { rx::dio1 = true; }); + + Dio2::setInput(modm::platform::Gpio::InputType::PullDown); + Exti::connect(Exti::Trigger::RisingEdge, [](uint8_t) { rx::dio2 = true; }); + + Dio3::setInput(modm::platform::Gpio::InputType::PullDown); + Exti::connect(Exti::Trigger::RisingEdge, [](uint8_t) { rx::dio3 = true; }); + + PT_CALL(radio.reset()); + PT_CALL(radio.setStandby()); + + // Initialize the sx128x + PT_CALL(radio.setPacketType(PacketType::LoRa)); + PT_CALL(radio.setRfFrequency(2457_MHz / radio.frequencyLsb)); + PT_CALL(radio.setRegulatorMode(RegulatorMode::Ldo)); + PT_CALL(radio.setBufferBaseAddress(0, 0)); + PT_CALL(radio.setModulationParams(modulationParams)); + PT_CALL(radio.writeRegister(Register::SfAdditionalConfiguration, 0x32)); + PT_CALL(radio.writeRegister(Register::FrequencyErrorCorrection, 0x01)); + PT_CALL(radio.setPacketParams(packetParams)); + PT_CALL(radio.setDioIrqParams(Irq::RxDone | Irq::RxTxTimeout, Irq::RxDone, Irq::RxTxTimeout)); + PT_CALL(radio.setRx(sx128x::PeriodBase::ms1, 1000)); + + MODM_LOG_DEBUG << "Sx128x initialization complete!" << modm::endl; + + while (true) + { + if (rx::dio1.exchange(false)) + { + PT_CALL(radio.getIrqStatus(&irqStatus)); + if (irqStatus.any(Irq::RxDone)) + { + PT_CALL(radio.clearIrqStatus(Irq::RxDone | Irq::RxTxTimeout)); + PT_CALL(radio.setRx(sx128x::PeriodBase::ms1, 1000)); + + PT_CALL(radio.getRxBufferStatus(&rxBufferStatus)); + PT_CALL(radio.getPacketStatus(&packetStatus)); + PT_CALL(radio.readBuffer(rxBufferStatus.rxStartBufferPointer, std::span{buffer, rxBufferStatus.rxPayloadLength})); + + if (rxBufferStatus.rxPayloadLength > 0) + { + uint32_t counter; + std::memcpy((uint8_t *) &counter, buffer, sizeof(counter)); + MODM_LOG_DEBUG << "Received Message" << modm::endl; + MODM_LOG_DEBUG << "Counter: " << counter << modm::endl; + } + } + } + + if (rx::dio2.exchange(false)) + { + PT_CALL(radio.getIrqStatus(&irqStatus)); + if (irqStatus.any(Irq::RxTxTimeout)) + { + // RxTxTimeout Interrupt received! + // Clear irq and set to rx again. + PT_CALL(radio.clearIrqStatus(Irq::RxTxTimeout)); + PT_CALL(radio.setRx(sx128x::PeriodBase::ms1, 1000)); + MODM_LOG_DEBUG << "RxTxTimeout Interrupt!" << modm::endl; + } + } + PT_YIELD(); + } + PT_END(); + } + +private: + Irq_t irqStatus; + PacketStatus packetStatus; + RxBufferStatus rxBufferStatus; + + modm::Sx128x< Transport, Reset, Busy > radio; + +private: + static constexpr size_t bufferSize = 256; + uint8_t buffer[bufferSize]; +} rxThread; + +class TxThread : public modm::sx128x, public modm::pt::Protothread +{ + using Reset = modm::platform::GpioC2; + using Busy = modm::platform::GpioC3; + using Dio1 = modm::platform::GpioA0; + using Dio2 = modm::platform::GpioA1; + using Dio3 = modm::platform::GpioA2; + + using Nss = modm::platform::GpioC1; + using Transport = modm::Sx128xTransportSpi; + +public: + TxThread() : timer(std::chrono::milliseconds(500)) {} + + inline bool + run() + { + PT_BEGIN(); + + Nss::setOutput(modm::Gpio::High); + Reset::setOutput(modm::Gpio::Low); + Busy::setInput(modm::platform::Gpio::InputType::PullDown); + + Dio1::setInput(modm::platform::Gpio::InputType::PullDown); + Exti::connect(Exti::Trigger::RisingEdge, [](uint8_t) { tx::dio1 = true; }); + + Dio2::setInput(modm::platform::Gpio::InputType::PullDown); + Exti::connect(Exti::Trigger::RisingEdge, [](uint8_t) { tx::dio2 = true; }); + + Dio3::setInput(modm::platform::Gpio::InputType::PullDown); + Exti::connect(Exti::Trigger::RisingEdge, [](uint8_t) { tx::dio3 = true; }); + + + PT_CALL(radio.reset()); + PT_CALL(radio.setStandby()); + + // Initialize the sx128x + PT_CALL(radio.setPacketType(PacketType::LoRa)); + PT_CALL(radio.setRfFrequency(2457_MHz / radio.frequencyLsb)); + PT_CALL(radio.setRegulatorMode(RegulatorMode::Ldo)); + PT_CALL(radio.setBufferBaseAddress(0, 0)); + PT_CALL(radio.setModulationParams(modulationParams)); + PT_CALL(radio.writeRegister(Register::SfAdditionalConfiguration, 0x32)); + PT_CALL(radio.writeRegister(Register::FrequencyErrorCorrection, 0x01)); + PT_CALL(radio.setPacketParams(packetParams)); + PT_CALL(radio.setDioIrqParams(Irq::TxDone | Irq::RxTxTimeout, Irq::TxDone, Irq::RxTxTimeout)); + + MODM_LOG_DEBUG << "Sx128x initialization complete!" << modm::endl; + + while (true) + { + if (tx::dio1.exchange(false)) + { + PT_CALL(radio.getIrqStatus(&irqStatus)); + if (irqStatus.any(Irq::TxDone)) + { + PT_CALL(radio.clearIrqStatus(Irq::TxDone)); + irqStatus.reset(Irq::TxDone); + + MODM_LOG_DEBUG << "Message sent" << modm::endl; + MODM_LOG_DEBUG << "Counter: " << counter << modm::endl; + counter++; + } + } + + if (tx::dio2.exchange(false)) + { + PT_CALL(radio.getIrqStatus(&irqStatus)); + if (irqStatus.any(Irq::RxTxTimeout)) + { + PT_CALL(radio.clearIrqStatus(Irq::RxTxTimeout)); + irqStatus.reset(Irq::RxTxTimeout); + MODM_LOG_DEBUG << "Received a timeout" << modm::endl; + } + } + + if (timer.execute()) + { + PT_CALL(radio.writeBuffer(0, std::span{(uint8_t *) &counter, sizeof(counter)})); + PT_CALL(radio.setTx(PeriodBase::ms1, 100)); + } + PT_YIELD(); + } + + PT_END(); + } + +private: + Irq_t irqStatus; + modm::Sx128x< Transport, Reset, Busy > radio; + + uint32_t counter = 0; + modm::PeriodicTimer timer; +}; + +int +main() +{ + Board::initialize(); + + SpiMaster::connect(); + SpiMaster::initialize(); + + MODM_LOG_INFO << "==========SX128x Test==========" << modm::endl; + MODM_LOG_DEBUG << "Debug logging here" << modm::endl; + MODM_LOG_INFO << "Info logging here" << modm::endl; + MODM_LOG_WARNING << "Warning logging here" << modm::endl; + MODM_LOG_ERROR << "Error logging here" << modm::endl; + MODM_LOG_INFO << "===============================" << modm::endl; + + while (true) + { + rxThread.run(); + } + + return 0; +} \ No newline at end of file diff --git a/examples/nucleo_g474re/sx128x_lora/project.xml b/examples/nucleo_g474re/sx128x_lora/project.xml new file mode 100644 index 0000000000..544f5d2171 --- /dev/null +++ b/examples/nucleo_g474re/sx128x_lora/project.xml @@ -0,0 +1,15 @@ + + modm:nucleo-g474re + + + + + modm:driver:sx128x + modm:platform:exti + modm:platform:gpio + modm:platform:spi:1 + modm:processing:protothread + modm:processing:timer + modm:build:scons + + \ No newline at end of file