cervin sx1265 operating with transceiver firmware

Dependents:   lr1110_wifi_geolocation_device lr1110_wifi_geolocation_gateway

To use this driver, your LR1110 must be programmed as transceiver (not modem, which is LoRaWAN only).
Visit https://github.com/Lora-net/lr1110_updater_tool to update your LR1110, or change it from modem to transceiver if necessary.

Revision:
0:987d9022c152
Child:
1:29294cae9f9a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lr1110.cpp	Tue May 19 15:30:29 2020 -0700
@@ -0,0 +1,508 @@
+#include "sx12xx.h"
+
+Callback<void()> SX1265::dio9_topHalf;    // low latency ISR context
+
+void SX1265::dio9isr()
+{
+    if (dio9_topHalf)
+        dio9_topHalf.call();
+}
+
+SX1265::SX1265(SPI& _spi, PinName _nss, PinName _busy, PinName _dio9, PinName _nrst, uint32_t di, unsigned tto, uint8_t tv)
+    : spi(_spi), nss(_nss), busy(_busy), dio9(_dio9), nrst(_nrst), default_irqs(di), tcxoStartDelay(tto), tcxoVolts(tv)
+{
+    nss = 1;
+    dio9.mode(PullDown);    // dio9 floats if no interrupts enabled
+
+    t.start();
+
+    if (busy) {
+        hw_reset();
+    }
+
+    dio9.rise(dio9isr);
+    txTimeout = 0;    // default tx-timeout off
+}
+
+
+void SX1265::hw_reset(void)
+{
+    nrst.output();
+    nrst.write(0);
+    ThisThread::sleep_for(2);
+    nrst.write(1);
+    nrst.input();
+    /* measured 211ms to startup (busy-hi during chip startup) */
+    ThisThread::sleep_for(200);
+    while (busy)
+        ThisThread::sleep_for(2);
+
+    enable_default_irqs_();
+}
+
+const char *SX1265::cmdStatus_toString(uint8_t s)
+{
+    switch (s) {
+        case CMD_FAIL: return "CMD_FAIL";
+        case CMD_PERR: return "CMD_PERR";
+        case CMD_OK: return "CMD_OK";
+        case CMD_DAT: return "CMD_DAT";
+        default: return NULL;
+    }
+}
+
+uint32_t SX1265::service(void)
+{
+    uint32_t ret = 0;
+    uint8_t buf[4];
+    uint8_t irqbuf[4];
+
+    inService = true;
+    if (!busy && dio9) {
+        bool try_rx = false;
+        irq_t irq;
+        stat_t stat;
+        stat.word = xfer(OPCODE_GET_STATUS, 4, 4, irqbuf);
+        if (stat.bits.rfu) {
+            inService = false;
+            return -1;
+        }
+        irq.dword = from_big_endian32(irqbuf);
+        ret = irq.dword;
+        if (stat.bits.rfu) {
+            ret = -1;
+            goto done;
+        }
+        if (irq.bits.TxDone) {
+            chipMode = CHIPMODE_NONE;
+            if (chipModeChange)
+                chipModeChange.call();  // might change to Rx
+            if (txDone)
+                txDone.call();
+        }
+        if (irq.bits.RxDone) {
+            uint8_t len, offset;
+            float rssi, snr;
+            int8_t s;
+
+            stat.word = xfer(OPCODE_GET_RX_BUFFER_STATUS, 0, 0, NULL);
+            stat.word = xfer(0x0000, 0, 2, buf);
+            rx_buf_offset = buf[1];
+            len = buf[0];
+            offset = buf[1];
+            
+            buf[0] = offset;
+            buf[1] = len;
+            stat.word = xfer(OPCODE_READ_BUFFER8, 2, 0, buf);
+            stat.word = xfer(0x0000, 0, len, rx_buf);
+            stat.word = xfer(OPCODE_GET_PKT_STATUS, 0, 0, NULL);
+            stat.word = xfer(0x0000, 0, 3, buf);
+            rssi = buf[0] / -2.0;
+            s = buf[1];
+            snr = s / 4.0;
+
+            rxDone(len, rssi, snr);
+            stat.word = xfer(OPCODE_CLEAR_RX_BUFFER, 0, 0, NULL); // yyy
+        }
+        if (irq.bits.CadDone) {
+            if (cadDone)
+                cadDone(irq.bits.CadDetected);
+        }
+        if (irq.bits.CmdErr) {
+            err_opcode = prev_opcode; // culprit opcode
+        }
+        if (irq.bits.Error) {
+            stat.word = xfer(OPCODE_GET_ERRORS, 0, 0, NULL);
+            stat.word = xfer(0x0000, 0, 2, buf);
+            if (stat.bits.cmdStatus == CMD_DAT) {
+                errorStat.word = buf[0];
+                errorStat.word <<= 8;
+                errorStat.word |= buf[1];
+                if (errorStat.bits.hf_xosc_start_) {
+                    buf[0] = tcxoVolts;
+                    to_big_endian32(tcxoStartDelay, buf+1);
+                    xfer(OPCODE_SET_TCXO_MODE, 5, 0, buf);
+                    buf[0] = 0;
+                    xfer(OPCODE_CALIBRATE, 1, 0, buf);
+
+                    if (chipMode == CHIPMODE_RX) {
+                        /* RX start failed due to HF xosc being a tcxo */
+                        try_rx = true;
+                    }
+                }
+                /* ? OPCODE_CLEAR_ERRORS ? */
+            }
+        }
+        if (irq.bits.Timeout) {
+            if (chipMode != CHIPMODE_NONE) {
+                if (timeout)
+                    timeout(chipMode == CHIPMODE_TX);
+            }
+            chipMode = CHIPMODE_NONE;
+            if (chipModeChange)
+                chipModeChange.call();
+        }
+
+        stat.word = xfer(OPCODE_CLEAR_IRQ, 4, 0, irqbuf);
+
+        if (try_rx) {
+            /* RX wasnt started because xosc wasnt started */
+            xfer(OPCODE_SET_RX, 3, 0, rxArgs);
+        }
+    } // ..if (!busy && dio9)
+
+done:
+    inService = false;
+    return ret;
+}
+
+uint16_t SX1265::xfer(uint16_t opcode, uint16_t wlen, uint16_t rlen, uint8_t* ptr)
+{
+    const uint8_t* stopPtr;
+    const uint8_t* wstop;
+    const uint8_t* rstop;
+    const uint8_t nop = 0;
+    uint16_t oc;
+    stat_t ret;
+    unsigned f;
+
+    if (opcode == OPCODE_SET_RX) {
+        const uint8_t* _ptr = ptr;
+        rxArgs[0] = *_ptr++;
+        rxArgs[1] = *_ptr++;
+        rxArgs[2] = *_ptr++;
+    }
+
+    if (sleeping) {
+        nss = 0;
+        while (busy)
+            ;
+        sleeping = false;
+    } else {
+        if (busy) {
+            int startAt = t.read_ms();
+            while (busy) {
+                if (t.read_ms() - startAt > 50) {
+                    ret.bits.rfu = 15;
+                    return ret.word;
+                }
+            }
+        }
+        nss = 0;
+    }
+
+    prev_opcode = this_opcode;
+    this_opcode = opcode;
+
+    oc = opcode;
+    f = oc >> 8;
+    ret.word = spi.write(f);
+    ret.word <<= 8;
+    if (opcode != 0) {
+        /* two byte command sent */
+        ret.word |= spi.write(oc & 0xff);
+    } /* else: response */
+    else
+        ret.word |= 0xff;   // put impossible value for stat2, indicating only stat1 returned
+
+    wstop = ptr + wlen;
+    rstop = ptr + rlen;
+    if (rlen > wlen)
+        stopPtr = rstop;
+    else
+        stopPtr = wstop;
+
+    for (; ptr < stopPtr; ptr++) {
+        if (ptr < wstop && ptr < rstop)
+            *ptr = spi.write(*ptr);
+        else if (ptr < wstop)
+            spi.write(*ptr);
+        else
+            *ptr = spi.write(nop);    // n >= write length: send NOP
+    }
+
+    nss = 1;
+
+    if (opcode == OPCODE_SET_SLEEP ||
+        opcode == OPCODE_SET_TX ||
+        opcode == OPCODE_SET_RX ||
+        opcode == OPCODE_SET_STANDBY ||
+        opcode == OPCODE_SET_FS)
+    {
+        if (opcode == OPCODE_SET_TX)
+            chipMode = CHIPMODE_TX;
+        else if (opcode == OPCODE_SET_RX)
+            chipMode = CHIPMODE_RX;
+        else if (opcode == OPCODE_SET_SLEEP || opcode == OPCODE_SET_STANDBY || opcode == OPCODE_SET_FS) {
+            if (opcode == OPCODE_SET_SLEEP)
+                sleeping = true;
+            chipMode = CHIPMODE_NONE;
+        }
+
+        if (chipModeChange)
+            chipModeChange.call();
+    }
+
+    if (!inService && ret.bits.intActive)
+        service();
+
+    return ret.word;
+}
+
+uint32_t SX1265::from_big_endian32(const uint8_t *in)
+{
+    uint32_t ret;
+    ret = *in++;
+    ret <<= 8;
+    ret |= *in++;
+    ret <<= 8;
+    ret |= *in++;
+    ret <<= 8;
+    ret |= *in;
+    return ret;
+}
+
+void SX1265::to_big_endian16(uint16_t in, uint8_t *out)
+{
+    out[1] = in & 0xff;
+    in >>= 8;
+    out[0] = in & 0xff;
+}
+
+void SX1265::to_big_endian24(uint32_t in, uint8_t *out)
+{
+    out[2] = in & 0xff;
+    in >>= 8;
+    out[1] = in & 0xff;
+    in >>= 8;
+    out[0] = in & 0xff;
+}
+
+void SX1265::to_big_endian32(uint32_t in, uint8_t *out)
+{
+    out[3] = in & 0xff;
+    in >>= 8;
+    out[2] = in & 0xff;
+    in >>= 8;
+    out[1] = in & 0xff;
+    in >>= 8;
+    out[0] = in & 0xff;
+}
+
+int SX1265::memRegRead(uint32_t addr, uint16_t len_dwords, uint32_t *dest)
+{
+    uint8_t buf[5];
+    stat_t stat;
+    while (len_dwords > 0) {
+        unsigned this_len_dwords = len_dwords;
+        if (this_len_dwords > 64)
+            this_len_dwords = 64;
+        buf[4] = this_len_dwords;
+        to_big_endian32(addr, buf);
+        stat.word = xfer(OPCODE_READREGMEM32, 5, 5, buf);
+        if (stat.bits.rfu) {
+            hw_reset();
+            return -1;
+        }
+        unsigned n, len_bytes = this_len_dwords * 4;
+        stat.word = xfer(0x0000, 0, len_bytes, (uint8_t*)dest);
+        if (stat.bits.rfu) {
+            hw_reset();
+            return -1;
+        } else {
+            if (stat.bits.cmdStatus != CMD_DAT) {
+                return -1;
+            }
+            for (n = 0; n < this_len_dwords; n++) {
+                *dest = from_big_endian32((uint8_t*)dest);
+                dest++;
+            }
+        }
+
+        addr += len_bytes;
+        len_dwords -= this_len_dwords;
+    } // ..while (len_dwords > 0)
+
+    return 0;
+} // ..memRegRead()
+
+void SX1265::enable_default_irqs_()
+{
+    /* DIO9 is hi-z when no irqs enabled (floats high) */
+    uint8_t buf[4];
+    to_big_endian32(default_irqs, buf);
+    xfer(OPCODE_SET_DIOIRQPARAMS, 4, 4, buf);
+}
+
+float SX1265::getMHz()
+{
+    uint32_t frf;
+    memRegRead(REG_ADDR_RFFREQ, 1, &frf);
+    return frf / 1048576.0;
+}
+
+uint8_t SX1265::setMHz(float MHz)
+{
+    uint8_t buf[4];
+    to_big_endian32(MHz * 1000000, buf);
+    xfer(OPCODE_SET_RF_FREQ_HZ, 4, 0, buf);
+    return 0;
+}
+
+void SX1265::setPacketType(uint8_t pt)
+{
+    xfer(OPCODE_SET_PACKET_TYPE, 1, 0, &pt);
+}
+
+uint8_t SX1265::getPacketType()
+{
+    uint8_t buf;
+    stat_t stat;
+    xfer(OPCODE_GET_PACKET_TYPE, 0, 0, NULL);
+    stat.word = xfer(0x0000, 0, 1, &buf);
+    if (stat.bits.cmdStatus == CMD_DAT)
+        return buf;
+    else
+        return 0;
+}
+
+void SX1265::start_tx(uint8_t pktLen)
+{
+    uint8_t buf[3];
+    xfer(OPCODE_WRITE_BUFFER8, pktLen, 0, tx_buf);
+    to_big_endian24(txTimeout, buf);
+    xfer(OPCODE_SET_TX, 3, 0, buf);
+}
+
+void SX1265::GetPaConfig(uint8_t *out)
+{
+    txParamsA_t tpa;
+    txParamsB_t tpb;
+    /* PaSel also in 0x00f30088 */
+    memRegRead(REG_ADDR_TX_PARAMS_A, 1, &tpa.dword);
+    memRegRead(REG_ADDR_TX_PARAMS_B, 1, &tpb.dword);
+    out[0] = tpb.bits.PaSel;
+    out[1] = tpa.bits.RegPASupply;
+    out[2] = tpb.bits.PaDutyCycle;
+    out[3] = tpb.bits.PaHPSel;
+}
+
+void SX1265::GetLoRaModulationParameters(uint8_t *out)
+{
+    loraConfig0_t cfg0;
+    memRegRead(REG_ADDR_LORA_CONFIG0, 1, &cfg0.dword);
+
+    out[0] = cfg0.bits.modem_sf;
+    out[1] = cfg0.bits.modem_bw;
+    out[2] = cfg0.bits.coding_rate;
+    out[3] = cfg0.bits.ppm_offset;
+}
+
+void SX1265::GetGfskModulationParameters(uint8_t *out)
+{
+    uint32_t u32;
+    gfskBW_t bw_reg;
+    gfskConfig0_t cfg0;
+    uint8_t bwf;
+    unsigned hz;
+
+    memRegRead(REG_ADDR_GFSK_BITRATE, 1, &u32);
+    hz = GFSK_BITRATE_NUMERATOR / u32;
+    to_big_endian32(hz, out);
+
+    memRegRead(REG_ADDR_GFSK_CFG0, 1, &cfg0.dword);
+    switch (cfg0.bits.bt) {
+        case 0: /* off */ out[4] = GFSK_BT_OFF; break;
+        case 1: /* 0.3 */ out[4] = GFSK_BT_0_3; break;
+        case 2: /* 0.5 */ out[4] = GFSK_BT_0_5; break;
+        case 3: /* 0.7 */ out[4] = GFSK_BT_0_7; break;
+        case 4: /* 1.0 */ out[4] = GFSK_BT_1_0; break;
+    }
+
+    memRegRead(REG_ADDR_GFSK_BWF, 1, &bw_reg.dword);
+    bwf = bw_reg.bits.bwf_hi;
+    bwf <<= 3;
+    bwf |= bw_reg.bits.bwf_lo;
+    out[5] = bwf;
+
+    memRegRead(REG_ADDR_GFSK_FDEV, 1, &u32);
+    hz = (unsigned) ((u32 * FREQ_STEP) - 0.5);
+    to_big_endian32(hz, out+6);
+}
+
+void SX1265::GetLoRaPacketParameters(uint8_t *out)
+{
+    loraConfig0_t cfg0;
+    loraConfigA_t cfgA;
+    loraConfigC_t cfgc;
+    unsigned pl;
+
+    memRegRead(REG_ADDR_LORA_CONFIGC, 1, &cfgc.dword);
+    pl = cfgc.bits.preamble_length;
+    out[1] = pl & 0xff;
+    pl >>= 8;
+    out[0] = pl;
+
+    memRegRead(REG_ADDR_LORA_CONFIG0, 1, &cfg0.dword);
+    out[2] = cfg0.bits.implicit_header;
+    out[3] = cfg0.bits.payload_length;
+    out[4] = cfg0.bits.crc_on;
+
+    memRegRead(REG_ADDR_LORA_CONFIGA, 1, &cfgA.dword);
+    out[5] = cfgA.bits.invertIQ;
+}
+
+void SX1265::GetGfskPacketParameters(uint8_t *out)
+{
+    gfskConfig1_t cfg1;
+    gfskConfig2_t cfg2;
+    gfskConfig3_t cfg3;
+    gfskConfig4_t cfg4;
+    gfskConfig5_t cfg5;
+    uint32_t u32;
+
+    memRegRead(REG_ADDR_GFSK_CFG1, 1, &cfg1.dword);
+
+    u32 = cfg1.bits.preamble_length;
+    out[1] = u32;
+    u32 >>= 8;
+    out[0] = u32;
+
+    memRegRead(REG_ADDR_GFSK_CFG1, 1, &cfg1.dword);
+    if (cfg1.bits.preamble_det_enable)
+        out[2] = cfg1.bits.preamble_det_len;
+    else
+        out[2] = 0;
+
+    memRegRead(REG_ADDR_GFSK_CFG2, 1, &cfg2.dword);
+    out[3] = cfg2.bits.sync_word_length;
+
+    memRegRead(REG_ADDR_GFSK_PAYLOAD_LENGTH_B, 1, &cfg4.dword);
+    out[4] = cfg4.bits.addr_comp;
+
+    memRegRead(REG_ADDR_GFSK_CFG3, 1, &cfg3.dword);
+    out[5] = cfg3.bits.variable_length;
+
+    memRegRead(REG_ADDR_GFSK_PAYLOAD_LENGTH_A, 1, &u32);
+    out[6] = u32 & 0xff;
+
+    memRegRead(REG_ADDR_GFSK_CFG5, 1, &cfg5.dword);
+
+    if (cfg5.bits.crc_off)
+        out[7] |= 1;
+    else
+        out[7] &= ~1;
+
+    if (cfg5.bits.crc_size)
+        out[7] |= 2;
+    else
+        out[7] &= ~2;
+
+    if (cfg5.bits.crc_invert)
+        out[7] |= 4;
+    else
+        out[7] &= ~4;
+
+    out[8] = cfg5.bits.whitening_enable;
+}
+