wayne roberts / sx126x

Dependents:   alarm_slave iq_sx126x sx126x_simple_TX_shield_2020a sx126x_simple_RX_shield_2020a ... more

Files at this revision

API Documentation at this revision

Comitter:
Wayne Roberts
Date:
Wed May 16 11:20:24 2018 -0700
Child:
1:497af0bd9e53
Commit message:
initial commit

Changed in this revision

sx126x.cpp Show annotated file Show diff for this revision Revisions of this file
sx126x.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sx126x.cpp	Wed May 16 11:20:24 2018 -0700
@@ -0,0 +1,324 @@
+#include "sx126x.h"
+
+Callback<void()> SX126x::dio1_topHalf;    // low latency ISR context
+
+void SX126x::dio1isr()
+{
+    if (dio1_topHalf)
+        dio1_topHalf.call();
+}
+
+SX126x::SX126x(SPI& _spi, PinName _nss, PinName _busy, PinName _dio1)
+    : spi(_spi), nss(_nss), busy(_busy), dio1(_dio1)
+{
+    uint8_t buf[8];
+    IrqFlags_t irqEnable;
+
+    irqEnable.word = 0;
+    irqEnable.bits.TxDone = 1;
+    irqEnable.bits.RxDone = 1;
+    irqEnable.bits.Timeout = 1;
+
+    buf[0] = irqEnable.word >> 8;    // enable bits
+    buf[1] = irqEnable.word; // enable bits
+    buf[2] = irqEnable.word >> 8;     // dio1
+    buf[3] = irqEnable.word;  // dio1
+    buf[4] = 0; // dio2
+    buf[5] = 0; // dio2
+    buf[6] = 0; // dio3
+    buf[7] = 0; // dio3
+    xfer(OPCODE_SET_DIO_IRQ_PARAMS, 8, buf);
+
+    dio1.rise(dio1isr);
+}
+
+void SX126x::PrintChipStatus(status_t status)
+{
+    printf("%02x cmdStatus:", status.octet);
+    switch (status.bits.cmdStatus) {
+        case 0: printf("Reserved"); break;
+        case 1: printf("RFU"); break;
+        case 2: printf("dataAvail"); break;
+        case 3: printf("cmdTimeout"); break;
+        case 4: printf("cmdError"); break;
+        case 5: printf("execFail"); break;
+        case 6: printf("cmdTxDone"); break;
+    }
+    printf(" chipMode:");
+    switch (status.bits.chipMode) {
+        case 0: printf("Unused"); break;
+        case 1: printf("RFU"); break;
+        case 2: printf("STBY_RC"); break;
+        case 3: printf("STBY_XOSC"); break;
+        case 4: printf("FS"); break;
+        case 5: printf("RX"); break;
+        case 6: printf("TX"); break;
+    }
+    printf("\r\n");
+}
+
+void SX126x::service()
+{
+    IrqFlags_t irqFlags, clearIrqFlags;
+    uint8_t buf[4];
+
+    if (busy)
+        return;
+
+    while (dio1) {
+        xfer(OPCODE_GET_IRQ_STATUS, 3, buf);
+        irqFlags.word = buf[1] << 8;
+        irqFlags.word |= buf[2];
+        clearIrqFlags.word = 0;
+        if (irqFlags.bits.TxDone) {
+            if (txDone)
+                txDone.call();
+            clearIrqFlags.bits.TxDone = 1;
+            txing = false;
+        }
+        if (irqFlags.bits.RxDone) {
+            if (rxDone) {
+                uint8_t len;
+                float snr, rssi;
+                int8_t s;
+                xfer(OPCODE_GET_RX_BUFFER_STATUS, 3, buf);
+                len = buf[1];
+                ReadBuffer(len);
+                xfer(OPCODE_GET_PACKET_STATUS, 4, buf);
+                rssi = -buf[1] / 2.0;   // TODO FSK
+                s = buf[2];
+                snr = s / 4.0;
+                rxDone(len, rssi, snr);
+            }
+            clearIrqFlags.bits.RxDone = 1;
+        }
+        if (irqFlags.bits.Timeout) {
+            if (timeout)
+                timeout(txing);
+            txing = false;
+            clearIrqFlags.bits.Timeout = 1;
+        }
+
+        if (clearIrqFlags.word != 0) {
+            buf[0] = clearIrqFlags.word >> 8;
+            buf[1] = (uint8_t)clearIrqFlags.word;
+            xfer(OPCODE_CLEAR_IRQ_STATUS, 2, buf);
+        }
+
+    } // ...while (dio1)
+
+} // ..service()
+
+void SX126x::xfer(uint8_t opcode, uint8_t len, uint8_t* ptr)
+{
+    if (sleeping) {
+        nss = 0;
+        while (busy)
+            ;
+        sleeping = false;
+    } else {
+        while (busy)
+            ;
+        nss = 0;
+    }
+
+    spi.write(opcode);
+    while (len > 0) {
+        *ptr = spi.write(*ptr);
+        len--;
+        ptr++;
+    }
+
+    nss = 1;
+
+    if (opcode == OPCODE_SET_SLEEP)
+        sleeping = true;
+}
+
+void SX126x::start_tx(uint8_t pktLen)
+{
+    uint8_t buf[8];
+
+    {
+        uint8_t i;
+
+        while (busy)
+            ;
+
+        nss = 0;
+        spi.write(OPCODE_WRITE_BUFFER);
+        spi.write(0);   // offset
+        i = 0;
+        for (i = 0; i < pktLen; i++) {
+            spi.write(tx_buf[i]);
+        }
+        nss = 1;
+    }
+
+    buf[0] = 0x40;
+    buf[1] = 0x00;
+    buf[2] = 0x00;
+    xfer(OPCODE_SET_TX, 3, buf);
+    txing = true;
+}
+
+void SX126x::start_rx(unsigned timeout)
+{
+    uint8_t buf[8];
+
+    buf[0] = timeout >> 16;
+    buf[1] = timeout >> 8;
+    buf[2] = timeout;
+    xfer(OPCODE_SET_RX, 3, buf);
+}
+
+#define MHZ_TO_FRF      1048576 // = (1<<25) / Fxtal_MHz
+uint8_t SX126x::setMHz(float MHz)
+{
+    unsigned frf = MHz * MHZ_TO_FRF;
+    uint8_t buf[4];
+
+    buf[0] = frf >> 24;
+    buf[1] = frf >> 16;
+    buf[2] = frf >> 8;
+    buf[3] = frf;
+    xfer(OPCODE_SET_RF_FREQUENCY, 4, buf);
+    return buf[3];
+}
+
+void SX126x::setPacketType(uint8_t type)
+{
+    xfer(OPCODE_SET_PACKET_TYPE, 1, &type);
+}
+
+void SX126x::SetDIO2AsRfSwitchCtrl(uint8_t en)
+{
+    xfer(OPCODE_SET_DIO2_AS_RFSWITCH, 1, &en);
+}
+
+void SX126x::ReadBuffer(uint8_t size)
+{
+    unsigned i;
+    while (busy)
+        ;
+
+    nss = 0;
+
+    spi.write(OPCODE_READ_BUFFER);
+    spi.write(0);   // offset
+    spi.write(0);   // NOP
+    i = 0;
+    for (i = 0; i < size; i++) {
+        rx_buf[i] = spi.write(0);
+    }
+
+    nss = 1;
+}
+
+void SX126x::set_tx_dbm(bool is1262, int8_t dbm)
+{
+    uint8_t buf[4];
+    // use OCP default
+
+    printf("set_tx_dbm(%d) ", dbm);
+    buf[3] = 1;
+    if (is1262) {
+        printf("sx1262 ");
+        buf[0] = 4;
+        buf[1] = 7;
+        buf[2] = 0;
+
+        if (dbm > 22)
+            dbm = 22;
+        else if (dbm < -3)
+            dbm = -3;
+    } else {
+        printf("sx1261 ");
+        if (dbm == 15)
+            buf[0] = 6;
+        else
+            buf[0] = 4;
+        buf[1] = 0;
+        buf[2] = 1;
+
+        if (dbm > 14)
+            dbm = 14;
+        else if (dbm < -3)
+            dbm = -3;
+    }
+    xfer(OPCODE_SET_PA_CONFIG, 4, buf);
+
+    if (is1262 && dbm > 18) {
+        /* OCP is set by chip whenever SetPaConfig() is called */
+        writeReg(REG_ADDR_OCP, 0x38, 1);
+    }
+
+    // SetTxParams
+    buf[0] = dbm;
+    //if (opt == 0) txco
+    buf[1] = SET_RAMP_200U;
+    xfer(OPCODE_SET_TX_PARAMS, 2, buf);
+}
+
+void SX126x::writeReg(uint16_t addr, uint32_t data, uint8_t len)
+{
+    uint8_t buf[6];
+    uint8_t n;
+    buf[0] = addr >> 8;
+    buf[1] = (uint8_t)addr;
+    for (n = len; n > 0; n--) {
+        //printf("data:%02x to buf[%u]\r\n", (uint8_t)data, n+1);
+        buf[n+1] = (uint8_t)data;
+        data >>= 8;
+    }
+    //printf("write reg %04x: %02x %02x %02x %02x\r\n", addr, buf[0], buf[1], buf[2], buf[3]);
+    xfer(OPCODE_WRITE_REGISTER, 2+len, buf);
+    //printf("write status %02x %02x %02x %02x\r\n", buf[0], buf[1], buf[2], buf[3]);
+}
+
+void SX126x::setStandby(stby_t stby)
+{
+    uint8_t octet = stby;
+    xfer(OPCODE_SET_STANDBY, 1, &octet);
+}
+
+void SX126x::setSleep(bool warmStart, bool rtcWakeup)
+{
+    sleepConfig_t sc;
+
+    sc.octet = 0;
+    sc.bits.rtcWakeup = rtcWakeup;
+    sc.bits.warmStart = warmStart;
+    xfer(OPCODE_SET_SLEEP, 1, &sc.octet);
+}
+
+void SX126x::hw_reset(PinName pin)
+{
+    DigitalInOut nrst(pin);
+    nrst.output();
+    nrst = 0;
+    wait_us(100);
+    nrst = 1;
+    nrst.mode(PullUp);
+    nrst.input();
+
+    while (busy)
+        ;
+}
+
+uint32_t SX126x::readReg(uint16_t addr, uint8_t len)
+{
+    uint32_t ret = 0;
+    unsigned i;
+
+    uint8_t buf[7];
+    buf[0] = addr >> 8;
+    buf[1] = (uint8_t)addr;
+    xfer(OPCODE_READ_REGISTER, 3+len, buf);
+    for (i = 0; i < len; i++) {
+        ret <<= 8;
+        ret |= buf[i+3];
+    }
+    return ret;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sx126x.h	Wed May 16 11:20:24 2018 -0700
@@ -0,0 +1,218 @@
+#include "mbed.h"
+
+#define RC_TICKS_PER_MS         0.015625    /* 64KHz */
+
+/***************************************************************/
+#define OPCODE_CLEAR_IRQ_STATUS         0x02
+#define OPCODE_CLEAR_DEVICE_ERRORS      0x07
+#define OPCODE_SET_DIO_IRQ_PARAMS       0x08
+#define OPCODE_WRITE_REGISTER           0x0d
+#define OPCODE_WRITE_BUFFER             0x0e
+#define OPCODE_GET_PACKET_TYPE          0x11
+#define OPCODE_GET_IRQ_STATUS           0x12
+#define OPCODE_GET_RX_BUFFER_STATUS     0x13
+#define OPCODE_GET_PACKET_STATUS        0x14
+#define OPCODE_GET_RSSIINST             0x15
+#define OPCODE_GET_DEVICE_ERRORS        0x17
+#define OPCODE_READ_REGISTER            0x1d
+#define OPCODE_READ_BUFFER              0x1e
+#define OPCODE_SET_STANDBY              0x80
+#define OPCODE_SET_RX                   0x82
+#define OPCODE_SET_TX                   0x83
+#define OPCODE_SET_SLEEP                0x84
+#define OPCODE_SET_RF_FREQUENCY         0x86
+#define OPCODE_CALIBRATE                0x89
+#define OPCODE_SET_PACKET_TYPE          0x8a
+#define OPCODE_SET_MODULATION_PARAMS    0x8b
+#define OPCODE_SET_PACKET_PARAMS        0x8c
+#define OPCODE_SET_TX_PARAMS            0x8e
+#define OPCODE_SET_BUFFER_BASE_ADDR     0x8f
+#define OPCODE_SET_PA_CONFIG            0x95
+#define OPCODE_SET_REGULATOR_MODE       0x96
+#define OPCODE_SET_DIO3_AS_TCXO_CTRL    0x97
+#define OPCODE_SET_DIO2_AS_RFSWITCH     0x9d
+#define OPCODE_SET_LORA_SYMBOL_TIMEOUT  0xa0
+#define OPCODE_GET_STATUS               0xc0
+#define OPCODE_SET_TX_CONTINUOUS        0xd1
+/***************************************************************/
+#define PACKET_TYPE_GFSK    1
+#define PACKET_TYPE_LORA    1
+
+#define HEADER_TYPE_VARIABLE_LENGTH     0
+#define HEADER_TYPE_FIXED_LENGTH        1
+
+#define CRC_OFF                         0
+#define CRC_ON                          0
+
+#define STANDARD_IQ                     0
+#define INVERTED_IQ                     1
+
+/* direct register access */
+#define REG_ADDR_LORA_CONFIG0    0x0703 // 8bit  bw/sf
+#define REG_ADDR_LORA_IRQ_MASK   0x070a // 24bit
+#define REG_ADDR_LORA_SYNC       0x0740 // config22, config23: frame sync peak position
+#define REG_ADDR_RANDOM          0x0819
+#define REG_ADDR_OCP             0x08e7
+#define REG_ADDR_
+
+#define SET_RAMP_10U        0x00
+#define SET_RAMP_20U        0x01
+#define SET_RAMP_40U        0x02
+#define SET_RAMP_80U        0x03
+#define SET_RAMP_200U       0x04
+#define SET_RAMP_800U       0x05
+#define SET_RAMP_1700U      0x06
+#define SET_RAMP_3400U      0x07
+
+typedef union {
+    struct {
+        uint8_t rtcWakeup    : 1;    // 0
+        uint8_t rfu          : 1;    // 1
+        uint8_t warmStart    : 1;    // 2
+    } bits;
+    uint8_t octet;
+} sleepConfig_t;
+
+typedef union {
+    struct {
+        uint8_t PreambleLengthHi;   // param1
+        uint8_t PreambleLengthLo;   // param2
+        uint8_t HeaderType;         // param3
+        uint8_t PayloadLength;      // param4
+        uint8_t CRCType;            // param5
+        uint8_t InvertIQ;           // param6
+        uint8_t unused[2];
+    } lora;
+    struct {
+        uint8_t PreambleLengthHi;       // param1
+        uint8_t PreambleLengthLo;       // param2
+        uint8_t PreambleDetectorLength; // param3
+        uint8_t SyncWordLength;         // param4
+        uint8_t AddrComp;               // param5
+        uint8_t PacketType;             // param6
+        uint8_t PayloadLength;          // param7
+        uint8_t CRCType;                // param8
+    } gfsk;
+    uint8_t buf[8];
+} PacketParams_t;
+
+
+#define LORA_BW_7           0x00 //( .81 kHz real
+#define LORA_BW_10          0x08 // 10.42 kHz real
+#define LORA_BW_15          0x01 // 15.63 kHz real
+#define LORA_BW_20          0x09 // 20.83 kHz real
+#define LORA_BW_31          0x02 // 31.25 kHz real
+#define LORA_BW_41          0x0A // 41.67 kHz real
+#define LORA_BW_62          0x03 // 62.50 kHz real
+#define LORA_BW_125         0x04 // 125 kHz real
+#define LORA_BW_250         0x05 // 250 kHz real
+#define LORA_BW_500         0x06 // 500 kHz real
+
+#define LORA_CR_4_5         1
+#define LORA_CR_4_6         2
+#define LORA_CR_4_7         3
+#define LORA_CR_4_8         4
+
+typedef enum {
+    STBY_RC = 0,
+    STBY_XOSC
+} stby_t;
+
+typedef union {
+    struct {
+        uint8_t spreadingFactor; // param1
+        uint8_t bandwidth; // param2
+        uint8_t codingRate; // param3
+        uint8_t LowDatarateOptimize; // param4
+    } lora;
+    struct {
+        uint8_t bitrateHi;  // param1
+        uint8_t bitrateMid; // param2
+        uint8_t bitrateLo;  // param3
+        uint8_t PulseShape; // param4
+        uint8_t bandwith;   // param5
+        uint8_t fdevHi; // param6
+        uint8_t fdevMid;    // param7
+        uint8_t fdevLo; // param8
+    } gfsk;
+    uint8_t buf[8];
+} ModulationParams_t;
+
+typedef union {
+    struct {    // 
+        uint8_t TxDone           : 1;    // 0
+        uint8_t RxDone           : 1;    // 1
+        uint8_t PreambleDetected : 1;    // 2
+        uint8_t SyncWordValid    : 1;    // 3
+        uint8_t HeaderValid      : 1;    // 4
+        uint8_t HeaderErr        : 1;    // 5
+        uint8_t CrCerr           : 1;    // 6
+        uint8_t CadDone          : 1;    // 7
+        uint8_t CadDetected      : 1;    // 8
+        uint8_t Timeout          : 1;    // 9
+    } bits;
+    uint16_t word;
+} IrqFlags_t;
+
+typedef union {
+    struct {    // 
+        uint8_t _reserved    : 1;    // 0
+        uint8_t cmdStatus    : 3;    // 1,2,3
+        uint8_t chipMode     : 3;    // 4,5,6
+        uint8_t reserved_    : 1;    // 7
+    } bits;
+    uint8_t octet;
+} status_t;
+
+class SX126x {
+    public:
+        SX126x(SPI&, PinName nss, PinName busy, PinName dio1);
+
+
+        void hw_reset(PinName nrst);
+        void xfer(uint8_t opcode, uint8_t len, uint8_t* buf);
+        void setPacketType(uint8_t);
+        uint8_t setMHz(float);
+
+        /* start_tx and start_rx assumes DIO1 is connected, and only pin used to generate radio interrupt */
+        void start_tx(uint8_t pktLen);  // tx_buf must be filled prior to calling
+
+#define RX_TIMEOUT_SINGLE         0x000000
+#define RX_TIMEOUT_CONTINUOUS     0xffffff
+        void start_rx(unsigned);
+
+        void ReadBuffer(uint8_t size);
+        void SetDIO2AsRfSwitchCtrl(uint8_t);
+        void set_tx_dbm(bool is1262, int8_t dbm);
+        uint32_t readReg(uint16_t addr, uint8_t len);
+        void writeReg(uint16_t addr, uint32_t data, uint8_t len);
+        void setStandby(stby_t);
+        void setSleep(bool warmStart, bool rtcWakeup);
+
+        static Callback<void()> dio1_topHalf;    // low latency ISR context
+        void service(void);
+        Callback<void()> txDone; // user context
+        void (*rxDone)(uint8_t size, float rssi, float snr); // user context
+        void (*timeout)(bool tx); // user context
+
+        //! RF transmit packet buffer
+        uint8_t tx_buf[256];    // lora fifo size
+        
+        //! RF receive packet buffer
+        uint8_t rx_buf[256];    // lora fifo size
+
+        /** Test if dio1 pin is asserted 
+         */
+        inline bool getDIO1(void) { return dio1.read(); }
+        void PrintChipStatus(status_t);
+
+    private:
+        SPI& spi;
+        DigitalOut nss;
+        DigitalIn busy;
+        InterruptIn dio1;
+        static void dio1isr(void);
+        bool sleeping;
+        bool txing;
+};
+