driver for sx1280

Dependents:   alarm_slave_extended_SX1280 alarm_master_extended_Vance_SX1280

sx1280.cpp

Committer:
Wayne Roberts
Date:
2018-07-16
Revision:
3:54612373bec6
Parent:
2:8a442c3511ae
Child:
4:ac55132283cb

File content as of revision 3:54612373bec6:

#include "sx12xx.h"

Callback<void()> SX128x::diox_topHalf;    // low latency ISR context

const float SX128x::timeOutStep[] = { 0.015625, 0.0625, 1, 4 };

void SX128x::dioxisr()
{
    if (diox_topHalf)
        diox_topHalf.call();
}

SX128x::SX128x(SPI& _spi, PinName _nss, PinName _busy, PinName _diox, PinName _nrst)
    : spi(_spi), nss(_nss), busy(_busy), diox(_diox), nrst(_nrst)
{
    unsigned busyCnt = 0;
    nss = 1;

    while (busy) {
        if (++busyCnt > 0x80000) {
            hw_reset();
        }
    }

    periodBase = 3;

    diox.rise(dioxisr);
}

uint8_t SX128x::xfer(uint8_t opcode, uint8_t wlen, uint8_t rlen, uint8_t* ptr)
{
    const uint8_t* stopPtr;
    const uint8_t* wstop;
    const uint8_t* rstop;
    uint8_t nop = 0;
    uint8_t ret;

    if (opcode != OPCODE_GET_STATUS) {
        if (sleeping) {
            nss = 0;
            while (busy)
                ;
            sleeping = false;
        } else {
            while (busy)
                ;

            nss = 0;
        }
    } else
        nss = 0;

    ret = spi.write(opcode);

    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)
        sleeping = true;

    return ret;
}

uint32_t SX128x::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, 2, 3+len, buf);
    for (i = 0; i < len; i++) {
        ret <<= 8;
        ret |= buf[i+3];
    }
    return ret;
}

void SX128x::start_tx(uint8_t pktLen, float timeout_ms)
{
    IrqFlags_t irqEnable;
    uint8_t buf[8];

    irqEnable.word = 0;
    irqEnable.bits.TxDone = 1;
    irqEnable.bits.RxTxTimeout = 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, 0, buf);

    {
        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] = periodBase;
    if (timeout_ms > 0) {
        unsigned t_o = timeout_ms / timeOutStep[periodBase];
        buf[1] = t_o >> 8;
        buf[2] = t_o;
    } else {
        /* no timeout */
        buf[1] = 0;
        buf[2] = 0;
    }
    xfer(OPCODE_SET_TX, 3, 0, buf);

    chipMode = CHIPMODE_TX;
    if (chipModeChange)
        chipModeChange.call();
}

void SX128x::hw_reset()
{
    nrst.output();
    nrst = 0;
    wait_us(100);
    nrst = 1;
    nrst.mode(PullUp);
    nrst.input();

    while (busy)
        ;
}

uint64_t SX128x::getSyncAddr(uint8_t num)
{
    uint64_t ret;
    unsigned regAdr = REG_ADDR_PKT_SYNC_ADRS_1 + ((num-1) * 5);
    ret = readReg(regAdr, 1);
    ret <<= 32;
    ret |= readReg(regAdr+1, 4);
    return ret;
}

void SX128x::setSyncAddr(uint8_t num, uint64_t sa)
{
    unsigned regAdr = REG_ADDR_PKT_SYNC_ADRS_1 + ((num-1) * 5);
    writeReg(regAdr+1, sa & 0xffffffff, 4);
    sa >>= 32;
    writeReg(regAdr, sa & 0xff, 1);
}

void SX128x::set_tx_dbm(int8_t dbm)
{
    uint8_t buf[2];

    buf[0] = dbm;
    buf[1] = RADIO_RAMP_20_US;
    xfer(OPCODE_SET_TX_PARAMS, 2, 0, buf);
}

void SX128x::setMHz(float MHz)
{
    unsigned frf = MHz / PLL_STEP_MHZ;
    uint8_t buf[3];

    buf[0] = frf >> 16;
    buf[1] = frf >> 8;
    buf[2] = frf;
    xfer(OPCODE_SET_RF_FREQUENCY, 3, 0, buf);
}

float SX128x::getMHz()
{
    uint32_t frf = readReg(REG_ADDR_RFFREQ, 3);
    return frf * PLL_STEP_MHZ;
}

void SX128x::setStandby(stby_t stby)
{
    uint8_t octet = stby;
    xfer(OPCODE_SET_STANDBY, 1, 0, &octet);

    chipMode = CHIPMODE_NONE;
    if (chipModeChange)
        chipModeChange.call();
}

void SX128x::setFS()
{
    xfer(OPCODE_SET_FS, 0, 0, NULL);

    chipMode = CHIPMODE_NONE;
    if (chipModeChange)
        chipModeChange.call();
}

void SX128x::setSleep(bool warm)
{
    sleepConfig_t sc;

    if (warm) {
        xfer(OPCODE_SAVE_CONTEXT, 0, 0, NULL);
    }

    chipMode = CHIPMODE_NONE;
    if (chipModeChange)
        chipModeChange.call();

    sc.octet = 0;
    sc.retentionBits.dataRAM         = warm;
    sc.retentionBits.dataBuffer      = warm;
    sc.retentionBits.instructionRAM  = warm;
    xfer(OPCODE_SET_SLEEP, 1, 0, &sc.octet);
}

uint8_t SX128x::getPacketType()
{
    uint8_t buf[2];

    xfer(OPCODE_GET_PACKET_TYPE, 0, 2, buf);
    pktType = buf[1];

    return buf[1];
}

void SX128x::setPacketType(uint8_t type)
{
    xfer(OPCODE_SET_PACKET_TYPE, 1, 0, &type);
    pktType = type;
}

void SX128x::setBufferBase(uint8_t txAddr, uint8_t rxAddr)
{
    uint8_t buf[2];

    buf[0] = txAddr; // TX base address
    buf[1] = rxAddr; // RX base address
    xfer(OPCODE_SET_BUFFER_BASE_ADDR, 2, 0, buf);
}

void SX128x::setRegulator(uint8_t rmp)
{
    xfer(OPCODE_SET_REGULATOR_MODE, 1, 0, &rmp);
}

void SX128x::start_rx(float timeout_ms)
{
    IrqFlags_t irqEnable;
    uint8_t buf[8];
    unsigned t_o;

    irqEnable.word = 0;
    irqEnable.bits.RxDone = 1;
    irqEnable.bits.RxTxTimeout = 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, 0, buf);

    buf[0] = periodBase;
    if (timeout_ms > 0) {
        t_o = timeout_ms / timeOutStep[periodBase];
        buf[1] = t_o >> 8;
        buf[2] = t_o;
    } else {
        /* receive packets forever */
        buf[1] = 0xff;
        buf[2] = 0xff;
    }
    xfer(OPCODE_SET_RX, 3, 0, buf);

    chipMode = CHIPMODE_RX;
    if (chipModeChange) {
        chipModeChange.call();
    }
}

void SX128x::service()
{
    IrqFlags_t irqFlags, clearIrqFlags;
    uint8_t buf[6];
    pktStatus_t pktStatus;
    //status_t st;

    if (busy) {
        return;
    }

    while (diox) {
        xfer(OPCODE_GET_IRQ_STATUS, 0, 3, buf);
        /*st.octet = buf[0]; */
        irqFlags.word = buf[1] << 8;
        irqFlags.word |= buf[2];
        clearIrqFlags.word = 0;
        if (irqFlags.bits.TxDone) {
            chipMode = CHIPMODE_NONE;
            if (chipModeChange)
                chipModeChange.call();
            if (txDone)
                txDone.call();  // might change to Rx
            clearIrqFlags.bits.TxDone = 1;
        }
        if (irqFlags.bits.RxDone) {
            if (rxDone) {
                uint8_t len, slen;
                xfer(OPCODE_GET_RX_BUFFER_STATUS, 0, 3, buf);
                /*st.octet = buf[0]; */
                if (buf[1] == 0)
                    len = readReg(REG_ADDR_LORA_TX_PAYLOAD_LENGTH, 1);  // lora implicit
                else
                    len = buf[1];

                ReadBuffer(len, buf[2]);

                if (pktType == PACKET_TYPE_LORA || pktType == PACKET_TYPE_RANGING)
                    slen = 3;
                else
                    slen = 6;

                xfer(OPCODE_GET_PACKET_STATUS, 0, slen, pktStatus.buf);
                rxDone(len, &pktStatus);
            }

            clearIrqFlags.bits.RxDone = 1;
        }
        if (irqFlags.bits.RxTxTimeout) {
            if (chipMode != CHIPMODE_NONE) {
                if (timeout)
                    timeout(chipMode == CHIPMODE_TX);
            }
            chipMode = CHIPMODE_NONE;
            if (chipModeChange)
                chipModeChange.call();
            clearIrqFlags.bits.RxTxTimeout = 1;
        }

        if (clearIrqFlags.word != 0) {
            buf[0] = clearIrqFlags.word >> 8;
            buf[1] = (uint8_t)clearIrqFlags.word;
            xfer(OPCODE_CLEAR_IRQ_STATUS, 2, 0, buf);
        }

    } // ...while (diox)

} // ..service()

void SX128x::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--) {
        buf[n+1] = (uint8_t)data;
        data >>= 8;
    }
    xfer(OPCODE_WRITE_REGISTER, 2+len, 2+len, buf);
}

void SX128x::ReadBuffer(uint8_t size, uint8_t offset)
{
    //status_t st;
    unsigned i;

    while (busy)
        ;

    nss = 0;

    /*st.octet =*/ spi.write(OPCODE_READ_BUFFER);
    /*st.octet =*/ spi.write(offset);
    /*st.octet =*/ spi.write(0);   // NOP
    i = 0;
    for (i = 0; i < size; i++) {
        rx_buf[i] = spi.write(0);
    }

    nss = 1;
}