summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/driver/nrf24l01.h159
-rw-r--r--src/app/nrf24l01test/main.cc3
-rw-r--r--src/driver/nrf24l01.cc101
3 files changed, 261 insertions, 2 deletions
diff --git a/include/driver/nrf24l01.h b/include/driver/nrf24l01.h
index c10332d..44f8205 100644
--- a/include/driver/nrf24l01.h
+++ b/include/driver/nrf24l01.h
@@ -22,8 +22,42 @@ private:
uint8_t addr_width; /**< The address width to use - 3,4 or 5 bytes. */
uint32_t txRxDelay; /**< Var for adjusting delays depending on datarate */
+ /**
+ * Write a single byte to a register
+ *
+ * @param reg Which register. Use constants from nRF24L01.h
+ * @param value The new value to write
+ * @return Current value of status register
+ */
uint8_t writeRegister(uint8_t reg, uint8_t value);
+
+ /**
+ * Write a chunk of data to a register
+ *
+ * @param reg Which register. Use constants from nRF24L01.h
+ * @param buf Where to get the data
+ * @param len How many bytes of data to transfer
+ * @return Current value of status register
+ */
+ uint8_t writeRegister(uint8_t reg, const uint8_t *buf, uint8_t len);
+
+ /**
+ * Read single byte from a register
+ *
+ * @param reg Which register. Use constants from nRF24L01.h
+ * @return Current value of register @p reg
+ */
uint8_t readRegister(uint8_t reg);
+
+ /**
+ * Write the transmit payload
+ *
+ * The size of data written is the fixed payload size, see getPayloadSize()
+ *
+ * @param buf Where to get the data
+ * @param len Number of bytes to be sent
+ * @return Current value of status register
+ */
uint8_t writePayload(const void *buf, uint8_t data_len, const uint8_t writeType);
inline void csnHigh()
@@ -46,7 +80,7 @@ private:
}
public:
- Nrf24l01() : payload_size(32), dynamic_payloads_enabled(false), addr_width(5) {}
+ Nrf24l01() : payload_size(32), dynamic_payloads_enabled(false), addr_width(5) { pipe0_reading_address[0] = 0; }
/**
* Power Amplifier level.
@@ -231,9 +265,130 @@ public:
* are enabled. See the datasheet for details.
*/
void toggleFeatures(void);
-
+ /**
+ * Be sure to call openWritingPipe() first to set the destination
+ * of where to write to.
+ *
+ * This blocks until the message is successfully acknowledged by
+ * the receiver or the timeout/retransmit maxima are reached. In
+ * the current configuration, the max delay here is 60-70ms.
+ *
+ * The maximum size of data written is the fixed payload size, see
+ * getPayloadSize(). However, you can write less, and the remainder
+ * will just be filled with zeroes.
+ *
+ * TX/RX/RT interrupt flags will be cleared every time write is called
+ *
+ * @param buf Pointer to the data to be sent
+ * @param len Number of bytes to be sent
+ *
+ * @code
+ * radio.stopListening();
+ * radio.write(&data,sizeof(data));
+ * @endcode
+ * @return True if the payload was delivered successfully false if not
+ */
uint8_t write(const void *buf, uint8_t len, bool blocking);
+ /**
+ * Start listening on the pipes opened for reading.
+ *
+ * 1. Be sure to call openReadingPipe() first.
+ * 2. Do not call write() while in this mode, without first calling stopListening().
+ * 3. Call available() to check for incoming traffic, and read() to get it.
+ *
+ * @code
+ * Open reading pipe 1 using address CCCECCCECC
+ *
+ * byte address[] = { 0xCC,0xCE,0xCC,0xCE,0xCC };
+ * radio.openReadingPipe(1,address);
+ * radio.startListening();
+ * @endcode
+ */
+ void startListening(void);
+
+ /**
+ * Stop listening for incoming messages, and switch to transmit mode.
+ *
+ * Do this before calling write().
+ * @code
+ * radio.stopListening();
+ * radio.write(&data,sizeof(data));
+ * @endcode
+ */
+ void stopListening(void);
+
+ /**
+ * Check whether there are bytes available to be read
+ * @code
+ * if(radio.available()){
+ * radio.read(&data,sizeof(data));
+ * }
+ * @endcode
+ * @return True if there is a payload available, false if none is
+ */
+ bool available(void);
+
+ /**
+ * Read the available payload
+ *
+ * The size of data read is the fixed payload size, see getPayloadSize()
+ *
+ * @note I specifically chose 'void*' as a data type to make it easier
+ * for beginners to use. No casting needed.
+ *
+ * @note No longer boolean. Use available to determine if packets are
+ * available. Interrupt flags are now cleared during reads instead of
+ * when calling available().
+ *
+ * @param buf Pointer to a buffer where the data should be written
+ * @param len Maximum number of bytes to read into the buffer
+ *
+ * @code
+ * if(radio.available()){
+ * radio.read(&data,sizeof(data));
+ * }
+ * @endcode
+ * @return No return value. Use available().
+ */
+ void read(void *buf, uint8_t len);
+
+ /**
+ * Open a pipe for reading
+ *
+ * Up to 6 pipes can be open for reading at once. Open all the required
+ * reading pipes, and then call startListening().
+ *
+ * @see openWritingPipe
+ * @see setAddressWidth
+ *
+ * @note Pipes 0 and 1 will store a full 5-byte address. Pipes 2-5 will technically
+ * only store a single byte, borrowing up to 4 additional bytes from pipe #1 per the
+ * assigned address width.
+ * @warning Pipes 1-5 should share the same address, except the first byte.
+ * Only the first byte in the array should be unique, e.g.
+ * @code
+ * uint8_t addresses[][6] = {"1Node","2Node"};
+ * openReadingPipe(1,addresses[0]);
+ * openReadingPipe(2,addresses[1]);
+ * @endcode
+ *
+ * @warning Pipe 0 is also used by the writing pipe. So if you open
+ * pipe 0 for reading, and then startListening(), it will overwrite the
+ * writing pipe. Ergo, do an openWritingPipe() again before write().
+ *
+ * @param number Which pipe# to open, 0-5.
+ * @param address The 24, 32 or 40 bit address of the pipe to open.
+ */
+ void openReadingPipe(uint8_t number, const uint8_t *address);
+
+ /**
+ * Close a pipe after it has been previously opened.
+ * Can be safely called without having previously opened a pipe.
+ * @param pipe Which pipe # to close, 0-5.
+ */
+ void closeReadingPipe(uint8_t pipe);
+
uint8_t getStatus();
};
diff --git a/src/app/nrf24l01test/main.cc b/src/app/nrf24l01test/main.cc
index c12cbf5..ad1bc11 100644
--- a/src/app/nrf24l01test/main.cc
+++ b/src/app/nrf24l01test/main.cc
@@ -9,6 +9,9 @@ void loop(void)
kout << "status: " << hex << nrf24l01.getStatus() << endl;
kout << "write: ";
kout << nrf24l01.write("foo", 3, true) << endl;
+ nrf24l01.startListening();
+ arch.delay_ms(10);
+ nrf24l01.stopListening();
}
int main(void)
diff --git a/src/driver/nrf24l01.cc b/src/driver/nrf24l01.cc
index ff19240..5992b17 100644
--- a/src/driver/nrf24l01.cc
+++ b/src/driver/nrf24l01.cc
@@ -18,6 +18,16 @@
#error makeflag nrf24l01_cs_pin required
#endif
+static const uint8_t child_pipe[] =
+ {
+ RX_ADDR_P0, RX_ADDR_P1, RX_ADDR_P2, RX_ADDR_P3, RX_ADDR_P4, RX_ADDR_P5};
+static const uint8_t child_payload_size[] =
+ {
+ RX_PW_P0, RX_PW_P1, RX_PW_P2, RX_PW_P3, RX_PW_P4, RX_PW_P5};
+static const uint8_t child_pipe_enable[] =
+ {
+ ERX_P0, ERX_P1, ERX_P2, ERX_P3, ERX_P4, ERX_P5};
+
void Nrf24l01::setup()
{
spi.setup();
@@ -181,6 +191,85 @@ uint8_t Nrf24l01::write(const void *buf, uint8_t len, bool blocking)
return 1;
}
+void Nrf24l01::startListening(void)
+{
+ writeRegister(NRF_CONFIG, readRegister(NRF_CONFIG) | (1 << PRIM_RX));
+ writeRegister(NRF_STATUS, (1 << RX_DR) | (1 << TX_DS) | (1 << MAX_RT));
+ gpio.write(NRF24L01_EN_PIN, 1);
+
+ // Restore the pipe0 adddress, if exists
+ if (pipe0_reading_address[0] > 0)
+ {
+ writeRegister(RX_ADDR_P0, pipe0_reading_address, addr_width);
+ }
+ else
+ {
+ closeReadingPipe(0);
+ }
+
+ if (readRegister(FEATURE) & (1 << EN_ACK_PAY))
+ {
+ flushTx();
+ }
+}
+
+void Nrf24l01::stopListening(void)
+{
+ gpio.write(NRF24L01_EN_PIN, 0);
+
+ arch.delay_us(txRxDelay);
+
+ if (readRegister(FEATURE) & (1 << EN_ACK_PAY))
+ {
+ arch.delay_us(txRxDelay); //200
+ flushTx();
+ }
+ //flush_rx();
+ writeRegister(NRF_CONFIG, (readRegister(NRF_CONFIG)) & ~(1 << PRIM_RX));
+
+ writeRegister(EN_RXADDR, readRegister(EN_RXADDR) | (1 << child_pipe_enable[0])); // Enable RX on pipe0
+}
+
+void Nrf24l01::openReadingPipe(uint8_t child, const uint8_t *address)
+{
+ // If this is pipe 0, cache the address. This is needed because
+ // openWritingPipe() will overwrite the pipe 0 address, so
+ // startListening() will have to restore it.
+ if (child == 0)
+ {
+ pipe0_reading_address[0] = address[0];
+ pipe0_reading_address[1] = address[1];
+ pipe0_reading_address[2] = address[2];
+ pipe0_reading_address[3] = address[3];
+ pipe0_reading_address[4] = address[4];
+ }
+ if (child <= 6)
+ {
+ // For pipes 2-5, only write the LSB
+ if (child < 2)
+ {
+ writeRegister(child_pipe[child], address, addr_width);
+ }
+ else
+ {
+ writeRegister(child_pipe[child], address, 1);
+ }
+ writeRegister(child_payload_size[child], payload_size);
+
+ // Note it would be more efficient to set all of the bits for all open
+ // pipes at once. However, I thought it would make the calling code
+ // more simple to do it this way.
+ writeRegister(EN_RXADDR, readRegister(EN_RXADDR) | (1 << child_pipe_enable[child]));
+ }
+}
+
+/****************************************************************************/
+
+void Nrf24l01::closeReadingPipe(uint8_t pipe)
+{
+ writeRegister(EN_RXADDR, readRegister(EN_RXADDR) & ~(1 << child_pipe_enable[pipe]));
+}
+
void Nrf24l01::maskIRQ(bool tx, bool fail, bool rx)
{
@@ -214,6 +303,18 @@ uint8_t Nrf24l01::readRegister(uint8_t reg)
return rxbuf[1];
}
+uint8_t Nrf24l01::writeRegister(uint8_t reg, const uint8_t *buf, uint8_t len)
+{
+ txbuf[0] = W_REGISTER | (REGISTER_MASK & reg);
+
+ beginTransaction();
+ spi.xmit(1, txbuf, 1, rxbuf);
+ spi.xmit(len, (unsigned char *)buf, 0, NULL);
+ endTransaction();
+
+ return rxbuf[0];
+}
+
uint8_t Nrf24l01::writeRegister(uint8_t reg, uint8_t value)
{
txbuf[0] = W_REGISTER | (REGISTER_MASK & reg);