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; }