Quick & dirty port of the RadioHead library with minimal support for the RF95 radio. It is designed to be used with the swspi library which in turn is designed to be used on the MAX32630FTHR board.

Dependents:   Rocket

Revision:
0:e69d086cb053
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/RH_RF95.cpp	Sun Jun 11 04:05:05 2017 +0000
@@ -0,0 +1,399 @@
+// RH_RF95.cpp
+//
+// Copyright (C) 2011 Mike McCauley
+// $Id: RH_RF95.cpp,v 1.14 2017/03/04 00:59:41 mikem Exp $
+//
+// Ported to mbed - support only a single radio - Dan Julio - 5/2017 
+//
+
+#include <RH_RF95.h>
+
+// These are indexed by the values of ModemConfigChoice
+// Stored in flash (program) memory to save SRAM
+static const RH_RF95::ModemConfig MODEM_CONFIG_TABLE[] =
+{
+    //  1d,     1e,      26
+    { 0x72,   0x74,    0x00}, // Bw125Cr45Sf128 (the chip default)
+    { 0x92,   0x74,    0x00}, // Bw500Cr45Sf128
+    { 0x48,   0x94,    0x00}, // Bw31_25Cr48Sf512
+    { 0x78,   0xc4,    0x00}, // Bw125Cr48Sf4096
+    
+};
+
+RH_RF95::RH_RF95(swspi& spi, int ssNum)
+    :
+    _spi(spi),
+    _ssn(ssNum),
+    _rxBufValid(0)
+{
+}
+
+bool RH_RF95::init()
+{
+    // Set sleep mode, so we can also set LORA mode:
+    _spi.spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_SLEEP | RH_RF95_LONG_RANGE_MODE, 0, _ssn);
+    wait_ms(10); // Wait for sleep mode to take over from say, CAD
+    // Check we are in sleep mode, with LORA set
+    if (_spi.spiRead(RH_RF95_REG_01_OP_MODE, 0, _ssn) != (RH_RF95_MODE_SLEEP | RH_RF95_LONG_RANGE_MODE))
+    {
+//        printf("REG_01_OP_MODE: 0x%2x\n", _spi.spiRead(RH_RF95_REG_01_OP_MODE, 0, _ssn));
+        return false; // No device present?
+    }
+
+    // Set up FIFO
+    // We configure so that we can use the entire 256 byte FIFO for either receive
+    // or transmit, but not both at the same time
+    _spi.spiWrite(RH_RF95_REG_0E_FIFO_TX_BASE_ADDR, 0, 0, _ssn);
+    _spi.spiWrite(RH_RF95_REG_0F_FIFO_RX_BASE_ADDR, 0, 0, _ssn);
+
+    // Packet format is preamble + explicit-header + payload + crc
+    // Explicit Header Mode
+    // payload is TO + FROM + ID + FLAGS + message data
+    // RX mode is implmented with RXCONTINUOUS
+    // max message data length is 255 - 4 = 251 octets
+
+    setModeIdle();
+
+    // Set up default configuration
+    // No Sync Words in LORA mode.
+    setModemConfig(Bw125Cr45Sf128); // Radio default
+//    setModemConfig(Bw125Cr48Sf4096); // slow and reliable?
+    setPreambleLength(8); // Default is 8
+    // An innocuous ISM frequency, same as RF22's
+    setFrequency(434.0);
+    // Lowish power
+    setTxPower(13);
+
+    return true;
+}
+
+// C++ level interrupt handler for this instance
+// LORA is unusual in that it has several interrupt lines, and not a single, combined one.
+// On MiniWirelessLoRa, only one of the several interrupt lines (DI0) from the RFM95 is usefuly 
+// connnected to the processor.
+// We use this to get RxDone and TxDone interrupts
+void RH_RF95::handleInterrupt()
+{
+    // Read the interrupt register
+    uint8_t irq_flags = _spi.spiRead(RH_RF95_REG_12_IRQ_FLAGS, 0, _ssn);
+    if (_mode == RHModeRx && irq_flags & (RH_RF95_RX_TIMEOUT | RH_RF95_PAYLOAD_CRC_ERROR))
+    {
+        _rxBad++;
+    }
+    else if (_mode == RHModeRx && irq_flags & RH_RF95_RX_DONE)
+    {
+    // Have received a packet
+    uint8_t len = _spi.spiRead(RH_RF95_REG_13_RX_NB_BYTES, 0, _ssn);
+
+    // Reset the fifo read ptr to the beginning of the packet
+    _spi.spiWrite(RH_RF95_REG_0D_FIFO_ADDR_PTR, _spi.spiRead(RH_RF95_REG_10_FIFO_RX_CURRENT_ADDR, 0, _ssn), 0, _ssn);
+    _spi.spiBurstRead(RH_RF95_REG_00_FIFO, _buf, len, 0, _ssn);
+    _bufLen = len;
+    _spi.spiWrite(RH_RF95_REG_12_IRQ_FLAGS, 0xff, 0, _ssn); // Clear all IRQ flags
+
+    // Remember the last signal to noise ratio, LORA mode
+    // Per page 111, SX1276/77/78/79 datasheet
+    _lastSNR = (int8_t)_spi.spiRead(RH_RF95_REG_19_PKT_SNR_VALUE, 0, _ssn) / 4;
+
+    // Remember the RSSI of this packet, LORA mode
+    // this is according to the doc, but is it really correct?
+    // weakest receiveable signals are reported RSSI at about -66
+    _lastRssi = _spi.spiRead(RH_RF95_REG_1A_PKT_RSSI_VALUE, 0, _ssn);
+    // Adjust the RSSI, datasheet page 87
+    if (_lastSNR < 0)
+        _lastRssi = _lastRssi + _lastSNR;
+    else
+        _lastRssi = (int)_lastRssi * 16 / 15;
+    if (_usingHFport)
+        _lastRssi -= 157;
+    else
+        _lastRssi -= 164;
+        
+    // We have received a message.
+    validateRxBuf(); 
+    if (_rxBufValid)
+        setModeIdle(); // Got one 
+    }
+    else if (_mode == RHModeTx && irq_flags & RH_RF95_TX_DONE)
+    {
+        _txGood++;
+        setModeIdle();
+    }
+    else if (_mode == RHModeCad && irq_flags & RH_RF95_CAD_DONE)
+    {
+        _cad = irq_flags & RH_RF95_CAD_DETECTED;
+        setModeIdle();
+    }
+    
+    _spi.spiWrite(RH_RF95_REG_12_IRQ_FLAGS, 0xff, 0, _ssn); // Clear all IRQ flags
+}
+
+// Check whether the latest received message is complete and uncorrupted
+void RH_RF95::validateRxBuf()
+{
+    if (_bufLen < 4)
+        return; // Too short to be a real message
+    
+    // Extract the 4 headers
+    _rxHeaderTo    = _buf[0];
+    _rxHeaderFrom  = _buf[1];
+    _rxHeaderId    = _buf[2];
+    _rxHeaderFlags = _buf[3];
+    if (_promiscuous ||
+      _rxHeaderTo == _thisAddress ||
+      _rxHeaderTo == RH_BROADCAST_ADDRESS)
+    {
+        _rxGood++;
+        _rxBufValid = true;
+    }
+}
+
+bool RH_RF95::available()
+{
+    if (_mode == RHModeTx)
+        return false;
+    
+    setModeRx();
+    return _rxBufValid; // Will be set by the interrupt handler when a good message is received
+}
+
+void RH_RF95::clearRxBuf()
+{
+    _rxBufValid = false;
+    _bufLen = 0;
+}
+
+bool RH_RF95::recv(uint8_t* buf, uint8_t* len)
+{
+    if (!available())
+        return false;
+    
+    if (buf && len)
+    {
+        // Skip the 4 headers that are at the beginning of the rxBuf
+        if (*len > _bufLen-RH_RF95_HEADER_LEN)
+            *len = _bufLen-RH_RF95_HEADER_LEN;
+        memcpy(buf, _buf+RH_RF95_HEADER_LEN, *len);
+    }
+    clearRxBuf(); // This message accepted and cleared
+    return true;
+}
+
+bool RH_RF95::send(const uint8_t* data, uint8_t len)
+{
+    if (len > RH_RF95_MAX_MESSAGE_LEN)
+        return false;
+
+    waitPacketSent(); // Make sure we dont interrupt an outgoing message
+    setModeIdle();
+
+    if (!waitCAD()) 
+        return false;  // Check channel activity
+
+    // Position at the beginning of the FIFO
+    _spi.spiWrite(RH_RF95_REG_0D_FIFO_ADDR_PTR, 0, 0, _ssn);
+    // The headers
+    _spi.spiWrite(RH_RF95_REG_00_FIFO, _txHeaderTo, 0, _ssn);
+    _spi.spiWrite(RH_RF95_REG_00_FIFO, _txHeaderFrom, 0, _ssn);
+    _spi.spiWrite(RH_RF95_REG_00_FIFO, _txHeaderId, 0, _ssn);
+    _spi.spiWrite(RH_RF95_REG_00_FIFO, _txHeaderFlags, 0, _ssn);
+    // The message data
+    _spi.spiBurstWrite(RH_RF95_REG_00_FIFO, data, len, 0, _ssn);
+    _spi.spiWrite(RH_RF95_REG_22_PAYLOAD_LENGTH, len + RH_RF95_HEADER_LEN, 0, _ssn);
+
+    setModeTx(); // Start the transmitter
+    // when Tx is done, interruptHandler will fire and radio mode will return to STANDBY
+    return true;
+}
+
+bool RH_RF95::printRegisters()
+{
+    uint8_t registers[] = { 0x01, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x014, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27};
+
+    uint8_t i;
+    for (i = 0; i < sizeof(registers); i++)
+    {
+        printf("0x%x: 0x%x\n\r", registers[i], _spi.spiRead(registers[i], 0, _ssn));
+    }
+    return true;
+}
+
+uint8_t RH_RF95::maxMessageLength()
+{
+    return RH_RF95_MAX_MESSAGE_LEN;
+}
+
+bool RH_RF95::setFrequency(float centre)
+{
+    // Frf = FRF / FSTEP
+    uint32_t frf = (centre * 1000000.0f) / RH_RF95_FSTEP;
+    _spi.spiWrite(RH_RF95_REG_06_FRF_MSB, (frf >> 16) & 0xff, 0, _ssn);
+    _spi.spiWrite(RH_RF95_REG_07_FRF_MID, (frf >> 8) & 0xff, 0, _ssn);
+    _spi.spiWrite(RH_RF95_REG_08_FRF_LSB, frf & 0xff, 0, _ssn);
+    _usingHFport = (centre >= 779.0f);
+
+    return true;
+}
+
+void RH_RF95::setModeIdle()
+{
+    if (_mode != RHModeIdle)
+    {
+        _spi.spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_STDBY, 0, _ssn);
+        _mode = RHModeIdle;
+    }
+}
+
+bool RH_RF95::sleep()
+{
+    if (_mode != RHModeSleep)
+    {
+        _spi.spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_SLEEP, 0, _ssn);
+        _mode = RHModeSleep;
+    }
+    return true;
+}
+
+void RH_RF95::setModeRx()
+{
+    if (_mode != RHModeRx)
+    {
+        _spi.spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_RXCONTINUOUS, 0, _ssn);
+        _spi.spiWrite(RH_RF95_REG_40_DIO_MAPPING1, 0x00, 0, _ssn); // Interrupt on RxDone
+        _mode = RHModeRx;
+    }
+}
+
+void RH_RF95::setModeTx()
+{
+    if (_mode != RHModeTx)
+    {
+        _spi.spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_TX, 0, _ssn);
+        _spi.spiWrite(RH_RF95_REG_40_DIO_MAPPING1, 0x40, 0, _ssn); // Interrupt on TxDone
+        _mode = RHModeTx;
+    }
+}
+
+void RH_RF95::setTxPower(int8_t power, bool useRFO)
+{
+    // Sigh, different behaviours depending on whther the module use PA_BOOST or the RFO pin
+    // for the transmitter output
+    if (useRFO)
+    {
+        if (power > 14)
+            power = 14;
+        if (power < -1)
+            power = -1;
+        _spi.spiWrite(RH_RF95_REG_09_PA_CONFIG, RH_RF95_MAX_POWER | (power + 1), 0, _ssn);
+    }
+    else
+    {
+        if (power > 23)
+            power = 23;
+        if (power < 5)
+            power = 5;
+
+        // For RH_RF95_PA_DAC_ENABLE, manual says '+20dBm on PA_BOOST when OutputPower=0xf'
+        // RH_RF95_PA_DAC_ENABLE actually adds about 3dBm to all power levels. We will us it
+        // for 21, 22 and 23dBm
+        if (power > 20)
+        {
+            _spi.spiWrite(RH_RF95_REG_4D_PA_DAC, RH_RF95_PA_DAC_ENABLE, 0, _ssn);
+            power -= 3;
+        }
+        else
+        {
+            _spi.spiWrite(RH_RF95_REG_4D_PA_DAC, RH_RF95_PA_DAC_DISABLE, 0, _ssn);
+        }
+
+        // RFM95/96/97/98 does not have RFO pins connected to anything. Only PA_BOOST
+        // pin is connected, so must use PA_BOOST
+        // Pout = 2 + OutputPower.
+        // The documentation is pretty confusing on this topic: PaSelect says the max power is 20dBm,
+        // but OutputPower claims it would be 17dBm.
+        // My measurements show 20dBm is correct
+        _spi.spiWrite(RH_RF95_REG_09_PA_CONFIG, RH_RF95_PA_SELECT | (power-5), 0, _ssn);
+    }
+}
+
+// Sets registers from a canned modem configuration structure
+void RH_RF95::setModemRegisters(const ModemConfig* config)
+{
+    _spi.spiWrite(RH_RF95_REG_1D_MODEM_CONFIG1,       config->reg_1d, 0, _ssn);
+    _spi.spiWrite(RH_RF95_REG_1E_MODEM_CONFIG2,       config->reg_1e, 0, _ssn);
+    _spi.spiWrite(RH_RF95_REG_26_MODEM_CONFIG3,       config->reg_26, 0, _ssn);
+}
+
+// Set one of the canned FSK Modem configs
+// Returns true if its a valid choice
+bool RH_RF95::setModemConfig(ModemConfigChoice index)
+{
+    if (index > (signed int)(sizeof(MODEM_CONFIG_TABLE) / sizeof(ModemConfig)))
+        return false;
+
+    ModemConfig cfg;
+    memcpy(&cfg, &MODEM_CONFIG_TABLE[index], sizeof(RH_RF95::ModemConfig));
+    setModemRegisters(&cfg);
+
+    return true;
+}
+
+void RH_RF95::setPreambleLength(uint16_t bytes)
+{
+    _spi.spiWrite(RH_RF95_REG_20_PREAMBLE_MSB, bytes >> 8, 0, _ssn);
+    _spi.spiWrite(RH_RF95_REG_21_PREAMBLE_LSB, bytes & 0xff, 0, _ssn);
+}
+
+bool RH_RF95::isChannelActive()
+{
+    // Set mode RHModeCad
+    if (_mode != RHModeCad)
+    {
+        _spi.spiWrite(RH_RF95_REG_01_OP_MODE, RH_RF95_MODE_CAD, 0, _ssn);
+        _spi.spiWrite(RH_RF95_REG_40_DIO_MAPPING1, 0x80, 0, _ssn); // Interrupt on CadDone
+        _mode = RHModeCad;
+    }
+
+    while (_mode == RHModeCad)
+        Thread::yield();
+
+    return _cad;
+}
+
+void RH_RF95::enableTCXO()
+{
+    while ((_spi.spiRead(RH_RF95_REG_4B_TCXO, 0, _ssn) & RH_RF95_TCXO_TCXO_INPUT_ON) != RH_RF95_TCXO_TCXO_INPUT_ON)
+    {
+        sleep();
+        _spi.spiWrite(RH_RF95_REG_4B_TCXO, (_spi.spiRead(RH_RF95_REG_4B_TCXO, 0, _ssn) | RH_RF95_TCXO_TCXO_INPUT_ON), 0, _ssn);
+    } 
+}
+
+// From section 4.1.5 of SX1276/77/78/79
+// Ferror = FreqError * 2**24 * BW / Fxtal / 500
+int RH_RF95::frequencyError()
+{
+    int32_t freqerror = 0;
+
+    // Convert 2.5 bytes (5 nibbles, 20 bits) to 32 bit signed int
+    freqerror = _spi.spiRead(RH_RF95_REG_28_FEI_MSB, 0, _ssn) << 16;
+    freqerror |= _spi.spiRead(RH_RF95_REG_29_FEI_MID, 0, _ssn) << 8;
+    freqerror |= _spi.spiRead(RH_RF95_REG_2A_FEI_LSB, 0, _ssn);
+    // Sign extension into top 3 nibbles
+    if (freqerror & 0x80000)
+        freqerror |= 0xfff00000;
+
+    int error = 0; // In hertz
+    float bw_tab[] = {7.8, 10.4, 15.6, 20.8, 31.25, 41.7, 62.5, 125, 250, 500};
+    uint8_t bwindex = _spi.spiRead(RH_RF95_REG_1D_MODEM_CONFIG1, 0, _ssn) >> 4;
+    if (bwindex < (sizeof(bw_tab) / sizeof(float)))
+        error = (float)freqerror * bw_tab[bwindex] * ((float)(1L << 24) / (float)RH_RF95_FXOSC / 500.0f);
+    // else not defined
+
+    return error;
+}
+
+int RH_RF95::lastSNR()
+{
+    return _lastSNR;
+}