wayne roberts / lorawan1v1

Dependencies:   sx12xx_hal

Dependents:   LoRaWAN-SanJose_Bootcamp LoRaWAN-grove-cayenne LoRaWAN-classC-demo LoRaWAN-grove-cayenne ... more

radio/radio_sx126x.cpp

Committer:
Wayne Roberts
Date:
2018-05-23
Revision:
8:5a5ea7cc946f
Child:
9:fe8e08792ae9

File content as of revision 8:5a5ea7cc946f:

#include "radio.h"
#ifdef SX126x_H 
#include "board.h"
#include "SPIu.h"

LowPowerTimer Radio::lpt;
volatile us_timestamp_t dio1at;

#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
#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, 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);
}

void Radio::Standby()
{
    radio.setStandby(STBY_RC);  // STBY_XOSC

    antswPower = 0;
}

void Radio::Sleep()
{
    radio.setSleep(true, false);

    antswPower = 0;
}

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, 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);
}

void Radio::SetRxConfig(
    RadioModems_t modem,
    uint32_t bandwidth,
    uint32_t datarate,
    uint8_t coderate,
    uint32_t bandwidthAfc,
    uint16_t preambleLen,
    uint16_t symbTimeout,
    bool fixLen,
    uint8_t payloadLen,
    bool crcOn,
    bool iqInverted)
{
    uint8_t buf[8];

    if (modem == MODEM_FSK)
        buf[0] = PACKET_TYPE_GFSK;
    else
        buf[0] = PACKET_TYPE_LORA;

    radio.xfer(OPCODE_SET_PACKET_TYPE, 1, buf);

    buf[0] = 0; // TX base address
    buf[1] = 0; // RX base address
    radio.xfer(OPCODE_SET_BUFFER_BASE_ADDR, 2, buf);

    if (modem == MODEM_FSK) {
        FSKConfig(bandwidth, datarate, 0, preambleLen, fixLen, crcOn);

        pp.gfsk.PayloadLength = payloadLen;

        memcpy(buf, pp.buf, 8);
        radio.xfer(OPCODE_SET_PACKET_PARAMS, 8, buf);
    } else if (modem == MODEM_LORA) {
        LoRaConfig(bandwidth, datarate, coderate, preambleLen, fixLen, crcOn, iqInverted);

        pp.lora.PayloadLength = payloadLen;

        memcpy(buf, pp.buf, 6);
        radio.xfer(OPCODE_SET_PACKET_PARAMS, 6, buf);

        buf[0] = symbTimeout;
        radio.xfer(OPCODE_SET_LORA_SYMBOL_TIMEOUT, 1, buf);
    }

    {
        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, buf);
    }

} // ..SetRxConfig()

void Radio::FSKConfig(
    uint32_t bandwidth,
    uint32_t datarate,
    uint32_t fdev,
    uint32_t preambleLen,
    bool fixLen,
    bool crcOn)
{
    ModulationParams_t mp;
    uint32_t u32;

    u32  = 32 * (XTAL_FREQ / datarate);
    mp.gfsk.bitrateHi = datarate >> 16;  // param1
    mp.gfsk.bitrateMid = datarate >> 8; // param2
    mp.gfsk.bitrateLo = datarate;  // param3
    mp.gfsk.PulseShape = GFSK_SHAPE_BT1_0; // param4
    // param5:
    if (bandwidth < 5800)
        mp.gfsk.bandwith = GFSK_RX_BW_4800;
    else if (bandwidth < 7300)
        mp.gfsk.bandwith = GFSK_RX_BW_5800;
    else if (bandwidth < 9700)
        mp.gfsk.bandwith = GFSK_RX_BW_7300;
    else if (bandwidth < 11700)
        mp.gfsk.bandwith = GFSK_RX_BW_9700;
    else if (bandwidth < 14600)
        mp.gfsk.bandwith = GFSK_RX_BW_11700;
    else if (bandwidth < 19500)
        mp.gfsk.bandwith = GFSK_RX_BW_14600;
    else if (bandwidth < 23400)
        mp.gfsk.bandwith = GFSK_RX_BW_19500;
    else if (bandwidth < 29300)
        mp.gfsk.bandwith = GFSK_RX_BW_23400;
    else if (bandwidth < 39000)
        mp.gfsk.bandwith = GFSK_RX_BW_29300;
    else if (bandwidth < 46900)
        mp.gfsk.bandwith = GFSK_RX_BW_39000;
    else if (bandwidth < 58600)
        mp.gfsk.bandwith = GFSK_RX_BW_46900;
    else if (bandwidth < 78200)
        mp.gfsk.bandwith = GFSK_RX_BW_58600;
    else if (bandwidth < 93800)
        mp.gfsk.bandwith = GFSK_RX_BW_78200;
    else if (bandwidth < 117300)
        mp.gfsk.bandwith = GFSK_RX_BW_93800;
    else if (bandwidth < 156200)
        mp.gfsk.bandwith = GFSK_RX_BW_117300;
    else if (bandwidth < 187200)
        mp.gfsk.bandwith = GFSK_RX_BW_156200;
    else if (bandwidth < 234300)
        mp.gfsk.bandwith = GFSK_RX_BW_187200;
    else if (bandwidth < 312000)
        mp.gfsk.bandwith = GFSK_RX_BW_234300;
    else if (bandwidth < 373600)
        mp.gfsk.bandwith = GFSK_RX_BW_312000;
    else if (bandwidth < 467000)
        mp.gfsk.bandwith = GFSK_RX_BW_373600;
    else
        mp.gfsk.bandwith = GFSK_RX_BW_467000;

    if (fdev > 0) {
        u32 = fdev / 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, mp.buf);


    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;
}

void Radio::LoRaConfig(
    uint32_t bandwidth,
    uint8_t datarate,
    uint8_t coderate,
    uint16_t preambleLen,
    bool fixLen,
    bool crcOn,
    bool iqInverted)
{
    ModulationParams_t mp;
    float sp, khz;

    mp.lora.spreadingFactor = datarate; // param1
    mp.lora.bandwidth = bandwidth + 4; // param2
    mp.lora.codingRate = coderate; // param3

    switch (mp.lora.bandwidth) {
        case LORA_BW_7: khz = 7.81; break;
        case LORA_BW_10: khz = 10.42; break;
        case LORA_BW_15: khz = 15.625; break;
        case LORA_BW_20: khz = 20.83; break;
        case LORA_BW_31: khz = 31.25; break;
        case LORA_BW_41: khz = 41.67; break;
        case LORA_BW_62: khz = 62.5; break;
        case LORA_BW_125: khz = 125; break;
        case LORA_BW_250: khz = 250; break;
        case LORA_BW_500: khz = 500; break;
        default: khz = 0; break;
    }
    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, mp.buf);

    pp.lora.PreambleLengthHi = preambleLen >> 8;
    pp.lora.PreambleLengthLo = preambleLen;
    pp.lora.HeaderType = fixLen;
    pp.lora.CRCType = crcOn;
    pp.lora.InvertIQ = iqInverted;
}

void Radio::SetTxConfig(
        RadioModems_t modem,
        int8_t dbm,
        uint32_t fdev, 
        uint32_t bandwidth,
        uint32_t datarate,
        uint8_t coderate,
        uint16_t preambleLen,
        bool fixLen,
        bool crcOn,
        bool iqInverted)
{
    uint8_t buf[8];

    radio.set_tx_dbm(chipType == CHIP_TYPE_SX1262, dbm);

    if (modem == MODEM_FSK)
        buf[0] = PACKET_TYPE_GFSK;
    else
        buf[0] = PACKET_TYPE_LORA;

    radio.xfer(OPCODE_SET_PACKET_TYPE, 1, buf);


    buf[0] = 0; // TX base address
    buf[1] = 0; // RX base address
    radio.xfer(OPCODE_SET_BUFFER_BASE_ADDR, 2, buf);

    if (modem == MODEM_FSK) {
        FSKConfig(bandwidth, datarate, fdev, preambleLen, fixLen, crcOn);
    } else if (modem == MODEM_LORA) {
        LoRaConfig(bandwidth, datarate, coderate, preambleLen, fixLen, crcOn, iqInverted);
    }

    {
        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, buf);
    }

    _m_ = modem;
    /* SetPacketParams written at Send() where payloadLength provided */

    antswPower = 1;
} // ..SetTxConfig()

int Radio::Send(uint8_t size, timestamp_t maxListenTime, timestamp_t channelFreeTime, int rssiThresh)
{
    uint8_t buf[8];

    if (_m_ == MODEM_FSK) {
        pp.gfsk.PayloadLength = size;

        memcpy(buf, pp.buf, 8);
        radio.xfer(OPCODE_SET_PACKET_PARAMS, 8, buf);
    } else if (_m_ == MODEM_LORA) {
        pp.lora.PayloadLength = size;

        memcpy(buf, pp.buf, 6);
        radio.xfer(OPCODE_SET_PACKET_PARAMS, 6, buf);
    }

    if (maxListenTime > 0) {
        int rssi;
        us_timestamp_t startAt, chFreeAt, now;
        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, 2, buf);
            rssi = buf[1] / -2;
        } while (rssi > rssiThresh);
        chFreeAt = lpt.read_us();
        do {
            now = lpt.read_us();
            radio.xfer(OPCODE_GET_RSSIINST, 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, buf);
    } else if (modem == MODEM_LORA) {
        pp.lora.PayloadLength = max;
        memcpy(buf, pp.buf, 6);
        radio.xfer(OPCODE_SET_PACKET_PARAMS, 6, buf);
    }
}

void Radio::dio1_top_half()
{
    dio1at = lpt.read_us();

    if (radio.chipMode == CHIPMODE_TX) {
        /* TxDone handling requires low latency */
        if (RadioEvents->TxDone) {
            RadioEvents->TxDone(dio1at);
        }
    }
#ifdef TARGET_FF_MORPHO
    else
        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(radio.rx_buf, size, rssi, snr, dio1at);
}

void Radio::Init(const RadioEvents_t* e)
{
    radio.txDone = NULL;
    radio.rxDone = rx_done;
    radio.timeout = timeout_callback;
    radio.dio1_topHalf = dio1_top_half;

    RadioEvents = e;
    lpt.start();

    radio.SetDIO2AsRfSwitchCtrl(1);
}

void Radio::UserContext()
{
    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);
}

void Radio::PrintStatus()
{
    uint8_t buf[4];
    status_t status;
    IrqFlags_t irqFlags;
    radio.xfer(OPCODE_GET_IRQ_STATUS, 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, 1, &status.octet);
    radio.PrintChipStatus(status);
}

#endif /* ..SX126x_H */