Port of the nRF24l01+ library of these dudes. Not GPLed, so yeah, you can use it. Copyright (c) 2007 Stefan Engelke <mbox@stefanengelke.de> Some parts copyright (c) 2012 Eric Brundick <spirilis [at] linux dot com>

Enrf24.cpp

Committer:
heroic
Date:
2013-05-28
Revision:
0:670ecbc1478a

File content as of revision 0:670ecbc1478a:

/* nRF24L01+ I/O for mbed
 * Ported by Jas Strong <jasmine@heroicrobotics.com>
 *
 * Copyright (c) 2013 Eric Brundick <spirilis [at] linux dot com>
 *  Permission is hereby granted, free of charge, to any person 
 *  obtaining a copy of this software and associated documentation 
 *  files (the "Software"), to deal in the Software without 
 *  restriction, including without limitation the rights to use, copy, 
 *  modify, merge, publish, distribute, sublicense, and/or sell copies 
 *  of the Software, and to permit persons to whom the Software is 
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be 
 *  included in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
 *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
 *  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
 *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 *  DEALINGS IN THE SOFTWARE.
 */

#include "mbed.h"
#include <stdint.h>
#include "Enrf24.h"

/* Constructor */
Enrf24::Enrf24(PinName cePin, PinName csnPin, PinName irqPin, PinName miso, PinName mosi, PinName sck) :
    _cePin(cePin),
    _csnPin(csnPin),
    _irqPin(irqPin),
    _miso(miso),
    _mosi(mosi),
    _sck(sck),
    _spi(mosi, miso, sck)
{

  rf_status = 0;
  rf_addr_width = 5;
  txbuf_len = 0;
  readpending = 0;
}

/* Initialization */
void Enrf24::begin(uint32_t datarate, uint8_t channel)
{
  _cePin = 0;
  _csnPin = 1;

  _spi.format(8,0);
  _spi.frequency(2000000);
    
  _spi.write(0);  // Strawman transfer, fixes USCI issue on G2553

  // Is the transceiver present/alive?
  if (!_isAlive())
    return;  // Nothing more to do here...

  // Init certain registers
  _writeReg(RF24_CONFIG, 0x00);  // Deep power-down, everything disabled
  _writeReg(RF24_EN_AA, 0x03);
  _writeReg(RF24_EN_RXADDR, 0x03);
  _writeReg(RF24_RF_SETUP, 0x00);
  _writeReg(RF24_STATUS, ENRF24_IRQ_MASK);  // Clear all IRQs
  _writeReg(RF24_DYNPD, 0x03);
  _writeReg(RF24_FEATURE, RF24_EN_DPL);  // Dynamic payloads enabled by default

  // Set all parameters
  if (channel > 125)
    channel = 125;
  deepsleep();
  _issueCmd(RF24_FLUSH_TX);
  _issueCmd(RF24_FLUSH_RX);
  readpending = 0;
  _irq_clear(ENRF24_IRQ_MASK);
  setChannel(channel);
  setSpeed(datarate);
  setTXpower();
  setAutoAckParams();
  setAddressLength(rf_addr_width);
  setCRC(true);  // Default = CRC on, 8-bit
}

/* Formal shut-down/clearing of library state */
void Enrf24::end()
{
  txbuf_len = 0;
  rf_status = 0;
  rf_addr_width = 5;

  if (!_isAlive())
    return;
  deepsleep();
  _issueCmd(RF24_FLUSH_TX);
  _issueCmd(RF24_FLUSH_RX);
  readpending = 0;
  _irq_clear(ENRF24_IRQ_MASK);
  _cePin = 0;
  _csnPin = 1;
}

/* Basic SPI I/O */
uint8_t Enrf24::_readReg(uint8_t addr)
{
  uint8_t result;

  _csnPin = 0;
  rf_status = _spi.write(RF24_R_REGISTER | addr);
  result = _spi.write(RF24_NOP);
  _csnPin = 1;
  return result;
}

void Enrf24::_readRegMultiLSB(uint8_t addr, uint8_t *buf, size_t len)
{
  uint8_t i;
  _csnPin = 0;
  rf_status = _spi.write(RF24_R_REGISTER | addr);
  for (i=0; i<len; i++) {
    buf[len-i-1] = _spi.write(RF24_NOP);
  }
  _csnPin = 1;
}

void Enrf24::_writeReg(uint8_t addr, uint8_t val)
{
  _csnPin=0;
  rf_status = _spi.write(RF24_W_REGISTER | addr);
  _spi.write(val);
  _csnPin=1;
}

void Enrf24::_writeRegMultiLSB(uint8_t addr, uint8_t *buf, size_t len)
{
  size_t i;

  _csnPin = 0;
  rf_status = _spi.write(RF24_W_REGISTER | addr);
  for (i=0; i<len; i++) {
    _spi.write(buf[len-i-1]);
  }
  _csnPin = 1;
}

void Enrf24::_issueCmd(uint8_t cmd)
{
  _csnPin=0;
  rf_status = _spi.write(cmd);
  _csnPin=1;
}

void Enrf24::_issueCmdPayload(uint8_t cmd, uint8_t *buf, size_t len)
{
  size_t i;

  _csnPin = 0;
  rf_status = _spi.write(cmd);
  for (i=0; i<len; i++) {
    _spi.write(buf[i]);
  }
  _csnPin = 1;
}

void Enrf24::_readCmdPayload(uint8_t cmd, uint8_t *buf, size_t len, size_t maxlen)
{
  size_t i;

  _csnPin = 0;
  rf_status = _spi.write(cmd);
  for (i=0; i<len; i++) {
    if (i < maxlen) {
      buf[i] = _spi.write(RF24_NOP);
    } else {
      _spi.write(RF24_NOP);  // Beyond maxlen bytes, just discard the remaining data.
    }
  }
  _csnPin = 1;
}

boolean Enrf24::_isAlive()
{
  uint8_t aw;

  aw = _readReg(RF24_SETUP_AW);
  return ((aw & 0xFC) == 0x00 && (aw & 0x03) != 0x00);
}

uint8_t Enrf24::_irq_getreason()
{
  lastirq = _readReg(RF24_STATUS) & ENRF24_IRQ_MASK;
  return lastirq;
}

// Get IRQ from last known rf_status update without querying module over SPI.
uint8_t Enrf24::_irq_derivereason()
{
  lastirq = rf_status & ENRF24_IRQ_MASK;
  return lastirq;
}

void Enrf24::_irq_clear(uint8_t irq)
{
  _writeReg(RF24_STATUS, irq & ENRF24_IRQ_MASK);
}

#define ENRF24_CFGMASK_CRC(a) (a & (RF24_EN_CRC | RF24_CRCO))

void Enrf24::_readTXaddr(uint8_t *buf)
{
  _readRegMultiLSB(RF24_TX_ADDR, buf, rf_addr_width);
}

void Enrf24::_writeRXaddrP0(uint8_t *buf)
{
  _writeRegMultiLSB(RF24_RX_ADDR_P0, buf, rf_addr_width);
}


/* nRF24 I/O maintenance--called as a "hook" inside other I/O functions to give
 * the library a chance to take care of its buffers et al
 */
void Enrf24::_maintenanceHook()
{
  uint8_t i;

  _irq_getreason();

  if (lastirq & ENRF24_IRQ_TXFAILED) {
    lastTXfailed = true;
    _issueCmd(RF24_FLUSH_TX);
    _irq_clear(ENRF24_IRQ_TXFAILED);
  }

  if (lastirq & ENRF24_IRQ_TX) {
    lastTXfailed = false;
    _irq_clear(ENRF24_IRQ_TX);
  }

  if (lastirq & ENRF24_IRQ_RX) {
    if ( !(_readReg(RF24_FIFO_STATUS) & RF24_RX_FULL) ) {  /* Don't feel it's necessary
                                                            * to be notified of new
                                                            * incoming packets if the RX
                                                            * queue is full.
                                                            */
      _irq_clear(ENRF24_IRQ_RX);
    }

    /* Check if RX payload is 0-byte or >32byte (erroneous conditions)
     * Also check if data was received on pipe#0, which we are ignoring.
     * The reason for this is pipe#0 is needed for receiving AutoACK acknowledgements,
     * its address gets reset to the module's default and we do not care about data
     * coming in to that address...
     */
    _readCmdPayload(RF24_R_RX_PL_WID, &i, 1, 1);
    if (i == 0 || i > 32 || ((rf_status & 0x0E) >> 1) == 0) {
                             /* Zero-width RX payload is an error that happens a lot
                              * with non-AutoAck, and must be cleared with FLUSH_RX.
                              * Erroneous >32byte packets are a similar phenomenon.
                              */
      _issueCmd(RF24_FLUSH_RX);
      _irq_clear(ENRF24_IRQ_RX);
      readpending = 0;
    } else {
      readpending = 1;
    }
    // Actual scavenging of RX queues is performed by user-directed use of read().
  }
}



/* Public functions */
boolean Enrf24::available(boolean checkIrq)
{
  if (checkIrq && _irqPin && readpending == 0)
    return false;
  _maintenanceHook();
  if ( !(_readReg(RF24_FIFO_STATUS) & RF24_RX_EMPTY) ) {
    return true;
  }
  if (readpending) {
    return true;
  }
  return false;
}

size_t Enrf24::read(void *inbuf, uint8_t maxlen)
{
  uint8_t *buf = (uint8_t *)inbuf;
  uint8_t plwidth;

  _maintenanceHook();
  readpending = 0;
  if ((_readReg(RF24_FIFO_STATUS) & RF24_RX_EMPTY) || maxlen < 1) {
    return 0;
  }
  _readCmdPayload(RF24_R_RX_PL_WID, &plwidth, 1, 1);
  _readCmdPayload(RF24_R_RX_PAYLOAD, buf, plwidth, maxlen);
  buf[plwidth] = '\0';  // Zero-terminate in case this is a string.
  if (_irq_derivereason() & ENRF24_IRQ_RX) {
    _irq_clear(ENRF24_IRQ_RX);
  }

  return (size_t) plwidth;
}

// Perform TX of current ring-buffer contents
void Enrf24::flush()
{
  uint8_t reg, addrbuf[5];
  boolean enaa=false, origrx=false;

  if (!txbuf_len)
    return;  // Zero-length buffer?  Nothing to send!

  reg = _readReg(RF24_FIFO_STATUS);
  if (reg & BIT5) {  // RF24_TX_FULL #define is BIT0, which is not the correct bit for FIFO_STATUS.
    return;  // Should never happen, but nonetheless a precaution to take.
  }

  _maintenanceHook();

  if (reg & RF24_TX_REUSE) {
    // If somehow TX_REUSE is enabled, we need to flush the TX queue before loading our new payload.
    _issueCmd(RF24_FLUSH_TX);
  }

  if (_readReg(RF24_EN_AA) & 0x01 && (_readReg(RF24_RF_SETUP) & 0x28) != 0x20) {
    /* AutoACK enabled, must write TX addr to RX pipe#0
     * Note that 250Kbps doesn't support auto-ack, so we check RF24_RF_SETUP to verify that.
     */
    enaa = true;
    _readTXaddr(addrbuf);
    _writeRXaddrP0(addrbuf);
  }

  reg = _readReg(RF24_CONFIG);
  if ( !(reg & RF24_PWR_UP) ) {
    _writeReg(RF24_CONFIG, ENRF24_CFGMASK_IRQ | ENRF24_CFGMASK_CRC(reg) | RF24_PWR_UP);
    wait_us(5000);  // 5ms delay required for nRF24 oscillator start-up
  }
  if (reg & RF24_PRIM_RX) {
    origrx=true;
    _cePin = 0;
    _writeReg(RF24_CONFIG, ENRF24_CFGMASK_IRQ | ENRF24_CFGMASK_CRC(reg) | RF24_PWR_UP);
  }

  _issueCmdPayload(RF24_W_TX_PAYLOAD, txbuf, txbuf_len);
  _cePin = 1;
  wait_us(30);
  _cePin = 0;

  txbuf_len = 0;  // Reset TX ring buffer

  while (_irqPin)  // Wait until IRQ fires
    ;
  // IRQ fired
  _maintenanceHook();  // Handle/clear IRQ

  // Purge Pipe#0 address (set to module's power-up default)
  if (enaa) {
    addrbuf[0] = 0xE7; addrbuf[1] = 0xE7; addrbuf[2] = 0xE7; addrbuf[3] = 0xE7; addrbuf[4] = 0xE7;
    _writeRXaddrP0(addrbuf);
  }

  // If we were in RX mode before writing, return back to RX mode.
  if (origrx) {
    enableRX();
  }
}

void Enrf24::purge()
{
  txbuf_len = 0;
}

size_t Enrf24::write(uint8_t c)
{
  if (txbuf_len == 32) {  // If we're trying to stuff an already-full buffer...
    flush();  // Blocking OTA TX
  }

  txbuf[txbuf_len] = c;
  txbuf_len++;

  return 1;
}

uint8_t Enrf24::radioState()
{
  uint8_t reg;

  if (!_isAlive())
    return ENRF24_STATE_NOTPRESENT;
  
  reg = _readReg(RF24_CONFIG);
  if ( !(reg & RF24_PWR_UP) )
    return ENRF24_STATE_DEEPSLEEP;

  // At this point it's either Standby-I, II or PRX.
  if (reg & RF24_PRIM_RX) {
    if (_cePin)
      return ENRF24_STATE_PRX;
    // PRIM_RX=1 but CE=0 is a form of idle state.
    return ENRF24_STATE_IDLE;
  }
  // Check if TX queue is empty, if so it's idle, if not it's PTX.
  if (_readReg(RF24_FIFO_STATUS) & RF24_TX_EMPTY)
    return ENRF24_STATE_IDLE;
  return ENRF24_STATE_PTX;
}

void Enrf24::deepsleep()
{
  uint8_t reg;

  reg = _readReg(RF24_CONFIG);
  if (reg & (RF24_PWR_UP | RF24_PRIM_RX)) {
    _writeReg(RF24_CONFIG, ENRF24_CFGMASK_IRQ | ENRF24_CFGMASK_CRC(reg));
  }
  _cePin = 0;
}

void Enrf24::enableRX()
{
  uint8_t reg;

  reg = _readReg(RF24_CONFIG);
  _writeReg(RF24_CONFIG, ENRF24_CFGMASK_IRQ | ENRF24_CFGMASK_CRC(reg) | RF24_PWR_UP | RF24_PRIM_RX);
  _cePin = 1;

  if ( !(reg & RF24_PWR_UP) ) {  // Powering up from deep-sleep requires 5ms oscillator start delay
    wait(5 / 1000);
  }
}

void Enrf24::disableRX()
{
  uint8_t reg;

  _cePin = 0;

  reg = _readReg(RF24_CONFIG);
  if (reg & RF24_PWR_UP) {  /* Keep us in standby-I if we're coming from RX mode, otherwise stay
                             * in deep-sleep if we call this while already in PWR_UP=0 mode.
                             */
    _writeReg(RF24_CONFIG, ENRF24_CFGMASK_IRQ | ENRF24_CFGMASK_CRC(reg) | RF24_PWR_UP);
  } else {
    _writeReg(RF24_CONFIG, ENRF24_CFGMASK_IRQ | ENRF24_CFGMASK_CRC(reg));
  }
}

void Enrf24::autoAck(boolean onoff)
{
  uint8_t reg;

  reg = _readReg(RF24_EN_AA);
  if (onoff) {
    if ( !(reg & 0x01) || !(reg & 0x02) ) {
      _writeReg(RF24_EN_AA, 0x03);
    }
  } else {
    if (reg & 0x03) {
      _writeReg(RF24_EN_AA, 0x00);
    }
  }
}

void Enrf24::setChannel(uint8_t channel)
{
  if (channel > 125)
    channel = 125;
  _writeReg(RF24_RF_CH, channel);
}

void Enrf24::setTXpower(int8_t dBm)
{
  uint8_t reg, pwr;

  reg = _readReg(RF24_RF_SETUP) & 0xF8;  // preserve RF speed settings
  pwr = 0x03;
  if (dBm < 0)
    pwr = 0x02;
  if (dBm < -6)
    pwr = 0x01;
  if (dBm < -12)
    pwr = 0x00;
  _writeReg(RF24_RF_SETUP, reg | (pwr << 1));
}

void Enrf24::setSpeed(uint32_t rfspeed)
{
  uint8_t reg, spd;

  reg = _readReg(RF24_RF_SETUP) & 0xD7;  // preserve RF power settings
  spd = 0x01;
  if (rfspeed < 2000000)
    spd = 0x00;
  if (rfspeed < 1000000)
    spd = 0x04;
  _writeReg(RF24_RF_SETUP, reg | (spd << 3));
}

void Enrf24::setCRC(boolean onoff, boolean crc16bit)
{
  uint8_t reg, crcbits=0;

  reg = _readReg(RF24_CONFIG) & 0xF3;  // preserve IRQ mask, PWR_UP/PRIM_RX settings
  if (onoff)
    crcbits |= RF24_EN_CRC;
  if (crc16bit)
    crcbits |= RF24_CRCO;
  _writeReg(RF24_CONFIG, reg | crcbits);
}

void Enrf24::setAutoAckParams(uint8_t autoretry_count, uint16_t autoretry_timeout)
{
  uint8_t setup_retr=0;

  setup_retr = autoretry_count & 0x0F;
  autoretry_timeout -= 250;
  setup_retr |= ((autoretry_timeout / 250) & 0x0F) << 4;
  _writeReg(RF24_SETUP_RETR, setup_retr);
}

void Enrf24::setAddressLength(size_t len)
{
  if (len < 3)
    len = 3;
  if (len > 5)
    len = 5;

  _writeReg(RF24_SETUP_AW, len-2);
  rf_addr_width = len;
}

void Enrf24::setRXaddress(void *rxaddr)
{
  _writeRegMultiLSB(RF24_RX_ADDR_P1, (uint8_t*)rxaddr, rf_addr_width);
}

void Enrf24::setTXaddress(void *rxaddr)
{
  _writeRegMultiLSB(RF24_TX_ADDR, (uint8_t*)rxaddr, rf_addr_width);
}

boolean Enrf24::rfSignalDetected()
{
  uint8_t rpd;

  rpd = _readReg(RF24_RPD);
  return (boolean)rpd;
}