diff --git a/radio_sx127x_spi/radio_sx127x_spi.cc b/radio_sx127x_spi/radio_sx127x_spi.cc new file mode 100644 index 0000000..d9c66a6 --- /dev/null +++ b/radio_sx127x_spi/radio_sx127x_spi.cc @@ -0,0 +1,239 @@ +#include "radio_sx127x_spi.h" + +RadioSx127xSpi::RadioSx127xSpi(SPI_HandleTypeDef *hspi, GPIO_TypeDef *csPort, uint16_t csPin, GPIO_TypeDef *rstPort, uint16_t rstPin, uint8_t syncWord, RfPort rfPort, unsigned int frequency, + unsigned int transmitPower, RampTime rampTime, Bandwidth bandwidth, CodingRate codingRate, SpreadingFactor spreadingFactor, unsigned int preambleLength, + unsigned int payloadLength, bool crcEnable, unsigned int txTimeout, unsigned int rxTimeout) + : _hspi(hspi), + _csPort(csPort), + _csPin(csPin), + _rstPort(rstPort), + _rstPin(rstPin), + _syncWord(syncWord), + _rfPort(rfPort), + _frequency(frequency), + _transmitPower(transmitPower), + _rampTime(rampTime), + _bandwidth(bandwidth), + _codingRate(codingRate), + _spreadingFactor(spreadingFactor), + _preambleLength(preambleLength), + _payloadLength(payloadLength), + _crcEnable(crcEnable), + _txTimeout(txTimeout), + _rxTimeout(rxTimeout) {} + +bool RadioSx127xSpi::Reset() { + HAL_GPIO_WritePin(_rstPort, _rstPin, GPIO_PIN_RESET); + HAL_Delay(1); + HAL_GPIO_WritePin(_rstPort, _rstPin, GPIO_PIN_SET); + HAL_Delay(5); + return true; +} + +RadioSx127xSpi::State RadioSx127xSpi::Init() { + _state = IDLE; + + // sleep mode + if (WriteRegister(0x01, 0b00000000) == false) _state = ERROR; + + // LoRa mode, set frequency mode + if (WriteRegister(0x01, 0b10000000 | ((uint8_t)_rfPort << 3)) == false) _state = ERROR; + + // set carrier frequency + uint32_t f = (uint64_t)_frequency * 524288ULL / 32000000ULL; + if (WriteRegister(0x06, (uint8_t)(f >> 16)) == false) _state = ERROR; + if (WriteRegister(0x07, (uint8_t)(f >> 8)) == false) _state = ERROR; + if (WriteRegister(0x08, (uint8_t)f) == false) _state = ERROR; + + // use PA_BOOST, set transmit power + if (WriteRegister(0x09, 0b10000000 | (uint8_t)(_transmitPower - 2)) == false) _state = ERROR; + + // set PA ramp time + if (WriteRegister(0x0A, (uint8_t)_rampTime) == false) _state = ERROR; + + // configure over-current protection + if (WriteRegister(0x0B, 0b00111011) == false) _state = ERROR; + + // configure LNA + if (WriteRegister(0x0C, 0b00100000) == false) _state = ERROR; + + // set FIFO TX base address + if (WriteRegister(0x0E, 0x80) == false) _state = ERROR; + + // set FIFO RX base address + if (WriteRegister(0x0F, 0x00) == false) _state = ERROR; + + // set bandwidth, set coding rate, use implicit header mode + if (WriteRegister(0x1D, 0b00000001 | ((uint8_t)_bandwidth << 4) | ((uint8_t)_codingRate << 1)) == false) _state = ERROR; + + // set spreading factor, use non-continuous mode, set CRC, set RX timeout MSB + if (WriteRegister(0x1E, ((uint8_t)_spreadingFactor << 4) | ((uint8_t)_crcEnable << 2) | ((uint8_t)(0x3 & (_rxTimeout >> 8)))) == false) _state = ERROR; + + // set RX timeout LSB + if (WriteRegister(0x1F, (uint8_t)_rxTimeout) == false) _state = ERROR; + + // set preamble length + if (WriteRegister(0x20, (uint8_t)(_preambleLength >> 8)) == false) _state = ERROR; + if (WriteRegister(0x21, (uint8_t)_preambleLength) == false) _state = ERROR; + + // set payload length + if (WriteRegister(0x22, (uint8_t)_payloadLength) == false) _state = ERROR; + + if (_spreadingFactor == SF6) { + // LoRa detection optimize + if (WriteRegister(0x31, 0x05) == false) _state = ERROR; + + // LoRa detection threshold + if (WriteRegister(0x37, 0x0C) == false) _state = ERROR; + } else { + // LoRa detection optimize + if (WriteRegister(0x31, 0x03) == false) _state = ERROR; + + // LoRa detection threshold + if (WriteRegister(0x37, 0x0A) == false) _state = ERROR; + } + + // set sync word + if (WriteRegister(0x39, (uint8_t)_syncWord) == false) _state = ERROR; + + // standby mode + if (WriteRegister(0x01, 0b10000001 | ((uint8_t)_rfPort << 3)) == false) _state = ERROR; + + return _state; +} + +RadioSx127xSpi::State RadioSx127xSpi::Transmit(const uint8_t *payload) { + switch (_state) { + case IDLE: { + _state = TX; + + // set FIFO address pointer + if (WriteRegister(0x0D, 0x80) == false) _state = ERROR; + + // write payload to FIFO + HAL_GPIO_WritePin(_csPort, _csPin, GPIO_PIN_RESET); + if (HAL_SPI_Transmit(_hspi, (uint8_t *)(const uint8_t[]){0x80}, 1, SERIAL_TIMEOUT) != HAL_OK) _state = ERROR; + if (HAL_SPI_Transmit(_hspi, (uint8_t *)payload, _payloadLength, SERIAL_TIMEOUT) != HAL_OK) _state = ERROR; + HAL_GPIO_WritePin(_csPort, _csPin, GPIO_PIN_SET); + + // start TX + if (WriteRegister(0x01, 0b10000011 | ((uint8_t)_rfPort << 3)) == false) _state = ERROR; + + _txStartTime = HAL_GetTick(); + + break; + } + + case TX: { + uint8_t irqFlags = 0x00; + if (ReadRegister(0x12, &irqFlags) == false) _state = ERROR; + + if ((irqFlags & 0x08) != 0) { + _state = TX_COMPLETE; + + // clear TxDone IRQ + if (WriteRegister(0x12, 0x08) == false) _state = ERROR; + } else if (HAL_GetTick() - _txStartTime > _txTimeout) { + _state = TX_TIMEOUT; + } + break; + } + + default: + break; + } + return _state; +} + +RadioSx127xSpi::State RadioSx127xSpi::Receive(uint8_t *payload, int *rssi) { + switch (_state) { + case IDLE: { + _state = RX; + + // start RX single + if (WriteRegister(0x01, 0b10000110 | ((uint8_t)_rfPort << 3)) == false) _state = ERROR; + + break; + } + + case RX: { + uint8_t irqFlags = 0x00; + if (ReadRegister(0x12, &irqFlags) == false) _state = ERROR; + + if ((irqFlags & 0x40) != 0) { + _state = RX_COMPLETE; + + // standby mode + if (WriteRegister(0x01, 0b10000001 | ((uint8_t)_rfPort << 3)) == false) _state = ERROR; + + // clear RxDone IRQ + if (WriteRegister(0x12, 0x40) == false) _state = ERROR; + + // check valid CRC + if ((irqFlags & 0x20) != 0) { + _state = RX_CRC_ERROR; + + // clear CRC Error IRQ + if (WriteRegister(0x12, 0x20) == false) _state = ERROR; + + break; + } + + // set FIFO address pointer + if (WriteRegister(0x0D, 0x00) == false) _state = ERROR; + + // read payload from FIFO + HAL_GPIO_WritePin(_csPort, _csPin, GPIO_PIN_RESET); + if (HAL_SPI_Transmit(_hspi, (uint8_t *)(const uint8_t[]){0x00}, 1, SERIAL_TIMEOUT) != HAL_OK) _state = ERROR; + if (HAL_SPI_Receive(_hspi, (uint8_t *)payload, _payloadLength, SERIAL_TIMEOUT) != HAL_OK) _state = ERROR; + HAL_GPIO_WritePin(_csPort, _csPin, GPIO_PIN_SET); + + // read RSSI + uint8_t data; + if (ReadRegister(0x1A, &data) == false) _state = ERROR; + if (_rfPort == HF) { + *rssi = data - 157; + } else if (_rfPort == LF) { + *rssi = data - 164; + } + } else if (irqFlags & 0x80) { + _state = RX_TIMEOUT; + + // clear RxTimeout IRQ + if (WriteRegister(0x12, 0x80) == false) _state = ERROR; + } + break; + } + + default: + break; + } + return _state; +} + +RadioSx127xSpi::State RadioSx127xSpi::ClearState() { + _state = IDLE; + + // clear all IRQ + if (WriteRegister(0x12, 0xFF) == false) _state = ERROR; + + return _state; +} + +bool RadioSx127xSpi::ReadRegister(uint8_t address, uint8_t *data) { + bool status = true; + HAL_GPIO_WritePin(_csPort, _csPin, GPIO_PIN_RESET); + if (HAL_SPI_Transmit(_hspi, &address, 1, SERIAL_TIMEOUT) != HAL_OK) status = false; + if (HAL_SPI_Receive(_hspi, data, 1, SERIAL_TIMEOUT) != HAL_OK) status = false; + HAL_GPIO_WritePin(_csPort, _csPin, GPIO_PIN_SET); + return status; +} + +bool RadioSx127xSpi::WriteRegister(uint8_t address, uint8_t data) { + bool status = true; + uint8_t payload[2] = {(uint8_t)(address | 0x80), data}; + HAL_GPIO_WritePin(_csPort, _csPin, GPIO_PIN_RESET); + if (HAL_SPI_Transmit(_hspi, payload, 2, SERIAL_TIMEOUT) != HAL_OK) status = false; + HAL_GPIO_WritePin(_csPort, _csPin, GPIO_PIN_SET); + return status; +} diff --git a/radio_sx127x_spi/radio_sx127x_spi.h b/radio_sx127x_spi/radio_sx127x_spi.h new file mode 100644 index 0000000..a02d371 --- /dev/null +++ b/radio_sx127x_spi/radio_sx127x_spi.h @@ -0,0 +1,184 @@ +#pragma once + +#if defined(STM32F1) +#include "stm32f1xx_hal.h" +#elif defined(STM32F4xx) +#include "stm32f4xx_hal.h" +#endif + +#ifndef SERIAL_TIMEOUT +#define SERIAL_TIMEOUT 10 +#endif + +class RadioSx127xSpi { + public: + enum State { IDLE, TX, TX_COMPLETE, TX_TIMEOUT, RX, RX_COMPLETE, RX_CRC_ERROR, RX_TIMEOUT, ERROR }; + + enum RfPort { + // high frequency port 779-1020 MHz + HF = 0, + // low frequency port 137-525 MHz + LF = 1 + }; + + enum RampTime { + // 3.4 ms + RT3M4S = 0, + // 2 ms + RT2MS = 1, + // 1 ms + RT1MS = 2, + // 500 us + RT500US = 3, + // 250 us + RT250US = 4, + // 125 us + RT125US = 5, + // 100 us + RT100US = 6, + // 62 us + RT62US = 7, + // 50 us + RT50US = 8, + // 40 us + RT40US = 9, + // 31 us + RT31US = 10, + // 25 us + RT25US = 11, + // 20 us + RT20US = 12, + // 15 us + RT15US = 13, + // 12 us + RT12US = 14, + // 10 us + RT10US = 15 + }; + + enum Bandwidth { + // 7.8 kHz + BW7K8HZ = 0, + // 10.4 kHz + BW10K4HZ = 1, + // 15.6 kHz + BW15K6HZ = 2, + // 20.8 kHz + BW20K8HZ = 3, + // 31.25 kHz + BW31K25HZ = 4, + // 41.7 kHz + BW41K7HZ = 5, + // 62.5 kHz + BW62K5HZ = 6, + // 125 kHz + BW125KHZ = 7, + // 250 kHz + BW250KHZ = 8, + // 500 kHz + BW500KHZ = 9 + }; + + enum CodingRate { + // 4/5 + CR45 = 1, + // 4/6 + CR46 = 2, + // 4/7 + CR47 = 3, + // 4/8 + CR48 = 4 + }; + + enum SpreadingFactor { SF6 = 6, SF7 = 7, SF8 = 8, SF9 = 9, SF10 = 10, SF11 = 11, SF12 = 12 }; + + /** + * @param hspi SPI bus handler + * @param csPort chip select GPIO port + * @param csPin chip select GPIO pin + * @param rstPort reset GPIO port + * @param rstPin reset GPIO pin + * @param syncWord LoRa sync word + * @param rfPort transponder physical RF port + * @param frequency RF center frequency in Hz, valid range 137000000-525000000, 779000000-1020000000 + * @param transmitPower transmit power in dBm, valid range 2-15 + * @param rampTime PA ramp time + * @param bandwidth signal bandwidth + * @param codingRate error coding rate + * @param spreadingFactor spreading factor rate + * @param preambleLength preamble length in symbols, valid range 6-65535 + * @param payloadLength packet length in bytes, valid range 1-255 + * @param crcEnable enable CRC + * @param txTimeout TX timeout in milliseconds + * @param rxTimeout RX timeout in number of symbols, valid range 4-1023, timeout in seconds = rxTimeout * (2 ** spreadingFactor) / bandwidth + */ + RadioSx127xSpi(SPI_HandleTypeDef *hspi, GPIO_TypeDef *csPort, uint16_t csPin, GPIO_TypeDef *rstPort, uint16_t rstPin, uint8_t syncWord, RfPort rfPort, unsigned int frequency, + unsigned int transmitPower, RampTime rampTime, Bandwidth bandwidth, CodingRate codingRate, SpreadingFactor spreadingFactor, unsigned int preambleLength, unsigned int payloadLength, + bool crcEnable, unsigned int txTimeout, unsigned int rxTimeout); + + /** + * @brief Resets radio + * @retval Operation status, true for success + */ + bool Reset(); + + /** + * @brief Initializes and configures radio + * @retval Current state + */ + State Init(); + + /** + * @brief Transmits payload + * @param payload pointer to payload buffer, must be the same length as specified by payloadLength + * @retval Current state, state is IDLE when transmit complete + */ + State Transmit(const uint8_t *payload); + + /** + * @brief Listens and receives one packet + * @param payload pointer to payload buffer, must be the same length as specified by payloadLength + * @param rssi received signal strength indication, this is an output + * @retval Current state, state is RX_COMPLETE when a new valid packet is received, state is IDLE when timeout or CRC error + */ + State Receive(uint8_t *payload, int *rssi); + + /** + * @brief Resets state to IDLE + * @retval Current state + */ + State ClearState(); + + private: + /** + * @retval Operation status, true for success + */ + bool ReadRegister(uint8_t address, uint8_t *data); + + /** + * @retval Operation status, true for success + */ + bool WriteRegister(uint8_t address, uint8_t data); + + SPI_HandleTypeDef *_hspi; + GPIO_TypeDef *_csPort; + uint16_t _csPin; + GPIO_TypeDef *_rstPort; + uint16_t _rstPin; + uint8_t _syncWord; + RfPort _rfPort; + unsigned int _frequency; + unsigned int _transmitPower; + RampTime _rampTime; + Bandwidth _bandwidth; + CodingRate _codingRate; + SpreadingFactor _spreadingFactor; + unsigned int _preambleLength; + unsigned int _payloadLength; + bool _crcEnable; + unsigned int _txTimeout; + unsigned int _rxTimeout; + + uint32_t _txStartTime; + State _state; +};