nRF24L01 driver

Dependents:   Nucleo_IOT1 wireless

Files at this revision

API Documentation at this revision

Comitter:
ianmcc
Date:
Sun Aug 11 11:58:05 2013 +0000
Child:
1:cd113026825f
Commit message:
Initial

Changed in this revision

nRF24L01P.cpp Show annotated file Show diff for this revision Revisions of this file
nRF24L01P.h Show annotated file Show diff for this revision Revisions of this file
nRF24L01P_PRX.cpp Show annotated file Show diff for this revision Revisions of this file
nRF24L01P_PRX.h Show annotated file Show diff for this revision Revisions of this file
nRF24L01P_PTX.cpp Show annotated file Show diff for this revision Revisions of this file
nRF24L01P_PTX.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nRF24L01P.cpp	Sun Aug 11 11:58:05 2013 +0000
@@ -0,0 +1,555 @@
+#include "nRF24L01P.h"
+
+#define NRF24L01P_SPI_MAX_DATA_RATE     10000000
+
+#define NRF24L01P_CMD_R_REGISTER        0x00
+#define NRF24L01P_CMD_W_REGISTER        0x20
+#define NRF24L01P_CMD_R_RX_PAYLOAD      0x61   
+#define NRF24L01P_CMD_W_TX_PAYLOAD      0xA0
+#define NRF24L01P_CMD_FLUSH_TX          0xE1
+#define NRF24L01P_CMD_FLUSH_RX          0xE2
+#define NRF24L01P_CMD_REUSE_TX_PL       0xE3
+#define NRF24L01P_CMD_R_RX_PL_WID       0x60
+#define NRF24L01P_CMD_W_ACK_PAYLOAD     0xA8
+#define NRF24L01P_CMD_W_TX_PYLD_NO_ACK  0xB0
+#define NRF24L01P_CMD_NOP               0xFF
+
+// registers
+#define NRF24L01P_CONFIG                0x00
+#define NRF24L01P_EN_AA                 0x01
+#define NRF24L01P_EN_RXADDR             0x02
+#define NRF24L01P_SETUP_AW              0x03
+#define NRF24L01P_SETUP_RETR            0x04
+#define NRF24L01P_RF_CH                 0x05
+#define NRF24L01P_RF_SETUP              0x06
+#define NRF24L01P_STATUS                0x07
+#define NRF24L01P_OBSERVE_TX            0x08
+#define NRF24L01P_RPD                   0x09
+#define NRF24L01P_RX_ADDR_P0            0x0A
+#define NRF24L01P_RX_ADDR_P1            0x0B
+#define NRF24L01P_RX_ADDR_P2            0x0C
+#define NRF24L01P_RX_ADDR_P3            0x0D
+#define NRF24L01P_RX_ADDR_P4            0x0E
+#define NRF24L01P_RX_ADDR_P5            0x0F
+#define NRF24L01P_TX_ADDR               0x10
+#define NRF24L01P_RX_PW_P0              0x11
+#define NRF24L01P_RX_PW_P1              0x12
+#define NRF24L01P_RX_PW_P2              0x13
+#define NRF24L01P_RX_PW_P3              0x14
+#define NRF24L01P_RX_PW_P4              0x15
+#define NRF24L01P_RX_PW_P5              0x16
+#define NRF24L01P_FIFO_STATUS           0x17
+#define NRF24L01P_DYNPD                 0x1C
+#define NRF24L01P_FEATURE               0x1D
+
+#define NRF24L01P_MAX                   0x1F
+
+nRF24L01P::nRF24L01P(PinName mosi, PinName miso, PinName sck, PinName csn, long SPIFrequency)
+ : Spi(mosi, miso, sck), 
+   NCS(csn)
+{
+   Spi.format(8,0);                      // 8-bit, ClockPhase = 0, ClockPolarity = 0
+   Spi.frequency(SPIFrequency);
+   NCS = 1;
+   this->reset();
+}
+
+void
+nRF24L01P::reset()
+{
+   this->write_register(NRF24L01P_CONFIG,        0x08); // power down
+   this->flush_tx_fifo();
+   this->flush_rx_fifo();
+   this->write_register(NRF24L01P_EN_AA,         0x3F);
+   this->write_register(NRF24L01P_EN_RXADDR,     0x03);
+   this->write_register(NRF24L01P_SETUP_AW,      0x03);
+   this->write_register(NRF24L01P_SETUP_RETR,    0x03);
+   this->write_register(NRF24L01P_RF_CH,         0x02);
+   this->write_register(NRF24L01P_RF_SETUP,      0x07);
+   this->write_register(NRF24L01P_STATUS,        0x0E); 
+   this->write_register_40(NRF24L01P_RX_ADDR_P0, 0xE7E7E7E7E7ULL);
+   this->write_register_40(NRF24L01P_RX_ADDR_P1, 0xC2C2C2C2C2ULL);
+   this->write_register(NRF24L01P_RX_ADDR_P2,    0xC3);
+   this->write_register(NRF24L01P_RX_ADDR_P3,    0xC4);
+   this->write_register(NRF24L01P_RX_ADDR_P4,    0xC4);
+   this->write_register(NRF24L01P_RX_ADDR_P5,    0xC5);
+   this->write_register_40(NRF24L01P_TX_ADDR,    0xE7E7E7E7E7ULL);    
+   this->write_register(NRF24L01P_RX_PW_P0,      0x00);
+   this->write_register(NRF24L01P_RX_PW_P1,      0x00);
+   this->write_register(NRF24L01P_RX_PW_P2,      0x00);
+   this->write_register(NRF24L01P_RX_PW_P3,      0x00);
+   this->write_register(NRF24L01P_RX_PW_P4,      0x00);
+   this->write_register(NRF24L01P_RX_PW_P5,      0x00);
+   this->write_register(NRF24L01P_FIFO_STATUS,   0x00);
+   this->write_register(NRF24L01P_DYNPD,         0x00);
+   this->write_register(NRF24L01P_FEATURE,       0x00); 
+}
+
+void
+nRF24L01P::set_spi_frequency(long Freq)
+{
+   Spi.frequency(Freq);
+}
+
+void
+nRF24L01P::set_air_data_rate(int Rate)
+{
+   char r = 0;
+   if (Rate == 250)
+      r = 0x20;
+   else if (Rate == 1000)
+      r = 0x00;
+   else if (Rate == 2000)
+      r = 0x04;
+   char c = this->read_register(NRF24L01P_RF_SETUP);
+   this->write_register(NRF24L01P_RF_SETUP, (c & 0xDB) | r);
+}
+
+void
+nRF24L01P::set_channel(int Channel)
+{
+   char c = Channel;
+   c = c & 0x7F;
+   this->write_register(NRF24L01P_RF_CH, c);   
+}
+
+bool
+nRF24L01P::received_power_detector()
+{
+   return this->read_register(NRF24L01P_RPD) == 0x01;
+}
+
+void
+nRF24L01P::set_tx_power(int Power)
+{
+   char p = 0;
+   if (Power == 0)
+      p = 0x03;
+   if (Power == -6)
+      p = 0x02;
+   else if (Power == -12)
+      p = 0x01;
+   else if (Power == -18)
+      p = 0x00;
+   char c = this->read_register(NRF24L01P_RF_SETUP);
+   this->write_register(NRF24L01P_RF_SETUP, (c & 0xFC) | p);     
+}
+
+void
+nRF24L01P::enable_dynamic_payload(int Pipe, bool Enable)
+{
+   char Mask = 1 << Pipe;
+   char c = this->read_register(NRF24L01P_DYNPD);
+   if (Enable)
+      c = c | Mask;
+   else
+      c = c & ~Mask;
+   this->write_register(NRF24L01P_DYNPD, c);      
+}
+
+void
+nRF24L01P::enable_ack_payload(bool Enable)
+{
+   char c = this->read_register(NRF24L01P_FEATURE);
+   this->write_register(NRF24L01P_FEATURE, Enable ? (c | 0x02) : (c & 0xFD));
+}
+
+void
+nRF24L01P::enable_no_ack(bool Enable)
+{
+   char c = this->read_register(NRF24L01P_FEATURE);
+   this->write_register(NRF24L01P_FEATURE, Enable ? (c | 0x01) : (c & 0xFE));
+}
+
+void
+nRF24L01P::set_address_width(int Width)
+{
+   char c = 0;
+   if (Width == 3)
+      c = 0x01;
+   else if (Width == 4)
+      c = 0x02;
+   else if (Width == 5)
+      c = 0x03;
+   this->write_register(NRF24L01P_SETUP_AW, c);
+}
+
+int
+nRF24L01P::get_address_width()
+{
+   int r = this->read_register(NRF24L01P_SETUP_AW);
+   switch (r & 0x03)
+   {
+      case 0x01 : return 3;
+      case 0x02 : return 4;
+      case 0x03 : return 5;
+   }
+   return 0;
+}
+
+void
+nRF24L01P::set_rx_address(int Pipe, uint64_t Address)
+{
+   if (Pipe < 0 || Pipe > 1)
+      error("RF24L01P::set_rx_address: invalid pipe");
+   this->write_register_40(NRF24L01P_RX_ADDR_P0+Pipe, Address);
+}
+
+void
+nRF24L01P::set_rx_address_low(int Pipe, uint8_t Address)
+{
+   this->write_register(NRF24L01P_RX_ADDR_P0+Pipe, Address);
+}
+
+void
+nRF24L01P::set_rx_payload_bytes(int Pipe, int Bytes)
+{
+   this->write_register(NRF24L01P_RX_PW_P0+Pipe, Bytes & 0x1F);
+}
+
+bool
+nRF24L01P::is_rx_full()
+{
+   return (this->read_register(NRF24L01P_FIFO_STATUS) & 0x02) != 0x00;
+}
+
+bool
+nRF24L01P::is_rx_empty()
+{
+   return (this->read_register(NRF24L01P_FIFO_STATUS) & 0x01) != 0x00;
+}
+
+bool
+nRF24L01P::is_rx_ready()
+{
+   return (this->read_register(NRF24L01P_STATUS) & 0x40) != 0x00;
+}
+
+void
+nRF24L01P::clear_rx_ready()
+{
+   char c = this->read_register(NRF24L01P_STATUS);
+   this->write_register(NRF24L01P_STATUS, c | 0x40);
+}
+
+void
+nRF24L01P::set_interrupt_rx_ready(bool Enable)
+{
+   char c = this->read_register(NRF24L01P_CONFIG);
+   this->write_register(NRF24L01P_CONFIG, Enable ? (c & 0xBF) : (c | 0x40));
+}
+
+bool
+nRF24L01P::is_tx_full()
+{
+   return (this->read_register(NRF24L01P_FIFO_STATUS) & 0x20) != 0x00;
+}
+
+bool
+nRF24L01P::is_tx_empty()
+{
+   return (this->read_register(NRF24L01P_FIFO_STATUS) & 0x10) != 0x00;
+}
+
+bool
+nRF24L01P::is_tx_sent()
+{
+   return (this->read_register(NRF24L01P_STATUS) & 0x20) != 0x00;
+}
+
+void
+nRF24L01P::clear_tx_sent()
+{
+   char c = this->read_register(NRF24L01P_STATUS);
+   this->write_register(NRF24L01P_STATUS, c | 0x20);
+}
+
+void
+nRF24L01P::set_interrupt_tx(bool Enable)
+{
+   char c = this->read_register(NRF24L01P_CONFIG);
+   this->write_register(NRF24L01P_CONFIG, Enable ? (c & 0xDF) : (c | 0x20));
+}
+
+void
+nRF24L01P::set_retransmit_delay(int Delay)
+{
+}
+
+bool
+nRF24L01P::is_max_rt()
+{
+   return (this->read_register(NRF24L01P_STATUS) & 0x10) != 0x00;
+}
+
+void
+nRF24L01P::clear_max_rt()
+{
+   char c = this->read_register(NRF24L01P_STATUS);
+   this->write_register(NRF24L01P_STATUS, c | 0x10);
+}
+
+void
+nRF24L01P::set_interrupt_max_rt(bool Enable)
+{
+   char c = this->read_register(NRF24L01P_CONFIG);
+   this->write_register(NRF24L01P_CONFIG, Enable ? (c & 0xEF) : (c | 0x10));
+}
+
+void
+nRF24L01P::set_crc_width(int Width)
+{
+   char c = this->read_register(NRF24L01P_CONFIG);
+   this->write_register(NRF24L01P_CONFIG, Width == 1 ? (c & 0xFB) : (c | 0x04));
+}
+
+void
+nRF24L01P::enable_crc(bool Enable)
+{
+   char c = this->read_register(NRF24L01P_CONFIG);
+   this->write_register(NRF24L01P_CONFIG, Enable ? (c | 0x08) : (c & 0xF7));
+}
+
+
+void
+nRF24L01P::set_power_up()
+{
+   char c = this->read_register(NRF24L01P_CONFIG);
+   this->write_register(NRF24L01P_CONFIG, c | 0x02);
+}
+
+void
+nRF24L01P::set_power_down()
+{
+   char c = this->read_register(NRF24L01P_CONFIG);
+   this->write_register(NRF24L01P_CONFIG, c & 0xFD);
+}
+
+void
+nRF24L01P::set_prx_mode()
+{
+   char c = this->read_register(NRF24L01P_CONFIG);
+   this->write_register(NRF24L01P_CONFIG, c | 0x01);
+}
+
+void
+nRF24L01P::set_ptx_mode()
+{
+   char c = this->read_register(NRF24L01P_CONFIG);
+   this->write_register(NRF24L01P_CONFIG, c & 0xFE);
+}
+
+
+void
+nRF24L01P::set_auto_acknowledge(int Pipe, bool Enable)
+{
+   char c = this->read_register(NRF24L01P_EN_AA);
+   char Mask = 1 << Pipe;
+   this->write_register(NRF24L01P_EN_AA, Enable ? (c | Mask) : (c & ~Mask));
+}
+
+void
+nRF24L01P::enable_rx_pipe(int Pipe, bool Enable)
+{
+   char c = this->read_register(NRF24L01P_EN_RXADDR);
+   char Mask = 1 << Pipe;
+   this->write_register(NRF24L01P_EN_RXADDR, Enable ? (c | Mask) : (c & ~Mask));
+}
+
+
+int
+nRF24L01P::which_rx_pipe()
+{
+   return (this->read_register(NRF24L01P_STATUS) & 0x0E) >> 1;
+}
+
+bool
+nRF24L01P::tx_full()
+{
+   return this->read_register(NRF24L01P_STATUS) & 0x01;
+}
+
+int
+nRF24L01P::num_lost_packets()
+{
+   return (this->read_register(NRF24L01P_OBSERVE_TX) & 0xF0) >> 4;
+}
+
+int 
+nRF24L01P::num_retransmitted_packets()
+{
+   return this->read_register(NRF24L01P_OBSERVE_TX) & 0x0F;
+}
+
+void
+nRF24L01P::set_tx_address(uint64_t Address)
+{
+   this->write_register_40(NRF24L01P_TX_ADDR, Address);
+}
+
+// commands
+
+int
+nRF24L01P::read_register(int Register)
+{
+   NCS = 0;
+   Spi.write(NRF24L01P_CMD_R_REGISTER | Register);
+   int Result = Spi.write(NRF24L01P_CMD_NOP);
+   NCS = 1;
+   return Result;
+}
+
+int
+nRF24L01P::read_register_n(int Register, char* Buf, int Bytes)
+{
+   NCS = 0;
+   Spi.write(NRF24L01P_CMD_R_REGISTER | Register);
+   for (int i = 0; i < Bytes; ++i)
+   {
+      *Buf++ = Spi.write(NRF24L01P_CMD_NOP);
+   }
+   NCS = 1;
+   return 0;
+}
+
+int
+nRF24L01P::write_register(int Register, char x)
+{
+   NCS = 0;
+   Spi.write(NRF24L01P_CMD_W_REGISTER | Register);
+   Spi.write(x);
+   NCS = 1;
+   return 0;
+}
+
+int
+nRF24L01P::write_register_16(int Register, uint16_t x)
+{
+   NCS = 0;
+   Spi.write(NRF24L01P_CMD_W_REGISTER | Register);
+   Spi.write(x & 0xFF);
+   Spi.write((x >> 8) & 0xFF);
+   NCS = 1;
+   return 0;
+}
+
+int64_t
+nRF24L01P::write_register_40(int Register, uint64_t x)
+{
+   NCS = 0;
+   Spi.write(NRF24L01P_CMD_W_REGISTER | Register);
+   Spi.write(x & 0xFF);
+   x = x >> 8;
+   Spi.write(x & 0xFF);
+   x = x >> 8;
+   Spi.write(x & 0xFF);
+   x = x >> 8;
+   Spi.write(x & 0xFF);
+   x = x >> 8;
+   Spi.write(x & 0xFF);
+   NCS = 1;
+   return 0;
+}
+
+int
+nRF24L01P::write_register_bytes(int Register, char const* Buf, int Bytes)
+{
+   NCS = 0;
+   Spi.write(NRF24L01P_CMD_W_REGISTER | Register);
+   for (int i = 0; i < Bytes; ++i)
+   {
+      Spi.write(*Buf++);
+   }
+   NCS = 1;
+   return 0;
+}
+
+void
+nRF24L01P::write_tx_payload(char const* Buf, int Bytes)
+{
+   NCS = 0;
+   Spi.write(NRF24L01P_CMD_W_TX_PAYLOAD);
+   for (int i = 0; i < Bytes; ++i)
+   {
+      Spi.write(*Buf++);
+   }
+   NCS = 1;
+}
+
+void
+nRF24L01P::write_tx_payload_no_ack(char const* Buf, int Bytes)
+{
+   NCS = 0;
+   Spi.write(NRF24L01P_CMD_W_TX_PYLD_NO_ACK);
+   for (int i = 0; i < Bytes; ++i)
+   {
+      Spi.write(*Buf++);
+   }
+   NCS = 1;
+}
+
+void
+nRF24L01P::flush_tx_fifo()
+{
+   NCS = 0;
+   Spi.write(NRF24L01P_CMD_FLUSH_TX);
+   NCS = 1;
+}
+
+void
+nRF24L01P::reuse_tx_payload()
+{
+   NCS = 0;
+   Spi.write(NRF24L01P_CMD_REUSE_TX_PL);
+   NCS = 1;
+}
+
+int
+nRF24L01P::rx_payload_width()
+{
+   NCS = 0;
+   Spi.write(NRF24L01P_CMD_R_RX_PL_WID);
+   int Result = Spi.write(NRF24L01P_CMD_NOP);
+   NCS = 1;
+   return Result;
+   
+}
+
+void
+nRF24L01P::read_rx_payload(char* Buf, int Bytes)
+{
+   NCS = 0;
+   Spi.write(NRF24L01P_CMD_R_RX_PAYLOAD);
+   for (int i = 0; i < Bytes; ++i)
+   {
+      *Buf++ = Spi.write(NRF24L01P_CMD_NOP);
+   }
+   NCS = 1;
+}
+
+int
+nRF24L01P::read_rx_payload(char* Buf)
+{
+   this->read_rx_payload(Buf, this->rx_payload_width());
+   return 0;
+}
+
+void
+nRF24L01P::flush_rx_fifo()
+{
+   NCS = 0;
+   Spi.write(NRF24L01P_CMD_FLUSH_RX);
+   NCS = 1;
+}
+
+void
+nRF24L01P::write_ack_payload(int Pipe, char const* Buf, int Bytes)
+{
+   NCS = 0;
+   Spi.write(NRF24L01P_CMD_W_ACK_PAYLOAD);
+   for (int i = 0; i < Bytes; ++i)
+   {
+      Spi.write(*Buf++);
+   }
+   NCS = 1;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nRF24L01P.h	Sun Aug 11 11:58:05 2013 +0000
@@ -0,0 +1,263 @@
+//
+// Interface for Nordic Semiconductor nRF24L01 and nRF24L01+
+// see http://www.nordicsemi.no/files/Product/data_sheet/nRF24L01P_Product_Specification_1_0.pdf
+//
+// This class exposes basic SPI functionality for the nRF24L01+
+// A driver program is required to actually send or receive data.
+// This class doesn't provide any CE line control
+//
+// Differences between nRF24L01 and nRF24L01+
+// 1. The nRF24L01 doesn't support 250kHz air data rate
+// 2. The nRF24L01 received_power_detector() works differently
+//
+
+#ifndef NRF24L01P_H
+#define NRF24L01P_H
+
+#include "mbed.h"
+#include <stdint.h>
+
+#define NRF24L01P_MIN_RF_FREQUENCY    2400
+#define NRF24L01P_MAX_RF_FREQUENCY    2525
+
+// timing constants
+#define Tundef2pd_us     100000   // 100mS, time from reset to power-down mode
+#define Tstby2a_us          130   // 130uS, time from standby mode to TX/RX mode
+#define Thce_us              10   //  10uS, minimum time for CE high
+#define Tpd2stby_us        4500   // 4.5mS worst case, time from power-down to standby
+#define Tpece2csn_us          4   //   4uS, delay from CE positive edge to CSN low
+
+class nRF24L01P
+{
+   public:
+      nRF24L01P(PinName mosi, PinName miso, PinName sck, PinName csn, long SPIFrequency = 2000000);
+
+      // Clears the TX and RX FIFOs and resets all registers to their power-on values
+      void reset();
+      
+      // change the SPI frequency
+      void set_spi_frequency(long Freq);
+
+      // register operations
+
+      // set the data rate in kb/s, valid values are 250 (nRF24L01+ only), 1000, 2000.
+      // DEFAULT:
+      void set_air_data_rate(int Rate);
+      
+      // set the RF channel, 0 .. 125.
+      // The frequency in MHz is 2400 + Channel.
+      // In 2Mbps mode, you must use channels at least 2MHz appart to avoid overlap
+      // DEFAULT:
+      void set_channel(int Channel);
+      
+      // PRECONDITION: Mode = Receive.  True if a signal higher than -64bB is detected by the receiver
+      bool received_power_detector();
+
+      // set the transmit power in dB.  Valid values are 0, -6, -12, -18
+      // DEFAULT: 0
+      void set_tx_power(int Power);
+
+ 
+      // enable or disable dynamic payload size on the given pipe
+      // DEFAULT:
+      void enable_dynamic_payload(int Pipe, bool Enable);
+
+      // Enable including a payload with ACK
+      // DEFAULT:
+      void enable_ack_payload(bool Enable);
+
+      // enable the write_tx_payload_no_ack() command
+      // DEFAULT: disabled
+      void enable_no_ack(bool Enable);
+
+      //
+      // receive functionality
+      //
+      
+      // set the width in bytes for addresses.  Valid values are 3,4,5
+      // DEFAULT: 5
+      void set_address_width(int Width);
+      
+      // returns the width in bytes for addresses.  Valid values are 3,4,5
+      int get_address_width();
+
+      // set the receive address of the given pipe.  Pipes 0,1 have 3,4 or 5 byte addresses
+      // as specified by set_address_width().  Pipes 2,3,4,5 can only set the low order byte of the
+      // address, the other bytes are shared with pipe 1.
+      
+      // Set the rx address for pipe 0 or 1
+      void set_rx_address(int Pipe, uint64_t Address);
+      
+      // Set the low byte of the address for pipe 2,3,4,5
+      void set_rx_address_low(int Pipe, uint8_t Address);
+      
+      // Set the payload size for the receive buffer for the given pipe
+      // (not used if dynamic payload size is used)
+      void set_rx_payload_bytes(int Pipe, int Bytes);
+
+      // returns true if the receive FIFO is full
+      bool is_rx_full();
+
+      // returns true if the receive FIFO is empty
+      bool is_rx_empty();
+
+      // receive packet status
+
+      // read the status register and return true if there is a packet ready to be received (RX_DR)
+      bool is_rx_ready();
+
+      // clear the RX_DR bit
+      void clear_rx_ready();
+      
+      // if Enable, reflect RX_DR as active low on the INT line.  The procedure to read a packet is:
+      // 1) Read payload through SPI, 2) clear_rc_ready(), read is_rx_ready() to see if there is another 
+      // packet waiting
+      // DEFAULT: enabled
+      void set_interrupt_rx_ready(bool Enable);
+
+      // returns true if the transmit FIFO is full
+      bool is_tx_full();
+
+      // returns true if the transmit FIFO is empty
+      bool is_tx_empty();
+
+      // transmit packet status
+
+      // reads the status register and returns true if a packet has been transmitted. (TX_DS)  If auto-acknowledge
+      // is set, then this is only asserted when an ack is received.
+      bool is_tx_sent();
+      
+      // Clear the TX_DS bit
+      void clear_tx_sent();
+
+      // if Enable, reflect TX_DS as active low on the INT line
+      // DEFAULT: enabled
+      void set_interrupt_tx(bool Enable);
+
+      // retransmit behaviour
+      
+      // set the auto-retransmit delay, in units of 250 microseconds.  
+      // Valid input is 0,1,...,15 (for 250 .. 4000 us)
+      // DEFAULT:
+      void set_retransmit_delay(int Delay);
+
+      // set the number of auto-retransmit attempts, 0 .. 15
+      // DEFAULT:
+      void set_retransmit_attempts(int Attempts);
+
+      // returns true if we've hit the maximum number of restransmits
+      bool is_max_rt();
+
+      // clear the maximum number of retransmits flag.
+      void clear_max_rt();
+
+      // if Enable, reflect MAX_RT as active low on the INT line
+      // DEFAULT: enabled
+      void set_interrupt_max_rt(bool Enable);
+
+      // enable or disable the CRC check.  Auto-acknowledge forces CRC enabled.
+      // DEFAULT:
+      void enable_crc(bool Enable);      
+      
+      // set the CRC width, either 1 byte or 2 bytes
+      // DEFAULT: 
+      void set_crc_width(int width);
+
+      // set PWR_UP (move into standby mode)
+      void set_power_up();
+
+      // turn off power (PWR_UP=0)
+      void set_power_down();
+      
+      // set as primary receiver (disables transmit mode)
+      void set_prx_mode();
+
+      // set as primary transmitter (disables receive mode)
+      void set_ptx_mode();
+
+      // Enable or disable receiving on the given pipe
+      // DEFAULT: Pipes 0,1 enabled, pipes 2,3,4,5 disabled.
+      void enable_rx_pipe(int Pipe, bool Enable);
+
+      // Enable or disable auto-acknowledge on the given pipe 0,1,2,3,4,5
+      // DEFAULT: Enabled on all pipes
+      void set_auto_acknowledge(int Pipe, bool Enable);
+      
+      // Enable or disable RX addresses on the given pipe
+      void enable_receive_pipe(int Pipe, bool Enable);
+
+      // returns the pipe number for the payload available on the rx pipe, or -1 if the rx fifo is empty
+      int which_rx_pipe();
+
+      // returns true if the transmit fifo is full
+      bool tx_full();   
+      
+      // returns the number of transmitted but lost packets.  Capped at 15, reset by writing the channel number.
+      int num_lost_packets();
+      
+      // returns the number of times the current packet has been retransmitted.  Reset when a new packet starts
+      int num_retransmitted_packets();
+
+       
+      // Set the tx address.  Used only for a PTX, set this the same as the rx_address of pipe 0 to enable 
+      // auto-acknowledge with Enhanced ShockBurst(TM)
+      // DEFAULT:
+      void set_tx_address(uint64_t Address);
+      
+      // commands
+      
+      // read a byte from the given register
+      int read_register(int Register);
+
+      // read n bytes from the given register
+      int read_register_n(int Register, char* Buf, int Bytes);
+
+      // write one byte to the given register
+      int write_register(int Register, char x);
+
+      // write two bytes to a register
+      int write_register_16(int Register, uint16_t x);
+
+      // write five bytes to a register
+      int64_t write_register_40(int Register, uint64_t x);
+
+      // write some bytes to the given register
+      int write_register_bytes(int Register, char const* Buf, int Bytes);
+
+      // Write a transmit payload, 1-32 bytes
+      void write_tx_payload(char const* Buf, int Bytes);
+
+      // Write a transmit payload, 1-32 bytes, with ACK disabled.
+      // NOTE: enable_no_ack() must be enabled
+      void write_tx_payload_no_ack(char const* Buf, int Bytes);
+
+      // flush the transmit FIFO
+      void flush_tx_fifo();
+
+      // reuse the last transmitted payload.  Payload reuse is activated until the next flush_tx_fifo() or write_tx_payload()
+      void reuse_tx_payload();
+
+      // returns the width of the receive payload.  (If this isn't in the range 0..32 then the rx fifo should be flushed)
+      int rx_payload_width();
+
+      // read 1-32 bytes from the RX FIFO.  The payload is deleted after it is read.  The user must
+      // know how many bytes to read.
+      void read_rx_payload(char* Buf, int Bytes);
+      
+      // this version reads the complete payload into Buf, and returns the number of bytes in the payload
+      int read_rx_payload(char* Buf);
+
+      // flush the receive FIFO
+      void flush_rx_fifo();
+      
+      // set the ACK payload for the given pipe.  
+      void write_ack_payload(int Pipe, char const* Buf, int Bytes);
+
+
+   private:
+      SPI         Spi;
+      DigitalOut  NCS;
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nRF24L01P_PRX.cpp	Sun Aug 11 11:58:05 2013 +0000
@@ -0,0 +1,160 @@
+
+#include "nRF24L01P_PRX.h"
+
+// status
+#define STATUS_UNDEFINED       0
+#define STATUS_READY_INIT      1
+#define STATUS_POWER_DOWN      2
+#define STATUS_STARTUP_STANDBY 3
+#define STATUS_STANDBY         4
+#define STATUS_RECEIVE         5
+
+nRF24L01P_PRX::nRF24L01P_PRX(nRF24L01P& Device_, PinName CE_, PinName Int_)
+  : Device(Device_),
+    CE(CE_),
+    Int(Int_)
+{
+   CE = 0;
+   PayloadSize = 0;
+   Status = STATUS_UNDEFINED;
+   Int.mode(PullNone);
+   Int.fall(NULL);
+   InitializeTimer.attach_us(this, &nRF24L01P_PRX::ReadyInitialize, Tundef2pd_us);
+}
+
+void
+nRF24L01P_PRX::Initialize()
+{
+   while (Status == STATUS_UNDEFINED)
+   {
+      wait_us(1);
+   }
+   PowerOnTimer.detach();
+   CE = 0;
+   Device.reset();
+   Device.set_prx_mode();
+   Status = STATUS_POWER_DOWN;
+   PayloadSize = 0;
+   Int.fall(this, &nRF24L01P_PRX::IntHandler);
+}
+
+void
+nRF24L01P_PRX::SetChannel(int Channel)
+{
+   Device.set_channel(Channel);
+}
+
+void
+nRF24L01P_PRX::SetDataRate(int Rate)
+{
+   Device.set_air_data_rate(Rate);
+}
+
+void
+nRF24L01P_PRX::SetAddress(uint64_t Address)
+{
+   Device.set_rx_address(0, Address);
+}
+
+void
+nRF24L01P_PRX::SetPayloadSize(int Size)
+{
+   PayloadSize = Size;
+   Device.set_rx_payload_bytes(0, Size);
+}
+
+void
+nRF24L01P_PRX::PowerUp()
+{
+   if (Status != STATUS_POWER_DOWN)
+   {
+      error("nRF24L01P_PRX::PowerUp(): can only be called when device is powered down");
+   }
+   Status = STATUS_STARTUP_STANDBY;
+   Device.set_power_up();
+   PowerOnTimer.attach_us(this, &nRF24L01P_PRX::ReadyStandby, Tpd2stby_us);
+}
+
+void
+nRF24L01P_PRX::PowerDown()
+{
+   if (Status == STATUS_UNDEFINED || Status == STATUS_READY_INIT || Status == STATUS_POWER_DOWN)
+   {
+      error("nRF24L01P_PRX::PowerDown(): error: device is not powered up!");
+   }
+   PowerOnTimer.detach();
+   Device.set_power_down();
+   Status = STATUS_POWER_DOWN;
+}
+
+void
+nRF24L01P_PRX::StartReceive()
+{
+   if (Status == STATUS_UNDEFINED || Status == STATUS_READY_INIT || Status == STATUS_POWER_DOWN)
+   {
+      error("nRF24L01P_PRX::StartReceive(): error: device is not powered up!");
+   }
+   while (Status == STATUS_STARTUP_STANDBY)
+   {
+      wait_us(1);
+   }
+   CE = 1;
+   Status = STATUS_RECEIVE;
+}
+
+void
+nRF24L01P_PRX::StopReceive()
+{
+   if (Status != STATUS_RECEIVE)
+   {
+      error("nRF24L01P_PRX::StopReceive(): error: device is not receiving!");
+   }
+   CE = 0;
+   Status = STATUS_STANDBY;
+}
+   
+bool
+nRF24L01P_PRX::IsPacketReady()
+{
+   return Device.is_rx_ready();
+}
+
+int
+nRF24L01P_PRX::ReadPacket(char* Buf)
+{
+   if (!this->IsPacketReady())
+   {
+      error("nRF24L01P_PRX::ReadPacket(): error: no packet to read!");
+   }
+
+   Device.read_rx_payload(Buf);
+   Device.clear_rx_ready();
+   return PayloadSize;
+}
+
+void
+nRF24L01P_PRX::IntHandler()
+{
+// here we do nothing, since we use polling instead
+/*
+   while (Device.is_rx_ready())
+   {
+      // read payload
+      Device.read_rx_payload(Buf);
+      Device.clear_rx_ready();
+   }
+*/
+}
+
+void
+nRF24L01P_PRX::ReadyInitialize()
+{
+   Status = STATUS_READY_INIT;
+}
+
+void
+nRF24L01P_PRX::ReadyStandby()
+{
+   if (Status == STATUS_STARTUP_STANDBY)
+      Status = STATUS_STANDBY;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nRF24L01P_PRX.h	Sun Aug 11 11:58:05 2013 +0000
@@ -0,0 +1,66 @@
+// A simple PRX driver for the nRF24L01+
+// for polling mode with fixed payload length
+
+#ifndef NRF24L01P_PRX_H
+#define NRF24L01P_PRX_H
+
+#include "nRF24L01P.h"
+
+class nRF24L01P_PRX
+{
+   public:
+      nRF24L01P_PRX(nRF24L01P& Device_, PinName CE_, PinName Int_);
+
+      // Initialize the device for transmitting.  There must be a delay of 
+      // at least 100ms from initial power on before this function is called.
+      void Initialize();
+
+      // Set the channel number, 0 .. 125
+      void SetChannel(int Channel);
+
+      // sets the data rate in Kbps, valid values are 250 (nRF24L01+ only), 1000, 2000
+      void SetDataRate(int Rate);
+      
+      // Set the 40-bit address
+      void SetAddress(uint64_t Address);
+
+      // Set the expected payload size
+      void SetPayloadSize(int Size);
+
+      // Power up ready to enter receive mode.  There is a delay of Tpd2stby_us
+      // after PowerUp() before receiving can start.
+      void PowerUp();
+      
+      // Powers down the device.  PowerUp must be called before a packet can be transmitted.
+      void PowerDown();
+
+      // Start listening for packets.
+      void StartReceive();
+      
+      // Stop listening for packets.
+      void StopReceive();
+
+      // returns true if there is a packet ready to read
+      bool IsPacketReady();
+
+      // PRECONDITION: IsPacketReady().
+      // Reads the packet into the given buffer.
+      // Returns the size of the packet.
+      int ReadPacket(char* Buf);
+
+   private:
+      void IntHandler();
+      void ReadyInitialize();
+      void ReadyStandby();
+   
+      nRF24L01P& Device;
+      DigitalOut CE;
+      InterruptIn Int;
+      Timeout PowerOnTimer;
+      Timeout InitializeTimer;
+      
+      int volatile Status;
+      int PayloadSize;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nRF24L01P_PTX.cpp	Sun Aug 11 11:58:05 2013 +0000
@@ -0,0 +1,169 @@
+
+#include "nRF24L01P_PTX.h"
+
+// status
+#define STATUS_UNDEFINED       0
+#define STATUS_READY_INIT      1
+#define STATUS_POWER_DOWN      2
+#define STATUS_STARTUP_STANDBY 3
+#define STATUS_STANDBY         4
+#define STATUS_TRANSMITTING    5
+#define STATUS_PACKET_OK       6
+#define STATUS_PACKET_MAX_RT   7
+
+nRF24L01P_PTX::nRF24L01P_PTX(nRF24L01P& Device_, PinName CE_, PinName Int_)
+  : Device(Device_),
+    CE(CE_),
+    Int(Int_)
+{
+   CE = 0;
+   Status = STATUS_UNDEFINED;
+   Int.mode(PullNone);
+   Int.fall(NULL);
+   InitializeTimer.attach_us(this, &nRF24L01P_PTX::ReadyInitialize, Tundef2pd_us);
+}
+
+void
+nRF24L01P_PTX::Initialize()
+{
+   while (Status == STATUS_UNDEFINED)
+   {
+      wait_us(1);
+   }
+   PowerOnTimer.detach();
+   CE = 0;
+   Device.reset();
+   Device.set_ptx_mode();
+   Status = STATUS_POWER_DOWN;
+   Int.fall(this, &nRF24L01P_PTX::IntHandler);
+}
+
+void
+nRF24L01P_PTX::SetChannel(int Channel)
+{
+   Device.set_channel(Channel);
+}
+
+void
+nRF24L01P_PTX::SetDataRate(int Rate)
+{
+   Device.set_air_data_rate(Rate);
+}
+
+void
+nRF24L01P_PTX::SetTransmitPower(int Power)
+{
+   Device.set_tx_power(Power);
+}
+
+void
+nRF24L01P_PTX::SetDestinationAddress(uint64_t Address)
+{
+   Device.set_tx_address(Address);
+}
+
+void
+nRF24L01P_PTX::PowerUp()
+{
+   if (Status != STATUS_POWER_DOWN)
+   {
+      error("nRF24L01P_PTX::PowerUp(): can only be called when device is powered down");
+   }
+   Status = STATUS_STARTUP_STANDBY;
+   Device.set_power_up();
+   PowerOnTimer.attach_us(this, &nRF24L01P_PTX::ReadyStandby, Tpd2stby_us);
+}
+
+void
+nRF24L01P_PTX::PowerDown()
+{
+   if (Status == STATUS_UNDEFINED || Status == STATUS_READY_INIT)
+   {
+      error("nRF24L01P_PTX::PowerDown(): error: device is not initialized!");
+   }
+   // Although it is technically possible to turn the power off at any time,
+   // if we're currently processing a packet, wait until we've finished
+   while (Status == STATUS_TRANSMITTING || Status == STATUS_PACKET_OK || Status == STATUS_PACKET_MAX_RT)
+   {
+      wait_us(1);
+   }
+   PowerOnTimer.detach();
+   Device.set_power_down();
+   Status = STATUS_POWER_DOWN;
+}
+
+bool
+nRF24L01P_PTX::IsReadyTransmit()
+{
+   return (Status == STATUS_STANDBY);
+}
+  
+int
+nRF24L01P_PTX::TransmitPacket(char* Buf, int Size)
+{
+   // If the device isn't powered up then there is a logic error
+   if (Status == STATUS_UNDEFINED || Status == STATUS_READY_INIT || Status == STATUS_POWER_DOWN)
+   {
+      error("nRF24L01P_PTX::TransmitPacket(): error: device power must be on!");
+   }
+   // Wait until the device is ready to transmit
+   while (Status != STATUS_STANDBY)
+   {
+      wait_us(1);
+   }
+   Device.write_tx_payload(Buf, Size);
+   Status = STATUS_TRANSMITTING;
+   CE = 1;
+   wait_us(Thce_us);
+   CE = 0;
+   // wait until the packet is transmitted (or some error)
+   while (Status == STATUS_TRANSMITTING)
+   {
+      wait_us(1);
+   }
+   int OldStatus = Status;
+   if (OldStatus == STATUS_PACKET_OK)
+   {
+      Status = STATUS_STANDBY;
+      return 0;
+   }
+   else if (OldStatus == STATUS_PACKET_MAX_RT)
+   {
+      Status = STATUS_STANDBY;
+      return -1;
+   }
+   // else the Status isn't what we expected, which
+   // shouldn't happen in blocking mode.  Some interrupt must have
+   // reset Status.  Bug out with an error.
+   return -1;
+}
+
+void
+nRF24L01P_PTX::IntHandler()
+{
+   if (Device.is_tx_sent())
+   {
+      // we successfully sent a packet
+      Status = STATUS_PACKET_OK;
+      Device.clear_tx_sent();
+   }
+   else if (Device.is_max_rt())
+   {
+      // we failed to send the packet
+      Status = STATUS_PACKET_MAX_RT;
+      Device.clear_max_rt();
+   }
+}
+
+void
+nRF24L01P_PTX::ReadyInitialize()
+{
+   Status = STATUS_READY_INIT;
+}
+
+void
+nRF24L01P_PTX::ReadyStandby()
+{
+   if (Status == STATUS_STARTUP_STANDBY)
+      Status = STATUS_STANDBY;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nRF24L01P_PTX.h	Sun Aug 11 11:58:05 2013 +0000
@@ -0,0 +1,62 @@
+// A simple PTX driver for the nRF24L01+
+// with fixed payload length
+
+#ifndef NRF24L01P_PTX_H
+#define NRF24L01P_PTX_H
+
+#include "nRF24L01P.h"
+
+class nRF24L01P_PTX
+{
+   public:
+      nRF24L01P_PTX(nRF24L01P& Device_, PinName CE_, PinName Int_);
+
+      // Initialize the device for transmitting.  There must be a delay of 
+      // at least 100ms from initial power on before this function is called.
+      void Initialize();
+
+      // Set the channel number, 0 .. 125
+      void SetChannel(int Channel);
+
+      // sets the data rate in Kbps, valid values are 250 (nRF24L01+ only), 1000, 2000
+      void SetDataRate(int Rate);
+      
+      // Set the transmitter power in dB.  Valid values are -18, -12, -6, 0
+      void SetTransmitPower(int Power);
+      
+      // Set the 40-bit destination address
+      void SetDestinationAddress(uint64_t Address);
+
+      // Power up ready to transmit.  There is a delay of Tpd2stby_us
+      // after PowerUp() before a packet can be transmitted.  This
+      // is handled automatically with a timer, so that TransmitPacket()
+      // will block until the device is ready.
+      void PowerUp();
+      
+      // Powers down the device.  PowerUp must be called before a packet can be transmitted.
+      void PowerDown();
+
+      // Returns true if the device is ready to transmit a packet, ie
+      // powered on and not busy
+      bool IsReadyTransmit();
+
+      // Does the actual transmit of a packet, in blocking mode.
+      // Returns 0 on success.
+      // Returns -1 if the packet wasn't successfully acknowledged.
+      int TransmitPacket(char* Buf, int Size);
+
+   private:
+      void IntHandler();
+      void ReadyInitialize();
+      void ReadyStandby();
+   
+      nRF24L01P& Device;
+      DigitalOut CE;
+      InterruptIn Int;
+      Timeout PowerOnTimer;
+      Timeout InitializeTimer;
+      
+      int volatile Status;
+};
+
+#endif