diff --git a/nRF24L01.h b/nRF24L01.h new file mode 100644 index 0000000..2012ce6 --- /dev/null +++ b/nRF24L01.h @@ -0,0 +1,125 @@ +/* + Copyright (c) 2007 Stefan Engelke + + 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. +*/ + +/* Memory Map */ +#define CONFIG 0x00 +#define EN_AA 0x01 +#define EN_RXADDR 0x02 +#define SETUP_AW 0x03 +#define SETUP_RETR 0x04 +#define RF_CH 0x05 +#define RF_SETUP 0x06 +#define STATUS 0x07 +#define OBSERVE_TX 0x08 +#define CD 0x09 +#define RX_ADDR_P0 0x0A +#define RX_ADDR_P1 0x0B +#define RX_ADDR_P2 0x0C +#define RX_ADDR_P3 0x0D +#define RX_ADDR_P4 0x0E +#define RX_ADDR_P5 0x0F +#define TX_ADDR 0x10 +#define RX_PW_P0 0x11 +#define RX_PW_P1 0x12 +#define RX_PW_P2 0x13 +#define RX_PW_P3 0x14 +#define RX_PW_P4 0x15 +#define RX_PW_P5 0x16 +#define FIFO_STATUS 0x17 +#define DYNPD 0x1C +#define FEATURE 0x1D + +/* Bit Mnemonics */ +#define MASK_RX_DR 6 +#define MASK_TX_DS 5 +#define MASK_MAX_RT 4 +#define EN_CRC 3 +#define CRCO 2 +#define PWR_UP 1 +#define PRIM_RX 0 +#define ENAA_P5 5 +#define ENAA_P4 4 +#define ENAA_P3 3 +#define ENAA_P2 2 +#define ENAA_P1 1 +#define ENAA_P0 0 +#define ERX_P5 5 +#define ERX_P4 4 +#define ERX_P3 3 +#define ERX_P2 2 +#define ERX_P1 1 +#define ERX_P0 0 +#define AW 0 +#define ARD 4 +#define ARC 0 +#define PLL_LOCK 4 +#define RF_DR 3 +#define RF_PWR 6 +#define RX_DR 6 +#define TX_DS 5 +#define MAX_RT 4 +#define RX_P_NO 1 +#define TX_FULL 0 +#define PLOS_CNT 4 +#define ARC_CNT 0 +#define TX_REUSE 6 +#define FIFO_FULL 5 +#define TX_EMPTY 4 +#define RX_FULL 1 +#define RX_EMPTY 0 +#define DPL_P5 5 +#define DPL_P4 4 +#define DPL_P3 3 +#define DPL_P2 2 +#define DPL_P1 1 +#define DPL_P0 0 +#define EN_DPL 2 +#define EN_ACK_PAY 1 +#define EN_DYN_ACK 0 + +/* Instruction Mnemonics */ +#define R_REGISTER 0x00 +#define W_REGISTER 0x20 +#define REGISTER_MASK 0x1F +#define ACTIVATE 0x50 +#define R_RX_PL_WID 0x60 +#define R_RX_PAYLOAD 0x61 +#define W_TX_PAYLOAD 0xA0 +#define W_ACK_PAYLOAD 0xA8 +#define FLUSH_TX 0xE1 +#define FLUSH_RX 0xE2 +#define REUSE_TX_PL 0xE3 +#define NOP 0xFF + +/* Non-P omissions */ +#define LNA_HCURR 0 + +/* P model memory Map */ +#define RPD 0x09 + +/* P model bit Mnemonics */ +#define RF_DR_LOW 5 +#define RF_DR_HIGH 3 +#define RF_PWR_LOW 1 +#define RF_PWR_HIGH 2 diff --git a/rf24.c b/rf24.c new file mode 100644 index 0000000..69f4150 --- /dev/null +++ b/rf24.c @@ -0,0 +1,270 @@ + +#include "rf24.h" +#include + + +uint8_t PTX=0; +uint8_t rf24_CONFIG = _BV(EN_CRC) | _BV(CRCO); +uint8_t rf24_EN_AA = 0x3F; //ENAA_P0 - ENAA_P5 +uint8_t rf24_EN_RXADDR = _BV(ERX_P0)|_BV(ERX_P1); +uint8_t rf24_SETUP_AW = 0x03; // 5 bytes addresses +uint8_t rf24_SETUP_RETR = 0x03; +uint8_t rf24_RF_CH = 0x02; // Channel +uint8_t rf24_RF_SETUP = _BV(RF_DR_HIGH) | _BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH); +uint8_t rf24_FEATURE = 0; +uint8_t rf24_DYNPD= 0; +uint8_t rf24_RX_PW[6] = { 32,32,0,0,0,0 }; + + + +void inline rf24_ceHi(){ + //CE_PIN_PORT |= _BV(CE_PIN_BIT); +} + +void inline rf24_ceLow(){ + //CE_PIN_PORT &= ~_BV(CE_PIN_BIT); +} + +void inline rf24_csnHi(){ + CSN_PIN_PORT |= _BV(CSN_PIN_BIT); +} + +void inline rf24_csnLow(){ + CSN_PIN_PORT &= ~_BV(CSN_PIN_BIT); +} + +void rf24_init() +{ + //CE_PIN_DDR |= _BV(CE_PIN_BIT); + CSN_PIN_DDR |= _BV(CSN_PIN_BIT); + + rf24_ceLow(); + rf24_csnHi(); + + // Initialize spi module + spi_begin(); + _delay_ms(2); + rf24_configure(); + +} + + +void inline rf24_transferSync(uint8_t *dataout,uint8_t *datain,uint8_t len){ + uint8_t i; + for(i = 0;i < len;i++){ + datain[i] = spi_transfer(dataout[i]); + } +} + +void inline rf24_transmitSync(uint8_t *dataout,uint8_t len){ + uint8_t i; + for(i = 0;i < len;i++){ + spi_transfer(dataout[i]); + } +} + +void inline rf24_configRegister(uint8_t reg, uint8_t value) +// Clocks only one byte into the given MiRF register +{ + rf24_csnLow(); + spi_transfer(W_REGISTER | (REGISTER_MASK & reg)); + spi_transfer(value); + rf24_csnHi(); +} + +void inline rf24_readRegister(uint8_t reg, uint8_t * value, uint8_t len) +// Reads an array of bytes from the given start position in the MiRF registers. +{ + rf24_csnLow(); + spi_transfer(R_REGISTER | (REGISTER_MASK & reg)); + rf24_transferSync(value,value,len); + rf24_csnHi(); +} + + + +void inline rf24_writeRegister(uint8_t reg, uint8_t * value, uint8_t len) +// Writes an array of bytes into inte the MiRF registers. +{ + rf24_csnLow(); + spi_transfer(W_REGISTER | (REGISTER_MASK & reg)); + rf24_transmitSync(value,len); + rf24_csnHi(); +} + +void rf24_getData(uint8_t * data,uint8_t len) +{ + rf24_csnLow(); // Pull down chip select + spi_transfer( R_RX_PAYLOAD ); // Send cmd to read rx payload + rf24_transferSync(data,data,len); // Read payload + rf24_csnHi(); // Pull up chip select + // NVI: per product spec, p 67, note c: + // "The RX_DR IRQ is asserted by a new packet arrival event. The procedure + // for handling this interrupt should be: 1) read payload through SPI, + // 2) clear RX_DR IRQ, 3) read FIFO_STATUS to check if there are more + // payloads available in RX FIFO, 4) if there are more data in RX FIFO, + // repeat from step 1)." + // So if we're going to clear RX_DR here, we need to check the RX FIFO + // in the dataReady() function + rf24_configRegister(STATUS,(1<to_read && to_read>0 ) len=to_read; + + rf24_csnLow(); // Pull down chip select + spi_transfer( R_RX_PAYLOAD ); // Send cmd to read rx payload + rf24_transferSync(data,data,len); // Read payload + + if(to_read!=len){ + to_read-=len; + while((to_read--)>0) // read remaining + spi_transfer(0); + } + rf24_csnHi(); // Pull up chip select + // NVI: per product spec, p 67, note c: + // "The RX_DR IRQ is asserted by a new packet arrival event. The procedure + // for handling this interrupt should be: 1) read payload through SPI, + // 2) clear RX_DR IRQ, 3) read FIFO_STATUS to check if there are more + // payloads available in RX FIFO, 4) if there are more data in RX FIFO, + // repeat from step 1)." + // So if we're going to clear RX_DR here, we need to check the RX FIFO + // in the dataReady() function + rf24_configRegister(STATUS,(1< +#include "defines.h" + + +#ifdef __cplusplus +extern "C"{ +#endif + + +void rf24_init(); + +void rf24_configRegister(uint8_t reg, uint8_t value); +void rf24_readRegister(uint8_t reg, uint8_t * value, uint8_t len); +void rf24_writeRegister(uint8_t reg, uint8_t * value, uint8_t len); + +void rf24_getData(uint8_t * data,uint8_t len); +uint8_t rf24_getDataDL(uint8_t * data,uint8_t len); +uint8_t rf24_dataReady(); +uint8_t rf24_rxFifoEmpty(); + + +void rf24_send(uint8_t * value,uint8_t len); +uint8_t rf24_isSending(); +uint8_t rf24_getStatus(); + +void rf24_powerUpRx(); +void rf24_powerUpTx(); +void rf24_powerDown(); +void rf24_flushRx(); +void rf24_configure(); + + +extern uint8_t rf24_CONFIG; +extern uint8_t rf24_EN_AA; +extern uint8_t rf24_EN_RXADDR; +extern uint8_t rf24_SETUP_AW; +extern uint8_t rf24_SETUP_RETR; +extern uint8_t rf24_RF_CH; +extern uint8_t rf24_RF_SETUP; +extern uint8_t rf24_FEATURE; +extern uint8_t rf24_DYNPD; +extern uint8_t rf24_RX_PW[6]; + +#if defined (__AVR_ATtiny84__) || defined (__AVR_ATtiny84A__) + +#define CE_PIN_PORT UNDEF +#define CE_PIN_DDR UNDEF +#define CE_PIN_BIT UNDEF + +#define CSN_PIN_PORT PORTB +#define CSN_PIN_DDR DDRB +#define CSN_PIN_BIT PB2 + +#elif defined(__AVR_ATmega32U4__) + +#define CE_PIN_PORT PORTD +#define CE_PIN_DDR DDRD +#define CE_PIN_BIT PD4 + +#define CSN_PIN_PORT PORTC +#define CSN_PIN_DDR DDRC +#define CSN_PIN_BIT PC6 + +#endif + + +#ifdef __cplusplus +} // extern "C" +#endif + diff --git a/spi.c b/spi.c new file mode 100644 index 0000000..cb4a1b8 --- /dev/null +++ b/spi.c @@ -0,0 +1,79 @@ +#include "spi.h" +#include + + +void spi_begin() { +#if defined(SPCR) +#error to write ... + // Set SS to high so a connected chip will be "deselected" by default + digitalWrite(SS, HIGH); + // When the SS pin is set as OUTPUT, it can be used as + // a general purpose output port (it doesn't influence + // SPI operations). + pinMode(SS, OUTPUT); + // Warning: if the SS pin ever becomes a LOW INPUT then SPI + // automatically switches to Slave, so the data direction of + // the SS pin MUST be kept as OUTPUT. + SPCR |= _BV(MSTR); + SPCR |= _BV(SPE); + // Set direction register for SCK and MOSI pin. + // MISO pin automatically overrides to INPUT. + // By doing this AFTER enabling SPI, we avoid accidentally + // clocking in a single bit since the lines go directly + // from "input" to SPI control. + // http://code.google.com/p/arduino/issues/detail?id=888 + pinMode(SCK, OUTPUT); + pinMode(MOSI, OUTPUT); +#else + PRR &= ~(_BV(PRUSI)); + USICR = _BV(USIWM0); + USCK_DDR |= _BV(USCK_BIT); //set the USCK pin as output + DO_DDR |= _BV(DO_BIT); //set the DO pin as output + DI_DDR &= ~_BV(DI_BIT); //set the DI pin as input +#endif +} + +uint8_t spi_transfer(uint8_t b) { +#if defined(SPCR) + SPDR = b; + while (!(SPSR & _BV(SPIF))); + return SPDR; +#else + USIDR = b; + USISR = _BV(USIOIF); + do { + USICR = _BV(USIWM0) | _BV(USICS1) | _BV(USICLK) | _BV(USITC); +// _delay_ms(1); + }while ((USISR & _BV(USIOIF)) == 0); + return USIDR; +#endif +} + +void spi_end() { +#if defined(SPCR) + SPCR &= ~_BV(SPE); +#else + USICR &= ~(_BV(USIWM1) | _BV(USIWM0)); +#endif +} + +void spi_setBitOrder(uint8_t bitOrder) +{ +#if defined(SPCR) + if(bitOrder == LSBFIRST) { + SPCR |= _BV(DORD); + } else { + SPCR &= ~(_BV(DORD)); + } +#endif +} + +void spi_setDataMode(uint8_t mode) +{ +#if defined(SPCR) + SPCR = (SPCR & ~SPI_MODE_MASK) | mode; +#else + +#endif +} + diff --git a/spi.h b/spi.h new file mode 100644 index 0000000..eabdb91 --- /dev/null +++ b/spi.h @@ -0,0 +1,29 @@ +#ifndef tinySPI_h +#define tinySPI_h + +#include +#include +#include + +#ifdef __cplusplus +extern "C"{ +#endif + + +//SPI data modes +#define SPI_MODE0 0x00 +#define SPI_MODE1 0x04 + +void spi_begin(void); +void spi_setDataMode(uint8_t spiDataMode); +uint8_t spi_transfer(uint8_t spiData); +void spi_end(void); + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif + +