]> git.tnoah.ca Git - lora-radio.git/commitdiff
Added NRF52 support
authorjacob.eva <x00010@disroot.org>
Fri, 19 Jan 2024 10:08:55 +0000 (10:08 +0000)
committerjacob.eva <x00010@disroot.org>
Fri, 19 Jan 2024 10:08:55 +0000 (10:08 +0000)
14 files changed:
Config.h
Device.h
Display.h
LoRa.cpp
LoRa.h
Makefile
Modem.h [new file with mode: 0644]
RNode_Firmware.ino
ROM.h
Utilities.h
flash_cache.c [new file with mode: 0644]
flash_cache.h [new file with mode: 0644]
flash_nrf5x.c [new file with mode: 0644]
flash_nrf5x.h [new file with mode: 0644]

index 07927f4286aee2bc3ff3d724ed43df37f053fd32..78808940fc3213ae6c8ae54c002fc166a12665a8 100644 (file)
--- a/Config.h
+++ b/Config.h
@@ -14,6 +14,7 @@
 // along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 #include "ROM.h"
+#include "Modem.h"
 
 #ifndef CONFIG_H
        #define CONFIG_H
 
        #define PLATFORM_AVR   0x90
   #define PLATFORM_ESP32 0x80
+    #define PLATFORM_NRF52 0x70
 
        #define MCU_1284P 0x91
        #define MCU_2560  0x92
        #define MCU_ESP32 0x81
+       #define MCU_NRF52 0x71
 
        #define BOARD_RNODE         0x31
        #define BOARD_HMBRW         0x32
@@ -39,6 +42,8 @@
        #define BOARD_HELTEC32_V2   0x38
        #define BOARD_RNODE_NG_20   0x40
        #define BOARD_RNODE_NG_21   0x41
+       #define BOARD_GENERIC_NRF52 0x50
+       #define BOARD_RAK4630       0x51
 
        #define MODE_HOST 0x11
        #define MODE_TNC  0x12
@@ -61,7 +66,7 @@
        #define M_FRQ_S 27388122
        #define M_FRQ_R 27388061
        bool console_active = false;
-       bool sx1276_installed = false;
+       bool modem_installed = false;
 
        #if defined(__AVR_ATmega1284P__)
            #define PLATFORM PLATFORM_AVR
@@ -72,6 +77,9 @@
        #elif defined(ESP32)
            #define PLATFORM PLATFORM_ESP32
            #define MCU_VARIANT MCU_ESP32
+    #elif defined(nRF52)
+        #define PLATFORM PLATFORM_NRF52
+        #define MCU_VARIANT MCU_NRF52
        #else
            #error "The firmware cannot be compiled for the selected MCU variant"
        #endif
@@ -90,6 +98,7 @@
     #define HAS_TCXO false
     #define HAS_PMU false
     #define HAS_NP false
+    #define HAS_EEPROM false
 
        #if MCU_VARIANT == MCU_1284P
                const int pin_cs = 4;
 
                #define BOARD_MODEL BOARD_RNODE
 
+        #define HAS_EEPROM true
+
                #define CONFIG_UART_BUFFER_SIZE 6144
                #define CONFIG_QUEUE_SIZE 6144
                #define CONFIG_QUEUE_MAX_LENGTH 200
 
                #define BOARD_MODEL BOARD_HMBRW
 
+        #define HAS_EEPROM true
+
                #define CONFIG_UART_BUFFER_SIZE 768
                #define CONFIG_QUEUE_SIZE 5120
                #define CONFIG_QUEUE_MAX_LENGTH 24
                // firmware, you can manually define model here.
                //
                // #define BOARD_MODEL BOARD_GENERIC_ESP32
+               #define CONFIG_UART_BUFFER_SIZE 6144
+               #define CONFIG_QUEUE_SIZE 6144
+               #define CONFIG_QUEUE_MAX_LENGTH 200
+
+               #define EEPROM_SIZE 1024
+               #define EEPROM_OFFSET EEPROM_SIZE-EEPROM_RESERVED
+
+               #define GPS_BAUD_RATE 9600
+               #define PIN_GPS_TX 12
+               #define PIN_GPS_RX 34
 
                #if BOARD_MODEL == BOARD_GENERIC_ESP32
                        const int pin_cs = 4;
                        const int pin_led_tx = 32;
             #define HAS_BLUETOOTH true
             #define HAS_CONSOLE true
+            #define HAS_EEPROM true
                #elif BOARD_MODEL == BOARD_TBEAM
                        const int pin_cs = 18;
                        const int pin_reset = 23;
             #define HAS_BLUETOOTH true
             #define HAS_CONSOLE true
             #define HAS_SD false
+            #define HAS_EEPROM true
                #elif BOARD_MODEL == BOARD_HUZZAH32
                        const int pin_cs = 4;
                        const int pin_reset = 36;
                                        const int pin_led_tx = 25;
                                #endif
                        #endif
-               #else
-                       #error An unsupported board was selected. Cannot compile RNode firmware.
-               #endif
-
-        bool mw_radio_online = false;
-
-               #define CONFIG_UART_BUFFER_SIZE 6144
-               #define CONFIG_QUEUE_SIZE 6144
-               #define CONFIG_QUEUE_MAX_LENGTH 200
-
-               #define EEPROM_SIZE 1024
-               #define EEPROM_OFFSET EEPROM_SIZE-EEPROM_RESERVED
-
-               #define GPS_BAUD_RATE 9600
-               #define PIN_GPS_TX 12
-               #define PIN_GPS_RX 34
+        #endif
+    #elif PLATFORM == PLATFORM_NRF52
+        #if BOARD_MODEL == BOARD_RAK4630
+            #define HAS_EEPROM false
+            #define HAS_DISPLAY false // set for debugging
+            #define HAS_BLUETOOTH false
+            #define HAS_CONSOLE false
+            #define HAS_PMU false
+            #define HAS_NP false
+            #define HAS_SD false
+            #define HAS_TCXO true
+            #define MODEM SX1262 
+            
+            #define CONFIG_UART_BUFFER_SIZE 6144
+            #define CONFIG_QUEUE_SIZE 6144
+            #define CONFIG_QUEUE_MAX_LENGTH 200
+            #define EEPROM_SIZE 4096
+            #define EEPROM_OFFSET EEPROM_SIZE+0xED000-EEPROM_RESERVED
+
+            // following pins are for the sx1262
+            const int pin_rxen = 37;
+            const int pin_reset = 38;
+            const int pin_cs = 42;
+            const int pin_sclk = 43;
+            const int pin_mosi = 44;
+            const int pin_miso = 45;
+            const int pin_busy = 46;
+            const int pin_dio = 47;
+            const int pin_led_rx = LED_BLUE;
+            const int pin_led_tx = LED_GREEN;
+        #endif
+       #else
+               #error An unsupported board was selected. Cannot compile RNode firmware.
        #endif
 
+    bool mw_radio_online = false;
+
        #if BOARD_MODEL == BOARD_TBEAM
                #define I2C_SDA 21
                #define I2C_SCL 22
                #define PMU_IRQ 35
        #endif
 
+
        #define eeprom_addr(a) (a+EEPROM_OFFSET)
 
+    #if MODEM == SX1276 || MODEM == SX1278
+        SPIClass spiModem(pin_miso, pin_sclk, pin_mosi);
+    #elif MODEM == SX1262
+        SPIClass spiModem(NRF_SPIM2, pin_miso, pin_sclk, pin_mosi);
+    #endif
+
        // MCU independent configuration parameters
        const long serial_baudrate  = 115200;
 
        uint32_t stat_tx                = 0;
 
        #define STATUS_INTERVAL_MS 3
-       #if MCU_VARIANT == MCU_ESP32
+       #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
          #define DCD_SAMPLES 2500
                #define UTIL_UPDATE_INTERVAL_MS 1000
                #define UTIL_UPDATE_INTERVAL (UTIL_UPDATE_INTERVAL_MS/STATUS_INTERVAL_MS)
index f70ea5509f9e8537411e282d6eda6c63136e9e12..5f5cbb4fbcf4e3b9f779a551ec111fe50b2e7421 100644 (file)
--- a/Device.h
+++ b/Device.h
 // along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 #include <Ed25519.h>
+
+#if MCU_VARIANT == MCU_ESP32
 #include "mbedtls/md.h"
 #include "esp_ota_ops.h"
 #include "esp_flash_partitions.h"
 #include "esp_partition.h"
+#endif
+
 
 // Forward declaration from Utilities.h
 void eeprom_update(int mapped_addr, uint8_t byte);
+uint8_t eeprom_read(uint32_t addr);
 void hard_reset(void);
 
 const uint8_t dev_keys [] PROGMEM = {
@@ -86,13 +91,21 @@ void device_save_signature() {
 
 void device_load_signature() {
   for (uint8_t i = 0; i < DEV_SIG_LEN; i++) {
-    dev_sig[i] = EEPROM.read(dev_sig_addr(i));
+    #if HAS_EEPROM
+        dev_sig[i] = EEPROM.read(dev_sig_addr(i));
+    #elif MCU_VARIANT == MCU_NRF52
+        dev_sig[i] = eeprom_read(dev_sig_addr(i));
+    #endif
   }
 }
 
 void device_load_firmware_hash() {
   for (uint8_t i = 0; i < DEV_HASH_LEN; i++) {
-    dev_firmware_hash_target[i] = EEPROM.read(dev_fwhash_addr(i));
+    #if HAS_EEPROM
+        dev_firmware_hash_target[i] = EEPROM.read(dev_fwhash_addr(i));
+    #elif MCU_VARIANT == MCU_NRF52
+        dev_firmware_hash_target[i] = eeprom_read(dev_fwhash_addr(i));
+    #endif
   }
 }
 
@@ -103,6 +116,7 @@ void device_save_firmware_hash() {
   if (!fw_signature_validated) hard_reset();
 }
 
+#if MCU_VARIANT == MCU_ESP32
 void device_validate_partitions() {
   device_load_firmware_hash();
   esp_partition_t partition;
@@ -122,11 +136,13 @@ void device_validate_partitions() {
     }
   }
 }
+#endif
 
 bool device_firmware_ok() {
   return fw_signature_validated;
 }
 
+#if MCU_VARIANT == MCU_ESP32
 bool device_init() {
   if (bt_ready) {
     for (uint8_t i=0; i<EEPROM_SIG_LEN; i++){dev_eeprom_signature[i]=EEPROM.read(eeprom_addr(ADDR_SIGNATURE+i));}
@@ -147,4 +163,5 @@ bool device_init() {
   } else {
     return false;
   }
-}
\ No newline at end of file
+}
+#endif
index 7fb1c2b45c50b7a897764239836adfb13837120a..47723b6a7377b5ffab9f3242ed46f6eee00bd0f9 100644 (file)
--- a/Display.h
+++ b/Display.h
@@ -452,7 +452,7 @@ void draw_disp_area() {
         if (!device_firmware_ok()) {
           disp_area.drawBitmap(0, 37, bm_fw_corrupt, disp_area.width(), 27, SSD1306_WHITE, SSD1306_BLACK);
         } else {
-          if (!sx1276_installed) {
+          if (!modem_installed) {
             disp_area.drawBitmap(0, 37, bm_no_radio, disp_area.width(), 27, SSD1306_WHITE, SSD1306_BLACK);
           } else {
             disp_area.drawBitmap(0, 37, bm_hwfail, disp_area.width(), 27, SSD1306_WHITE, SSD1306_BLACK);
index 99987f0274c63295d821230498082f54eedd3bfb..72e9818b048e7f181abc16c17d69c82de3c09b17 100644 (file)
--- a/LoRa.cpp
+++ b/LoRa.cpp
@@ -5,10 +5,12 @@
 // Obviously still under the MIT license.
 
 #include "LoRa.h"
+#include "Modem.h"
 
 #define MCU_1284P 0x91
 #define MCU_2560  0x92
 #define MCU_ESP32 0x81
+#define MCU_NRF52 0x71
 #if defined(__AVR_ATmega1284P__)
   #define PLATFORM PLATFORM_AVR
   #define MCU_VARIANT MCU_1284P
@@ -18,6 +20,9 @@
 #elif defined(ESP32)
   #define PLATFORM PLATFORM_ESP32
   #define MCU_VARIANT MCU_ESP32
+#elif defined(nRF52)
+  #define PLATFORM PLATFORM_NRF52
+  #define MCU_VARIANT MCU_NRF52
 #endif
 
 #ifndef MCU_VARIANT
   #define ISR_VECT
 #endif
 
-// Registers
-#define REG_FIFO                 0x00
-#define REG_OP_MODE              0x01
-#define REG_FRF_MSB              0x06
-#define REG_FRF_MID              0x07
-#define REG_FRF_LSB              0x08
-#define REG_PA_CONFIG            0x09
-#define REG_OCP                  0x0b
-#define REG_LNA                  0x0c
-#define REG_FIFO_ADDR_PTR        0x0d
-#define REG_FIFO_TX_BASE_ADDR    0x0e
-#define REG_FIFO_RX_BASE_ADDR    0x0f
-#define REG_FIFO_RX_CURRENT_ADDR 0x10
-#define REG_IRQ_FLAGS            0x12
-#define REG_RX_NB_BYTES          0x13
-#define REG_MODEM_STAT           0x18
-#define REG_PKT_SNR_VALUE        0x19
-#define REG_PKT_RSSI_VALUE       0x1a
-#define REG_RSSI_VALUE           0x1b
-#define REG_MODEM_CONFIG_1       0x1d
-#define REG_MODEM_CONFIG_2       0x1e
-#define REG_PREAMBLE_MSB         0x20
-#define REG_PREAMBLE_LSB         0x21
-#define REG_PAYLOAD_LENGTH       0x22
-#define REG_MODEM_CONFIG_3       0x26
-#define REG_FREQ_ERROR_MSB       0x28
-#define REG_FREQ_ERROR_MID       0x29
-#define REG_FREQ_ERROR_LSB       0x2a
-#define REG_RSSI_WIDEBAND        0x2c
-#define REG_DETECTION_OPTIMIZE   0x31
-#define REG_HIGH_BW_OPTIMIZE_1   0x36
-#define REG_DETECTION_THRESHOLD  0x37
-#define REG_SYNC_WORD            0x39
-#define REG_HIGH_BW_OPTIMIZE_2   0x3a
-#define REG_DIO_MAPPING_1        0x40
-#define REG_VERSION              0x42
-#define REG_TCXO                 0x4b
-#define REG_PA_DAC               0x4d
-
-// Modes
-#define MODE_LONG_RANGE_MODE     0x80
-#define MODE_SLEEP               0x00
-#define MODE_STDBY               0x01
-#define MODE_TX                  0x03
-#define MODE_RX_CONTINUOUS       0x05
-#define MODE_RX_SINGLE           0x06
-
-// PA config
-#define PA_BOOST                 0x80
-
-// IRQ masks
-#define IRQ_TX_DONE_MASK           0x08
-#define IRQ_PAYLOAD_CRC_ERROR_MASK 0x20
-#define IRQ_RX_DONE_MASK           0x40
+#if MODEM == SX1262
+    #define OP_FIFO_WRITE            0x0E
+    #define OP_FIFO_READ             0x1E
+    #define OP_RF_FREQ               0x86
+    #define OP_SLEEP                 0x84
+    #define OP_STANDBY               0x80
+    #define OP_TX                    0x83
+    #define OP_RX                    0x82
+    #define OP_PA_CONFIG             0x95
+    #define OP_SET_IRQ_FLAGS         0x08 // also provides info such as
+                                          // preamble detection, etc for
+                                          // knowing when it's safe to switch
+                                          // antenna modes
+    #define OP_CLEAR_IRQ_STATUS      0x02
+    #define OP_GET_IRQ_STATUS        0x12
+    #define OP_RX_BUFFER_STATUS      0x13
+    #define OP_PACKET_STATUS         0x14 // get snr & rssi of last packet
+    #define OP_CURRENT_RSSI          0x15
+    #define OP_MODULATION_PARAMS     0x8B // bw, sf, cr, etc.
+    #define OP_PACKET_PARAMS         0x8C // crc, preamble, payload length, etc.
+    #define OP_STATUS                0xC0
+    #define OP_TX_PARAMS             0x8E // set dbm, etc
+    #define OP_PACKET_TYPE           0x8A
+    #define OP_BUFFER_BASE_ADDR      0x8F
+    #define OP_READ_REGISTER         0x1D
+    #define OP_WRITE_REGISTER        0x0D
+    #define OP_DIO3_TCXO_CTRL        0x97
+    #define OP_DIO2_RF_CTRL          0x9D
+    #define OP_CALIBRATE             0x89
+
+    #define REG_OCP                0x08E7
+    #define REG_LNA                0x08AC // no agc in sx1262
+    #define REG_SYNC_WORD_MSB      0x0740
+    #define REG_SYNC_WORD_LSB      0x0741
+    #define REG_PAYLOAD_LENGTH     0x0702 // https://github.com/beegee-tokyo/SX126x-Arduino/blob/master/src/radio/sx126x/sx126x.h#L98
+    #define REG_RANDOM_GEN         0x0819
+
+    #define MODE_LONG_RANGE_MODE     0x01
+
+    #define MODE_TCXO_3_3V           0x07
+
+    #define IRQ_TX_DONE_MASK         0x01
+    #define IRQ_RX_DONE_MASK         0x02
+    #define IRQ_PREAMBLE_DET_MASK    0x04
+    #define IRQ_HEADER_DET_MASK      0x10
+    #define IRQ_PAYLOAD_CRC_ERROR_MASK 0x40
+
+    #define XTAL_FREQ (double)32000000
+    #define FREQ_DIV (double)pow(2.0, 25.0)
+    #define FREQ_STEP (double)(XTAL_FREQ / FREQ_DIV)
+    int fifo_tx_addr_ptr = 0;
+    int fifo_rx_addr_ptr = 0;
+    uint8_t packet[256] = {0};
+
+
+#elif MODEM == SX1276 || MODEM == SX1278
+    // Registers
+    #define REG_FIFO                 0x00
+    #define REG_OP_MODE              0x01
+    #define REG_FRF_MSB              0x06
+    #define REG_FRF_MID              0x07
+    #define REG_FRF_LSB              0x08
+    #define REG_PA_CONFIG            0x09
+    #define REG_OCP                  0x0b
+    #define REG_LNA                  0x0c
+    #define REG_FIFO_ADDR_PTR        0x0d
+    #define REG_FIFO_TX_BASE_ADDR    0x0e
+    #define REG_FIFO_RX_BASE_ADDR    0x0f
+    #define REG_FIFO_RX_CURRENT_ADDR 0x10
+    #define REG_IRQ_FLAGS            0x12
+    #define REG_RX_NB_BYTES          0x13
+    #define REG_MODEM_STAT           0x18
+    #define REG_PKT_SNR_VALUE        0x19
+    #define REG_PKT_RSSI_VALUE       0x1a
+    #define REG_RSSI_VALUE           0x1b
+    #define REG_MODEM_CONFIG_1       0x1d
+    #define REG_MODEM_CONFIG_2       0x1e
+    #define REG_PREAMBLE_MSB         0x20
+    #define REG_PREAMBLE_LSB         0x21
+    #define REG_PAYLOAD_LENGTH       0x22
+    #define REG_MODEM_CONFIG_3       0x26
+    #define REG_FREQ_ERROR_MSB       0x28
+    #define REG_FREQ_ERROR_MID       0x29
+    #define REG_FREQ_ERROR_LSB       0x2a
+    #define REG_RSSI_WIDEBAND        0x2c
+    #define REG_DETECTION_OPTIMIZE   0x31
+    #define REG_HIGH_BW_OPTIMIZE_1   0x36
+    #define REG_DETECTION_THRESHOLD  0x37
+    #define REG_SYNC_WORD            0x39
+    #define REG_HIGH_BW_OPTIMIZE_2   0x3a
+    #define REG_DIO_MAPPING_1        0x40
+    #define REG_VERSION              0x42
+    #define REG_TCXO                 0x4b
+    #define REG_PA_DAC               0x4d
+    
+    // Modes
+    #define MODE_LONG_RANGE_MODE     0x80
+    #define MODE_SLEEP               0x00
+    #define MODE_STDBY               0x01
+    #define MODE_TX                  0x03
+    #define MODE_RX_CONTINUOUS       0x05
+    #define MODE_RX_SINGLE           0x06
+    
+    // PA config
+    #define PA_BOOST                 0x80
+    
+    // IRQ masks
+    #define IRQ_TX_DONE_MASK           0x08
+    #define IRQ_PAYLOAD_CRC_ERROR_MASK 0x20
+    #define IRQ_RX_DONE_MASK           0x40
+#endif
 
 #define MAX_PKT_LENGTH           255
 
+extern SPIClass spiModem;
+
 bool lora_preinit_done = false;
 
 LoRaClass::LoRaClass() :
   _spiSettings(8E6, MSBFIRST, SPI_MODE0),
-  _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN),
+  _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), _rxen(LORA_DEFAULT_RXEN_PIN), _busy(LORA_DEFAULT_BUSY_PIN),
   _frequency(0),
+  _txp(0),
+  _sf(0x07),
+  _bw(0x04),
+  _cr(0x01),
+  _ldro(0x00),
   _packetIndex(0),
+  _preambleLength(18),
   _implicitHeaderMode(0),
+  _payloadLength(255),
+  _crcMode(1),
   _onReceive(NULL)
 {
   // overide Stream timeout value
@@ -108,26 +180,258 @@ bool LoRaClass::preInit() {
   // set SS high
   digitalWrite(_ss, HIGH);
   
-  SPI.begin();
+  spiModem.begin();
 
   // check version (retry for up to 2 seconds)
-  uint8_t version;
-  long start = millis();
-  while (((millis() - start) < 2000) && (millis() >= start)) {
-    version = readRegister(REG_VERSION);
-    if (version == 0x12) {
-      break;
+  #if MODEM == SX1276 || MODEM == SX1278
+    uint8_t version;
+    long start = millis();
+    while (((millis() - start) < 2000) && (millis() >= start)) {
+      version = readRegister(REG_VERSION);
+      if (version == 0x12) {
+        break;
+      }
+      delay(100);
+    }
+    if (version != 0x12) {
+      return false;
+    }
+  
+    lora_preinit_done = true;
+    return true;
+  #elif MODEM == SX1262
+    long start = millis();
+    uint8_t syncmsb;
+    uint8_t synclsb;
+    while (((millis() - start) < 2000) && (millis() >= start)) {
+      syncmsb = readRegister(REG_SYNC_WORD_MSB);
+      synclsb = readRegister(REG_SYNC_WORD_LSB);
+      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;
     }
-    delay(100);
-  }
-  if (version != 0x12) {
-    return false;
-  }
 
-  lora_preinit_done = true;
-  return true;
+    lora_preinit_done = true;
+    return true;
+  #else
+    return false;
+  #endif
 }
 
+#if MODEM == SX1276 || MODEM == SX1278
+    uint8_t ISR_VECT LoRaClass::readRegister(uint8_t address)
+    {
+      return singleTransfer(address & 0x7f, 0x00);
+    }
+    void LoRaClass::writeRegister(uint8_t address, uint8_t value)
+    {
+      singleTransfer(address | 0x80, value);
+    }
+
+    uint8_t ISR_VECT LoRaClass::singleTransfer(uint8_t address, uint8_t value)
+    {
+      uint8_t response;
+
+      digitalWrite(_ss, LOW);
+
+      spiModem.beginTransaction(_spiSettings);
+      spiModem.transfer(address);
+      response = spiModem.transfer(value);
+      spiModem.endTransaction();
+
+      digitalWrite(_ss, HIGH);
+
+      return response;
+    }
+#elif MODEM == SX1262
+    uint8_t ISR_VECT LoRaClass::readRegister(uint16_t address)
+    {
+      return singleTransfer(OP_READ_REGISTER, address, 0x00);
+    }
+
+    void LoRaClass::writeRegister(uint16_t address, uint8_t value)
+    {
+        singleTransfer(OP_WRITE_REGISTER, address, value);
+    }
+
+    uint8_t ISR_VECT LoRaClass::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) {
+            spiModem.transfer(0x00);
+        }
+        response = spiModem.transfer(value);
+        spiModem.endTransaction();
+
+        digitalWrite(_ss, HIGH);
+
+        return response;
+    }
+
+    void LoRaClass::enableAntenna()
+    {
+        uint8_t byte = 0x01;
+        // enable dio2 rf switch
+        executeOpcode(OP_DIO2_RF_CTRL, &byte, 1); 
+        digitalWrite(_rxen, HIGH);
+    }
+
+    void LoRaClass::disableAntenna()
+    {
+        digitalWrite(_rxen, LOW);
+    }
+
+    void LoRaClass::loraMode() {
+        // enable lora mode on the SX1262 chip
+        uint8_t mode = MODE_LONG_RANGE_MODE;
+        executeOpcode(OP_PACKET_TYPE, &mode, 1);
+    }
+
+    void LoRaClass::waitOnBusy() {
+        if (_busy != -1) {
+            while (digitalRead(_busy) == HIGH)
+            {
+                // do nothing
+            }
+        }
+    }
+
+    void LoRaClass::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]);
+        }
+
+        spiModem.endTransaction();
+
+        digitalWrite(_ss, HIGH);
+    }
+
+    void LoRaClass::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size)
+    {
+        waitOnBusy();
+
+        digitalWrite(_ss, LOW);
+
+        spiModem.beginTransaction(_spiSettings);
+        spiModem.transfer(opcode);
+        spiModem.transfer(0x00);
+
+        for (int i = 0; i < size; i++)
+        {
+            buffer[i] = spiModem.transfer(0x00);
+        }
+
+        spiModem.endTransaction();
+
+        digitalWrite(_ss, HIGH);
+    }
+
+    void LoRaClass::writeBuffer(const uint8_t* buffer, size_t size)
+    {
+        waitOnBusy();
+
+        digitalWrite(_ss, LOW);
+
+        spiModem.beginTransaction(_spiSettings);
+        spiModem.transfer(OP_FIFO_WRITE);
+        spiModem.transfer(fifo_tx_addr_ptr);
+
+        for (int i = 0; i < size; i++)
+        {
+            spiModem.transfer(buffer[i]);
+            fifo_tx_addr_ptr++;
+        }
+
+        spiModem.endTransaction();
+
+        digitalWrite(_ss, HIGH);
+    }
+
+    void LoRaClass::readBuffer(uint8_t* buffer, size_t size)
+    {
+        waitOnBusy();
+
+        digitalWrite(_ss, LOW);
+
+        spiModem.beginTransaction(_spiSettings);
+        spiModem.transfer(OP_FIFO_READ);
+        spiModem.transfer(fifo_rx_addr_ptr);
+        spiModem.transfer(0x00);
+
+        for (int i = 0; i < size; i++)
+        {
+            buffer[i] = spiModem.transfer(0x00);
+        }
+
+        spiModem.endTransaction();
+
+        digitalWrite(_ss, HIGH);
+    }
+
+    void LoRaClass::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, buf, 8);
+    }
+
+    void LoRaClass::setPacketParams(long preamble, uint8_t headermode, uint8_t length, uint8_t crc) {
+      // because there is no access to these registers on the sx1262, we have
+      // to set all these parameters at once or not at all.
+      uint8_t buf[9];
+
+      buf[0] = uint8_t((preamble & 0xFF00) >> 8);
+      buf[1] = uint8_t((preamble & 0x00FF));
+      buf[2] = headermode;
+      buf[3] = length;
+      buf[4] = crc;
+      // standard IQ setting (no inversion)
+      buf[5] = 0x00; 
+      // unused params
+      buf[6] = 0x00; 
+      buf[7] = 0x00; 
+      buf[8] = 0x00; 
+    
+      executeOpcode(OP_PACKET_PARAMS, buf, 9);
+    }
+#endif
+
+
 int LoRaClass::begin(long frequency)
 {
   if (_reset != -1) {
@@ -140,33 +444,69 @@ int LoRaClass::begin(long frequency)
     delay(10);
   }
 
+  if (_busy != -1) {
+      pinMode(_busy, INPUT);
+  }
+
   if (!lora_preinit_done) {
     if (!preInit()) {
       return false;
     }
   }
 
-  // put in sleep mode
-  sleep();
+  #if MODEM == SX1276 || MODEM == SX1278
+      // put in sleep mode
+      sleep();
 
-  // set frequency
-  setFrequency(frequency);
+      // set frequency
+      setFrequency(frequency);
 
-  // set base addresses
-  writeRegister(REG_FIFO_TX_BASE_ADDR, 0);
-  writeRegister(REG_FIFO_RX_BASE_ADDR, 0);
+      // set base addresses
+      writeRegister(REG_FIFO_TX_BASE_ADDR, 0);
+      writeRegister(REG_FIFO_RX_BASE_ADDR, 0);
 
-  // set LNA boost
-  writeRegister(REG_LNA, readRegister(REG_LNA) | 0x03);
+      // set LNA boost
+      writeRegister(REG_LNA, readRegister(REG_LNA) | 0x03);
 
-  // set auto AGC
-  writeRegister(REG_MODEM_CONFIG_3, 0x04);
+      // set auto AGC
+      writeRegister(REG_MODEM_CONFIG_3, 0x04);
 
-  // set output power to 2 dBm
-  setTxPower(2);
+      // set output power to 2 dBm
+      setTxPower(2);
 
-  // put in standby mode
-  idle();
+      // put in standby mode
+      idle();
+  #elif MODEM == SX1262
+      //#if HAS_TCXO
+          // turn TCXO on
+          enableTCXO();
+      //#endif
+      loraMode();
+      idle();
+      // cannot access registers in sleep mode on sx1262, set to idle instead
+      if (_rxen != -1) {
+          pinMode(_rxen, OUTPUT);
+          enableAntenna();
+      }
+      // calibrate RC64k, RC13M, PLL, ADC and image
+      uint8_t calibrate = 0x7F; 
+      executeOpcode(OP_CALIBRATE, &calibrate, 1);
+
+      setFrequency(frequency);
+
+      // set output power to 2 dBm
+      setTxPower(2);
+
+      // set LNA boost
+      writeRegister(REG_LNA, 0x96);
+
+      // set base addresses
+      uint8_t basebuf[2] = {0};
+      executeOpcode(OP_BUFFER_BASE_ADDR, basebuf, 2);
+
+      setModulationParams(_sf, _bw, _cr, _ldro);
+      setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+  #endif
 
   return 1;
 }
@@ -176,8 +516,8 @@ void LoRaClass::end()
   // put in sleep mode
   sleep();
 
-  // stop SPI
-  SPI.end();
+  // stop spiModem
+  spiModem.end();
 
   lora_preinit_done = false;
 }
@@ -193,138 +533,294 @@ int LoRaClass::beginPacket(int implicitHeader)
     explicitHeaderMode();
   }
 
-  // reset FIFO address and paload length
-  writeRegister(REG_FIFO_ADDR_PTR, 0);
-  writeRegister(REG_PAYLOAD_LENGTH, 0);
+  #if MODEM == SX1276 || MODEM == SX1278
+      // reset FIFO address and paload length
+      writeRegister(REG_FIFO_ADDR_PTR, 0);
+      writeRegister(REG_PAYLOAD_LENGTH, 0);
+  #elif MODEM == SX1262
+      _payloadLength = 0;
+      fifo_tx_addr_ptr = 0;
+      setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+  #endif
 
   return 1;
 }
 
 int LoRaClass::endPacket()
 {
-  // put in TX mode
-  writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_TX);
-
-  // wait for TX done
-  while ((readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) == 0) {
-    yield();
-  }
-
-  // clear IRQ's
-  writeRegister(REG_IRQ_FLAGS, IRQ_TX_DONE_MASK);
-
+  #if MODEM == SX1276 || MODEM == SX1278
+      // put in TX mode
+      writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_TX);
+
+      // wait for TX done
+      while ((readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) == 0) {
+        yield();
+      }
+
+      // clear IRQ's
+      writeRegister(REG_IRQ_FLAGS, IRQ_TX_DONE_MASK);
+  #elif MODEM == SX1262
+      setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+      // put in single TX mode
+      uint8_t timeout[3] = {0};
+      executeOpcode(OP_TX, timeout, 3);
+
+      uint8_t buf[2];
+
+      buf[0] = 0x00;
+      buf[1] = 0x00;
+
+      executeOpcodeRead(OP_GET_IRQ_STATUS, buf, 2);
+
+      // wait for TX done
+      while ((buf[1] & IRQ_TX_DONE_MASK) == 0) {
+        buf[0] = 0x00;
+        buf[1] = 0x00;
+        executeOpcodeRead(OP_GET_IRQ_STATUS, buf, 2);
+        yield();
+      }
+
+      // clear IRQ's
+
+      uint8_t mask[2];
+      mask[0] = 0x00;
+      mask[1] = IRQ_TX_DONE_MASK;
+      executeOpcode(OP_CLEAR_IRQ_STATUS, mask, 2);
+  #endif
   return 1;
 }
 
 int LoRaClass::parsePacket(int size)
 {
   int packetLength = 0;
-  int irqFlags = readRegister(REG_IRQ_FLAGS);
+  #if MODEM == SX1276 || MODEM == SX1278
+    int irqFlags = readRegister(REG_IRQ_FLAGS);
+  #elif MODEM == SX1262
+    uint8_t buf[2];
+    buf[0] = 0x00;
+    buf[1] = 0x00;
+
+    executeOpcodeRead(OP_GET_IRQ_STATUS, buf, 2);
+  #endif
 
   if (size > 0) {
     implicitHeaderMode();
 
-    writeRegister(REG_PAYLOAD_LENGTH, size & 0xff);
+    #if MODEM == SX1276 || MODEM == SX1278
+        writeRegister(REG_PAYLOAD_LENGTH, size & 0xff);
+    #elif MODEM == SX1262
+        // tell radio payload length
+        _payloadLength = size;
+        setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+    #endif
   } else {
     explicitHeaderMode();
   }
 
-  // clear IRQ's
-  writeRegister(REG_IRQ_FLAGS, irqFlags);
-
-  if ((irqFlags & IRQ_RX_DONE_MASK) && (irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) {
-    // received a packet
-    _packetIndex = 0;
-
-    // read packet length
-    if (_implicitHeaderMode) {
-      packetLength = readRegister(REG_PAYLOAD_LENGTH);
-    } else {
-      packetLength = readRegister(REG_RX_NB_BYTES);
-    }
-
-    // set FIFO address to current RX address
-    writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR));
+  #if MODEM == SX1276 || MODEM == SX1278
+    // clear IRQ's
+    writeRegister(REG_IRQ_FLAGS, irqFlags);
+    if ((irqFlags & IRQ_RX_DONE_MASK) && (irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) {
+  #elif MODEM == SX1262
+    uint8_t irqBufFlags[2];
+
+    irqBufFlags[0] = buf[0];
+    irqBufFlags[1] = buf[1];
+
+    executeOpcode(OP_CLEAR_IRQ_STATUS, irqBufFlags, 2);
+
+    if ((buf[0] & IRQ_RX_DONE_MASK) && (buf[1] & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) {
+  #endif
+
+        // received a packet
+        _packetIndex = 0;
+
+    #if MODEM == SX1276 || MODEM == SX1278
+        // read packet length
+        if (_implicitHeaderMode) {
+          packetLength = readRegister(REG_PAYLOAD_LENGTH);
+        } else {
+          packetLength = readRegister(REG_RX_NB_BYTES);
+        }
+    #elif MODEM == SX1262
+        buf[0] = 0x00;
+        buf[1] = 0x00;
+        executeOpcodeRead(OP_RX_BUFFER_STATUS, buf, 2);
+        packetLength = buf[0];
+    #endif
+
+    #if MODEM == SX1276 || MODEM == SX1278
+        // set FIFO address to current RX address
+        writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR));
+    #endif
 
     // put in standby mode
     idle();
-  } else if (readRegister(REG_OP_MODE) != (MODE_LONG_RANGE_MODE | MODE_RX_SINGLE)) {
-    // not currently in RX mode
+  
+  #if MODEM == SX1276 || MODEM == SX1278
+      } else if (readRegister(REG_OP_MODE) != (MODE_LONG_RANGE_MODE | MODE_RX_SINGLE)) {
+        // not currently in RX mode
 
-    // reset FIFO address
-    writeRegister(REG_FIFO_ADDR_PTR, 0);
+        // reset FIFO address
+        writeRegister(REG_FIFO_ADDR_PTR, 0);
 
-    // put in single RX mode
-    writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_SINGLE);
+        // put in single RX mode
+        writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_SINGLE);
+      }
+  #elif MODEM == SX1262
+  } else {
+    uint8_t status;
+    status = 0x00;
+    executeOpcodeRead(OP_STATUS, &status, 1); 
+    if ((status >> 4 & 0x7) != 0x5) {
+        // not currently in RX mode
+        
+        // put in single RX mode
+        uint8_t buf[3] = {0};
+        executeOpcode(OP_RX, buf, 3);
+    }
   }
-
+  #endif
   return packetLength;
 }
 
 uint8_t LoRaClass::modemStatus() {
-  return readRegister(REG_MODEM_STAT);
+  #if MODEM == SX1276 || MODEM == SX1278 
+    return readRegister(REG_MODEM_STAT);
+  #elif MODEM == SX1262
+    // imitate the register status from the sx1276 / 78
+    uint8_t buf[2] = {0};
+
+
+    executeOpcodeRead(OP_GET_IRQ_STATUS, buf, 2);
+
+    uint8_t clearbuf[2] = {0};
+
+    uint8_t byte = 0x00;
+
+    if (buf[1] & IRQ_PREAMBLE_DET_MASK != 0) {
+        byte = byte | 0x01 | 0x04;
+        // clear register after reading
+        clearbuf[1] = IRQ_PREAMBLE_DET_MASK; 
+    }
+
+    if (buf[1] & IRQ_HEADER_DET_MASK != 0) {
+        byte = byte | 0x02 | 0x04;
+        // clear register after reading
+        clearbuf[1] = clearbuf[1] | IRQ_HEADER_DET_MASK; 
+    }
+
+    executeOpcode(OP_CLEAR_IRQ_STATUS, clearbuf, 2);
+
+    return byte; 
+  #endif
 }
 
 
 uint8_t LoRaClass::currentRssiRaw() {
-  uint8_t rssi = readRegister(REG_RSSI_VALUE);
-  return rssi;
+  #if MODEM == SX1276 || MODEM == SX1278
+    uint8_t rssi = readRegister(REG_RSSI_VALUE);
+    return rssi;
+  #elif MODEM == SX1262
+    uint8_t byte = 0;
+    executeOpcodeRead(OP_CURRENT_RSSI, &byte, 1);
+    return byte;
+  #endif
 }
 
 int ISR_VECT LoRaClass::currentRssi() {
-  int rssi = (int)readRegister(REG_RSSI_VALUE) - RSSI_OFFSET;
-  if (_frequency < 820E6) rssi -= 7;
-  return rssi;
+  #if MODEM == SX1276 || MODEM == SX1278
+    int rssi = (int)readRegister(REG_RSSI_VALUE) - RSSI_OFFSET;
+    if (_frequency < 820E6) rssi -= 7;
+    return rssi;
+  #elif MODEM == SX1262
+    uint8_t byte = 0;
+    executeOpcodeRead(OP_CURRENT_RSSI, &byte, 1);
+    int rssi = -(int(byte)) / 2;
+    return rssi;
+  #endif
 }
 
 uint8_t LoRaClass::packetRssiRaw() {
-  uint8_t pkt_rssi_value = readRegister(REG_PKT_RSSI_VALUE);
-  return pkt_rssi_value;
+  #if MODEM == SX1276 || MODEM == SX1278
+    uint8_t pkt_rssi_value = readRegister(REG_PKT_RSSI_VALUE);
+    return pkt_rssi_value;
+  #elif MODEM == SX1262
+    uint8_t buf[3] = {0};
+    executeOpcodeRead(OP_PACKET_STATUS, buf, 3);
+    return buf[2];
+  #endif
 }
 
 int ISR_VECT LoRaClass::packetRssi() {
-  int pkt_rssi = (int)readRegister(REG_PKT_RSSI_VALUE) - 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;
+  #if MODEM == SX1276 || MODEM == SX1278
+      int pkt_rssi = (int)readRegister(REG_PKT_RSSI_VALUE) - 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;
+
+  #elif MODEM == SX1262
+    // may need more calculations here
+    uint8_t buf[3] = {0};
+    executeOpcodeRead(OP_PACKET_STATUS, buf, 3);
+    int pkt_rssi = -(int(buf[2])) / 2;
+    return pkt_rssi;
+  #endif
 }
 
 uint8_t ISR_VECT LoRaClass::packetSnrRaw() {
-  return readRegister(REG_PKT_SNR_VALUE);
+  #if MODEM == SX1276 || MODEM == SX1278
+    return readRegister(REG_PKT_SNR_VALUE);
+  #elif MODEM == SX1262
+    uint8_t buf[3] = {0};
+    executeOpcodeRead(OP_PACKET_STATUS, buf, 3);
+    return buf[1];
+  #endif
 }
 
 float ISR_VECT LoRaClass::packetSnr() {
-  return ((int8_t)readRegister(REG_PKT_SNR_VALUE)) * 0.25;
+  #if MODEM == SX1276 || MODEM == SX1278
+    return ((int8_t)readRegister(REG_PKT_SNR_VALUE)) * 0.25;
+  #elif MODEM == SX1262
+    uint8_t buf[3] = {0};
+    executeOpcodeRead(OP_PACKET_STATUS, buf, 3);
+    return float(buf[1]) / 4.0;
+  #endif
 }
 
 long LoRaClass::packetFrequencyError()
 {
   int32_t freqError = 0;
-  freqError = static_cast<int32_t>(readRegister(REG_FREQ_ERROR_MSB) & B111);
-  freqError <<= 8L;
-  freqError += static_cast<int32_t>(readRegister(REG_FREQ_ERROR_MID));
-  freqError <<= 8L;
-  freqError += static_cast<int32_t>(readRegister(REG_FREQ_ERROR_LSB));
-
-  if (readRegister(REG_FREQ_ERROR_MSB) & B1000) { // Sign bit is on
-     freqError -= 524288; // B1000'0000'0000'0000'0000
-  }
+  #if MODEM == SX1276 || MODEM == SX1278
+    freqError = static_cast<int32_t>(readRegister(REG_FREQ_ERROR_MSB) & B111);
+    freqError <<= 8L;
+    freqError += static_cast<int32_t>(readRegister(REG_FREQ_ERROR_MID));
+    freqError <<= 8L;
+    freqError += static_cast<int32_t>(readRegister(REG_FREQ_ERROR_LSB));
+
+    if (readRegister(REG_FREQ_ERROR_MSB) & 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); // p. 37
+    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); // p. 37
 
-  return static_cast<long>(fError);
+    return static_cast<long>(fError);
+  #elif MODEM == SX1262
+    // todo: implement this, no idea how to check it on the sx1262
+    const float fError = 0.0;
+    return static_cast<long>(fError);
+  #endif
 }
 
 size_t LoRaClass::write(uint8_t byte)
@@ -334,27 +830,43 @@ size_t LoRaClass::write(uint8_t byte)
 
 size_t LoRaClass::write(const uint8_t *buffer, size_t size)
 {
-  int currentLength = readRegister(REG_PAYLOAD_LENGTH);
-
-  // check size
-  if ((currentLength + size) > MAX_PKT_LENGTH) {
-    size = MAX_PKT_LENGTH - currentLength;
-  }
+  #if MODEM == SX1276 || MODEM == SX1278
+      int currentLength = readRegister(REG_PAYLOAD_LENGTH);
+
+      // check size
+      if ((currentLength + size) > MAX_PKT_LENGTH) {
+        size = MAX_PKT_LENGTH - currentLength;
+      }
+  #elif MODEM == SX1262
+      if ((_payloadLength + size) > MAX_PKT_LENGTH) {
+          size = MAX_PKT_LENGTH - _payloadLength;
+      }
+  #endif
 
   // write data
-  for (size_t i = 0; i < size; i++) {
-    writeRegister(REG_FIFO, buffer[i]);
-  }
-
-  // update length
-  writeRegister(REG_PAYLOAD_LENGTH, currentLength + size);
+  #if MODEM == SX1276 || MODEM == SX1278
+    for (size_t i = 0; i < size; i++) {
+        writeRegister(REG_FIFO, buffer[i]);
+    }
 
+    // update length
+    writeRegister(REG_PAYLOAD_LENGTH, currentLength + size);
+  #elif MODEM == SX1262
+    writeBuffer(buffer, size);
+    _payloadLength = _payloadLength + size;
+  #endif
   return size;
 }
 
 int ISR_VECT LoRaClass::available()
 {
-  return (readRegister(REG_RX_NB_BYTES) - _packetIndex);
+  #if MODEM == SX1276 || MODEM == SX1278
+    return (readRegister(REG_RX_NB_BYTES) - _packetIndex);
+  #elif MODEM == SX1262
+    uint8_t buf[2] = {0};
+    executeOpcodeRead(OP_RX_BUFFER_STATUS, buf, 2);
+    return buf[0] - _packetIndex;
+  #endif
 }
 
 int ISR_VECT LoRaClass::read()
@@ -363,9 +875,25 @@ int ISR_VECT LoRaClass::read()
     return -1;
   }
 
-  _packetIndex++;
+  #if MODEM == SX1276 || MODEM == SX1278
+    _packetIndex++;
+    return readRegister(REG_FIFO);
+  #elif MODEM == SX1262
+    // if received new packet
+    if (_packetIndex == 0) {
+        uint8_t rxbuf[2] = {0};
+        executeOpcodeRead(OP_RX_BUFFER_STATUS, rxbuf, 2);
+        int size = rxbuf[0];
+        fifo_rx_addr_ptr = rxbuf[1];
+
+        readBuffer(packet, size);
+    }
+
+    uint8_t byte = packet[_packetIndex];
+    _packetIndex++;
+    return byte;
+  #endif
 
-  return readRegister(REG_FIFO);
 }
 
 int LoRaClass::peek()
@@ -374,15 +902,29 @@ int LoRaClass::peek()
     return -1;
   }
 
-  // store current FIFO address
-  int currentAddress = readRegister(REG_FIFO_ADDR_PTR);
+  #if MODEM == SX1276 || MODEM == SX1278
+      // store current FIFO address
+      int currentAddress = readRegister(REG_FIFO_ADDR_PTR);
+
+      // read
+      uint8_t b = readRegister(REG_FIFO);
 
-  // read
-  uint8_t b = readRegister(REG_FIFO);
+      // restore FIFO address
+      writeRegister(REG_FIFO_ADDR_PTR, currentAddress);
 
-  // restore FIFO address
-  writeRegister(REG_FIFO_ADDR_PTR, currentAddress);
+  #elif MODEM == SX1262
+    // if received new packet
+    if (_packetIndex == 0) {
+        uint8_t rxbuf[2] = {0};
+        executeOpcodeRead(OP_RX_BUFFER_STATUS, rxbuf, 2);
+        int size = rxbuf[0];
+        fifo_rx_addr_ptr = rxbuf[1];
 
+        readBuffer(packet, size);
+    }
+
+    uint8_t b = packet[_packetIndex];
+  #endif
   return b;
 }
 
@@ -397,15 +939,38 @@ void LoRaClass::onReceive(void(*callback)(int))
   if (callback) {
     pinMode(_dio0, INPUT);
 
-    writeRegister(REG_DIO_MAPPING_1, 0x00);
+    #if MODEM == SX1276 || MODEM == SX1278
+      writeRegister(REG_DIO_MAPPING_1, 0x00);
+    #elif MODEM == SX1262
+      // 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; 
+
+      // set dio1 masks
+      buf[4] = 0x00; 
+      buf[5] = 0x00;
+
+      // set dio2 masks
+      buf[6] = 0x00; 
+      buf[7] = 0x00;
+
+      executeOpcode(OP_SET_IRQ_FLAGS, buf, 8);
+    #endif
 #ifdef SPI_HAS_NOTUSINGINTERRUPT
-    SPI.usingInterrupt(digitalPinToInterrupt(_dio0));
+    spiModem.usingInterrupt(digitalPinToInterrupt(_dio0));
 #endif
     attachInterrupt(digitalPinToInterrupt(_dio0), LoRaClass::onDio0Rise, RISING);
   } else {
     detachInterrupt(digitalPinToInterrupt(_dio0));
 #ifdef SPI_HAS_NOTUSINGINTERRUPT
-    SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0));
+    spiModem.notUsingInterrupt(digitalPinToInterrupt(_dio0));
 #endif
   }
 }
@@ -415,84 +980,182 @@ void LoRaClass::receive(int size)
   if (size > 0) {
     implicitHeaderMode();
 
-    writeRegister(REG_PAYLOAD_LENGTH, size & 0xff);
+    #if MODEM == SX1276 || MODEM == SX1278
+        writeRegister(REG_PAYLOAD_LENGTH, size & 0xff);
+    #elif MODEM == SX1262
+        // tell radio payload length
+        _payloadLength = size;
+        setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+    #endif
   } else {
     explicitHeaderMode();
   }
 
-  writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_CONTINUOUS);
+  #if MODEM == SX1276 || MODEM == SX1278
+    writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_CONTINUOUS);
+  #elif MODEM == SX1262
+    uint8_t mode[3] = {0xFF, 0xFF, 0xFF}; // continuous mode
+    executeOpcode(OP_RX, mode, 3);
+  #endif
 }
 
 void LoRaClass::idle()
 {
-  writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_STDBY);
+  #if MODEM == SX1276 || MODEM == SX1278
+      writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_STDBY);
+  #elif MODEM == SX1262
+      //#if HAS_TCXO
+          // STDBY_XOSC
+          uint8_t byte = 0x01;
+      //#else
+      //    // STDBY_RC
+      //    uint8_t byte = 0x00;
+      //#endif
+      executeOpcode(OP_STANDBY, &byte, 1); 
+  #endif
 }
 
 void LoRaClass::sleep()
 {
-  writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_SLEEP);
+  #if MODEM == SX1276 || MODEM == SX1278
+    writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_SLEEP);
+  #elif MODEM == SX1262
+    if (_rxen != -1) {
+       disableAntenna();
+     }
+     uint8_t byte = 0x00;
+     executeOpcode(OP_SLEEP, &byte, 1);
+  #endif
 }
 
 void LoRaClass::enableTCXO() {
-  uint8_t tcxo_reg = readRegister(REG_TCXO);
-  writeRegister(REG_TCXO, tcxo_reg | 0x10);
+  #if MODEM == SX1276 || MODEM == SX1278
+      uint8_t tcxo_reg = readRegister(REG_TCXO);
+      writeRegister(REG_TCXO, tcxo_reg | 0x10);
+  #elif MODEM == SX1262
+    // only tested for RAK4630, voltage may be different on other platforms
+    uint8_t buf[4] = {MODE_TCXO_3_3V, 0x00, 0x00, 0xFF};
+    executeOpcode(OP_DIO3_TCXO_CTRL, buf, 4);
+  #endif
 }
 
 void LoRaClass::disableTCXO() {
-  uint8_t tcxo_reg = readRegister(REG_TCXO);
-  writeRegister(REG_TCXO, tcxo_reg & 0xEF);
+  #if MODEM == SX1276 || MODEM == SX1278
+      uint8_t tcxo_reg = readRegister(REG_TCXO);
+      writeRegister(REG_TCXO, tcxo_reg & 0xEF);
+  #elif MODEM == SX1262
+      // currently cannot disable on SX1262?
+  #endif
 }
 
 void LoRaClass::setTxPower(int level, int outputPin) {
-  if (PA_OUTPUT_RFO_PIN == outputPin) {
-    // RFO
-    if (level < 0) {
-      level = 0;
-    } else if (level > 14) {
-      level = 14;
+  #if MODEM == SX1276 || MODEM == SX1278
+      if (PA_OUTPUT_RFO_PIN == outputPin) {
+        // RFO
+        if (level < 0) {
+          level = 0;
+        } else if (level > 14) {
+          level = 14;
+        }
+
+        writeRegister(REG_PA_DAC, 0x84);
+        writeRegister(REG_PA_CONFIG, 0x70 | level);
+
+      } else {
+        // PA BOOST
+        if (level < 2) {
+          level = 2;
+        } else if (level > 17) {
+          level = 17;
+        }
+
+        writeRegister(REG_PA_DAC, 0x84);
+        writeRegister(REG_PA_CONFIG, PA_BOOST | (level - 2));
+      }
+  #elif MODEM == SX1262
+    // 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;
+    pa_buf[1] = 0x07;
+    pa_buf[2] = 0x00;
+    pa_buf[3] = 0x01;
+
+    executeOpcode(OP_PA_CONFIG, pa_buf, 4); // set pa_config for high power
+
+    if (level > 22) {
+        level = 22;
+    }
+    else if (level < -9) {
+        level = -9;
     }
 
-    writeRegister(REG_PA_DAC, 0x84);
-    writeRegister(REG_PA_CONFIG, 0x70 | level);
+    writeRegister(REG_OCP, 0x38); // 160mA limit, overcurrent protection
 
-  } else {
-    // PA BOOST
-    if (level < 2) {
-      level = 2;
-    } else if (level > 17) {
-      level = 17;
-    }
+    uint8_t tx_buf[2];
 
-    writeRegister(REG_PA_DAC, 0x84);
-    writeRegister(REG_PA_CONFIG, PA_BOOST | (level - 2));
-  }
+    tx_buf[0] = level;
+    tx_buf[1] = 0x02; // ramping time - 40 microseconds
+    
+    executeOpcode(OP_TX_PARAMS, tx_buf, 2);
+
+    _txp = level;
+  #endif
 }
 
 uint8_t LoRaClass::getTxPower() {
-  byte txp = readRegister(REG_PA_CONFIG);
-  return txp;
+  #if MODEM == SX1276 || MODEM == SX1278
+      byte txp = readRegister(REG_PA_CONFIG);
+      return txp;
+  #elif MODEM == SX1262
+      return _txp;
+  #endif
 }
 
 void LoRaClass::setFrequency(long frequency) {
   _frequency = frequency;
 
-  uint32_t frf = ((uint64_t)frequency << 19) / 32000000;
+  #if MODEM == SX1276 || MODEM == SX1278
+      uint32_t frf = ((uint64_t)frequency << 19) / 32000000;
 
-  writeRegister(REG_FRF_MSB, (uint8_t)(frf >> 16));
-  writeRegister(REG_FRF_MID, (uint8_t)(frf >> 8));
-  writeRegister(REG_FRF_LSB, (uint8_t)(frf >> 0));
+      writeRegister(REG_FRF_MSB, (uint8_t)(frf >> 16));
+      writeRegister(REG_FRF_MID, (uint8_t)(frf >> 8));
+      writeRegister(REG_FRF_LSB, (uint8_t)(frf >> 0));
 
-  optimizeModemSensitivity();
+      optimizeModemSensitivity();
+  #elif MODEM == SX1262
+      uint8_t buf[4];
+
+      uint32_t freq = (uint32_t)((double)frequency / (double)FREQ_STEP);
+
+      buf[0] = ((freq >> 24) & 0xFF);
+      buf[1] = ((freq >> 16) & 0xFF);
+      buf[2] = ((freq >> 8) & 0xFF);
+      buf[3] = (freq & 0xFF);
+      executeOpcode(OP_RF_FREQ, buf, 4);
+  #endif
 }
 
 uint32_t LoRaClass::getFrequency() {
-  uint8_t msb = readRegister(REG_FRF_MSB);
-  uint8_t mid = readRegister(REG_FRF_MID);
-  uint8_t lsb = readRegister(REG_FRF_LSB);
+  #if MODEM == SX1276 || MODEM == SX1278
+      uint8_t msb = readRegister(REG_FRF_MSB);
+      uint8_t mid = readRegister(REG_FRF_MID);
+      uint8_t lsb = readRegister(REG_FRF_LSB);
 
-  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);
+      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);
+
+  #elif MODEM == SX1262
+      // we can't read the frequency on the sx1262
+      uint32_t frequency = _frequency;
+  #endif
 
   return frequency;
 }
@@ -505,93 +1168,149 @@ void LoRaClass::setSpreadingFactor(int sf)
     sf = 12;
   }
 
-  if (sf == 6) {
-    writeRegister(REG_DETECTION_OPTIMIZE, 0xc5);
-    writeRegister(REG_DETECTION_THRESHOLD, 0x0c);
-  } else {
-    writeRegister(REG_DETECTION_OPTIMIZE, 0xc3);
-    writeRegister(REG_DETECTION_THRESHOLD, 0x0a);
-  }
-
-  writeRegister(REG_MODEM_CONFIG_2, (readRegister(REG_MODEM_CONFIG_2) & 0x0f) | ((sf << 4) & 0xf0));
-
+  #if MODEM == SX1276 || MODEM == SX1278
+      if (sf == 6) {
+        writeRegister(REG_DETECTION_OPTIMIZE, 0xc5);
+        writeRegister(REG_DETECTION_THRESHOLD, 0x0c);
+      } else {
+        writeRegister(REG_DETECTION_OPTIMIZE, 0xc3);
+        writeRegister(REG_DETECTION_THRESHOLD, 0x0a);
+      }
+
+      writeRegister(REG_MODEM_CONFIG_2, (readRegister(REG_MODEM_CONFIG_2) & 0x0f) | ((sf << 4) & 0xf0));
+  #elif MODEM == SX1262
+      setModulationParams(sf, _bw, _cr, _ldro);
+  #endif
   handleLowDataRate();
 }
 
 long LoRaClass::getSignalBandwidth()
 {
-  byte bw = (readRegister(REG_MODEM_CONFIG_1) >> 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;
-  }
+  #if MODEM == SX1276 || MODEM == SX1278
+      byte bw = (readRegister(REG_MODEM_CONFIG_1) >> 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;
+      }
+  #elif MODEM == SX1262
+      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;
+      }
+  #endif
   
   return 0;
 }
 
 void LoRaClass::handleLowDataRate(){
-  int sf = (readRegister(REG_MODEM_CONFIG_2) >> 4);
-  if ( long( (1<<sf) / (getSignalBandwidth()/1000)) > 16) {
-    // set auto AGC and LowDataRateOptimize
-    writeRegister(REG_MODEM_CONFIG_3, (1<<3)|(1<<2));
-  }
-  else {
-    // set auto AGC
-    writeRegister(REG_MODEM_CONFIG_3, (1<<2));
-  }
+  #if MODEM == SX1276 || MODEM == SX1278
+      int sf = (readRegister(REG_MODEM_CONFIG_2) >> 4);
+      if ( long( (1<<sf) / (getSignalBandwidth()/1000)) > 16) {
+        // set auto AGC and LowDataRateOptimize
+        writeRegister(REG_MODEM_CONFIG_3, (1<<3)|(1<<2));
+      } else {
+        // set auto AGC
+        writeRegister(REG_MODEM_CONFIG_3, (1<<2));
+      }
+  #elif MODEM == SX1262
+      _ldro = 1;
+      setModulationParams(_sf, _bw, _cr, _ldro);
+  #endif
 }
 
 void LoRaClass::optimizeModemSensitivity(){
-  byte bw = (readRegister(REG_MODEM_CONFIG_1) >> 4);
-  uint32_t freq = getFrequency();
-
-  if (bw == 9 && (410E6 <= freq) && (freq <= 525E6)) {
-    writeRegister(REG_HIGH_BW_OPTIMIZE_1, 0x02);
-    writeRegister(REG_HIGH_BW_OPTIMIZE_2, 0x7f);
-  } else if (bw == 9 && (820E6 <= freq) && (freq <= 1020E6)) {
-    writeRegister(REG_HIGH_BW_OPTIMIZE_1, 0x02);
-    writeRegister(REG_HIGH_BW_OPTIMIZE_2, 0x64);
-  } else {
-    writeRegister(REG_HIGH_BW_OPTIMIZE_1, 0x03);
-  }
+  #if MODEM == SX1276 || MODEM == SX1278
+      byte bw = (readRegister(REG_MODEM_CONFIG_1) >> 4);
+      uint32_t freq = getFrequency();
+
+      if (bw == 9 && (410E6 <= freq) && (freq <= 525E6)) {
+        writeRegister(REG_HIGH_BW_OPTIMIZE_1, 0x02);
+        writeRegister(REG_HIGH_BW_OPTIMIZE_2, 0x7f);
+      } else if (bw == 9 && (820E6 <= freq) && (freq <= 1020E6)) {
+        writeRegister(REG_HIGH_BW_OPTIMIZE_1, 0x02);
+        writeRegister(REG_HIGH_BW_OPTIMIZE_2, 0x64);
+      } else {
+        writeRegister(REG_HIGH_BW_OPTIMIZE_1, 0x03);
+      }
+  #elif MODEM == SX1262
+      // todo: check if there's anything the sx1262 can do here
+  #endif
 }
 
 void LoRaClass::setSignalBandwidth(long sbw)
 {
-  int bw;
-
-  if (sbw <= 7.8E3) {
-    bw = 0;
-  } else if (sbw <= 10.4E3) {
-    bw = 1;
-  } else if (sbw <= 15.6E3) {
-    bw = 2;
-  } else if (sbw <= 20.8E3) {
-    bw = 3;
-  } else if (sbw <= 31.25E3) {
-    bw = 4;
-  } else if (sbw <= 41.7E3) {
-    bw = 5;
-  } else if (sbw <= 62.5E3) {
-    bw = 6;
-  } else if (sbw <= 125E3) {
-    bw = 7;
-  } else if (sbw <= 250E3) {
-    bw = 8;
-  } else /*if (sbw <= 250E3)*/ {
-    bw = 9;
-  }
+  #if MODEM == SX1276 || MODEM == SX1278
+      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, (readRegister(REG_MODEM_CONFIG_1) & 0x0f) | (bw << 4));
+
+  #elif MODEM == SX1262
+      uint8_t bw;
+
+      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;
+      }
+
+      setModulationParams(_sf, bw, _cr, _ldro);
+  #endif
 
-  writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0x0f) | (bw << 4));
-  
   handleLowDataRate();
   optimizeModemSensitivity();
 }
@@ -606,40 +1325,69 @@ void LoRaClass::setCodingRate4(int denominator)
 
   int cr = denominator - 4;
 
-  writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0xf1) | (cr << 1));
+  #if MODEM == SX1276 || MODEM == SX1278
+      writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0xf1) | (cr << 1));
+  #elif MODEM == SX1262
+      setModulationParams(_sf, _bw, cr, _ldro);
+  #endif
 }
 
 void LoRaClass::setPreambleLength(long length)
 {
-  writeRegister(REG_PREAMBLE_MSB, (uint8_t)(length >> 8));
-  writeRegister(REG_PREAMBLE_LSB, (uint8_t)(length >> 0));
+  #if MODEM == SX1276 || MODEM == SX1278
+      writeRegister(REG_PREAMBLE_MSB, (uint8_t)(length >> 8));
+      writeRegister(REG_PREAMBLE_LSB, (uint8_t)(length >> 0));
+  #elif MODEM == SX1262
+      setPacketParams(length, _implicitHeaderMode, _payloadLength, _crcMode);
+  #endif
 }
 
 void LoRaClass::setSyncWord(int sw)
 {
-  writeRegister(REG_SYNC_WORD, sw);
+  #if MODEM == SX1276 || MODEM == SX1278
+    writeRegister(REG_SYNC_WORD, sw);
+  #elif MODEM == SX1262
+    writeRegister(REG_SYNC_WORD_MSB, sw & 0xFF00);
+    writeRegister(REG_SYNC_WORD_LSB, sw & 0x00FF);
+  #endif
 }
 
 void LoRaClass::enableCrc()
 {
-  writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) | 0x04);
+  #if MODEM == SX1276 || MODEM == SX1278
+      writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) | 0x04);
+  #elif MODEM == SX1262
+      _crcMode = 1;
+      setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+  #endif
 }
 
 void LoRaClass::disableCrc()
 {
-  writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) & 0xfb);
+  #if MODEM == SX1276 || MODEM == SX1278
+    writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) & 0xfb);
+  #elif MODEM == SX1262
+    _crcMode = 0;
+    setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+  #endif
 }
 
 byte LoRaClass::random()
 {
-  return readRegister(REG_RSSI_WIDEBAND);
+  #if MODEM == SX1276 || MODEM == SX1278
+    return readRegister(REG_RSSI_WIDEBAND);
+  #elif MODEM == SX1262
+    return readRegister(REG_RANDOM_GEN);
+  #endif
 }
 
-void LoRaClass::setPins(int ss, int reset, int dio0)
+void LoRaClass::setPins(int ss, int reset, int dio0, int rxen, int busy)
 {
   _ss = ss;
   _reset = reset;
   _dio0 = dio0;
+  _rxen = rxen;
+  _busy = busy;
 }
 
 void LoRaClass::setSPIFrequency(uint32_t frequency)
@@ -661,72 +1409,77 @@ void LoRaClass::explicitHeaderMode()
 {
   _implicitHeaderMode = 0;
 
-  writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) & 0xfe);
+  #if MODEM == SX1276 || MODEM == SX1278
+      writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) & 0xfe);
+  #elif MODEM == SX1262
+      setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+  #endif
 }
 
 void LoRaClass::implicitHeaderMode()
 {
   _implicitHeaderMode = 1;
 
-  writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) | 0x01);
+  #if MODEM == SX1276 || MODEM == SX1278
+      writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) | 0x01);
+  #elif MODEM == SX1262
+      setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+  #endif
 }
 
 
 void ISR_VECT LoRaClass::handleDio0Rise()
 {
-  int irqFlags = readRegister(REG_IRQ_FLAGS);
+  #if MODEM == SX1276 || MODEM == SX1278
+    int irqFlags = readRegister(REG_IRQ_FLAGS);
+
+    // clear IRQ's
+    writeRegister(REG_IRQ_FLAGS, irqFlags);
+    if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) {
+  #elif MODEM == SX1262
+    uint8_t buf[2];
 
-  // clear IRQ's
-  writeRegister(REG_IRQ_FLAGS, irqFlags);
+    buf[0] = 0x00;
+    buf[1] = 0x00;
 
-  if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) {
-    // received a packet
-    _packetIndex = 0;
+    executeOpcodeRead(OP_GET_IRQ_STATUS, buf, 2);
+
+    executeOpcode(OP_CLEAR_IRQ_STATUS, buf, 2);
+
+
+    if ((buf[1] & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) {
+  #endif
+
+
+  // received a packet
+  _packetIndex = 0;
 
     // read packet length
+  #if MODEM == SX1276 || MODEM == SX1278
     int packetLength = _implicitHeaderMode ? readRegister(REG_PAYLOAD_LENGTH) : readRegister(REG_RX_NB_BYTES);
 
     // set FIFO address to current RX address
     writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR));
+  #elif MODEM == SX1262
+    uint8_t rxbuf[2] = {0};
+    executeOpcodeRead(OP_RX_BUFFER_STATUS, rxbuf, 2);
+    int packetLength = rxbuf[0];
+  #endif
+
+  if (_onReceive) {
+    _onReceive(packetLength);
+  }
 
-    if (_onReceive) {
-      _onReceive(packetLength);
-    }
-
+  #if MODEM == SX1276 || MODEM == SX1278
     // reset FIFO address
     writeRegister(REG_FIFO_ADDR_PTR, 0);
+  #endif
   }
 }
 
-uint8_t ISR_VECT LoRaClass::readRegister(uint8_t address)
-{
-  return singleTransfer(address & 0x7f, 0x00);
-}
-
-void LoRaClass::writeRegister(uint8_t address, uint8_t value)
-{
-  singleTransfer(address | 0x80, value);
-}
-
-uint8_t ISR_VECT LoRaClass::singleTransfer(uint8_t address, uint8_t value)
-{
-  uint8_t response;
-
-  digitalWrite(_ss, LOW);
-
-  SPI.beginTransaction(_spiSettings);
-  SPI.transfer(address);
-  response = SPI.transfer(value);
-  SPI.endTransaction();
-
-  digitalWrite(_ss, HIGH);
-
-  return response;
-}
-
 void ISR_VECT LoRaClass::onDio0Rise()
 {
   LoRa.handleDio0Rise();
 }
 
-LoRaClass LoRa;
\ No newline at end of file
+LoRaClass LoRa;
diff --git a/LoRa.h b/LoRa.h
index c4c290e605b411e0de07e1e4829fd13e705a99c0..102010d9c3b3a7b768bbc8a89c8df23e472f0b98 100644 (file)
--- a/LoRa.h
+++ b/LoRa.h
@@ -9,10 +9,13 @@
 
 #include <Arduino.h>
 #include <SPI.h>
+#include "Modem.h"
 
 #define LORA_DEFAULT_SS_PIN    10
 #define LORA_DEFAULT_RESET_PIN 9
 #define LORA_DEFAULT_DIO0_PIN  2
+#define LORA_DEFAULT_RXEN_PIN  -1
+#define LORA_DEFAULT_BUSY_PIN  -1
 
 #define PA_OUTPUT_RFO_PIN      0
 #define PA_OUTPUT_PA_BOOST_PIN 1
@@ -71,13 +74,26 @@ public:
   void enableTCXO();
   void disableTCXO();
 
+  #if MODEM == SX1262
+      void enableAntenna();
+      void disableAntenna();
+      void loraMode();
+      void waitOnBusy();
+      void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size);
+      void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size);
+      void writeBuffer(const uint8_t* buffer, size_t size);
+      void readBuffer(uint8_t* buffer, size_t size);
+      void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro);
+      void setPacketParams(long preamble, uint8_t headermode, uint8_t length, uint8_t crc);
+  #endif
+
   // deprecated
   void crc() { enableCrc(); }
   void noCrc() { disableCrc(); }
 
   byte random();
 
-  void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN);
+  void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN, int rxen = LORA_DEFAULT_RXEN_PIN, int busy = LORA_DEFAULT_BUSY_PIN);
   void setSPIFrequency(uint32_t frequency);
 
   void dumpRegisters(Stream& out);
@@ -88,9 +104,15 @@ private:
 
   void handleDio0Rise();
 
-  uint8_t readRegister(uint8_t address);
-  void writeRegister(uint8_t address, uint8_t value);
-  uint8_t singleTransfer(uint8_t address, uint8_t value);
+  #if MODEM == SX1276 || MODEM == SX1278
+      uint8_t readRegister(uint8_t address);
+      void writeRegister(uint8_t address, uint8_t value);
+      uint8_t singleTransfer(uint8_t address, uint8_t value);
+  #elif MODEM == SX1262
+      uint8_t readRegister(uint16_t address);
+      void writeRegister(uint16_t address, uint8_t value);
+      uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value);
+  #endif
 
   static void onDio0Rise();
 
@@ -102,12 +124,22 @@ private:
   int _ss;
   int _reset;
   int _dio0;
+  int _rxen;
+  int _busy;
   long _frequency;
+  int _txp;
+  uint8_t _sf;
+  uint8_t _bw;
+  uint8_t _cr;
+  uint8_t _ldro;
   int _packetIndex;
+  int _preambleLength;
   int _implicitHeaderMode;
+  int _payloadLength;
+  int _crcMode;
   void (*_onReceive)(int);
 };
 
 extern LoRaClass LoRa;
 
-#endif
\ No newline at end of file
+#endif
index 91cf74d6446e7ae4c573210b9c9d6d6eb256bd56..03957d660cbec9ca41abdd2db26db4e19a02dd02 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -91,6 +91,8 @@ firmware-featheresp32:
 firmware-genericesp32:
        arduino-cli compile --fqbn esp32:esp32:esp32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x35\""
 
+firmware-rak4630:
+       arduino-cli compile --fqbn rakwireless:nrf52:WisCoreRAK4631Board -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x51\" \"-DnRF52=true\" \"-DMODEM=0x03\""
 
 upload:
        arduino-cli upload -p /dev/ttyUSB0 --fqbn unsignedio:avr:rnode
@@ -154,6 +156,10 @@ upload-featheresp32:
        @sleep 3
        python ./Release/esptool/esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size 4MB 0x210000 ./Release/console_image.bin
 
+upload-rak4630:
+       arduino-cli upload -p /dev/ttyACM0 --fqbn rakwireless:nrf52:WisCoreRAK4631Board
+
+
 
 release: release-all
 
@@ -287,4 +293,4 @@ release-genericesp32:
 release-mega2560:
        arduino-cli compile --fqbn arduino:avr:mega -e
        cp build/arduino.avr.mega/RNode_Firmware.ino.hex Release/rnode_firmware_m2560.hex
-       rm -r build
\ No newline at end of file
+       rm -r build
diff --git a/Modem.h b/Modem.h
new file mode 100644 (file)
index 0000000..bb23a57
--- /dev/null
+++ b/Modem.h
@@ -0,0 +1,3 @@
+#define SX1276 0x01
+#define SX1278 0x02
+#define SX1262 0x03
index 53395640e8fd7c0ea7489d0779da64dec5436f57..b59c5d130149dad99ea2a549054106e2b95947be 100644 (file)
@@ -43,7 +43,7 @@ volatile bool serial_buffering = false;
 
 char sbuf[128];
 
-#if MCU_VARIANT == MCU_ESP32
+#if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
   bool packet_ready = false;
 #endif
 
@@ -86,15 +86,15 @@ void setup() {
 
   // Set chip select, reset and interrupt
   // pins for the LoRa module
-  LoRa.setPins(pin_cs, pin_reset, pin_dio);
+  LoRa.setPins(pin_cs, pin_reset, pin_dio, pin_rxen, pin_busy);
   
-  #if MCU_VARIANT == MCU_ESP32
+  #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
     init_channel_stats();
 
     // Check installed transceiver chip and
     // probe boot parameters.
     if (LoRa.preInit()) {
-      sx1276_installed = true;
+      modem_installed = true;
       uint32_t lfr = LoRa.getFrequency();
       if (lfr == 0) {
         // Normal boot
@@ -110,12 +110,12 @@ void setup() {
       }
       LoRa.setFrequency(M_FRQ_S);
     } else {
-      sx1276_installed = false;
+      modem_installed = false;
     }
   #else
     // Older variants only came with SX1276/78 chips,
     // so assume that to be the case for now.
-    sx1276_installed = true;
+    modem_installed = true;
   #endif
 
   #if HAS_DISPLAY
@@ -173,7 +173,7 @@ inline void kiss_write_packet() {
   }
   serial_write(FEND);
   read_len = 0;
-  #if MCU_VARIANT == MCU_ESP32
+  #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
     packet_ready = false;
   #endif
 }
@@ -202,7 +202,7 @@ void ISR_VECT receive_callback(int packet_size) {
       read_len = 0;
       seq = sequence;
 
-      #if MCU_VARIANT != MCU_ESP32
+      #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52
         last_rssi = LoRa.packetRssi();
         last_snr_raw = LoRa.packetSnrRaw();
       #endif
@@ -214,12 +214,14 @@ void ISR_VECT receive_callback(int packet_size) {
       // packet, so we add it to the buffer
       // and set the ready flag.
       
-      #if MCU_VARIANT != MCU_ESP32
+
+      #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52
         last_rssi = (last_rssi+LoRa.packetRssi())/2;
         last_snr_raw = (last_snr_raw+LoRa.packetSnrRaw())/2;
       #endif
-      
+
       getPacketData(packet_size);
+
       seq = SEQ_UNSET;
       ready = true;
 
@@ -231,7 +233,7 @@ void ISR_VECT receive_callback(int packet_size) {
       read_len = 0;
       seq = sequence;
 
-      #if MCU_VARIANT != MCU_ESP32
+      #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52
         last_rssi = LoRa.packetRssi();
         last_snr_raw = LoRa.packetSnrRaw();
       #endif
@@ -250,7 +252,7 @@ void ISR_VECT receive_callback(int packet_size) {
         seq = SEQ_UNSET;
       }
 
-      #if MCU_VARIANT != MCU_ESP32
+      #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52
         last_rssi = LoRa.packetRssi();
         last_snr_raw = LoRa.packetSnrRaw();
       #endif
@@ -260,7 +262,7 @@ void ISR_VECT receive_callback(int packet_size) {
     }
 
     if (ready) {
-      #if MCU_VARIANT != MCU_ESP32
+      #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52
         // We first signal the RSSI of the
         // recieved packet to the host.
         kiss_indicate_stat_rssi();
@@ -277,7 +279,7 @@ void ISR_VECT receive_callback(int packet_size) {
     // output directly to the host
     read_len = 0;
 
-    #if MCU_VARIANT != MCU_ESP32
+    #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52
       last_rssi = LoRa.packetRssi();
       last_snr_raw = LoRa.packetSnrRaw();
       getPacketData(packet_size);
@@ -375,7 +377,7 @@ void flushQueue(void) {
     led_tx_on();
     uint16_t processed = 0;
 
-    #if MCU_VARIANT == MCU_ESP32
+    #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
     while (!fifo16_isempty(&packet_starts)) {
     #else
     while (!fifo16_isempty_locked(&packet_starts)) {
@@ -402,7 +404,7 @@ void flushQueue(void) {
 
   queue_height = 0;
   queued_bytes = 0;
-  #if MCU_VARIANT == MCU_ESP32
+  #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
     update_airtime();
   #endif
   queue_flushing = false;
@@ -410,7 +412,7 @@ void flushQueue(void) {
 
 #define PHY_HEADER_LORA_SYMBOLS 8
 void add_airtime(uint16_t written) {
-  #if MCU_VARIANT == MCU_ESP32
+  #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
     float packet_cost_ms = 0.0;
     float payload_cost_ms = ((float)written * lora_us_per_byte)/1000.0;
     packet_cost_ms += payload_cost_ms;
@@ -424,7 +426,7 @@ void add_airtime(uint16_t written) {
 }
 
 void update_airtime() {
-  #if MCU_VARIANT == MCU_ESP32
+  #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
     uint16_t cb = current_airtime_bin();
     uint16_t pb = cb-1; if (cb-1 < 0) { pb = AIRTIME_BINS-1; }
     uint16_t nb = cb+1; if (nb == AIRTIME_BINS) { nb = 0; }
@@ -443,7 +445,7 @@ void update_airtime() {
     }
     longterm_channel_util = (float)longterm_channel_util_sum/(float)AIRTIME_BINS;
 
-    #if MCU_VARIANT == MCU_ESP32
+    #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
       update_csma_p();
     #endif
     kiss_indicate_channel_stats();
@@ -813,13 +815,13 @@ void serialCallback(uint8_t sbyte) {
         kiss_indicate_fb();
       }
     } else if (command == CMD_DEV_HASH) {
-      #if MCU_VARIANT == MCU_ESP32
+      #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
         if (sbyte != 0x00) {
           kiss_indicate_device_hash();
         }
       #endif
     } else if (command == CMD_DEV_SIG) {
-      #if MCU_VARIANT == MCU_ESP32
+      #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
         if (sbyte == FESC) {
               ESCAPE = true;
           } else {
@@ -843,7 +845,7 @@ void serialCallback(uint8_t sbyte) {
         firmware_update_mode = false;
       }
     } else if (command == CMD_HASHES) {
-      #if MCU_VARIANT == MCU_ESP32
+      #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
         if (sbyte == 0x01) {
           kiss_indicate_target_fw_hash();
         } else if (sbyte == 0x02) {
@@ -855,7 +857,7 @@ void serialCallback(uint8_t sbyte) {
         }
       #endif
     } else if (command == CMD_FW_HASH) {
-      #if MCU_VARIANT == MCU_ESP32
+      #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
         if (sbyte == FESC) {
               ESCAPE = true;
           } else {
@@ -925,6 +927,8 @@ void serialCallback(uint8_t sbyte) {
 void updateModemStatus() {
   #if MCU_VARIANT == MCU_ESP32
     portENTER_CRITICAL(&update_lock);
+  #elif MCU_VARIANT == MCU_NRF52
+    portENTER_CRITICAL();
   #endif
 
   uint8_t status = LoRa.modemStatus();
@@ -933,6 +937,8 @@ void updateModemStatus() {
 
   #if MCU_VARIANT == MCU_ESP32
     portEXIT_CRITICAL(&update_lock);
+  #elif MCU_VARIANT == MCU_NRF52
+    portEXIT_CRITICAL();
   #endif
 
   if ((status & SIG_DETECT) == SIG_DETECT) { stat_signal_detected = true; } else { stat_signal_detected = false; }
@@ -982,7 +988,7 @@ void checkModemStatus() {
   if (millis()-last_status_update >= status_interval_ms) {
     updateModemStatus();
 
-    #if MCU_VARIANT == MCU_ESP32
+    #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
       util_samples[dcd_sample] = dcd;
       dcd_sample = (dcd_sample+1)%DCD_SAMPLES;
       if (dcd_sample % UTIL_UPDATE_INTERVAL == 0) {
@@ -1023,6 +1029,12 @@ void validate_status() {
       uint8_t F_POR = 0x00;
       uint8_t F_BOR = 0x00;
       uint8_t F_WDR = 0x01;
+  #elif MCU_VARIANT == MCU_NRF52
+      // TODO: Get NRF52 boot flags
+      uint8_t boot_flags = 0x02;
+      uint8_t F_POR = 0x00;
+      uint8_t F_BOR = 0x00;
+      uint8_t F_WDR = 0x01;
   #endif
 
   if (hw_ready || device_init_done) {
@@ -1059,7 +1071,7 @@ void validate_status() {
       if (eeprom_product_valid() && eeprom_model_valid() && eeprom_hwrev_valid()) {
         if (eeprom_checksum_valid()) {
           eeprom_ok = true;
-          if (sx1276_installed) {
+          if (modem_installed) {
             #if PLATFORM == PLATFORM_ESP32
               if (device_init()) {
                 hw_ready = true;
@@ -1071,7 +1083,7 @@ void validate_status() {
             #endif
           } else {
             hw_ready = false;
-            Serial.write("No SX1276/SX1278 radio module found\r\n");
+            Serial.write("No valid radio module found\r\n");
             #if HAS_DISPLAY
               if (disp_ready) {
                 device_init_done = true;
@@ -1125,7 +1137,7 @@ void validate_status() {
   }
 }
 
-#if MCU_VARIANT == MCU_ESP32
+#if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
   #define _e 2.71828183
   #define _S 10.0
   float csma_slope(float u) { return (pow(_e,_S*u-_S/2.0))/(pow(_e,_S*u-_S/2.0)+1.0); }
@@ -1147,6 +1159,21 @@ void loop() {
         kiss_write_packet();
       }
 
+      airtime_lock = false;
+      if (st_airtime_limit != 0.0 && airtime >= st_airtime_limit) airtime_lock = true;
+      if (lt_airtime_limit != 0.0 && longterm_airtime >= lt_airtime_limit) airtime_lock = true;
+
+    #elif MCU_VARIANT == MCU_NRF52
+      if (packet_ready) {
+        portENTER_CRITICAL();
+        last_rssi = LoRa.packetRssi();
+        last_snr_raw = LoRa.packetSnrRaw();
+        portEXIT_CRITICAL();
+        kiss_indicate_stat_rssi();
+        kiss_indicate_stat_snr();
+        kiss_write_packet();
+      }
+
       airtime_lock = false;
       if (st_airtime_limit != 0.0 && airtime >= st_airtime_limit) airtime_lock = true;
       if (lt_airtime_limit != 0.0 && longterm_airtime >= lt_airtime_limit) airtime_lock = true;
@@ -1155,7 +1182,7 @@ void loop() {
     checkModemStatus();
     if (!airtime_lock) {
       if (queue_height > 0) {
-        #if MCU_VARIANT == MCU_ESP32
+        #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
           long check_time = millis();
           if (check_time > post_tx_yield_timeout) {
             if (dcd_waiting && (check_time >= dcd_wait_until)) { dcd_waiting = false; }
@@ -1212,7 +1239,7 @@ void loop() {
     }
   }
 
-  #if MCU_VARIANT == MCU_ESP32
+  #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
       buffer_serial();
       if (!fifo_isempty(&serialFIFO)) serial_poll();
   #else
@@ -1236,7 +1263,7 @@ volatile bool serial_polling = false;
 void serial_poll() {
   serial_polling = true;
 
-  #if MCU_VARIANT != MCU_ESP32
+  #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52
   while (!fifo_isempty_locked(&serialFIFO)) {
   #else
   while (!fifo_isempty(&serialFIFO)) {
@@ -1270,14 +1297,14 @@ void buffer_serial() {
     {
       c++;
 
-      #if MCU_VARIANT != MCU_ESP32
+      #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52
         if (!fifo_isfull_locked(&serialFIFO)) {
           fifo_push_locked(&serialFIFO, Serial.read());
         }
       #else
         if (HAS_BLUETOOTH && bt_state == BT_STATE_CONNECTED) {
           if (!fifo_isfull(&serialFIFO)) {
-            fifo_push(&serialFIFO, SerialBT.read());
+            //fifo_push(&serialFIFO, SerialBT.read());
           }
         } else {
           if (!fifo_isfull(&serialFIFO)) {
diff --git a/ROM.h b/ROM.h
index 05c89dc40c1d928916b122ad2299a2f96223d442..1f9616817fe9e77848d5561d059cfc3193ff399e 100644 (file)
--- a/ROM.h
+++ b/ROM.h
@@ -70,4 +70,4 @@
        #define BT_ENABLE_BYTE 0x73
 
        #define EEPROM_RESERVED 200
-#endif
\ No newline at end of file
+#endif
index e76f91c1ce897e14e76e9bbefad64a4bdf1b5341..a3597a418f433c0109e3dfcfb60a4b6a90f31924 100644 (file)
 // You should have received a copy of the GNU General Public License
 // along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
-#include <EEPROM.h>
+#if HAS_EEPROM 
+    #include <EEPROM.h>
+#elif PLATFORM == PLATFORM_NRF52
+    #include "flash_nrf5x.h"
+    int written_bytes = 0;
+#endif
 #include <stddef.h>
 #include "Config.h"
 #include "LoRa.h"
   #include "Power.h"
 #endif
 
-#if MCU_VARIANT == MCU_ESP32
+#if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
        #include "Device.h"
+#endif
+#if MCU_VARIANT == MCU_ESP32
   #include "soc/rtc_wdt.h"
   #define ISR_VECT IRAM_ATTR
 #else
@@ -57,6 +64,8 @@ uint8_t boot_vector = 0x00;
        }
 #elif MCU_VARIANT == MCU_ESP32
        // TODO: Get ESP32 boot flags
+#elif MCU_VARIANT == MCU_NRF52
+       // TODO: Get NRF52 boot flags
 #endif
 
 #if HAS_NP == true
@@ -175,6 +184,13 @@ uint8_t boot_vector = 0x00;
                void led_tx_on()  { digitalWrite(pin_led_tx, HIGH); }
                void led_tx_off() { digitalWrite(pin_led_tx, LOW); }
        #endif
+#elif MCU_VARIANT == MCU_NRF52
+    #if BOARD_MODEL == BOARD_RAK4630
+               void led_rx_on()  { digitalWrite(pin_led_rx, HIGH); }
+               void led_rx_off() {     digitalWrite(pin_led_rx, LOW); }
+               void led_tx_on()  { digitalWrite(pin_led_tx, HIGH); }
+               void led_tx_off() { digitalWrite(pin_led_tx, LOW); }
+    #endif
 #endif
 
 void hard_reset(void) {
@@ -185,6 +201,8 @@ void hard_reset(void) {
                }
        #elif MCU_VARIANT == MCU_ESP32
                ESP.restart();
+       #elif MCU_VARIANT == MCU_NRF52
+        // currently not possible to restart on this platform
        #endif
 }
 
@@ -285,7 +303,7 @@ void led_indicate_warning(int cycles) {
          }
          led_rx_off();
        }
-#elif MCU_VARIANT == MCU_ESP32
+#elif MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
        #if HAS_NP == true
                void led_indicate_info(int cycles) {
                        bool forever = (cycles == 0) ? true : false;
@@ -375,6 +393,17 @@ unsigned long led_standby_ticks = 0;
                unsigned long led_standby_wait = 1768;
                unsigned long led_notready_wait = 150;
        #endif
+
+#elif MCU_VARIANT == MCU_NRF52
+               uint8_t led_standby_min = 200;
+               uint8_t led_standby_max = 255;
+               uint8_t led_notready_min = 0;
+               uint8_t led_notready_max = 255;
+               uint8_t led_notready_value = led_notready_min;
+               int8_t  led_notready_direction = 0;
+               unsigned long led_notready_ticks = 0;
+               unsigned long led_standby_wait = 1768;
+               unsigned long led_notready_wait = 150;
 #endif
 
 unsigned long led_standby_value = led_standby_min;
@@ -396,7 +425,7 @@ int8_t  led_standby_direction = 0;
                }
        }
 
-#elif MCU_VARIANT == MCU_ESP32
+#elif MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
        #if HAS_NP == true
                void led_indicate_standby() {
                        led_standby_ticks++;
@@ -504,7 +533,7 @@ int8_t  led_standby_direction = 0;
                        led_rx_off();
                }
        }
-#elif MCU_VARIANT == MCU_ESP32
+#elif MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
        #if HAS_NP == true
     void led_indicate_not_ready() {
        led_standby_ticks++;
@@ -625,7 +654,11 @@ void kiss_indicate_stat_tx() {
 }
 
 void kiss_indicate_stat_rssi() {
-       uint8_t packet_rssi_val = (uint8_t)(last_rssi+rssi_offset);
+    #if MODEM == SX1276 || MODEM == SX1278
+        uint8_t packet_rssi_val = (uint8_t)(last_rssi+rssi_offset);
+    #elif MODEM == SX1262
+        uint8_t packet_rssi_val = (uint8_t)(last_rssi);
+    #endif
        serial_write(FEND);
        serial_write(CMD_STAT_RSSI);
        escaped_serial_write(packet_rssi_val);
@@ -799,7 +832,7 @@ void kiss_indicate_fbstate() {
        serial_write(FEND);
 }
 
-#if MCU_VARIANT == MCU_ESP32
+#if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
        void kiss_indicate_device_hash() {
          serial_write(FEND);
          serial_write(CMD_DEV_HASH);
@@ -944,7 +977,7 @@ void setPreamble() {
 }
 
 void updateBitrate() {
-       #if MCU_VARIANT == MCU_ESP32
+       #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
                if (radio_online) {
                        lora_symbol_rate = (float)lora_bw/(float)(pow(2, lora_sf));
                        lora_symbol_time_ms = (1.0/lora_symbol_rate)*1000.0;
@@ -1058,8 +1091,21 @@ void promisc_disable() {
        promisc = false;
 }
 
+#if !HAS_EEPROM && MCU_VARIANT == MCU_NRF52
+    uint8_t eeprom_read(uint32_t mapped_addr) {
+        uint8_t byte;
+        void* byte_ptr = &byte;
+        flash_nrf5x_read(byte_ptr, mapped_addr, 1);
+        return byte;
+    }
+#endif
+
 bool eeprom_info_locked() {
-       uint8_t lock_byte = EEPROM.read(eeprom_addr(ADDR_INFO_LOCK));
+    #if HAS_EEPROM
+           uint8_t lock_byte = EEPROM.read(eeprom_addr(ADDR_INFO_LOCK));
+    #elif MCU_VARIANT == MCU_NRF52
+        uint8_t lock_byte = eeprom_read(eeprom_addr(ADDR_INFO_LOCK));
+    #endif
        if (lock_byte == INFO_LOCK_BYTE) {
                return true;
        } else {
@@ -1069,21 +1115,33 @@ bool eeprom_info_locked() {
 
 void eeprom_dump_info() {
        for (int addr = ADDR_PRODUCT; addr <= ADDR_INFO_LOCK; addr++) {
-               uint8_t byte = EEPROM.read(eeprom_addr(addr));
+        #if HAS_EEPROM
+            uint8_t byte = EEPROM.read(eeprom_addr(addr));
+        #elif MCU_VARIANT == MCU_NRF52
+            uint8_t byte = eeprom_read(eeprom_addr(addr));
+        #endif
                escaped_serial_write(byte);
        }
 }
 
 void eeprom_dump_config() {
        for (int addr = ADDR_CONF_SF; addr <= ADDR_CONF_OK; addr++) {
-               uint8_t byte = EEPROM.read(eeprom_addr(addr));
+        #if HAS_EEPROM
+            uint8_t byte = EEPROM.read(eeprom_addr(addr));
+        #elif MCU_VARIANT == MCU_NRF52
+            uint8_t byte = eeprom_read(eeprom_addr(addr));
+        #endif
                escaped_serial_write(byte);
        }
 }
 
 void eeprom_dump_all() {
        for (int addr = 0; addr < EEPROM_RESERVED; addr++) {
-               uint8_t byte = EEPROM.read(eeprom_addr(addr));
+        #if HAS_EEPROM
+            uint8_t byte = EEPROM.read(eeprom_addr(addr));
+        #elif MCU_VARIANT == MCU_NRF52
+            uint8_t byte = eeprom_read(eeprom_addr(addr));
+        #endif
                escaped_serial_write(byte);
        }
 }
@@ -1103,6 +1161,22 @@ void eeprom_update(int mapped_addr, uint8_t byte) {
                        EEPROM.write(mapped_addr, byte);
                        EEPROM.commit();
                }
+    #elif !HAS_EEPROM && MCU_VARIANT == MCU_NRF52
+        uint8_t read_byte;
+        void* read_byte_ptr = &read_byte;
+        void const * byte_ptr = &byte;
+        flash_nrf5x_read(read_byte_ptr, mapped_addr, 1);
+        if (read_byte != byte) {
+            flash_nrf5x_write(mapped_addr, byte_ptr, 1);
+        }
+
+        written_bytes++;
+
+        // flush the cache every 4 bytes to make sure everything is synced
+        if (written_bytes == 4) {
+            written_bytes = 0;
+            flash_nrf5x_flush();
+        }
        #endif
 
 }
@@ -1123,7 +1197,11 @@ void eeprom_erase() {
 }
 
 bool eeprom_lock_set() {
-       if (EEPROM.read(eeprom_addr(ADDR_INFO_LOCK)) == INFO_LOCK_BYTE) {
+    #if HAS_EEPROM
+           if (EEPROM.read(eeprom_addr(ADDR_INFO_LOCK)) == INFO_LOCK_BYTE) {
+    #elif MCU_VARIANT == MCU_NRF52
+        if (eeprom_read(eeprom_addr(ADDR_INFO_LOCK)) == INFO_LOCK_BYTE) {
+    #endif
                return true;
        } else {
                return false;
@@ -1131,12 +1209,18 @@ bool eeprom_lock_set() {
 }
 
 bool eeprom_product_valid() {
-       uint8_t rval = EEPROM.read(eeprom_addr(ADDR_PRODUCT));
+    #if HAS_EEPROM
+           uint8_t rval = EEPROM.read(eeprom_addr(ADDR_PRODUCT));
+    #elif MCU_VARIANT == MCU_NRF52
+           uint8_t rval = eeprom_read(eeprom_addr(ADDR_PRODUCT));
+    #endif
 
        #if PLATFORM == PLATFORM_AVR
        if (rval == PRODUCT_RNODE || rval == PRODUCT_HMBRW) {
        #elif PLATFORM == PLATFORM_ESP32
        if (rval == PRODUCT_RNODE || rval == BOARD_RNODE_NG_20 || rval == BOARD_RNODE_NG_21 || rval == PRODUCT_HMBRW || rval == PRODUCT_TBEAM || rval == PRODUCT_T32_10 || rval == PRODUCT_T32_20 || rval == PRODUCT_T32_21 || rval == PRODUCT_H32_V2) {
+       #elif PLATFORM == PLATFORM_NRF52
+       if (rval == PRODUCT_HMBRW) {
        #else
        if (false) {
        #endif
@@ -1147,7 +1231,11 @@ bool eeprom_product_valid() {
 }
 
 bool eeprom_model_valid() {
-       model = EEPROM.read(eeprom_addr(ADDR_MODEL));
+    #if HAS_EEPROM
+        model = EEPROM.read(eeprom_addr(ADDR_MODEL));
+    #elif MCU_VARIANT == MCU_NRF52
+        model = eeprom_read(eeprom_addr(ADDR_MODEL));
+    #endif
        #if BOARD_MODEL == BOARD_RNODE
        if (model == MODEL_A4 || model == MODEL_A9 || model == MODEL_FF || model == MODEL_FE) {
        #elif BOARD_MODEL == BOARD_RNODE_NG_20
@@ -1166,6 +1254,8 @@ bool eeprom_model_valid() {
        if (model == MODEL_B4 || model == MODEL_B9) {
        #elif BOARD_MODEL == BOARD_HELTEC32_V2
        if (model == MODEL_C4 || model == MODEL_C9) {
+    #elif BOARD_MODEL == BOARD_RAK4630
+    if (model == MODEL_FF) {
        #elif BOARD_MODEL == BOARD_HUZZAH32
        if (model == MODEL_FF) {
        #elif BOARD_MODEL == BOARD_GENERIC_ESP32
@@ -1180,7 +1270,11 @@ bool eeprom_model_valid() {
 }
 
 bool eeprom_hwrev_valid() {
-       hwrev = EEPROM.read(eeprom_addr(ADDR_HW_REV));
+    #if HAS_EEPROM
+        hwrev = EEPROM.read(eeprom_addr(ADDR_HW_REV));
+    #elif MCU_VARIANT == MCU_NRF52
+        hwrev = eeprom_read(eeprom_addr(ADDR_HW_REV));
+    #endif
        if (hwrev != 0x00 && hwrev != 0xFF) {
                return true;
        } else {
@@ -1191,14 +1285,22 @@ bool eeprom_hwrev_valid() {
 bool eeprom_checksum_valid() {
        char *data = (char*)malloc(CHECKSUMMED_SIZE);
        for (uint8_t  i = 0; i < CHECKSUMMED_SIZE; i++) {
-               char byte = EEPROM.read(eeprom_addr(i));
+        #if HAS_EEPROM
+            char byte = EEPROM.read(eeprom_addr(i));
+        #elif MCU_VARIANT == MCU_NRF52
+            char byte = eeprom_read(eeprom_addr(i));
+        #endif
                data[i] = byte;
        }
        
        unsigned char *hash = MD5::make_hash(data, CHECKSUMMED_SIZE);
        bool checksum_valid = true;
        for (uint8_t i = 0; i < 16; i++) {
-               uint8_t stored_chk_byte = EEPROM.read(eeprom_addr(ADDR_CHKSUM+i));
+        #if HAS_EEPROM
+            uint8_t stored_chk_byte = EEPROM.read(eeprom_addr(ADDR_CHKSUM+i));
+        #elif MCU_VARIANT == MCU_NRF52
+            uint8_t stored_chk_byte = eeprom_read(eeprom_addr(ADDR_CHKSUM+i));
+        #endif
                uint8_t calced_chk_byte = (uint8_t)hash[i];
                if (stored_chk_byte != calced_chk_byte) {
                        checksum_valid = false;
@@ -1227,7 +1329,11 @@ void da_conf_save(uint8_t dadr) {
 }
 
 bool eeprom_have_conf() {
-       if (EEPROM.read(eeprom_addr(ADDR_CONF_OK)) == CONF_OK_BYTE) {
+    #if HAS_EEPROM
+           if (EEPROM.read(eeprom_addr(ADDR_CONF_OK)) == CONF_OK_BYTE) {
+    #elif MCU_VARIANT == MCU_NRF52
+        if (eeprom_read(eeprom_addr(ADDR_CONF_OK)) == CONF_OK_BYTE) {
+    #endif
                return true;
        } else {
                return false;
@@ -1236,11 +1342,19 @@ bool eeprom_have_conf() {
 
 void eeprom_conf_load() {
        if (eeprom_have_conf()) {
-               lora_sf = EEPROM.read(eeprom_addr(ADDR_CONF_SF));
-               lora_cr = EEPROM.read(eeprom_addr(ADDR_CONF_CR));
-               lora_txp = EEPROM.read(eeprom_addr(ADDR_CONF_TXP));
-               lora_freq = (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_FREQ)+0x00) << 24 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_FREQ)+0x01) << 16 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_FREQ)+0x02) << 8 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_FREQ)+0x03);
-               lora_bw = (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_BW)+0x00) << 24 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_BW)+0x01) << 16 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_BW)+0x02) << 8 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_BW)+0x03);
+        #if HAS_EEPROM
+            lora_sf = EEPROM.read(eeprom_addr(ADDR_CONF_SF));
+            lora_cr = EEPROM.read(eeprom_addr(ADDR_CONF_CR));
+            lora_txp = EEPROM.read(eeprom_addr(ADDR_CONF_TXP));
+            lora_freq = (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_FREQ)+0x00) << 24 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_FREQ)+0x01) << 16 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_FREQ)+0x02) << 8 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_FREQ)+0x03);
+            lora_bw = (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_BW)+0x00) << 24 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_BW)+0x01) << 16 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_BW)+0x02) << 8 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_BW)+0x03);
+        #elif MCU_VARIANT == MCU_NRF52
+            lora_sf = eeprom_read(eeprom_addr(ADDR_CONF_SF));
+            lora_cr = eeprom_read(eeprom_addr(ADDR_CONF_CR));
+            lora_txp = eeprom_read(eeprom_addr(ADDR_CONF_TXP));
+            lora_freq = (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_FREQ)+0x00) << 24 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_FREQ)+0x01) << 16 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_FREQ)+0x02) << 8 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_FREQ)+0x03);
+            lora_bw = (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_BW)+0x00) << 24 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_BW)+0x01) << 16 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_BW)+0x02) << 8 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_BW)+0x03);
+        #endif
        }
 }
 
@@ -1327,7 +1441,7 @@ inline void fifo_flush(FIFOBuffer *f) {
   f->head = f->tail;
 }
 
-#if MCU_VARIANT != MCU_ESP32
+#if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52
        static inline bool fifo_isempty_locked(const FIFOBuffer *f) {
          bool result;
          ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
@@ -1409,7 +1523,7 @@ inline void fifo16_flush(FIFOBuffer16 *f) {
   f->head = f->tail;
 }
 
-#if MCU_VARIANT != MCU_ESP32
+#if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52
        static inline bool fifo16_isempty_locked(const FIFOBuffer16 *f) {
          bool result;
          ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
diff --git a/flash_cache.c b/flash_cache.c
new file mode 100644 (file)
index 0000000..d9ac558
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach for Adafruit Industries
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <string.h>
+#include "flash_cache.h"
+#include "common_func.h"
+#include "variant.h"
+#include "wiring_digital.h"
+
+//--------------------------------------------------------------------+
+// MACRO TYPEDEF CONSTANT ENUM DECLARATION
+//--------------------------------------------------------------------+
+static inline uint32_t page_addr_of (uint32_t addr)
+{
+  return addr & ~(FLASH_CACHE_SIZE - 1);
+}
+
+static inline uint32_t page_offset_of (uint32_t addr)
+{
+  return addr & (FLASH_CACHE_SIZE - 1);
+}
+
+int flash_cache_write (flash_cache_t* fc, uint32_t dst, void const * src, uint32_t len)
+{
+  uint8_t const * src8 = (uint8_t const *) src;
+  uint32_t remain = len;
+
+  // Program up to page boundary each loop
+  while ( remain )
+  {
+    uint32_t const page_addr = page_addr_of(dst);
+    uint32_t const offset = page_offset_of(dst);
+
+    uint32_t wr_bytes = FLASH_CACHE_SIZE - offset;
+    wr_bytes = min32(remain, wr_bytes);
+
+    // Page changes, flush old and update new cache
+    if ( page_addr != fc->cache_addr )
+    {
+      flash_cache_flush(fc);
+      fc->cache_addr = page_addr;
+
+      // read a whole page from flash
+      fc->read(fc->cache_buf, page_addr, FLASH_CACHE_SIZE);
+    }
+
+    memcpy(fc->cache_buf + offset, src8, wr_bytes);
+
+    // adjust for next run
+    src8 += wr_bytes;
+    remain -= wr_bytes;
+    dst += wr_bytes;
+  }
+
+  return len - remain;
+}
+
+void flash_cache_flush (flash_cache_t* fc)
+{
+  if ( fc->cache_addr == FLASH_CACHE_INVALID_ADDR ) return;
+
+  // skip erase & program if verify() exists, and memory matches
+  if ( !(fc->verify && fc->verify(fc->cache_addr, fc->cache_buf, FLASH_CACHE_SIZE)) )
+  {
+    // indicator TODO allow to disable flash indicator
+    ledOn(LED_BUILTIN);
+
+    fc->erase(fc->cache_addr);
+    fc->program(fc->cache_addr, fc->cache_buf, FLASH_CACHE_SIZE);
+
+    ledOff(LED_BUILTIN);
+  }
+
+  fc->cache_addr = FLASH_CACHE_INVALID_ADDR;
+}
+
+int flash_cache_read (flash_cache_t* fc, void* dst, uint32_t addr, uint32_t count)
+{
+  // there is no check for overflow / wraparound for dst + count, addr + count.
+  // this might be a useful thing to add for at least debug builds.
+
+  // overwrite with cache value if available
+  if ( (fc->cache_addr != FLASH_CACHE_INVALID_ADDR) &&               // cache is not valid
+       !(addr < fc->cache_addr && addr + count <= fc->cache_addr) && // starts before, ends before cache area
+       !(addr >= fc->cache_addr + FLASH_CACHE_SIZE) )                // starts after end of cache area
+  {
+    // This block is entered only when the read overlaps the cache area by at least one byte.
+    // If the read starts before the cache area, it's further guaranteed
+    //    that count is large enough to cause the read to enter
+    //    the cache area by at least 1 byte.
+    uint32_t dst_off = 0;
+    uint32_t src_off = 0;
+    if (addr < fc->cache_addr)
+    {
+      dst_off = fc->cache_addr - addr;
+      // Read the bytes prior to the cache address
+      fc->read(dst, addr, dst_off);
+    }
+    else
+    {
+      src_off = addr - fc->cache_addr;      
+    }
+    
+    // Thus, after the above code block executes:
+    // *** AT MOST ***, only one of src_off and dst_off are non-zero;
+    // (Both may be zero when the read starts at the start of the cache area.)
+    // dst_off corresponds to the number of bytes already read from PRIOR to the cache area.
+    // src_off corresponds to the byte offset to start reading at, from WITHIN the cache area.
+
+    // How many bytes to memcpy from flash area?
+    // Remember that, AT MOST, one of src_off and dst_off are non-zero.
+    // If src_off is non-zero, then dst_off is zero, representing that the
+    //   read starts inside the cache.  In this case:
+    //     PARAM1 := FLASH_CACHE_SIZE - src_off == maximum possible bytes to read from cache
+    //     PARAM2 := count
+    //   Thus, taking the minimum of the two gives the number of bytes to read from cache,
+    //     in the range [ 1 .. FLASH_CACHE_SIZE-src_off ].
+    // Else if dst_off is non-zero, then src_off is zero, representing that the
+    //   read started prior to the cache area.  In this case:
+    //     PARAM1 := FLASH_CACHE_SIZE == full size of the cache
+    //     PARAM2 := count - dst_off == total bytes requested, minus the count of those already read
+    //   Because the original request is guaranteed to overlap the cache, the range for
+    //     PARAM2 is ensured to be [ 1 .. count-1 ].
+    //   Thus, taking the minimum of the two gives the number of bytes to read from cache,
+    //     in the range [ 1 .. FLASH_CACHE_SIZE ]
+    // Else both src_off and dst_off are zero, representing that the read is starting
+    //   exactly aligned to the cache.
+    //     PARAM1 := FLASH_CACHE_SIZE
+    //     PARAM2 := count
+    //   Thus, taking the minimum of the two gives the number of bytes to read from cache,
+    //     in the range [ 1 .. FLASH_CACHE_SIZE ]
+    // 
+    // Therefore, in all cases, there is assurance that cache_bytes
+    // will be in the final range [1..FLASH_CACHE_SIZE].
+    uint32_t cache_bytes = minof(FLASH_CACHE_SIZE-src_off, count - dst_off);
+
+    // Use memcpy to read cached data into the buffer
+    // If src_off is non-zero, then dst_off is zero, representing that the
+    //   read starts inside the cache.  In this case:
+    //     PARAM1 := dst
+    //     PARAM2 := fc->cache_buf + src_off
+    //     PARAM3 := cache_bytes
+    //   Thus, all works as expected when starting in the midst of the cache.
+    // Else if dst_off is non-zero, then src_off is zero, representing that the
+    //   read started prior to the cache.  In this case:
+    //     PARAM1 := dst + dst_off == destination offset by number of bytes already read
+    //     PARAM2 := fc->cache_buf
+    //     PARAM3 := cache_bytes
+    //   Thus, all works as expected when starting prior to the cache.
+    // Else both src_off and dst_off are zero, representing that the read is starting
+    //   exactly aligned to the cache.
+    //     PARAM1 := dst
+    //     PARAM2 := fc->cache_buf
+    //     PARAM3 := cache_bytes
+    //   Thus, all works as expected when starting exactly at the cache boundary
+    // 
+    // Therefore, in all cases, there is assurance that cache_bytes
+    // will be in the final range [1..FLASH_CACHE_SIZE].
+    memcpy(dst + dst_off, fc->cache_buf + src_off, cache_bytes);
+
+    // Read any final bytes from flash
+    // As noted above, dst_off represents the count of bytes read prior to the cache
+    // while cache_bytes represents the count of bytes read from the cache;
+    // This code block is guaranteed to overlap the cache area by at least one byte.
+    // Thus, copied will correspond to the total bytes already copied,
+    // and is guaranteed to be in the range [ 1 .. count ].
+    uint32_t copied = dst_off + cache_bytes;
+    
+    // 
+    if ( copied < count )
+    {
+      fc->read(dst + copied, addr + copied, count - copied);
+    }
+  }
+  else
+  {
+    // not using the cache, so just forward to read from flash
+    fc->read(dst, addr, count);
+  }
+
+  return (int) count;
+}
+
diff --git a/flash_cache.h b/flash_cache.h
new file mode 100644 (file)
index 0000000..efe87d2
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach for Adafruit Industries
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef FLASH_CACHE_H_
+#define FLASH_CACHE_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#define FLASH_CACHE_SIZE          4096        // must be a erasable page size
+#define FLASH_CACHE_INVALID_ADDR  0xffffffff
+
+typedef struct
+{
+    bool (*erase) (uint32_t addr);
+    uint32_t (*program) (uint32_t dst, void const * src, uint32_t len);
+    uint32_t (*read) (void* dst, uint32_t src, uint32_t len);
+    bool (*verify) (uint32_t addr, void const * buf, uint32_t len);
+
+    uint32_t cache_addr;
+    uint8_t* cache_buf;
+} flash_cache_t;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int flash_cache_write (flash_cache_t* fc, uint32_t dst, void const *src, uint32_t count);
+void flash_cache_flush (flash_cache_t* fc);
+int flash_cache_read (flash_cache_t* fc, void* dst, uint32_t addr, uint32_t count);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* FLASH_CACHE_H_ */
+
diff --git a/flash_nrf5x.c b/flash_nrf5x.c
new file mode 100644 (file)
index 0000000..8018f5e
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach for Adafruit Industries
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "flash_nrf5x.h"
+#include "flash_cache.h"
+#include "nrf_sdm.h"
+#include "nrf_soc.h"
+#include "delay.h"
+#include "rtos.h"
+
+
+#ifdef NRF52840_XXAA
+  #define BOOTLOADER_ADDR        0xF4000
+#else
+  #define BOOTLOADER_ADDR        0x74000
+#endif
+
+// defined in linker script
+extern uint32_t __flash_arduino_start[];
+//extern uint32_t __flash_arduino_end[];
+
+//--------------------------------------------------------------------+
+// MACRO TYPEDEF CONSTANT ENUM DECLARATION
+//--------------------------------------------------------------------+
+static SemaphoreHandle_t _sem = NULL;
+
+void flash_nrf5x_event_cb (uint32_t event)
+{
+//  if (event != NRF_EVT_FLASH_OPERATION_SUCCESS) LOG_LV1("IFLASH", "Flash op Failed");
+  if ( _sem ) xSemaphoreGive(_sem);
+}
+
+// Flash Abstraction Layer
+static bool fal_erase (uint32_t addr);
+static uint32_t fal_program (uint32_t dst, void const * src, uint32_t len);
+static uint32_t fal_read (void* dst, uint32_t src, uint32_t len);
+static bool fal_verify (uint32_t addr, void const * buf, uint32_t len);
+
+static uint8_t _cache_buffer[FLASH_CACHE_SIZE] __attribute__((aligned(4)));
+
+static flash_cache_t _cache =
+{
+  .erase      = fal_erase,
+  .program    = fal_program,
+  .read       = fal_read,
+  .verify     = fal_verify,
+
+  .cache_addr = FLASH_CACHE_INVALID_ADDR,
+  .cache_buf  = _cache_buffer
+};
+
+//--------------------------------------------------------------------+
+// Application API
+//--------------------------------------------------------------------+
+void flash_nrf5x_flush (void)
+{
+  flash_cache_flush(&_cache);
+}
+
+int flash_nrf5x_write (uint32_t dst, void const * src, uint32_t len)
+{
+  // Softdevice region
+  VERIFY(dst >= ((uint32_t) __flash_arduino_start), -1);
+
+  // Bootloader region
+  VERIFY(dst < BOOTLOADER_ADDR, -1);
+
+  return flash_cache_write(&_cache, dst, src, len);
+}
+
+int flash_nrf5x_read (void* dst, uint32_t src, uint32_t len)
+{
+  return flash_cache_read(&_cache, dst, src, len);
+}
+
+bool flash_nrf5x_erase(uint32_t addr)
+{
+  return fal_erase(addr);
+}
+
+//--------------------------------------------------------------------+
+// HAL for caching
+//--------------------------------------------------------------------+
+static bool fal_erase (uint32_t addr)
+{
+  // Init semaphore for first call
+  if ( _sem == NULL )
+  {
+    _sem = xSemaphoreCreateCounting(10, 0);
+    VERIFY(_sem);
+  }
+
+  // retry if busy
+  uint32_t err;
+  while ( NRF_ERROR_BUSY == (err = sd_flash_page_erase(addr / FLASH_NRF52_PAGE_SIZE)) )
+  {
+    delay(1);
+  }
+  VERIFY_STATUS(err, false);
+
+  // wait for async event if SD is enabled
+  uint8_t sd_en = 0;
+  (void) sd_softdevice_is_enabled(&sd_en);
+
+  if ( sd_en ) xSemaphoreTake(_sem, portMAX_DELAY);
+
+  return true;
+}
+
+static uint32_t fal_program (uint32_t dst, void const * src, uint32_t len)
+{
+  // wait for async event if SD is enabled
+  uint8_t sd_en = 0;
+  (void) sd_softdevice_is_enabled(&sd_en);
+
+  uint32_t err;
+
+  // Somehow S140 v6.1.1 assert an error when writing a whole page
+  // https://devzone.nordicsemi.com/f/nordic-q-a/40088/sd_flash_write-cause-nrf_fault_id_sd_assert
+  // Workaround: write half page at a time.
+#if NRF52832_XXAA
+  while ( NRF_ERROR_BUSY == (err = sd_flash_write((uint32_t*) dst, (uint32_t const *) src, len/4)) )
+  {
+    delay(1);
+  }
+  VERIFY_STATUS(err, 0);
+
+  if ( sd_en ) xSemaphoreTake(_sem, portMAX_DELAY);
+#else
+  while ( NRF_ERROR_BUSY == (err = sd_flash_write((uint32_t*) dst, (uint32_t const *) src, len/8)) )
+  {
+    delay(1);
+  }
+  VERIFY_STATUS(err, 0);
+  if ( sd_en ) xSemaphoreTake(_sem, portMAX_DELAY);
+
+  while ( NRF_ERROR_BUSY == (err = sd_flash_write((uint32_t*) (dst+ len/2), (uint32_t const *) (src + len/2), len/8)) )
+  {
+    delay(1);
+  }
+  VERIFY_STATUS(err, 0);
+  if ( sd_en ) xSemaphoreTake(_sem, portMAX_DELAY);
+#endif
+
+  return len;
+}
+
+static uint32_t fal_read (void* dst, uint32_t src, uint32_t len)
+{
+  memcpy(dst, (void*) src, len);
+  return len;
+}
+
+static bool fal_verify (uint32_t addr, void const * buf, uint32_t len)
+{
+  return 0 == memcmp((void*) addr, buf, len);
+}
+
diff --git a/flash_nrf5x.h b/flash_nrf5x.h
new file mode 100644 (file)
index 0000000..668e73c
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach for Adafruit Industries
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef FLASH_NRF52_H_
+#define FLASH_NRF52_H_
+
+#include "common_inc.h"
+
+#define FLASH_NRF52_PAGE_SIZE   4096
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+void flash_nrf5x_flush (void);
+bool flash_nrf5x_erase(uint32_t addr);
+
+int flash_nrf5x_write (uint32_t dst, void const * src, uint32_t len);
+int flash_nrf5x_read (void* dst, uint32_t src, uint32_t len);
+
+//--------------------------------------------------------------------+
+// Write helper
+//--------------------------------------------------------------------+
+static inline int flash_nrf5x_write8 (uint32_t dst, uint8_t num)
+{
+  return flash_nrf5x_write(dst, &num, sizeof(num));
+}
+
+static inline int flash_nrf5x_write16 (uint32_t dst, uint8_t num)
+{
+  return flash_nrf5x_write(dst, &num, sizeof(num));
+}
+static inline int flash_nrf5x_write32 (uint32_t dst, uint8_t num)
+{
+  return flash_nrf5x_write(dst, &num, sizeof(num));
+}
+
+//--------------------------------------------------------------------+
+// Read helper
+//--------------------------------------------------------------------+
+static inline uint8_t flash_nrf5x_read8 (uint32_t src)
+{
+  uint8_t num;
+  flash_nrf5x_read(&num, src, sizeof(num));
+  return num;
+}
+
+static inline uint16_t flash_nrf5x_read16 (uint32_t src)
+{
+  uint16_t num;
+  flash_nrf5x_read(&num, src, sizeof(num));
+  return num;
+}
+
+static inline uint16_t flash_nrf5x_read32 (uint32_t src)
+{
+  uint32_t num;
+  flash_nrf5x_read(&num, src, sizeof(num));
+  return num;
+}
+
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* FLASH_NRF52_H_ */
+