Skip to content

Add driver for SX126x modems #949

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion project_config/lmic_project_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@
//#define CFG_kr920 1
//#define CFG_in866 1
#define CFG_sx1276_radio 1
//#define LMIC_USE_INTERRUPTS
//#define CFG_sx1261_radio 1
//#define CFG_sx1262_radio 1
//#define ARDUINO_heltec_wifi_lora_32_V3
//#define LMIC_USE_INTERRUPTS
2 changes: 2 additions & 0 deletions src/arduino_lmic_hal_boards.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ const HalPinmap_t *GetPinmap_Catena4802();
const HalPinmap_t* GetPinmap_ttgo_lora32_v1();
const HalPinmap_t *GetPinmap_ttgo_lora32_v21();
const HalPinmap_t* GetPinmap_heltec_lora32();
const HalPinmap_t* GetPinmap_heltec_lora32_v3();
const HalPinmap_t* GetPinmap_Disco_L072cz_Lrwan1();
const HalPinmap_t* GetPinmap_ttgo_tbeam_s3();

const HalPinmap_t *GetPinmap_ThisBoard();

Expand Down
4 changes: 4 additions & 0 deletions src/arduino_lmic_hal_configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,11 @@ class HalConfiguration_t

virtual void begin(void) {}
virtual void end(void) {}
virtual uint8_t queryBusyPin(void) { return HalPinmap_t::LMIC_UNUSED_PIN; }
virtual bool queryUsingTcxo(void) { return false; }
virtual bool queryUsingDcdc(void) { return false; }
virtual bool queryUsingDIO2AsRfSwitch(void) { return false; }
virtual bool queryUsingDIO3AsTCXOSwitch(void) { return false; }

// compute desired transmit power policy. HopeRF needs
// (and previous versions of this library always chose)
Expand Down
76 changes: 76 additions & 0 deletions src/hal/getpinmap_heltec_lora32_v3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*

Module: getpinmap_heltec_lora32_v3.cpp

Function:
Arduino-LMIC C++ HAL pinmap for Heltec Wireless Stick Lite V3 and Wifi Lora32 V3

Copyright & License:
See accompanying LICENSE file.

Author:
Tristan Webber, Shrunk Innovation Labs July 2023

*/

#if defined(ARDUINO_heltec_wifi_lora_32_V3)

#include <arduino_lmic_hal_boards.h>
#include <Arduino.h>

#include "../lmic/oslmic.h"

namespace Arduino_LMIC
{

class HalConfiguration_heltec_lora32_v3 : public HalConfiguration_t
{
public:
enum DIGITAL_PINS : uint8_t
{
PIN_SX1262_NSS = SS,
PIN_SX1262_NRESET = RST_LoRa,
PIN_SX1262_BUSY = BUSY_LoRa,
PIN_SX1262_DIO1 = DIO0,
PIN_SX1262_DIO2 = HalPinmap_t::UNUSED_PIN,
PIN_SX1262_DIO3 = HalPinmap_t::UNUSED_PIN,
PIN_SX1262_ANT_SWITCH_RX = HalPinmap_t::UNUSED_PIN,
PIN_SX1262_ANT_SWITCH_TX_BOOST = HalPinmap_t::UNUSED_PIN,
PIN_SX1262_ANT_SWITCH_TX_RFO = HalPinmap_t::UNUSED_PIN,
PIN_VDD_BOOST_ENABLE = HalPinmap_t::UNUSED_PIN,
};

virtual u1_t queryBusyPin(void) override { return HalConfiguration_heltec_lora32_v3::PIN_SX1262_BUSY; };

virtual bool queryUsingDcdc(void) override { return true; };

virtual bool queryUsingDIO2AsRfSwitch(void) override { return true; };

virtual bool queryUsingDIO3AsTCXOSwitch(void) override { return true; };
};

static HalConfiguration_heltec_lora32_v3 myConfig;

static const HalPinmap_t myPinmap =
{
.nss = HalConfiguration_heltec_lora32_v3::PIN_SX1262_NSS,
.rxtx = HalConfiguration_heltec_lora32_v3::PIN_SX1262_ANT_SWITCH_RX,
.rst = HalConfiguration_heltec_lora32_v3::PIN_SX1262_NRESET,
.dio = {
HalConfiguration_heltec_lora32_v3::PIN_SX1262_DIO1,
HalConfiguration_heltec_lora32_v3::PIN_SX1262_DIO2,
HalConfiguration_heltec_lora32_v3::PIN_SX1262_DIO3,
},
.rxtx_rx_active = 0,
.rssi_cal = 10,
.spi_freq = 8000000, /* 8MHz */
.pConfig = &myConfig};

const HalPinmap_t *GetPinmap_heltec_lora32_v3(void)
{
return &myPinmap;
}

}; // namespace Arduino_LMIC

#endif // defined(ARDUINO_heltec_wifi_lora_32_V3)
4 changes: 4 additions & 0 deletions src/hal/getpinmap_thisboard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ const HalPinmap_t *GetPinmap_ThisBoard(void)
return GetPinmap_ttgo_lora32_v21();
#elif defined(ARDUINO_HELTEC_WIFI_LORA_32) || defined(ARDUINO_HELTEC_WIFI_LORA_32_V2) || defined(ARDUINO_HELTEC_WIRELESS_STICK)
return GetPinmap_heltec_lora32();
#elif defined(ARDUINO_heltec_wifi_lora_32_V3)
return GetPinmap_heltec_lora32_v3();
#elif defined(ARDUINO_TTGO_T_BEAM_S3)
return GetPinmap_ttgo_tbeam_s3();
#else
#pragma message("Board not supported -- use an explicit pinmap")
return nullptr;
Expand Down
70 changes: 70 additions & 0 deletions src/hal/getpinmap_ttgo_tbeam_s3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*

Module: getpinmap_ttgo_tbeam_s3.cpp.cpp

Function:
Arduino-LMIC C++ HAL pinmap for T-Beam S3 Core and T-Beam Supreme

Copyright & License:
See accompanying LICENSE file.

*/

#if defined(ARDUINO_TTGO_T_BEAM_S3)

#include <arduino_lmic_hal_boards.h>
#include <Arduino.h>

#include "../lmic/oslmic.h"

namespace Arduino_LMIC {

class HalConfiguration_ttgo_tbeam_s3 : public HalConfiguration_t {
public:
enum DIGITAL_PINS : uint8_t {
PIN_SX1262_NSS = 10,
PIN_SX1262_NRESET = 5,
PIN_SX1262_BUSY = 4,
PIN_SX1262_DIO1 = 1,
PIN_SX1262_DIO2 = HalPinmap_t::UNUSED_PIN,
PIN_SX1262_DIO3 = HalPinmap_t::UNUSED_PIN,
PIN_SX1262_ANT_SWITCH_RX = HalPinmap_t::UNUSED_PIN,
PIN_SX1262_ANT_SWITCH_TX_BOOST = HalPinmap_t::UNUSED_PIN,
PIN_SX1262_ANT_SWITCH_TX_RFO = HalPinmap_t::UNUSED_PIN,
PIN_VDD_BOOST_ENABLE = HalPinmap_t::UNUSED_PIN,
};

virtual u1_t queryBusyPin(void) override { return HalConfiguration_ttgo_tbeam_s3::PIN_SX1262_BUSY; };

virtual bool queryUsingDcdc(void) override { return true; };

virtual bool queryUsingDIO2AsRfSwitch(void) override { return true; };

virtual bool queryUsingDIO3AsTCXOSwitch(void) override { return true; };
};

static HalConfiguration_ttgo_tbeam_s3 myConfig;

static const HalPinmap_t myPinmap =
{
.nss = HalConfiguration_ttgo_tbeam_s3::PIN_SX1262_NSS,
.rxtx = HalConfiguration_ttgo_tbeam_s3::PIN_SX1262_ANT_SWITCH_RX,
.rst = HalConfiguration_ttgo_tbeam_s3::PIN_SX1262_NRESET,
.dio = {
HalConfiguration_ttgo_tbeam_s3::PIN_SX1262_DIO1,
HalConfiguration_ttgo_tbeam_s3::PIN_SX1262_DIO2,
HalConfiguration_ttgo_tbeam_s3::PIN_SX1262_DIO3,
},
.rxtx_rx_active = 0,
.rssi_cal = 8,
.spi_freq = 8000000, /* 8MHz */
.pConfig = &myConfig
};

const HalPinmap_t* GetPinmap_ttgo_tbeam_s3(void) {
return &myPinmap;
}

}; // namespace Arduino_LMIC

#endif // defined(ARDUINO_TTGO_T_BEAM_S3)
66 changes: 66 additions & 0 deletions src/hal/hal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ static void hal_io_init () {
// NSS and DIO0 are required, DIO1 is required for LoRa, DIO2 for FSK
ASSERT(plmic_pins->nss != LMIC_UNUSED_PIN);
ASSERT(plmic_pins->dio[0] != LMIC_UNUSED_PIN);
// SX126x family can operate with a single DIO
#if (defined(CFG_sx1276_radio) || defined(CFG_sx1272_radio))
ASSERT(plmic_pins->dio[1] != LMIC_UNUSED_PIN || plmic_pins->dio[2] != LMIC_UNUSED_PIN);
#endif

// Serial.print("nss: "); Serial.println(plmic_pins->nss);
// Serial.print("rst: "); Serial.println(plmic_pins->rst);
Expand All @@ -55,6 +58,10 @@ static void hal_io_init () {
pinMode(plmic_pins->rst, INPUT);
}

if (pHalConfig->queryBusyPin() != LMIC_UNUSED_PIN) {
pinMode(pHalConfig->queryBusyPin(), INPUT);
}

hal_interrupt_init();
}

Expand Down Expand Up @@ -184,6 +191,13 @@ static void hal_spi_init () {
SPI.begin();
}

#if (defined(CFG_sx1261_radio) || defined(CFG_sx1262_radio))
bit_t is_busy() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_busy() should not be a global -- or should be hal_is_radio_busy() if it needs to be global for the driver.

// SX126x uses BUSY pin
return digitalRead(pHalConfig->queryBusyPin()) ? true : false;
}
#endif

static void hal_spi_trx(u1_t cmd, u1_t* buf, size_t len, bit_t is_read) {
uint32_t spi_freq;
u1_t nss = plmic_pins->nss;
Expand All @@ -195,6 +209,11 @@ static void hal_spi_trx(u1_t cmd, u1_t* buf, size_t len, bit_t is_read) {
SPI.beginTransaction(settings);
digitalWrite(nss, 0);

// SX126x modems use BUSY pin. Only interact with SPI when BUSY goes LOW
#if (defined(CFG_sx1261_radio) || defined(CFG_sx1262_radio))
while (is_busy());
#endif

SPI.transfer(cmd);

for (; len > 0; --len, ++buf) {
Expand All @@ -216,6 +235,41 @@ void hal_spi_read(u1_t cmd, u1_t* buf, size_t len) {
hal_spi_trx(cmd, buf, len, 1);
}

// SX126x modems behave slightly differently to SX127x. They will often need to transfer multiple bytes before reading
#if (defined(CFG_sx1261_radio) || defined(CFG_sx1262_radio))
void hal_spi_read_sx126x(u1_t cmd, u1_t* addr, size_t addr_len, u1_t* buf, size_t buf_len) {
uint32_t spi_freq;
u1_t nss = plmic_pins->nss;

if ((spi_freq = plmic_pins->spi_freq) == 0)
spi_freq = LMIC_SPI_FREQ;

SPISettings settings(spi_freq, MSBFIRST, SPI_MODE0);
SPI.beginTransaction(settings);
digitalWrite(nss, 0);

while (is_busy());

SPI.transfer(cmd);

// Transfer address and NOP bits
for (; addr_len > 0; --addr_len, ++addr) {
u1_t addr_byte = *addr;
SPI.transfer(addr_byte);
}

// Read buf_len bytes to buf
for (; buf_len > 0; --buf_len, ++buf) {
u1_t data = 0x00;
data = SPI.transfer(data);
*buf = data;
}

digitalWrite(nss, 1);
SPI.endTransaction();
}
#endif

// -----------------------------------------------------------------------------
// TIME

Expand Down Expand Up @@ -499,6 +553,18 @@ bit_t hal_queryUsingTcxo(void) {
return pHalConfig->queryUsingTcxo();
}

bit_t hal_queryUsingDcdc(void) {
return pHalConfig->queryUsingDcdc();
}

bit_t hal_queryUsingDIO2AsRfSwitch(void) {
return pHalConfig->queryUsingDIO2AsRfSwitch();
}

bit_t hal_queryUsingDIO3AsTCXOSwitch(void) {
return pHalConfig->queryUsingDIO3AsTCXOSwitch();
}

uint8_t hal_getTxPowerPolicy(
u1_t inputPolicy,
s1_t requestedPower,
Expand Down
10 changes: 7 additions & 3 deletions src/lmic/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,15 @@
//#define CFG_sx1276_radio 1

// ensure that a radio is defined.
#if ! (defined(CFG_sx1272_radio) || defined(CFG_sx1276_radio))
#if !(defined(CFG_sx1272_radio) || defined(CFG_sx1276_radio) || defined(CFG_sx1261_radio) || defined(CFG_sx1262_radio))
# warning Target radio not defined, assuming CFG_sx1276_radio
#define CFG_sx1276_radio 1
#elif defined(CFG_sx1272_radio) && defined(CFG_sx1276_radio)
# error You can define at most one of CFG_sx1272_radio and CF_sx1276_radio
#elif defined(CFG_sx1272_radio) && (defined(CFG_sx1276_radio) || defined(CFG_sx1261_radio) || defined(CFG_sx1262_radio))
# error You can define at most one target radio
#elif defined(CFG_sx1276_radio) && (defined(CFG_sx1261_radio) || defined(CFG_sx1262_radio))
# error You can define at most one target radio
#elif defined(CFG_sx1261_radio) && (defined(CFG_sx1261_radio))
# error You can define at most one target radio
#endif

// LMIC requires ticks to be 15.5μs - 100 μs long
Expand Down
23 changes: 22 additions & 1 deletion src/lmic/hal.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,17 @@ void hal_spi_write(u1_t cmd, const u1_t* buf, size_t len);
*/
void hal_spi_read(u1_t cmd, u1_t* buf, size_t len);

/*
* Perform SPI read transaction with SX126x series radio chip
* - write the command byte 'cmd'
* - write the 'addr_len' register address bytes 'addr'
* - write the 'NOP' byte 0x00
* - read 'buf_len' bytes into 'buf'
*/
#if (defined(CFG_sx1261_radio) || defined(CFG_sx1262_radio))
void hal_spi_read_sx126x(u1_t cmd, u1_t* addr, size_t addr_len, u1_t* buf, size_t buf_len);
#endif

/*
* disable all CPU interrupts.
* - might be invoked nested
Expand Down Expand Up @@ -148,9 +159,18 @@ s1_t hal_getRssiCal (void);
*/
ostime_t hal_setModuleActive (bit_t val);

/* find out if we're using Tcxo */
/* find out if we're using Tcxo controlled by a host pin */
bit_t hal_queryUsingTcxo(void);

/* SX126x function: find out if the board is configured for DC-DC regulator control */
bit_t hal_queryUsingDcdc(void);

/* SX126x function: find out if the board is configured to control the RF switch with modem DIO2 */
bit_t hal_queryUsingDIO2AsRfSwitch(void);

/* SX126x function: find out if the board is configured to control a TCXO with modem DIO3 */
bit_t hal_queryUsingDIO3AsTCXOSwitch(void);

/* represent the various radio TX power policy */
enum {
LMICHAL_radio_tx_power_policy_rfo = 0,
Expand All @@ -171,6 +191,7 @@ uint8_t hal_getTxPowerPolicy(

void hal_pollPendingIRQs_helper();
void hal_processPendingIRQs(void);
bit_t is_busy();

/// \brief check for any pending interrupts: stub if interrupts are enabled.
static inline void hal_pollPendingIRQs(void)
Expand Down
Loading