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:
PCNF1 Register:
BASE0 = 0xABCDDBF5
PREFIX0.AP0 = 0xEF
TXADDRESS = 0x00
FREQUENCY = 0x00
CRCCNF:
CRCPOLY = 0xd175
Receiver Initialization:
RXADDRESSES = 0x01
Hi,
My apologies for not seeing this earlier. I believe you are the victim of this errata:
https://infocenter.nordicsemi.com/topic/errata_nRF52832_Rev3/ERR/nRF52832/Rev3/latest/anomaly_832_245.html
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).
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.
My apologies. I meant 0x10000 instead of 0x1. That being said, I’ll set the A bit and see if that begins transmission.
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(); }