V148
Fork of RadioHead-148 by
Diff: RH_NRF24.cpp
- Revision:
- 0:ab4e012489ef
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/RH_NRF24.cpp Thu Oct 15 01:27:00 2015 +0000 @@ -0,0 +1,338 @@ +// NRF24.cpp +// +// Copyright (C) 2012 Mike McCauley +// $Id: RH_NRF24.cpp,v 1.21 2015/03/29 03:53:47 mikem Exp $ + +#include <RH_NRF24.h> + +RH_NRF24::RH_NRF24(PINS chipEnablePin, PINS 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 +#if (RH_PLATFORM != RH_PLATFORM_MBED) + pinMode(_chipEnablePin, OUTPUT); +#endif + 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; + } +} + +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; +}