1

Revision:
0:9c052ff8dd6a
Child:
1:e79b0a55135f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/radio_sx126x.cpp	Thu Jul 05 17:31:54 2018 -0700
@@ -0,0 +1,577 @@
+#include "radio.h"
+#ifdef SX126x_H 
+#include "SPIu.h"
+
+extern RawSerial pc;
+
+LowPowerTimer Radio::lpt;
+volatile us_timestamp_t Radio::irqAt;
+
+#ifdef TARGET_FF_ARDUINO
+    SPIu spi(D11, D12, D13); // mosi, miso, sclk
+                   //spi, nss, busy, dio1
+    SX126x Radio::radio(spi, D7, D3, D5);
+
+    DigitalOut antswPower(D8);
+    AnalogIn xtalSel(A3);
+
+    DigitalIn chipType(A2);
+    #define CHIP_TYPE_SX1262        0
+    #define CHIP_TYPE_SX1261        1
+
+    #define PINNAME_NRST            A0
+
+    #define LED_ON      1
+    #define LED_OFF     0
+    DigitalOut tx_led(A4);
+    DigitalOut rx_led(A5);
+
+    void Radio::chipModeChange()
+    {
+        if (radio.chipMode == CHIPMODE_NONE) {
+            tx_led = LED_OFF;
+            rx_led = LED_OFF;
+        } else if (radio.chipMode == CHIPMODE_TX) {
+            tx_led = LED_ON;
+            rx_led = LED_OFF;
+        } else if (radio.chipMode == CHIPMODE_RX) {
+            tx_led = LED_OFF;
+            rx_led = LED_ON;
+        }
+    }
+#endif /* TARGET_FF_ARDUINO */
+
+const RadioEvents_t* RadioEvents;
+PacketParams_t Radio::pp;
+RadioModems_t Radio::_m_;
+
+#ifdef TARGET_FF_MORPHO
+    DigitalOut pc3(PC_3);   // debug RX indication, for nucleo boards
+#endif /* TARGET_FF_MORPHO */
+
+void Radio::Rx(unsigned timeout)
+{
+    antswPower = 1;
+
+    {
+        uint8_t buf[8];
+        IrqFlags_t irqEnable;
+        irqEnable.word = 0;
+        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
+        radio.xfer(OPCODE_SET_DIO_IRQ_PARAMS, 8, 0, buf);
+    }
+
+#ifdef TARGET_FF_MORPHO
+    pc3 = 1;
+#endif /* TARGET_FF_MORPHO */
+    if (timeout == 0)
+        radio.start_rx(RX_TIMEOUT_CONTINUOUS);
+    else
+        radio.start_rx(timeout * RC_TICKS_PER_US);
+
+    //pc.printf("start_rx %u  busy%u\r\n", timeout, busy.read());
+}
+
+void Radio::Standby()
+{
+    radio.setStandby(STBY_RC);  // STBY_XOSC
+
+    antswPower = 0;
+}
+
+void Radio::Sleep()
+{
+    radio.setSleep(true, false);
+
+    antswPower = 0;
+}
+
+void Radio::set_tx_dbm(int8_t dbm)
+{
+    radio.set_tx_dbm(chipType == CHIP_TYPE_SX1262, dbm);
+}
+
+void Radio::SetTxContinuousWave(unsigned hz, int8_t dbm, unsigned timeout_us)
+{
+    SetChannel(hz);
+    radio.set_tx_dbm(chipType == CHIP_TYPE_SX1262, dbm);
+    radio.xfer(OPCODE_SET_TX_CONTINUOUS, 0, 0, NULL);
+}
+
+uint32_t Radio::Random(void)
+{
+    uint32_t ret;
+
+    radio.start_rx(RX_TIMEOUT_CONTINUOUS);
+
+    ret = radio.readReg(REG_ADDR_RANDOM, 4);
+
+    Standby();
+
+    return ret;
+}
+
+bool Radio::CheckRfFrequency(unsigned hz)
+{
+    return true;
+}
+
+void Radio::SetChannel(unsigned hz)
+{
+    radio.setMHz(hz / 1000000.0);
+}
+
+float Radio::getFrfMHz()
+{
+    return radio.getMHz();
+}
+
+void Radio::LoRaPacketConfig(unsigned preambleLen, bool fixLen, bool crcOn, bool invIQ)
+{
+    if (radio.getPacketType() != PACKET_TYPE_LORA)
+        radio.setPacketType(PACKET_TYPE_LORA);
+
+    pp.lora.PreambleLengthHi = preambleLen >> 8;
+    pp.lora.PreambleLengthLo = preambleLen;
+    pp.lora.HeaderType = fixLen;
+    pp.lora.CRCType = crcOn;
+    pp.lora.InvertIQ = invIQ;
+
+    radio.xfer(OPCODE_SET_PACKET_PARAMS, 6, 0, pp.buf);
+}
+
+void Radio::GFSKModemConfig(unsigned bps, unsigned bw_hz, unsigned fdev_hz)
+{
+    ModulationParams_t mp;
+    uint32_t u32;
+
+    if (radio.getPacketType() != PACKET_TYPE_GFSK)
+        radio.setPacketType(PACKET_TYPE_GFSK);
+
+    u32  = 32 * (XTAL_FREQ_HZ / bps);
+    mp.gfsk.bitrateHi = u32 >> 16; // param1
+    mp.gfsk.bitrateMid = u32 >> 8; // param2
+    mp.gfsk.bitrateLo = u32;       // param3
+    mp.gfsk.PulseShape = GFSK_SHAPE_BT1_0; // param4
+    // param5:
+    if (bw_hz < 5800)
+        mp.gfsk.bandwidth = GFSK_RX_BW_4800;
+    else if (bw_hz < 7300)
+        mp.gfsk.bandwidth = GFSK_RX_BW_5800;
+    else if (bw_hz < 9700)
+        mp.gfsk.bandwidth = GFSK_RX_BW_7300;
+    else if (bw_hz < 11700)
+        mp.gfsk.bandwidth = GFSK_RX_BW_9700;
+    else if (bw_hz < 14600)
+        mp.gfsk.bandwidth = GFSK_RX_BW_11700;
+    else if (bw_hz < 19500)
+        mp.gfsk.bandwidth = GFSK_RX_BW_14600;
+    else if (bw_hz < 23400)
+        mp.gfsk.bandwidth = GFSK_RX_BW_19500;
+    else if (bw_hz < 29300)
+        mp.gfsk.bandwidth = GFSK_RX_BW_23400;
+    else if (bw_hz < 39000)
+        mp.gfsk.bandwidth = GFSK_RX_BW_29300;
+    else if (bw_hz < 46900)
+        mp.gfsk.bandwidth = GFSK_RX_BW_39000;
+    else if (bw_hz < 58600)
+        mp.gfsk.bandwidth = GFSK_RX_BW_46900;
+    else if (bw_hz < 78200)
+        mp.gfsk.bandwidth = GFSK_RX_BW_58600;
+    else if (bw_hz < 93800)
+        mp.gfsk.bandwidth = GFSK_RX_BW_78200;
+    else if (bw_hz < 117300)
+        mp.gfsk.bandwidth = GFSK_RX_BW_93800;
+    else if (bw_hz < 156200)
+        mp.gfsk.bandwidth = GFSK_RX_BW_117300;
+    else if (bw_hz < 187200)
+        mp.gfsk.bandwidth = GFSK_RX_BW_156200;
+    else if (bw_hz < 234300)
+        mp.gfsk.bandwidth = GFSK_RX_BW_187200;
+    else if (bw_hz < 312000)
+        mp.gfsk.bandwidth = GFSK_RX_BW_234300;
+    else if (bw_hz < 373600)
+        mp.gfsk.bandwidth = GFSK_RX_BW_312000;
+    else if (bw_hz < 467000)
+        mp.gfsk.bandwidth = GFSK_RX_BW_373600;
+    else
+        mp.gfsk.bandwidth = GFSK_RX_BW_467000;
+
+    if (fdev_hz > 0) {
+        u32 = fdev_hz / FREQ_STEP;
+        mp.gfsk.fdevHi = u32 >> 16; // param6
+        mp.gfsk.fdevMid = u32 >> 8;    // param7
+        mp.gfsk.fdevLo = u32; // param8
+    }
+
+    radio.xfer(OPCODE_SET_MODULATION_PARAMS, 8, 0, mp.buf);
+}
+
+void Radio::GFSKPacketConfig(unsigned preambleLen, bool fixLen, bool crcOn)
+{
+    if (radio.getPacketType() != PACKET_TYPE_GFSK)
+        radio.setPacketType(PACKET_TYPE_GFSK);
+
+    pp.gfsk.PreambleLengthHi = preambleLen >> 8;
+    pp.gfsk.PreambleLengthLo = preambleLen;
+    pp.gfsk.PreambleDetectorLength = GFSK_PREAMBLE_DETECTOR_LENGTH_16BITS;
+    pp.gfsk.SyncWordLength = 24; // 0xC194C1
+    pp.gfsk.AddrComp = 0;
+    pp.gfsk.PacketType = fixLen;
+    if (crcOn)
+        pp.gfsk.CRCType = GFSK_CRC_2_BYTE;
+    else
+        pp.gfsk.CRCType = GFSK_CRC_OFF;
+
+    //TODO pp.gfsk.PayloadLength = ;
+
+    radio.xfer(OPCODE_SET_PACKET_PARAMS, 8, 0, pp.buf);
+}
+
+void Radio::LoRaModemConfig(unsigned bwKHz, uint8_t sf, uint8_t cr)
+{
+    ModulationParams_t mp;
+    float khz, sp;
+
+    if (radio.getPacketType() != PACKET_TYPE_LORA)
+        radio.setPacketType(PACKET_TYPE_LORA);
+
+    if (bwKHz > 250) {
+        mp.lora.bandwidth = LORA_BW_500;
+        khz = 500;
+    } else if (bwKHz > 125) {
+        mp.lora.bandwidth = LORA_BW_250;
+        khz = 250;
+    } else if (bwKHz > 63) {
+        mp.lora.bandwidth = LORA_BW_125;
+        khz = 125;
+    } else if (bwKHz > 42) {
+        mp.lora.bandwidth = LORA_BW_62;
+        khz = 62.5;
+    } else if (bwKHz > 32) {
+        mp.lora.bandwidth = LORA_BW_41;
+        khz = 41.67;
+    } else if (bwKHz > 21) {
+        mp.lora.bandwidth = LORA_BW_31;
+        khz = 31.25;
+    } else if (bwKHz > 16) {
+        mp.lora.bandwidth = LORA_BW_20;
+        khz = 20.83;
+    } else if (bwKHz > 11) {
+        mp.lora.bandwidth = LORA_BW_15;
+        khz = 15.625;
+    } else if (bwKHz > 11) {
+        mp.lora.bandwidth = LORA_BW_10;
+        khz = 10.42;
+    } else {
+        mp.lora.bandwidth = LORA_BW_7;
+        khz = 7.81;
+    }
+
+    mp.lora.spreadingFactor = sf;
+    mp.lora.codingRate = cr;
+
+    sp = (1 << mp.lora.spreadingFactor) / khz;
+    /* TCXO dependent */
+    if (sp > 16)
+        mp.lora.LowDatarateOptimize = 1; // param4
+    else
+        mp.lora.LowDatarateOptimize = 0; // param4
+
+    radio.xfer(OPCODE_SET_MODULATION_PARAMS, 4, 0, mp.buf);
+
+}
+
+void Radio::SetLoRaSymbolTimeout(uint8_t symbs)
+{
+    if (radio.getPacketType() != PACKET_TYPE_LORA)
+        radio.setPacketType(PACKET_TYPE_LORA);
+
+    radio.xfer(OPCODE_SET_LORA_SYMBOL_TIMEOUT, 1, 0, &symbs);
+}
+
+int Radio::Send(uint8_t size, timestamp_t maxListenTime, timestamp_t channelFreeTime, int rssiThresh)
+{
+    uint8_t buf[8];
+    uint8_t pktType = radio.getPacketType();
+
+    buf[0] = 0; // TX base address
+    buf[1] = 0; // RX base address
+    radio.xfer(OPCODE_SET_BUFFER_BASE_ADDR, 2, 0, buf);
+
+    if (pktType == PACKET_TYPE_GFSK) {
+        pp.gfsk.PayloadLength = size;
+        radio.xfer(OPCODE_SET_PACKET_PARAMS, 8, 0, pp.buf);
+    } else if (pktType == PACKET_TYPE_LORA) {
+        pp.lora.PayloadLength = size;
+        radio.xfer(OPCODE_SET_PACKET_PARAMS, 6, 0, pp.buf);
+    }
+
+    {
+        IrqFlags_t irqEnable;
+        irqEnable.word = 0;
+        irqEnable.bits.TxDone = 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
+        radio.xfer(OPCODE_SET_DIO_IRQ_PARAMS, 8, 0, buf);
+    }
+
+    antswPower = 1;
+
+    if (maxListenTime > 0) {
+        int rssi;
+        us_timestamp_t startAt, chFreeAt, now;
+        uint8_t symbs = 0;
+
+        radio.xfer(OPCODE_SET_LORA_SYMBOL_TIMEOUT, 1, 0, &symbs);
+
+        radio.start_rx(RX_TIMEOUT_CONTINUOUS);
+        startAt = lpt.read_us();
+Lstart:
+        do {
+            now = lpt.read_us();
+            if ((now - startAt) > maxListenTime) {
+                return -1;
+            }
+            radio.xfer(OPCODE_GET_RSSIINST, 0, 2, buf);
+            rssi = buf[1] / -2;
+        } while (rssi > rssiThresh);
+        chFreeAt = lpt.read_us();
+        do {
+            now = lpt.read_us();
+            radio.xfer(OPCODE_GET_RSSIINST, 0, 2, buf);
+            rssi = buf[1] / -2;
+            if (rssi > rssiThresh) {
+                goto Lstart;
+            }
+        } while ((now - chFreeAt) < channelFreeTime);
+    } 
+
+    radio.start_tx(size);
+
+    return 0;
+} // ..Send()
+
+void Radio::SetRxMaxPayloadLength(RadioModems_t modem, uint8_t max)
+{
+    uint8_t buf[8];
+
+    if (modem == MODEM_FSK) {
+        pp.gfsk.PayloadLength = max;
+        memcpy(buf, pp.buf, 8);
+        radio.xfer(OPCODE_SET_PACKET_PARAMS, 8, 0, buf);
+    } else if (modem == MODEM_LORA) {
+        pp.lora.PayloadLength = max;
+        memcpy(buf, pp.buf, 6);
+        radio.xfer(OPCODE_SET_PACKET_PARAMS, 6, 0, buf);
+    }
+}
+
+void Radio::dio1_top_half()
+{
+    irqAt = lpt.read_us();
+
+    if (radio.chipMode == CHIPMODE_TX) {
+        /* TxDone handling requires low latency */
+        if (RadioEvents->TxDone_topHalf) {
+            RadioEvents->TxDone_topHalf();
+        } 
+    } else {
+#ifdef TARGET_FF_MORPHO
+        pc3 = 0;
+#endif /* TARGET_FF_MORPHO */
+    }
+}
+
+void Radio::timeout_callback(bool tx)
+{
+    if (!tx) {
+        if (RadioEvents->RxTimeout)
+            RadioEvents->RxTimeout();
+#ifdef TARGET_FF_MORPHO
+        pc3 = 0;
+#endif /* TARGET_FF_MORPHO */
+    } // else TODO tx timeout
+}
+
+void Radio::rx_done(uint8_t size, float rssi, float snr)
+{
+    RadioEvents->RxDone(size, rssi, snr);
+}
+
+void Radio::txDoneBottom()
+{
+    if (RadioEvents->TxDone_botHalf)
+        RadioEvents->TxDone_botHalf();
+}
+
+void Radio::Init(const RadioEvents_t* e)
+{
+    radio.txDone = txDoneBottom;
+    radio.rxDone = rx_done;
+    radio.timeout = timeout_callback;
+    radio.chipModeChange = chipModeChange;
+    radio.dio1_topHalf = dio1_top_half;
+
+    RadioEvents = e;
+    lpt.start();
+
+    radio.SetDIO2AsRfSwitchCtrl(1);
+}
+
+void Radio::service()
+{
+    radio.service();
+}
+
+void Radio::SetPublicNetwork(bool en)
+{
+    uint16_t ppg;
+
+    if (en)
+        ppg = 0x3444;
+    else
+        ppg = 0x1424;
+
+    radio.writeReg(REG_ADDR_LORA_SYNC, ppg, 2);
+}
+
+uint32_t Radio::lora_toa_us( uint8_t pktLen )
+{
+    double bwKHz;
+    unsigned preambleLen;
+    ModulationParams_t mp;
+
+    {
+        loraConfig1_t conf1;
+        conf1.octet = radio.readReg(REG_ADDR_LORA_CONFIG1, 1);
+        mp.lora.LowDatarateOptimize = conf1.bits.ppm_offset;
+        pp.lora.HeaderType = conf1.bits.implicit_header;
+        pp.lora.InvertIQ = conf1.bits.rx_invert_iq;
+        mp.lora.codingRate = conf1.bits.tx_coding_rate;
+    }
+
+    {
+        loraConfig2_t conf2;
+        conf2.octet = radio.readReg(REG_ADDR_LORA_CONFIG2, 1);
+        pp.lora.CRCType = conf2.bits.tx_payload_crc16_en;
+    }
+
+
+    {
+        uint32_t val;
+        val = radio.readReg(REG_ADDR_LORA_PREAMBLE_SYMBNB, 2);
+        pp.lora.PreambleLengthHi = val >> 8;
+        pp.lora.PreambleLengthLo = val;
+    }
+
+    preambleLen = (pp.lora.PreambleLengthHi << 8) + pp.lora.PreambleLengthLo;
+
+    {
+        loraConfig0_t conf0;
+        conf0.octet = radio.readReg(REG_ADDR_LORA_CONFIG0, 1);
+        mp.lora.spreadingFactor = conf0.bits.modem_sf;
+        mp.lora.bandwidth = conf0.bits.modem_bw;
+    }
+
+    switch (mp.lora.bandwidth) {
+        case LORA_BW_7: bwKHz = 7.81; break;
+        case LORA_BW_10: bwKHz = 10.42; break;
+        case LORA_BW_15: bwKHz = 15.625; break;
+        case LORA_BW_20: bwKHz = 20.83; break;
+        case LORA_BW_31: bwKHz = 31.25; break;
+        case LORA_BW_41: bwKHz = 41.67; break;
+        case LORA_BW_62: bwKHz = 62.5; break;
+        case LORA_BW_125: bwKHz = 125; break;
+        case LORA_BW_250: bwKHz = 250; break;
+        case LORA_BW_500: bwKHz = 500; break;
+        default: bwKHz = 0; break;
+    }
+
+    // Symbol rate : time for one symbol (secs)
+    double rs = bwKHz / ( 1 << mp.lora.spreadingFactor );
+    double ts = 1 / rs;
+    // time of preamble
+    double tPreamble = ( preambleLen + 4.25 ) * ts;
+    // Symbol length of payload and time
+    
+    double tmp = ceil( ( 8 * pktLen - 4 * mp.lora.spreadingFactor +
+                         28 + 16 * pp.lora.CRCType -
+                         ( pp.lora.HeaderType ? 20 : 0 ) ) /
+                         ( double )( 4 * ( mp.lora.spreadingFactor -
+                         ( ( mp.lora.LowDatarateOptimize > 0 ) ? 2 : 0 ) ) ) ) *
+                         ( mp.lora.codingRate + 4 );
+    double nPayload = 8 + ( ( tmp > 0 ) ? tmp : 0 );
+    double tPayload = nPayload * ts;
+    // Time on air
+    double tOnAir = tPreamble + tPayload;
+    // return microseconds
+    return floor( tOnAir * 1000 + 0.999 );
+}
+
+#if 0
+void Radio::PrintStatus()
+{
+/*    uint8_t buf[4];
+    status_t status;
+    IrqFlags_t irqFlags;
+    radio.xfer(OPCODE_GET_IRQ_STATUS, 0, 3, buf);
+    irqFlags.word = buf[1] << 8;
+    irqFlags.word |= buf[2];
+
+    printf("dio1:%u  irqFlags:%04x\r\n", radio.getDIO1(), irqFlags.word);
+    radio.xfer(OPCODE_GET_STATUS, 0, 1, &status.octet);
+    radio.PrintChipStatus(status);*/
+    {
+        loraConfig1_t conf1;
+        conf1.octet = radio.readReg(REG_ADDR_LORA_CONFIG1, 1);
+        printf("ldro%u %s %s cr%u\r\n",
+            conf1.bits.ppm_offset,
+            conf1.bits.implicit_header ? "fixed" : "var",
+            conf1.bits.rx_invert_iq ? "inv" : "std",
+            conf1.bits.tx_coding_rate
+        );
+    }
+
+    {
+        loraConfig2_t conf2;
+        conf2.octet = radio.readReg(REG_ADDR_LORA_CONFIG2, 1);
+        printf("crc16en:%u ", conf2.bits.tx_payload_crc16_en);
+    }
+
+
+    {
+        uint32_t val;
+        val = radio.readReg(REG_ADDR_LORA_PREAMBLE_SYMBNB, 2);
+        printf("prelen %lu ", val);
+    }
+
+    {
+        loraConfig0_t conf0;
+        conf0.octet = radio.readReg(REG_ADDR_LORA_CONFIG0, 1);
+        printf("sf%u, bw%u ", conf0.bits.modem_sf, conf0.bits.modem_bw);
+    }
+
+    printf("%.3fMHz\r\n", radio.getMHz());
+}
+#endif /* if 0 */
+
+#endif /* ..SX126x_H */