2024-06-10 08:05:21 -04:00
|
|
|
// 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.
|
|
|
|
|
2024-08-06 12:37:47 -04:00
|
|
|
#include "Radio.hpp"
|
2024-06-10 08:05:21 -04:00
|
|
|
|
|
|
|
#if PLATFORM == PLATFORM_ESP32
|
|
|
|
#if defined(ESP32) and !defined(CONFIG_IDF_TARGET_ESP32S3)
|
|
|
|
#include "soc/rtc_wdt.h"
|
|
|
|
#endif
|
|
|
|
#define ISR_VECT IRAM_ATTR
|
|
|
|
#else
|
|
|
|
#define ISR_VECT
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// SX126x registers
|
|
|
|
#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)
|
|
|
|
|
2024-09-04 12:37:09 -04:00
|
|
|
extern FIFOBuffer packet_rdy_interfaces;
|
2024-06-10 08:05:21 -04:00
|
|
|
extern RadioInterface* interface_obj[];
|
|
|
|
|
|
|
|
// ISRs cannot provide parameters to the functions they call. Since we have
|
2024-07-03 06:06:52 -04:00
|
|
|
// multiple interfaces, we have to read each dio0 pin for each one and see
|
2024-06-10 08:05:21 -04:00
|
|
|
// which one is high. We can then use the index of this pin in the 2D array to
|
2024-07-03 06:06:52 -04:00
|
|
|
// signal the correct interface to the main loop
|
|
|
|
void ISR_VECT onDio0Rise() {
|
2024-09-11 15:18:43 -04:00
|
|
|
BaseType_t int_status = taskENTER_CRITICAL_FROM_ISR();
|
2024-06-10 08:05:21 -04:00
|
|
|
for (int i = 0; i < INTERFACE_COUNT; i++) {
|
|
|
|
if (digitalRead(interface_pins[i][5]) == HIGH) {
|
2024-09-10 17:39:15 -04:00
|
|
|
if (interface_obj[i]->getPacketValidity()) {
|
2024-09-11 15:18:43 -04:00
|
|
|
interface_obj[i]->handleDio0Rise();
|
2024-09-10 17:39:15 -04:00
|
|
|
}
|
|
|
|
if (interfaces[i] == SX128X) {
|
|
|
|
// On the SX1280, there is a bug which can cause the busy line
|
|
|
|
// to remain high if a high amount of packets are received when
|
|
|
|
// in continuous RX mode. This is documented as Errata 16.1 in
|
|
|
|
// the SX1280 datasheet v3.2 (page 149)
|
|
|
|
// Therefore, the modem is set into receive mode each time a packet is received.
|
|
|
|
interface_obj[i]->receive();
|
|
|
|
}
|
2024-06-10 08:05:21 -04:00
|
|
|
}
|
|
|
|
}
|
2024-09-11 15:18:43 -04:00
|
|
|
taskEXIT_CRITICAL_FROM_ISR(int_status);
|
2024-06-10 08:05:21 -04:00
|
|
|
}
|
|
|
|
|
2024-08-06 12:37:47 -04:00
|
|
|
sx126x::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) :
|
2024-06-10 08:05:21 -04:00
|
|
|
RadioInterface(index),
|
2024-07-03 06:06:52 -04:00
|
|
|
_spiSettings(8E6, MSBFIRST, SPI_MODE0), _spiModem(spi), _ss(ss),
|
|
|
|
_sclk(sclk), _mosi(mosi), _miso(miso), _reset(reset), _dio0(dio0),
|
|
|
|
_busy(busy), _rxen(rxen), _frequency(0), _txp(0), _sf(0x07), _bw(0x04),
|
|
|
|
_cr(0x01), _ldro(0x00), _packetIndex(0), _implicitHeaderMode(0),
|
|
|
|
_payloadLength(255), _crcMode(1), _fifo_tx_addr_ptr(0),
|
|
|
|
_fifo_rx_addr_ptr(0), _preinit_done(false), _tcxo(tcxo),
|
|
|
|
_dio2_as_rf_switch(dio2_as_rf_switch)
|
2024-06-10 08:05:21 -04:00
|
|
|
{
|
|
|
|
// overide Stream timeout value
|
|
|
|
setTimeout(0);
|
|
|
|
// TODO, figure out why this has to be done. Using the index to reference the
|
|
|
|
// interface_obj list causes a crash otherwise
|
|
|
|
_index = getIndex();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool sx126x::preInit() {
|
|
|
|
pinMode(_ss, OUTPUT);
|
|
|
|
digitalWrite(_ss, HIGH);
|
|
|
|
|
|
|
|
// todo: check if this change causes issues on any platforms
|
|
|
|
#if MCU_VARIANT == MCU_ESP32
|
|
|
|
if (_sclk != -1 && _miso != -1 && _mosi != -1 && _ss != -1) {
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->begin(_sclk, _miso, _mosi, _ss);
|
2024-06-10 08:05:21 -04:00
|
|
|
} else {
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->begin();
|
2024-06-10 08:05:21 -04:00
|
|
|
}
|
|
|
|
#else
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->begin();
|
2024-06-10 08:05:21 -04:00
|
|
|
#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);
|
|
|
|
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->beginTransaction(_spiSettings);
|
|
|
|
_spiModem->transfer(opcode);
|
|
|
|
_spiModem->transfer((address & 0xFF00) >> 8);
|
|
|
|
_spiModem->transfer(address & 0x00FF);
|
2024-06-10 08:05:21 -04:00
|
|
|
if (opcode == OP_READ_REGISTER_6X) {
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->transfer(0x00);
|
2024-06-10 08:05:21 -04:00
|
|
|
}
|
2024-08-06 12:37:47 -04:00
|
|
|
response = _spiModem->transfer(value);
|
|
|
|
_spiModem->endTransaction();
|
2024-06-10 08:05:21 -04:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->beginTransaction(_spiSettings);
|
|
|
|
_spiModem->transfer(opcode);
|
2024-06-10 08:05:21 -04:00
|
|
|
|
|
|
|
for (int i = 0; i < size; i++)
|
|
|
|
{
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->transfer(buffer[i]);
|
2024-06-10 08:05:21 -04:00
|
|
|
}
|
|
|
|
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->endTransaction();
|
2024-06-10 08:05:21 -04:00
|
|
|
|
|
|
|
digitalWrite(_ss, HIGH);
|
|
|
|
}
|
|
|
|
|
|
|
|
void sx126x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size)
|
|
|
|
{
|
|
|
|
waitOnBusy();
|
|
|
|
|
|
|
|
digitalWrite(_ss, LOW);
|
|
|
|
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->beginTransaction(_spiSettings);
|
|
|
|
_spiModem->transfer(opcode);
|
|
|
|
_spiModem->transfer(0x00);
|
2024-06-10 08:05:21 -04:00
|
|
|
|
|
|
|
for (int i = 0; i < size; i++)
|
|
|
|
{
|
2024-08-06 12:37:47 -04:00
|
|
|
buffer[i] = _spiModem->transfer(0x00);
|
2024-06-10 08:05:21 -04:00
|
|
|
}
|
|
|
|
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->endTransaction();
|
2024-06-10 08:05:21 -04:00
|
|
|
|
|
|
|
digitalWrite(_ss, HIGH);
|
|
|
|
}
|
|
|
|
|
|
|
|
void sx126x::writeBuffer(const uint8_t* buffer, size_t size)
|
|
|
|
{
|
|
|
|
waitOnBusy();
|
|
|
|
|
|
|
|
digitalWrite(_ss, LOW);
|
|
|
|
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->beginTransaction(_spiSettings);
|
|
|
|
_spiModem->transfer(OP_FIFO_WRITE_6X);
|
|
|
|
_spiModem->transfer(_fifo_tx_addr_ptr);
|
2024-06-10 08:05:21 -04:00
|
|
|
|
|
|
|
for (int i = 0; i < size; i++)
|
|
|
|
{
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->transfer(buffer[i]);
|
2024-06-10 08:05:21 -04:00
|
|
|
_fifo_tx_addr_ptr++;
|
|
|
|
}
|
|
|
|
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->endTransaction();
|
2024-06-10 08:05:21 -04:00
|
|
|
|
|
|
|
digitalWrite(_ss, HIGH);
|
|
|
|
}
|
|
|
|
|
|
|
|
void sx126x::readBuffer(uint8_t* buffer, size_t size)
|
|
|
|
{
|
|
|
|
waitOnBusy();
|
|
|
|
|
|
|
|
digitalWrite(_ss, LOW);
|
|
|
|
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->beginTransaction(_spiSettings);
|
|
|
|
_spiModem->transfer(OP_FIFO_READ_6X);
|
|
|
|
_spiModem->transfer(_fifo_rx_addr_ptr);
|
|
|
|
_spiModem->transfer(0x00);
|
2024-06-10 08:05:21 -04:00
|
|
|
|
|
|
|
for (int i = 0; i < size; i++)
|
|
|
|
{
|
2024-08-06 12:37:47 -04:00
|
|
|
buffer[i] = _spiModem->transfer(0x00);
|
2024-06-10 08:05:21 -04:00
|
|
|
}
|
|
|
|
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->endTransaction();
|
2024-06-10 08:05:21 -04:00
|
|
|
|
|
|
|
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(uint32_t 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(uint32_t 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()
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
rxAntEnable();
|
|
|
|
|
|
|
|
setFrequency(_frequency);
|
|
|
|
|
|
|
|
setTxPower(_txp);
|
|
|
|
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);
|
|
|
|
|
|
|
|
_radio_online = true;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void sx126x::end()
|
|
|
|
{
|
|
|
|
// put in sleep mode
|
|
|
|
sleep();
|
|
|
|
|
|
|
|
// stop SPI
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->end();
|
2024-06-10 08:05:21 -04:00
|
|
|
|
|
|
|
_bitrate = 0;
|
|
|
|
|
|
|
|
_radio_online = false;
|
|
|
|
|
|
|
|
_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)(uint8_t, 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
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->usingInterrupt(digitalPinToInterrupt(_dio0));
|
2024-06-10 08:05:21 -04:00
|
|
|
#endif
|
|
|
|
// make function available
|
|
|
|
extern void onDio0Rise();
|
|
|
|
|
|
|
|
attachInterrupt(digitalPinToInterrupt(_dio0), onDio0Rise, RISING);
|
|
|
|
} else {
|
|
|
|
detachInterrupt(digitalPinToInterrupt(_dio0));
|
|
|
|
#ifdef SPI_HAS_NOTUSINGINTERRUPT
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->notUsingInterrupt(digitalPinToInterrupt(_dio0));
|
2024-06-10 08:05:21 -04:00
|
|
|
#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()
|
|
|
|
{
|
|
|
|
uint8_t byte;
|
|
|
|
if (_tcxo) {
|
|
|
|
// STDBY_XOSC
|
|
|
|
byte = MODE_STDBY_XOSC_6X;
|
|
|
|
} else {
|
|
|
|
// STDBY_RC
|
|
|
|
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 (_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};
|
2024-09-12 10:57:16 -04:00
|
|
|
#elif BOARD_MODEL == BOARD_TECHO
|
|
|
|
uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
|
2024-07-09 04:40:30 -04:00
|
|
|
#elif BOARD_MODEL == BOARD_T3S3
|
2024-06-10 08:05:21 -04:00
|
|
|
uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
|
|
|
|
#else
|
|
|
|
uint8_t buf[4] = {0};
|
|
|
|
#endif
|
|
|
|
executeOpcode(OP_DIO3_TCXO_CTRL_6X, buf, 4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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; }
|
|
|
|
|
|
|
|
_txp = level;
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t sx126x::getTxPower() {
|
|
|
|
return _txp;
|
|
|
|
}
|
|
|
|
|
|
|
|
void sx126x::setFrequency(uint32_t 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t sx126x::getSpreadingFactor()
|
|
|
|
{
|
|
|
|
return _sf;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t 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(uint32_t 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t sx126x::getCodingRate4()
|
|
|
|
{
|
|
|
|
return _cr + 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
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::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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-07-03 06:06:52 -04:00
|
|
|
void sx126x::handleDio0Rise()
|
2024-06-10 08:05:21 -04:00
|
|
|
{
|
2024-09-10 17:39:15 -04:00
|
|
|
// received a packet
|
|
|
|
_packetIndex = 0;
|
2024-06-10 08:05:21 -04:00
|
|
|
|
2024-09-10 17:39:15 -04:00
|
|
|
// read packet length
|
|
|
|
uint8_t rxbuf[2] = {0};
|
|
|
|
executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2);
|
|
|
|
int packetLength = rxbuf[0];
|
2024-06-10 08:05:21 -04:00
|
|
|
|
2024-09-10 17:39:15 -04:00
|
|
|
if (_onReceive) {
|
|
|
|
_onReceive(_index, packetLength);
|
2024-06-10 08:05:21 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void sx126x::updateBitrate() {
|
|
|
|
if (_radio_online) {
|
|
|
|
_lora_symbol_rate = (float)getSignalBandwidth()/(float)(pow(2, _sf));
|
|
|
|
_lora_symbol_time_ms = (1.0/_lora_symbol_rate)*1000.0;
|
|
|
|
_bitrate = (uint32_t)(_sf * ( (4.0/(float)(_cr+4)) / ((float)(pow(2, _sf))/((float)getSignalBandwidth()/1000.0)) ) * 1000.0);
|
|
|
|
_lora_us_per_byte = 1000000.0/((float)_bitrate/8.0);
|
|
|
|
//_csma_slot_ms = _lora_symbol_time_ms*10;
|
|
|
|
float target_preamble_symbols = (LORA_PREAMBLE_TARGET_MS/_lora_symbol_time_ms)-LORA_PREAMBLE_SYMBOLS_HW;
|
|
|
|
if (target_preamble_symbols < LORA_PREAMBLE_SYMBOLS_MIN) {
|
|
|
|
target_preamble_symbols = LORA_PREAMBLE_SYMBOLS_MIN;
|
|
|
|
} else {
|
|
|
|
target_preamble_symbols = ceil(target_preamble_symbols);
|
|
|
|
}
|
|
|
|
_preambleLength = (long)target_preamble_symbols;
|
|
|
|
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
|
|
|
|
} else {
|
|
|
|
_bitrate = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-10 17:39:15 -04:00
|
|
|
bool ISR_VECT sx126x::getPacketValidity() {
|
2024-07-03 06:06:52 -04:00
|
|
|
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);
|
2024-09-10 17:39:15 -04:00
|
|
|
|
|
|
|
if ((buf[1] & IRQ_PAYLOAD_CRC_ERROR_MASK_6X) == 0) {
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
2024-07-03 06:06:52 -04:00
|
|
|
}
|
2024-06-10 08:05:21 -04:00
|
|
|
// SX127x 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
|
|
|
|
|
2024-08-06 12:37:47 -04:00
|
|
|
sx127x::sx127x(uint8_t index, SPIClass* spi, int ss, int sclk, int mosi, int miso, int reset, int dio0, int busy) :
|
2024-06-10 08:05:21 -04:00
|
|
|
RadioInterface(index),
|
|
|
|
_spiSettings(8E6, MSBFIRST, SPI_MODE0),
|
|
|
|
_spiModem(spi),
|
|
|
|
_ss(ss), _sclk(sclk), _mosi(mosi), _miso(miso), _reset(reset), _dio0(dio0),
|
|
|
|
_busy(busy), _frequency(0), _packetIndex(0), _preinit_done(false)
|
|
|
|
{
|
|
|
|
setTimeout(0);
|
|
|
|
// TODO, figure out why this has to be done. Using the index to reference the
|
|
|
|
// interface_obj list causes a crash otherwise
|
|
|
|
_index = getIndex();
|
|
|
|
}
|
|
|
|
|
|
|
|
void sx127x::setSPIFrequency(uint32_t frequency) { _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); }
|
|
|
|
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);
|
|
|
|
// todo: check if this change causes issues on any platforms
|
|
|
|
#if MCU_VARIANT == MCU_ESP32
|
|
|
|
if (_sclk != -1 && _miso != -1 && _mosi != -1 && _ss != -1) {
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->begin(_sclk, _miso, _mosi, _ss);
|
2024-06-10 08:05:21 -04:00
|
|
|
} else {
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->begin();
|
2024-06-10 08:05:21 -04:00
|
|
|
}
|
|
|
|
#else
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->begin();
|
2024-06-10 08:05:21 -04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// 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);
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->beginTransaction(_spiSettings);
|
|
|
|
_spiModem->transfer(address);
|
|
|
|
response = _spiModem->transfer(value);
|
|
|
|
_spiModem->endTransaction();
|
2024-06-10 08:05:21 -04:00
|
|
|
digitalWrite(_ss, HIGH);
|
|
|
|
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
|
|
|
|
int sx127x::begin() {
|
|
|
|
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();
|
|
|
|
|
|
|
|
_radio_online = true;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void sx127x::end() {
|
|
|
|
sleep();
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->end();
|
2024-06-10 08:05:21 -04:00
|
|
|
_bitrate = 0;
|
|
|
|
_radio_online = false;
|
|
|
|
_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)(uint8_t, int)) {
|
|
|
|
_onReceive = callback;
|
|
|
|
|
|
|
|
if (callback) {
|
|
|
|
pinMode(_dio0, INPUT);
|
|
|
|
writeRegister(REG_DIO_MAPPING_1_7X, 0x00);
|
|
|
|
|
|
|
|
#ifdef SPI_HAS_NOTUSINGINTERRUPT
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->usingInterrupt(digitalPinToInterrupt(_dio0));
|
2024-06-10 08:05:21 -04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// make function available
|
|
|
|
extern void onDio0Rise();
|
|
|
|
|
|
|
|
attachInterrupt(digitalPinToInterrupt(_dio0), onDio0Rise, RISING);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
detachInterrupt(digitalPinToInterrupt(_dio0));
|
|
|
|
|
|
|
|
#ifdef SPI_HAS_NOTUSINGINTERRUPT
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->notUsingInterrupt(digitalPinToInterrupt(_dio0));
|
2024-06-10 08:05:21 -04:00
|
|
|
#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(uint32_t 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
_sf = sf;
|
|
|
|
|
|
|
|
writeRegister(REG_MODEM_CONFIG_2_7X, (readRegister(REG_MODEM_CONFIG_2_7X) & 0x0f) | ((sf << 4) & 0xf0));
|
|
|
|
handleLowDataRate();
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t sx127x::getSpreadingFactor()
|
|
|
|
{
|
|
|
|
return _sf;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t 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(uint32_t 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;
|
|
|
|
_cr = cr;
|
|
|
|
writeRegister(REG_MODEM_CONFIG_1_7X, (readRegister(REG_MODEM_CONFIG_1_7X) & 0xf1) | (cr << 1));
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t sx127x::getCodingRate4()
|
|
|
|
{
|
|
|
|
return _cr + 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
void sx127x::setPreambleLength(long length) {
|
|
|
|
_preambleLength = 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-03 06:06:52 -04:00
|
|
|
void sx127x::handleDio0Rise() {
|
2024-06-10 08:05:21 -04:00
|
|
|
_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(_index, packetLength);
|
|
|
|
}
|
|
|
|
writeRegister(REG_FIFO_ADDR_PTR_7X, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void sx127x::updateBitrate() {
|
|
|
|
if (_radio_online) {
|
|
|
|
_lora_symbol_rate = (float)getSignalBandwidth()/(float)(pow(2, _sf));
|
|
|
|
_lora_symbol_time_ms = (1.0/_lora_symbol_rate)*1000.0;
|
|
|
|
_bitrate = (uint32_t)(_sf * ( (4.0/(float)(_cr+4)) / ((float)(pow(2, _sf))/((float)getSignalBandwidth()/1000.0)) ) * 1000.0);
|
|
|
|
_lora_us_per_byte = 1000000.0/((float)_bitrate/8.0);
|
|
|
|
//_csma_slot_ms = _lora_symbol_time_ms*10;
|
|
|
|
float target_preamble_symbols = (LORA_PREAMBLE_TARGET_MS/_lora_symbol_time_ms)-LORA_PREAMBLE_SYMBOLS_HW;
|
|
|
|
if (target_preamble_symbols < LORA_PREAMBLE_SYMBOLS_MIN) {
|
|
|
|
target_preamble_symbols = LORA_PREAMBLE_SYMBOLS_MIN;
|
|
|
|
} else {
|
|
|
|
target_preamble_symbols = ceil(target_preamble_symbols);
|
|
|
|
}
|
|
|
|
_preambleLength = (long)target_preamble_symbols;
|
|
|
|
} else {
|
|
|
|
_bitrate = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-10 17:39:15 -04:00
|
|
|
bool ISR_VECT sx127x::getPacketValidity() {
|
2024-07-03 06:15:30 -04:00
|
|
|
int irqFlags = readRegister(REG_IRQ_FLAGS_7X);
|
|
|
|
|
|
|
|
// Clear IRQs
|
|
|
|
writeRegister(REG_IRQ_FLAGS_7X, irqFlags);
|
2024-09-10 17:39:15 -04:00
|
|
|
|
|
|
|
if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK_7X) == 0) {
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
2024-07-03 06:06:52 -04:00
|
|
|
}
|
|
|
|
|
2024-06-10 08:05:21 -04:00
|
|
|
// SX128x registers
|
|
|
|
#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)
|
|
|
|
|
2024-08-06 12:37:47 -04:00
|
|
|
sx128x::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) :
|
2024-06-10 08:05:21 -04:00
|
|
|
RadioInterface(index),
|
|
|
|
_spiSettings(8E6, MSBFIRST, SPI_MODE0),
|
|
|
|
_spiModem(spi),
|
|
|
|
_ss(ss), _sclk(sclk), _mosi(mosi), _miso(miso), _reset(reset), _dio0(dio0),
|
2024-07-03 06:06:52 -04:00
|
|
|
_busy(busy), _rxen(rxen), _txen(txen), _frequency(0), _txp(0), _sf(0x05),
|
2024-06-10 08:05:21 -04:00
|
|
|
_bw(0x34), _cr(0x01), _packetIndex(0), _implicitHeaderMode(0),
|
|
|
|
_payloadLength(255), _crcMode(0), _fifo_tx_addr_ptr(0), _fifo_rx_addr_ptr(0),
|
2024-07-03 06:06:52 -04:00
|
|
|
_rxPacketLength(0), _preinit_done(false),
|
2024-06-10 08:05:21 -04:00
|
|
|
_tcxo(tcxo)
|
|
|
|
{
|
|
|
|
// overide Stream timeout value
|
|
|
|
setTimeout(0);
|
|
|
|
// TODO, figure out why this has to be done. Using the index to reference the
|
|
|
|
// interface_obj list causes a crash otherwise
|
|
|
|
_index = getIndex();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool sx128x::preInit() {
|
|
|
|
// setup pins
|
|
|
|
pinMode(_ss, OUTPUT);
|
|
|
|
// set SS high
|
|
|
|
digitalWrite(_ss, HIGH);
|
|
|
|
|
|
|
|
// todo: check if this change causes issues on any platforms
|
|
|
|
#if MCU_VARIANT == MCU_ESP32
|
|
|
|
if (_sclk != -1 && _miso != -1 && _mosi != -1 && _ss != -1) {
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->begin(_sclk, _miso, _mosi, _ss);
|
2024-06-10 08:05:21 -04:00
|
|
|
} else {
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->begin();
|
2024-06-10 08:05:21 -04:00
|
|
|
}
|
|
|
|
#else
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->begin();
|
2024-06-10 08:05:21 -04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->beginTransaction(_spiSettings);
|
|
|
|
_spiModem->transfer(opcode);
|
|
|
|
_spiModem->transfer((address & 0xFF00) >> 8);
|
|
|
|
_spiModem->transfer(address & 0x00FF);
|
2024-06-10 08:05:21 -04:00
|
|
|
if (opcode == OP_READ_REGISTER_8X) {
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->transfer(0x00);
|
2024-06-10 08:05:21 -04:00
|
|
|
}
|
2024-08-06 12:37:47 -04:00
|
|
|
response = _spiModem->transfer(value);
|
|
|
|
_spiModem->endTransaction();
|
2024-06-10 08:05:21 -04:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->beginTransaction(_spiSettings);
|
|
|
|
_spiModem->transfer(opcode);
|
2024-06-10 08:05:21 -04:00
|
|
|
|
|
|
|
for (int i = 0; i < size; i++)
|
|
|
|
{
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->transfer(buffer[i]);
|
2024-06-10 08:05:21 -04:00
|
|
|
}
|
|
|
|
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->endTransaction();
|
2024-06-10 08:05:21 -04:00
|
|
|
|
|
|
|
digitalWrite(_ss, HIGH);
|
|
|
|
}
|
|
|
|
|
|
|
|
void sx128x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size)
|
|
|
|
{
|
|
|
|
waitOnBusy();
|
|
|
|
|
|
|
|
digitalWrite(_ss, LOW);
|
|
|
|
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->beginTransaction(_spiSettings);
|
|
|
|
_spiModem->transfer(opcode);
|
|
|
|
_spiModem->transfer(0x00);
|
2024-06-10 08:05:21 -04:00
|
|
|
|
|
|
|
for (int i = 0; i < size; i++)
|
|
|
|
{
|
2024-08-06 12:37:47 -04:00
|
|
|
buffer[i] = _spiModem->transfer(0x00);
|
2024-06-10 08:05:21 -04:00
|
|
|
}
|
|
|
|
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->endTransaction();
|
2024-06-10 08:05:21 -04:00
|
|
|
|
|
|
|
digitalWrite(_ss, HIGH);
|
|
|
|
}
|
|
|
|
|
|
|
|
void sx128x::writeBuffer(const uint8_t* buffer, size_t size)
|
|
|
|
{
|
|
|
|
waitOnBusy();
|
|
|
|
|
|
|
|
digitalWrite(_ss, LOW);
|
|
|
|
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->beginTransaction(_spiSettings);
|
|
|
|
_spiModem->transfer(OP_FIFO_WRITE_8X);
|
|
|
|
_spiModem->transfer(_fifo_tx_addr_ptr);
|
2024-06-10 08:05:21 -04:00
|
|
|
|
|
|
|
for (int i = 0; i < size; i++)
|
|
|
|
{
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->transfer(buffer[i]);
|
2024-06-10 08:05:21 -04:00
|
|
|
_fifo_tx_addr_ptr++;
|
|
|
|
}
|
|
|
|
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->endTransaction();
|
2024-06-10 08:05:21 -04:00
|
|
|
|
|
|
|
digitalWrite(_ss, HIGH);
|
|
|
|
}
|
|
|
|
|
|
|
|
void sx128x::readBuffer(uint8_t* buffer, size_t size)
|
|
|
|
{
|
|
|
|
waitOnBusy();
|
|
|
|
|
|
|
|
digitalWrite(_ss, LOW);
|
|
|
|
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->beginTransaction(_spiSettings);
|
|
|
|
_spiModem->transfer(OP_FIFO_READ_8X);
|
|
|
|
_spiModem->transfer(_fifo_rx_addr_ptr);
|
|
|
|
_spiModem->transfer(0x00);
|
2024-06-10 08:05:21 -04:00
|
|
|
|
|
|
|
for (int i = 0; i < size; i++)
|
|
|
|
{
|
2024-08-06 12:37:47 -04:00
|
|
|
buffer[i] = _spiModem->transfer(0x00);
|
2024-06-10 08:05:21 -04:00
|
|
|
}
|
|
|
|
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->endTransaction();
|
2024-06-10 08:05:21 -04:00
|
|
|
|
|
|
|
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 << 4;
|
|
|
|
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;
|
|
|
|
|
2024-07-02 07:15:40 -04:00
|
|
|
while (e <= 15) {
|
|
|
|
while (m <= 15) {
|
|
|
|
preamblelen = m * (pow(2,e));
|
2024-06-10 08:05:21 -04:00
|
|
|
if (preamblelen >= preamble) break;
|
2024-07-02 07:15:40 -04:00
|
|
|
m++;
|
2024-06-10 08:05:21 -04:00
|
|
|
}
|
|
|
|
if (preamblelen >= preamble) break;
|
2024-07-02 07:15:40 -04:00
|
|
|
m = 0;
|
|
|
|
e++;
|
2024-06-10 08:05:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
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()
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
standby();
|
|
|
|
loraMode();
|
|
|
|
rxAntEnable();
|
|
|
|
|
|
|
|
setFrequency(_frequency);
|
|
|
|
|
|
|
|
// set LNA boost
|
|
|
|
// todo: implement this
|
|
|
|
//writeRegister(REG_LNA, 0x96);
|
|
|
|
|
|
|
|
setModulationParams(_sf, _bw, _cr);
|
|
|
|
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
|
|
|
|
|
|
|
|
setTxPower(_txp);
|
|
|
|
|
|
|
|
// set base addresses
|
|
|
|
uint8_t basebuf[2] = {0};
|
|
|
|
executeOpcode(OP_BUFFER_BASE_ADDR_8X, basebuf, 2);
|
|
|
|
|
|
|
|
_radio_online = true;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void sx128x::end()
|
|
|
|
{
|
|
|
|
// put in sleep mode
|
|
|
|
sleep();
|
|
|
|
|
|
|
|
// stop SPI
|
2024-08-06 12:37:47 -04:00
|
|
|
_spiModem->end();
|
2024-06-10 08:05:21 -04:00
|
|
|
|
|
|
|
_bitrate = 0;
|
|
|
|
|
|
|
|
_radio_online = false;
|
|
|
|
_preinit_done = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int sx128x::beginPacket(int implicitHeader)
|
|
|
|
{
|
|
|
|
// put in standby mode
|
|
|
|
standby();
|
|
|
|
|
|
|
|
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] = IRQ_PREAMBLE_DET_MASK_8X;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((buf[1] & IRQ_HEADER_DET_MASK_8X) != 0) {
|
|
|
|
byte = byte | 0x02 | 0x04;
|
|
|
|
}
|
|
|
|
|
|
|
|
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};
|
2024-09-10 17:39:15 -04:00
|
|
|
executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 5);
|
2024-06-10 08:05:21 -04:00
|
|
|
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()
|
|
|
|
{
|
2024-09-10 17:39:15 -04:00
|
|
|
if (!available()) {
|
|
|
|
return -1;
|
|
|
|
}
|
2024-06-10 08:05:21 -04:00
|
|
|
|
2024-09-10 17:39:15 -04:00
|
|
|
// if received new packet
|
|
|
|
if (_packetIndex == 0) {
|
|
|
|
uint8_t rxbuf[2] = {0};
|
|
|
|
executeOpcodeRead(OP_RX_BUFFER_STATUS_8X, rxbuf, 2);
|
|
|
|
int size;
|
|
|
|
// If implicit header mode is enabled, read packet length as payload length instead.
|
|
|
|
// See SX1280 datasheet v3.2, page 92
|
|
|
|
if (_implicitHeaderMode == 0x80) {
|
|
|
|
size = _payloadLength;
|
|
|
|
} else {
|
|
|
|
size = rxbuf[0];
|
|
|
|
}
|
|
|
|
_fifo_rx_addr_ptr = rxbuf[1];
|
|
|
|
|
|
|
|
if (size > 255) {
|
|
|
|
size = 255;
|
|
|
|
}
|
|
|
|
|
|
|
|
readBuffer(_packet, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t byte = _packet[_packetIndex];
|
|
|
|
_packetIndex++;
|
|
|
|
return byte;
|
2024-06-10 08:05:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
int sx128x::peek()
|
|
|
|
{
|
|
|
|
if (!available()) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t b = _packet[_packetIndex];
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
|
|
|
void sx128x::flush()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void sx128x::onReceive(void(*callback)(uint8_t, 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;
|
|
|
|
|
2024-09-10 17:39:15 -04:00
|
|
|
// On the SX1280, no RxDone IRQ is generated if a packet is received with
|
|
|
|
// an invalid header, but the modem will be taken out of single RX mode.
|
|
|
|
// This can cause the modem to not receive packets until it is reset
|
|
|
|
// again. This is documented as Errata 16.2 in the SX1280 datasheet v3.2
|
|
|
|
// (page 150) Below, the header error IRQ is mapped to dio0 so that the
|
|
|
|
// modem can be set into RX mode again on reception of a corrupted
|
|
|
|
// header.
|
2024-06-10 08:05:21 -04:00
|
|
|
// set dio0 masks
|
|
|
|
buf[2] = 0x00;
|
2024-09-10 17:39:15 -04:00
|
|
|
buf[3] = IRQ_RX_DONE_MASK_8X | IRQ_HEADER_ERROR_MASK_8X;
|
2024-06-10 08:05:21 -04:00
|
|
|
|
|
|
|
// 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);
|
2024-09-10 17:39:15 -04:00
|
|
|
#ifdef SPI_HAS_NOTUSINGINTERRUPT
|
|
|
|
_spiModem->usingInterrupt(digitalPinToInterrupt(_dio0));
|
|
|
|
#endif
|
2024-06-10 08:05:21 -04:00
|
|
|
|
|
|
|
// make function available
|
|
|
|
extern void onDio0Rise();
|
|
|
|
|
|
|
|
attachInterrupt(digitalPinToInterrupt(_dio0), onDio0Rise, RISING);
|
|
|
|
} else {
|
|
|
|
detachInterrupt(digitalPinToInterrupt(_dio0));
|
2024-09-10 17:39:15 -04:00
|
|
|
#ifdef SPI_HAS_NOTUSINGINTERRUPT
|
|
|
|
_spiModem->notUsingInterrupt(digitalPinToInterrupt(_dio0));
|
|
|
|
#endif
|
2024-06-10 08:05:21 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void sx128x::receive(int size)
|
|
|
|
{
|
|
|
|
if (size > 0) {
|
|
|
|
implicitHeaderMode();
|
|
|
|
|
|
|
|
// tell radio payload length
|
2024-07-03 06:06:52 -04:00
|
|
|
//_rxPacketLength = size;
|
2024-06-10 08:05:21 -04:00
|
|
|
//_payloadLength = size;
|
|
|
|
//setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
|
|
|
|
} else {
|
|
|
|
explicitHeaderMode();
|
|
|
|
}
|
|
|
|
|
|
|
|
rxAntEnable();
|
|
|
|
|
2024-09-10 17:39:15 -04:00
|
|
|
// On the SX1280, there is a bug which can cause the busy line
|
|
|
|
// to remain high if a high amount of packets are received when
|
|
|
|
// in continuous RX mode. This is documented as Errata 16.1 in
|
|
|
|
// the SX1280 datasheet v3.2 (page 149)
|
|
|
|
// Therefore, the modem is set to single RX mode below instead.
|
|
|
|
uint8_t mode[3] = {0}; // single RX mode
|
2024-06-10 08:05:21 -04:00
|
|
|
executeOpcode(OP_RX_8X, mode, 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
void sx128x::standby()
|
|
|
|
{
|
|
|
|
uint8_t byte;
|
|
|
|
if (_tcxo) {
|
|
|
|
// STDBY_XOSC
|
|
|
|
byte = 0x01;
|
|
|
|
} else {
|
|
|
|
// STDBY_RC
|
|
|
|
byte = 0x00;
|
|
|
|
}
|
|
|
|
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) {
|
2024-07-09 05:37:50 -04:00
|
|
|
uint8_t tx_buf[2];
|
|
|
|
#if BOARD_VARIANT == MODEL_13
|
|
|
|
// RAK4631 with WisBlock SX1280 module (LIBSYS002)
|
|
|
|
if (level > 27) {
|
|
|
|
level = 27;
|
|
|
|
} else if (level < 0) {
|
|
|
|
level = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
_txp = level;
|
|
|
|
|
|
|
|
int reg_value;
|
|
|
|
|
|
|
|
switch (level) {
|
|
|
|
case 0:
|
|
|
|
reg_value = -18;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
reg_value = -17;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
reg_value = -16;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
reg_value = -15;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
reg_value = -14;
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
reg_value = -13;
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
reg_value = -12;
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
reg_value = -10;
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
reg_value = -9;
|
|
|
|
break;
|
|
|
|
case 9:
|
|
|
|
reg_value = -8;
|
|
|
|
break;
|
|
|
|
case 10:
|
|
|
|
reg_value = -7;
|
|
|
|
break;
|
|
|
|
case 11:
|
|
|
|
reg_value = -6;
|
|
|
|
break;
|
|
|
|
case 12:
|
|
|
|
reg_value = -5;
|
|
|
|
break;
|
|
|
|
case 13:
|
|
|
|
reg_value = -4;
|
|
|
|
break;
|
|
|
|
case 14:
|
|
|
|
reg_value = -3;
|
|
|
|
break;
|
|
|
|
case 15:
|
|
|
|
reg_value = -2;
|
|
|
|
break;
|
|
|
|
case 16:
|
|
|
|
reg_value = -1;
|
|
|
|
break;
|
|
|
|
case 17:
|
|
|
|
reg_value = 0;
|
|
|
|
break;
|
|
|
|
case 18:
|
|
|
|
reg_value = 1;
|
|
|
|
break;
|
|
|
|
case 19:
|
|
|
|
reg_value = 2;
|
|
|
|
break;
|
|
|
|
case 20:
|
|
|
|
reg_value = 3;
|
|
|
|
break;
|
|
|
|
case 21:
|
|
|
|
reg_value = 4;
|
|
|
|
break;
|
|
|
|
case 22:
|
|
|
|
reg_value = 5;
|
|
|
|
break;
|
|
|
|
case 23:
|
|
|
|
reg_value = 6;
|
|
|
|
break;
|
|
|
|
case 24:
|
|
|
|
reg_value = 8;
|
|
|
|
break;
|
|
|
|
case 25:
|
|
|
|
reg_value = 9;
|
|
|
|
break;
|
|
|
|
case 26:
|
|
|
|
reg_value = 12;
|
|
|
|
break;
|
|
|
|
case 27:
|
|
|
|
reg_value = 13;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
reg_value = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2024-07-24 10:52:08 -04:00
|
|
|
tx_buf[0] = reg_value + 18;
|
2024-07-09 05:37:50 -04:00
|
|
|
tx_buf[1] = 0xE0; // ramping time - 20 microseconds
|
|
|
|
|
|
|
|
executeOpcode(OP_TX_PARAMS_8X, tx_buf, 2);
|
|
|
|
|
|
|
|
#elif BOARD_VARIANT == MODEL_A5
|
|
|
|
// T3S3 SX1280 PA
|
|
|
|
if (level > 20) {
|
|
|
|
level = 20;
|
2024-07-03 07:25:49 -04:00
|
|
|
} else if (level < 0) {
|
|
|
|
level = 0;
|
|
|
|
}
|
2024-06-10 08:05:21 -04:00
|
|
|
|
2024-07-03 07:25:49 -04:00
|
|
|
_txp = level;
|
|
|
|
|
|
|
|
int reg_value;
|
|
|
|
|
|
|
|
switch (level) {
|
2024-07-09 05:37:50 -04:00
|
|
|
/*case 0:
|
2024-07-03 07:25:49 -04:00
|
|
|
reg_value = -18;
|
2024-07-03 08:27:50 -04:00
|
|
|
break;
|
2024-07-03 07:25:49 -04:00
|
|
|
case 1:
|
|
|
|
reg_value = -17;
|
2024-07-03 08:27:50 -04:00
|
|
|
break;
|
2024-07-03 07:25:49 -04:00
|
|
|
case 2:
|
|
|
|
reg_value = -16;
|
2024-07-03 08:27:50 -04:00
|
|
|
break;
|
2024-07-03 07:25:49 -04:00
|
|
|
case 3:
|
|
|
|
reg_value = -15;
|
2024-07-03 08:27:50 -04:00
|
|
|
break;
|
2024-07-03 07:25:49 -04:00
|
|
|
case 4:
|
|
|
|
reg_value = -14;
|
2024-07-03 08:27:50 -04:00
|
|
|
break;
|
2024-07-03 07:25:49 -04:00
|
|
|
case 5:
|
|
|
|
reg_value = -13;
|
2024-07-03 08:27:50 -04:00
|
|
|
break;
|
2024-07-03 07:25:49 -04:00
|
|
|
case 6:
|
|
|
|
reg_value = -12;
|
2024-07-03 08:27:50 -04:00
|
|
|
break;
|
2024-07-03 07:25:49 -04:00
|
|
|
case 7:
|
|
|
|
reg_value = -10;
|
2024-07-03 08:27:50 -04:00
|
|
|
break;
|
2024-07-03 07:25:49 -04:00
|
|
|
case 8:
|
|
|
|
reg_value = -9;
|
2024-07-03 08:27:50 -04:00
|
|
|
break;
|
2024-07-03 07:25:49 -04:00
|
|
|
case 9:
|
|
|
|
reg_value = -8;
|
2024-07-03 08:27:50 -04:00
|
|
|
break;
|
2024-07-03 07:25:49 -04:00
|
|
|
case 10:
|
|
|
|
reg_value = -7;
|
2024-07-03 08:27:50 -04:00
|
|
|
break;
|
2024-07-03 07:25:49 -04:00
|
|
|
case 11:
|
|
|
|
reg_value = -6;
|
2024-07-03 08:27:50 -04:00
|
|
|
break;
|
2024-07-03 07:25:49 -04:00
|
|
|
case 12:
|
|
|
|
reg_value = -5;
|
2024-07-03 08:27:50 -04:00
|
|
|
break;
|
2024-07-03 07:25:49 -04:00
|
|
|
case 13:
|
|
|
|
reg_value = -4;
|
2024-07-03 08:27:50 -04:00
|
|
|
break;
|
2024-07-03 07:25:49 -04:00
|
|
|
case 14:
|
|
|
|
reg_value = -3;
|
2024-07-03 08:27:50 -04:00
|
|
|
break;
|
2024-07-03 07:25:49 -04:00
|
|
|
case 15:
|
|
|
|
reg_value = -2;
|
2024-07-03 08:27:50 -04:00
|
|
|
break;
|
2024-07-03 07:25:49 -04:00
|
|
|
case 16:
|
|
|
|
reg_value = -1;
|
2024-07-03 08:27:50 -04:00
|
|
|
break;
|
2024-07-03 07:25:49 -04:00
|
|
|
case 17:
|
|
|
|
reg_value = 0;
|
2024-07-03 08:27:50 -04:00
|
|
|
break;
|
2024-07-03 07:25:49 -04:00
|
|
|
case 18:
|
|
|
|
reg_value = 1;
|
2024-07-03 08:27:50 -04:00
|
|
|
break;
|
2024-07-03 07:25:49 -04:00
|
|
|
case 19:
|
|
|
|
reg_value = 2;
|
2024-07-09 05:37:50 -04:00
|
|
|
break;*/
|
2024-07-03 07:25:49 -04:00
|
|
|
case 20:
|
|
|
|
reg_value = 3;
|
2024-07-03 08:27:50 -04:00
|
|
|
break;
|
2024-07-09 05:37:50 -04:00
|
|
|
default:
|
|
|
|
reg_value = 0;
|
2024-07-03 08:27:50 -04:00
|
|
|
break;
|
2024-07-03 07:25:49 -04:00
|
|
|
}
|
2024-06-10 08:05:21 -04:00
|
|
|
|
2024-07-24 10:52:08 -04:00
|
|
|
tx_buf[0] = reg_value + 18;
|
2024-07-03 07:25:49 -04:00
|
|
|
tx_buf[1] = 0xE0; // ramping time - 20 microseconds
|
2024-07-09 05:37:50 -04:00
|
|
|
#else
|
|
|
|
// For SX1280 boards with no specific PA requirements
|
2024-07-03 07:25:49 -04:00
|
|
|
if (level > 13) {
|
|
|
|
level = 13;
|
|
|
|
} else if (level < -18) {
|
|
|
|
level = -18;
|
|
|
|
}
|
|
|
|
|
2024-07-09 05:37:50 -04:00
|
|
|
_txp = level;
|
2024-07-03 07:25:49 -04:00
|
|
|
|
2024-07-24 10:52:08 -04:00
|
|
|
tx_buf[0] = level + 18;
|
2024-07-03 07:25:49 -04:00
|
|
|
tx_buf[1] = 0xE0; // ramping time - 20 microseconds
|
2024-07-09 05:37:50 -04:00
|
|
|
#endif
|
|
|
|
executeOpcode(OP_TX_PARAMS_8X, tx_buf, 2);
|
2024-06-10 08:05:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t sx128x::getTxPower() {
|
|
|
|
return _txp;
|
|
|
|
}
|
|
|
|
|
|
|
|
void sx128x::setFrequency(uint32_t 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;
|
|
|
|
|
|
|
|
setModulationParams(sf, _bw, _cr);
|
|
|
|
handleLowDataRate();
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t sx128x::getSpreadingFactor()
|
|
|
|
{
|
|
|
|
return _sf;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t 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(uint32_t 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t sx128x::getCodingRate4()
|
|
|
|
{
|
|
|
|
return _cr + 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2024-09-12 15:32:54 -04:00
|
|
|
uint8_t sx128x::random()
|
2024-06-10 08:05:21 -04:00
|
|
|
{
|
|
|
|
// todo: implement
|
2024-09-12 10:57:16 -04:00
|
|
|
return 0x4; //chosen by fair die roll
|
|
|
|
//guarenteed to be random
|
|
|
|
//https://xkcd.com/221/
|
2024-06-10 08:05:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-07-03 06:06:52 -04:00
|
|
|
void sx128x::handleDio0Rise()
|
2024-06-10 08:05:21 -04:00
|
|
|
{
|
2024-09-10 17:39:15 -04:00
|
|
|
// received a packet
|
|
|
|
_packetIndex = 0;
|
2024-09-05 08:10:11 -04:00
|
|
|
|
2024-09-10 17:39:15 -04:00
|
|
|
uint8_t rxbuf[2] = {0};
|
|
|
|
executeOpcodeRead(OP_RX_BUFFER_STATUS_8X, rxbuf, 2);
|
2024-06-10 08:05:21 -04:00
|
|
|
|
2024-09-10 17:39:15 -04:00
|
|
|
// If implicit header mode is enabled, read packet length as payload length instead.
|
|
|
|
// See SX1280 datasheet v3.2, page 92
|
|
|
|
if (_implicitHeaderMode == 0x80) {
|
|
|
|
_rxPacketLength = _payloadLength;
|
|
|
|
} else {
|
|
|
|
_rxPacketLength = rxbuf[0];
|
|
|
|
}
|
2024-06-10 08:05:21 -04:00
|
|
|
|
2024-09-10 17:39:15 -04:00
|
|
|
if (_onReceive) {
|
|
|
|
_onReceive(_index, _rxPacketLength);
|
2024-06-10 08:05:21 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void sx128x::updateBitrate() {
|
|
|
|
if (_radio_online) {
|
|
|
|
_lora_symbol_rate = (float)getSignalBandwidth()/(float)(pow(2, _sf));
|
|
|
|
_lora_symbol_time_ms = (1.0/_lora_symbol_rate)*1000.0;
|
|
|
|
_bitrate = (uint32_t)(_sf * ( (4.0/(float)(_cr+4)) / ((float)(pow(2, _sf))/((float)getSignalBandwidth()/1000.0)) ) * 1000.0);
|
|
|
|
_lora_us_per_byte = 1000000.0/((float)_bitrate/8.0);
|
2024-07-03 06:06:52 -04:00
|
|
|
_csma_slot_ms = 10;
|
|
|
|
|
|
|
|
float target_preamble_symbols;
|
2024-09-11 15:18:43 -04:00
|
|
|
//if (_bitrate <= LORA_FAST_BITRATE_THRESHOLD) {
|
2024-07-03 06:06:52 -04:00
|
|
|
target_preamble_symbols = (LORA_PREAMBLE_TARGET_MS/_lora_symbol_time_ms)-LORA_PREAMBLE_SYMBOLS_HW;
|
2024-09-11 15:18:43 -04:00
|
|
|
//} else {
|
|
|
|
/*target_preamble_symbols = (LORA_PREAMBLE_FAST_TARGET_MS/_lora_symbol_time_ms)-LORA_PREAMBLE_SYMBOLS_HW;
|
|
|
|
}*/
|
2024-06-10 08:05:21 -04:00
|
|
|
if (target_preamble_symbols < LORA_PREAMBLE_SYMBOLS_MIN) {
|
|
|
|
target_preamble_symbols = LORA_PREAMBLE_SYMBOLS_MIN;
|
|
|
|
} else {
|
|
|
|
target_preamble_symbols = ceil(target_preamble_symbols);
|
|
|
|
}
|
|
|
|
_preambleLength = (long)target_preamble_symbols;
|
|
|
|
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
|
|
|
|
} else {
|
|
|
|
_bitrate = 0;
|
|
|
|
}
|
|
|
|
}
|
2024-07-03 06:06:52 -04:00
|
|
|
|
2024-09-10 17:39:15 -04:00
|
|
|
bool ISR_VECT sx128x::getPacketValidity() {
|
2024-07-03 06:06:52 -04:00
|
|
|
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);
|
2024-09-10 17:39:15 -04:00
|
|
|
|
|
|
|
if ((buf[1] & IRQ_PAYLOAD_CRC_ERROR_MASK_8X) == 0) {
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
2024-07-03 06:06:52 -04:00
|
|
|
}
|