nRF24L01 driver
Dependents: Nucleo_IOT1 wireless
Revision 0:7313e63394c3, committed 2013-08-11
- Comitter:
- ianmcc
- Date:
- Sun Aug 11 11:58:05 2013 +0000
- Child:
- 1:cd113026825f
- Commit message:
- Initial
Changed in this revision
--- /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