Port of Maniacbug's Arduino RF24 library to mbed.
Dependents: STM32F407VET6_nRF24L01_Master STM32F407VET6_nRF24L01_Slave Main_ntp_sd_nrf IRC_MASTER
Diff: RF24.cpp
- Revision:
- 0:2007da485383
- Child:
- 1:d96c2056bf37
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/RF24.cpp Wed Jan 23 17:34:34 2019 +0000 @@ -0,0 +1,1235 @@ +/* + Copyright (C) 2011 J. Coliz <maniacbug@ymail.com> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as published by the Free Software Foundation. + */ +#include "RF24.h" + +/** + * @brief + * @note + * @param + * @retval + */ +void RF24::csn(int mode) +{ + // Minimum ideal spi bus speed is 2x data rate + + // If we assume 2Mbs data rate and 16Mhz clock, a + // divider of 4 is the minimum we want. + // CLK:BUS 8Mhz:2Mhz, 16Mhz:4Mhz, or 20Mhz:5Mhz + //#ifdef ARDUINO + // spi.setBitOrder(MSBFIRST); + // spi.setDataMode(spi_MODE0); + // spi.setClockDivider(spi_CLOCK_DIV4); + //#endif + // digitalWrite(csn_pin,mode); + csn_pin = mode; +} + +/** + * @brief + * @note + * @param + * @retval + */ +void RF24::ce(int level) +{ + //digitalWrite(ce_pin,level); + + ce_pin = level; +} + +/** + * @brief + * @note + * @param + * @retval + */ +uint8_t RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len) +{ + /*-----------*/ + uint8_t status; + /*-----------*/ + + csn(LOW); + status = spi.write(R_REGISTER | (REGISTER_MASK & reg)); + while (len--) + *buf++ = spi.write(0xff); + + csn(HIGH); + + return status; +} + +/** + * @brief + * @note + * @param + * @retval + */ +uint8_t RF24::read_register(uint8_t reg) +{ + csn(LOW); + spi.write(R_REGISTER | (REGISTER_MASK & reg)); + + /*-----------------------------*/ + uint8_t result = spi.write(0xff); + /*-----------------------------*/ + + csn(HIGH); + return result; +} + +/** + * @brief + * @note + * @param + * @retval + */ +uint8_t RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len) +{ + /*-----------*/ + uint8_t status; + /*-----------*/ + + csn(LOW); + status = spi.write(W_REGISTER | (REGISTER_MASK & reg)); + while (len--) + spi.write(*buf++); + + csn(HIGH); + + return status; +} + +/** + * @brief + * @note + * @param + * @retval + */ +uint8_t RF24::write_register(uint8_t reg, uint8_t value) +{ + /*-----------*/ + uint8_t status; + /*-----------*/ + + // IF_SERIAL_DEBUG(printf(("write_register(%02x,%02x)\r\n"),reg,value)); + csn(LOW); + status = spi.write(W_REGISTER | (REGISTER_MASK & reg)); + spi.write(value); + csn(HIGH); + + return status; +} + +/** + * @brief + * @note + * @param + * @retval + */ +uint8_t RF24::write_payload(const void* buf, uint8_t len) +{ + /*-------------------------------------------------------------------------------*/ + uint8_t status; + const uint8_t* current = reinterpret_cast < const uint8_t * > (buf); + uint8_t data_len = min(len, payload_size); + uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len; + /*-------------------------------------------------------------------------------*/ + + //printf("[Writing %u bytes %u blanks]",data_len,blank_len); + csn(LOW); + status = spi.write(W_TX_PAYLOAD); + while (data_len--) + spi.write(*current++); + while (blank_len--) + spi.write(0); + csn(HIGH); + + return status; +} + +/** + * @brief + * @note + * @param + * @retval + */ +uint8_t RF24::read_payload(void* buf, uint8_t len) +{ + /*---------------------------------------------------------------------------*/ + uint8_t status; + uint8_t* current = reinterpret_cast < uint8_t * > (buf); + uint8_t data_len = min(len, payload_size); + uint8_t blank_len = dynamic_payloads_enabled ? 0 : payload_size - data_len; + /*---------------------------------------------------------------------------*/ + + //printf("[Reading %u bytes %u blanks]",data_len,blank_len); + csn(LOW); + status = spi.write(R_RX_PAYLOAD); + while (data_len--) + *current++ = spi.write(0xff); + while (blank_len--) + spi.write(0xff); + csn(HIGH); + + return status; +} + +/** + * @brief + * @note + * @param + * @retval + */ +uint8_t RF24::flush_rx(void) +{ + /*-----------*/ + uint8_t status; + /*-----------*/ + + csn(LOW); + status = spi.write(FLUSH_RX); + csn(HIGH); + + return status; +} + +/** + * @brief + * @note + * @param + * @retval + */ +uint8_t RF24::flush_tx(void) +{ + /*-----------*/ + uint8_t status; + /*-----------*/ + + csn(LOW); + status = spi.write(FLUSH_TX); + csn(HIGH); + + return status; +} + +/** + * @brief + * @note + * @param + * @retval + */ +uint8_t RF24::get_status(void) +{ + /*-----------*/ + uint8_t status; + /*-----------*/ + + csn(LOW); + status = spi.write(NOP); + csn(HIGH); + + return status; +} + +//void RF24::print_status(uint8_t status) +//{ +// printf(("STATUS\t\t = 0x%02x RX_DR=%x TX_DS=%x MAX_RT=%x RX_P_NO=%x TX_FULL=%x\r\n"), +// status, +// (status & _BV(RX_DR))?1:0, +// (status & _BV(TX_DS))?1:0, +// (status & _BV(MAX_RT))?1:0, +// ((status >> RX_P_NO) & 7), +// (status & _BV(TX_FULL))?1:0 +// ); +//} +// +// +// +//void RF24::print_observe_tx(uint8_t value) +//{ +// printf(("OBSERVE_TX=%02x: POLS_CNT=%x ARC_CNT=%x\r\n"), +// value, +// (value >> PLOS_CNT) & 15, +// (value >> ARC_CNT) & 15 +// ); +//} +// +// +// +//void RF24::print_byte_register(const char* name, uint8_t reg, uint8_t qty) +//{ +// char extra_tab = strlen(name) < 8 ? '\t' : 0; +// printf("%s =",name); +// while (qty--) +// printf((" 0x%02x"),read_register(reg++)); +// printf(("\r\n")); +//} +// +// +// +//void RF24::print_address_register(const char* name, uint8_t reg, uint8_t qty) +//{ +// char extra_tab = strlen_P(name) < 8 ? '\t' : 0; +// printf("%s =",name); +// +// while (qty--) +// { +// uint8_t buffer[5]; +// read_register(reg++,buffer,sizeof buffer); +// +// printf((" 0x")); +// uint8_t* bufptr = buffer + sizeof buffer; +// while( --bufptr >= buffer ) +// printf(("%02x"),*bufptr); +// } +// +// printf(("\r\n")); +//} + +// +RF24::RF24(PinName mosi, PinName miso, PinName sck, PinName _csnpin, PinName _cepin) : + ce_pin(_cepin), + csn_pin(_csnpin), + wide_band(true), + p_variant(false), + payload_size(32), + ack_payload_available(false), + dynamic_payloads_enabled(false), + pipe0_reading_address(0), + spi(mosi, miso, sck) +{ + spi.frequency(10000000 / 5); // 2Mbit, 1/5th the maximum transfer rate for the spi bus + spi.format(8, 0); // 8-bit, ClockPhase = 0, ClockPolarity = 0 + wait_ms(100); +} + +/** + * @brief + * @note + * @param + * @retval + */ +void RF24::setChannel(uint8_t channel) +{ + // TODO: This method could take advantage of the 'wide_band' calculation + + // done in setChannel() to require certain channel spacing. + /*------------------------------*/ + const uint8_t max_channel = 127; + /*------------------------------*/ + + write_register(RF_CH, min(channel, max_channel)); +} + +/** + * @brief + * @note + * @param + * @retval + */ +void RF24::setPayloadSize(uint8_t size) +{ + /*----------------------------------*/ + const uint8_t max_payload_size = 32; + /*----------------------------------*/ + + payload_size = min(size, max_payload_size); +} + +/** + * @brief + * @note + * @param + * @retval + */ +uint8_t RF24::getPayloadSize(void) +{ + return payload_size; +} + +/*$off*/ +static const char rf24_datarate_e_str_0[] = "1MBPS"; +static const char rf24_datarate_e_str_1[] = "2MBPS"; +static const char rf24_datarate_e_str_2[] = "250KBPS"; +static const char *const rf24_datarate_e_str_P[] = { rf24_datarate_e_str_0, rf24_datarate_e_str_1, rf24_datarate_e_str_2, }; + +static const char rf24_model_e_str_0[] = "nRF24L01"; +static const char rf24_model_e_str_1[] = "nRF24L01+"; +static const char *const rf24_model_e_str_P[] = { rf24_model_e_str_0, rf24_model_e_str_1, }; + +static const char rf24_crclength_e_str_0[] = "Disabled"; +static const char rf24_crclength_e_str_1[] = "8 bits"; +static const char rf24_crclength_e_str_2[] = "16 bits"; +static const char *const rf24_crclength_e_str_P[] = { rf24_crclength_e_str_0, rf24_crclength_e_str_1, rf24_crclength_e_str_2, }; + +static const char rf24_pa_dbm_e_str_0[] = "PA_MIN"; +static const char rf24_pa_dbm_e_str_1[] = "PA_LOW"; +static const char rf24_pa_dbm_e_str_2[] = "PA_MED"; +static const char rf24_pa_dbm_e_str_3[] = "PA_HIGH"; +static const char *const rf24_pa_dbm_e_str_P[] = { rf24_pa_dbm_e_str_0, rf24_pa_dbm_e_str_1, rf24_pa_dbm_e_str_2, rf24_pa_dbm_e_str_3, }; + +//void RF24::printDetails(void) + +//{ +// print_status(get_status()); +// +// print_address_register(("RX_ADDR_P0-1"),RX_ADDR_P0,2); +// print_byte_register(("RX_ADDR_P2-5"),RX_ADDR_P2,4); +// print_address_register(("TX_ADDR"),TX_ADDR); +// +// print_byte_register(("RX_PW_P0-6"),RX_PW_P0,6); +// print_byte_register(("EN_AA"),EN_AA); +// print_byte_register(("EN_RXADDR"),EN_RXADDR); +// print_byte_register(("RF_CH"),RF_CH); +// print_byte_register(("RF_SETUP"),RF_SETUP); +// print_byte_register(("CONFIG"),CONFIG); +// print_byte_register(("DYNPD/FEATURE"),DYNPD,2); +// +// printf(("Data Rate\t = %s\r\n"), rf24_datarate_e_str_P[getDataRate()]); +// printf(("Model\t\t = %s\r\n"), rf24_model_e_str_P[isPVariant()]); +// printf(("CRC Length\t = %s\r\n"),rf24_crclength_e_str_P[getCRCLength()]); +// printf(("PA Power\t = %s\r\n"),rf24_pa_dbm_e_str_P[getPALevel()]); +//} + +// + +/*$on*/ +void RF24::begin (void) +{ + // Initialize pins + // pinMode(ce_pin,OUTPUT); + // pinMode(csn_pin,OUTPUT); + // Initialize spi bus + //spi.begin(); + mainTimer.start(); + + ce(LOW); + csn(HIGH); + + // Must allow the radio time to settle else configuration bits will not necessarily stick. + // This is actually only required following power up but some settling time also appears to + // be required after resets too. For full coverage, we'll always assume the worst. + // Enabling 16b CRC is by far the most obvious case if the wrong timing is used - or skipped. + // Technically we require 4.5ms + 14us as a worst case. We'll just call it 5ms for good measure. + // WARNING: wait_ms is based on P-variant whereby non-P *may* require different timing. + wait_ms(5); + + // Set 1500uS (minimum for 32B payload in ESB@250KBPS) timeouts, to make testing a little easier + // WARNING: If this is ever lowered, either 250KBS mode with AA is broken or maximum packet + // sizes must never be used. See documentation for a more complete explanation. + write_register(SETUP_RETR, (4 << ARD) | (15 << ARC)); + + // Restore our default PA level + setPALevel(RF24_PA_MAX); + + // Determine if this is a p or non-p RF24 module and then + // reset our data rate back to default value. This works + // because a non-P variant won't allow the data rate to + // be set to 250Kbps. + if (setDataRate(RF24_250KBPS)) + { + p_variant = true; + } + // Then set the data rate to the slowest (and most reliable) speed supported by all + // hardware. + setDataRate(RF24_1MBPS); + + // Initialize CRC and request 2-byte (16bit) CRC + setCRCLength(RF24_CRC_16); + + // Disable dynamic payloads, to match dynamic_payloads_enabled setting + write_register(DYNPD, 0); + + // Reset current status + // Notice reset and flush is the last thing we do + write_register(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT)); + + // Set up default configuration. Callers can always change it later. + // This channel should be universally safe and not bleed over into adjacent + // spectrum. + setChannel(76); + + //setChannel(90); + // Flush buffers + flush_rx(); + flush_tx(); + + // set EN_RXADDRR to 0 to prevent pipe 0 and pipe 1 from receiving + write_register(EN_RXADDR, 0); +} + +/** + * @brief + * @note + * @param + * @retval + */ +void RF24::startListening(void) +{ + write_register(CONFIG, read_register(CONFIG) | _BV(PWR_UP) | _BV(PRIM_RX)); + write_register(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT)); + + // Restore the pipe0 adddress, if exists + if (pipe0_reading_address) + write_register(RX_ADDR_P0, reinterpret_cast < const uint8_t * > (&pipe0_reading_address), 5); + + // Flush buffers + flush_rx(); + flush_tx(); + + // Go! + ce(HIGH); + + // wait for the radio to come up + wait_us(130); +} + +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 }; + +/** + * @brief + * @note + * @param + * @retval + */ +void RF24::stopListening(void) +{ + ce(LOW); + + wait_us(txRxDelay); + + if (read_register(FEATURE) & _BV(EN_ACK_PAY)) + { + wait_us(txRxDelay); //200 + flush_tx(); + } + + //flush_rx(); + write_register(CONFIG, (read_register(CONFIG)) &~_BV(PRIM_RX)); + + write_register(EN_RXADDR, read_register(EN_RXADDR) | _BV(child_pipe_enable[0])); // Enable RX on pipe0 + wait_us(100); +} + +/** + * @brief + * @note + * @param + * @retval + */ +void RF24::powerDown(void) +{ + write_register(CONFIG, read_register(CONFIG) &~_BV(PWR_UP)); +} + +/** + * @brief + * @note + * @param + * @retval + */ +void RF24::powerUp(void) +{ + write_register(CONFIG, read_register(CONFIG) | _BV(PWR_UP)); +} + +/** + * @brief + * @note + * @param + * @retval + */ +bool RF24::write(const void* buf, uint8_t len) +{ + /*------------------------------------------*/ + bool result = false; + uint8_t observe_tx; + uint8_t status; + uint32_t sent_at = mainTimer.read_ms(); + const uint32_t timeout = 500; //ms to wait for timeout + + /*------------------------------------------*/ + // Begin the write + startWrite(buf, len); + + // ------------ + // At this point we could return from a non-blocking write, and then call + // the rest after an interrupt + // Instead, we are going to block here until we get TX_DS (transmission completed and ack'd) + // or MAX_RT (maximum retries, transmission failed). Also, we'll timeout in case the radio + // is flaky and we get neither. + // IN the end, the send should be blocking. It comes back in 60ms worst case, or much faster + // if I tighted up the retry logic. (Default settings will be 1500us. + // Monitor the send + do { + status = read_register(OBSERVE_TX, &observe_tx, 1); + + // IF_SERIAL_DEBUG(Serial.print(observe_tx,HEX)); + } while (!(status & (_BV(TX_DS) | _BV(MAX_RT))) && (mainTimer.read_ms() - sent_at < timeout)); + + // The part above is what you could recreate with your own interrupt handler, + // and then call this when you got an interrupt + // ------------ + // Call this when you get an interrupt + // The status tells us three things + // * The send was successful (TX_DS) + // * The send failed, too many retries (MAX_RT) + // * There is an ack packet waiting (RX_DR) + bool tx_ok, tx_fail; + /*-------------------*/ + + whatHappened(tx_ok, tx_fail, ack_payload_available); + + //printf("%u%u%u\r\n",tx_ok,tx_fail,ack_payload_available); + result = tx_ok; + + // IF_SERIAL_DEBUG(Serial.print(result?"...OK.":"...Failed")); + // Handle the ack packet + if (ack_payload_available) + { + ack_payload_length = getDynamicPayloadSize(); + + // IF_SERIAL_DEBUG(Serial.print("[AckPacket]/")); + // IF_SERIAL_DEBUG(Serial.println(ack_payload_length,DEC)); + } + + // Yay, we are done. + // Power down + // powerDown(); + // Flush buffers (Is this a relic of past experimentation, and not needed anymore? + // flush_tx(); + return result; +} + +/** + * @brief + * @note + * @param + * @retval + */ +void RF24::startWrite(const void* buf, uint8_t len) +{ + // Transmitter power-up + + write_register(CONFIG, (read_register(CONFIG) | _BV(PWR_UP)) &~_BV(PRIM_RX)); + wait_us(130); + + // Send the payload + write_payload(buf, len); + + ce(HIGH); + wait_us(15); + ce(LOW); +} + +/** + * @brief + * @note + * @param + * @retval + */ +uint8_t RF24::getDynamicPayloadSize(void) +{ + /*---------------*/ + uint8_t result = 0; + /*---------------*/ + + csn(LOW); + spi.write(R_RX_PL_WID); + result = spi.write(0xff); + csn(HIGH); + + return result; +} + +/** + * @brief + * @note + * @param + * @retval + */ +bool RF24::available(void) +{ + return available(NULL); +} + +/** + * @brief + * @note + * @param + * @retval + */ +bool RF24::available(uint8_t* pipe_num) +{ + /*-----------------------------------*/ + uint8_t status = get_status(); + bool result = (status & _BV(RX_DR)); + /*-----------------------------------*/ + + // Too noisy, enable if you really want lots o data!! + //IF_SERIAL_DEBUG(print_status(status)); + if (result) + { + // If the caller wants the pipe number, include that + if (pipe_num) + *pipe_num = (status >> RX_P_NO) & 7; + + // Clear the status bit + // ??? Should this REALLY be cleared now? Or wait until we + // actually READ the payload? + write_register(STATUS, _BV(RX_DR)); + + // Handle ack payload receipt + if (status & _BV(TX_DS)) + { + write_register(STATUS, _BV(TX_DS)); + } + } + + return result; +} + +/** + * @brief + * @note + * @param + * @retval + */ +bool RF24::read(void* buf, uint8_t len) +{ + // Fetch the payload + + read_payload(buf, len); + + // was this the last of the data available? + return read_register(FIFO_STATUS) & _BV(RX_EMPTY); +} + +/** + * @brief + * @note + * @param + * @retval + */ +void RF24::whatHappened(bool& tx_ok, bool& tx_fail, bool& rx_ready) +{ + // Read the status & reset the status in one easy call + + // Or is that such a good idea? + uint8_t status = write_register(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT)); + + // Report to the user what happened + + tx_ok = status & _BV(TX_DS); + tx_fail = status & _BV(MAX_RT); + rx_ready = status & _BV(RX_DR); +} + +/** + * @brief + * @note + * @param + * @retval + */ +void RF24::openWritingPipe(uint64_t value) +{ + // Note that AVR 8-bit uC's store this LSB first, and the NRF24L01(+) + // expects it LSB first too, so we're good. + write_register(RX_ADDR_P0, reinterpret_cast < uint8_t * > (&value), 5); + write_register(TX_ADDR, reinterpret_cast < uint8_t * > (&value), 5); + + /*----------------------------------*/ + const uint8_t max_payload_size = 32; + /*----------------------------------*/ + + write_register(RX_PW_P0, min(payload_size, max_payload_size)); + + flush_tx(); +} + +/** + * @brief + * @note + * @param + * @retval + */ +void RF24::openReadingPipe(uint8_t child, uint64_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 = address; + + if (child <= 6) + { + // For pipes 2-5, only write the LSB + //if (child < 2) + write_register(child_pipe[child], reinterpret_cast < const uint8_t * > (&address), 5); + //else + // write_register(child_pipe[child], reinterpret_cast < const uint8_t * > (&address), 1); + + write_register(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. + write_register(EN_RXADDR, read_register(EN_RXADDR) | _BV(child_pipe_enable[child])); + } +} + +/** + * @brief + * @note + * @param + * @retval + */ +void RF24::toggle_features(void) +{ + csn(LOW); + spi.write(ACTIVATE); + spi.write(0x73); + csn(HIGH); +} + +/** + * @brief + * @note + * @param + * @retval + */ +void RF24::enableDynamicPayloads(void) +{ + // Enable dynamic payload throughout the system + + write_register(FEATURE, read_register(FEATURE) | _BV(EN_DPL)); + + // If it didn't work, the features are not enabled + if (!read_register(FEATURE)) + { + // So enable them and try again + toggle_features(); + write_register(FEATURE, read_register(FEATURE) | _BV(EN_DPL)); + } + + // IF_SERIAL_DEBUG(printf("FEATURE=%i\r\n",read_register(FEATURE))); + // Enable dynamic payload on all pipes + // + // Not sure the use case of only having dynamic payload on certain + // pipes, so the library does not support it. + write_register + ( + DYNPD, + read_register(DYNPD) | _BV(DPL_P5) | _BV(DPL_P4) | _BV(DPL_P3) | _BV(DPL_P2) | _BV(DPL_P1) | _BV(DPL_P0) + ); + + dynamic_payloads_enabled = true; +} + +/** + * @brief + * @note + * @param + * @retval + */ +void RF24::enableAckPayload(void) +{ + // + + // enable ack payload and dynamic payload features + // + write_register(FEATURE, read_register(FEATURE) | _BV(EN_ACK_PAY) | _BV(EN_DPL)); + + // If it didn't work, the features are not enabled + if (!read_register(FEATURE)) + { + // So enable them and try again + toggle_features(); + write_register(FEATURE, read_register(FEATURE) | _BV(EN_ACK_PAY) | _BV(EN_DPL)); + } + + // IF_SERIAL_DEBUG(printf("FEATURE=%i\r\n",read_register(FEATURE))); + // + // Enable dynamic payload on pipes 0 & 1 + // + write_register(DYNPD, read_register(DYNPD) | _BV(DPL_P1) | _BV(DPL_P0)); +} + +/** + * @brief + * @note + * @param + * @retval + */ +void RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len) +{ + /*-----------------------------------------------------------------*/ + const uint8_t* current = reinterpret_cast < const uint8_t * > (buf); + /*-----------------------------------------------------------------*/ + + csn(LOW); + spi.write(W_ACK_PAYLOAD | (pipe & 7)); + + /*--------------------------------------------------*/ + const uint8_t max_payload_size = 32; + uint8_t data_len = min(len, max_payload_size); + /*--------------------------------------------------*/ + + while (data_len--) + spi.write(*current++); + + csn(HIGH); +} + +/** + * @brief + * @note + * @param + * @retval + */ +bool RF24::isAckPayloadAvailable(void) +{ + /*-----------------------------------*/ + bool result = ack_payload_available; + /*-----------------------------------*/ + + ack_payload_available = false; + return result; +} + +/** + * @brief + * @note + * @param + * @retval + */ +bool RF24::isPVariant(void) +{ + return p_variant; +} + +/** + * @brief + * @note + * @param + * @retval + */ +void RF24::setAutoAck(bool enable) +{ + if (enable) + write_register(EN_AA, 63); + else + write_register(EN_AA, 0); +} + +/** + * @brief + * @note + * @param + * @retval + */ +void RF24::setAutoAck(uint8_t pipe, bool enable) +{ + if (pipe <= 6) + { + /*---------------------------------*/ + uint8_t en_aa = read_register(EN_AA); + /*---------------------------------*/ + + if (enable) + { + en_aa |= _BV(pipe); + } + else + { + en_aa &= ~_BV(pipe); + } + + write_register(EN_AA, en_aa); + } +} + +/** + * @brief + * @note + * @param + * @retval + */ +bool RF24::testCarrier(void) +{ + return(read_register(CD) & 1); +} + +/** + * @brief + * @note + * @param + * @retval + */ +bool RF24::testRPD(void) +{ + return(read_register(RPD) & 1); +} + +/** + * @brief + * @note + * @param + * @retval + */ +void RF24::setPALevel(rf24_pa_dbm_e level) +{ + /*------------------------------------*/ + uint8_t setup = read_register(RF_SETUP); + /*------------------------------------*/ + + setup &= ~(_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)); + + // switch uses RAM (evil!) + if (level == RF24_PA_MAX) + { + setup |= (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)); + } + else + if (level == RF24_PA_HIGH) + { + setup |= _BV(RF_PWR_HIGH); + } + else + if (level == RF24_PA_LOW) + { + setup |= _BV(RF_PWR_LOW); + } + else + if (level == RF24_PA_MIN) + { + // nothing + } + else + if (level == RF24_PA_ERROR) + { + // On error, go to maximum PA + setup |= (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)); + } + + write_register(RF_SETUP, setup); +} + +/** + * @brief + * @note + * @param + * @retval + */ +rf24_pa_dbm_e RF24::getPALevel(void) +{ + /*-----------------------------------------------------------------------------------*/ + rf24_pa_dbm_e result = RF24_PA_ERROR; + uint8_t power = read_register(RF_SETUP) & (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH)); + /*-----------------------------------------------------------------------------------*/ + + // switch uses RAM (evil!) + if (power == (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH))) + { + result = RF24_PA_MAX; + } + else + if (power == _BV(RF_PWR_HIGH)) + { + result = RF24_PA_HIGH; + } + else + if (power == _BV(RF_PWR_LOW)) + { + result = RF24_PA_LOW; + } + else + { + result = RF24_PA_MIN; + } + + return result; +} + +/** + * @brief + * @note + * @param + * @retval + */ +bool RF24::setDataRate(rf24_datarate_e speed) +{ + /*------------------------------------*/ + bool result = false; + uint8_t setup = read_register(RF_SETUP); + /*------------------------------------*/ + + // HIGH and LOW '00' is 1Mbs - our default + setup &= ~(_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)); + + txRxDelay = 250; + if (speed == RF24_250KBPS) + { + // Must set the RF_DR_LOW to 1; RF_DR_HIGH (used to be RF_DR) is already 0 + // Making it '10'. + setup |= _BV(RF_DR_LOW); + txRxDelay = 450; + } + else + { + // Set 2Mbs, RF_DR (RF_DR_HIGH) is set 1 + // Making it '01' + if (speed == RF24_2MBPS) + { + setup |= _BV(RF_DR_HIGH); + txRxDelay = 190; + } + } + + write_register(RF_SETUP, setup); + + // Verify our result + if (read_register(RF_SETUP) == setup) + { + result = true; + } + + return result; +} + +/** + * @brief + * @note + * @param + * @retval + */ +rf24_datarate_e RF24::getDataRate(void) +{ + /*------------------------------------------------------------------------------*/ + rf24_datarate_e result; + uint8_t dr = read_register(RF_SETUP) & (_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)); + /*------------------------------------------------------------------------------*/ + + // switch uses RAM (evil!) + // Order matters in our case below + if (dr == _BV(RF_DR_LOW)) + { + // '10' = 250KBPS + result = RF24_250KBPS; + } + else + if (dr == _BV(RF_DR_HIGH)) + { + // '01' = 2MBPS + result = RF24_2MBPS; + } + else + { + // '00' = 1MBPS + result = RF24_1MBPS; + } + + return result; +} + +/** + * @brief + * @note + * @param + * @retval + */ +void RF24::setCRCLength(rf24_crclength_e length) +{ + /*---------------------------------------------------------------*/ + uint8_t config = read_register(CONFIG) &~(_BV(CRCO) | _BV(EN_CRC)); + /*---------------------------------------------------------------*/ + + if (length == RF24_CRC_DISABLED) + { + // Do nothing, we turned it off above. + } + else + if (length == RF24_CRC_8) + { + config |= _BV(EN_CRC); + } + else + { + config |= _BV(EN_CRC); + config |= _BV(CRCO); + } + + write_register(CONFIG, config); +} + +/** + * @brief + * @note + * @param + * @retval + */ +rf24_crclength_e RF24::getCRCLength(void) +{ + /*---------------------------------------------------------------------------*/ + rf24_crclength_e result = RF24_CRC_DISABLED; + uint8_t config = read_register(CONFIG) & (_BV(CRCO) | _BV(EN_CRC)); + /*---------------------------------------------------------------------------*/ + + if (config & _BV(EN_CRC)) + { + if (config & _BV(CRCO)) + result = RF24_CRC_16; + else + result = RF24_CRC_8; + } + + return result; +} + +/** + * @brief + * @note + * @param + * @retval + */ +void RF24::disableCRC(void) +{ + /*--------------------------------------------------*/ + uint8_t disable = read_register(CONFIG) &~_BV(EN_CRC); + /*--------------------------------------------------*/ + + write_register(CONFIG, disable); +} + +/** + * @brief + * @note + * @param + * @retval + */ +void RF24::setRetries(uint8_t delay, uint8_t count) +{ + write_register(SETUP_RETR, (delay & 0xf) << ARD | (count & 0xf) << ARC); +} + +/** + * @brief + * @note + * @param + * @retval + */ +uint8_t RF24::min(uint8_t a, uint8_t b) +{ + if (a < b) + return a; + else + return b; +}