From 6cdc93317831f122906bb66e1715af4fce0ad7e9 Mon Sep 17 00:00:00 2001 From: Charles Date: Fri, 22 Jul 2016 02:58:39 +0200 Subject: [PATCH] Added IRQLESS mode, moteino and modem config --- RH_RF69.cpp | 139 +++++++++++++++++++++++++++++++++++++++++++++------- RH_RF69.h | 36 ++++++++++++++ 2 files changed, 158 insertions(+), 17 deletions(-) diff --git a/RH_RF69.cpp b/RH_RF69.cpp index 9d08fac..66b0d5f 100644 --- a/RH_RF69.cpp +++ b/RH_RF69.cpp @@ -8,8 +8,10 @@ // Interrupt vectors for the 3 Arduino interrupt pins // Each interrupt can be handled by a different instance of RH_RF69, allowing you to have // 2 or more RF69s per Arduino +#ifndef RH_RF69_IRQLESS RH_RF69* RH_RF69::_deviceForInterrupt[RH_RF69_NUM_INTERRUPTS] = {0, 0, 0}; uint8_t RH_RF69::_interruptCount = 0; // Index into _deviceForInterrupt for next device +#endif // These are indexed by the values of ModemConfigChoice // Stored in flash (program) memory to save SRAM @@ -45,6 +47,10 @@ PROGMEM static const RH_RF69::ModemConfig MODEM_CONFIG_TABLE[] = { CONFIG_FSK, 0x00, 0x80, 0x10, 0x00, 0xe0, 0xe0, CONFIG_WHITE}, // FSK_Rb250Fd250 { CONFIG_FSK, 0x02, 0x40, 0x03, 0x33, 0x42, 0x42, CONFIG_WHITE}, // FSK_Rb55555Fd50 + // MOTEINO + // REGAFCBW set to default 0x8A, No DC-free encoding/decoding performed + { CONFIG_FSK, 0x02, 0x40, 0x03, 0x33, 0x42, 0x8A, CONFIG_NOWHITE}, // FSK_MOTEINO + // 02, 03, 04, 05, 06, 19, 1a, 37 // GFSK (BT=1.0), No Manchester, whitening, CRC, no address filtering // AFC BW == RX BW == 2 x bit rate @@ -87,9 +93,11 @@ RH_RF69::RH_RF69(uint8_t slaveSelectPin, uint8_t interruptPin, RHGenericSPI& spi : RHSPIDriver(slaveSelectPin, spi) { + #ifndef RH_RF69_IRQLESS _interruptPin = interruptPin; - _idleMode = RH_RF69_OPMODE_MODE_STDBY; _myInterruptIndex = 0xff; // Not allocated yet + #endif + _idleMode = RH_RF69_OPMODE_MODE_STDBY; } void RH_RF69::setIdleMode(uint8_t idleMode) @@ -97,23 +105,44 @@ void RH_RF69::setIdleMode(uint8_t idleMode) _idleMode = idleMode; } -bool RH_RF69::init() +// Set configuration to be moteino compatible +void RH_RF69::setConfigMoteino() +{ + setModemConfig(FSK_MOTEINO); + setPreambleLength(3); + spiWrite(RH_RF69_REG_29_RSSITHRESH, 220); + spiWrite(RH_RF69_REG_3D_PACKETCONFIG2, RH_RF69_PACKETCONFIG2_RXRESTARTDELAY_2BITS | RH_RF69_PACKETCONFIG2_AUTORXRESTARTON); + // default moteino Frequency For 433 MHz + setFrequency(433.0); + + // Moteino default network ID is 1 + uint8_t syncwords[] = { 0x2d, 1 }; + setSyncWords(syncwords, sizeof(syncwords)); + //spiWrite(RH_RF69_REG_07_FRFMSB, 0x6C); + //spiWrite(RH_RF69_REG_08_FRFMID, 0x40); + //spiWrite(RH_RF69_REG_09_FRFLSB, 0x00); +} + +bool RH_RF69::init(void) { if (!RHSPIDriver::init()) return false; + #ifndef RH_RF69_IRQLESS // Determine the interrupt number that corresponds to the interruptPin int interruptNumber = digitalPinToInterrupt(_interruptPin); if (interruptNumber == NOT_AN_INTERRUPT) return false; + #endif // Get the device type and check it // This also tests whether we are really connected to a device // My test devices return 0x24 - _deviceType = spiRead(RH_RF69_REG_10_VERSION); - if (_deviceType == 00 || - _deviceType == 0xff) - return false; + _deviceType = spiRead(RH_RF69_REG_10_VERSION); + if (_deviceType == 00 || _deviceType == 0xff) + return false; + + #ifndef RH_RF69_IRQLESS // Add by Adrien van den Bossche for Teensy // ARM M4 requires the below. else pin interrupt doesn't work properly. @@ -144,6 +173,7 @@ bool RH_RF69::init() else return false; // Too many devices, not enough interrupt vectors + #endif setModeIdle(); // Configure important RH_RF69 registers @@ -172,7 +202,6 @@ bool RH_RF69::init() // Set up default configuration uint8_t syncwords[] = { 0x2d, 0xd4 }; setSyncWords(syncwords, sizeof(syncwords)); // Same as RF22's - // Reasonably fast and reliable default speed and modulation setModemConfig(GFSK_Rb250Fd250); // 3 would be sufficient, but this is the same as RF22's @@ -184,6 +213,9 @@ bool RH_RF69::init() // +13dBm, same as power-on default setTxPower(13); + // override and set config to moteino + setConfigMoteino(); + return true; } @@ -191,16 +223,17 @@ bool RH_RF69::init() // RH_RF69 is unusual in Mthat it has several interrupt lines, and not a single, combined one. // On Moteino, only one of the several interrupt lines (DI0) from the RH_RF69 is connnected to the processor. // We use this to get PACKETSDENT and PAYLOADRADY interrupts. +#ifndef RH_RF69_IRQLESS void RH_RF69::handleInterrupt() { // Get the interrupt cause uint8_t irqflags2 = spiRead(RH_RF69_REG_28_IRQFLAGS2); if (_mode == RHModeTx && (irqflags2 & RH_RF69_IRQFLAGS2_PACKETSENT)) { + // Serial.println("PACKETSENT"); // A transmitter message has been fully sent setModeIdle(); // Clears FIFO _txGood++; -// Serial.println("PACKETSENT"); } // Must look for PAYLOADREADY, not CRCOK, since only PAYLOADREADY occurs _after_ AES decryption // has been done @@ -210,12 +243,14 @@ void RH_RF69::handleInterrupt() _lastRssi = -((int8_t)(spiRead(RH_RF69_REG_24_RSSIVALUE) >> 1)); _lastPreambleTime = millis(); +// Serial.println("PAYLOADREADY"); setModeIdle(); + // Save it in our buffer readFifo(); -// Serial.println("PAYLOADREADY"); } } +#endif // Low level function reads the FIFO and checks the address // Caution: since we put our headers in what the RH_RF69 considers to be the payload, if encryption is enabled @@ -231,18 +266,20 @@ void RH_RF69::readFifo() payloadlen >= RH_RF69_HEADER_LEN) { _rxHeaderTo = _spi.transfer(0); - // Check addressing + // Check addressing if (_promiscuous || _rxHeaderTo == _thisAddress || _rxHeaderTo == RH_BROADCAST_ADDRESS) { // Get the rest of the headers _rxHeaderFrom = _spi.transfer(0); - _rxHeaderId = _spi.transfer(0); + _rxHeaderId = _spi.transfer(0); _rxHeaderFlags = _spi.transfer(0); + // And now the real payload for (_bufLen = 0; _bufLen < (payloadlen - RH_RF69_HEADER_LEN); _bufLen++) - _buf[_bufLen] = _spi.transfer(0); + _buf[_bufLen] = _spi.transfer(0); + _rxGood++; _rxBufValid = true; } @@ -255,6 +292,7 @@ void RH_RF69::readFifo() // These are low level functions that call the interrupt handler for the correct // instance of RH_RF69. // 3 interrupts allows us to have 3 different devices +#ifndef RH_RF69_IRQLESS void RH_RF69::isr0() { if (_deviceForInterrupt[0]) @@ -270,6 +308,7 @@ void RH_RF69::isr2() if (_deviceForInterrupt[2]) _deviceForInterrupt[2]->handleInterrupt(); } +#endif int8_t RH_RF69::temperatureRead() { @@ -307,6 +346,17 @@ int8_t RH_RF69::rssiRead() void RH_RF69::setOpMode(uint8_t mode) { + /* + Serial.print("\r\nsetOpMode "); + if (mode & 0x40) Serial.print(" ListOn"); + if (mode & 0x20) Serial.print(" ListAbrt"); + if ((mode & 0x1C) == 0x00) Serial.print(" Sleep"); + if ((mode & 0x1C) == 0x04) Serial.print(" Standby"); + if ((mode & 0x1C) == 0x08) Serial.print(" FS"); + if ((mode & 0x1C) == 0x0C) Serial.print(" TX"); + if ((mode & 0x1C) == 0x10) Serial.print(" RX"); +*/ + uint8_t opmode = spiRead(RH_RF69_REG_01_OPMODE); opmode &= ~RH_RF69_OPMODE_MODE; opmode |= (mode & RH_RF69_OPMODE_MODE); @@ -369,6 +419,12 @@ void RH_RF69::setModeTx() spiWrite(RH_RF69_REG_5A_TESTPA1, RH_RF69_TESTPA1_BOOST); spiWrite(RH_RF69_REG_5C_TESTPA2, RH_RF69_TESTPA2_BOOST); } + +/* + Serial.print(" 5A_PA1=0x"); Serial.print(spiRead(RH_RF69_REG_5A_TESTPA1),HEX); + Serial.print(" 5C_PA2=0x"); Serial.print(spiRead(RH_RF69_REG_5C_TESTPA2),HEX); + Serial.print(" TxPower=0x"); Serial.println(spiRead(RH_RF69_REG_11_PALEVEL),HEX); +*/ spiWrite(RH_RF69_REG_25_DIOMAPPING1, RH_RF69_DIOMAPPING1_DIO0MAPPING_00); // Set interrupt line 0 PacketSent setOpMode(RH_RF69_OPMODE_MODE_TX); // Clears FIFO _mode = RHModeTx; @@ -428,6 +484,15 @@ bool RH_RF69::setModemConfig(ModemConfigChoice index) return true; } +bool RH_RF69::getModemConfig(ModemConfigChoice index, ModemConfig* config) +{ + if (index > (signed int)(sizeof(MODEM_CONFIG_TABLE) / sizeof(ModemConfig))) + return false; + + memcpy_P(config, &MODEM_CONFIG_TABLE[index], sizeof(RH_RF69::ModemConfig)); + + return true; +} void RH_RF69::setPreambleLength(uint16_t bytes) { spiWrite(RH_RF69_REG_2C_PREAMBLEMSB, bytes >> 8); @@ -464,10 +529,31 @@ void RH_RF69::setEncryptionKey(uint8_t* key) bool RH_RF69::available() { - if (_mode == RHModeTx) - return false; - setModeRx(); // Make sure we are receiving - return _rxBufValid; + #ifdef RH_RF69_IRQLESS + // Must look for PAYLOADREADY, not CRCOK, since only PAYLOADREADY occurs _after_ AES decryption + // has been done + + // Get the interrupt state + uint8_t irqflags2 = spiRead(RH_RF69_REG_28_IRQFLAGS2); + + if (_mode == RHModeRx && (irqflags2 & RH_RF69_IRQFLAGS2_PAYLOADREADY)) + { + // A complete message has been received with good CRC + _lastRssi = -((int8_t)(spiRead(RH_RF69_REG_24_RSSIVALUE) >> 1)); + _lastPreambleTime = millis(); + + setModeIdle(); + + // Save it in our buffer + readFifo(); + } + #endif + + if (_mode == RHModeTx) + return false; + + setModeRx(); // Make sure we are receiving + return _rxBufValid; } bool RH_RF69::recv(uint8_t* buf, uint8_t* len) @@ -507,7 +593,7 @@ bool RH_RF69::send(const uint8_t* data, uint8_t len) _spi.transfer(_txHeaderFlags); // Now the payload while (len--) - _spi.transfer(*data++); + _spi.transfer(*data++); digitalWrite(_slaveSelectPin, HIGH); ATOMIC_BLOCK_END; @@ -515,6 +601,25 @@ bool RH_RF69::send(const uint8_t* data, uint8_t len) return true; } +#ifdef RH_RF69_IRQLESS +bool RH_RF69::waitPacketSent() +{ + // If we are not currently in transmit mode, there is no packet to wait for + if (_mode != RHModeTx) + return false; + + while (!(spiRead(RH_RF69_REG_28_IRQFLAGS2) & RH_RF69_IRQFLAGS2_PACKETSENT)){ + YIELD; + } + + // A transmitter message has been fully sent + setModeIdle(); // Clears FIFO + _txGood++; + return true; +} +#endif + + uint8_t RH_RF69::maxMessageLength() { return RH_RF69_MAX_MESSAGE_LEN; diff --git a/RH_RF69.h b/RH_RF69.h index 33859e9..387b6af 100644 --- a/RH_RF69.h +++ b/RH_RF69.h @@ -262,6 +262,19 @@ #define RH_RF69_FIFOTHRESH_FIFOTHRESHOLD 0x7f // RH_RF69_REG_3D_PACKETCONFIG2 +#define RH_RF69_PACKETCONFIG2_RXRESTARTDELAY_1BIT 0x00 // Default +#define RH_RF69_PACKETCONFIG2_RXRESTARTDELAY_2BITS 0x10 +#define RH_RF69_PACKETCONFIG2_RXRESTARTDELAY_4BITS 0x20 +#define RH_RF69_PACKETCONFIG2_RXRESTARTDELAY_8BITS 0x30 +#define RH_RF69_PACKETCONFIG2_RXRESTARTDELAY_16BITS 0x40 +#define RH_RF69_PACKETCONFIG2_RXRESTARTDELAY_32BITS 0x50 +#define RH_RF69_PACKETCONFIG2_RXRESTARTDELAY_64BITS 0x60 +#define RH_RF69_PACKETCONFIG2_RXRESTARTDELAY_128BITS 0x70 +#define RH_RF69_PACKETCONFIG2_RXRESTARTDELAY_256BITS 0x80 +#define RH_RF69_PACKETCONFIG2_RXRESTARTDELAY_512BITS 0x90 +#define RH_RF69_PACKETCONFIG2_RXRESTARTDELAY_1024BITS 0xA0 +#define RH_RF69_PACKETCONFIG2_RXRESTARTDELAY_2048BITS 0xB0 +#define RH_RF69_PACKETCONFIG2_RXRESTARTDELAY_NONE 0xC0 #define RH_RF69_PACKETCONFIG2_INTERPACKETRXDELAY 0xf0 #define RH_RF69_PACKETCONFIG2_RESTARTRX 0x04 #define RH_RF69_PACKETCONFIG2_AUTORXRESTARTON 0x02 @@ -583,6 +596,7 @@ class RH_RF69 : public RHSPIDriver FSK_Rb125Fd125, ///< FSK, Whitening, Rb = 125kbs, Fd = 125kHz FSK_Rb250Fd250, ///< FSK, Whitening, Rb = 250kbs, Fd = 250kHz FSK_Rb55555Fd50, ///< FSK, Whitening, Rb = 55555kbs,Fd = 50kHz for RFM69 lib compatibility + FSK_MOTEINO, ///< FSK, No Whitening, Rb = 55555kbs,Fd = 50kHz, No DC-Free for RFM69 lib compatibility GFSK_Rb2Fd5, ///< GFSK, Whitening, Rb = 2kbs, Fd = 5kHz GFSK_Rb2_4Fd4_8, ///< GFSK, Whitening, Rb = 2.4kbs, Fd = 4.8kHz @@ -636,6 +650,10 @@ class RH_RF69 : public RHSPIDriver /// \return true if everything was successful bool init(); + + /// Configure the radio module to be in Moteino configuration + void setConfigMoteino(); + /// Reads the on-chip temperature sensor. /// The RF69 must be in Idle mode (= RF69 Standby) to measure temperature. /// The measurement is uncalibrated and without calibration, you can expect it to be far from @@ -698,6 +716,13 @@ class RH_RF69 : public RHSPIDriver /// \return true if index is a valid choice. bool setModemConfig(ModemConfigChoice index); + /// Get the values of one of the predefined modem configurations. + /// + /// \param[in] index The configuration choice. + /// \param[in] config A ModemConfig structure that will contains values of the modem configuration values. + /// \return true if index is a valid choice and config has been filled with values + bool getModemConfig(ModemConfigChoice index, ModemConfig* config); + /// Starts the receiver and checks whether a received message is available. /// This can be called multiple times in a timeout loop /// \return true if a complete, valid message has been received and is able to be retrieved by @@ -723,6 +748,13 @@ class RH_RF69 : public RHSPIDriver /// \return true if the message length was valid and it was correctly queued for transmit bool send(const uint8_t* data, uint8_t len); + /// Blocks until the current message (if any) + /// has been transmitted + /// \return true on success, false if the chip is not in transmit mode or other transmit failure + #ifdef RH_RF69_IRQLESS + virtual bool waitPacketSent(); + #endif + /// Sets the length of the preamble /// in bytes. /// Caution: this should be set to the same @@ -787,13 +819,16 @@ class RH_RF69 : public RHSPIDriver /// This is a low level function to handle the interrupts for one instance of RF69. /// Called automatically by isr*() /// Should not need to be called by user code. + #ifndef RH_RF69_IRQLESS void handleInterrupt(); + #endif /// Low level function to read the FIFO and put the received data into the receive buffer /// Should not need to be called by user code. void readFifo(); protected: + #ifndef RH_RF69_IRQLESS /// Low level interrupt service routine for RF69 connected to interrupt 0 static void isr0(); @@ -815,6 +850,7 @@ class RH_RF69 : public RHSPIDriver /// The index into _deviceForInterrupt[] for this device (if an interrupt is already allocated) /// else 0xff uint8_t _myInterruptIndex; + #endif /// The radio OP mode to use when mode is RHModeIdle uint8_t _idleMode;