339 lines
9.3 KiB
C++
339 lines
9.3 KiB
C++
|
// NRF24.cpp
|
||
|
//
|
||
|
// Copyright (C) 2012 Mike McCauley
|
||
|
// $Id: RH_NRF24.cpp,v 1.22 2016/04/04 01:40:12 mikem Exp $
|
||
|
|
||
|
#include <RH_NRF24.h>
|
||
|
|
||
|
RH_NRF24::RH_NRF24(uint8_t chipEnablePin, uint8_t slaveSelectPin, RHGenericSPI& spi)
|
||
|
:
|
||
|
RHNRFSPIDriver(slaveSelectPin, spi),
|
||
|
_rxBufValid(0)
|
||
|
{
|
||
|
_configuration = RH_NRF24_EN_CRC | RH_NRF24_CRCO; // Default: 2 byte CRC enabled
|
||
|
_chipEnablePin = chipEnablePin;
|
||
|
}
|
||
|
|
||
|
bool RH_NRF24::init()
|
||
|
{
|
||
|
// Teensy with nRF24 is unreliable at 8MHz:
|
||
|
// so is Arduino with RF73
|
||
|
_spi.setFrequency(RHGenericSPI::Frequency1MHz);
|
||
|
if (!RHNRFSPIDriver::init())
|
||
|
return false;
|
||
|
|
||
|
// Initialise the slave select pin
|
||
|
pinMode(_chipEnablePin, OUTPUT);
|
||
|
digitalWrite(_chipEnablePin, LOW);
|
||
|
|
||
|
// Clear interrupts
|
||
|
spiWriteRegister(RH_NRF24_REG_07_STATUS, RH_NRF24_RX_DR | RH_NRF24_TX_DS | RH_NRF24_MAX_RT);
|
||
|
// Enable dynamic payload length on all pipes
|
||
|
spiWriteRegister(RH_NRF24_REG_1C_DYNPD, RH_NRF24_DPL_ALL);
|
||
|
// Enable dynamic payload length, disable payload-with-ack, enable noack
|
||
|
spiWriteRegister(RH_NRF24_REG_1D_FEATURE, RH_NRF24_EN_DPL | RH_NRF24_EN_DYN_ACK);
|
||
|
// Test if there is actually a device connected and responding
|
||
|
// CAUTION: RFM73 and version 2.0 silicon may require ACTIVATE
|
||
|
if (spiReadRegister(RH_NRF24_REG_1D_FEATURE) != (RH_NRF24_EN_DPL | RH_NRF24_EN_DYN_ACK))
|
||
|
{
|
||
|
spiWrite(RH_NRF24_COMMAND_ACTIVATE, 0x73);
|
||
|
// Enable dynamic payload length, disable payload-with-ack, enable noack
|
||
|
spiWriteRegister(RH_NRF24_REG_1D_FEATURE, RH_NRF24_EN_DPL | RH_NRF24_EN_DYN_ACK);
|
||
|
if (spiReadRegister(RH_NRF24_REG_1D_FEATURE) != (RH_NRF24_EN_DPL | RH_NRF24_EN_DYN_ACK))
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Make sure we are powered down
|
||
|
setModeIdle();
|
||
|
|
||
|
// Flush FIFOs
|
||
|
flushTx();
|
||
|
flushRx();
|
||
|
|
||
|
setChannel(2); // The default, in case it was set by another app without powering down
|
||
|
setRF(RH_NRF24::DataRate2Mbps, RH_NRF24::TransmitPower0dBm);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Use the register commands to read and write the registers
|
||
|
uint8_t RH_NRF24::spiReadRegister(uint8_t reg)
|
||
|
{
|
||
|
return spiRead((reg & RH_NRF24_REGISTER_MASK) | RH_NRF24_COMMAND_R_REGISTER);
|
||
|
}
|
||
|
|
||
|
uint8_t RH_NRF24::spiWriteRegister(uint8_t reg, uint8_t val)
|
||
|
{
|
||
|
return spiWrite((reg & RH_NRF24_REGISTER_MASK) | RH_NRF24_COMMAND_W_REGISTER, val);
|
||
|
}
|
||
|
|
||
|
uint8_t RH_NRF24::spiBurstReadRegister(uint8_t reg, uint8_t* dest, uint8_t len)
|
||
|
{
|
||
|
return spiBurstRead((reg & RH_NRF24_REGISTER_MASK) | RH_NRF24_COMMAND_R_REGISTER, dest, len);
|
||
|
}
|
||
|
|
||
|
uint8_t RH_NRF24::spiBurstWriteRegister(uint8_t reg, uint8_t* src, uint8_t len)
|
||
|
{
|
||
|
return spiBurstWrite((reg & RH_NRF24_REGISTER_MASK) | RH_NRF24_COMMAND_W_REGISTER, src, len);
|
||
|
}
|
||
|
|
||
|
uint8_t RH_NRF24::statusRead()
|
||
|
{
|
||
|
// status is a side-effect of NOP, faster than reading reg 07
|
||
|
return spiCommand(RH_NRF24_COMMAND_NOP);
|
||
|
}
|
||
|
|
||
|
uint8_t RH_NRF24::flushTx()
|
||
|
{
|
||
|
return spiCommand(RH_NRF24_COMMAND_FLUSH_TX);
|
||
|
}
|
||
|
|
||
|
uint8_t RH_NRF24::flushRx()
|
||
|
{
|
||
|
return spiCommand(RH_NRF24_COMMAND_FLUSH_RX);
|
||
|
}
|
||
|
|
||
|
bool RH_NRF24::setChannel(uint8_t channel)
|
||
|
{
|
||
|
spiWriteRegister(RH_NRF24_REG_05_RF_CH, channel & RH_NRF24_RF_CH);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool RH_NRF24::setOpMode(uint8_t mode)
|
||
|
{
|
||
|
_configuration = mode;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool RH_NRF24::setNetworkAddress(uint8_t* address, uint8_t len)
|
||
|
{
|
||
|
if (len < 3 || len > 5)
|
||
|
return false;
|
||
|
|
||
|
// Set both TX_ADDR and RX_ADDR_P0 for auto-ack with Enhanced shockwave
|
||
|
spiWriteRegister(RH_NRF24_REG_03_SETUP_AW, len-2); // Mapping [3..5] = [1..3]
|
||
|
spiBurstWriteRegister(RH_NRF24_REG_0A_RX_ADDR_P0, address, len);
|
||
|
spiBurstWriteRegister(RH_NRF24_REG_10_TX_ADDR, address, len);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool RH_NRF24::setRF(DataRate data_rate, TransmitPower power)
|
||
|
{
|
||
|
uint8_t value = (power << 1) & RH_NRF24_PWR;
|
||
|
// Ugly mapping of data rates to noncontiguous 2 bits:
|
||
|
if (data_rate == DataRate250kbps)
|
||
|
value |= RH_NRF24_RF_DR_LOW;
|
||
|
else if (data_rate == DataRate2Mbps)
|
||
|
value |= RH_NRF24_RF_DR_HIGH;
|
||
|
// else DataRate1Mbps, 00
|
||
|
|
||
|
// RFM73 needs this:
|
||
|
value |= RH_NRF24_LNA_HCURR;
|
||
|
|
||
|
spiWriteRegister(RH_NRF24_REG_06_RF_SETUP, value);
|
||
|
// If we were using auto-ack, we would have to set the appropriate timeout in reg 4 here
|
||
|
// see NRF24::setRF()
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void RH_NRF24::setModeIdle()
|
||
|
{
|
||
|
if (_mode != RHModeIdle)
|
||
|
{
|
||
|
spiWriteRegister(RH_NRF24_REG_00_CONFIG, _configuration);
|
||
|
digitalWrite(_chipEnablePin, LOW);
|
||
|
_mode = RHModeIdle;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool RH_NRF24::sleep()
|
||
|
{
|
||
|
if (_mode != RHModeSleep)
|
||
|
{
|
||
|
spiWriteRegister(RH_NRF24_REG_00_CONFIG, 0); // Power Down mode
|
||
|
digitalWrite(_chipEnablePin, LOW);
|
||
|
_mode = RHModeSleep;
|
||
|
return true;
|
||
|
}
|
||
|
return false; // Already there?
|
||
|
}
|
||
|
|
||
|
void RH_NRF24::setModeRx()
|
||
|
{
|
||
|
if (_mode != RHModeRx)
|
||
|
{
|
||
|
spiWriteRegister(RH_NRF24_REG_00_CONFIG, _configuration | RH_NRF24_PWR_UP | RH_NRF24_PRIM_RX);
|
||
|
digitalWrite(_chipEnablePin, HIGH);
|
||
|
_mode = RHModeRx;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void RH_NRF24::setModeTx()
|
||
|
{
|
||
|
if (_mode != RHModeTx)
|
||
|
{
|
||
|
// Its the CE rising edge that puts us into TX mode
|
||
|
// CE staying high makes us go to standby-II when the packet is sent
|
||
|
digitalWrite(_chipEnablePin, LOW);
|
||
|
// Ensure DS is not set
|
||
|
spiWriteRegister(RH_NRF24_REG_07_STATUS, RH_NRF24_TX_DS | RH_NRF24_MAX_RT);
|
||
|
spiWriteRegister(RH_NRF24_REG_00_CONFIG, _configuration | RH_NRF24_PWR_UP);
|
||
|
digitalWrite(_chipEnablePin, HIGH);
|
||
|
_mode = RHModeTx;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool RH_NRF24::send(const uint8_t* data, uint8_t len)
|
||
|
{
|
||
|
if (len > RH_NRF24_MAX_MESSAGE_LEN)
|
||
|
return false;
|
||
|
// Set up the headers
|
||
|
_buf[0] = _txHeaderTo;
|
||
|
_buf[1] = _txHeaderFrom;
|
||
|
_buf[2] = _txHeaderId;
|
||
|
_buf[3] = _txHeaderFlags;
|
||
|
memcpy(_buf+RH_NRF24_HEADER_LEN, data, len);
|
||
|
spiBurstWrite(RH_NRF24_COMMAND_W_TX_PAYLOAD_NOACK, _buf, len + RH_NRF24_HEADER_LEN);
|
||
|
setModeTx();
|
||
|
// Radio will return to Standby II mode after transmission is complete
|
||
|
_txGood++;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool RH_NRF24::waitPacketSent()
|
||
|
{
|
||
|
// If we are not currently in transmit mode, there is no packet to wait for
|
||
|
if (_mode != RHModeTx)
|
||
|
return false;
|
||
|
|
||
|
// Wait for either the Data Sent or Max ReTries flag, signalling the
|
||
|
// end of transmission
|
||
|
// We dont actually use auto-ack, so prob dont expect to see RH_NRF24_MAX_RT
|
||
|
uint8_t status;
|
||
|
while (!((status = statusRead()) & (RH_NRF24_TX_DS | RH_NRF24_MAX_RT)))
|
||
|
YIELD;
|
||
|
|
||
|
// Must clear RH_NRF24_MAX_RT if it is set, else no further comm
|
||
|
if (status & RH_NRF24_MAX_RT)
|
||
|
flushTx();
|
||
|
setModeIdle();
|
||
|
spiWriteRegister(RH_NRF24_REG_07_STATUS, RH_NRF24_TX_DS | RH_NRF24_MAX_RT);
|
||
|
// Return true if data sent, false if MAX_RT
|
||
|
return status & RH_NRF24_TX_DS;
|
||
|
}
|
||
|
|
||
|
bool RH_NRF24::isSending()
|
||
|
{
|
||
|
return !(spiReadRegister(RH_NRF24_REG_00_CONFIG) & RH_NRF24_PRIM_RX) &&
|
||
|
!(statusRead() & (RH_NRF24_TX_DS | RH_NRF24_MAX_RT));
|
||
|
}
|
||
|
|
||
|
bool RH_NRF24::printRegisters()
|
||
|
{
|
||
|
#ifdef RH_HAVE_SERIAL
|
||
|
// Iterate over register range, but don't process registers not in use.
|
||
|
for (uint8_t r = RH_NRF24_REG_00_CONFIG; r <= RH_NRF24_REG_1D_FEATURE; r++)
|
||
|
{
|
||
|
if ((r <= RH_NRF24_REG_17_FIFO_STATUS) || (r >= RH_NRF24_REG_1C_DYNPD))
|
||
|
{
|
||
|
Serial.print(r, HEX);
|
||
|
Serial.print(": ");
|
||
|
uint8_t len = 1;
|
||
|
// Address registers are 5 bytes in size
|
||
|
if ( (RH_NRF24_REG_0A_RX_ADDR_P0 == r)
|
||
|
|| (RH_NRF24_REG_0B_RX_ADDR_P1 == r)
|
||
|
|| (RH_NRF24_REG_10_TX_ADDR == r) )
|
||
|
{
|
||
|
len = 5;
|
||
|
}
|
||
|
uint8_t buf[5];
|
||
|
spiBurstReadRegister(r, buf, len);
|
||
|
for (uint8_t j = 0; j < len; ++j)
|
||
|
{
|
||
|
Serial.print(buf[j], HEX);
|
||
|
Serial.print(" ");
|
||
|
}
|
||
|
Serial.println("");
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Check whether the latest received message is complete and uncorrupted
|
||
|
void RH_NRF24::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_NRF24::available()
|
||
|
{
|
||
|
if (!_rxBufValid)
|
||
|
{
|
||
|
if (_mode == RHModeTx)
|
||
|
return false;
|
||
|
setModeRx();
|
||
|
if (spiReadRegister(RH_NRF24_REG_17_FIFO_STATUS) & RH_NRF24_RX_EMPTY)
|
||
|
return false;
|
||
|
// Manual says that messages > 32 octets should be discarded
|
||
|
uint8_t len = spiRead(RH_NRF24_COMMAND_R_RX_PL_WID);
|
||
|
if (len > 32)
|
||
|
{
|
||
|
flushRx();
|
||
|
clearRxBuf();
|
||
|
setModeIdle();
|
||
|
return false;
|
||
|
}
|
||
|
// Clear read interrupt
|
||
|
spiWriteRegister(RH_NRF24_REG_07_STATUS, RH_NRF24_RX_DR);
|
||
|
// Get the message into the RX buffer, so we can inspect the headers
|
||
|
spiBurstRead(RH_NRF24_COMMAND_R_RX_PAYLOAD, _buf, len);
|
||
|
_bufLen = len;
|
||
|
// 140 microsecs (32 octet payload)
|
||
|
validateRxBuf();
|
||
|
if (_rxBufValid)
|
||
|
setModeIdle(); // Got one
|
||
|
}
|
||
|
return _rxBufValid;
|
||
|
}
|
||
|
|
||
|
void RH_NRF24::clearRxBuf()
|
||
|
{
|
||
|
_rxBufValid = false;
|
||
|
_bufLen = 0;
|
||
|
}
|
||
|
|
||
|
bool RH_NRF24::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_NRF24_HEADER_LEN)
|
||
|
*len = _bufLen-RH_NRF24_HEADER_LEN;
|
||
|
memcpy(buf, _buf+RH_NRF24_HEADER_LEN, *len);
|
||
|
}
|
||
|
clearRxBuf(); // This message accepted and cleared
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
uint8_t RH_NRF24::maxMessageLength()
|
||
|
{
|
||
|
return RH_NRF24_MAX_MESSAGE_LEN;
|
||
|
}
|