This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Using NRF52840 RADIO with basic/custom protocol without ESB

Hello,

I have adapted some of the ESB proprietary Protocol from the NRF SDK into my own project without installing the SDK directly; however, complete integration seems out of reach due to Arduino's overlapping functionality. 

With that being said, my goal is to develop a simple dedicated Transmitter and Receiver pair on two separate NRF52840s. No acknowledgements are needed due to time constraints. Below are the current steps I have taken to initialize the RADIO in the transmitter and receiver, respectively. Currently, the receiver cannot seem to capture any packets despite the two boards being <20cm. 

I was able to verify that each respective board is in TX (during packet transmission) and RX RADIO states when expected. I was wondering if there are any issues with my current initialization as the ADDRESS event never triggers on the receiver. I also have surrounded each initialization with "assert" to fall in line with the NRF52840 RADIO specifications.

Transmitter Initialization:

PCNF0 Register:

LFLEN 8
S0LEN 0
S1LEN 0
S1INC 0
CILEN 0
PLEN 2
CRCINC 0
TERMLEN 0

PCNF1 Register:

MAXLEN 255
STATLEN 0
BALEN 4
ENDIAN 0
WHITEEN 1

BASE0 = 0xABCDDBF5

PREFIX0.AP0 = 0xEF

TXADDRESS = 0x00

FREQUENCY = 0x00

CRCCNF:

LEN 2
SKIPADDR 0

CRCPOLY = 0xd175

Receiver Initialization:

PCNF0 Register:

LFLEN 8
S0LEN 0
S1LEN 0
S1INC 0
CILEN 0
PLEN 2
CRCINC 0
TERMLEN 0

PCNF1 Register:

MAXLEN 255
STATLEN 0
BALEN 4
ENDIAN 0
WHITEEN 1

BASE0 = 0xABCDDBF5

PREFIX0.AP0 = 0xEF

RXADDRESSES = 0x01

FREQUENCY = 0x00

CRCCNF:

LEN 2
SKIPADDR 0

CRCPOLY = 0xd175

  • Hi,

     

    Is the HFCLK started on both sides?

    That is done by calling this sequence:

    NRF_CLOCK->TASKS_HFCLKSTART = 1;
    while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0);
    NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;

     

    Kind regards,

    Håkon

  • Hi Håkon, 

    Thanks for the response. Yes! I checked the status of the high frequency clock on both boards and it was running (indicated by a 1UL in the HFSTAT register). 

  • Hi,

     

    The HFCLKSTAT register should read 0x10001:

    https://infocenter.nordicsemi.com/topic/com.nordic.infocenter.nrf52832.ps.v1.1/clock.html?cp=4_2_0_18_2_3#register.HFCLKSTAT

     

    If it reads 0x1, it is not running on external hfclk.

     

    Kind regards,

    Håkon

  • My apologies. I meant 0x10000 instead of 0x1. That being said, I’ll set the A bit and see if that begins transmission. 

  • Hi Håkon,

    I added the following function to both sides before initiatialization:

    /**
     * Start the High Frequency Clock
     */
    inline void start_hf_clock()
    {
      NRF_CLOCK->TASKS_HFCLKSTART = 1;
      while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0);
      NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
    }
     

    And I verified that the HFCLKSTAT register was at 0x10001 following the execution of that function (triggered before the enabling for transmission and reception commands below).

    // TRANSMITTER BOARD
    NRF_RADIO->TASKS_TXEN = 1UL; // Enable the RADIO in TX Mode
    
    // RECEIVER BOARD
    NRF_RADIO->TASKS_RXEN = 1UL; // Enable the RADIO in RX Mode

    Still, the issue seems to persist. I will provide a breakdown of each of my initialization functions.

    Transmitter Functions:

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ///
    ///   POSITIONS & MASKS
    ///
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    enum NRF_RADIO_PCNF0_POS
    {
      RADIO_PCNF0_LFLEN_POS   = 0UL,
      RADIO_PCNF0_S0LEN_POS   = 8UL,
      RADIO_PCNF0_S1LEN_POS   = 16UL,
      RADIO_PCNF0_S1INCL_POS  = 20UL,
      RADIO_PCNF0_CILEN_POS   = 22UL,
      RADIO_PCNF0_PLEN_POS    = 24UL,
      RADIO_PCNF0_CRCINC_POS  = 26UL,
      RADIO_PCNF0_TERMLEN_POS = 29UL
    };
    
    enum NRF_RADIO_PCNF0_MSK
    {
      RADIO_PCNF0_LFLEN_MSK   = 0x000FUL,
      RADIO_PCNF0_S0LEN_MSK   = 0x0001UL,
      RADIO_PCNF0_S1LEN_MSK   = 0x000FUL,
      RADIO_PCNF0_S1INCL_MSK  = 0x0001UL,
      RADIO_PCNF0_CILEN_MSK   = 0x0003UL,
      RADIO_PCNF0_PLEN_MSK    = 0x0003UL,
      RADIO_PCNF0_CRCINC_MSK  = 0x0001UL,
      RADIO_PCNF0_TERMLEN_MSK = 0x0003UL
    };
    
    enum NRF_RADIO_PCNF1_POS
    {
      RADIO_PCNF1_MAXLEN_POS  = 0UL,
      RADIO_PCNF1_STATLEN_POS = 8UL,
      RADIO_PCNF1_BALEN_POS   = 16UL,
      RADIO_PCNF1_ENDIAN_POS  = 24UL,
      RADIO_PCNF1_WHITEEN_POS = 25UL
    };
    
    enum NRF_RADIO_PCNF1_MSK
    {
      RADIO_PCNF1_MAXLEN_MSK  = 0x00FFUL,
      RADIO_PCNF1_STATLEN_MSK = 0x00FFUL,
      RADIO_PCNF1_BALEN_MSK   = 0x0007UL,
      RADIO_PCNF1_ENDIAN_MSK  = 0x0001UL,
      RADIO_PCNF1_WHITEEN_MSK = 0x0001UL
    };
    
    enum NRF_RADIO_PREFIX0_POS
    {
      RADIO_PREFIX0_AP0 = 0UL,
      RADIO_PREFIX0_AP1 = 8UL,
      RADIO_PREFIX0_AP2 = 16UL,
      RADIO_PREFIX0_AP3 = 24UL
    };
    
    enum NRF_PREFIX1_POS
    {
      RADIO_PREFIX1_AP4 = 0UL,
      RADIO_PREFIX1_AP5 = 8UL,
      RADIO_PREFIX1_AP6 = 16UL,
      RADIO_PREFIX1_AP7 = 24UL
    };
    
    enum NRF_RADIO_PREFIX_MSK
    {
      RADIO_PREFIX_MSK = 0xFFUL
    };
    
    enum NRF_RADIO_TXADDRESS_MSK
    {
      RADIO_TXADDRESS_MSK = 0x0007UL
    };
    
    enum NRF_RADIO_CRCCNF_POS
    {
      RADIO_CRCCNF_LEN_POS      = 0UL,
      RADIO_CRCCNF_SKIPADDR_POS = 8UL
    };
    
    enum NRF_RADIO_CRCCNF_MSK
    {
      RADIO_CRCCNF_LEN_MSK = 0x0003UL,
      RADIO_CRCCNF_SKIPADDR_MSK = 0x0003UL
    };
    
    enum NRF_RADIO_CRCPOLY_MSK
    {
      RADIO_CRCPOLY_MSK = 0xFFFFFFUL
    };
    
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ///
    ///   VALUE IDS
    ///
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    
    // Data Rate and Modulation
    enum NRF_RADIO_MODE
    {
      NRF_1MBIT          = 0,  // 1 Mbit/s Nordic proprietary radio mode
      NRF_2MBIT          = 1,  // 2 Mbit/s Nordic proprietary radio mode
      BLE_1MBIT          = 3,  // 1 Mbit/s BLE
      BLE_2MBIT          = 4,  // 2 Mbit/s BLE
      BLE_LR125KBIT      = 5,  // Long range 125 kbit/s TX, 125 kbit/s and 500 kbit/s RX
      BLE_LR500KBIT      = 6,  // Long range 500 kbit/s TX, 125 kbit/s and 500 kbit/s RX
      IEEE802154_250KBIT = 15  // IEEE 802.15.4-2006 250 kbit/s
    };
    
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ///
    ///   HELPER FUNCTIONS
    ///
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    /**
     * Set PCNF0 Register in NRF RADIO Peripheral
     * 
     * @param length_field_len: Length on air of LENGTH field in number of bits.
     * @param s0_field_len: Length on air of S0 field in number of bytes.
     * @param s1_field_len: Length on air of S1 field in number of bits.
     * @param include_s1_field: Include or exclude S1 field in RAM
     * @param code_indicator_len: Length of code indicator - long range
     * @param preamble_len: Length of preamble on air. Decision point: TASKS_START task
     * @param include_crc_in_len: Indicates if LENGTH field contains CRC or not
     * @param term_field_len: Length of TERM field in Long Range operation
     */
    void set_pcnf0(uint32_t length_field_len, 
                   uint32_t s0_field_len,
                   uint32_t s1_field_len,
                   uint32_t include_s1_field,
                   uint32_t code_indicator_len,
                   uint32_t preamble_len,
                   uint32_t include_crc,
                   uint32_t term_field_len)
    {
      assert(length_field_len   <= RADIO_PCNF0_LFLEN_MSK);
      assert(s0_field_len       <= RADIO_PCNF0_S0LEN_MSK);
      assert(s1_field_len       <= RADIO_PCNF0_S1LEN_MSK);
      assert(include_s1_field   <= RADIO_PCNF0_S1INCL_MSK);
      assert(code_indicator_len <= RADIO_PCNF0_CILEN_MSK);
      assert(preamble_len       <= RADIO_PCNF0_PLEN_MSK);
      assert(include_crc        <= RADIO_PCNF0_CRCINC_MSK);
      assert(term_field_len     <= RADIO_PCNF0_TERMLEN_MSK);
      
      NRF_RADIO->PCNF0 = (length_field_len   << RADIO_PCNF0_LFLEN_POS)  |
                         (s0_field_len       << RADIO_PCNF0_S0LEN_POS)  |
                         (s1_field_len       << RADIO_PCNF0_S1LEN_POS)  |
                         (include_s1_field   << RADIO_PCNF0_S1INCL_POS) |
                         (code_indicator_len << RADIO_PCNF0_CILEN_POS)  |
                         (preamble_len       << RADIO_PCNF0_PLEN_POS)   |
                         (include_crc        << RADIO_PCNF0_CRCINC_POS) |
                         (term_field_len     << RADIO_PCNF0_TERMLEN_POS);
    }
    
    /**
     * Set PCNF1 Register in NRF RADIO Peripheral
     * 
     * @param maximum_packet_payload_length: Maximum length of packet payload
     * @param static_length: Static length in number of bytes
     * @param base_address_length: Base address length in number of bytes
     * @param endianness: On air endianness of packet, this applies to the S0, LENGTH, S1 and the PAYLOAD fields.
     * @param packet_whitening_enable: Enable or disable packet whitening
     */
    void set_pcnf1(uint32_t maximum_packet_payload_length,
                   uint32_t static_length,
                   uint32_t base_address_length,
                   uint32_t endianness,
                   uint32_t packet_whitening_enable)
    {
      assert(maximum_packet_payload_length <= RADIO_PCNF1_MAXLEN_MSK);
      assert(static_length                 <= RADIO_PCNF1_STATLEN_MSK);
      assert(base_address_length           <= RADIO_PCNF1_BALEN_MSK);
      assert(endianness                    <= RADIO_PCNF1_ENDIAN_MSK);
      assert(packet_whitening_enable       <= RADIO_PCNF1_WHITEEN_MSK);
    
      NRF_RADIO->PCNF1 = (maximum_packet_payload_length << RADIO_PCNF1_MAXLEN_POS)  |
                         (static_length                 << RADIO_PCNF1_STATLEN_POS) |
                         (base_address_length           << RADIO_PCNF1_BALEN_POS)   |
                         (endianness                    << RADIO_PCNF1_ENDIAN_POS)  |
                         (packet_whitening_enable       << RADIO_PCNF1_WHITEEN_POS);
    }
    
    /**
     * Set the BASE Register
     * 
     * @param base_address: Base address 0
     */
    void set_base0(uint32_t base_address)
    {
      NRF_RADIO->BASE0 = base_address;
    }
    
    /**
     * Set the PREFIX0.AP0 Register
     * 
     * @param prefix0_address: PREFIX0 Address for AP0
     */
    void set_prefix0_ap0(uint32_t prefix0_address)
    {
      prefix0_address &= RADIO_PREFIX_MSK;
      NRF_RADIO->PREFIX0 = prefix0_address << RADIO_PREFIX0_AP0;
    }
    
    /**
     * Set the transmission address
     * 
     * @param tx_address: Transmission Address
     */
    void set_txaddress(uint32_t tx_address)
    {
      tx_address &= RADIO_TXADDRESS_MSK;
      NRF_RADIO->TXADDRESS = tx_address;
    }
    
    /**
     * Configure the CRC
     * 
     * @param crc_length: CRC length in number of bytes.
     * @param crc_skipaddress: Include or exclude packet address field out of CRC calculation.
     */
    void configure_crc(uint32_t crc_length, uint32_t crc_skipaddress)
    {
      assert(crc_length      <= RADIO_CRCCNF_LEN_MSK);
      assert(crc_skipaddress <= RADIO_CRCCNF_SKIPADDR_MSK);
      
      NRF_RADIO->CRCCNF = (crc_length      << RADIO_CRCCNF_LEN_POS) |
                          (crc_skipaddress << RADIO_CRCCNF_SKIPADDR_MSK);
    }
    
    /**
     * Set the CRC Polynomial
     * 
     * @param crc_polynomial: CRC Polynomial
     *                        Each term in the CRC polynomial is mapped to a bit in this register which index 
     *                        corresponds to the term's exponent. The least significant term/bit is hard-wired 
     *                        internally to 1, and bit number 0 of the register content is ignored by the hardware. 
     *                        The following example is for an 8 bit CRC polynomial: x8 + x7 + x3 + x2 + 1 = 1 1000 1101 .
     */
    void set_crc_polynomial(uint32_t crc_polynomial)
    {
      crc_polynomial &= RADIO_CRCPOLY_MSK;
      NRF_RADIO->CRCPOLY = crc_polynomial;
    }
    
    /**
     * Prepare the NRF RADIO Peripheral Packet Pointer (Double Buffered)
     */
    inline void prepare_radio_packet_ptr(uint8_t* packet_ptr)
    {
      NRF_RADIO->PACKETPTR = (uint32_t)packet_ptr;
    }
    
    /**
     * Set the radio frequency
     * 
     * @param frequency: X + 2400MHz
     */
    inline void set_radio_frequency(uint32_t frequency)
    {
      assert(frequency <= 100);
    
      NRF_RADIO->FREQUENCY = frequency;
    }
    
    /**
     * Transmit a Packet using the NRF RADIO Peripheral
     */
    inline void tx_radio_packet()
    {
      NRF_RADIO->TASKS_START = 1UL;
      while(NRF_RADIO->EVENTS_END == 0); // Wait for the packet to finish transmission
    }
    
    /**
     * Start the High Frequency Clock
     */
    inline void start_hf_clock()
    {
      NRF_CLOCK->TASKS_HFCLKSTART = 1;
      while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0);
      NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
    }
    
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ///
    ///   APPLICATION-SPECIFIC FUNCTIONS
    ///
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    /**
     * Initialize the NRF RADIO Peripheral in the desired configuration for Transmission to the Relay
     */
    void init_radio_for_transmission()
    { 
      NRF_RADIO->MODE = NRF_2MBIT; // Set to 2Mbps bandwidth
    
      // Set the PCNF0 & PCNF1 registers
      set_pcnf0(8UL, 0UL, 0UL, 0UL, 0UL, 2UL, 0UL, 0UL);
      set_pcnf1(255UL, 0UL, 4UL, 0UL, 1UL);
    
      // Set the BASE and PREFIX registers
      set_base0(0xABCDDBF5);
      set_prefix0_ap0(0xEF);
      set_txaddress(0x00); // Logical Address 0
      set_radio_frequency(0x00); // Set to 2400MHz
    
      // Set the CRCCNF & CRCPOLY Registers
      configure_crc(2UL, 0UL);
    
      // Set the CRC Polynomial - Utilizing this table:
      //   https://users.ece.cmu.edu/~koopman/crc/
      //    - From Carnegie Mellon University
      set_crc_polynomial(0xd175);
    
      start_hf_clock();
      
      NRF_RADIO->TASKS_TXEN = 1UL; // Enable the Transmitter
    }

    Receiver Functions:

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ///
    ///   POSITIONS & MASKS
    ///
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    enum NRF_RADIO_PCNF0_POS
    {
      RADIO_PCNF0_LFLEN_POS   = 0UL,
      RADIO_PCNF0_S0LEN_POS   = 8UL,
      RADIO_PCNF0_S1LEN_POS   = 16UL,
      RADIO_PCNF0_S1INCL_POS  = 20UL,
      RADIO_PCNF0_CILEN_POS   = 22UL,
      RADIO_PCNF0_PLEN_POS    = 24UL,
      RADIO_PCNF0_CRCINC_POS  = 26UL,
      RADIO_PCNF0_TERMLEN_POS = 29UL
    };
    
    enum NRF_RADIO_PCNF0_MSK
    {
      RADIO_PCNF0_LFLEN_MSK   = 0x000FUL,
      RADIO_PCNF0_S0LEN_MSK   = 0x0001UL,
      RADIO_PCNF0_S1LEN_MSK   = 0x000FUL,
      RADIO_PCNF0_S1INCL_MSK  = 0x0001UL,
      RADIO_PCNF0_CILEN_MSK   = 0x0003UL,
      RADIO_PCNF0_PLEN_MSK    = 0x0003UL,
      RADIO_PCNF0_CRCINC_MSK  = 0x0001UL,
      RADIO_PCNF0_TERMLEN_MSK = 0x0003UL
    };
    
    enum NRF_RADIO_PCNF1_POS
    {
      RADIO_PCNF1_MAXLEN_POS  = 0UL,
      RADIO_PCNF1_STATLEN_POS = 8UL,
      RADIO_PCNF1_BALEN_POS   = 16UL,
      RADIO_PCNF1_ENDIAN_POS  = 24UL,
      RADIO_PCNF1_WHITEEN_POS = 25UL
    };
    
    enum NRF_RADIO_PCNF1_MSK
    {
      RADIO_PCNF1_MAXLEN_MSK  = 0x00FFUL,
      RADIO_PCNF1_STATLEN_MSK = 0x00FFUL,
      RADIO_PCNF1_BALEN_MSK   = 0x0007UL,
      RADIO_PCNF1_ENDIAN_MSK  = 0x0001UL,
      RADIO_PCNF1_WHITEEN_MSK = 0x0001UL
    };
    
    enum NRF_RADIO_PREFIX0_POS
    {
      RADIO_PREFIX0_AP0 = 0UL,
      RADIO_PREFIX0_AP1 = 8UL,
      RADIO_PREFIX0_AP2 = 16UL,
      RADIO_PREFIX0_AP3 = 24UL
    };
    
    enum NRF_PREFIX1_POS
    {
      RADIO_PREFIX1_AP4 = 0UL,
      RADIO_PREFIX1_AP5 = 8UL,
      RADIO_PREFIX1_AP6 = 16UL,
      RADIO_PREFIX1_AP7 = 24UL
    };
    
    enum NRF_RADIO_PREFIX_MSK
    {
      RADIO_PREFIX_MSK = 0xFFUL
    };
    
    enum NRF_RADIO_TXADDRESS_MSK
    {
      RADIO_TXADDRESS_MSK = 0x0007UL
    };
    
    typedef enum NRF_RADIO_RXADDRESS_POS
    {
      RADIO_RX_ADDRESS0_POS = 0UL,
      RADIO_RX_ADDRESS1_POS = 1UL,
      RADIO_RX_ADDRESS2_POS = 2UL,
      RADIO_RX_ADDRESS3_POS = 3UL,
      RADIO_RX_ADDRESS4_POS = 4UL,
      RADIO_RX_ADDRESS5_POS = 5UL,
      RADIO_RX_ADDRESS6_POS = 6UL,
      RADIO_RX_ADDRESS7_POS = 7UL
    } nrf_radio_rxaddress_pos_t;
    
    enum NRF_RADIO_CRCCNF_POS
    {
      RADIO_CRCCNF_LEN_POS      = 0UL,
      RADIO_CRCCNF_SKIPADDR_POS = 8UL
    };
    
    enum NRF_RADIO_CRCCNF_MSK
    {
      RADIO_CRCCNF_LEN_MSK = 0x0003UL,
      RADIO_CRCCNF_SKIPADDR_MSK = 0x0003UL
    };
    
    enum NRF_RADIO_CRCPOLY_MSK
    {
      RADIO_CRCPOLY_MSK = 0xFFFFFFUL
    };
    
    typedef enum NRF_RADIO_SHORTS_POS
    {
      RADIO_SHORTS_READY_START_POS        = 0UL,
      RADIO_SHORTS_END_DISABLE_POS        = 1UL,
      RADIO_SHORTS_DISABLED_TXEN_POS      = 2UL,
      RADIO_SHORTS_DISABLED_RXEN_POS      = 3UL,
      RADIO_SHORTS_ADDRESS_RSSISTART_POS  = 4UL,
      RADIO_SHORTS_END_START_POS          = 5UL,
      RADIO_SHORTS_ADDRESS_BCSTART_POS    = 6UL,
      RADIO_SHORTS_DISABLED_RSSISTOP_POS  = 8UL,
      RADIO_SHORTS_RXREADY_CCASTART_POS   = 11UL,
      RADIO_SHORTS_CCAIDLE_TXEN_POS       = 12UL,
      RADIO_SHORTS_CCABUSY_DISABLE_POS    = 13UL,
      RADIO_SHORTS_FRAMESTART_BCSTART_POS = 14UL,
      RADIO_SHORTS_READY_EDSTART_POS      = 15UL,
      RADIO_SHORTS_EDEND_DISABLE_POS      = 16UL,
      RADIO_SHORTS_CCAIDLE_STOP_POS       = 17UL,
      RADIO_SHORTS_TXREADY_START_POS      = 18UL,
      RADIO_SHORTS_RXREADY_START_POS      = 19UL,
      RADIO_SHORTS_PHYEND_DISABLE_POS     = 20UL,
      RADIO_SHORTS_PHYEND_START_POS       = 21UL
    } nrf_radio_shorts_pos_t;
    
    
    typedef enum NRF_RADIO_SHORTS_MSK
    {
      RADIO_SHORTS_READY_START_MSK        = 0x000001UL,
      RADIO_SHORTS_END_DISABLE_MSK        = 0x000002UL,
      RADIO_SHORTS_DISABLED_TXEN_MSK      = 0x000004UL,
      RADIO_SHORTS_DISABLED_RXEN_MSK      = 0x000008UL,
      RADIO_SHORTS_ADDRESS_RSSISTART_MSK  = 0x000010UL,
      RADIO_SHORTS_END_START_MSK          = 0x000020UL,
      RADIO_SHORTS_ADDRESS_BCSTART_MSK    = 0x000040UL,
      RADIO_SHORTS_DISABLED_RSSISTOP_MSK  = 0x000100UL,
      RADIO_SHORTS_RXREADY_CCASTART_MSK   = 0x000800UL,
      RADIO_SHORTS_CCAIDLE_TXEN_MSK       = 0x001000UL,
      RADIO_SHORTS_CCABUSY_DISABLE_MSK    = 0x002000UL,
      RADIO_SHORTS_FRAMESTART_BCSTART_MSK = 0x004000UL,
      RADIO_SHORTS_READY_EDSTART_MSK      = 0x008000UL,
      RADIO_SHORTS_EDEND_DISABLE_MSK      = 0x010000UL,
      RADIO_SHORTS_CCAIDLE_STOP_MSK       = 0x020000UL,
      RADIO_SHORTS_TXREADY_START_MSK      = 0x040000UL,
      RADIO_SHORTS_RXREADY_START_MSK      = 0x080000UL,
      RADIO_SHORTS_PHYEND_DISABLE_MSK     = 0x100000UL,
      RADIO_SHORTS_PHYEND_START_MSK       = 0x200000UL
    } nrf_radio_shorts_msk_t;
    
    typedef enum NRF_RADIO_INTENSET_POS
    {
      RADIO_INTENSET_READY_POS      = 0UL,
      RADIO_INTENSET_ADDRESS_POS    = 1UL,
      RADIO_INTENSET_PAYLOAD_POS    = 2UL,
      RADIO_INTENSET_END_POS        = 3UL,
      RADIO_INTENSET_DISABLED_POS   = 4UL,
      RADIO_INTENSET_DEVMATCH_POS   = 5UL,
      RADIO_INTENSET_DEVMISS_POS    = 6UL,
      RADIO_INTENSET_RSSIEND_POS    = 7UL,
      RADIO_INTENSET_BCMATCH_POS    = 10UL,
      RADIO_INTENSET_CRCOK_POS      = 12UL,
      RADIO_INTENSET_CRCERROR_POS   = 13UL,
      RADIO_INTENSET_FRAMESTART_POS = 14UL,
      RADIO_INTENSET_EDEND_POS      = 15UL,
      RADIO_INTENSET_EDSTOPPED_POS  = 16UL,
      RADIO_INTENSET_CCAIDLE_POS    = 17UL,
      RADIO_INTENSET_CCABUSY_POS    = 18UL,
      RADIO_INTENSET_CCASTOPPED_POS = 19UL,
      RADIO_INTENSET_RATEBOOST_POS  = 20UL,
      RADIO_INTENSET_TXREADY_POS    = 21UL,
      RADIO_INTENSET_RXREADY_POS    = 22UL,
      RADIO_INTENSET_MHRMATCH_POS   = 23UL,
      RADIO_INTENSET_SYNC_POS       = 26UL,
      RADIO_INTENSET_PHYEND_POS     = 27UL
    } nrf_radio_intenset_pos_t;
    
    typedef enum NRF_RADIO_INTENSET_MSK
    {
      RADIO_INTENSET_READY_MSK      = 0x0000001UL,
      RADIO_INTENSET_ADDRESS_MSK    = 0x0000002UL,
      RADIO_INTENSET_PAYLOAD_MSK    = 0x0000004UL,
      RADIO_INTENSET_END_MSK        = 0x0000008UL,
      RADIO_INTENSET_DISABLED_MSK   = 0x0000010UL,
      RADIO_INTENSET_DEVMATCH_MSK   = 0x0000020UL,
      RADIO_INTENSET_DEVMISS_MSK    = 0x0000040UL,
      RADIO_INTENSET_RSSIEND_MSK    = 0x0000080UL,
      RADIO_INTENSET_BCMATCH_MSK    = 0x0000400UL,
      RADIO_INTENSET_CRCOK_MSK      = 0x0001000UL,
      RADIO_INTENSET_CRCERROR_MSK   = 0x0002000UL,
      RADIO_INTENSET_FRAMESTART_MSK = 0x0004000UL,
      RADIO_INTENSET_EDEND_MSK      = 0x0008000UL,
      RADIO_INTENSET_EDSTOPPED_MSK  = 0x0010000UL,
      RADIO_INTENSET_CCAIDLE_MSK    = 0x0020000UL,
      RADIO_INTENSET_CCABUSY_MSK    = 0x0040000UL,
      RADIO_INTENSET_CCASTOPPED_MSK = 0x0080000UL,
      RADIO_INTENSET_RATEBOOST_MSK  = 0x0100000UL,
      RADIO_INTENSET_TXREADY_MSK    = 0x0200000UL,
      RADIO_INTENSET_RXREADY_MSK    = 0x0400000UL,
      RADIO_INTENSET_MHRMATCH_MSK   = 0x0800000UL,
      RADIO_INTENSET_SYNC_MSK       = 0x4000000UL,
      RADIO_INTENSET_PHYEND_MSK     = 0x8000000UL
    } nrf_radio_intenset_msk_t;
    
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ///
    ///   VALUE IDS
    ///
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    
    // Data Rate and Modulation
    enum NRF_RADIO_MODE
    {
      NRF_1MBIT          = 0,  // 1 Mbit/s Nordic proprietary radio mode
      NRF_2MBIT          = 1,  // 2 Mbit/s Nordic proprietary radio mode
      BLE_1MBIT          = 3,  // 1 Mbit/s BLE
      BLE_2MBIT          = 4,  // 2 Mbit/s BLE
      BLE_LR125KBIT      = 5,  // Long range 125 kbit/s TX, 125 kbit/s and 500 kbit/s RX
      BLE_LR500KBIT      = 6,  // Long range 500 kbit/s TX, 125 kbit/s and 500 kbit/s RX
      IEEE802154_250KBIT = 15  // IEEE 802.15.4-2006 250 kbit/s
    };
    
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ///
    ///   HELPER FUNCTIONS
    ///
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    /**
     * Set PCNF0 Register in NRF RADIO Peripheral
     * 
     * @param length_field_len: Length on air of LENGTH field in number of bits.
     * @param s0_field_len: Length on air of S0 field in number of bytes.
     * @param s1_field_len: Length on air of S1 field in number of bits.
     * @param include_s1_field: Include or exclude S1 field in RAM
     * @param code_indicator_len: Length of code indicator - long range
     * @param preamble_len: Length of preamble on air. Decision point: TASKS_START task
     * @param include_crc_in_len: Indicates if LENGTH field contains CRC or not
     * @param term_field_len: Length of TERM field in Long Range operation
     */
    void set_pcnf0(uint32_t length_field_len, 
                   uint32_t s0_field_len,
                   uint32_t s1_field_len,
                   uint32_t include_s1_field,
                   uint32_t code_indicator_len,
                   uint32_t preamble_len,
                   uint32_t include_crc,
                   uint32_t term_field_len)
    {
      length_field_len   &= RADIO_PCNF0_LFLEN_MSK;
      s0_field_len       &= RADIO_PCNF0_S0LEN_MSK;
      s1_field_len       &= RADIO_PCNF0_S1LEN_MSK;
      include_s1_field   &= RADIO_PCNF0_S1INCL_MSK;
      code_indicator_len &= RADIO_PCNF0_CILEN_MSK;
      preamble_len       &= RADIO_PCNF0_PLEN_MSK;
      include_crc        &= RADIO_PCNF0_CRCINC_MSK;
      term_field_len     &= RADIO_PCNF0_TERMLEN_MSK;
      
      NRF_RADIO->PCNF0 = (length_field_len   << RADIO_PCNF0_LFLEN_POS)  |
                         (s0_field_len       << RADIO_PCNF0_S0LEN_POS)  |
                         (s1_field_len       << RADIO_PCNF0_S1LEN_POS)  |
                         (include_s1_field   << RADIO_PCNF0_S1INCL_POS) |
                         (code_indicator_len << RADIO_PCNF0_CILEN_POS)  |
                         (preamble_len       << RADIO_PCNF0_PLEN_POS)   |
                         (include_crc        << RADIO_PCNF0_CRCINC_POS) |
                         (term_field_len     << RADIO_PCNF0_TERMLEN_POS);
    }
    
    /**
     * Set PCNF1 Register in NRF RADIO Peripheral
     * 
     * @param maximum_packet_payload_length: Maximum length of packet payload
     * @param static_length: Static length in number of bytes
     * @param base_address_length: Base address length in number of bytes
     * @param endianness: On air endianness of packet, this applies to the S0, LENGTH, S1 and the PAYLOAD fields.
     * @param packet_whitening_enable: Enable or disable packet whitening
     */
    void set_pcnf1(uint32_t maximum_packet_payload_length,
                   uint32_t static_length,
                   uint32_t base_address_length,
                   uint32_t endianness,
                   uint32_t packet_whitening_enable)
    {
      maximum_packet_payload_length &= RADIO_PCNF1_MAXLEN_MSK;
      static_length                 &= RADIO_PCNF1_STATLEN_MSK;
      base_address_length           &= RADIO_PCNF1_BALEN_MSK;
      endianness                    &= RADIO_PCNF1_ENDIAN_MSK;
      packet_whitening_enable       &= RADIO_PCNF1_WHITEEN_MSK;
    
      NRF_RADIO->PCNF1 = (maximum_packet_payload_length << RADIO_PCNF1_MAXLEN_POS)  |
                         (static_length                 << RADIO_PCNF1_STATLEN_POS) |
                         (base_address_length           << RADIO_PCNF1_BALEN_POS)   |
                         (endianness                    << RADIO_PCNF1_ENDIAN_POS)  |
                         (packet_whitening_enable       << RADIO_PCNF1_WHITEEN_POS);
    }
    
    /**
     * Set the BASE Register
     * 
     * @param base_address: Base address 0
     */
    void set_base0(uint32_t base_address)
    {
      NRF_RADIO->BASE0 = base_address;
    }
    
    /**
     * Set the PREFIX0.AP0 Register
     * 
     * @param prefix0_address: PREFIX0 Address for AP0
     */
    void set_prefix0_ap0(uint32_t prefix0_address)
    {
      prefix0_address &= RADIO_PREFIX_MSK;
      NRF_RADIO->PREFIX0 = prefix0_address << RADIO_PREFIX0_AP0;
    }
    
    /**
     * Set the transmission address
     * 
     * @param tx_address: Transmission Address
     */
    void set_txaddress(uint32_t tx_address)
    {
      tx_address &= RADIO_TXADDRESS_MSK;
      NRF_RADIO->TXADDRESS = tx_address;
    }
    
    /**
     * Enable the reception Address
     * 
     * @param rx_address_pos: RX Address Position in the register
     */
    void enable_rxaddress(nrf_radio_rxaddress_pos_t rx_address_pos)
    {
      NRF_RADIO->RXADDRESSES = 1UL << rx_address_pos;
    }
    
    /**
     * Disable the reception Address
     * 
     * @param rx_address_pos: RX Address Position in the register
     */
    void disable_rxaddress(nrf_radio_rxaddress_pos_t rx_address_pos)
    {
      NRF_RADIO->RXADDRESSES = 0UL << rx_address_pos;
    }
    
    
    /**
     * Configure the CRC
     * 
     * @param crc_length: CRC length in number of bytes.
     * @param crc_skipaddress: Include or exclude packet address field out of CRC calculation.
     */
    void configure_crc(uint32_t crc_length, uint32_t crc_skipaddress)
    {
      crc_length      &= RADIO_CRCCNF_LEN_MSK;
      crc_skipaddress &= RADIO_CRCCNF_SKIPADDR_MSK;
      
      NRF_RADIO->CRCCNF = (crc_length      << RADIO_CRCCNF_LEN_POS) |
                          (crc_skipaddress << RADIO_CRCCNF_SKIPADDR_MSK);
    }
    
    /**
     * Set the CRC Polynomial
     * 
     * @param crc_polynomial: CRC Polynomial
     *                        Each term in the CRC polynomial is mapped to a bit in this register which index 
     *                        corresponds to the term's exponent. The least significant term/bit is hard-wired 
     *                        internally to 1, and bit number 0 of the register content is ignored by the hardware. 
     *                        The following example is for an 8 bit CRC polynomial: x8 + x7 + x3 + x2 + 1 = 1 1000 1101 .
     */
    void set_crc_polynomial(uint32_t crc_polynomial)
    {
      crc_polynomial &= RADIO_CRCPOLY_MSK;
      NRF_RADIO->CRCPOLY = crc_polynomial;
    }
    
    /**
     * Enable a shortcut on the RADIO SHORTS register
     *
     * @param shorts_position: The mask of the shortcuts in the register
     */
    void radio_enable_shorts(uint32_t shorts_msk)
    {
      NRF_RADIO->SHORTS = 1UL & shorts_msk;
    }
    
    /**
     * Disable a shortcut on the RADIO SHORTS register
     */
    void radio_disable_shorts()
    {
      NRF_RADIO->SHORTS = 0UL;
    }
    
    /**
     * Enable a specific interrupts() in the INTENSET RADIO Register
     * 
     * @param interrupt_msk: The mask of the interrupt(s) in the register
     */
    void radio_enable_interrupt(uint32_t interrupt_msk)
    {
      NRF_RADIO->INTENSET = 1UL & interrupt_msk;
    }
    
    /**
     * Disable a specific interrupt in the INTENSET RADIO Register
     */
    void radio_disable_interrupts()
    {
      NRF_RADIO->INTENSET = 0UL;
    }
    
    /**
     * Set the NRF RADIO Peripheral Packet Pointer (Double Buffered)
     */
    void set_radio_packet_ptr(uint32_t packet_ptr)
    {
      NRF_RADIO->PACKETPTR = packet_ptr;
    }
    
    /**
     * Set the radio frequency
     * 
     * @param frequency: X + 2400MHz
     */
    inline void set_radio_frequency(uint32_t frequency)
    {
      assert(frequency <= 100);
    
      NRF_RADIO->FREQUENCY = frequency;
    }
    
    
    /**
     * Begin listening for incoming packets
     */
    void begin_radio_listen()
    {
      NRF_RADIO->TASKS_START = 1UL;
    }
    
    /**
     * Start the High Frequency Clock
     */
    inline void start_hf_clock()
    {
      NRF_CLOCK->TASKS_HFCLKSTART = 1;
      while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0);
      NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
    }
    
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ///
    ///   IRQs
    ///
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    volatile bool RADIO_PACKET_RECEIVED = false;
    
    /**
     * RADIO IRQ Handler: Weak Link to be taken here and triggered during END event in SAADC (Enabled elsewhere)
     */
    extern "C" void RADIO_IRQHandler_v( void ) {
        // Check to see if the RADIO has completed reception of a packet
        if (NRF_RADIO->EVENTS_END == 1UL)
        {
            NRF_RADIO->EVENTS_END = 0; // Clear the "END" event
            
            RADIO_PACKET_RECEIVED = true; // Start transmission
    
            RADIO_RX_BUFFER_INDEX_TO_SEND = RADIO_RX_BUFFER_INDEX;
    
            // This will apply to the next reception sequence (double buffered packet pointer)
            increment_rx_buffer_index();
            set_radio_packet_ptr((uint32_t)(&RADIO_RX_BUFFER[RADIO_RX_BUFFER_INDEX]));
        }
        else
        {
          pinMode(LEDR, OUTPUT);
          digitalWrite(LEDR, LOW);
        }
    }
    
    
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ///
    ///   APPLICATION-SPECIFIC FUNCTIONS
    ///
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    /**
     * Initialize the Blueooth RADIO Peripheral in the desired configuration for Transmission to the Relay
     */
    void init_radio_for_reception()
    { 
      NRF_RADIO->MODE = NRF_2MBIT; // Set to 2Mbps bandwidth
    
      // Set the PCNF0 & PCNF1 registers
      set_pcnf0(8UL, 0UL, 0UL, 0UL, 0UL, 2UL, 0UL, 0UL);
      set_pcnf1(255UL, 0UL, 4UL, 0UL, 1UL);
    
      // Set the BASE and PREFIX registers
      set_base0(0xABCDDBF5);
      set_prefix0_ap0(0xEF);
      enable_rxaddress(RADIO_RX_ADDRESS0_POS);
      set_radio_frequency(0x00); // Set to 2400MHz
    
      // Set the CRCCNF & CRCPOLY Registers
      configure_crc(2UL, 0UL);
    
      // Set the CRC Polynomial - Utilizing this table:
      //   https://users.ece.cmu.edu/~koopman/crc/
      //    - From Carnegie Mellon University
      set_crc_polynomial(0xd175);
    
      start_hf_clock();
    
      // Set the packet
      set_radio_packet_ptr((uint32_t)RADIO_RX_BUFFER);
    
      radio_enable_shorts(RADIO_SHORTS_END_START_POS);
    
      radio_enable_interrupt(RADIO_INTENSET_END_MSK | RADIO_INTENSET_ADDRESS_MSK);
      NVIC_SetPriority( RADIO_IRQn, 1UL );
      NVIC_EnableIRQ( RADIO_IRQn );
      
      NRF_RADIO->TASKS_RXEN = 1UL; // Enable the RADIO in RX Mode
    
      // Wait until the RADIO has begun to move forward with the application
      while(NRF_RADIO->EVENTS_READY == 0UL); 
    
      begin_radio_listen();  
    }

Related