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.
lr1110.cpp
- Committer:
- Wayne Roberts
- Date:
- 2021-02-05
- Revision:
- 1:29294cae9f9a
- Parent:
- 0:987d9022c152
File content as of revision 1:29294cae9f9a:
#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 (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 (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; }