operate LoRa radio over I2C

Dependencies:   TimeoutAbs lib_i2c_slave_block sx12xx_hal

radio chip selection

Radio chip driver is not included, allowing choice of radio device.
If you're using SX1272 or SX1276, then import sx127x driver into your program.
if you're using SX1261 or SX1262, then import sx126x driver into your program.
if you're using SX1280, then import sx1280 driver into your program.
If you're using NAmote72 or Murata discovery, then you must import only sx127x driver.

This project is used as slave device with i2c_lora_master on raspberry pi. This i2c_lora_slave offloads the real-time requirements onto microcontroller. Also permits multiple slave radio devices connected to master. Radio MAC layer exists on I2C master, along with application layer.

If beacon operation is enabled, I2C functions which access radio chip are blocked while beacon is loaded and transmitted.
See lib_i2c_slave_block for wiring connections.

device_sx126x.cpp

Committer:
Wayne Roberts
Date:
2019-02-08
Revision:
0:9eb5b8bf9f7b

File content as of revision 0:9eb5b8bf9f7b:

#include "radio_device.h"
#ifdef SX126x_H 
void get_opmode()
{
    status_t status;
    Radio::radio.xfer(OPCODE_GET_STATUS, 0, 1, &status.octet);

    switch (status.bits.chipMode) {
        case 2: // STBY_RC
        case 3: // STBY_XOSC
            irq.buf[1] = OPMODE_STANDBY;
            break;
        case 4: // FS
            irq.buf[1] = OPMODE_FS;
            break;
        case 5: // RX
            irq.buf[1] = OPMODE_RX;
            break;
        case 6: // TX
            irq.buf[1] = OPMODE_TX;
            break;
        default:
            irq.buf[1] = OPMODE_FAIL;
            break;
    }

    irq.fields.flags.irq_type = IRQ_TYPE_OPMODE;
    irqOutPin = 1;
}

void get_lora_packet()
{
    loraConfig1_t conf1;
    loraConfig2_t conf2;
    //LoRaPacketConfig(unsigned preambleLen, bool fixLen, bool crcOn, bool invIQ)
    uint32_t val = Radio::radio.readReg(REG_ADDR_LORA_PREAMBLE_SYMBNB, 2);
    irq.buf[1] = val & 0xff;
    val >>= 8;
    irq.buf[2] = val & 0xff;

    conf1.octet = Radio::radio.readReg(REG_ADDR_LORA_CONFIG1, 1);
    irq.buf[3] = conf1.bits.implicit_header;
    irq.buf[4] = conf1.bits.rx_invert_iq;

    conf2.octet = Radio::radio.readReg(REG_ADDR_LORA_CONFIG2, 1);
    irq.buf[5] = conf2.bits.tx_payload_crc16_en;

    irq.fields.flags.irq_type = IRQ_TYPE_LORA_PKT;
    irqOutPin = 1;
}

const uint8_t loraBWs[] = {
    LORA_BW_7, LORA_BW_10, LORA_BW_15,
    LORA_BW_20, LORA_BW_31, LORA_BW_41,
    LORA_BW_62, LORA_BW_125, LORA_BW_250,
    LORA_BW_500
};

void get_lora_modem()
{
    uint16_t khz = 0;
    loraConfig1_t conf1;
    loraConfig0_t conf0;
    conf0.octet = Radio::radio.readReg(REG_ADDR_LORA_CONFIG0, 1);

    switch (conf0.bits.modem_bw) {
        case LORA_BW_7: khz = 8; break;         // 7.81 kHz real
        case LORA_BW_10: khz = 10; break;        // 10.42 kHz real
        case LORA_BW_15: khz = 16; break;        // 15.63 kHz real
        case LORA_BW_20: khz = 21; break;        // 20.83 kHz real
        case LORA_BW_31: khz = 31; break;        // 31.25 kHz real
        case LORA_BW_41: khz = 42; break;        // 41.67 kHz real
        case LORA_BW_62: khz = 63; break;        // 62.50 kHz real
        case LORA_BW_125: khz = 125; break;       // 125 kHz real
        case LORA_BW_250: khz = 250; break;       // 250 kHz real
        case LORA_BW_500: khz = 500; break;       // 500 kHz real
    }

    irq.buf[1] = khz & 0xff;
    khz >>= 8;
    irq.buf[2] = khz & 0xff;

    //LoRaModemConfig(unsigned KHz, unsigned sf, unsigned cr)
    irq.buf[3] = conf0.bits.modem_sf;

    conf1.octet = Radio::radio.readReg(REG_ADDR_LORA_CONFIG1, 1);
    irq.buf[4] = conf1.bits.tx_coding_rate;
    
    irq.fields.flags.irq_type = IRQ_TYPE_LORA_MODEM;
    irqOutPin = 1;
}

void get_tx_dbm()
{
    int8_t dbm;
    PwrCtrl_t PwrCtrl;
    PaCtrl1b_t PaCtrl1b;
    unsigned v = Radio::radio.readReg(REG_ADDR_ANACTRL16, 1);

    if (v & 0x10) {
        dbm = PA_OFF_DBM;
        goto dbmDone;
    }

    PwrCtrl.octet = Radio::radio.readReg(REG_ADDR_PWR_CTRL, 1);

    PaCtrl1b.octet = Radio::radio.readReg(REG_ADDR_PA_CTRL1B, 1);
    // PaCtrl1b.bits.tx_mode_bat ---  deviceSel

    if (PaCtrl1b.bits.tx_mode_bat)
        dbm = PwrCtrl.bits.tx_pwr - 17;
    else
        dbm = PwrCtrl.bits.tx_pwr - 9;

dbmDone:
    irq.buf[1] = dbm;
    irq.fields.flags.irq_type = IRQ_TYPE_TXDBM;
    irqOutPin = 1;
}

void get_fsk_sync()
{
    unsigned idx = 1;
    unsigned addr = REG_ADDR_SYNCADDR;
    uint8_t swl_bits = Radio::radio.readReg(REG_ADDR_FSK_SYNC_LEN, 1);
    if (swl_bits & 7) {
        swl_bits |= 7;
        swl_bits++;
    }
    irq.buf[idx++] = swl_bits >> 3;

    while (swl_bits > 0) {
        irq.buf[idx++] = Radio::radio.readReg(addr++, 1);
        swl_bits -= 8;
    }

    irq.fields.flags.irq_type = IRQ_TYPE_FSK_SYNC;
    irqOutPin = 1;
}

void get_fsk_modem()
{
    uint32_t u32;
    bwSel_t bwSel;
    // GFSKModemConfig(unsigned bps, unsigned bwKHz, unsigned fdev_hz)

    unsigned d = Radio::radio.readReg(REG_ADDR_BITRATE, 3);
    float f = d / 32.0;

    u32 = XTAL_FREQ_HZ / f;
    irq.buf[1] = u32 & 0xff;
    u32 >>= 8;
    irq.buf[2] = u32 & 0xff;
    u32 >>= 8;
    irq.buf[3] = u32 & 0xff;
    u32 >>= 8;
    irq.buf[4] = u32 & 0xff;

    bwSel.octet = Radio::radio.readReg(REG_ADDR_BWSEL, 1);
    switch (bwSel.octet) {
        case GFSK_RX_BW_4800: u32 = 4800; break;
        case GFSK_RX_BW_5800: u32 = 5800; break;
        case GFSK_RX_BW_7300: u32 = 7300; break;
        case GFSK_RX_BW_9700: u32 = 9700; break;
        case GFSK_RX_BW_11700: u32 = 11700; break;
        case GFSK_RX_BW_14600: u32 = 14600; break;
        case GFSK_RX_BW_19500: u32 = 19500; break;
        case GFSK_RX_BW_23400: u32 = 23400; break;
        case GFSK_RX_BW_29300: u32 = 29300; break;
        case GFSK_RX_BW_39000: u32 = 39000; break;
        case GFSK_RX_BW_46900: u32 = 46900; break;
        case GFSK_RX_BW_58600: u32 = 58600; break;
        case GFSK_RX_BW_78200: u32 = 78200; break;
        case GFSK_RX_BW_93800: u32 = 93800; break;
        case GFSK_RX_BW_117300: u32 = 117300; break;
        case GFSK_RX_BW_156200: u32 = 156200; break;
        case GFSK_RX_BW_187200: u32 = 187200; break;
        case GFSK_RX_BW_234300: u32 = 234300; break;
        case GFSK_RX_BW_312000: u32 = 312000; break;
        case GFSK_RX_BW_373600: u32 = 373600; break;
        case GFSK_RX_BW_467000: u32 = 467000; break;
    }

    irq.buf[5] = u32 & 0xff;
    u32 >>= 8;
    irq.buf[6] = u32 & 0xff;

    d = Radio::radio.readReg(REG_ADDR_FREQDEV, 3);
    u32 = d * FREQ_STEP;
    irq.buf[7] = u32 & 0xff;
    u32 >>= 8;
    irq.buf[8] = u32 & 0xff;
    u32 >>= 8;
    irq.buf[9] = u32 & 0xff;
    u32 >>= 8;
    irq.buf[10] = u32 & 0xff;

    irq.fields.flags.irq_type = IRQ_TYPE_FSK_MODEM;
    irqOutPin = 1;
}

void get_fsk_packet()
{
    pktCtrl0_t pktCtrl0;
    pktCtrl2_t pktCtrl2;
    // GFSKPacketConfig(unsigned preambleLen, bool fixLen, bool crcOn)
    unsigned pl = Radio::radio.readReg(REG_ADDR_FSK_PREAMBLE_TXLEN , 2);
    irq.buf[1] = pl & 0xff;
    pl >>= 8;
    irq.buf[2] = pl & 0xff;

    pktCtrl0.octet = Radio::radio.readReg(REG_ADDR_FSK_PKTCTRL0, 1);
    irq.buf[3] = pktCtrl0.bits.pkt_len_format; // true = fixed

    pktCtrl2.octet = Radio::radio.readReg(REG_ADDR_FSK_PKTCTRL2, 1);
    switch (pktCtrl2.octet & 0x7) { // param8
        case GFSK_CRC_OFF: irq.buf[4] = 0;
        case GFSK_CRC_1_BYTE: irq.buf[4] = 1;
        case GFSK_CRC_2_BYTE: irq.buf[4] = 2;
        case GFSK_CRC_1_BYTE_INV: irq.buf[4] = 3;
        case GFSK_CRC_2_BYTE_INV: irq.buf[4] = 4;
        default: irq.buf[4] = 5;
    }
    
    irq.fields.flags.irq_type = IRQ_TYPE_FSK_PKT;
    irqOutPin = 1;
}

void radio_reset()
{
#ifdef TARGET_FF_ARDUINO
    #define PINNAME_NRST            A0
    Radio::radio.hw_reset(PINNAME_NRST);
#else
    #error reset_pin
#endif
}

void radio_device_init()
{
}

#endif /* ..SX126x_H */