Skip to content

Commit

Permalink
Added IRQLESS mode, moteino and modem config
Browse files Browse the repository at this point in the history
  • Loading branch information
hallard committed Jul 22, 2016
1 parent 814a0b8 commit 6cdc933
Show file tree
Hide file tree
Showing 2 changed files with 158 additions and 17 deletions.
139 changes: 122 additions & 17 deletions RH_RF69.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -87,33 +93,56 @@ 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)
{
_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 <[email protected]> for Teensy
// ARM M4 requires the below. else pin interrupt doesn't work properly.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -184,23 +213,27 @@ bool RH_RF69::init()
// +13dBm, same as power-on default
setTxPower(13);

// override and set config to moteino
setConfigMoteino();

return true;
}

// C++ level interrupt handler for this instance
// 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
Expand All @@ -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
Expand All @@ -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;
}
Expand All @@ -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])
Expand All @@ -270,6 +308,7 @@ void RH_RF69::isr2()
if (_deviceForInterrupt[2])
_deviceForInterrupt[2]->handleInterrupt();
}
#endif

int8_t RH_RF69::temperatureRead()
{
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -507,14 +593,33 @@ 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;

setModeTx(); // Start the transmitter
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;
Expand Down
36 changes: 36 additions & 0 deletions RH_RF69.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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();

Expand All @@ -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;
Expand Down

0 comments on commit 6cdc933

Please sign in to comment.