|
@@ -0,0 +1,393 @@
|
|
|
+/**
|
|
|
+ * @file sx1262.cpp
|
|
|
+ * @brief implementation file for the SX1262 transceiver class.
|
|
|
+ *
|
|
|
+ * @author Julia CABARBAYE (Student, ENSIL-ENSCI [ELT], France)
|
|
|
+ * @author Adrien CHEVRIER (Student, ENSIL-ENSCI [ELT], France)
|
|
|
+ *
|
|
|
+ * @date Created on: 2024-11-25
|
|
|
+ * @date Last updated: 2025-01-05
|
|
|
+ *
|
|
|
+ * @see sx1262.h
|
|
|
+ * @see { @link https://www.semtech.fr/products/wireless-rf/lora-connect/sx1262 }
|
|
|
+ * for detailed information about the SX1262.
|
|
|
+ */
|
|
|
+
|
|
|
+/// Include class header
|
|
|
+#include "sx1262.h"
|
|
|
+#include "logger.h"
|
|
|
+#include "board.h"
|
|
|
+
|
|
|
+extern logging::Logger logger;
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Begin the SPI commmunication with the SX1262 transceiver.
|
|
|
+ */
|
|
|
+void Sx1262::beginSPI() const
|
|
|
+{
|
|
|
+ SPI.begin(Sx1262::getSclk(), Sx1262::getMiso(), Sx1262::getMosi(), Sx1262::getCs());
|
|
|
+ pinMode(Sx1262::getCs(), OUTPUT);
|
|
|
+ digitalWrite(Sx1262::getCs(), HIGH);
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Get the reset pin.
|
|
|
+ * @return The reset pin number.
|
|
|
+ */
|
|
|
+uint8_t Sx1262::getRst() const
|
|
|
+{
|
|
|
+ return _rst;
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Get the specified DIO pin.
|
|
|
+ * @param n The DIO pin index (1-3). Default is 1.
|
|
|
+ * @return The DIO pin number.
|
|
|
+ */
|
|
|
+uint8_t Sx1262::getDio(uint8_t n) const
|
|
|
+{
|
|
|
+ uint8_t dioPin;
|
|
|
+ switch(n)
|
|
|
+ {
|
|
|
+ case 1: dioPin = _dio1; break;
|
|
|
+ case 2: dioPin = _dio2; break;
|
|
|
+ case 3: dioPin = _dio3; break;
|
|
|
+ default: dioPin = _dio1; break;
|
|
|
+ }
|
|
|
+ return dioPin;
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Get the SPI clock pin.
|
|
|
+ * @return The SPI clock pin number.
|
|
|
+ */
|
|
|
+uint8_t Sx1262::getSclk() const
|
|
|
+{
|
|
|
+ return _sclk;
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Get the SPI MOSI pin.
|
|
|
+ * @return The SPI MOSI pin number.
|
|
|
+ */
|
|
|
+uint8_t Sx1262::getMosi() const
|
|
|
+{
|
|
|
+ return _mosi;
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Get the SPI MISO pin.
|
|
|
+ * @return The SPI MISO pin number.
|
|
|
+ */
|
|
|
+uint8_t Sx1262::getMiso() const
|
|
|
+{
|
|
|
+ return _miso;
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Get the chip select pin.
|
|
|
+ * @return The chip select pin number.
|
|
|
+ */
|
|
|
+uint8_t Sx1262::getCs() const
|
|
|
+{
|
|
|
+ return _cs;
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Reset the Sx1262 device.
|
|
|
+ */
|
|
|
+void Sx1262::reset() const
|
|
|
+{
|
|
|
+ for (uint32_t delay = 0; delay < 0xFFF; delay++)
|
|
|
+ {
|
|
|
+ /// The reset pin operates with inverted logic
|
|
|
+ digitalWrite(_rst, LOW);
|
|
|
+ }
|
|
|
+ for (uint32_t delay = 0; delay < 0xFFFFF; delay++)
|
|
|
+ {
|
|
|
+ digitalWrite(_rst, HIGH);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Introduce an uninterruptible delay.
|
|
|
+ *
|
|
|
+ * @details This member calls `digitalRead()` to read the SX1276 RST pin value.
|
|
|
+ * `digitalRead()` utilizes interrupts to work. When calling `wait()`,
|
|
|
+ * this ensures that other interrupt events are temporarily blocked,
|
|
|
+ * allowing the SX1276 registers to be configured without interference.
|
|
|
+ *
|
|
|
+ * @warning This member should only be used within other members of the
|
|
|
+ * Sx1276 class, as blocking interrupts may cause issues with FreeRTOS.
|
|
|
+ * Make sure that the delay remains short if you plan to use them in
|
|
|
+ * members that would be executed in a FreeRTOS task.
|
|
|
+ *
|
|
|
+ * @param delay The delay duration in loop rounds.
|
|
|
+ */
|
|
|
+void Sx1262::wait(uint32_t delay) const
|
|
|
+{
|
|
|
+ for (uint32_t i = 0; i < delay; i++)
|
|
|
+ {
|
|
|
+ digitalRead(_rst);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Read a register value.
|
|
|
+ * @param reg The register address.
|
|
|
+ */
|
|
|
+uint8_t Sx1262::regRead(uint16_t reg) const
|
|
|
+{
|
|
|
+ /**
|
|
|
+ * Standard method to read data from a device at a specified address via SPI.
|
|
|
+ * pareil, on a un adressage en 16 bits donc on split en 2 adresses sur 8 bits
|
|
|
+ */
|
|
|
+ uint8_t ret;
|
|
|
+ digitalWrite(_cs, LOW); ///< Lower Chip Select pin
|
|
|
+ SPI.transfer(SX126X_READ_REGISTER); ///< Write the register address with a read-enabled flag
|
|
|
+ SPI.transfer((reg >> 8) & 0xFF); ///< Write the register address bit [15:8]
|
|
|
+ SPI.transfer(reg & 0xFF); ///< Write the register address bit [7:0]
|
|
|
+ // read SPI.transfert until it equals 0x00
|
|
|
+ if (SPI.transfer(0x00) == 0xAA) {
|
|
|
+ ret = SPI.transfer(0x00); ///< Read the value at the register address
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ ret = 0x00;
|
|
|
+ }
|
|
|
+ digitalWrite(_cs, HIGH); ///< Raise Chip Select pin
|
|
|
+ return ret;
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Write a value to a register.
|
|
|
+ * @param reg The register address.
|
|
|
+ * @param val The value to write.
|
|
|
+ */
|
|
|
+void Sx1262::regWrite(uint16_t reg, uint8_t val) const
|
|
|
+{
|
|
|
+ /**
|
|
|
+ * Standard method to write data to a device at a specified address via SPI.
|
|
|
+ * on a une adresse sur 16 bits, on va donc split en 2 adresses sur 8 bits
|
|
|
+ */
|
|
|
+ digitalWrite(_cs, LOW); ///< Lower Chip Select pin
|
|
|
+ SPI.transfer(SX126X_WRITE_REGISTER);
|
|
|
+ SPI.transfer((reg >> 8) & 0xFF); ///< Write the register address bit [15:8]
|
|
|
+ SPI.transfer(reg & 0xFF); ///< Write the register address bit [7:0]
|
|
|
+ SPI.transfer(val); ///< Write the value at the register address
|
|
|
+ digitalWrite(_cs, HIGH); ///< Raise Chip Select pin
|
|
|
+};
|
|
|
+
|
|
|
+/*
|
|
|
+ * @brief Method to read the Status Command Communication
|
|
|
+ * 11.5 Status Commands in datasheet SX1261-2
|
|
|
+ * @param opcode The opcode of the status command
|
|
|
+ * @return The status
|
|
|
+ *
|
|
|
+ * on aura le Status byte en position 1 tout le temps pour chaque opcode
|
|
|
+ * les autres bytes sont les réponses fonction de l'opcode
|
|
|
+ * on va donc aprser le StatusByte pour vérifier que tout s'est bien passé
|
|
|
+ * puis on va lire le reste de la réponse
|
|
|
+ *
|
|
|
+ */
|
|
|
+uint8_t Sx1262::readStatusCommand(uint8_t opcode) const
|
|
|
+{
|
|
|
+ digitalWrite(_cs, LOW);
|
|
|
+ SPI.transfer(opcode);
|
|
|
+ uint8_t statusByte = SPI.transfer(0x00);
|
|
|
+
|
|
|
+ // Le 1er byte de réponse c'est le statusCode
|
|
|
+ // on va parser tout ça
|
|
|
+ uint8_t cmdStatus = (statusByte & 0x0E) >> 1;
|
|
|
+ uint8_t cmdChipMode = (statusByte & 0x70) >> 4;
|
|
|
+ logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "SX1262", "cmdStatus : 0x%02X", cmdStatus);
|
|
|
+ logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "SX1262", "cmdChipMode : 0x%02X", cmdChipMode);
|
|
|
+
|
|
|
+ //on parse le status byte pour vérifier que tout s'est bien passé
|
|
|
+ if (cmdStatus == 0x02) logger.log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, "[SX1262]", "cmdStatus : Data is available to host");
|
|
|
+ else if (cmdStatus == 0x03) logger.log(logging::LoggerLevel::LOGGER_LEVEL_WARN, "[SX1262]", "cmdStatus : Command timeout");
|
|
|
+ else if (cmdStatus == 0x04) logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "[SX1262]", "cmdStatus : Command processing error");
|
|
|
+ else if (cmdStatus == 0x05) logger.log(logging::LoggerLevel::LOGGER_LEVEL_ERROR, "[SX1262]", "cmdStatus : Failure to execute command");
|
|
|
+ else if (cmdStatus == 0x06) logger.log(logging::LoggerLevel::LOGGER_LEVEL_INFO, "[SX1262]", "cmdStatus : Command TX done");
|
|
|
+ else return -1;
|
|
|
+
|
|
|
+ // on parse le mode du chip pour vérifier que tout s'est bien passé
|
|
|
+ if (cmdChipMode == 0x02) logger.log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, "[SX1262]", "cmdChipMode : STBY_RC ");
|
|
|
+ else if (cmdChipMode == 0x03) logger.log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, "[SX1262]", "cmdChipMode : STBY_XOSC ");
|
|
|
+ else if (cmdChipMode == 0x04) logger.log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, "[SX1262]", "cmdChipMode : FS");
|
|
|
+ else if (cmdChipMode == 0x05) logger.log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, "[SX1262]", "cmdChipMode : RX");
|
|
|
+ else if (cmdChipMode == 0x06) logger.log(logging::LoggerLevel::LOGGER_LEVEL_DEBUG, "[SX1262]", "cmdChipMode : TX");
|
|
|
+ else return -1;
|
|
|
+
|
|
|
+ switch (opcode) {
|
|
|
+ case (SX126X_GET_RX_BUFFER_STATUS): {
|
|
|
+ uint8_t rxBufferStatus = SPI.transfer(0x00);
|
|
|
+ uint8_t rxBufferPayloadLenghtRx = SPI.transfer(0x00);
|
|
|
+ uint8_t rxBufferRxStartBufferPointer = SPI.transfer(0x00);
|
|
|
+ // todo print the log
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ case (SX126X_GET_PKT_STATUS): {
|
|
|
+ uint8_t rxStatus = SPI.transfer(0x00);
|
|
|
+ uint8_t rssiSync = SPI.transfer(0x00);
|
|
|
+ uint8_t rssiAvg = SPI.transfer(0x00);
|
|
|
+ // todo print the log
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ case (SX126X_GET_RSSI_INST): {
|
|
|
+ uint8_t rssiValue = SPI.transfer(0x00);
|
|
|
+ // todo print the log
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ case (SX126X_GET_STATS): {
|
|
|
+ uint8_t statsPktReceived2 = SPI.transfer(0x00);
|
|
|
+ uint8_t statsPktReceived1 = SPI.transfer(0x00);
|
|
|
+ uint8_t statsPktError2 = SPI.transfer(0x00);
|
|
|
+ uint8_t statsPktError1 = SPI.transfer(0x00);
|
|
|
+ uint8_t NbPktLengthError2 = SPI.transfer(0x00);
|
|
|
+ uint8_t NbPktLengthError1 = SPI.transfer(0x00);
|
|
|
+ // todo print the log
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ default:
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ }
|
|
|
+ // hé oui, on lache le trig ;)
|
|
|
+ digitalWrite(_cs, HIGH);
|
|
|
+ return 0;
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Set the transceiver carrier frequency to work on.
|
|
|
+ * @param freq The carrier frequency in Hz.
|
|
|
+ * @return `0` if successful, `-1` otherwise.
|
|
|
+ */
|
|
|
+int8_t Sx1262::setFreq(uint32_t freq) const
|
|
|
+{
|
|
|
+ int8_t exitCode = 0;
|
|
|
+ /**
|
|
|
+ * @details Carrier frequency conversion for the SX1262 registers
|
|
|
+ * as specified into the datasheet. The covertyed frequency
|
|
|
+ * must stands on 24 bits.
|
|
|
+ */
|
|
|
+ /*
|
|
|
+ uint32_t tmpFreq = static_cast<uint32_t>(
|
|
|
+ round(freq * sx1262::radio_div / sx1262::radio_clk)
|
|
|
+ );
|
|
|
+ if (tmpFreq > 0x00FFFFFF)
|
|
|
+ {
|
|
|
+ /// Handeling too high converted carrier frequency
|
|
|
+ tmpFreq = 0x00FFFFFF;
|
|
|
+ exitCode = -1;
|
|
|
+ }
|
|
|
+ */
|
|
|
+ /**
|
|
|
+ * @details Extract the frequency bytes (MSB, MID, LSB) using bitwise masks.
|
|
|
+ *
|
|
|
+ * @example With the carrier frequency MSB
|
|
|
+ *
|
|
|
+ * tempFreq: 0x0012AC7E
|
|
|
+ * & 0x00FF0000 (bitwise AND to take the MSB)
|
|
|
+ * --------------
|
|
|
+ * 0x00120000
|
|
|
+ * >> 16 (right shifting on 16 bits)
|
|
|
+ * --------------
|
|
|
+ * 0x00000012 => REG_FRFMSB
|
|
|
+ */
|
|
|
+ /*
|
|
|
+ uint8_t msb = (tmpFreq & 0x00FF0000) >> 16;
|
|
|
+ regWrite(REG_FRFMSB, msb);
|
|
|
+ uint8_t mid = (tmpFreq & 0x0000FF00) >> 8;
|
|
|
+ regWrite(REG_FRFMID, mid);
|
|
|
+ uint8_t lsb = tmpFreq & 0x000000FF;
|
|
|
+ regWrite(REG_FRFLSB, lsb);
|
|
|
+ */
|
|
|
+ return exitCode;
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Get the FIFO buffer size.
|
|
|
+ * @return The FIFO size in bytes.
|
|
|
+ */
|
|
|
+uint8_t Sx1262::getFifoSize() const
|
|
|
+{
|
|
|
+ return 0; //regRead(SX126X_REG_RXTX_PAYLOAD_LEN);
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Perform a FIFO read/write operation.
|
|
|
+ *
|
|
|
+ * @param mode The operation mode (0x00 to read, 0xFF to write).
|
|
|
+ * @param buff The buffer to read/write (as a span of bytes).
|
|
|
+ *
|
|
|
+ * @return `0` if successful.
|
|
|
+ * @return `-1` if the buffer is too short.
|
|
|
+ * @return `-2` if the operation mode is wrong.
|
|
|
+ *
|
|
|
+ * @note A writting should be performed only if the SX1276 is in idle mode.
|
|
|
+ * @see At namespace sx1276: fifo_read and fifo_write
|
|
|
+ * @see rxToStdby()
|
|
|
+ */
|
|
|
+int8_t Sx1262::fifo(uint8_t mode, uint8_t *buff) const
|
|
|
+{
|
|
|
+ int8_t exitCode = 0;
|
|
|
+ /*
|
|
|
+ uint8_t sz = getFifoSize();
|
|
|
+ if (mode == sx1262::fifo_read)
|
|
|
+ {
|
|
|
+ if (sz + 1 <= sizeof(buff))
|
|
|
+ {
|
|
|
+ /// Reading by using special internal FIFO buffer SPI burst method
|
|
|
+ digitalWrite(_cs, LOW);
|
|
|
+ SPI.transfer(REG_FIFO);
|
|
|
+ for (uint8_t i = 0; i < sz; i++)
|
|
|
+ {
|
|
|
+ /// Byte-per-byte reading without the need to reselect the address
|
|
|
+ buff[i] = SPI.transfer(0x00);
|
|
|
+ }
|
|
|
+ digitalWrite(_cs, HIGH);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ /// Handeling too short buffer
|
|
|
+ exitCode = -1;
|
|
|
+ }
|
|
|
+ else if (mode == sx1262::fifo_write)
|
|
|
+ {
|
|
|
+ /// Writting by using special internal FIFO buffer SPI burst method
|
|
|
+ regWrite(REG_OPMODE, 0x01);
|
|
|
+ wait(0xFFFF);
|
|
|
+ digitalWrite(_cs, LOW);
|
|
|
+ SPI.transfer(REG_FIFO | 0x80);
|
|
|
+ for (uint8_t i = 0; i < sz; i++)
|
|
|
+ {
|
|
|
+ /// Byte-per-byte writting without the need to reselect the address
|
|
|
+ SPI.transfer(buff[i]);
|
|
|
+ }
|
|
|
+ digitalWrite(_cs, HIGH);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ /// Handeling wrong modes
|
|
|
+ exitCode = -2;
|
|
|
+
|
|
|
+ */
|
|
|
+ return exitCode;
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+int8_t Sx1262::setTxConfig(uint32_t freq, uint8_t debug) const
|
|
|
+{
|
|
|
+ int8_t exitCode = 0;
|
|
|
+ regWrite(0x01, 0x01);
|
|
|
+ wait(0xFFFF);
|
|
|
+
|
|
|
+
|
|
|
+ return exitCode;
|
|
|
+};
|
|
|
+
|