parent
ebdec7f3e0
commit
3f8d012457
18 changed files with 4310 additions and 3895 deletions
@ -0,0 +1,7 @@ |
|||||||
|
#define SX127X 0x00 |
||||||
|
#define SX1276 0x01 |
||||||
|
#define SX1278 0x02 |
||||||
|
#define SX126X 0x10 |
||||||
|
#define SX1262 0x11 |
||||||
|
#define SX128X 0x20 |
||||||
|
#define SX1280 0x21 |
@ -1,4 +0,0 @@ |
|||||||
#define SX1276 0x01 |
|
||||||
#define SX1278 0x02 |
|
||||||
#define SX1262 0x03 |
|
||||||
#define SX1280 0x04 |
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,656 @@ |
|||||||
|
// Copyright (c) Sandeep Mistry. All rights reserved.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
// Modifications and additions copyright 2023 by Mark Qvist & Jacob Eva
|
||||||
|
// Obviously still under the MIT license.
|
||||||
|
|
||||||
|
#ifndef RADIO_H |
||||||
|
#define RADIO_H |
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
#include <SPI.h> |
||||||
|
#include "Interfaces.h" |
||||||
|
#include "Boards.h" |
||||||
|
|
||||||
|
// TX
|
||||||
|
#define PA_OUTPUT_RFO_PIN 0 |
||||||
|
#define PA_OUTPUT_PA_BOOST_PIN 1 |
||||||
|
|
||||||
|
// DCD
|
||||||
|
#define STATUS_INTERVAL_MS 3 |
||||||
|
#define DCD_SAMPLES 2500 |
||||||
|
#define UTIL_UPDATE_INTERVAL_MS 1000 |
||||||
|
#define UTIL_UPDATE_INTERVAL (UTIL_UPDATE_INTERVAL_MS/STATUS_INTERVAL_MS) |
||||||
|
#define AIRTIME_LONGTERM 3600 |
||||||
|
#define AIRTIME_LONGTERM_MS (AIRTIME_LONGTERM*1000) |
||||||
|
#define AIRTIME_BINLEN_MS (STATUS_INTERVAL_MS*DCD_SAMPLES) |
||||||
|
#define AIRTIME_BINS ((AIRTIME_LONGTERM*1000)/AIRTIME_BINLEN_MS) |
||||||
|
#define current_airtime_bin(void) (millis()%AIRTIME_LONGTERM_MS)/AIRTIME_BINLEN_MS |
||||||
|
#define DCD_THRESHOLD 2 |
||||||
|
#define DCD_LED_STEP_D 3 |
||||||
|
#define LORA_PREAMBLE_SYMBOLS_HW 4 |
||||||
|
#define LORA_PREAMBLE_SYMBOLS_MIN 18 |
||||||
|
#define LORA_PREAMBLE_TARGET_MS 15 |
||||||
|
|
||||||
|
#define RSSI_OFFSET 157 |
||||||
|
|
||||||
|
#define PHY_HEADER_LORA_SYMBOLS 8 |
||||||
|
|
||||||
|
#define _e 2.71828183 |
||||||
|
#define _S 10.0 |
||||||
|
|
||||||
|
// Status flags
|
||||||
|
const uint8_t SIG_DETECT = 0x01; |
||||||
|
const uint8_t SIG_SYNCED = 0x02; |
||||||
|
const uint8_t RX_ONGOING = 0x04; |
||||||
|
|
||||||
|
// forward declare Utilities.h LED functions
|
||||||
|
void led_rx_on(); |
||||||
|
void led_rx_off(); |
||||||
|
void led_indicate_airtime_lock(); |
||||||
|
|
||||||
|
#if PLATFORM == PLATFORM_ESP32 |
||||||
|
// get update_lock for ESP32
|
||||||
|
extern portMUX_TYPE update_lock; |
||||||
|
#endif |
||||||
|
|
||||||
|
class RadioInterface : public Stream { |
||||||
|
public: |
||||||
|
// todo: in the future define _spiModem and _spiSettings from here for inheritence by child classes
|
||||||
|
RadioInterface(uint8_t index) : _index(index), _radio_locked(false), |
||||||
|
_radio_online(false), _st_airtime_limit(0.0), _lt_airtime_limit(0.0), |
||||||
|
_airtime_lock(false), _airtime(0.0), _longterm_airtime(0.0), |
||||||
|
_local_channel_util(0.0), _total_channel_util(0.0), |
||||||
|
_longterm_channel_util(0.0), _last_status_update(0), _last_dcd(0), |
||||||
|
_stat_rx_ongoing(false), _stat_signal_detected(false), |
||||||
|
_stat_signal_synced(false), _dcd_count(0), _dcd(false), _dcd_led(false), |
||||||
|
_dcd_waiting(false), _dcd_wait_until(0), _dcd_sample(0), |
||||||
|
_post_tx_yield_timeout(0), _csma_slot_ms(50), _csma_p_min(0.1), |
||||||
|
_csma_p_max(0.8), _preambleLength(6), _lora_symbol_time_ms(0.0), |
||||||
|
_lora_symbol_rate(0.0), _lora_us_per_byte(0.0), _bitrate(0), |
||||||
|
_onReceive(NULL) {}; |
||||||
|
virtual int begin(); |
||||||
|
virtual void end(); |
||||||
|
|
||||||
|
virtual int beginPacket(int implicitHeader = false); |
||||||
|
virtual int endPacket(); |
||||||
|
|
||||||
|
virtual int packetRssi(); |
||||||
|
virtual int currentRssi(); |
||||||
|
virtual uint8_t packetRssiRaw(); |
||||||
|
virtual uint8_t currentRssiRaw(); |
||||||
|
virtual uint8_t packetSnrRaw(); |
||||||
|
virtual float packetSnr(); |
||||||
|
virtual long packetFrequencyError(); |
||||||
|
|
||||||
|
// from Print
|
||||||
|
virtual size_t write(uint8_t byte); |
||||||
|
virtual size_t write(const uint8_t *buffer, size_t size); |
||||||
|
|
||||||
|
// from Stream
|
||||||
|
virtual int available(); |
||||||
|
virtual int read(); |
||||||
|
virtual int peek(); |
||||||
|
virtual void flush(); |
||||||
|
|
||||||
|
virtual void onReceive(void(*callback)(uint8_t, int)); |
||||||
|
|
||||||
|
virtual void receive(int size = 0); |
||||||
|
virtual void standby(); |
||||||
|
virtual void sleep(); |
||||||
|
|
||||||
|
virtual bool preInit(); |
||||||
|
virtual uint8_t getTxPower(); |
||||||
|
virtual void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); |
||||||
|
virtual uint32_t getFrequency(); |
||||||
|
virtual void setFrequency(uint32_t frequency); |
||||||
|
virtual void setSpreadingFactor(int sf); |
||||||
|
virtual uint8_t getSpreadingFactor(); |
||||||
|
virtual uint32_t getSignalBandwidth(); |
||||||
|
virtual void setSignalBandwidth(uint32_t sbw); |
||||||
|
virtual void setCodingRate4(int denominator); |
||||||
|
virtual uint8_t getCodingRate4(); |
||||||
|
virtual void setPreambleLength(long length); |
||||||
|
virtual uint8_t modemStatus(); |
||||||
|
virtual void enableCrc(); |
||||||
|
virtual void disableCrc(); |
||||||
|
virtual void enableTCXO(); |
||||||
|
virtual void disableTCXO(); |
||||||
|
|
||||||
|
virtual byte random(); |
||||||
|
|
||||||
|
virtual void setSPIFrequency(uint32_t frequency); |
||||||
|
|
||||||
|
virtual void updateBitrate(); |
||||||
|
virtual void handleDio0Rise(); |
||||||
|
uint32_t getBitrate() { return _bitrate; }; |
||||||
|
uint8_t getIndex() { return _index; }; |
||||||
|
void setRadioLock(bool lock) { _radio_locked = lock; }; |
||||||
|
bool getRadioLock() { return _radio_locked; }; |
||||||
|
void setRadioOnline(bool online) { _radio_online = online; }; |
||||||
|
bool getRadioOnline() { return _radio_online; }; |
||||||
|
void setSTALock(float at) { _st_airtime_limit = at; }; |
||||||
|
float getSTALock() { return _st_airtime_limit; }; |
||||||
|
void setLTALock(float at) { _lt_airtime_limit = at; }; |
||||||
|
float getLTALock() { return _lt_airtime_limit; }; |
||||||
|
bool calculateALock() { |
||||||
|
_airtime_lock = false; |
||||||
|
if (_st_airtime_limit != 0.0 && _airtime >= _st_airtime_limit) { |
||||||
|
_airtime_lock = true; |
||||||
|
} |
||||||
|
if (_lt_airtime_limit != 0.0 && _longterm_airtime >= _lt_airtime_limit) { |
||||||
|
_airtime_lock = true; |
||||||
|
} |
||||||
|
return _airtime_lock; |
||||||
|
}; |
||||||
|
void updateAirtime() { |
||||||
|
uint16_t cb = current_airtime_bin(); |
||||||
|
uint16_t pb = cb-1; if (cb-1 < 0) { pb = AIRTIME_BINS-1; } |
||||||
|
uint16_t nb = cb+1; if (nb == AIRTIME_BINS) { nb = 0; } |
||||||
|
_airtime_bins[nb] = 0; |
||||||
|
_airtime = (float)(_airtime_bins[cb]+_airtime_bins[pb])/(2.0*AIRTIME_BINLEN_MS); |
||||||
|
|
||||||
|
uint32_t longterm_airtime_sum = 0; |
||||||
|
for (uint16_t bin = 0; bin < AIRTIME_BINS; bin++) { |
||||||
|
longterm_airtime_sum += _airtime_bins[bin]; |
||||||
|
} |
||||||
|
_longterm_airtime = (float)longterm_airtime_sum/(float)AIRTIME_LONGTERM_MS; |
||||||
|
|
||||||
|
float longterm_channel_util_sum = 0.0; |
||||||
|
for (uint16_t bin = 0; bin < AIRTIME_BINS; bin++) { |
||||||
|
longterm_channel_util_sum += _longterm_bins[bin]; |
||||||
|
} |
||||||
|
_longterm_channel_util = (float)longterm_channel_util_sum/(float)AIRTIME_BINS; |
||||||
|
|
||||||
|
updateCSMAp(); |
||||||
|
|
||||||
|
//kiss_indicate_channel_stats(); // todo: enable me!
|
||||||
|
}; |
||||||
|
void addAirtime(uint16_t written) { |
||||||
|
float packet_cost_ms = 0.0; |
||||||
|
float payload_cost_ms = ((float)written * _lora_us_per_byte)/1000.0; |
||||||
|
packet_cost_ms += payload_cost_ms; |
||||||
|
packet_cost_ms += (_preambleLength+4.25)*_lora_symbol_time_ms; |
||||||
|
packet_cost_ms += PHY_HEADER_LORA_SYMBOLS * _lora_symbol_time_ms; |
||||||
|
uint16_t cb = current_airtime_bin(); |
||||||
|
uint16_t nb = cb+1; if (nb == AIRTIME_BINS) { nb = 0; } |
||||||
|
_airtime_bins[cb] += packet_cost_ms; |
||||||
|
_airtime_bins[nb] = 0; |
||||||
|
}; |
||||||
|
void checkModemStatus() { |
||||||
|
if (millis()-_last_status_update >= STATUS_INTERVAL_MS) { |
||||||
|
updateModemStatus(); |
||||||
|
|
||||||
|
_util_samples[_dcd_sample] = _dcd; |
||||||
|
_dcd_sample = (_dcd_sample+1)%DCD_SAMPLES; |
||||||
|
if (_dcd_sample % UTIL_UPDATE_INTERVAL == 0) { |
||||||
|
int util_count = 0; |
||||||
|
for (int ui = 0; ui < DCD_SAMPLES; ui++) { |
||||||
|
if (_util_samples[ui]) util_count++; |
||||||
|
} |
||||||
|
_local_channel_util = (float)util_count / (float)DCD_SAMPLES; |
||||||
|
_total_channel_util = _local_channel_util + _airtime; |
||||||
|
if (_total_channel_util > 1.0) _total_channel_util = 1.0; |
||||||
|
|
||||||
|
int16_t cb = current_airtime_bin(); |
||||||
|
uint16_t nb = cb+1; if (nb == AIRTIME_BINS) { nb = 0; } |
||||||
|
if (_total_channel_util > _longterm_bins[cb]) _longterm_bins[cb] = _total_channel_util; |
||||||
|
_longterm_bins[nb] = 0.0; |
||||||
|
|
||||||
|
updateAirtime(); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
void updateModemStatus() { |
||||||
|
#if PLATFORM == PLATFORM_ESP32 |
||||||
|
portENTER_CRITICAL(&update_lock); |
||||||
|
#elif PLATFORM == PLATFORM_NRF52 |
||||||
|
portENTER_CRITICAL(); |
||||||
|
#endif |
||||||
|
|
||||||
|
uint8_t status = modemStatus(); |
||||||
|
|
||||||
|
_last_status_update = millis(); |
||||||
|
|
||||||
|
#if PLATFORM == PLATFORM_ESP32 |
||||||
|
portEXIT_CRITICAL(&update_lock); |
||||||
|
#elif PLATFORM == PLATFORM_NRF52 |
||||||
|
portEXIT_CRITICAL(); |
||||||
|
#endif |
||||||
|
|
||||||
|
if ((status & SIG_DETECT) == SIG_DETECT) { _stat_signal_detected = true; } else { _stat_signal_detected = false; } |
||||||
|
if ((status & SIG_SYNCED) == SIG_SYNCED) { _stat_signal_synced = true; } else { _stat_signal_synced = false; } |
||||||
|
if ((status & RX_ONGOING) == RX_ONGOING) { _stat_rx_ongoing = true; } else { _stat_rx_ongoing = false; } |
||||||
|
|
||||||
|
// if (stat_signal_detected || stat_signal_synced || stat_rx_ongoing) {
|
||||||
|
if (_stat_signal_detected || _stat_signal_synced) { |
||||||
|
if (_stat_rx_ongoing) { |
||||||
|
if (_dcd_count < DCD_THRESHOLD) { |
||||||
|
_dcd_count++; |
||||||
|
} else { |
||||||
|
_last_dcd = _last_status_update; |
||||||
|
_dcd_led = true; |
||||||
|
_dcd = true; |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
if (_dcd_count == 0) { |
||||||
|
_dcd_led = false; |
||||||
|
} else if (_dcd_count > DCD_LED_STEP_D) { |
||||||
|
_dcd_count -= DCD_LED_STEP_D; |
||||||
|
} else { |
||||||
|
_dcd_count = 0; |
||||||
|
} |
||||||
|
|
||||||
|
if (_last_status_update > _last_dcd+_csma_slot_ms) { |
||||||
|
_dcd = false; |
||||||
|
_dcd_led = false; |
||||||
|
_dcd_count = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (_dcd_led) { |
||||||
|
led_rx_on(); |
||||||
|
} else { |
||||||
|
if (_airtime_lock) { |
||||||
|
led_indicate_airtime_lock(); |
||||||
|
} else { |
||||||
|
led_rx_off(); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
void setPostTxYieldTimeout(uint32_t timeout) { _post_tx_yield_timeout = timeout; }; |
||||||
|
uint32_t getPostTxYieldTimeout() { return _post_tx_yield_timeout; }; |
||||||
|
void setDCD(bool dcd) { _dcd = dcd; }; |
||||||
|
bool getDCD() { return _dcd; }; |
||||||
|
void setDCDWaiting(bool dcd_waiting) { _dcd_waiting = dcd_waiting; }; |
||||||
|
bool getDCDWaiting() { return _dcd_waiting; }; |
||||||
|
void setDCDWaitUntil(uint32_t dcd_wait_until) { _dcd_wait_until = dcd_wait_until; }; |
||||||
|
bool getDCDWaitUntil() { return _dcd_wait_until; }; |
||||||
|
float getAirtime() { return _airtime; }; |
||||||
|
float getLongtermAirtime() { return _longterm_airtime; }; |
||||||
|
float getTotalChannelUtil() { return _total_channel_util; }; |
||||||
|
float getLongtermChannelUtil() { return _longterm_channel_util; }; |
||||||
|
float CSMASlope(float u) { return (pow(_e,_S*u-_S/2.0))/(pow(_e,_S*u-_S/2.0)+1.0); }; |
||||||
|
void updateCSMAp() { |
||||||
|
_csma_p = (uint8_t)((1.0-(_csma_p_min+(_csma_p_max-_csma_p_min)*CSMASlope(_airtime)))*255.0); |
||||||
|
}; |
||||||
|
uint8_t getCSMAp() { return _csma_p; }; |
||||||
|
void setCSMASlotMS(int slot_size) { _csma_slot_ms = slot_size; }; |
||||||
|
int getCSMASlotMS() { return _csma_slot_ms; }; |
||||||
|
float getSymbolTime() { return _lora_symbol_time_ms; }; |
||||||
|
float getSymbolRate() { return _lora_symbol_rate; }; |
||||||
|
long getPreambleLength() { return _preambleLength; }; |
||||||
|
protected: |
||||||
|
virtual void explicitHeaderMode(); |
||||||
|
virtual void implicitHeaderMode(); |
||||||
|
|
||||||
|
uint8_t _index; |
||||||
|
bool _radio_locked; |
||||||
|
bool _radio_online; |
||||||
|
float _st_airtime_limit; |
||||||
|
float _lt_airtime_limit; |
||||||
|
bool _airtime_lock; |
||||||
|
uint16_t _airtime_bins[AIRTIME_BINS] = {0}; |
||||||
|
uint16_t _longterm_bins[AIRTIME_BINS] = {0}; |
||||||
|
float _airtime; |
||||||
|
float _longterm_airtime; |
||||||
|
float _local_channel_util; |
||||||
|
float _total_channel_util; |
||||||
|
float _longterm_channel_util; |
||||||
|
uint32_t _last_status_update; |
||||||
|
bool _stat_signal_detected; |
||||||
|
bool _stat_signal_synced; |
||||||
|
bool _stat_rx_ongoing; |
||||||
|
uint32_t _last_dcd; |
||||||
|
uint16_t _dcd_count; |
||||||
|
bool _dcd; |
||||||
|
bool _dcd_led; |
||||||
|
bool _dcd_waiting; |
||||||
|
long _dcd_wait_until; |
||||||
|
bool _util_samples[DCD_SAMPLES] = {false}; |
||||||
|
int _dcd_sample; |
||||||
|
uint32_t _post_tx_yield_timeout; |
||||||
|
uint8_t _csma_p; |
||||||
|
int _csma_slot_ms; |
||||||
|
float _csma_p_min; |
||||||
|
float _csma_p_max; |
||||||
|
long _preambleLength; |
||||||
|
float _lora_symbol_time_ms; |
||||||
|
float _lora_symbol_rate; |
||||||
|
float _lora_us_per_byte; |
||||||
|
uint32_t _bitrate; |
||||||
|
void (*_onReceive)(uint8_t, int); |
||||||
|
}; |
||||||
|
|
||||||
|
class sx126x : public RadioInterface { |
||||||
|
public: |
||||||
|
sx126x(uint8_t index, SPIClass spi, bool tcxo, bool dio2_as_rf_switch, int ss, int sclk, int mosi, int miso, int reset, int |
||||||
|
dio0, int busy, int rxen); |
||||||
|
|
||||||
|
int begin(); |
||||||
|
void end(); |
||||||
|
|
||||||
|
int beginPacket(int implicitHeader = false); |
||||||
|
int endPacket(); |
||||||
|
|
||||||
|
int packetRssi(); |
||||||
|
int currentRssi(); |
||||||
|
uint8_t packetRssiRaw(); |
||||||
|
uint8_t currentRssiRaw(); |
||||||
|
uint8_t packetSnrRaw(); |
||||||
|
float packetSnr(); |
||||||
|
long packetFrequencyError(); |
||||||
|
|
||||||
|
// from Print
|
||||||
|
size_t write(uint8_t byte); |
||||||
|
size_t write(const uint8_t *buffer, size_t size); |
||||||
|
|
||||||
|
// from Stream
|
||||||
|
int available(); |
||||||
|
int read(); |
||||||
|
int peek(); |
||||||
|
void flush(); |
||||||
|
|
||||||
|
void onReceive(void(*callback)(uint8_t, int)); |
||||||
|
|
||||||
|
void receive(int size = 0); |
||||||
|
void standby(); |
||||||
|
void sleep(); |
||||||
|
|
||||||
|
bool preInit(); |
||||||
|
uint8_t getTxPower(); |
||||||
|
void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); |
||||||
|
uint32_t getFrequency(); |
||||||
|
void setFrequency(uint32_t frequency); |
||||||
|
void setSpreadingFactor(int sf); |
||||||
|
uint8_t getSpreadingFactor(); |
||||||
|
uint32_t getSignalBandwidth(); |
||||||
|
void setSignalBandwidth(uint32_t sbw); |
||||||
|
void setCodingRate4(int denominator); |
||||||
|
uint8_t getCodingRate4(); |
||||||
|
void setPreambleLength(long length); |
||||||
|
uint8_t modemStatus(); |
||||||
|
void enableCrc(); |
||||||
|
void disableCrc(); |
||||||
|
void enableTCXO(); |
||||||
|
void disableTCXO(); |
||||||
|
|
||||||
|
|
||||||
|
byte random(); |
||||||
|
|
||||||
|
void setSPIFrequency(uint32_t frequency); |
||||||
|
|
||||||
|
void dumpRegisters(Stream& out); |
||||||
|
|
||||||
|
void updateBitrate(); |
||||||
|
|
||||||
|
void handleDio0Rise(); |
||||||
|
private: |
||||||
|
void writeBuffer(const uint8_t* buffer, size_t size); |
||||||
|
void readBuffer(uint8_t* buffer, size_t size); |
||||||
|
void loraMode(); |
||||||
|
void rxAntEnable(); |
||||||
|
void setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc); |
||||||
|
void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro); |
||||||
|
void setSyncWord(uint16_t sw); |
||||||
|
void waitOnBusy(); |
||||||
|
void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size); |
||||||
|
void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size); |
||||||
|
void explicitHeaderMode(); |
||||||
|
void implicitHeaderMode(); |
||||||
|
|
||||||
|
|
||||||
|
uint8_t readRegister(uint16_t address); |
||||||
|
void writeRegister(uint16_t address, uint8_t value); |
||||||
|
uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value); |
||||||
|
|
||||||
|
static void onDio0Rise(); |
||||||
|
|
||||||
|
void handleLowDataRate(); |
||||||
|
void optimizeModemSensitivity(); |
||||||
|
|
||||||
|
void reset(void); |
||||||
|
void calibrate(void); |
||||||
|
void calibrate_image(uint32_t frequency); |
||||||
|
|
||||||
|
private: |
||||||
|
SPISettings _spiSettings; |
||||||
|
SPIClass _spiModem; |
||||||
|
int _ss; |
||||||
|
int _sclk; |
||||||
|
int _mosi; |
||||||
|
int _miso; |
||||||
|
int _reset; |
||||||
|
int _dio0; |
||||||
|
int _rxen; |
||||||
|
int _busy; |
||||||
|
uint32_t _frequency; |
||||||
|
int _txp; |
||||||
|
uint8_t _sf; |
||||||
|
uint8_t _bw; |
||||||
|
uint8_t _cr; |
||||||
|
uint8_t _ldro; |
||||||
|
int _packetIndex; |
||||||
|
int _implicitHeaderMode; |
||||||
|
int _payloadLength; |
||||||
|
int _crcMode; |
||||||
|
int _fifo_tx_addr_ptr; |
||||||
|
int _fifo_rx_addr_ptr; |
||||||
|
uint8_t _packet[255]; |
||||||
|
bool _preinit_done; |
||||||
|
uint8_t _index; |
||||||
|
bool _tcxo; |
||||||
|
bool _dio2_as_rf_switch; |
||||||
|
}; |
||||||
|
|
||||||
|
class sx127x : public RadioInterface { |
||||||
|
public: |
||||||
|
sx127x(uint8_t index, SPIClass spi, int ss, int sclk, int mosi, int miso, int reset, int dio0, int busy); |
||||||
|
|
||||||
|
int begin(); |
||||||
|
void end(); |
||||||
|
|
||||||
|
int beginPacket(int implicitHeader = false); |
||||||
|
int endPacket(); |
||||||
|
|
||||||
|
int packetRssi(); |
||||||
|
int currentRssi(); |
||||||
|
uint8_t packetRssiRaw(); |
||||||
|
uint8_t currentRssiRaw(); |
||||||
|
uint8_t packetSnrRaw(); |
||||||
|
float packetSnr(); |
||||||
|
long packetFrequencyError(); |
||||||
|
|
||||||
|
// from Print
|
||||||
|
size_t write(uint8_t byte); |
||||||
|
size_t write(const uint8_t *buffer, size_t size); |
||||||
|
|
||||||
|
// from Stream
|
||||||
|
int available(); |
||||||
|
int read(); |
||||||
|
int peek(); |
||||||
|
void flush(); |
||||||
|
|
||||||
|
void onReceive(void(*callback)(uint8_t, int)); |
||||||
|
|
||||||
|
void receive(int size = 0); |
||||||
|
void standby(); |
||||||
|
void sleep(); |
||||||
|
|
||||||
|
bool preInit(); |
||||||
|
uint8_t getTxPower(); |
||||||
|
void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); |
||||||
|
uint32_t getFrequency(); |
||||||
|
void setFrequency(uint32_t frequency); |
||||||
|
void setSpreadingFactor(int sf); |
||||||
|
uint8_t getSpreadingFactor(); |
||||||
|
uint32_t getSignalBandwidth(); |
||||||
|
void setSignalBandwidth(uint32_t sbw); |
||||||
|
void setCodingRate4(int denominator); |
||||||
|
uint8_t getCodingRate4(); |
||||||
|
void setPreambleLength(long length); |
||||||
|
uint8_t modemStatus(); |
||||||
|
void enableCrc(); |
||||||
|
void disableCrc(); |
||||||
|
void enableTCXO(); |
||||||
|
void disableTCXO(); |
||||||
|
|
||||||
|
byte random(); |
||||||
|
|
||||||
|
void setSPIFrequency(uint32_t frequency); |
||||||
|
|
||||||
|
void updateBitrate(); |
||||||
|
|
||||||
|
void handleDio0Rise(); |
||||||
|
private: |
||||||
|
void setSyncWord(uint8_t sw); |
||||||
|
void explicitHeaderMode(); |
||||||
|
void implicitHeaderMode(); |
||||||
|
|
||||||
|
|
||||||
|
uint8_t readRegister(uint8_t address); |
||||||
|
void writeRegister(uint8_t address, uint8_t value); |
||||||
|
uint8_t singleTransfer(uint8_t address, uint8_t value); |
||||||
|
|
||||||
|
static void onDio0Rise(); |
||||||
|
|
||||||
|
void handleLowDataRate(); |
||||||
|
void optimizeModemSensitivity(); |
||||||
|
|
||||||
|
private: |
||||||
|
SPISettings _spiSettings; |
||||||
|
SPIClass _spiModem; |
||||||
|
int _ss; |
||||||
|
int _sclk; |
||||||
|
int _mosi; |
||||||
|
int _miso; |
||||||
|
int _reset; |
||||||
|
int _dio0; |
||||||
|
int _busy; |
||||||
|
uint32_t _frequency; |
||||||
|
int _packetIndex; |
||||||
|
int _implicitHeaderMode; |
||||||
|
bool _preinit_done; |
||||||
|
uint8_t _index; |
||||||
|
uint8_t _sf; |
||||||
|
uint8_t _cr; |
||||||
|
}; |
||||||
|
|
||||||
|
class sx128x : public RadioInterface { |
||||||
|
public: |
||||||
|
sx128x(uint8_t index, SPIClass spi, bool tcxo, int ss, int sclk, int mosi, int miso, int reset, int dio0, int busy, int rxen, int txen); |
||||||
|
|
||||||
|
int begin(); |
||||||
|
void end(); |
||||||
|
|
||||||
|
int beginPacket(int implicitHeader = false); |
||||||
|
int endPacket(); |
||||||
|
|
||||||
|
int packetRssi(); |
||||||
|
int currentRssi(); |
||||||
|
uint8_t packetRssiRaw(); |
||||||
|
uint8_t currentRssiRaw(); |
||||||
|
uint8_t packetSnrRaw(); |
||||||
|
float packetSnr(); |
||||||
|
long packetFrequencyError(); |
||||||
|
|
||||||
|
// from Print
|
||||||
|
size_t write(uint8_t byte); |
||||||
|
size_t write(const uint8_t *buffer, size_t size); |
||||||
|
|
||||||
|
// from Stream
|
||||||
|
int available(); |
||||||
|
int read(); |
||||||
|
int peek(); |
||||||
|
void flush(); |
||||||
|
|
||||||
|
void onReceive(void(*callback)(uint8_t, int)); |
||||||
|
|
||||||
|
void receive(int size = 0); |
||||||
|
void standby(); |
||||||
|
void sleep(); |
||||||
|
|
||||||
|
bool preInit(); |
||||||
|
uint8_t getTxPower(); |
||||||
|
void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); |
||||||
|
uint32_t getFrequency(); |
||||||
|
void setFrequency(uint32_t frequency); |
||||||
|
void setSpreadingFactor(int sf); |
||||||
|
uint8_t getSpreadingFactor(); |
||||||
|
uint32_t getSignalBandwidth(); |
||||||
|
void setSignalBandwidth(uint32_t sbw); |
||||||
|
void setCodingRate4(int denominator); |
||||||
|
uint8_t getCodingRate4(); |
||||||
|
void setPreambleLength(long length); |
||||||
|
uint8_t modemStatus(); |
||||||
|
void enableCrc(); |
||||||
|
void disableCrc(); |
||||||
|
void enableTCXO(); |
||||||
|
void disableTCXO(); |
||||||
|
|
||||||
|
byte random(); |
||||||
|
|
||||||
|
void setSPIFrequency(uint32_t frequency); |
||||||
|
|
||||||
|
void dumpRegisters(Stream& out); |
||||||
|
|
||||||
|
void updateBitrate(); |
||||||
|
|
||||||
|
void handleDio0Rise(); |
||||||
|
private: |
||||||
|
void writeBuffer(const uint8_t* buffer, size_t size); |
||||||
|
void readBuffer(uint8_t* buffer, size_t size); |
||||||
|
void txAntEnable(); |
||||||
|
void rxAntEnable(); |
||||||
|
void loraMode(); |
||||||
|
void waitOnBusy(); |
||||||
|
void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size); |
||||||
|
void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size); |
||||||
|
void setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc); |
||||||
|
void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr); |
||||||
|
void setSyncWord(int sw); |
||||||
|
void explicitHeaderMode(); |
||||||
|
void implicitHeaderMode(); |
||||||
|
|
||||||
|
|
||||||
|
uint8_t readRegister(uint16_t address); |
||||||
|
void writeRegister(uint16_t address, uint8_t value); |
||||||
|
uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value); |
||||||
|
|
||||||
|
static void onDio0Rise(); |
||||||
|
|
||||||
|
void handleLowDataRate(); |
||||||
|
void optimizeModemSensitivity(); |
||||||
|
|
||||||
|
private: |
||||||
|
SPISettings _spiSettings; |
||||||
|
SPIClass _spiModem; |
||||||
|
int _ss; |
||||||
|
int _sclk; |
||||||
|
int _mosi; |
||||||
|
int _miso; |
||||||
|
int _reset; |
||||||
|
int _dio0; |
||||||
|
int _rxen; |
||||||
|
int _txen; |
||||||
|
int _busy; |
||||||
|
int _modem; |
||||||
|
uint32_t _frequency; |
||||||
|
int _txp; |
||||||
|
uint8_t _sf; |
||||||
|
uint8_t _bw; |
||||||
|
uint8_t _cr; |
||||||
|
int _packetIndex; |
||||||
|
int _implicitHeaderMode; |
||||||
|
int _payloadLength; |
||||||
|
int _crcMode; |
||||||
|
int _fifo_tx_addr_ptr; |
||||||
|
int _fifo_rx_addr_ptr; |
||||||
|
uint8_t _packet[255]; |
||||||
|
bool _preinit_done; |
||||||
|
int _rxPacketLength; |
||||||
|
uint8_t _index; |
||||||
|
bool _tcxo; |
||||||
|
}; |
||||||
|
#endif |
@ -1,985 +0,0 @@ |
|||||||
// Copyright (c) Sandeep Mistry. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
// Modifications and additions copyright 2024 by Mark Qvist & Jacob Eva
|
|
||||||
// Obviously still under the MIT license.
|
|
||||||
|
|
||||||
#include "Boards.h" |
|
||||||
|
|
||||||
#if MODEM == SX1262 |
|
||||||
#include "sx126x.h" |
|
||||||
|
|
||||||
#if MCU_VARIANT == MCU_ESP32 |
|
||||||
#if MCU_VARIANT == MCU_ESP32 and !defined(CONFIG_IDF_TARGET_ESP32S3) |
|
||||||
#include "soc/rtc_wdt.h" |
|
||||||
#endif |
|
||||||
#define ISR_VECT IRAM_ATTR |
|
||||||
#else |
|
||||||
#define ISR_VECT |
|
||||||
#endif |
|
||||||
|
|
||||||
#define OP_RF_FREQ_6X 0x86 |
|
||||||
#define OP_SLEEP_6X 0x84 |
|
||||||
#define OP_STANDBY_6X 0x80 |
|
||||||
#define OP_TX_6X 0x83 |
|
||||||
#define OP_RX_6X 0x82 |
|
||||||
#define OP_PA_CONFIG_6X 0x95 |
|
||||||
#define OP_SET_IRQ_FLAGS_6X 0x08 // also provides info such as
|
|
||||||
// preamble detection, etc for
|
|
||||||
// knowing when it's safe to switch
|
|
||||||
// antenna modes
|
|
||||||
#define OP_CLEAR_IRQ_STATUS_6X 0x02 |
|
||||||
#define OP_GET_IRQ_STATUS_6X 0x12 |
|
||||||
#define OP_RX_BUFFER_STATUS_6X 0x13 |
|
||||||
#define OP_PACKET_STATUS_6X 0x14 // get snr & rssi of last packet
|
|
||||||
#define OP_CURRENT_RSSI_6X 0x15 |
|
||||||
#define OP_MODULATION_PARAMS_6X 0x8B // bw, sf, cr, etc.
|
|
||||||
#define OP_PACKET_PARAMS_6X 0x8C // crc, preamble, payload length, etc.
|
|
||||||
#define OP_STATUS_6X 0xC0 |
|
||||||
#define OP_TX_PARAMS_6X 0x8E // set dbm, etc
|
|
||||||
#define OP_PACKET_TYPE_6X 0x8A |
|
||||||
#define OP_BUFFER_BASE_ADDR_6X 0x8F |
|
||||||
#define OP_READ_REGISTER_6X 0x1D |
|
||||||
#define OP_WRITE_REGISTER_6X 0x0D |
|
||||||
#define OP_DIO3_TCXO_CTRL_6X 0x97 |
|
||||||
#define OP_DIO2_RF_CTRL_6X 0x9D |
|
||||||
#define OP_CAD_PARAMS 0x88 |
|
||||||
#define OP_CALIBRATE_6X 0x89 |
|
||||||
#define OP_RX_TX_FALLBACK_MODE_6X 0x93 |
|
||||||
#define OP_REGULATOR_MODE_6X 0x96 |
|
||||||
#define OP_CALIBRATE_IMAGE_6X 0x98 |
|
||||||
|
|
||||||
#define MASK_CALIBRATE_ALL 0x7f |
|
||||||
|
|
||||||
#define IRQ_TX_DONE_MASK_6X 0x01 |
|
||||||
#define IRQ_RX_DONE_MASK_6X 0x02 |
|
||||||
#define IRQ_HEADER_DET_MASK_6X 0x10 |
|
||||||
#define IRQ_PREAMBLE_DET_MASK_6X 0x04 |
|
||||||
#define IRQ_PAYLOAD_CRC_ERROR_MASK_6X 0x40 |
|
||||||
#define IRQ_ALL_MASK_6X 0b0100001111111111 |
|
||||||
|
|
||||||
#define MODE_LONG_RANGE_MODE_6X 0x01 |
|
||||||
|
|
||||||
#define OP_FIFO_WRITE_6X 0x0E |
|
||||||
#define OP_FIFO_READ_6X 0x1E |
|
||||||
#define REG_OCP_6X 0x08E7 |
|
||||||
#define REG_LNA_6X 0x08AC // no agc in sx1262
|
|
||||||
#define REG_SYNC_WORD_MSB_6X 0x0740 |
|
||||||
#define REG_SYNC_WORD_LSB_6X 0x0741 |
|
||||||
#define REG_PAYLOAD_LENGTH_6X 0x0702 // https://github.com/beegee-tokyo/SX126x-Arduino/blob/master/src/radio/sx126x/sx126x.h#L98
|
|
||||||
#define REG_RANDOM_GEN_6X 0x0819 |
|
||||||
|
|
||||||
#define MODE_TCXO_3_3V_6X 0x07 |
|
||||||
#define MODE_TCXO_3_0V_6X 0x06 |
|
||||||
#define MODE_TCXO_2_7V_6X 0x06 |
|
||||||
#define MODE_TCXO_2_4V_6X 0x06 |
|
||||||
#define MODE_TCXO_2_2V_6X 0x03 |
|
||||||
#define MODE_TCXO_1_8V_6X 0x02 |
|
||||||
#define MODE_TCXO_1_7V_6X 0x01 |
|
||||||
#define MODE_TCXO_1_6V_6X 0x00 |
|
||||||
|
|
||||||
#define MODE_STDBY_RC_6X 0x00 |
|
||||||
#define MODE_STDBY_XOSC_6X 0x01 |
|
||||||
#define MODE_FALLBACK_STDBY_RC_6X 0x20 |
|
||||||
#define MODE_IMPLICIT_HEADER 0x01 |
|
||||||
#define MODE_EXPLICIT_HEADER 0x00 |
|
||||||
|
|
||||||
#define SYNC_WORD_6X 0x1424 |
|
||||||
|
|
||||||
#define XTAL_FREQ_6X (double)32000000 |
|
||||||
#define FREQ_DIV_6X (double)pow(2.0, 25.0) |
|
||||||
#define FREQ_STEP_6X (double)(XTAL_FREQ_6X / FREQ_DIV_6X) |
|
||||||
|
|
||||||
#if defined(NRF52840_XXAA) |
|
||||||
extern SPIClass spiModem; |
|
||||||
#define SPI spiModem |
|
||||||
#endif |
|
||||||
|
|
||||||
extern SPIClass SPI; |
|
||||||
|
|
||||||
#define MAX_PKT_LENGTH 255 |
|
||||||
|
|
||||||
sx126x::sx126x() : |
|
||||||
_spiSettings(8E6, MSBFIRST, SPI_MODE0), |
|
||||||
_ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), _busy(LORA_DEFAULT_BUSY_PIN), _rxen(LORA_DEFAULT_RXEN_PIN), |
|
||||||
_frequency(0), |
|
||||||
_txp(0), |
|
||||||
_sf(0x07), |
|
||||||
_bw(0x04), |
|
||||||
_cr(0x01), |
|
||||||
_ldro(0x00), |
|
||||||
_packetIndex(0), |
|
||||||
_preambleLength(18), |
|
||||||
_implicitHeaderMode(0), |
|
||||||
_payloadLength(255), |
|
||||||
_crcMode(1), |
|
||||||
_fifo_tx_addr_ptr(0), |
|
||||||
_fifo_rx_addr_ptr(0), |
|
||||||
_packet({0}), |
|
||||||
_preinit_done(false), |
|
||||||
_onReceive(NULL) |
|
||||||
{ |
|
||||||
// overide Stream timeout value
|
|
||||||
setTimeout(0); |
|
||||||
} |
|
||||||
|
|
||||||
bool sx126x::preInit() { |
|
||||||
pinMode(_ss, OUTPUT); |
|
||||||
digitalWrite(_ss, HIGH); |
|
||||||
|
|
||||||
#if BOARD_MODEL == BOARD_RNODE_NG_22 || BOARD_MODEL == BOARD_HELTEC32_V3 |
|
||||||
SPI.begin(pin_sclk, pin_miso, pin_mosi, pin_cs); |
|
||||||
#else |
|
||||||
SPI.begin(); |
|
||||||
#endif |
|
||||||
|
|
||||||
// check version (retry for up to 2 seconds)
|
|
||||||
// TODO: Actually read version registers, not syncwords
|
|
||||||
long start = millis(); |
|
||||||
uint8_t syncmsb; |
|
||||||
uint8_t synclsb; |
|
||||||
while (((millis() - start) < 2000) && (millis() >= start)) { |
|
||||||
syncmsb = readRegister(REG_SYNC_WORD_MSB_6X); |
|
||||||
synclsb = readRegister(REG_SYNC_WORD_LSB_6X); |
|
||||||
if ( uint16_t(syncmsb << 8 | synclsb) == 0x1424 || uint16_t(syncmsb << 8 | synclsb) == 0x4434) { |
|
||||||
break; |
|
||||||
} |
|
||||||
delay(100); |
|
||||||
} |
|
||||||
if ( uint16_t(syncmsb << 8 | synclsb) != 0x1424 && uint16_t(syncmsb << 8 | synclsb) != 0x4434) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
_preinit_done = true; |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
uint8_t ISR_VECT sx126x::readRegister(uint16_t address) |
|
||||||
{ |
|
||||||
return singleTransfer(OP_READ_REGISTER_6X, address, 0x00); |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::writeRegister(uint16_t address, uint8_t value) |
|
||||||
{ |
|
||||||
singleTransfer(OP_WRITE_REGISTER_6X, address, value); |
|
||||||
} |
|
||||||
|
|
||||||
uint8_t ISR_VECT sx126x::singleTransfer(uint8_t opcode, uint16_t address, uint8_t value) |
|
||||||
{ |
|
||||||
waitOnBusy(); |
|
||||||
|
|
||||||
uint8_t response; |
|
||||||
|
|
||||||
digitalWrite(_ss, LOW); |
|
||||||
|
|
||||||
SPI.beginTransaction(_spiSettings); |
|
||||||
SPI.transfer(opcode); |
|
||||||
SPI.transfer((address & 0xFF00) >> 8); |
|
||||||
SPI.transfer(address & 0x00FF); |
|
||||||
if (opcode == OP_READ_REGISTER_6X) { |
|
||||||
SPI.transfer(0x00); |
|
||||||
} |
|
||||||
response = SPI.transfer(value); |
|
||||||
SPI.endTransaction(); |
|
||||||
|
|
||||||
digitalWrite(_ss, HIGH); |
|
||||||
|
|
||||||
return response; |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::rxAntEnable() |
|
||||||
{ |
|
||||||
if (_rxen != -1) { |
|
||||||
digitalWrite(_rxen, HIGH); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::loraMode() { |
|
||||||
// enable lora mode on the SX1262 chip
|
|
||||||
uint8_t mode = MODE_LONG_RANGE_MODE_6X; |
|
||||||
executeOpcode(OP_PACKET_TYPE_6X, &mode, 1); |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::waitOnBusy() { |
|
||||||
unsigned long time = millis(); |
|
||||||
while (digitalRead(_busy) == HIGH) |
|
||||||
{ |
|
||||||
if (millis() >= (time + 100)) { |
|
||||||
break; |
|
||||||
} |
|
||||||
// do nothing
|
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size) |
|
||||||
{ |
|
||||||
waitOnBusy(); |
|
||||||
|
|
||||||
digitalWrite(_ss, LOW); |
|
||||||
|
|
||||||
SPI.beginTransaction(_spiSettings); |
|
||||||
SPI.transfer(opcode); |
|
||||||
|
|
||||||
for (int i = 0; i < size; i++) |
|
||||||
{ |
|
||||||
SPI.transfer(buffer[i]); |
|
||||||
} |
|
||||||
|
|
||||||
SPI.endTransaction(); |
|
||||||
|
|
||||||
digitalWrite(_ss, HIGH); |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size) |
|
||||||
{ |
|
||||||
waitOnBusy(); |
|
||||||
|
|
||||||
digitalWrite(_ss, LOW); |
|
||||||
|
|
||||||
SPI.beginTransaction(_spiSettings); |
|
||||||
SPI.transfer(opcode); |
|
||||||
SPI.transfer(0x00); |
|
||||||
|
|
||||||
for (int i = 0; i < size; i++) |
|
||||||
{ |
|
||||||
buffer[i] = SPI.transfer(0x00); |
|
||||||
} |
|
||||||
|
|
||||||
SPI.endTransaction(); |
|
||||||
|
|
||||||
digitalWrite(_ss, HIGH); |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::writeBuffer(const uint8_t* buffer, size_t size) |
|
||||||
{ |
|
||||||
waitOnBusy(); |
|
||||||
|
|
||||||
digitalWrite(_ss, LOW); |
|
||||||
|
|
||||||
SPI.beginTransaction(_spiSettings); |
|
||||||
SPI.transfer(OP_FIFO_WRITE_6X); |
|
||||||
SPI.transfer(_fifo_tx_addr_ptr); |
|
||||||
|
|
||||||
for (int i = 0; i < size; i++) |
|
||||||
{ |
|
||||||
SPI.transfer(buffer[i]); |
|
||||||
_fifo_tx_addr_ptr++; |
|
||||||
} |
|
||||||
|
|
||||||
SPI.endTransaction(); |
|
||||||
|
|
||||||
digitalWrite(_ss, HIGH); |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::readBuffer(uint8_t* buffer, size_t size) |
|
||||||
{ |
|
||||||
waitOnBusy(); |
|
||||||
|
|
||||||
digitalWrite(_ss, LOW); |
|
||||||
|
|
||||||
SPI.beginTransaction(_spiSettings); |
|
||||||
SPI.transfer(OP_FIFO_READ_6X); |
|
||||||
SPI.transfer(_fifo_rx_addr_ptr); |
|
||||||
SPI.transfer(0x00); |
|
||||||
|
|
||||||
for (int i = 0; i < size; i++) |
|
||||||
{ |
|
||||||
buffer[i] = SPI.transfer(0x00); |
|
||||||
} |
|
||||||
|
|
||||||
SPI.endTransaction(); |
|
||||||
|
|
||||||
digitalWrite(_ss, HIGH); |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro) { |
|
||||||
// because there is no access to these registers on the sx1262, we have
|
|
||||||
// to set all these parameters at once or not at all.
|
|
||||||
uint8_t buf[8]; |
|
||||||
|
|
||||||
buf[0] = sf; |
|
||||||
buf[1] = bw; |
|
||||||
buf[2] = cr;
|
|
||||||
// low data rate toggle
|
|
||||||
buf[3] = ldro; |
|
||||||
// unused params in LoRa mode
|
|
||||||
buf[4] = 0x00;
|
|
||||||
buf[5] = 0x00; |
|
||||||
buf[6] = 0x00; |
|
||||||
buf[7] = 0x00; |
|
||||||
|
|
||||||
executeOpcode(OP_MODULATION_PARAMS_6X, buf, 8); |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::setPacketParams(long preamble, uint8_t headermode, uint8_t length, uint8_t crc) { |
|
||||||
// because there is no access to these registers on the sx1262, we have
|
|
||||||
// to set all these parameters at once or not at all.
|
|
||||||
uint8_t buf[9]; |
|
||||||
|
|
||||||
buf[0] = uint8_t((preamble & 0xFF00) >> 8); |
|
||||||
buf[1] = uint8_t((preamble & 0x00FF)); |
|
||||||
buf[2] = headermode; |
|
||||||
buf[3] = length; |
|
||||||
buf[4] = crc; |
|
||||||
// standard IQ setting (no inversion)
|
|
||||||
buf[5] = 0x00;
|
|
||||||
// unused params
|
|
||||||
buf[6] = 0x00;
|
|
||||||
buf[7] = 0x00;
|
|
||||||
buf[8] = 0x00;
|
|
||||||
|
|
||||||
executeOpcode(OP_PACKET_PARAMS_6X, buf, 9); |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::reset(void) { |
|
||||||
if (_reset != -1) { |
|
||||||
pinMode(_reset, OUTPUT); |
|
||||||
|
|
||||||
// perform reset
|
|
||||||
digitalWrite(_reset, LOW); |
|
||||||
delay(10); |
|
||||||
digitalWrite(_reset, HIGH); |
|
||||||
delay(10); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::calibrate(void) { |
|
||||||
// Put in STDBY_RC mode before calibration
|
|
||||||
uint8_t mode_byte = MODE_STDBY_RC_6X; |
|
||||||
executeOpcode(OP_STANDBY_6X, &mode_byte, 1); |
|
||||||
|
|
||||||
// calibrate RC64k, RC13M, PLL, ADC and image
|
|
||||||
uint8_t calibrate = MASK_CALIBRATE_ALL; |
|
||||||
executeOpcode(OP_CALIBRATE_6X, &calibrate, 1); |
|
||||||
|
|
||||||
delay(5); |
|
||||||
waitOnBusy(); |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::calibrate_image(long frequency) { |
|
||||||
uint8_t image_freq[2] = {0}; |
|
||||||
|
|
||||||
if (frequency >= 430E6 && frequency <= 440E6) { |
|
||||||
image_freq[0] = 0x6B; |
|
||||||
image_freq[1] = 0x6F; |
|
||||||
} |
|
||||||
else if (frequency >= 470E6 && frequency <= 510E6) { |
|
||||||
image_freq[0] = 0x75; |
|
||||||
image_freq[1] = 0x81; |
|
||||||
} |
|
||||||
else if (frequency >= 779E6 && frequency <= 787E6) { |
|
||||||
image_freq[0] = 0xC1; |
|
||||||
image_freq[1] = 0xC5; |
|
||||||
} |
|
||||||
else if (frequency >= 863E6 && frequency <= 870E6) { |
|
||||||
image_freq[0] = 0xD7; |
|
||||||
image_freq[1] = 0xDB; |
|
||||||
} |
|
||||||
else if (frequency >= 902E6 && frequency <= 928E6) { |
|
||||||
image_freq[0] = 0xE1; |
|
||||||
image_freq[1] = 0xE9; |
|
||||||
} |
|
||||||
|
|
||||||
executeOpcode(OP_CALIBRATE_IMAGE_6X, image_freq, 2); |
|
||||||
waitOnBusy(); |
|
||||||
} |
|
||||||
|
|
||||||
int sx126x::begin(long frequency) |
|
||||||
{ |
|
||||||
reset(); |
|
||||||
|
|
||||||
if (_busy != -1) { |
|
||||||
pinMode(_busy, INPUT); |
|
||||||
} |
|
||||||
|
|
||||||
if (!_preinit_done) { |
|
||||||
if (!preInit()) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (_rxen != -1) { |
|
||||||
pinMode(_rxen, OUTPUT); |
|
||||||
} |
|
||||||
|
|
||||||
calibrate(); |
|
||||||
calibrate_image(frequency); |
|
||||||
|
|
||||||
enableTCXO(); |
|
||||||
|
|
||||||
loraMode(); |
|
||||||
standby(); |
|
||||||
|
|
||||||
// Set sync word
|
|
||||||
setSyncWord(SYNC_WORD_6X); |
|
||||||
|
|
||||||
#if DIO2_AS_RF_SWITCH |
|
||||||
// enable dio2 rf switch
|
|
||||||
uint8_t byte = 0x01; |
|
||||||
executeOpcode(OP_DIO2_RF_CTRL_6X, &byte, 1); |
|
||||||
#endif |
|
||||||
|
|
||||||
rxAntEnable(); |
|
||||||
|
|
||||||
setFrequency(frequency); |
|
||||||
|
|
||||||
// set output power to 2 dBm
|
|
||||||
setTxPower(2); |
|
||||||
enableCrc(); |
|
||||||
|
|
||||||
// set LNA boost
|
|
||||||
writeRegister(REG_LNA_6X, 0x96); |
|
||||||
|
|
||||||
// set base addresses
|
|
||||||
uint8_t basebuf[2] = {0}; |
|
||||||
executeOpcode(OP_BUFFER_BASE_ADDR_6X, basebuf, 2); |
|
||||||
|
|
||||||
setModulationParams(_sf, _bw, _cr, _ldro); |
|
||||||
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); |
|
||||||
|
|
||||||
return 1; |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::end() |
|
||||||
{ |
|
||||||
// put in sleep mode
|
|
||||||
sleep(); |
|
||||||
|
|
||||||
// stop SPI
|
|
||||||
SPI.end(); |
|
||||||
|
|
||||||
_preinit_done = false; |
|
||||||
} |
|
||||||
|
|
||||||
int sx126x::beginPacket(int implicitHeader) |
|
||||||
{ |
|
||||||
standby(); |
|
||||||
|
|
||||||
if (implicitHeader) { |
|
||||||
implicitHeaderMode(); |
|
||||||
} else { |
|
||||||
explicitHeaderMode(); |
|
||||||
} |
|
||||||
|
|
||||||
_payloadLength = 0; |
|
||||||
_fifo_tx_addr_ptr = 0; |
|
||||||
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); |
|
||||||
|
|
||||||
return 1; |
|
||||||
} |
|
||||||
|
|
||||||
int sx126x::endPacket() |
|
||||||
{ |
|
||||||
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); |
|
||||||
|
|
||||||
// put in single TX mode
|
|
||||||
uint8_t timeout[3] = {0}; |
|
||||||
executeOpcode(OP_TX_6X, timeout, 3); |
|
||||||
|
|
||||||
uint8_t buf[2]; |
|
||||||
|
|
||||||
buf[0] = 0x00; |
|
||||||
buf[1] = 0x00; |
|
||||||
|
|
||||||
executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2); |
|
||||||
|
|
||||||
// wait for TX done
|
|
||||||
while ((buf[1] & IRQ_TX_DONE_MASK_6X) == 0) { |
|
||||||
buf[0] = 0x00; |
|
||||||
buf[1] = 0x00; |
|
||||||
executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2); |
|
||||||
yield(); |
|
||||||
} |
|
||||||
|
|
||||||
// clear IRQ's
|
|
||||||
|
|
||||||
uint8_t mask[2]; |
|
||||||
mask[0] = 0x00; |
|
||||||
mask[1] = IRQ_TX_DONE_MASK_6X; |
|
||||||
executeOpcode(OP_CLEAR_IRQ_STATUS_6X, mask, 2); |
|
||||||
return 1; |
|
||||||
} |
|
||||||
|
|
||||||
uint8_t sx126x::modemStatus() { |
|
||||||
// imitate the register status from the sx1276 / 78
|
|
||||||
uint8_t buf[2] = {0}; |
|
||||||
|
|
||||||
executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2); |
|
||||||
uint8_t clearbuf[2] = {0}; |
|
||||||
uint8_t byte = 0x00; |
|
||||||
|
|
||||||
if ((buf[1] & IRQ_PREAMBLE_DET_MASK_6X) != 0) { |
|
||||||
byte = byte | 0x01 | 0x04; |
|
||||||
// clear register after reading
|
|
||||||
clearbuf[1] = IRQ_PREAMBLE_DET_MASK_6X; |
|
||||||
} |
|
||||||
|
|
||||||
if ((buf[1] & IRQ_HEADER_DET_MASK_6X) != 0) { |
|
||||||
byte = byte | 0x02 | 0x04; |
|
||||||
} |
|
||||||
|
|
||||||
executeOpcode(OP_CLEAR_IRQ_STATUS_6X, clearbuf, 2); |
|
||||||
|
|
||||||
return byte;
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
uint8_t sx126x::currentRssiRaw() { |
|
||||||
uint8_t byte = 0; |
|
||||||
executeOpcodeRead(OP_CURRENT_RSSI_6X, &byte, 1); |
|
||||||
return byte; |
|
||||||
} |
|
||||||
|
|
||||||
int ISR_VECT sx126x::currentRssi() { |
|
||||||
uint8_t byte = 0; |
|
||||||
executeOpcodeRead(OP_CURRENT_RSSI_6X, &byte, 1); |
|
||||||
int rssi = -(int(byte)) / 2; |
|
||||||
return rssi; |
|
||||||
} |
|
||||||
|
|
||||||
uint8_t sx126x::packetRssiRaw() { |
|
||||||
uint8_t buf[3] = {0}; |
|
||||||
executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); |
|
||||||
return buf[2]; |
|
||||||
} |
|
||||||
|
|
||||||
int ISR_VECT sx126x::packetRssi() { |
|
||||||
// may need more calculations here
|
|
||||||
uint8_t buf[3] = {0}; |
|
||||||
executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); |
|
||||||
int pkt_rssi = -buf[0] / 2; |
|
||||||
return pkt_rssi; |
|
||||||
} |
|
||||||
|
|
||||||
uint8_t ISR_VECT sx126x::packetSnrRaw() { |
|
||||||
uint8_t buf[3] = {0}; |
|
||||||
executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); |
|
||||||
return buf[1]; |
|
||||||
} |
|
||||||
|
|
||||||
float ISR_VECT sx126x::packetSnr() { |
|
||||||
uint8_t buf[3] = {0}; |
|
||||||
executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); |
|
||||||
return float(buf[1]) * 0.25; |
|
||||||
} |
|
||||||
|
|
||||||
long sx126x::packetFrequencyError() |
|
||||||
{ |
|
||||||
// todo: implement this, no idea how to check it on the sx1262
|
|
||||||
const float fError = 0.0; |
|
||||||
return static_cast<long>(fError); |
|
||||||
} |
|
||||||
|
|
||||||
size_t sx126x::write(uint8_t byte) |
|
||||||
{ |
|
||||||
return write(&byte, sizeof(byte)); |
|
||||||
} |
|
||||||
|
|
||||||
size_t sx126x::write(const uint8_t *buffer, size_t size) |
|
||||||
{ |
|
||||||
if ((_payloadLength + size) > MAX_PKT_LENGTH) { |
|
||||||
size = MAX_PKT_LENGTH - _payloadLength; |
|
||||||
} |
|
||||||
|
|
||||||
// write data
|
|
||||||
writeBuffer(buffer, size); |
|
||||||
_payloadLength = _payloadLength + size; |
|
||||||
return size; |
|
||||||
} |
|
||||||
|
|
||||||
int ISR_VECT sx126x::available() |
|
||||||
{ |
|
||||||
uint8_t buf[2] = {0}; |
|
||||||
executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, buf, 2); |
|
||||||
return buf[0] - _packetIndex; |
|
||||||
} |
|
||||||
|
|
||||||
int ISR_VECT sx126x::read() |
|
||||||
{ |
|
||||||
if (!available()) { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
// if received new packet
|
|
||||||
if (_packetIndex == 0) { |
|
||||||
uint8_t rxbuf[2] = {0}; |
|
||||||
executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2); |
|
||||||
int size = rxbuf[0]; |
|
||||||
_fifo_rx_addr_ptr = rxbuf[1]; |
|
||||||
|
|
||||||
readBuffer(_packet, size); |
|
||||||
} |
|
||||||
|
|
||||||
uint8_t byte = _packet[_packetIndex]; |
|
||||||
_packetIndex++; |
|
||||||
return byte; |
|
||||||
} |
|
||||||
|
|
||||||
int sx126x::peek() |
|
||||||
{ |
|
||||||
if (!available()) { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
// if received new packet
|
|
||||||
if (_packetIndex == 0) { |
|
||||||
uint8_t rxbuf[2] = {0}; |
|
||||||
executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2); |
|
||||||
int size = rxbuf[0]; |
|
||||||
_fifo_rx_addr_ptr = rxbuf[1]; |
|
||||||
|
|
||||||
readBuffer(_packet, size); |
|
||||||
} |
|
||||||
|
|
||||||
uint8_t b = _packet[_packetIndex]; |
|
||||||
return b; |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::flush() |
|
||||||
{ |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::onReceive(void(*callback)(int)) |
|
||||||
{ |
|
||||||
_onReceive = callback; |
|
||||||
|
|
||||||
if (callback) { |
|
||||||
pinMode(_dio0, INPUT); |
|
||||||
|
|
||||||
// set preamble and header detection irqs, plus dio0 mask
|
|
||||||
uint8_t buf[8]; |
|
||||||
|
|
||||||
// set irq masks, enable all
|
|
||||||
buf[0] = 0xFF;
|
|
||||||
buf[1] = 0xFF; |
|
||||||
|
|
||||||
// set dio0 masks
|
|
||||||
buf[2] = 0x00; |
|
||||||
buf[3] = IRQ_RX_DONE_MASK_6X;
|
|
||||||
|
|
||||||
// set dio1 masks
|
|
||||||
buf[4] = 0x00;
|
|
||||||
buf[5] = 0x00; |
|
||||||
|
|
||||||
// set dio2 masks
|
|
||||||
buf[6] = 0x00;
|
|
||||||
buf[7] = 0x00; |
|
||||||
|
|
||||||
executeOpcode(OP_SET_IRQ_FLAGS_6X, buf, 8); |
|
||||||
#ifdef SPI_HAS_NOTUSINGINTERRUPT |
|
||||||
SPI.usingInterrupt(digitalPinToInterrupt(_dio0)); |
|
||||||
#endif |
|
||||||
attachInterrupt(digitalPinToInterrupt(_dio0), sx126x::onDio0Rise, RISING); |
|
||||||
} else { |
|
||||||
detachInterrupt(digitalPinToInterrupt(_dio0)); |
|
||||||
#ifdef SPI_HAS_NOTUSINGINTERRUPT |
|
||||||
SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0)); |
|
||||||
#endif |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::receive(int size) |
|
||||||
{ |
|
||||||
if (size > 0) { |
|
||||||
implicitHeaderMode(); |
|
||||||
|
|
||||||
// tell radio payload length
|
|
||||||
_payloadLength = size; |
|
||||||
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); |
|
||||||
} else { |
|
||||||
explicitHeaderMode(); |
|
||||||
} |
|
||||||
|
|
||||||
if (_rxen != -1) { |
|
||||||
rxAntEnable(); |
|
||||||
} |
|
||||||
|
|
||||||
uint8_t mode[3] = {0xFF, 0xFF, 0xFF}; // continuous mode
|
|
||||||
executeOpcode(OP_RX_6X, mode, 3); |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::standby() |
|
||||||
{ |
|
||||||
// STDBY_XOSC
|
|
||||||
uint8_t byte = MODE_STDBY_XOSC_6X; |
|
||||||
// STDBY_RC
|
|
||||||
// uint8_t byte = MODE_STDBY_RC_6X;
|
|
||||||
executeOpcode(OP_STANDBY_6X, &byte, 1);
|
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::sleep() |
|
||||||
{ |
|
||||||
uint8_t byte = 0x00; |
|
||||||
executeOpcode(OP_SLEEP_6X, &byte, 1); |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::enableTCXO() { |
|
||||||
#if HAS_TCXO |
|
||||||
#if BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_HELTEC32_V3 |
|
||||||
uint8_t buf[4] = {MODE_TCXO_3_3V_6X, 0x00, 0x00, 0xFF}; |
|
||||||
#elif BOARD_MODEL == BOARD_TBEAM |
|
||||||
uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF}; |
|
||||||
#elif BOARD_MODEL == BOARD_RNODE_NG_22 |
|
||||||
uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF}; |
|
||||||
#endif |
|
||||||
executeOpcode(OP_DIO3_TCXO_CTRL_6X, buf, 4); |
|
||||||
#endif |
|
||||||
} |
|
||||||
|
|
||||||
// TODO: Once enabled, SX1262 needs a complete reset to disable TCXO
|
|
||||||
void sx126x::disableTCXO() { } |
|
||||||
|
|
||||||
void sx126x::setTxPower(int level, int outputPin) { |
|
||||||
// currently no low power mode for SX1262 implemented, assuming PA boost
|
|
||||||
|
|
||||||
// WORKAROUND - Better Resistance of the SX1262 Tx to Antenna Mismatch, see DS_SX1261-2_V1.2 datasheet chapter 15.2
|
|
||||||
// RegTxClampConfig = @address 0x08D8
|
|
||||||
writeRegister(0x08D8, readRegister(0x08D8) | (0x0F << 1)); |
|
||||||
|
|
||||||
uint8_t pa_buf[4]; |
|
||||||
|
|
||||||
pa_buf[0] = 0x04; // PADutyCycle needs to be 0x04 to achieve 22dBm output, but can be lowered for better efficiency at lower outputs
|
|
||||||
pa_buf[1] = 0x07; // HPMax at 0x07 is maximum supported for SX1262
|
|
||||||
pa_buf[2] = 0x00; // DeviceSel 0x00 for SX1262 (0x01 for SX1261)
|
|
||||||
pa_buf[3] = 0x01; // PALut always 0x01 (reserved according to datasheet)
|
|
||||||
|
|
||||||
executeOpcode(OP_PA_CONFIG_6X, pa_buf, 4); // set pa_config for high power
|
|
||||||
|
|
||||||
if (level > 22) { level = 22; } |
|
||||||
else if (level < -9) { level = -9; } |
|
||||||
|
|
||||||
writeRegister(REG_OCP_6X, 0x38); // 160mA limit, overcurrent protection
|
|
||||||
|
|
||||||
uint8_t tx_buf[2]; |
|
||||||
|
|
||||||
tx_buf[0] = level; |
|
||||||
tx_buf[1] = 0x02; // PA ramping time - 40 microseconds
|
|
||||||
|
|
||||||
executeOpcode(OP_TX_PARAMS_6X, tx_buf, 2); |
|
||||||
|
|
||||||
_txp = level; |
|
||||||
} |
|
||||||
|
|
||||||
uint8_t sx126x::getTxPower() { |
|
||||||
return _txp; |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::setFrequency(long frequency) { |
|
||||||
_frequency = frequency; |
|
||||||
|
|
||||||
uint8_t buf[4]; |
|
||||||
|
|
||||||
uint32_t freq = (uint32_t)((double)frequency / (double)FREQ_STEP_6X); |
|
||||||
|
|
||||||
buf[0] = ((freq >> 24) & 0xFF); |
|
||||||
buf[1] = ((freq >> 16) & 0xFF); |
|
||||||
buf[2] = ((freq >> 8) & 0xFF); |
|
||||||
buf[3] = (freq & 0xFF); |
|
||||||
|
|
||||||
executeOpcode(OP_RF_FREQ_6X, buf, 4); |
|
||||||
} |
|
||||||
|
|
||||||
uint32_t sx126x::getFrequency() { |
|
||||||
// we can't read the frequency on the sx1262 / 80
|
|
||||||
uint32_t frequency = _frequency; |
|
||||||
|
|
||||||
return frequency; |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::setSpreadingFactor(int sf) |
|
||||||
{ |
|
||||||
if (sf < 5) { |
|
||||||
sf = 5; |
|
||||||
} else if (sf > 12) { |
|
||||||
sf = 12; |
|
||||||
} |
|
||||||
|
|
||||||
_sf = sf; |
|
||||||
|
|
||||||
handleLowDataRate(); |
|
||||||
setModulationParams(sf, _bw, _cr, _ldro); |
|
||||||
} |
|
||||||
|
|
||||||
long sx126x::getSignalBandwidth() |
|
||||||
{ |
|
||||||
int bw = _bw; |
|
||||||
switch (bw) { |
|
||||||
case 0x00: return 7.8E3; |
|
||||||
case 0x01: return 15.6E3; |
|
||||||
case 0x02: return 31.25E3; |
|
||||||
case 0x03: return 62.5E3; |
|
||||||
case 0x04: return 125E3; |
|
||||||
case 0x05: return 250E3; |
|
||||||
case 0x06: return 500E3; |
|
||||||
case 0x08: return 10.4E3; |
|
||||||
case 0x09: return 20.8E3; |
|
||||||
case 0x0A: return 41.7E3; |
|
||||||
} |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::handleLowDataRate(){ |
|
||||||
if ( long( (1<<_sf) / (getSignalBandwidth()/1000)) > 16) { |
|
||||||
_ldro = 0x01; |
|
||||||
} else { |
|
||||||
_ldro = 0x00; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::optimizeModemSensitivity(){ |
|
||||||
// todo: check if there's anything the sx1262 can do here
|
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::setSignalBandwidth(long sbw) |
|
||||||
{ |
|
||||||
if (sbw <= 7.8E3) { |
|
||||||
_bw = 0x00; |
|
||||||
} else if (sbw <= 10.4E3) { |
|
||||||
_bw = 0x08; |
|
||||||
} else if (sbw <= 15.6E3) { |
|
||||||
_bw = 0x01; |
|
||||||
} else if (sbw <= 20.8E3) { |
|
||||||
_bw = 0x09; |
|
||||||
} else if (sbw <= 31.25E3) { |
|
||||||
_bw = 0x02; |
|
||||||
} else if (sbw <= 41.7E3) { |
|
||||||
_bw = 0x0A; |
|
||||||
} else if (sbw <= 62.5E3) { |
|
||||||
_bw = 0x03; |
|
||||||
} else if (sbw <= 125E3) { |
|
||||||
_bw = 0x04; |
|
||||||
} else if (sbw <= 250E3) { |
|
||||||
_bw = 0x05; |
|
||||||
} else /*if (sbw <= 250E3)*/ { |
|
||||||
_bw = 0x06; |
|
||||||
} |
|
||||||
|
|
||||||
handleLowDataRate(); |
|
||||||
setModulationParams(_sf, _bw, _cr, _ldro); |
|
||||||
|
|
||||||
optimizeModemSensitivity(); |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::setCodingRate4(int denominator) |
|
||||||
{ |
|
||||||
if (denominator < 5) { |
|
||||||
denominator = 5; |
|
||||||
} else if (denominator > 8) { |
|
||||||
denominator = 8; |
|
||||||
} |
|
||||||
|
|
||||||
int cr = denominator - 4; |
|
||||||
|
|
||||||
_cr = cr; |
|
||||||
|
|
||||||
setModulationParams(_sf, _bw, cr, _ldro); |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::setPreambleLength(long length) |
|
||||||
{ |
|
||||||
_preambleLength = length; |
|
||||||
setPacketParams(length, _implicitHeaderMode, _payloadLength, _crcMode); |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::setSyncWord(uint16_t sw) |
|
||||||
{ |
|
||||||
// TODO: Fix
|
|
||||||
// writeRegister(REG_SYNC_WORD_MSB_6X, (sw & 0xFF00) >> 8);
|
|
||||||
// writeRegister(REG_SYNC_WORD_LSB_6X, sw & 0x00FF);
|
|
||||||
writeRegister(REG_SYNC_WORD_MSB_6X, 0x14); |
|
||||||
writeRegister(REG_SYNC_WORD_LSB_6X, 0x24); |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::enableCrc() |
|
||||||
{ |
|
||||||
_crcMode = 1; |
|
||||||
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::disableCrc() |
|
||||||
{ |
|
||||||
_crcMode = 0; |
|
||||||
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); |
|
||||||
} |
|
||||||
|
|
||||||
byte sx126x::random() |
|
||||||
{ |
|
||||||
return readRegister(REG_RANDOM_GEN_6X); |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::setPins(int ss, int reset, int dio0, int busy, int rxen) |
|
||||||
{ |
|
||||||
_ss = ss; |
|
||||||
_reset = reset; |
|
||||||
_dio0 = dio0; |
|
||||||
_busy = busy; |
|
||||||
_rxen = rxen; |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::setSPIFrequency(uint32_t frequency) |
|
||||||
{ |
|
||||||
_spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::dumpRegisters(Stream& out) |
|
||||||
{ |
|
||||||
for (int i = 0; i < 128; i++) { |
|
||||||
out.print("0x"); |
|
||||||
out.print(i, HEX); |
|
||||||
out.print(": 0x"); |
|
||||||
out.println(readRegister(i), HEX); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::explicitHeaderMode() |
|
||||||
{ |
|
||||||
_implicitHeaderMode = 0; |
|
||||||
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); |
|
||||||
} |
|
||||||
|
|
||||||
void sx126x::implicitHeaderMode() |
|
||||||
{ |
|
||||||
_implicitHeaderMode = 1; |
|
||||||
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
void ISR_VECT sx126x::handleDio0Rise() |
|
||||||
{ |
|
||||||
uint8_t buf[2]; |
|
||||||
|
|
||||||
buf[0] = 0x00; |
|
||||||
buf[1] = 0x00; |
|
||||||
|
|
||||||
executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2); |
|
||||||
|
|
||||||
executeOpcode(OP_CLEAR_IRQ_STATUS_6X, buf, 2); |
|
||||||
|
|
||||||
if ((buf[1] & IRQ_PAYLOAD_CRC_ERROR_MASK_6X) == 0) { |
|
||||||
// received a packet
|
|
||||||
_packetIndex = 0; |
|
||||||
|
|
||||||
// read packet length
|
|
||||||
uint8_t rxbuf[2] = {0}; |
|
||||||
executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2); |
|
||||||
int packetLength = rxbuf[0]; |
|
||||||
|
|
||||||
if (_onReceive) { |
|
||||||
_onReceive(packetLength); |
|
||||||
} |
|
||||||
} |
|
||||||
// else {
|
|
||||||
// Serial.println("CRCE");
|
|
||||||
// Serial.println(buf[0]);
|
|
||||||
// Serial.println(buf[1]);
|
|
||||||
// }
|
|
||||||
} |
|
||||||
|
|
||||||
void ISR_VECT sx126x::onDio0Rise() |
|
||||||
{ |
|
||||||
sx126x_modem.handleDio0Rise(); |
|
||||||
} |
|
||||||
|
|
||||||
sx126x sx126x_modem; |
|
||||||
|
|
||||||
#endif |
|
@ -1,147 +0,0 @@ |
|||||||
// Copyright (c) Sandeep Mistry. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
// Modifications and additions copyright 2023 by Mark Qvist
|
|
||||||
// Obviously still under the MIT license.
|
|
||||||
|
|
||||||
#ifndef SX126X_H |
|
||||||
#define SX126X_H |
|
||||||
|
|
||||||
#include <Arduino.h> |
|
||||||
#include <SPI.h> |
|
||||||
#include "Interfaces.h" |
|
||||||
|
|
||||||
#define LORA_DEFAULT_SS_PIN 10 |
|
||||||
#define LORA_DEFAULT_RESET_PIN 9 |
|
||||||
#define LORA_DEFAULT_DIO0_PIN 2 |
|
||||||
#define LORA_DEFAULT_RXEN_PIN -1 |
|
||||||
#define LORA_DEFAULT_TXEN_PIN -1 |
|
||||||
#define LORA_DEFAULT_BUSY_PIN 11 |
|
||||||
|
|
||||||
#define PA_OUTPUT_RFO_PIN 0 |
|
||||||
#define PA_OUTPUT_PA_BOOST_PIN 1 |
|
||||||
|
|
||||||
#define RSSI_OFFSET 157 |
|
||||||
|
|
||||||
class sx126x : public Stream { |
|
||||||
public: |
|
||||||
sx126x(); |
|
||||||
|
|
||||||
int begin(long frequency); |
|
||||||
void end(); |
|
||||||
|
|
||||||
int beginPacket(int implicitHeader = false); |
|
||||||
int endPacket(); |
|
||||||
|
|
||||||
int parsePacket(int size = 0); |
|
||||||
int packetRssi(); |
|
||||||
int currentRssi(); |
|
||||||
uint8_t packetRssiRaw(); |
|
||||||
uint8_t currentRssiRaw(); |
|
||||||
uint8_t packetSnrRaw(); |
|
||||||
float packetSnr(); |
|
||||||
long packetFrequencyError(); |
|
||||||
|
|
||||||
// from Print
|
|
||||||
virtual size_t write(uint8_t byte); |
|
||||||
virtual size_t write(const uint8_t *buffer, size_t size); |
|
||||||
|
|
||||||
// from Stream
|
|
||||||
virtual int available(); |
|
||||||
virtual int read(); |
|
||||||
virtual int peek(); |
|
||||||
virtual void flush(); |
|
||||||
|
|
||||||
void onReceive(void(*callback)(int)); |
|
||||||
|
|
||||||
void receive(int size = 0); |
|
||||||
void standby(); |
|
||||||
void sleep(); |
|
||||||
|
|
||||||
bool preInit(); |
|
||||||
uint8_t getTxPower(); |
|
||||||
void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); |
|
||||||
uint32_t getFrequency(); |
|
||||||
void setFrequency(long frequency); |
|
||||||
void setSpreadingFactor(int sf); |
|
||||||
long getSignalBandwidth(); |
|
||||||
void setSignalBandwidth(long sbw); |
|
||||||
void setCodingRate4(int denominator); |
|
||||||
void setPreambleLength(long length); |
|
||||||
void setSyncWord(uint16_t sw); |
|
||||||
uint8_t modemStatus(); |
|
||||||
void enableCrc(); |
|
||||||
void disableCrc(); |
|
||||||
void enableTCXO(); |
|
||||||
void disableTCXO(); |
|
||||||
|
|
||||||
void rxAntEnable(); |
|
||||||
void loraMode(); |
|
||||||
void waitOnBusy(); |
|
||||||
void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size); |
|
||||||
void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size); |
|
||||||
void writeBuffer(const uint8_t* buffer, size_t size); |
|
||||||
void readBuffer(uint8_t* buffer, size_t size); |
|
||||||
void setPacketParams(long preamble, uint8_t headermode, uint8_t length, uint8_t crc); |
|
||||||
|
|
||||||
void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro); |
|
||||||
|
|
||||||
// deprecated
|
|
||||||
void crc() { enableCrc(); } |
|
||||||
void noCrc() { disableCrc(); } |
|
||||||
|
|
||||||
byte random(); |
|
||||||
|
|
||||||
void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN, int busy = LORA_DEFAULT_BUSY_PIN, int rxen = LORA_DEFAULT_RXEN_PIN); |
|
||||||
void setSPIFrequency(uint32_t frequency); |
|
||||||
|
|
||||||
void dumpRegisters(Stream& out); |
|
||||||
|
|
||||||
private: |
|
||||||
void explicitHeaderMode(); |
|
||||||
void implicitHeaderMode(); |
|
||||||
|
|
||||||
void handleDio0Rise(); |
|
||||||
|
|
||||||
uint8_t readRegister(uint16_t address); |
|
||||||
void writeRegister(uint16_t address, uint8_t value); |
|
||||||
uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value); |
|
||||||
|
|
||||||
static void onDio0Rise(); |
|
||||||
|
|
||||||
void handleLowDataRate(); |
|
||||||
void optimizeModemSensitivity(); |
|
||||||
|
|
||||||
void reset(void); |
|
||||||
void calibrate(void); |
|
||||||
void calibrate_image(long frequency); |
|
||||||
|
|
||||||
private: |
|
||||||
SPISettings _spiSettings; |
|
||||||
int _ss; |
|
||||||
int _reset; |
|
||||||
int _dio0; |
|
||||||
int _rxen; |
|
||||||
int _busy; |
|
||||||
long _frequency; |
|
||||||
int _txp; |
|
||||||
uint8_t _sf; |
|
||||||
uint8_t _bw; |
|
||||||
uint8_t _cr; |
|
||||||
uint8_t _ldro; |
|
||||||
int _packetIndex; |
|
||||||
int _preambleLength; |
|
||||||
int _implicitHeaderMode; |
|
||||||
int _payloadLength; |
|
||||||
int _crcMode; |
|
||||||
int _fifo_tx_addr_ptr; |
|
||||||
int _fifo_rx_addr_ptr; |
|
||||||
uint8_t _packet[255]; |
|
||||||
bool _preinit_done; |
|
||||||
void (*_onReceive)(int); |
|
||||||
bool _tcxo; |
|
||||||
}; |
|
||||||
|
|
||||||
extern sx126x sx126x_modem; |
|
||||||
|
|
||||||
#endif |
|
@ -1,498 +0,0 @@ |
|||||||
// Copyright (c) Sandeep Mistry. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
// Modifications and additions copyright 2023 by Mark Qvist
|
|
||||||
// Obviously still under the MIT license.
|
|
||||||
|
|
||||||
#include "Boards.h" |
|
||||||
|
|
||||||
#if MODEM == SX1276 |
|
||||||
#include "sx127x.h" |
|
||||||
|
|
||||||
#if MCU_VARIANT == MCU_ESP32 |
|
||||||
#if MCU_VARIANT == MCU_ESP32 and !defined(CONFIG_IDF_TARGET_ESP32S3) |
|
||||||
#include "soc/rtc_wdt.h" |
|
||||||
#endif |
|
||||||
#define ISR_VECT IRAM_ATTR |
|
||||||
#else |
|
||||||
#define ISR_VECT |
|
||||||
#endif |
|
||||||
|
|
||||||
// Registers
|
|
||||||
#define REG_FIFO_7X 0x00 |
|
||||||
#define REG_OP_MODE_7X 0x01 |
|
||||||
#define REG_FRF_MSB_7X 0x06 |
|
||||||
#define REG_FRF_MID_7X 0x07 |
|
||||||
#define REG_FRF_LSB_7X 0x08 |
|
||||||
#define REG_PA_CONFIG_7X 0x09 |
|
||||||
#define REG_OCP_7X 0x0b |
|
||||||
#define REG_LNA_7X 0x0c |
|
||||||
#define REG_FIFO_ADDR_PTR_7X 0x0d |
|
||||||
#define REG_FIFO_TX_BASE_ADDR_7X 0x0e |
|
||||||
#define REG_FIFO_RX_BASE_ADDR_7X 0x0f |
|
||||||
#define REG_FIFO_RX_CURRENT_ADDR_7X 0x10 |
|
||||||
#define REG_IRQ_FLAGS_7X 0x12 |
|
||||||
#define REG_RX_NB_BYTES_7X 0x13 |
|
||||||
#define REG_MODEM_STAT_7X 0x18 |
|
||||||
#define REG_PKT_SNR_VALUE_7X 0x19 |
|
||||||
#define REG_PKT_RSSI_VALUE_7X 0x1a |
|
||||||
#define REG_RSSI_VALUE_7X 0x1b |
|
||||||
#define REG_MODEM_CONFIG_1_7X 0x1d |
|
||||||
#define REG_MODEM_CONFIG_2_7X 0x1e |
|
||||||
#define REG_PREAMBLE_MSB_7X 0x20 |
|
||||||
#define REG_PREAMBLE_LSB_7X 0x21 |
|
||||||
#define REG_PAYLOAD_LENGTH_7X 0x22 |
|
||||||
#define REG_MODEM_CONFIG_3_7X 0x26 |
|
||||||
#define REG_FREQ_ERROR_MSB_7X 0x28 |
|
||||||
#define REG_FREQ_ERROR_MID_7X 0x29 |
|
||||||
#define REG_FREQ_ERROR_LSB_7X 0x2a |
|
||||||
#define REG_RSSI_WIDEBAND_7X 0x2c |
|
||||||
#define REG_DETECTION_OPTIMIZE_7X 0x31 |
|
||||||
#define REG_HIGH_BW_OPTIMIZE_1_7X 0x36 |
|
||||||
#define REG_DETECTION_THRESHOLD_7X 0x37 |
|
||||||
#define REG_SYNC_WORD_7X 0x39 |
|
||||||
#define REG_HIGH_BW_OPTIMIZE_2_7X 0x3a |
|
||||||
#define REG_DIO_MAPPING_1_7X 0x40 |
|
||||||
#define REG_VERSION_7X 0x42 |
|
||||||
#define REG_TCXO_7X 0x4b |
|
||||||
#define REG_PA_DAC_7X 0x4d |
|
||||||
|
|
||||||
// Modes
|
|
||||||
#define MODE_LONG_RANGE_MODE_7X 0x80 |
|
||||||
#define MODE_SLEEP_7X 0x00 |
|
||||||
#define MODE_STDBY_7X 0x01 |
|
||||||
#define MODE_TX_7X 0x03 |
|
||||||
#define MODE_RX_CONTINUOUS_7X 0x05 |
|
||||||
#define MODE_RX_SINGLE_7X 0x06 |
|
||||||
|
|
||||||
// PA config
|
|
||||||
#define PA_BOOST_7X 0x80 |
|
||||||
|
|
||||||
// IRQ masks
|
|
||||||
#define IRQ_TX_DONE_MASK_7X 0x08 |
|
||||||
#define IRQ_RX_DONE_MASK_7X 0x40 |
|
||||||
#define IRQ_PAYLOAD_CRC_ERROR_MASK_7X 0x20 |
|
||||||
|
|
||||||
#define SYNC_WORD_7X 0x12 |
|
||||||
#define MAX_PKT_LENGTH 255 |
|
||||||
|
|
||||||
extern SPIClass SPI; |
|
||||||
|
|
||||||
sx127x::sx127x() : |
|
||||||
_spiSettings(8E6, MSBFIRST, SPI_MODE0), |
|
||||||
_ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), |
|
||||||
_frequency(0), |
|
||||||
_packetIndex(0), |
|
||||||
_preinit_done(false), |
|
||||||
_onReceive(NULL) { setTimeout(0); } |
|
||||||
|
|
||||||
void sx127x::setSPIFrequency(uint32_t frequency) { _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); } |
|
||||||
void sx127x::setPins(int ss, int reset, int dio0, int busy) { _ss = ss; _reset = reset; _dio0 = dio0; _busy = busy; } |
|
||||||
uint8_t ISR_VECT sx127x::readRegister(uint8_t address) { return singleTransfer(address & 0x7f, 0x00); } |
|
||||||
void sx127x::writeRegister(uint8_t address, uint8_t value) { singleTransfer(address | 0x80, value); } |
|
||||||
void sx127x::standby() { writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_STDBY_7X); } |
|
||||||
void sx127x::sleep() { writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_SLEEP_7X); } |
|
||||||
uint8_t sx127x::modemStatus() { return readRegister(REG_MODEM_STAT_7X); } |
|
||||||
void sx127x::setSyncWord(uint8_t sw) { writeRegister(REG_SYNC_WORD_7X, sw); } |
|
||||||
void sx127x::enableCrc() { writeRegister(REG_MODEM_CONFIG_2_7X, readRegister(REG_MODEM_CONFIG_2_7X) | 0x04); } |
|
||||||
void sx127x::disableCrc() { writeRegister(REG_MODEM_CONFIG_2_7X, readRegister(REG_MODEM_CONFIG_2_7X) & 0xfb); } |
|
||||||
void sx127x::enableTCXO() { uint8_t tcxo_reg = readRegister(REG_TCXO_7X); writeRegister(REG_TCXO_7X, tcxo_reg | 0x10); } |
|
||||||
void sx127x::disableTCXO() { uint8_t tcxo_reg = readRegister(REG_TCXO_7X); writeRegister(REG_TCXO_7X, tcxo_reg & 0xEF); } |
|
||||||
void sx127x::explicitHeaderMode() { _implicitHeaderMode = 0; writeRegister(REG_MODEM_CONFIG_1_7X, readRegister(REG_MODEM_CONFIG_1_7X) & 0xfe); } |
|
||||||
void sx127x::implicitHeaderMode() { _implicitHeaderMode = 1; writeRegister(REG_MODEM_CONFIG_1_7X, readRegister(REG_MODEM_CONFIG_1_7X) | 0x01); } |
|
||||||
byte sx127x::random() { return readRegister(REG_RSSI_WIDEBAND_7X); } |
|
||||||
void sx127x::flush() { } |
|
||||||
|
|
||||||
bool sx127x::preInit() { |
|
||||||
pinMode(_ss, OUTPUT); |
|
||||||
digitalWrite(_ss, HIGH); |
|
||||||
SPI.begin(); |
|
||||||
|
|
||||||
// Check modem version
|
|
||||||
uint8_t version; |
|
||||||
long start = millis(); |
|
||||||
while (((millis() - start) < 500) && (millis() >= start)) { |
|
||||||
version = readRegister(REG_VERSION_7X); |
|
||||||
if (version == 0x12) { break; } |
|
||||||
delay(100); |
|
||||||
} |
|
||||||
|
|
||||||
if (version != 0x12) { return false; } |
|
||||||
|
|
||||||
_preinit_done = true; |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
uint8_t ISR_VECT sx127x::singleTransfer(uint8_t address, uint8_t value) { |
|
||||||
uint8_t response; |
|
||||||
|
|
||||||
digitalWrite(_ss, LOW); |
|
||||||
SPI.beginTransaction(_spiSettings); |
|
||||||
SPI.transfer(address); |
|
||||||
response = SPI.transfer(value); |
|
||||||
SPI.endTransaction(); |
|
||||||
digitalWrite(_ss, HIGH); |
|
||||||
|
|
||||||
return response; |
|
||||||
} |
|
||||||
|
|
||||||
int sx127x::begin(long frequency) { |
|
||||||
if (_reset != -1) { |
|
||||||
pinMode(_reset, OUTPUT); |
|
||||||
|
|
||||||
// Perform reset
|
|
||||||
digitalWrite(_reset, LOW); |
|
||||||
delay(10); |
|
||||||
digitalWrite(_reset, HIGH); |
|
||||||
delay(10); |
|
||||||
} |
|
||||||
|
|
||||||
if (_busy != -1) { pinMode(_busy, INPUT); } |
|
||||||
|
|
||||||
if (!_preinit_done) { |
|
||||||
if (!preInit()) { return false; } |
|
||||||
} |
|
||||||
|
|
||||||
sleep(); |
|
||||||
setFrequency(frequency); |
|
||||||
|
|
||||||
// set base addresses
|
|
||||||
writeRegister(REG_FIFO_TX_BASE_ADDR_7X, 0); |
|
||||||
writeRegister(REG_FIFO_RX_BASE_ADDR_7X, 0); |
|
||||||
|
|
||||||
// set LNA boost and auto AGC
|
|
||||||
writeRegister(REG_LNA_7X, readRegister(REG_LNA_7X) | 0x03); |
|
||||||
writeRegister(REG_MODEM_CONFIG_3_7X, 0x04); |
|
||||||
|
|
||||||
setSyncWord(SYNC_WORD_7X); |
|
||||||
enableCrc(); |
|
||||||
setTxPower(2); |
|
||||||
|
|
||||||
standby(); |
|
||||||
|
|
||||||
return 1; |
|
||||||
} |
|
||||||
|
|
||||||
void sx127x::end() { |
|
||||||
sleep(); |
|
||||||
SPI.end(); |
|
||||||
_preinit_done = false; |
|
||||||
} |
|
||||||
|
|
||||||
int sx127x::beginPacket(int implicitHeader) { |
|
||||||
standby(); |
|
||||||
|
|
||||||
if (implicitHeader) { |
|
||||||
implicitHeaderMode(); |
|
||||||
} else { |
|
||||||
explicitHeaderMode(); |
|
||||||
} |
|
||||||
|
|
||||||
// Reset FIFO address and payload length
|
|
||||||
writeRegister(REG_FIFO_ADDR_PTR_7X, 0); |
|
||||||
writeRegister(REG_PAYLOAD_LENGTH_7X, 0); |
|
||||||
|
|
||||||
return 1; |
|
||||||
} |
|
||||||
|
|
||||||
int sx127x::endPacket() { |
|
||||||
// Enter TX mode
|
|
||||||
writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_TX_7X); |
|
||||||
|
|
||||||
// Wait for TX completion
|
|
||||||
while ((readRegister(REG_IRQ_FLAGS_7X) & IRQ_TX_DONE_MASK_7X) == 0) { |
|
||||||
yield(); |
|
||||||
} |
|
||||||
|
|
||||||
// Clear TX complete IRQ
|
|
||||||
writeRegister(REG_IRQ_FLAGS_7X, IRQ_TX_DONE_MASK_7X); |
|
||||||
return 1; |
|
||||||
} |
|
||||||
|
|
||||||
uint8_t sx127x::currentRssiRaw() { |
|
||||||
uint8_t rssi = readRegister(REG_RSSI_VALUE_7X); |
|
||||||
return rssi; |
|
||||||
} |
|
||||||
|
|
||||||
int ISR_VECT sx127x::currentRssi() { |
|
||||||
int rssi = (int)readRegister(REG_RSSI_VALUE_7X) - RSSI_OFFSET; |
|
||||||
if (_frequency < 820E6) rssi -= 7; |
|
||||||
return rssi; |
|
||||||
} |
|
||||||
|
|
||||||
uint8_t sx127x::packetRssiRaw() { |
|
||||||
uint8_t pkt_rssi_value = readRegister(REG_PKT_RSSI_VALUE_7X); |
|
||||||
return pkt_rssi_value; |
|
||||||
} |
|
||||||
|
|
||||||
int ISR_VECT sx127x::packetRssi() { |
|
||||||
int pkt_rssi = (int)readRegister(REG_PKT_RSSI_VALUE_7X) - RSSI_OFFSET; |
|
||||||
int pkt_snr = packetSnr(); |
|
||||||
|
|
||||||
if (_frequency < 820E6) pkt_rssi -= 7; |
|
||||||
|
|
||||||
if (pkt_snr < 0) { |
|
||||||
pkt_rssi += pkt_snr; |
|
||||||
} else { |
|
||||||
// Slope correction is (16/15)*pkt_rssi,
|
|
||||||
// this estimation looses one floating point
|
|
||||||
// operation, and should be precise enough.
|
|
||||||
pkt_rssi = (int)(1.066 * pkt_rssi); |
|
||||||
} |
|
||||||
return pkt_rssi; |
|
||||||
} |
|
||||||
|
|
||||||
uint8_t ISR_VECT sx127x::packetSnrRaw() { |
|
||||||
return readRegister(REG_PKT_SNR_VALUE_7X); |
|
||||||
} |
|
||||||
|
|
||||||
float ISR_VECT sx127x::packetSnr() { |
|
||||||
return ((int8_t)readRegister(REG_PKT_SNR_VALUE_7X)) * 0.25; |
|
||||||
} |
|
||||||
|
|
||||||
long sx127x::packetFrequencyError() { |
|
||||||
int32_t freqError = 0; |
|
||||||
freqError = static_cast<int32_t>(readRegister(REG_FREQ_ERROR_MSB_7X) & B111); |
|
||||||
freqError <<= 8L; |
|
||||||
freqError += static_cast<int32_t>(readRegister(REG_FREQ_ERROR_MID_7X)); |
|
||||||
freqError <<= 8L; |
|
||||||
freqError += static_cast<int32_t>(readRegister(REG_FREQ_ERROR_LSB_7X)); |
|
||||||
|
|
||||||
if (readRegister(REG_FREQ_ERROR_MSB_7X) & B1000) { // Sign bit is on
|
|
||||||
freqError -= 524288; // B1000'0000'0000'0000'0000
|
|
||||||
} |
|
||||||
|
|
||||||
const float fXtal = 32E6; // FXOSC: crystal oscillator (XTAL) frequency (2.5. Chip Specification, p. 14)
|
|
||||||
const float fError = ((static_cast<float>(freqError) * (1L << 24)) / fXtal) * (getSignalBandwidth() / 500000.0f); |
|
||||||
|
|
||||||
return static_cast<long>(fError); |
|
||||||
} |
|
||||||
|
|
||||||
size_t sx127x::write(uint8_t byte) { return write(&byte, sizeof(byte)); } |
|
||||||
|
|
||||||
size_t sx127x::write(const uint8_t *buffer, size_t size) { |
|
||||||
int currentLength = readRegister(REG_PAYLOAD_LENGTH_7X); |
|
||||||
if ((currentLength + size) > MAX_PKT_LENGTH) { |
|
||||||
size = MAX_PKT_LENGTH - currentLength; |
|
||||||
} |
|
||||||
|
|
||||||
for (size_t i = 0; i < size; i++) { |
|
||||||
writeRegister(REG_FIFO_7X, buffer[i]); |
|
||||||
} |
|
||||||
|
|
||||||
writeRegister(REG_PAYLOAD_LENGTH_7X, currentLength + size); |
|
||||||
return size; |
|
||||||
} |
|
||||||
|
|
||||||
int ISR_VECT sx127x::available() { return (readRegister(REG_RX_NB_BYTES_7X) - _packetIndex); } |
|
||||||
|
|
||||||
int ISR_VECT sx127x::read() { |
|
||||||
if (!available()) { return -1; } |
|
||||||
_packetIndex++; |
|
||||||
return readRegister(REG_FIFO_7X); |
|
||||||
} |
|
||||||
|
|
||||||
int sx127x::peek() { |
|
||||||
if (!available()) { return -1; } |
|
||||||
|
|
||||||
// Remember current FIFO address, read, and then reset address
|
|
||||||
int currentAddress = readRegister(REG_FIFO_ADDR_PTR_7X); |
|
||||||
uint8_t b = readRegister(REG_FIFO_7X); |
|
||||||
writeRegister(REG_FIFO_ADDR_PTR_7X, currentAddress); |
|
||||||
|
|
||||||
return b; |
|
||||||
} |
|
||||||
|
|
||||||
void sx127x::onReceive(void(*callback)(int)) { |
|
||||||
_onReceive = callback; |
|
||||||
|
|
||||||
if (callback) { |
|
||||||
pinMode(_dio0, INPUT); |
|
||||||
writeRegister(REG_DIO_MAPPING_1_7X, 0x00); |
|
||||||
|
|
||||||
#ifdef SPI_HAS_NOTUSINGINTERRUPT |
|
||||||
SPI.usingInterrupt(digitalPinToInterrupt(_dio0)); |
|
||||||
#endif |
|
||||||
|
|
||||||
attachInterrupt(digitalPinToInterrupt(_dio0), sx127x::onDio0Rise, RISING); |
|
||||||
|
|
||||||
} else { |
|
||||||
detachInterrupt(digitalPinToInterrupt(_dio0)); |
|
||||||
|
|
||||||
#ifdef SPI_HAS_NOTUSINGINTERRUPT |
|
||||||
SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0)); |
|
||||||
#endif |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void sx127x::receive(int size) { |
|
||||||
if (size > 0) { |
|
||||||
implicitHeaderMode(); |
|
||||||
writeRegister(REG_PAYLOAD_LENGTH_7X, size & 0xff); |
|
||||||
} else { explicitHeaderMode(); } |
|
||||||
|
|
||||||
writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_RX_CONTINUOUS_7X); |
|
||||||
} |
|
||||||
|
|
||||||
void sx127x::setTxPower(int level, int outputPin) { |
|
||||||
// Setup according to RFO or PA_BOOST output pin
|
|
||||||
if (PA_OUTPUT_RFO_PIN == outputPin) { |
|
||||||
if (level < 0) { level = 0; } |
|
||||||
else if (level > 14) { level = 14; } |
|
||||||
|
|
||||||
writeRegister(REG_PA_DAC_7X, 0x84); |
|
||||||
writeRegister(REG_PA_CONFIG_7X, 0x70 | level); |
|
||||||
|
|
||||||
} else { |
|
||||||
if (level < 2) { level = 2; } |
|
||||||
else if (level > 17) { level = 17; } |
|
||||||
|
|
||||||
writeRegister(REG_PA_DAC_7X, 0x84); |
|
||||||
writeRegister(REG_PA_CONFIG_7X, PA_BOOST_7X | (level - 2)); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
uint8_t sx127x::getTxPower() { byte txp = readRegister(REG_PA_CONFIG_7X); return txp; } |
|
||||||
|
|
||||||
void sx127x::setFrequency(unsigned long frequency) { |
|
||||||
_frequency = frequency; |
|
||||||
uint32_t frf = ((uint64_t)frequency << 19) / 32000000; |
|
||||||
|
|
||||||
writeRegister(REG_FRF_MSB_7X, (uint8_t)(frf >> 16)); |
|
||||||
writeRegister(REG_FRF_MID_7X, (uint8_t)(frf >> 8)); |
|
||||||
writeRegister(REG_FRF_LSB_7X, (uint8_t)(frf >> 0)); |
|
||||||
|
|
||||||
optimizeModemSensitivity(); |
|
||||||
} |
|
||||||
|
|
||||||
uint32_t sx127x::getFrequency() { |
|
||||||
uint8_t msb = readRegister(REG_FRF_MSB_7X); |
|
||||||
uint8_t mid = readRegister(REG_FRF_MID_7X); |
|
||||||
uint8_t lsb = readRegister(REG_FRF_LSB_7X); |
|
||||||
|
|
||||||
uint32_t frf = ((uint32_t)msb << 16) | ((uint32_t)mid << 8) | (uint32_t)lsb; |
|
||||||
uint64_t frm = (uint64_t)frf*32000000; |
|
||||||
uint32_t frequency = (frm >> 19); |
|
||||||
|
|
||||||
return frequency; |
|
||||||
} |
|
||||||
|
|
||||||
void sx127x::setSpreadingFactor(int sf) { |
|
||||||
if (sf < 6) { sf = 6; } |
|
||||||
else if (sf > 12) { sf = 12; } |
|
||||||
|
|
||||||
if (sf == 6) { |
|
||||||
writeRegister(REG_DETECTION_OPTIMIZE_7X, 0xc5); |
|
||||||
writeRegister(REG_DETECTION_THRESHOLD_7X, 0x0c); |
|
||||||
} else { |
|
||||||
writeRegister(REG_DETECTION_OPTIMIZE_7X, 0xc3); |
|
||||||
writeRegister(REG_DETECTION_THRESHOLD_7X, 0x0a); |
|
||||||
} |
|
||||||
|
|
||||||
writeRegister(REG_MODEM_CONFIG_2_7X, (readRegister(REG_MODEM_CONFIG_2_7X) & 0x0f) | ((sf << 4) & 0xf0)); |
|
||||||
handleLowDataRate(); |
|
||||||
} |
|
||||||
|
|
||||||
long sx127x::getSignalBandwidth() { |
|
||||||
byte bw = (readRegister(REG_MODEM_CONFIG_1_7X) >> 4); |
|
||||||
switch (bw) { |
|
||||||
case 0: return 7.8E3; |
|
||||||
case 1: return 10.4E3; |
|
||||||
case 2: return 15.6E3; |
|
||||||
case 3: return 20.8E3; |
|
||||||
case 4: return 31.25E3; |
|
||||||
case 5: return 41.7E3; |
|
||||||
case 6: return 62.5E3; |
|
||||||
case 7: return 125E3; |
|
||||||
case 8: return 250E3; |
|
||||||
case 9: return 500E3; } |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
void sx127x::setSignalBandwidth(long sbw) { |
|
||||||
int bw; |
|
||||||
if (sbw <= 7.8E3) { |
|
||||||
bw = 0; |
|
||||||
} else if (sbw <= 10.4E3) { |
|
||||||
bw = 1; |
|
||||||
} else if (sbw <= 15.6E3) { |
|
||||||
bw = 2; |
|
||||||
} else if (sbw <= 20.8E3) { |
|
||||||
bw = 3; |
|
||||||
} else if (sbw <= 31.25E3) { |
|
||||||
bw = 4; |
|
||||||
} else if (sbw <= 41.7E3) { |
|
||||||
bw = 5; |
|
||||||
} else if (sbw <= 62.5E3) { |
|
||||||
bw = 6; |
|
||||||
} else if (sbw <= 125E3) { |
|
||||||
bw = 7; |
|
||||||
} else if (sbw <= 250E3) { |
|
||||||
bw = 8; |
|
||||||
} else /*if (sbw <= 250E3)*/ { |
|
||||||
bw = 9; |
|
||||||
} |
|
||||||
|
|
||||||
writeRegister(REG_MODEM_CONFIG_1_7X, (readRegister(REG_MODEM_CONFIG_1_7X) & 0x0f) | (bw << 4)); |
|
||||||
handleLowDataRate(); |
|
||||||
optimizeModemSensitivity(); |
|
||||||
} |
|
||||||
|
|
||||||
void sx127x::setCodingRate4(int denominator) { |
|
||||||
if (denominator < 5) { denominator = 5; } |
|
||||||
else if (denominator > 8) { denominator = 8; } |
|
||||||
int cr = denominator - 4; |
|
||||||
writeRegister(REG_MODEM_CONFIG_1_7X, (readRegister(REG_MODEM_CONFIG_1_7X) & 0xf1) | (cr << 1)); |
|
||||||
} |
|
||||||
|
|
||||||
void sx127x::setPreambleLength(long length) {
|
|
||||||
writeRegister(REG_PREAMBLE_MSB_7X, (uint8_t)(length >> 8)); |
|
||||||
writeRegister(REG_PREAMBLE_LSB_7X, (uint8_t)(length >> 0)); |
|
||||||
} |
|
||||||
|
|
||||||
void sx127x::handleLowDataRate() { |
|
||||||
int sf = (readRegister(REG_MODEM_CONFIG_2_7X) >> 4); |
|
||||||
if ( long( (1<<sf) / (getSignalBandwidth()/1000)) > 16) { |
|
||||||
// Set auto AGC and LowDataRateOptimize
|
|
||||||
writeRegister(REG_MODEM_CONFIG_3_7X, (1<<3)|(1<<2)); |
|
||||||
} else { |
|
||||||
// Only set auto AGC
|
|
||||||
writeRegister(REG_MODEM_CONFIG_3_7X, (1<<2)); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void sx127x::optimizeModemSensitivity() { |
|
||||||
byte bw = (readRegister(REG_MODEM_CONFIG_1_7X) >> 4); |
|
||||||
uint32_t freq = getFrequency(); |
|
||||||
|
|
||||||
if (bw == 9 && (410E6 <= freq) && (freq <= 525E6)) { |
|
||||||
writeRegister(REG_HIGH_BW_OPTIMIZE_1_7X, 0x02); |
|
||||||
writeRegister(REG_HIGH_BW_OPTIMIZE_2_7X, 0x7f); |
|
||||||
} else if (bw == 9 && (820E6 <= freq) && (freq <= 1020E6)) { |
|
||||||
writeRegister(REG_HIGH_BW_OPTIMIZE_1_7X, 0x02); |
|
||||||
writeRegister(REG_HIGH_BW_OPTIMIZE_2_7X, 0x64); |
|
||||||
} else { |
|
||||||
writeRegister(REG_HIGH_BW_OPTIMIZE_1_7X, 0x03); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void ISR_VECT sx127x::handleDio0Rise() { |
|
||||||
int irqFlags = readRegister(REG_IRQ_FLAGS_7X); |
|
||||||
|
|
||||||
// Clear IRQs
|
|
||||||
writeRegister(REG_IRQ_FLAGS_7X, irqFlags); |
|
||||||
if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK_7X) == 0) { |
|
||||||
_packetIndex = 0; |
|
||||||
int packetLength = _implicitHeaderMode ? readRegister(REG_PAYLOAD_LENGTH_7X) : readRegister(REG_RX_NB_BYTES_7X); |
|
||||||
writeRegister(REG_FIFO_ADDR_PTR_7X, readRegister(REG_FIFO_RX_CURRENT_ADDR_7X)); |
|
||||||
if (_onReceive) { _onReceive(packetLength); } |
|
||||||
writeRegister(REG_FIFO_ADDR_PTR_7X, 0); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void ISR_VECT sx127x::onDio0Rise() { sx127x_modem.handleDio0Rise(); } |
|
||||||
|
|
||||||
sx127x sx127x_modem; |
|
||||||
|
|
||||||
#endif |
|
@ -1,111 +0,0 @@ |
|||||||
// Copyright (c) Sandeep Mistry. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
// Modifications and additions copyright 2023 by Mark Qvist
|
|
||||||
// Obviously still under the MIT license.
|
|
||||||
|
|
||||||
#ifndef SX1276_H |
|
||||||
#define SX1276_H |
|
||||||
|
|
||||||
#include <Arduino.h> |
|
||||||
#include <SPI.h> |
|
||||||
#include "Modem.h" |
|
||||||
|
|
||||||
#define LORA_DEFAULT_SS_PIN 10 |
|
||||||
#define LORA_DEFAULT_RESET_PIN 9 |
|
||||||
#define LORA_DEFAULT_DIO0_PIN 2 |
|
||||||
#define LORA_DEFAULT_BUSY_PIN -1 |
|
||||||
|
|
||||||
#define PA_OUTPUT_RFO_PIN 0 |
|
||||||
#define PA_OUTPUT_PA_BOOST_PIN 1 |
|
||||||
|
|
||||||
#define RSSI_OFFSET 157 |
|
||||||
|
|
||||||
class sx127x : public Stream { |
|
||||||
public: |
|
||||||
sx127x(); |
|
||||||
|
|
||||||
int begin(long frequency); |
|
||||||
void end(); |
|
||||||
|
|
||||||
int beginPacket(int implicitHeader = false); |
|
||||||
int endPacket(); |
|
||||||
|
|
||||||
int parsePacket(int size = 0); |
|
||||||
int packetRssi(); |
|
||||||
int currentRssi(); |
|
||||||
uint8_t packetRssiRaw(); |
|
||||||
uint8_t currentRssiRaw(); |
|
||||||
uint8_t packetSnrRaw(); |
|
||||||
float packetSnr(); |
|
||||||
long packetFrequencyError(); |
|
||||||
|
|
||||||
// from Print
|
|
||||||
virtual size_t write(uint8_t byte); |
|
||||||
virtual size_t write(const uint8_t *buffer, size_t size); |
|
||||||
|
|
||||||
// from Stream
|
|
||||||
virtual int available(); |
|
||||||
virtual int read(); |
|
||||||
virtual int peek(); |
|
||||||
virtual void flush(); |
|
||||||
|
|
||||||
void onReceive(void(*callback)(int)); |
|
||||||
|
|
||||||
void receive(int size = 0); |
|
||||||
void standby(); |
|
||||||
void sleep(); |
|
||||||
|
|
||||||
bool preInit(); |
|
||||||
uint8_t getTxPower(); |
|
||||||
void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); |
|
||||||
uint32_t getFrequency(); |
|
||||||
void setFrequency(unsigned long frequency); |
|
||||||
void setSpreadingFactor(int sf); |
|
||||||
long getSignalBandwidth(); |
|
||||||
void setSignalBandwidth(long sbw); |
|
||||||
void setCodingRate4(int denominator); |
|
||||||
void setPreambleLength(long length); |
|
||||||
void setSyncWord(uint8_t sw); |
|
||||||
uint8_t modemStatus(); |
|
||||||
void enableCrc(); |
|
||||||
void disableCrc(); |
|
||||||
void enableTCXO(); |
|
||||||
void disableTCXO(); |
|
||||||
|
|
||||||
byte random(); |
|
||||||
|
|
||||||
void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN, int busy = LORA_DEFAULT_BUSY_PIN); |
|
||||||
void setSPIFrequency(uint32_t frequency); |
|
||||||
|
|
||||||
private: |
|
||||||
void explicitHeaderMode(); |
|
||||||
void implicitHeaderMode(); |
|
||||||
|
|
||||||
void handleDio0Rise(); |
|
||||||
|
|
||||||
uint8_t readRegister(uint8_t address); |
|
||||||
void writeRegister(uint8_t address, uint8_t value); |
|
||||||
uint8_t singleTransfer(uint8_t address, uint8_t value); |
|
||||||
|
|
||||||
static void onDio0Rise(); |
|
||||||
|
|
||||||
void handleLowDataRate(); |
|
||||||
void optimizeModemSensitivity(); |
|
||||||
|
|
||||||
private: |
|
||||||
SPISettings _spiSettings; |
|
||||||
int _ss; |
|
||||||
int _reset; |
|
||||||
int _dio0; |
|
||||||
int _busy; |
|
||||||
long _frequency; |
|
||||||
int _packetIndex; |
|
||||||
int _implicitHeaderMode; |
|
||||||
bool _preinit_done; |
|
||||||
void (*_onReceive)(int); |
|
||||||
}; |
|
||||||
|
|
||||||
extern sx127x sx127x_modem; |
|
||||||
|
|
||||||
#endif |
|
@ -1,887 +0,0 @@ |
|||||||
// Copyright (c) Sandeep Mistry. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
// Modifications and additions copyright 2024 by Mark Qvist & Jacob Eva
|
|
||||||
// Obviously still under the MIT license.
|
|
||||||
|
|
||||||
#include "sx128x.h" |
|
||||||
#include "Boards.h" |
|
||||||
|
|
||||||
#define MCU_1284P 0x91 |
|
||||||
#define MCU_2560 0x92 |
|
||||||
#define MCU_ESP32 0x81 |
|
||||||
#define MCU_NRF52 0x71 |
|
||||||
#if defined(__AVR_ATmega1284P__) |
|
||||||
#define PLATFORM PLATFORM_AVR |
|
||||||
#define MCU_VARIANT MCU_1284P |
|
||||||
#elif defined(__AVR_ATmega2560__) |
|
||||||
#define PLATFORM PLATFORM_AVR |
|
||||||
#define MCU_VARIANT MCU_2560 |
|
||||||
#elif defined(ESP32) |
|
||||||
#define PLATFORM PLATFORM_ESP32 |
|
||||||
#define MCU_VARIANT MCU_ESP32 |
|
||||||
#elif defined(NRF52840_XXAA) |
|
||||||
#define PLATFORM PLATFORM_NRF52 |
|
||||||
#define MCU_VARIANT MCU_NRF52 |
|
||||||
#endif |
|
||||||
|
|
||||||
#ifndef MCU_VARIANT |
|
||||||
#error No MCU variant defined, cannot compile |
|
||||||
#endif |
|
||||||
|
|
||||||
#if MCU_VARIANT == MCU_ESP32 |
|
||||||
#if MCU_VARIANT == MCU_ESP32 and !defined(CONFIG_IDF_TARGET_ESP32S3) |
|
||||||
#include "soc/rtc_wdt.h" |
|
||||||
#endif |
|
||||||
#define ISR_VECT IRAM_ATTR |
|
||||||
#else |
|
||||||
#define ISR_VECT |
|
||||||
#endif |
|
||||||
|
|
||||||
#define OP_RF_FREQ_8X 0x86 |
|
||||||
#define OP_SLEEP_8X 0x84 |
|
||||||
#define OP_STANDBY_8X 0x80 |
|
||||||
#define OP_TX_8X 0x83 |
|
||||||
#define OP_RX_8X 0x82 |
|
||||||
#define OP_SET_IRQ_FLAGS_8X 0x8D // also provides info such as
|
|
||||||
// preamble detection, etc for
|
|
||||||
// knowing when it's safe to switch
|
|
||||||
// antenna modes
|
|
||||||
#define OP_CLEAR_IRQ_STATUS_8X 0x97 |
|
||||||
#define OP_GET_IRQ_STATUS_8X 0x15 |
|
||||||
#define OP_RX_BUFFER_STATUS_8X 0x17 |
|
||||||
#define OP_PACKET_STATUS_8X 0x1D // get snr & rssi of last packet
|
|
||||||
#define OP_CURRENT_RSSI_8X 0x1F |
|
||||||
#define OP_MODULATION_PARAMS_8X 0x8B // bw, sf, cr, etc.
|
|
||||||
#define OP_PACKET_PARAMS_8X 0x8C // crc, preamble, payload length, etc.
|
|
||||||
#define OP_STATUS_8X 0xC0 |
|
||||||
#define OP_TX_PARAMS_8X 0x8E // set dbm, etc
|
|
||||||
#define OP_PACKET_TYPE_8X 0x8A |
|
||||||
#define OP_BUFFER_BASE_ADDR_8X 0x8F |
|
||||||
#define OP_READ_REGISTER_8X 0x19 |
|
||||||
#define OP_WRITE_REGISTER_8X 0x18 |
|
||||||
#define IRQ_TX_DONE_MASK_8X 0x01 |
|
||||||
#define IRQ_RX_DONE_MASK_8X 0x02 |
|
||||||
#define IRQ_HEADER_DET_MASK_8X 0x10 |
|
||||||
#define IRQ_HEADER_ERROR_MASK_8X 0x20 |
|
||||||
#define IRQ_PAYLOAD_CRC_ERROR_MASK_8X 0x40 |
|
||||||
|
|
||||||
#define MODE_LONG_RANGE_MODE_8X 0x01 |
|
||||||
|
|
||||||
#define OP_FIFO_WRITE_8X 0x1A |
|
||||||
#define OP_FIFO_READ_8X 0x1B |
|
||||||
#define IRQ_PREAMBLE_DET_MASK_8X 0x80 |
|
||||||
|
|
||||||
#define REG_PACKET_SIZE 0x901 |
|
||||||
#define REG_FIRM_VER_MSB 0x154 |
|
||||||
#define REG_FIRM_VER_LSB 0x153 |
|
||||||
|
|
||||||
#define XTAL_FREQ_8X (double)52000000 |
|
||||||
#define FREQ_DIV_8X (double)pow(2.0, 18.0) |
|
||||||
#define FREQ_STEP_8X (double)(XTAL_FREQ_8X / FREQ_DIV_8X) |
|
||||||
|
|
||||||
#if defined(NRF52840_XXAA) |
|
||||||
extern SPIClass spiModem; |
|
||||||
#define SPI spiModem |
|
||||||
#endif |
|
||||||
|
|
||||||
extern SPIClass SPI; |
|
||||||
|
|
||||||
#define MAX_PKT_LENGTH 255 |
|
||||||
|
|
||||||
sx128x::sx128x() : |
|
||||||
_spiSettings(8E6, MSBFIRST, SPI_MODE0), |
|
||||||
_ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), _rxen(LORA_DEFAULT_RXEN_PIN), _busy(LORA_DEFAULT_BUSY_PIN), |
|
||||||
_frequency(0), |
|
||||||
_txp(0), |
|
||||||
_sf(0x50), |
|
||||||
_bw(0x34), |
|
||||||
_cr(0x01), |
|
||||||
_packetIndex(0), |
|
||||||
_preambleLength(18), |
|
||||||
_implicitHeaderMode(0), |
|
||||||
_payloadLength(255), |
|
||||||
_crcMode(0), |
|
||||||
_fifo_tx_addr_ptr(0), |
|
||||||
_fifo_rx_addr_ptr(0), |
|
||||||
_packet({0}), |
|
||||||
_rxPacketLength(0), |
|
||||||
_preinit_done(false), |
|
||||||
_onReceive(NULL) |
|
||||||
{ |
|
||||||
// overide Stream timeout value
|
|
||||||
setTimeout(0); |
|
||||||
} |
|
||||||
|
|
||||||
bool sx128x::preInit() { |
|
||||||
// setup pins
|
|
||||||
pinMode(_ss, OUTPUT); |
|
||||||
// set SS high
|
|
||||||
digitalWrite(_ss, HIGH); |
|
||||||
|
|
||||||
SPI.begin(); |
|
||||||
|
|
||||||
// check version (retry for up to 2 seconds)
|
|
||||||
long start = millis(); |
|
||||||
|
|
||||||
uint8_t version_msb; |
|
||||||
uint8_t version_lsb; |
|
||||||
|
|
||||||
while (((millis() - start) < 2000) && (millis() >= start)) { |
|
||||||
|
|
||||||
version_msb = readRegister(REG_FIRM_VER_MSB); |
|
||||||
version_lsb = readRegister(REG_FIRM_VER_LSB); |
|
||||||
|
|
||||||
if ((version_msb == 0xB7 && version_lsb == 0xA9) || (version_msb == 0xB5 && version_lsb == 0xA9)) { |
|
||||||
break; |
|
||||||
} |
|
||||||
delay(100); |
|
||||||
} |
|
||||||
if ((version_msb != 0xB7 || version_lsb != 0xA9) && (version_msb != 0xB5 || version_lsb != 0xA9)) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
_preinit_done = true; |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
uint8_t ISR_VECT sx128x::readRegister(uint16_t address) |
|
||||||
{ |
|
||||||
return singleTransfer(OP_READ_REGISTER_8X, address, 0x00); |
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::writeRegister(uint16_t address, uint8_t value) |
|
||||||
{ |
|
||||||
singleTransfer(OP_WRITE_REGISTER_8X, address, value); |
|
||||||
} |
|
||||||
|
|
||||||
uint8_t ISR_VECT sx128x::singleTransfer(uint8_t opcode, uint16_t address, uint8_t value) |
|
||||||
{ |
|
||||||
waitOnBusy(); |
|
||||||
|
|
||||||
uint8_t response; |
|
||||||
|
|
||||||
digitalWrite(_ss, LOW); |
|
||||||
|
|
||||||
SPI.beginTransaction(_spiSettings); |
|
||||||
SPI.transfer(opcode); |
|
||||||
SPI.transfer((address & 0xFF00) >> 8); |
|
||||||
SPI.transfer(address & 0x00FF); |
|
||||||
if (opcode == OP_READ_REGISTER_8X) { |
|
||||||
SPI.transfer(0x00); |
|
||||||
} |
|
||||||
response = SPI.transfer(value); |
|
||||||
SPI.endTransaction(); |
|
||||||
|
|
||||||
digitalWrite(_ss, HIGH); |
|
||||||
|
|
||||||
return response; |
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::rxAntEnable() |
|
||||||
{ |
|
||||||
if (_txen != -1) { |
|
||||||
digitalWrite(_txen, LOW); |
|
||||||
} |
|
||||||
if (_rxen != -1) { |
|
||||||
digitalWrite(_rxen, HIGH); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::txAntEnable() |
|
||||||
{ |
|
||||||
if (_txen != -1) { |
|
||||||
digitalWrite(_txen, HIGH); |
|
||||||
} |
|
||||||
if (_rxen != -1) { |
|
||||||
digitalWrite(_rxen, LOW); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::loraMode() { |
|
||||||
// enable lora mode on the SX1262 chip
|
|
||||||
uint8_t mode = MODE_LONG_RANGE_MODE_8X; |
|
||||||
executeOpcode(OP_PACKET_TYPE_8X, &mode, 1); |
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::waitOnBusy() { |
|
||||||
unsigned long time = millis(); |
|
||||||
while (digitalRead(_busy) == HIGH) |
|
||||||
{ |
|
||||||
if (millis() >= (time + 100)) { |
|
||||||
break; |
|
||||||
} |
|
||||||
// do nothing
|
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size) |
|
||||||
{ |
|
||||||
waitOnBusy(); |
|
||||||
|
|
||||||
digitalWrite(_ss, LOW); |
|
||||||
|
|
||||||
SPI.beginTransaction(_spiSettings); |
|
||||||
SPI.transfer(opcode); |
|
||||||
|
|
||||||
for (int i = 0; i < size; i++) |
|
||||||
{ |
|
||||||
SPI.transfer(buffer[i]); |
|
||||||
} |
|
||||||
|
|
||||||
SPI.endTransaction(); |
|
||||||
|
|
||||||
digitalWrite(_ss, HIGH); |
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size) |
|
||||||
{ |
|
||||||
waitOnBusy(); |
|
||||||
|
|
||||||
digitalWrite(_ss, LOW); |
|
||||||
|
|
||||||
SPI.beginTransaction(_spiSettings); |
|
||||||
SPI.transfer(opcode); |
|
||||||
SPI.transfer(0x00); |
|
||||||
|
|
||||||
for (int i = 0; i < size; i++) |
|
||||||
{ |
|
||||||
buffer[i] = SPI.transfer(0x00); |
|
||||||
} |
|
||||||
|
|
||||||
SPI.endTransaction(); |
|
||||||
|
|
||||||
digitalWrite(_ss, HIGH); |
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::writeBuffer(const uint8_t* buffer, size_t size) |
|
||||||
{ |
|
||||||
waitOnBusy(); |
|
||||||
|
|
||||||
digitalWrite(_ss, LOW); |
|
||||||
|
|
||||||
SPI.beginTransaction(_spiSettings); |
|
||||||
SPI.transfer(OP_FIFO_WRITE_8X); |
|
||||||
SPI.transfer(_fifo_tx_addr_ptr); |
|
||||||
|
|
||||||
for (int i = 0; i < size; i++) |
|
||||||
{ |
|
||||||
SPI.transfer(buffer[i]); |
|
||||||
_fifo_tx_addr_ptr++; |
|
||||||
} |
|
||||||
|
|
||||||
SPI.endTransaction(); |
|
||||||
|
|
||||||
digitalWrite(_ss, HIGH); |
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::readBuffer(uint8_t* buffer, size_t size) |
|
||||||
{ |
|
||||||
waitOnBusy(); |
|
||||||
|
|
||||||
digitalWrite(_ss, LOW); |
|
||||||
|
|
||||||
SPI.beginTransaction(_spiSettings); |
|
||||||
SPI.transfer(OP_FIFO_READ_8X); |
|
||||||
SPI.transfer(_fifo_rx_addr_ptr); |
|
||||||
SPI.transfer(0x00); |
|
||||||
|
|
||||||
for (int i = 0; i < size; i++) |
|
||||||
{ |
|
||||||
buffer[i] = SPI.transfer(0x00); |
|
||||||
} |
|
||||||
|
|
||||||
SPI.endTransaction(); |
|
||||||
|
|
||||||
digitalWrite(_ss, HIGH); |
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr) { |
|
||||||
// because there is no access to these registers on the sx1280, we have
|
|
||||||
// to set all these parameters at once or not at all.
|
|
||||||
uint8_t buf[3]; |
|
||||||
|
|
||||||
buf[0] = sf; |
|
||||||
buf[1] = bw; |
|
||||||
buf[2] = cr;
|
|
||||||
executeOpcode(OP_MODULATION_PARAMS_8X, buf, 3); |
|
||||||
|
|
||||||
if (sf <= 6) { |
|
||||||
writeRegister(0x925, 0x1E); |
|
||||||
} else if (sf <= 8) { |
|
||||||
writeRegister(0x925, 0x37); |
|
||||||
} else if (sf >= 9) { |
|
||||||
writeRegister(0x925, 0x32); |
|
||||||
} |
|
||||||
writeRegister(0x093C, 0x1); |
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc) { |
|
||||||
// because there is no access to these registers on the sx1280, we have
|
|
||||||
// to set all these parameters at once or not at all.
|
|
||||||
uint8_t buf[7]; |
|
||||||
|
|
||||||
// calculate exponent and mantissa values for modem
|
|
||||||
uint8_t e = 1; |
|
||||||
uint8_t m = 1; |
|
||||||
uint32_t preamblelen; |
|
||||||
|
|
||||||
for (e <= 15; e++;) { |
|
||||||
for (m <= 15; m++;) { |
|
||||||
preamblelen = m * (uint32_t(1) << e); |
|
||||||
if (preamblelen >= preamble) break; |
|
||||||
} |
|
||||||
if (preamblelen >= preamble) break; |
|
||||||
} |
|
||||||
|
|
||||||
buf[0] = (e << 4) | m; |
|
||||||
buf[1] = headermode; |
|
||||||
buf[2] = length; |
|
||||||
buf[3] = crc; |
|
||||||
// standard IQ setting (no inversion)
|
|
||||||
buf[4] = 0x40;
|
|
||||||
// unused params
|
|
||||||
buf[5] = 0x00;
|
|
||||||
buf[6] = 0x00;
|
|
||||||
|
|
||||||
executeOpcode(OP_PACKET_PARAMS_8X, buf, 7); |
|
||||||
} |
|
||||||
|
|
||||||
int sx128x::begin(unsigned long frequency) |
|
||||||
{ |
|
||||||
if (_reset != -1) { |
|
||||||
pinMode(_reset, OUTPUT); |
|
||||||
|
|
||||||
// perform reset
|
|
||||||
digitalWrite(_reset, LOW); |
|
||||||
delay(10); |
|
||||||
digitalWrite(_reset, HIGH); |
|
||||||
delay(10); |
|
||||||
} |
|
||||||
|
|
||||||
if (_rxen != -1) { |
|
||||||
pinMode(_rxen, OUTPUT); |
|
||||||
} |
|
||||||
|
|
||||||
if (_txen != -1) { |
|
||||||
pinMode(_txen, OUTPUT); |
|
||||||
} |
|
||||||
|
|
||||||
if (_busy != -1) { |
|
||||||
pinMode(_busy, INPUT); |
|
||||||
} |
|
||||||
|
|
||||||
if (!_preinit_done) { |
|
||||||
if (!preInit()) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
idle(); |
|
||||||
loraMode(); |
|
||||||
rxAntEnable(); |
|
||||||
|
|
||||||
setFrequency(frequency); |
|
||||||
|
|
||||||
// set LNA boost
|
|
||||||
// todo: implement this
|
|
||||||
//writeRegister(REG_LNA, 0x96);
|
|
||||||
|
|
||||||
setModulationParams(_sf, _bw, _cr); |
|
||||||
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); |
|
||||||
|
|
||||||
// set output power to 2 dBm
|
|
||||||
setTxPower(2); |
|
||||||
|
|
||||||
// set base addresses
|
|
||||||
uint8_t basebuf[2] = {0}; |
|
||||||
executeOpcode(OP_BUFFER_BASE_ADDR_8X, basebuf, 2); |
|
||||||
|
|
||||||
return 1; |
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::end() |
|
||||||
{ |
|
||||||
// put in sleep mode
|
|
||||||
sleep(); |
|
||||||
|
|
||||||
// stop SPI
|
|
||||||
SPI.end(); |
|
||||||
|
|
||||||
_preinit_done = false; |
|
||||||
} |
|
||||||
|
|
||||||
int sx128x::beginPacket(int implicitHeader) |
|
||||||
{ |
|
||||||
// put in standby mode
|
|
||||||
idle(); |
|
||||||
|
|
||||||
if (implicitHeader) { |
|
||||||
implicitHeaderMode(); |
|
||||||
} else { |
|
||||||
explicitHeaderMode(); |
|
||||||
} |
|
||||||
|
|
||||||
_payloadLength = 0; |
|
||||||
_fifo_tx_addr_ptr = 0; |
|
||||||
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); |
|
||||||
|
|
||||||
return 1; |
|
||||||
} |
|
||||||
|
|
||||||
int sx128x::endPacket() |
|
||||||
{ |
|
||||||
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); |
|
||||||
|
|
||||||
txAntEnable(); |
|
||||||
|
|
||||||
// put in single TX mode
|
|
||||||
uint8_t timeout[3] = {0}; |
|
||||||
executeOpcode(OP_TX_8X, timeout, 3); |
|
||||||
|
|
||||||
uint8_t buf[2]; |
|
||||||
|
|
||||||
buf[0] = 0x00; |
|
||||||
buf[1] = 0x00; |
|
||||||
|
|
||||||
executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2); |
|
||||||
|
|
||||||
// wait for TX done
|
|
||||||
while ((buf[1] & IRQ_TX_DONE_MASK_8X) == 0) { |
|
||||||
buf[0] = 0x00; |
|
||||||
buf[1] = 0x00; |
|
||||||
executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2); |
|
||||||
yield(); |
|
||||||
} |
|
||||||
|
|
||||||
// clear IRQ's
|
|
||||||
|
|
||||||
uint8_t mask[2]; |
|
||||||
mask[0] = 0x00; |
|
||||||
mask[1] = IRQ_TX_DONE_MASK_8X; |
|
||||||
executeOpcode(OP_CLEAR_IRQ_STATUS_8X, mask, 2); |
|
||||||
return 1; |
|
||||||
} |
|
||||||
|
|
||||||
uint8_t sx128x::modemStatus() { |
|
||||||
// imitate the register status from the sx1276 / 78
|
|
||||||
uint8_t buf[2] = {0}; |
|
||||||
|
|
||||||
executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2); |
|
||||||
|
|
||||||
uint8_t clearbuf[2] = {0}; |
|
||||||
|
|
||||||
uint8_t byte = 0x00; |
|
||||||
|
|
||||||
if ((buf[0] & IRQ_PREAMBLE_DET_MASK_8X) != 0) { |
|
||||||
byte = byte | 0x01 | 0x04; |
|
||||||
// clear register after reading
|
|
||||||
clearbuf[0] = 0xFF; |
|
||||||
} |
|
||||||
|
|
||||||
if ((buf[1] & IRQ_HEADER_DET_MASK_8X) != 0) { |
|
||||||
byte = byte | 0x02 | 0x04; |
|
||||||
// clear register after reading
|
|
||||||
clearbuf[1] = 0xFF; |
|
||||||
} |
|
||||||
|
|
||||||
executeOpcode(OP_CLEAR_IRQ_STATUS_8X, clearbuf, 2); |
|
||||||
|
|
||||||
return byte; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
uint8_t sx128x::currentRssiRaw() { |
|
||||||
uint8_t byte = 0; |
|
||||||
executeOpcodeRead(OP_CURRENT_RSSI_8X, &byte, 1); |
|
||||||
return byte; |
|
||||||
} |
|
||||||
|
|
||||||
int ISR_VECT sx128x::currentRssi() { |
|
||||||
uint8_t byte = 0; |
|
||||||
executeOpcodeRead(OP_CURRENT_RSSI_8X, &byte, 1); |
|
||||||
int rssi = -byte / 2; |
|
||||||
return rssi; |
|
||||||
} |
|
||||||
|
|
||||||
uint8_t sx128x::packetRssiRaw() { |
|
||||||
uint8_t buf[5] = {0}; |
|
||||||
executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 5); |
|
||||||
return buf[0]; |
|
||||||
} |
|
||||||
|
|
||||||
int ISR_VECT sx128x::packetRssi() { |
|
||||||
// may need more calculations here
|
|
||||||
uint8_t buf[5] = {0}; |
|
||||||
executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 5); |
|
||||||
int pkt_rssi = -buf[0] / 2; |
|
||||||
return pkt_rssi; |
|
||||||
} |
|
||||||
|
|
||||||
uint8_t ISR_VECT sx128x::packetSnrRaw() { |
|
||||||
uint8_t buf[5] = {0}; |
|
||||||
executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 5); |
|
||||||
return buf[1]; |
|
||||||
} |
|
||||||
|
|
||||||
float ISR_VECT sx128x::packetSnr() { |
|
||||||
uint8_t buf[5] = {0}; |
|
||||||
executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 3); |
|
||||||
return float(buf[1]) * 0.25; |
|
||||||
} |
|
||||||
|
|
||||||
long sx128x::packetFrequencyError() |
|
||||||
{ |
|
||||||
int32_t freqError = 0; |
|
||||||
// todo: implement this, page 120 of sx1280 datasheet
|
|
||||||
const float fError = 0.0; |
|
||||||
return static_cast<long>(fError); |
|
||||||
} |
|
||||||
|
|
||||||
size_t sx128x::write(uint8_t byte) |
|
||||||
{ |
|
||||||
return write(&byte, sizeof(byte)); |
|
||||||
} |
|
||||||
|
|
||||||
size_t sx128x::write(const uint8_t *buffer, size_t size) |
|
||||||
{ |
|
||||||
if ((_payloadLength + size) > MAX_PKT_LENGTH) { |
|
||||||
size = MAX_PKT_LENGTH - _payloadLength; |
|
||||||
} |
|
||||||
|
|
||||||
// write data
|
|
||||||
writeBuffer(buffer, size); |
|
||||||
_payloadLength = _payloadLength + size; |
|
||||||
return size; |
|
||||||
} |
|
||||||
|
|
||||||
int ISR_VECT sx128x::available() |
|
||||||
{ |
|
||||||
return _rxPacketLength - _packetIndex; |
|
||||||
} |
|
||||||
|
|
||||||
int ISR_VECT sx128x::read() |
|
||||||
{ |
|
||||||
if (!available()) { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
uint8_t byte = _packet[_packetIndex]; |
|
||||||
_packetIndex++; |
|
||||||
return byte; |
|
||||||
} |
|
||||||
|
|
||||||
int sx128x::peek() |
|
||||||
{ |
|
||||||
if (!available()) { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
uint8_t b = _packet[_packetIndex]; |
|
||||||
return b; |
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::flush() |
|
||||||
{ |
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::onReceive(void(*callback)(int)) |
|
||||||
{ |
|
||||||
_onReceive = callback; |
|
||||||
|
|
||||||
if (callback) { |
|
||||||
pinMode(_dio0, INPUT); |
|
||||||
|
|
||||||
// set preamble and header detection irqs, plus dio0 mask
|
|
||||||
uint8_t buf[8]; |
|
||||||
|
|
||||||
// set irq masks, enable all
|
|
||||||
buf[0] = 0xFF;
|
|
||||||
buf[1] = 0xFF; |
|
||||||
|
|
||||||
// set dio0 masks
|
|
||||||
buf[2] = 0x00; |
|
||||||
buf[3] = IRQ_RX_DONE_MASK_8X;
|
|
||||||
|
|
||||||
// set dio1 masks
|
|
||||||
buf[4] = 0x00;
|
|
||||||
buf[5] = 0x00; |
|
||||||
|
|
||||||
// set dio2 masks
|
|
||||||
buf[6] = 0x00;
|
|
||||||
buf[7] = 0x00; |
|
||||||
|
|
||||||
executeOpcode(OP_SET_IRQ_FLAGS_8X, buf, 8); |
|
||||||
//#ifdef SPI_HAS_NOTUSINGINTERRUPT
|
|
||||||
// SPI.usingInterrupt(digitalPinToInterrupt(_dio0));
|
|
||||||
//#endif
|
|
||||||
attachInterrupt(digitalPinToInterrupt(_dio0), sx128x::onDio0Rise, RISING); |
|
||||||
} else { |
|
||||||
detachInterrupt(digitalPinToInterrupt(_dio0)); |
|
||||||
//#ifdef SPI_HAS_NOTUSINGINTERRUPT
|
|
||||||
// SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0));
|
|
||||||
//#endif
|
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::receive(int size) |
|
||||||
{ |
|
||||||
if (size > 0) { |
|
||||||
implicitHeaderMode(); |
|
||||||
|
|
||||||
// tell radio payload length
|
|
||||||
_rxPacketLength = size; |
|
||||||
//_payloadLength = size;
|
|
||||||
//setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
|
|
||||||
} else { |
|
||||||
explicitHeaderMode(); |
|
||||||
} |
|
||||||
|
|
||||||
rxAntEnable(); |
|
||||||
|
|
||||||
uint8_t mode[3] = {0xFF, 0xFF, 0xFF}; // continuous mode
|
|
||||||
executeOpcode(OP_RX_8X, mode, 3); |
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::idle() |
|
||||||
{ |
|
||||||
#if HAS_TCXO |
|
||||||
// STDBY_XOSC
|
|
||||||
uint8_t byte = 0x01; |
|
||||||
#else |
|
||||||
// STDBY_RC
|
|
||||||
uint8_t byte = 0x00; |
|
||||||
#endif |
|
||||||
executeOpcode(OP_STANDBY_8X, &byte, 1);
|
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::sleep() |
|
||||||
{ |
|
||||||
uint8_t byte = 0x00; |
|
||||||
executeOpcode(OP_SLEEP_8X, &byte, 1); |
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::enableTCXO() { |
|
||||||
// todo: need to check how to implement on sx1280
|
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::disableTCXO() { |
|
||||||
// todo: need to check how to implement on sx1280
|
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::setTxPower(int level, int outputPin) { |
|
||||||
if (level > 13) { |
|
||||||
level = 13; |
|
||||||
} else if (level < -18) { |
|
||||||
level = -18; |
|
||||||
} |
|
||||||
|
|
||||||
_txp = level; |
|
||||||
|
|
||||||
level = level + 18; |
|
||||||
|
|
||||||
uint8_t tx_buf[2]; |
|
||||||
|
|
||||||
tx_buf[0] = level; |
|
||||||
tx_buf[1] = 0xE0; // ramping time - 20 microseconds
|
|
||||||
|
|
||||||
executeOpcode(OP_TX_PARAMS_8X, tx_buf, 2); |
|
||||||
} |
|
||||||
|
|
||||||
uint8_t sx128x::getTxPower() { |
|
||||||
return _txp; |
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::setFrequency(unsigned long frequency) { |
|
||||||
_frequency = frequency; |
|
||||||
|
|
||||||
uint8_t buf[3]; |
|
||||||
|
|
||||||
uint32_t freq = (uint32_t)((double)frequency / (double)FREQ_STEP_8X); |
|
||||||
|
|
||||||
buf[0] = ((freq >> 16) & 0xFF); |
|
||||||
buf[1] = ((freq >> 8) & 0xFF); |
|
||||||
buf[2] = (freq & 0xFF); |
|
||||||
|
|
||||||
executeOpcode(OP_RF_FREQ_8X, buf, 3); |
|
||||||
} |
|
||||||
|
|
||||||
uint32_t sx128x::getFrequency() { |
|
||||||
// we can't read the frequency on the sx1280
|
|
||||||
uint32_t frequency = _frequency; |
|
||||||
|
|
||||||
return frequency; |
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::setSpreadingFactor(int sf) |
|
||||||
{ |
|
||||||
if (sf < 5) { |
|
||||||
sf = 5; |
|
||||||
} else if (sf > 12) { |
|
||||||
sf = 12; |
|
||||||
} |
|
||||||
|
|
||||||
_sf = sf << 4; |
|
||||||
|
|
||||||
setModulationParams(sf << 4, _bw, _cr); |
|
||||||
handleLowDataRate(); |
|
||||||
} |
|
||||||
|
|
||||||
long sx128x::getSignalBandwidth() |
|
||||||
{ |
|
||||||
int bw = _bw; |
|
||||||
switch (bw) { |
|
||||||
case 0x34: return 203.125E3; |
|
||||||
case 0x26: return 406.25E3; |
|
||||||
case 0x18: return 812.5E3; |
|
||||||
case 0x0A: return 1625E3; |
|
||||||
} |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::handleLowDataRate(){ |
|
||||||
// todo: do i need this??
|
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::optimizeModemSensitivity(){ |
|
||||||
// todo: check if there's anything the sx1280 can do here
|
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::setSignalBandwidth(long sbw) |
|
||||||
{ |
|
||||||
if (sbw <= 203.125E3) { |
|
||||||
_bw = 0x34; |
|
||||||
} else if (sbw <= 406.25E3) { |
|
||||||
_bw = 0x26; |
|
||||||
} else if (sbw <= 812.5E3) { |
|
||||||
_bw = 0x18; |
|
||||||
} else { |
|
||||||
_bw = 0x0A; |
|
||||||
} |
|
||||||
|
|
||||||
setModulationParams(_sf, _bw, _cr); |
|
||||||
|
|
||||||
handleLowDataRate(); |
|
||||||
optimizeModemSensitivity(); |
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::setCodingRate4(int denominator) |
|
||||||
{ |
|
||||||
if (denominator < 5) { |
|
||||||
denominator = 5; |
|
||||||
} else if (denominator > 8) { |
|
||||||
denominator = 8; |
|
||||||
} |
|
||||||
|
|
||||||
_cr = denominator - 4; |
|
||||||
|
|
||||||
// todo: add support for new interleaving scheme, see page 117 of sx1280
|
|
||||||
// datasheet
|
|
||||||
|
|
||||||
// update cr values for sx1280's use
|
|
||||||
|
|
||||||
setModulationParams(_sf, _bw, _cr); |
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::setPreambleLength(long length) |
|
||||||
{ |
|
||||||
_preambleLength = length; |
|
||||||
setPacketParams(length, _implicitHeaderMode, _payloadLength, _crcMode); |
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::setSyncWord(int sw) |
|
||||||
{ |
|
||||||
// not implemented
|
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::enableCrc() |
|
||||||
{ |
|
||||||
_crcMode = 0x20; |
|
||||||
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); |
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::disableCrc() |
|
||||||
{ |
|
||||||
_crcMode = 0; |
|
||||||
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); |
|
||||||
} |
|
||||||
|
|
||||||
byte sx128x::random() |
|
||||||
{ |
|
||||||
// todo: implement
|
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::setPins(int ss, int reset, int dio0, int busy, int rxen, int txen) |
|
||||||
{ |
|
||||||
_ss = ss; |
|
||||||
_reset = reset; |
|
||||||
_dio0 = dio0; |
|
||||||
_busy = busy; |
|
||||||
_rxen = rxen; |
|
||||||
_txen = txen; |
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::setSPIFrequency(uint32_t frequency) |
|
||||||
{ |
|
||||||
_spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); |
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::dumpRegisters(Stream& out) |
|
||||||
{ |
|
||||||
for (int i = 0; i < 128; i++) { |
|
||||||
out.print("0x"); |
|
||||||
out.print(i, HEX); |
|
||||||
out.print(": 0x"); |
|
||||||
out.println(readRegister(i), HEX); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::explicitHeaderMode() |
|
||||||
{ |
|
||||||
_implicitHeaderMode = 0; |
|
||||||
|
|
||||||
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); |
|
||||||
} |
|
||||||
|
|
||||||
void sx128x::implicitHeaderMode() |
|
||||||
{ |
|
||||||
_implicitHeaderMode = 0x80; |
|
||||||
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
void ISR_VECT sx128x::handleDio0Rise() |
|
||||||
{ |
|
||||||
uint8_t buf[2]; |
|
||||||
|
|
||||||
buf[0] = 0x00; |
|
||||||
buf[1] = 0x00; |
|
||||||
|
|
||||||
executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2); |
|
||||||
|
|
||||||
executeOpcode(OP_CLEAR_IRQ_STATUS_8X, buf, 2); |
|
||||||
|
|
||||||
if ((buf[1] & IRQ_PAYLOAD_CRC_ERROR_MASK_8X) == 0) { |
|
||||||
// received a packet
|
|
||||||
_packetIndex = 0; |
|
||||||
|
|
||||||
uint8_t rxbuf[2] = {0}; |
|
||||||
executeOpcodeRead(OP_RX_BUFFER_STATUS_8X, rxbuf, 2); |
|
||||||
_rxPacketLength = rxbuf[0]; |
|
||||||
_fifo_rx_addr_ptr = rxbuf[1]; |
|
||||||
readBuffer(_packet, _rxPacketLength); |
|
||||||
|
|
||||||
if (_onReceive) { |
|
||||||
_onReceive(_rxPacketLength); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void ISR_VECT sx128x::onDio0Rise() |
|
||||||
{ |
|
||||||
sx128x_modem.handleDio0Rise(); |
|
||||||
} |
|
||||||
|
|
||||||
sx128x sx128x_modem; |
|
@ -1,144 +0,0 @@ |
|||||||
// Copyright (c) Sandeep Mistry. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
// Modifications and additions copyright 2023 by Mark Qvist
|
|
||||||
// Obviously still under the MIT license.
|
|
||||||
|
|
||||||
#ifndef SX128X_H |
|
||||||
#define SX128X_H |
|
||||||
|
|
||||||
#include <Arduino.h> |
|
||||||
#include <SPI.h> |
|
||||||
#include "Interfaces.h" |
|
||||||
|
|
||||||
#define LORA_DEFAULT_SS_PIN 10 |
|
||||||
#define LORA_DEFAULT_RESET_PIN 9 |
|
||||||
#define LORA_DEFAULT_DIO0_PIN 2 |
|
||||||
#define LORA_DEFAULT_RXEN_PIN -1 |
|
||||||
#define LORA_DEFAULT_TXEN_PIN -1 |
|
||||||
#define LORA_DEFAULT_BUSY_PIN 11 |
|
||||||
|
|
||||||
#define PA_OUTPUT_RFO_PIN 0 |
|
||||||
#define PA_OUTPUT_PA_BOOST_PIN 1 |
|
||||||
|
|
||||||
#define RSSI_OFFSET 157 |
|
||||||
|
|
||||||
class sx128x : public Stream { |
|
||||||
public: |
|
||||||
sx128x(); |
|
||||||
|
|
||||||
int begin(unsigned long frequency); |
|
||||||
void end(); |
|
||||||
|
|
||||||
int beginPacket(int implicitHeader = false); |
|
||||||
int endPacket(); |
|
||||||
|
|
||||||
int parsePacket(int size = 0); |
|
||||||
int packetRssi(); |
|
||||||
int currentRssi(); |
|
||||||
uint8_t packetRssiRaw(); |
|
||||||
uint8_t currentRssiRaw(); |
|
||||||
uint8_t packetSnrRaw(); |
|
||||||
float packetSnr(); |
|
||||||
long packetFrequencyError(); |
|
||||||
|
|
||||||
// from Print
|
|
||||||
virtual size_t write(uint8_t byte); |
|
||||||
virtual size_t write(const uint8_t *buffer, size_t size); |
|
||||||
|
|
||||||
// from Stream
|
|
||||||
virtual int available(); |
|
||||||
virtual int read(); |
|
||||||
virtual int peek(); |
|
||||||
virtual void flush(); |
|
||||||
|
|
||||||
void onReceive(void(*callback)(int)); |
|
||||||
|
|
||||||
void receive(int size = 0); |
|
||||||
void idle(); |
|
||||||
void sleep(); |
|
||||||
|
|
||||||
bool preInit(); |
|
||||||
uint8_t getTxPower(); |
|
||||||
void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); |
|
||||||
uint32_t getFrequency(); |
|
||||||
void setFrequency(unsigned long frequency); |
|
||||||
void setSpreadingFactor(int sf); |
|
||||||
long getSignalBandwidth(); |
|
||||||
void setSignalBandwidth(long sbw); |
|
||||||
void setCodingRate4(int denominator); |
|
||||||
void setPreambleLength(long length); |
|
||||||
void setSyncWord(int sw); |
|
||||||
uint8_t modemStatus(); |
|
||||||
void enableCrc(); |
|
||||||
void disableCrc(); |
|
||||||
void enableTCXO(); |
|
||||||
void disableTCXO(); |
|
||||||
|
|
||||||
void txAntEnable(); |
|
||||||
void rxAntEnable(); |
|
||||||
void loraMode(); |
|
||||||
void waitOnBusy(); |
|
||||||
void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size); |
|
||||||
void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size); |
|
||||||
void writeBuffer(const uint8_t* buffer, size_t size); |
|
||||||
void readBuffer(uint8_t* buffer, size_t size); |
|
||||||
void setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc); |
|
||||||
void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr); |
|
||||||
|
|
||||||
// deprecated
|
|
||||||
void crc() { enableCrc(); } |
|
||||||
void noCrc() { disableCrc(); } |
|
||||||
|
|
||||||
byte random(); |
|
||||||
|
|
||||||
void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN, int busy = LORA_DEFAULT_BUSY_PIN, int rxen = LORA_DEFAULT_RXEN_PIN, int txen = LORA_DEFAULT_TXEN_PIN); |
|
||||||
void setSPIFrequency(uint32_t frequency); |
|
||||||
|
|
||||||
void dumpRegisters(Stream& out); |
|
||||||
|
|
||||||
private: |
|
||||||
void explicitHeaderMode(); |
|
||||||
void implicitHeaderMode(); |
|
||||||
|
|
||||||
void handleDio0Rise(); |
|
||||||
|
|
||||||
uint8_t readRegister(uint16_t address); |
|
||||||
void writeRegister(uint16_t address, uint8_t value); |
|
||||||
uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value); |
|
||||||
|
|
||||||
static void onDio0Rise(); |
|
||||||
|
|
||||||
void handleLowDataRate(); |
|
||||||
void optimizeModemSensitivity(); |
|
||||||
|
|
||||||
private: |
|
||||||
SPISettings _spiSettings; |
|
||||||
int _ss; |
|
||||||
int _reset; |
|
||||||
int _dio0; |
|
||||||
int _rxen; |
|
||||||
int _txen; |
|
||||||
int _busy; |
|
||||||
int _modem; |
|
||||||
unsigned long _frequency; |
|
||||||
int _txp; |
|
||||||
uint8_t _sf; |
|
||||||
uint8_t _bw; |
|
||||||
uint8_t _cr; |
|
||||||
int _packetIndex; |
|
||||||
uint32_t _preambleLength; |
|
||||||
int _implicitHeaderMode; |
|
||||||
int _payloadLength; |
|
||||||
int _crcMode; |
|
||||||
int _fifo_tx_addr_ptr; |
|
||||||
int _fifo_rx_addr_ptr; |
|
||||||
uint8_t _packet[256]; |
|
||||||
bool _preinit_done; |
|
||||||
int _rxPacketLength; |
|
||||||
void (*_onReceive)(int); |
|
||||||
}; |
|
||||||
|
|
||||||
extern sx128x sx128x_modem; |
|
||||||
|
|
||||||
#endif |
|
Loading…
Reference in new issue