267 lines
6.6 KiB
C++
267 lines
6.6 KiB
C++
|
// RH_NRF905.cpp
|
||
|
//
|
||
|
// Copyright (C) 2012 Mike McCauley
|
||
|
// $Id: RH_NRF905.cpp,v 1.6 2015/12/11 01:10:24 mikem Exp $
|
||
|
|
||
|
#include <RH_NRF905.h>
|
||
|
|
||
|
RH_NRF905::RH_NRF905(uint8_t chipEnablePin, uint8_t txEnablePin, uint8_t slaveSelectPin, RHGenericSPI& spi)
|
||
|
:
|
||
|
RHNRFSPIDriver(slaveSelectPin, spi)
|
||
|
{
|
||
|
_chipEnablePin = chipEnablePin;
|
||
|
_txEnablePin = txEnablePin;
|
||
|
}
|
||
|
|
||
|
bool RH_NRF905::init()
|
||
|
{
|
||
|
#if defined (__MK20DX128__) || defined (__MK20DX256__)
|
||
|
// Teensy is unreliable at 8MHz:
|
||
|
_spi.setFrequency(RHGenericSPI::Frequency1MHz);
|
||
|
#else
|
||
|
_spi.setFrequency(RHGenericSPI::Frequency8MHz);
|
||
|
#endif
|
||
|
if (!RHNRFSPIDriver::init())
|
||
|
return false;
|
||
|
|
||
|
// Initialise the slave select pin and the tx Enable pin
|
||
|
pinMode(_chipEnablePin, OUTPUT);
|
||
|
pinMode(_txEnablePin, OUTPUT);
|
||
|
digitalWrite(_chipEnablePin, LOW);
|
||
|
digitalWrite(_txEnablePin, LOW);
|
||
|
|
||
|
// Configure the chip
|
||
|
// CRC 16 bits enabled. 16MHz crystal freq
|
||
|
spiWriteRegister(RH_NRF905_CONFIG_9, RH_NRF905_CONFIG_9_CRC_EN | RH_NRF905_CONFIG_9_CRC_MODE_16BIT | RH_NRF905_CONFIG_9_XOF_16MHZ);
|
||
|
|
||
|
// Make sure we are powered down
|
||
|
setModeIdle();
|
||
|
|
||
|
// Some innocuous defaults
|
||
|
setChannel(108, LOW); // 433.2 MHz
|
||
|
setRF(RH_NRF905::TransmitPowerm10dBm);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Use the register commands to read and write the registers
|
||
|
uint8_t RH_NRF905::spiReadRegister(uint8_t reg)
|
||
|
{
|
||
|
return spiRead((reg & RH_NRF905_REG_MASK) | RH_NRF905_REG_R_CONFIG);
|
||
|
}
|
||
|
|
||
|
uint8_t RH_NRF905::spiWriteRegister(uint8_t reg, uint8_t val)
|
||
|
{
|
||
|
return spiWrite((reg & RH_NRF905_REG_MASK) | RH_NRF905_REG_W_CONFIG, val);
|
||
|
}
|
||
|
|
||
|
uint8_t RH_NRF905::spiBurstReadRegister(uint8_t reg, uint8_t* dest, uint8_t len)
|
||
|
{
|
||
|
return spiBurstRead((reg & RH_NRF905_REG_MASK) | RH_NRF905_REG_R_CONFIG, dest, len);
|
||
|
}
|
||
|
|
||
|
uint8_t RH_NRF905::spiBurstWriteRegister(uint8_t reg, uint8_t* src, uint8_t len)
|
||
|
{
|
||
|
return spiBurstWrite((reg & RH_NRF905_REG_MASK) | RH_NRF905_REG_W_CONFIG, src, len);
|
||
|
}
|
||
|
|
||
|
uint8_t RH_NRF905::statusRead()
|
||
|
{
|
||
|
// The status is a byproduct of sending a command
|
||
|
return spiCommand(0);
|
||
|
}
|
||
|
|
||
|
bool RH_NRF905::setChannel(uint16_t channel, bool hiFrequency)
|
||
|
{
|
||
|
spiWriteRegister(RH_NRF905_CONFIG_0, channel & RH_NRF905_CONFIG_0_CH_NO);
|
||
|
// Set or clear the high bit of the channel
|
||
|
uint8_t bit8 = (channel >> 8) & 0x01;
|
||
|
uint8_t reg1 = spiReadRegister(RH_NRF905_CONFIG_1);
|
||
|
reg1 = (reg1 & ~0x01) | bit8;
|
||
|
// Set or clear the HFREQ_PLL bit
|
||
|
reg1 &= ~RH_NRF905_CONFIG_1_HFREQ_PLL;
|
||
|
if (hiFrequency)
|
||
|
reg1 |= RH_NRF905_CONFIG_1_HFREQ_PLL;
|
||
|
spiWriteRegister(RH_NRF905_CONFIG_1, reg1);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool RH_NRF905::setNetworkAddress(uint8_t* address, uint8_t len)
|
||
|
{
|
||
|
if (len < 1 || len > 4)
|
||
|
return false;
|
||
|
// Set RX_AFW and TX_AFW
|
||
|
spiWriteRegister(RH_NRF905_CONFIG_2, len | (len << 4));
|
||
|
spiBurstWrite(RH_NRF905_REG_W_TX_ADDRESS, address, len);
|
||
|
spiBurstWriteRegister(RH_NRF905_CONFIG_5, address, len);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool RH_NRF905::setRF(TransmitPower power)
|
||
|
{
|
||
|
// Enum definitions of power are the same numerical values as the register
|
||
|
uint8_t reg1 = spiReadRegister(RH_NRF905_CONFIG_1);
|
||
|
reg1 &= ~RH_NRF905_CONFIG_1_PA_PWR;
|
||
|
reg1 |= ((power & 0x3) << 2) & RH_NRF905_CONFIG_1_PA_PWR;
|
||
|
spiWriteRegister(RH_NRF905_CONFIG_1, reg1);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void RH_NRF905::setModeIdle()
|
||
|
{
|
||
|
if (_mode != RHModeIdle)
|
||
|
{
|
||
|
digitalWrite(_chipEnablePin, LOW);
|
||
|
digitalWrite(_txEnablePin, LOW);
|
||
|
_mode = RHModeIdle;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void RH_NRF905::setModeRx()
|
||
|
{
|
||
|
if (_mode != RHModeRx)
|
||
|
{
|
||
|
digitalWrite(_txEnablePin, LOW);
|
||
|
digitalWrite(_chipEnablePin, HIGH);
|
||
|
_mode = RHModeRx;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void RH_NRF905::setModeTx()
|
||
|
{
|
||
|
if (_mode != RHModeTx)
|
||
|
{
|
||
|
// Its the high transition that puts us into TX mode
|
||
|
digitalWrite(_txEnablePin, HIGH);
|
||
|
digitalWrite(_chipEnablePin, HIGH);
|
||
|
_mode = RHModeTx;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool RH_NRF905::send(const uint8_t* data, uint8_t len)
|
||
|
{
|
||
|
if (len > RH_NRF905_MAX_MESSAGE_LEN)
|
||
|
return false;
|
||
|
// Set up the headers
|
||
|
_buf[0] = _txHeaderTo;
|
||
|
_buf[1] = _txHeaderFrom;
|
||
|
_buf[2] = _txHeaderId;
|
||
|
_buf[3] = _txHeaderFlags;
|
||
|
_buf[4] = len;
|
||
|
memcpy(_buf+RH_NRF905_HEADER_LEN, data, len);
|
||
|
spiBurstWrite(RH_NRF905_REG_W_TX_PAYLOAD, _buf, len + RH_NRF905_HEADER_LEN);
|
||
|
setModeTx();
|
||
|
// Radio will return to Standby mode after transmission is complete
|
||
|
_txGood++;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool RH_NRF905::waitPacketSent()
|
||
|
{
|
||
|
if (_mode != RHModeTx)
|
||
|
return false;
|
||
|
|
||
|
while (!(statusRead() & RH_NRF905_STATUS_DR))
|
||
|
YIELD;
|
||
|
setModeIdle();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool RH_NRF905::isSending()
|
||
|
{
|
||
|
if (_mode != RHModeTx)
|
||
|
return false;
|
||
|
|
||
|
return !(statusRead() & RH_NRF905_STATUS_DR);
|
||
|
}
|
||
|
|
||
|
bool RH_NRF905::printRegister(uint8_t reg)
|
||
|
{
|
||
|
#ifdef RH_HAVE_SERIAL
|
||
|
Serial.print(reg, HEX);
|
||
|
Serial.print(": ");
|
||
|
Serial.println(spiReadRegister(reg), HEX);
|
||
|
#endif
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool RH_NRF905::printRegisters()
|
||
|
{
|
||
|
uint8_t registers[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09};
|
||
|
|
||
|
uint8_t i;
|
||
|
for (i = 0; i < sizeof(registers); i++)
|
||
|
printRegister(registers[i]);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Check whether the latest received message is complete and uncorrupted
|
||
|
void RH_NRF905::validateRxBuf()
|
||
|
{
|
||
|
// Check the length
|
||
|
uint8_t len = _buf[4];
|
||
|
if (len > RH_NRF905_MAX_MESSAGE_LEN)
|
||
|
return; // Silly LEN header
|
||
|
|
||
|
// 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++;
|
||
|
_bufLen = len + RH_NRF905_HEADER_LEN; // _buf still includes the headers
|
||
|
_rxBufValid = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool RH_NRF905::available()
|
||
|
{
|
||
|
if (!_rxBufValid)
|
||
|
{
|
||
|
if (_mode == RHModeTx)
|
||
|
return false;
|
||
|
setModeRx();
|
||
|
if (!(statusRead() & RH_NRF905_STATUS_DR))
|
||
|
return false;
|
||
|
// Get the message into the RX buffer, so we can inspect the headers
|
||
|
// we still dont know how long is the user message
|
||
|
spiBurstRead(RH_NRF905_REG_R_RX_PAYLOAD, _buf, RH_NRF905_MAX_PAYLOAD_LEN);
|
||
|
validateRxBuf();
|
||
|
if (_rxBufValid)
|
||
|
setModeIdle(); // Got one
|
||
|
|
||
|
}
|
||
|
return _rxBufValid;
|
||
|
}
|
||
|
|
||
|
void RH_NRF905::clearRxBuf()
|
||
|
{
|
||
|
_rxBufValid = false;
|
||
|
_bufLen = 0;
|
||
|
}
|
||
|
|
||
|
bool RH_NRF905::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_NRF905_HEADER_LEN)
|
||
|
*len = _bufLen-RH_NRF905_HEADER_LEN;
|
||
|
memcpy(buf, _buf+RH_NRF905_HEADER_LEN, *len);
|
||
|
}
|
||
|
clearRxBuf(); // This message accepted and cleared
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
uint8_t RH_NRF905::maxMessageLength()
|
||
|
{
|
||
|
return RH_NRF905_MAX_MESSAGE_LEN;
|
||
|
}
|