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.
RH_RF95.cpp
- Committer:
- danjulio
- Date:
- 2017-06-11
- Revision:
- 0:e69d086cb053
File content as of revision 0:e69d086cb053:
// 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; }