2679 lines
66 KiB
C++
2679 lines
66 KiB
C++
// 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 "Radio.hpp"
|
|
|
|
#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)
|
|
|
|
extern FIFOBuffer packet_rdy_interfaces;
|
|
extern RadioInterface* interface_obj[];
|
|
|
|
// ISRs cannot provide parameters to the functions they call. Since we have
|
|
// multiple interfaces, we have to read each dio0 pin for each one and see
|
|
// which one is high. We can then use the index of this pin in the 2D array to
|
|
// signal the correct interface to the main loop
|
|
void ISR_VECT onDio0Rise() {
|
|
BaseType_t int_status = taskENTER_CRITICAL_FROM_ISR();
|
|
for (int i = 0; i < INTERFACE_COUNT; i++) {
|
|
if (digitalRead(interface_pins[i][5]) == HIGH) {
|
|
if (interface_obj[i]->getPacketValidity()) {
|
|
interface_obj[i]->handleDio0Rise();
|
|
}
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
taskEXIT_CRITICAL_FROM_ISR(int_status);
|
|
}
|
|
|
|
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) :
|
|
RadioInterface(index),
|
|
_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), _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)
|
|
{
|
|
// 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) {
|
|
_spiModem->begin(_sclk, _miso, _mosi, _ss);
|
|
} else {
|
|
_spiModem->begin();
|
|
}
|
|
#else
|
|
_spiModem->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);
|
|
|
|
_spiModem->beginTransaction(_spiSettings);
|
|
_spiModem->transfer(opcode);
|
|
_spiModem->transfer((address & 0xFF00) >> 8);
|
|
_spiModem->transfer(address & 0x00FF);
|
|
if (opcode == OP_READ_REGISTER_6X) {
|
|
_spiModem->transfer(0x00);
|
|
}
|
|
response = _spiModem->transfer(value);
|
|
_spiModem->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);
|
|
|
|
_spiModem->beginTransaction(_spiSettings);
|
|
_spiModem->transfer(opcode);
|
|
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
_spiModem->transfer(buffer[i]);
|
|
}
|
|
|