Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs

minimum ESB latency

Hi,

I have an application where I need a latency below 100µs between transmission and reception over the air of a 32 bits payload.

We are currently evaluating the ESB protocol, but I thing I'm reaching the limit...

I'm using:

- ISP1507-AL (embedding NRF52810) as a transceiver

- nrf52832 (on nRF52-DK) as a receiver

- The ESB example projects from nFR5 SDK V17.1.0 on Segger Embedded Studio V6.40



I have the following protocol configuration: (ESB_DPL, 2MBPS,  no_ack, 8bits CRC)

    nrf_esb_config_t nrf_esb_config         = NRF_ESB_DEFAULT_CONFIG;
    nrf_esb_config.payload_length           = 4; // 4 bytes
    nrf_esb_config.protocol                 = NRF_ESB_PROTOCOL_ESB_DPL;
    nrf_esb_config.bitrate                  = NRF_ESB_BITRATE_2MBPS;
    nrf_esb_config.crc                      = NRF_ESB_CRC_8BIT; // NRF_ESB_CRC_OFF
    nrf_esb_config.retransmit_delay         = 200;
    nrf_esb_config.retransmit_count         = 2;
    nrf_esb_config.event_handler            = nrf_esb_event_handler;
    nrf_esb_config.mode                     = NRF_ESB_MODE_PTX;
    nrf_esb_config.selective_auto_ack       = true;             // Must be set to true to handle packets witout ACK

I measure:

- 192.48 µs between NRF_RADIO register setup and RADIO_IRQHandler() call for end of transmission on the transmitter side
- 216,54 µs letency between NRF_RADIO register setup on TX side and NRF_ESB_EVENT_RX_RECEIVED on RX side.



If I take the old nRF24L01P specifications where ESB timings are available, I have the following:



Toa = (8 *(1+5+4+1) + 9 ) / 2Mbps = 48.5 µs
Tirq ~=10 µs
Tstdby = 130 µs


As I have no ack and no SPI
Tesb = Toa + Tstdby + Tirq = 188.5 µs which is close to the observed timings.

According to the thread below, the 130µs delay is needed for lecacy reasons on nRF51 and nRF24L series)
Minimize ESB latency on nRF52832


My questions are the followings:

1. Do my assumptions look correct to someone who has experience with ESB ?


2. As I will forever working with nRF52 devices, is there a way to get rid on this 130µs start-up delay ? I found nothing in the driver, but something might be hardware ready hidden somewhere ?


3. If it is not possible, can Nordik recommend any wireless protocol, that would allow me to achieve under 100µs latency for 32bits payload transmission overt the air ?
(even if it implies a full hardware redesign)


Thank you in advance for your inputs Slight smile

Parents
  • Hi 

    First off I would recommend having a look at the nRF Connect SDK rather than the nRF5 SDK if you are starting out today with the nRF52. The nRF5 SDK is in maintenance mode only at this point, and new features are being added to the nRF Connect SDK only. 

    It is true that the current implementation of the ESB library (in both SDK's) uses the 130us startup time. It is lucky timing though as I just recently had another customer with a similar request, whether or not there was any way to speed up the operation of the ESB library. 

    The nRF52 devices introduced a faster startup time option which cuts the 130us delay down to only 40us, and I tried to enable this mode in the existing library to see if it would cause any issues. I only had limited time for testing, but it seems that with only 2 lines of code changed I was able to modify the library to use this faster startup time. 

    I have included the library below. You will need to replace the original esb.c file in your SDK, which should be located in the following folder:
    \ncs\v2.1.2\nrf\subsys\esb

    /*
     * Copyright (c) 2018 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
     */
    #include <errno.h>
    #include <zephyr/irq.h>
    #include <zephyr/sys/byteorder.h>
    #include <nrf.h>
    #include <esb.h>
    #ifdef DPPI_PRESENT
    #include <nrfx_dppi.h>
    #else
    #include <nrfx_ppi.h>
    #endif
    #include <helpers/nrfx_gppi.h>
    #include <stddef.h>
    #include <string.h>
    #include <nrf_erratas.h>
    
    /* Constants */
    
    /* 2 Mb RX wait for acknowledgment time-out value.
     * Smallest reliable value: 160.
     */
    #define RX_ACK_TIMEOUT_US_2MBPS 160
    /* 1 Mb RX wait for acknowledgment time-out value. */
    #define RX_ACK_TIMEOUT_US_1MBPS 300
    /* 250 Kb RX wait for acknowledgment time-out value. */
    #define RX_ACK_TIMEOUT_US_250KBPS 300
    /* 1 Mb RX wait for acknowledgment time-out (combined with BLE). */
    #define RX_ACK_TIMEOUT_US_1MBPS_BLE 300
    
    /* Minimum retransmit time */
    #define RETRANSMIT_DELAY_MIN 435
    
    /* Interrupt flags */
    /* Interrupt mask value for TX success. */
    #define INT_TX_SUCCESS_MSK 0x01
    /* Interrupt mask value for TX failure. */
    #define INT_TX_FAILED_MSK 0x02
    /* Interrupt mask value for RX_DR. */
    #define INT_RX_DATA_RECEIVED_MSK 0x04
    
    /* Mask value to signal updating BASE0 radio address. */
    #define ADDR_UPDATE_MASK_BASE0 (1 << 0)
    /* Mask value to signal updating BASE1 radio address. */
    #define ADDR_UPDATE_MASK_BASE1 (1 << 1)
    /* Mask value to signal updating radio prefixes. */
    #define ADDR_UPDATE_MASK_PREFIX (1 << 2)
    
     /* The maximum value for PID. */
    #define PID_MAX 3
    
    #define BIT_MASK_UINT_8(x) (0xFF >> (8 - (x)))
    
    #define RADIO_SHORTS_COMMON                                                    \
    	(RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_DISABLE_Msk |         \
    	 RADIO_SHORTS_ADDRESS_RSSISTART_Msk |                                  \
    	 RADIO_SHORTS_DISABLED_RSSISTOP_Msk)
    
    #ifdef CONFIG_ESB_SYS_TIMER0
    #define ESB_SYS_TIMER NRF_TIMER0
    #define ESB_SYS_TIMER_IRQn TIMER0_IRQn
    #endif
    #ifdef CONFIG_ESB_SYS_TIMER1
    #define ESB_SYS_TIMER NRF_TIMER1
    #define ESB_SYS_TIMER_IRQn TIMER1_IRQn
    #endif
    #ifdef CONFIG_ESB_SYS_TIMER2
    #define ESB_SYS_TIMER NRF_TIMER2
    #define ESB_SYS_TIMER_IRQn TIMER2_IRQn
    #endif
    #ifdef CONFIG_ESB_SYS_TIMER3
    #define ESB_SYS_TIMER NRF_TIMER3
    #define ESB_SYS_TIMER_IRQn TIMER3_IRQn
    #endif
    #ifdef CONFIG_ESB_SYS_TIMER4
    #define ESB_SYS_TIMER NRF_TIMER4
    #define ESB_SYS_TIMER_IRQn TIMER4_IRQn
    #endif
    
    /* Internal Enhanced ShockBurst module state. */
    enum esb_state {
    	ESB_STATE_IDLE,		/* Idle. */
    	ESB_STATE_PTX_TX,       /* Transmitting without acknowledgment. */
    	ESB_STATE_PTX_TX_ACK,   /* Transmitting with acknowledgment. */
    	ESB_STATE_PTX_RX_ACK,   /* Transmitting with acknowledgment and
    				 * reception of payload with the
    				 * acknowledgment response.
    				 */
    	ESB_STATE_PRX,		/* Receiving packets without ACK. */
    	ESB_STATE_PRX_SEND_ACK, /* Transmitting ACK in RX mode. */
    };
    
    /* Pipe info PID and CRC and acknowledgment payload. */
    struct pipe_info {
    	uint16_t crc;	  /* CRC of the last received packet.
    			   * Used to detect retransmits.
    			   */
    	uint8_t pid;	  /* Packet ID of the last received packet
    			   * Used to detect retransmits.
    			   */
    	bool ack_payload; /* State of the transmission of ACK payloads. */
    };
    
    /* Structure used by the PRX to organize ACK payloads for multiple pipes. */
    struct payload_wrap {
    	/* Pointer to the ACK payload. */
    	struct esb_payload  *p_payload;
    	/* Value used to determine if the current payload pointer is used. */
    	bool in_use;
    	/* Pointer to the next ACK payload queued on the same pipe. */
    	struct payload_wrap *p_next;
    };
    
    /* First-in, first-out queue of payloads to be transmitted. */
    struct payload_tx_fifo {
    	 /* Payload queue */
    	struct esb_payload *payload[CONFIG_ESB_TX_FIFO_SIZE];
    
    	uint32_t back;	/* Back of the queue (last in). */
    	uint32_t front;	/* Front of queue (first out). */
    	uint32_t count;	/* Number of elements in the queue. */
    };
    
    /* First-in, first-out queue of received payloads. */
    struct payload_rx_fifo {
    	 /* Payload queue */
    	struct esb_payload *payload[CONFIG_ESB_RX_FIFO_SIZE];
    
    	uint32_t back;	/* Back of the queue (last in). */
    	uint32_t front;	/* Front of queue (first out). */
    	uint32_t count;	/* Number of elements in the queue. */
    };
    
    /* Enhanced ShockBurst address.
     *
     * Enhanced ShockBurst addresses consist of a base address and a prefix
     * that is unique for each pipe. See @ref esb_addressing in the ESB user
     * guide for more information.
     */
    struct esb_address {
    	uint8_t base_addr_p0[4];	/* Base address for pipe 0, in big endian. */
    	uint8_t base_addr_p1[4];   /* Base address for pipe 1-7, in big endian. */
    	uint8_t pipe_prefixes[8];	/* Address prefix for pipe 0 to 7. */
    	uint8_t num_pipes;		/* Number of pipes available. */
    	uint8_t addr_length;	/* Length of the address plus the prefix. */
    	uint8_t rx_pipes_enabled;	/* Bitfield for enabled pipes. */
    	uint8_t rf_channel;        /* Channel to use (between 0 and 100). */
    };
    
    
    static bool esb_initialized;
    static struct esb_config esb_cfg;
    static volatile enum esb_state esb_state = ESB_STATE_IDLE;
    
    /* Default address configuration for ESB.
     * Roughly equal to the nRF24Lxx defaults, except for the number of pipes,
     * because more pipes are supported.
     */
    __ALIGN(4)
    static struct esb_address esb_addr = {
    	.base_addr_p0 = {0xE7, 0xE7, 0xE7, 0xE7},
    	.base_addr_p1 = {0xC2, 0xC2, 0xC2, 0xC2},
    	.pipe_prefixes = {0xE7, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8},
    	.addr_length = 5,
    	.num_pipes = CONFIG_ESB_PIPE_COUNT,
    	.rf_channel = 2,
    	.rx_pipes_enabled = 0xFF
    };
    
    static esb_event_handler event_handler;
    static struct esb_payload *current_payload;
    
    /* FIFOs and buffers */
    static struct payload_tx_fifo tx_fifo;
    static struct payload_rx_fifo rx_fifo;
    static uint8_t tx_payload_buffer[CONFIG_ESB_MAX_PAYLOAD_LENGTH + 2];
    static uint8_t rx_payload_buffer[CONFIG_ESB_MAX_PAYLOAD_LENGTH + 2];
    
    /* Random access buffer variables for ACK payload handling */
    struct payload_wrap ack_pl_wrap[CONFIG_ESB_TX_FIFO_SIZE];
    struct payload_wrap *ack_pl_wrap_pipe[CONFIG_ESB_PIPE_COUNT];
    
    /* Run time variables */
    static uint8_t pids[CONFIG_ESB_PIPE_COUNT];
    static struct pipe_info rx_pipe_info[CONFIG_ESB_PIPE_COUNT];
    static volatile uint32_t interrupt_flags;
    static volatile uint32_t retransmits_remaining;
    static volatile uint32_t last_tx_attempts;
    static volatile uint32_t wait_for_ack_timeout_us;
    
    static uint32_t radio_shorts_common = RADIO_SHORTS_COMMON;
    
    /* PPI or DPPI instances */
    #ifdef DPPI_PRESENT
    typedef uint8_t ppi_channel_t;
    #else
    typedef nrf_ppi_channel_t ppi_channel_t;
    #endif
    
    static ppi_channel_t ppi_ch_radio_ready_timer_start;
    static ppi_channel_t ppi_ch_radio_address_timer_stop;
    static ppi_channel_t ppi_ch_timer_compare0_radio_disable;
    static ppi_channel_t ppi_ch_timer_compare1_radio_txen;
    
    static uint32_t ppi_all_channels_mask;
    
    /* These function pointers are changed dynamically, depending on protocol
     * configuration and state. Note that they will be 0 initialized.
     */
    static void (*on_radio_disabled)(void);
    static void (*on_radio_end)(void);
    static void (*update_rf_payload_format)(uint32_t payload_length);
    
    /*  The following functions are assigned to the function pointers above. */
    static void on_radio_disabled_tx_noack(void);
    static void on_radio_disabled_tx(void);
    static void on_radio_disabled_tx_wait_for_ack(void);
    static void on_radio_disabled_rx(void);
    static void on_radio_disabled_rx_ack(void);
    
    /*  Function to do bytewise bit-swap on an unsigned 32-bit value */
    static uint32_t bytewise_bit_swap(const uint8_t *input)
    {
    #if __CORTEX_M == (0x04U)
    	uint32_t inp = (*(uint32_t *)input);
    
    	return sys_cpu_to_be32((uint32_t)__RBIT(inp));
    #else
    	uint32_t inp = sys_cpu_to_le32(*(uint32_t *)input);
    
    	inp = (inp & 0xF0F0F0F0) >> 4 | (inp & 0x0F0F0F0F) << 4;
    	inp = (inp & 0xCCCCCCCC) >> 2 | (inp & 0x33333333) << 2;
    	inp = (inp & 0xAAAAAAAA) >> 1 | (inp & 0x55555555) << 1;
    	return inp;
    #endif
    }
    
    /* Convert a base address from nRF24L format to nRF5 format */
    static uint32_t addr_conv(const uint8_t *addr)
    {
    	return __REV(bytewise_bit_swap(addr));
    }
    
    static inline void apply_errata143_workaround(void)
    {
    	/* Workaround for Errata 143
    	 * Check if the most significant bytes of address 0 (including
    	 * prefix) match those of another address. It's recommended to
    	 * use a unique address 0 since this will avoid the 3dBm penalty
    	 * incurred from the workaround.
    	 */
    	uint32_t base_address_mask =
    		esb_addr.addr_length == 5 ? 0xFFFF0000 : 0xFF000000;
    
    	/* Load the two addresses before comparing them to ensure
    	 * defined ordering of volatile accesses.
    	 */
    	uint32_t addr0 = NRF_RADIO->BASE0 & base_address_mask;
    	uint32_t addr1 = NRF_RADIO->BASE1 & base_address_mask;
    
    	if (addr0 == addr1) {
    		uint32_t prefix0 = NRF_RADIO->PREFIX0 & 0x000000FF;
    		uint32_t prefix1 = (NRF_RADIO->PREFIX0 & 0x0000FF00) >> 8;
    		uint32_t prefix2 = (NRF_RADIO->PREFIX0 & 0x00FF0000) >> 16;
    		uint32_t prefix3 = (NRF_RADIO->PREFIX0 & 0xFF000000) >> 24;
    		uint32_t prefix4 = NRF_RADIO->PREFIX1 & 0x000000FF;
    		uint32_t prefix5 = (NRF_RADIO->PREFIX1 & 0x0000FF00) >> 8;
    		uint32_t prefix6 = (NRF_RADIO->PREFIX1 & 0x00FF0000) >> 16;
    		uint32_t prefix7 = (NRF_RADIO->PREFIX1 & 0xFF000000) >> 24;
    
    		if (prefix0 == prefix1 || prefix0 == prefix2 ||
    			prefix0 == prefix3 || prefix0 == prefix4 ||
    			prefix0 == prefix5 || prefix0 == prefix6 ||
    			prefix0 == prefix7) {
    			/* This will cause a 3dBm sensitivity loss,
    			 * avoid using such address combinations if possible.
    			 */
    			*(volatile uint32_t *)0x40001774 =
    				((*(volatile uint32_t *)0x40001774) & 0xfffffffe) | 0x01000000;
    		}
    	}
    }
    
    static void update_rf_payload_format_esb_dpl(uint32_t payload_length)
    {
    #if (CONFIG_ESB_MAX_PAYLOAD_LENGTH <= 32)
    	/* Using 6 bits for length */
    	NRF_RADIO->PCNF0 = (0 << RADIO_PCNF0_S0LEN_Pos) |
    			   (6 << RADIO_PCNF0_LFLEN_Pos) |
    			   (3 << RADIO_PCNF0_S1LEN_Pos);
    #else
    	/* Using 8 bits for length */
    	NRF_RADIO->PCNF0 = (0 << RADIO_PCNF0_S0LEN_Pos) |
    			   (8 << RADIO_PCNF0_LFLEN_Pos) |
    			   (3 << RADIO_PCNF0_S1LEN_Pos);
    #endif
    	NRF_RADIO->PCNF1 =
    		(RADIO_PCNF1_WHITEEN_Disabled << RADIO_PCNF1_WHITEEN_Pos) |
    		(RADIO_PCNF1_ENDIAN_Big << RADIO_PCNF1_ENDIAN_Pos) |
    		((esb_addr.addr_length - 1) << RADIO_PCNF1_BALEN_Pos) |
    		(0 << RADIO_PCNF1_STATLEN_Pos) |
    		(CONFIG_ESB_MAX_PAYLOAD_LENGTH << RADIO_PCNF1_MAXLEN_Pos);
    }
    
    static void update_rf_payload_format_esb(uint32_t payload_length)
    {
    	NRF_RADIO->PCNF0 = (1 << RADIO_PCNF0_S0LEN_Pos) |
    			   (0 << RADIO_PCNF0_LFLEN_Pos) |
    			   (1 << RADIO_PCNF0_S1LEN_Pos);
    
    	NRF_RADIO->PCNF1 =
    		(RADIO_PCNF1_WHITEEN_Disabled << RADIO_PCNF1_WHITEEN_Pos) |
    		(RADIO_PCNF1_ENDIAN_Big << RADIO_PCNF1_ENDIAN_Pos) |
    		((esb_addr.addr_length - 1) << RADIO_PCNF1_BALEN_Pos) |
    		(payload_length << RADIO_PCNF1_STATLEN_Pos) |
    		(payload_length << RADIO_PCNF1_MAXLEN_Pos);
    }
    
    static void update_radio_addresses(uint8_t update_mask)
    {
    	if ((update_mask & ADDR_UPDATE_MASK_BASE0) != 0) {
    		NRF_RADIO->BASE0 = addr_conv(esb_addr.base_addr_p0);
    	}
    
    	if ((update_mask & ADDR_UPDATE_MASK_BASE1) != 0) {
    		NRF_RADIO->BASE1 = addr_conv(esb_addr.base_addr_p1);
    	}
    
    	if ((update_mask & ADDR_UPDATE_MASK_PREFIX) != 0) {
    		NRF_RADIO->PREFIX0 =
    			bytewise_bit_swap(&esb_addr.pipe_prefixes[0]);
    		NRF_RADIO->PREFIX1 =
    			bytewise_bit_swap(&esb_addr.pipe_prefixes[4]);
    	}
    
    	/* Workaround for Errata 143 */
    #if NRF52_ERRATA_143_ENABLE_WORKAROUND
    	if (nrf52_errata_143()) {
    		apply_errata143_workaround();
    	}
    #endif
    }
    
    static void update_radio_tx_power(void)
    {
    	NRF_RADIO->TXPOWER = esb_cfg.tx_output_power
    			     << RADIO_TXPOWER_TXPOWER_Pos;
    }
    
    static bool update_radio_bitrate(void)
    {
    	NRF_RADIO->MODE = esb_cfg.bitrate << RADIO_MODE_MODE_Pos;
    
    	switch (esb_cfg.bitrate) {
    	case ESB_BITRATE_2MBPS:
    #if defined(CONFIG_SOC_SERIES_NRF52X) || defined(CONFIG_SOC_NRF5340_CPUNET)
    	case ESB_BITRATE_2MBPS_BLE:
    #endif
    		wait_for_ack_timeout_us = RX_ACK_TIMEOUT_US_2MBPS;
    		break;
    
    	case ESB_BITRATE_1MBPS:
    		wait_for_ack_timeout_us = RX_ACK_TIMEOUT_US_1MBPS;
    		break;
    
    #ifdef CONFIG_SOC_SERIES_NRF51X
    	case ESB_BITRATE_250KBPS:
    		wait_for_ack_timeout_us = RX_ACK_TIMEOUT_US_250KBPS;
    		break;
    #endif /* CONFIG_SOC_SERIES_NRF51X */
    
    	case ESB_BITRATE_1MBPS_BLE:
    		wait_for_ack_timeout_us = RX_ACK_TIMEOUT_US_1MBPS_BLE;
    		break;
    
    	default:
    		/* Should not be reached */
    		return false;
    	}
    
    	return true;
    }
    
    static bool update_radio_protocol(void)
    {
    	switch (esb_cfg.protocol) {
    	case ESB_PROTOCOL_ESB_DPL:
    		update_rf_payload_format = update_rf_payload_format_esb_dpl;
    		break;
    
    	case ESB_PROTOCOL_ESB:
    		update_rf_payload_format = update_rf_payload_format_esb;
    		break;
    
    	default:
    		/* Should not be reached */
    		return false;
    	}
    	return true;
    }
    
    static bool update_radio_crc(void)
    {
    	switch (esb_cfg.crc) {
    	case ESB_CRC_16BIT:
    		NRF_RADIO->CRCINIT = 0xFFFFUL;  /* Initial value */
    		NRF_RADIO->CRCPOLY = 0x11021UL; /* CRC poly: x^16+x^12^x^5+1 */
    		NRF_RADIO->CRCCNF = ESB_CRC_16BIT << RADIO_CRCCNF_LEN_Pos;
    		break;
    
    	case ESB_CRC_8BIT:
    		NRF_RADIO->CRCINIT = 0xFFUL;  /* Initial value */
    		NRF_RADIO->CRCPOLY = 0x107UL; /* CRC poly: x^8+x^2^x^1+1 */
    		NRF_RADIO->CRCCNF = ESB_CRC_8BIT << RADIO_CRCCNF_LEN_Pos;
    		break;
    
    	case ESB_CRC_OFF:
    		NRF_RADIO->CRCINIT = 0x00UL;
    		NRF_RADIO->CRCPOLY = 0x00UL;
    		NRF_RADIO->CRCCNF = ESB_CRC_OFF << RADIO_CRCCNF_LEN_Pos;
    		break;
    
    	default:
    		return false;
    	}
    
    	return true;
    }
    
    static bool update_radio_parameters(void)
    {
    	bool params_valid = true;
    
    	update_radio_tx_power();
    	params_valid &= update_radio_bitrate();
    	params_valid &= update_radio_protocol();
    	params_valid &= update_radio_crc();
    	update_rf_payload_format(esb_cfg.payload_length);
    	params_valid &=
    	    (esb_cfg.retransmit_delay >= RETRANSMIT_DELAY_MIN);
    
    	return params_valid;
    }
    
    static void reset_fifos(void)
    {
    	tx_fifo.back = 0;
    	tx_fifo.front = 0;
    	tx_fifo.count = 0;
    
    	rx_fifo.back = 0;
    	rx_fifo.front = 0;
    	rx_fifo.count = 0;
    }
    
    static void initialize_fifos(void)
    {
    	static struct esb_payload rx_payload[CONFIG_ESB_RX_FIFO_SIZE];
    	static struct esb_payload tx_payload[CONFIG_ESB_TX_FIFO_SIZE];
    
    	reset_fifos();
    
    	for (size_t i = 0; i < CONFIG_ESB_TX_FIFO_SIZE; i++) {
    		tx_fifo.payload[i] = &tx_payload[i];
    	}
    
    	for (size_t i = 0; i < CONFIG_ESB_RX_FIFO_SIZE; i++) {
    		rx_fifo.payload[i] = &rx_payload[i];
    	}
    
    	for (size_t i = 0; i < CONFIG_ESB_TX_FIFO_SIZE; i++) {
    		ack_pl_wrap[i].p_payload = &tx_payload[i];
    		ack_pl_wrap[i].in_use = false;
    		ack_pl_wrap[i].p_next = 0;
    	}
    
    	for (size_t i = 0; i < CONFIG_ESB_PIPE_COUNT; i++) {
    		ack_pl_wrap_pipe[i] = 0;
    	}
    }
    
    static void tx_fifo_remove_last(void)
    {
    	if (tx_fifo.count == 0) {
    		return;
    	}
    
    	uint32_t key = irq_lock();
    
    	tx_fifo.count--;
    	if (++tx_fifo.front >= CONFIG_ESB_TX_FIFO_SIZE) {
    		tx_fifo.front = 0;
    	}
    
    	irq_unlock(key);
    }
    
    /*  Function to push the content of the rx_buffer to the RX FIFO.
     *
     *  The module will point the register NRF_RADIO->PACKETPTR to a buffer for
     *  receiving packets. After receiving a packet the module will call this
     *  function to copy the received data to the RX FIFO.
     *
     *  @param  pipe Pipe number to set for the packet.
     *  @param  pid  Packet ID.
     *
     *  @retval true   Operation successful.
     *  @retval false  Operation failed.
     */
    static bool rx_fifo_push_rfbuf(uint8_t pipe, uint8_t pid)
    {
    	if (rx_fifo.count >= CONFIG_ESB_RX_FIFO_SIZE) {
    		return false;
    	}
    
    	if (esb_cfg.protocol == ESB_PROTOCOL_ESB_DPL) {
    		if (rx_payload_buffer[0] > CONFIG_ESB_MAX_PAYLOAD_LENGTH) {
    			return false;
    		}
    		rx_fifo.payload[rx_fifo.back]->length = rx_payload_buffer[0];
    	} else if (esb_cfg.mode == ESB_MODE_PTX) {
    		/* Received packet is an acknowledgment */
    		rx_fifo.payload[rx_fifo.back]->length = 0;
    	} else {
    		rx_fifo.payload[rx_fifo.back]->length = esb_cfg.payload_length;
    	}
    
    	memcpy(rx_fifo.payload[rx_fifo.back]->data, &rx_payload_buffer[2],
    	       rx_fifo.payload[rx_fifo.back]->length);
    
    	rx_fifo.payload[rx_fifo.back]->pipe = pipe;
    	rx_fifo.payload[rx_fifo.back]->rssi = NRF_RADIO->RSSISAMPLE;
    	rx_fifo.payload[rx_fifo.back]->pid = pid;
    	rx_fifo.payload[rx_fifo.back]->noack = !(rx_payload_buffer[1] & 0x01);
    
    	if (++rx_fifo.back >= CONFIG_ESB_RX_FIFO_SIZE) {
    		rx_fifo.back = 0;
    	}
    	rx_fifo.count++;
    
    	return true;
    }
    
    static void sys_timer_init(void)
    {
    	/* Configure the system timer with a 1 MHz base frequency */
    	ESB_SYS_TIMER->PRESCALER = 4;
    	ESB_SYS_TIMER->BITMODE = TIMER_BITMODE_BITMODE_16Bit;
    	ESB_SYS_TIMER->SHORTS = TIMER_SHORTS_COMPARE1_CLEAR_Msk |
    				TIMER_SHORTS_COMPARE1_STOP_Msk;
    }
    
    static void ppi_init(void)
    {
    #ifdef DPPI_PRESENT
    	nrfx_dppi_channel_alloc(&ppi_ch_radio_ready_timer_start);
    	nrfx_dppi_channel_alloc(&ppi_ch_radio_address_timer_stop);
    	nrfx_dppi_channel_alloc(&ppi_ch_timer_compare0_radio_disable);
    	nrfx_dppi_channel_alloc(&ppi_ch_timer_compare1_radio_txen);
    
    	NRF_RADIO->PUBLISH_READY          = DPPIC_SUBSCRIBE_CHG_EN_EN_Msk | ppi_ch_radio_ready_timer_start;
    	ESB_SYS_TIMER->SUBSCRIBE_START    = DPPIC_SUBSCRIBE_CHG_EN_EN_Msk | ppi_ch_radio_ready_timer_start;
    	NRF_RADIO->PUBLISH_ADDRESS        = DPPIC_SUBSCRIBE_CHG_EN_EN_Msk | ppi_ch_radio_address_timer_stop;
    	ESB_SYS_TIMER->SUBSCRIBE_SHUTDOWN = DPPIC_SUBSCRIBE_CHG_EN_EN_Msk | ppi_ch_radio_address_timer_stop;
    	ESB_SYS_TIMER->PUBLISH_COMPARE[0] = DPPIC_SUBSCRIBE_CHG_EN_EN_Msk | ppi_ch_timer_compare0_radio_disable;
    	NRF_RADIO->SUBSCRIBE_DISABLE      = DPPIC_SUBSCRIBE_CHG_EN_EN_Msk | ppi_ch_timer_compare0_radio_disable;
    	ESB_SYS_TIMER->PUBLISH_COMPARE[1] = DPPIC_SUBSCRIBE_CHG_EN_EN_Msk | ppi_ch_timer_compare1_radio_txen;
    	NRF_RADIO->SUBSCRIBE_TXEN         = DPPIC_SUBSCRIBE_CHG_EN_EN_Msk | ppi_ch_timer_compare1_radio_txen;
    #else
    	nrfx_ppi_channel_alloc(&ppi_ch_radio_ready_timer_start);
    	nrfx_ppi_channel_alloc(&ppi_ch_radio_address_timer_stop);
    	nrfx_ppi_channel_alloc(&ppi_ch_timer_compare0_radio_disable);
    	nrfx_ppi_channel_alloc(&ppi_ch_timer_compare1_radio_txen);
    
    	nrfx_ppi_channel_assign(ppi_ch_radio_ready_timer_start,
    		(uint32_t)&NRF_RADIO->EVENTS_READY, (uint32_t)&ESB_SYS_TIMER->TASKS_START);
    	nrfx_ppi_channel_assign(ppi_ch_radio_address_timer_stop,
    		(uint32_t)&NRF_RADIO->EVENTS_ADDRESS, (uint32_t)&ESB_SYS_TIMER->TASKS_SHUTDOWN);
    	nrfx_ppi_channel_assign(ppi_ch_timer_compare0_radio_disable,
    		(uint32_t)&ESB_SYS_TIMER->EVENTS_COMPARE[0], (uint32_t)&NRF_RADIO->TASKS_DISABLE);
    	nrfx_ppi_channel_assign(ppi_ch_timer_compare1_radio_txen,
    		(uint32_t)&ESB_SYS_TIMER->EVENTS_COMPARE[1], (uint32_t)&NRF_RADIO->TASKS_TXEN);
    #endif
    	ppi_all_channels_mask = (1 << ppi_ch_radio_ready_timer_start) | (1 << ppi_ch_radio_address_timer_stop) |
    							(1 << ppi_ch_timer_compare0_radio_disable) | (1 << ppi_ch_timer_compare1_radio_txen);
    }
    
    static void start_tx_transaction(void)
    {
    	bool ack;
    
    	last_tx_attempts = 1;
    	/* Prepare the payload */
    	current_payload = tx_fifo.payload[tx_fifo.front];
    
    	switch (esb_cfg.protocol) {
    	case ESB_PROTOCOL_ESB:
    		update_rf_payload_format(current_payload->length);
    		tx_payload_buffer[0] = current_payload->pid;
    		tx_payload_buffer[1] = 0;
    		memcpy(&tx_payload_buffer[2], current_payload->data,
    		       current_payload->length);
    
    		NRF_RADIO->SHORTS = radio_shorts_common |
    				    RADIO_SHORTS_DISABLED_RXEN_Msk;
    		NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk |
    				      RADIO_INTENSET_READY_Msk;
    
    		/* Configure the retransmit counter */
    		retransmits_remaining = esb_cfg.retransmit_count;
    		on_radio_disabled = on_radio_disabled_tx;
    		esb_state = ESB_STATE_PTX_TX_ACK;
    		break;
    
    	case ESB_PROTOCOL_ESB_DPL:
    		ack = !current_payload->noack || !esb_cfg.selective_auto_ack;
    		tx_payload_buffer[0] = current_payload->length;
    		tx_payload_buffer[1] = current_payload->pid << 1;
    		tx_payload_buffer[1] |= current_payload->noack ? 0x00 : 0x01;
    		memcpy(&tx_payload_buffer[2], current_payload->data,
    		       current_payload->length);
    
    		/* Handling ack if noack is set to false or if
    		 * selective auto ack is turned off
    		 */
    		if (ack) {
    			NRF_RADIO->SHORTS = radio_shorts_common |
    					    RADIO_SHORTS_DISABLED_RXEN_Msk;
    			NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk |
    					      RADIO_INTENSET_READY_Msk;
    
    			/* Configure the retransmit counter */
    			retransmits_remaining = esb_cfg.retransmit_count;
    			on_radio_disabled = on_radio_disabled_tx;
    			esb_state = ESB_STATE_PTX_TX_ACK;
    		} else {
    			NRF_RADIO->SHORTS = radio_shorts_common;
    			NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk;
    			on_radio_disabled = on_radio_disabled_tx_noack;
    			esb_state = ESB_STATE_PTX_TX;
    		}
    
    		break;
    
    	default:
    		/* Should not be reached */
    		break;
    	}
    
    	NRF_RADIO->TXADDRESS = current_payload->pipe;
    	NRF_RADIO->RXADDRESSES = 1 << current_payload->pipe;
    	NRF_RADIO->FREQUENCY = esb_addr.rf_channel;
    
    	NRF_RADIO->PACKETPTR = (uint32_t)tx_payload_buffer;
    
    	NVIC_ClearPendingIRQ(RADIO_IRQn);
    	irq_enable(RADIO_IRQn);
    
    	NRF_RADIO->EVENTS_ADDRESS = 0;
    	NRF_RADIO->EVENTS_PAYLOAD = 0;
    	NRF_RADIO->EVENTS_DISABLED = 0;
    
    	NRF_RADIO->TASKS_TXEN = 1;
    }
    
    static void on_radio_disabled_tx_noack(void)
    {
    	interrupt_flags |= INT_TX_SUCCESS_MSK;
    	tx_fifo_remove_last();
    
    	if (tx_fifo.count == 0) {
    		esb_state = ESB_STATE_IDLE;
    		NVIC_SetPendingIRQ(ESB_EVT_IRQ);
    	} else {
    		NVIC_SetPendingIRQ(ESB_EVT_IRQ);
    		start_tx_transaction();
    	}
    }
    
    static void on_radio_disabled_tx(void)
    {
    	/* Remove the DISABLED -> RXEN shortcut, to make sure the radio stays
    	 * disabled after the RX window
    	 */
    	NRF_RADIO->SHORTS = radio_shorts_common;
    
    	/* Make sure the timer is started the next time the radio is ready,
    	 * and that it will disable the radio automatically if no packet is
    	 * received by the time defined in wait_for_ack_timeout_us
    	 */
    	ESB_SYS_TIMER->CC[0] = wait_for_ack_timeout_us;
    	ESB_SYS_TIMER->CC[1] = esb_cfg.retransmit_delay - 40;
    	ESB_SYS_TIMER->TASKS_CLEAR = 1;
    	ESB_SYS_TIMER->EVENTS_COMPARE[0] = 0;
    	ESB_SYS_TIMER->EVENTS_COMPARE[1] = 0;
    
    	/* Remove */
    	ESB_SYS_TIMER->TASKS_START = 1;
    
    	nrfx_gppi_channels_enable(ppi_all_channels_mask);
    	nrfx_gppi_channels_disable(1 << ppi_ch_timer_compare1_radio_txen);
    
    	NRF_RADIO->EVENTS_END = 0;
    
    	if (esb_cfg.protocol == ESB_PROTOCOL_ESB) {
    		update_rf_payload_format(0);
    	}
    
    	NRF_RADIO->PACKETPTR = (uint32_t)rx_payload_buffer;
    	on_radio_disabled = on_radio_disabled_tx_wait_for_ack;
    	esb_state = ESB_STATE_PTX_RX_ACK;
    }
    
    static void on_radio_disabled_tx_wait_for_ack(void)
    {
    	/* This marks the completion of a TX_RX sequence (TX with ACK) */
    
    	/* Make sure the timer will not deactivate the radio if a packet is
    	 * received.
    	 */
    	nrfx_gppi_channels_disable(ppi_all_channels_mask);
    
    	/* If the radio has received a packet and the CRC status is OK */
    	if (NRF_RADIO->EVENTS_END && NRF_RADIO->CRCSTATUS != 0) {
    		ESB_SYS_TIMER->TASKS_SHUTDOWN = 1;
    
    		interrupt_flags |= INT_TX_SUCCESS_MSK;
    		last_tx_attempts = esb_cfg.retransmit_count -
    				   retransmits_remaining + 1;
    
    		tx_fifo_remove_last();
    
    		if (esb_cfg.protocol != ESB_PROTOCOL_ESB &&
    		    rx_payload_buffer[0] > 0) {
    			if (rx_fifo_push_rfbuf((uint8_t)NRF_RADIO->TXADDRESS,
    					       rx_payload_buffer[1] >> 1)) {
    				interrupt_flags |=
    					INT_RX_DATA_RECEIVED_MSK;
    			}
    		}
    
    		if ((tx_fifo.count == 0) ||
    		    (esb_cfg.tx_mode == ESB_TXMODE_MANUAL)) {
    			esb_state = ESB_STATE_IDLE;
    			NVIC_SetPendingIRQ(ESB_EVT_IRQ);
    		} else {
    			NVIC_SetPendingIRQ(ESB_EVT_IRQ);
    			start_tx_transaction();
    		}
    	} else {
    		if (retransmits_remaining-- == 0) {
    			ESB_SYS_TIMER->TASKS_SHUTDOWN = 1;
    
    			/* All retransmits are expended, and the TX operation is
    			 * suspended
    			 */
    			last_tx_attempts = esb_cfg.retransmit_count + 1;
    			interrupt_flags |= INT_TX_FAILED_MSK;
    
    			esb_state = ESB_STATE_IDLE;
    			NVIC_SetPendingIRQ(ESB_EVT_IRQ);
    		} else {
    			/* There are still more retransmits left, TX mode should
    			 * be entered again as soon as the system timer reaches
    			 * CC[1].
    			 */
    			NRF_RADIO->SHORTS = radio_shorts_common |
    					    RADIO_SHORTS_DISABLED_RXEN_Msk;
    			update_rf_payload_format(current_payload->length);
    			NRF_RADIO->PACKETPTR = (uint32_t)tx_payload_buffer;
    			on_radio_disabled = on_radio_disabled_tx;
    			esb_state = ESB_STATE_PTX_TX_ACK;
    			ESB_SYS_TIMER->TASKS_START = 1;
    			nrfx_gppi_channels_enable(1 << ppi_ch_timer_compare1_radio_txen);
    			if (ESB_SYS_TIMER->EVENTS_COMPARE[1]) {
    				NRF_RADIO->TASKS_TXEN = 1;
    			}
    		}
    	}
    }
    
    static void clear_events_restart_rx(void)
    {
    	NRF_RADIO->SHORTS = radio_shorts_common;
    	update_rf_payload_format(esb_cfg.payload_length);
    	NRF_RADIO->PACKETPTR = (uint32_t)rx_payload_buffer;
    	NRF_RADIO->EVENTS_DISABLED = 0;
    	NRF_RADIO->TASKS_DISABLE = 1;
    
    	while (NRF_RADIO->EVENTS_DISABLED == 0) {
    		/* wait for register to settle */
    	}
    
    	NRF_RADIO->EVENTS_DISABLED = 0;
    	NRF_RADIO->SHORTS = radio_shorts_common |
    			    RADIO_SHORTS_DISABLED_TXEN_Msk;
    
    	NRF_RADIO->TASKS_RXEN = 1;
    }
    
    static void on_radio_disabled_rx_dpl(bool retransmit_payload,
    				     struct pipe_info *pipe_info)
    {
    	uint32_t pipe = NRF_RADIO->RXMATCH;
    
    	if (tx_fifo.count > 0 && ack_pl_wrap_pipe[pipe] != 0) {
    		current_payload = ack_pl_wrap_pipe[pipe]->p_payload;
    
    		/* Pipe stays in ACK with payload until TX FIFO is empty */
    		/* Do not report TX success on first ack payload or retransmit */
    		if (pipe_info->ack_payload == true && !retransmit_payload) {
    			ack_pl_wrap_pipe[pipe]->in_use = false;
    			ack_pl_wrap_pipe[pipe] = ack_pl_wrap_pipe[pipe]->p_next;
    			tx_fifo.count--;
    			if (tx_fifo.count > 0 && ack_pl_wrap_pipe[pipe] != 0) {
    				current_payload = ack_pl_wrap_pipe[pipe]->p_payload;
    			} else {
    				current_payload = 0;
    			}
    
    			/* ACK payloads also require TX_DS */
    			/* (page 40 of the 'nRF24LE1_Product_Specification_rev1_6.pdf') */
    			interrupt_flags |= INT_TX_SUCCESS_MSK;
    		}
    
    		if (current_payload != 0) {
    			pipe_info->ack_payload = true;
    			update_rf_payload_format(current_payload->length);
    			tx_payload_buffer[0] = current_payload->length;
    			memcpy(&tx_payload_buffer[2],
    					current_payload->data,
    					current_payload->length);
    		} else {
    			pipe_info->ack_payload = false;
    			update_rf_payload_format(0);
    			tx_payload_buffer[0] = 0;
    		}
    	} else {
    		pipe_info->ack_payload = false;
    		update_rf_payload_format(0);
    		tx_payload_buffer[0] = 0;
    	}
    
    	tx_payload_buffer[1] = rx_payload_buffer[1];
    }
    
    static void on_radio_disabled_rx(void)
    {
    	bool retransmit_payload = false;
    	bool send_rx_event = true;
    	struct pipe_info *pipe_info;
    
    	if (NRF_RADIO->CRCSTATUS == 0) {
    		clear_events_restart_rx();
    		return;
    	}
    
    	if (rx_fifo.count >= CONFIG_ESB_RX_FIFO_SIZE) {
    		clear_events_restart_rx();
    		return;
    	}
    
    	pipe_info = &rx_pipe_info[NRF_RADIO->RXMATCH];
    	if (NRF_RADIO->RXCRC == pipe_info->crc &&
    	    (rx_payload_buffer[1] >> 1) == pipe_info->pid) {
    		retransmit_payload = true;
    		send_rx_event = false;
    	}
    
    	pipe_info->pid = rx_payload_buffer[1] >> 1;
    	pipe_info->crc = NRF_RADIO->RXCRC;
    
    	/* Check if an ack should be sent */
    	if ((esb_cfg.selective_auto_ack == false) ||
    	    ((rx_payload_buffer[1] & 0x01) == 1)) {
    		NRF_RADIO->SHORTS = radio_shorts_common |
    				    RADIO_SHORTS_DISABLED_RXEN_Msk;
    
    		switch (esb_cfg.protocol) {
    		case ESB_PROTOCOL_ESB_DPL:
    			on_radio_disabled_rx_dpl(retransmit_payload, pipe_info);
    			break;
    
    		case ESB_PROTOCOL_ESB:
    			update_rf_payload_format(0);
    			tx_payload_buffer[0] = rx_payload_buffer[0];
    			tx_payload_buffer[1] = 0;
    			break;
    		}
    
    		esb_state = ESB_STATE_PRX_SEND_ACK;
    		NRF_RADIO->TXADDRESS = NRF_RADIO->RXMATCH;
    
    		NRF_RADIO->PACKETPTR = (uint32_t)tx_payload_buffer;
    		on_radio_disabled = on_radio_disabled_rx_ack;
    	} else {
    		clear_events_restart_rx();
    	}
    
    	if (send_rx_event) {
    		/* Push the new packet to the RX buffer and trigger a received
    		 * event if the operation was
    		 * successful.
    		 */
    		if (rx_fifo_push_rfbuf(NRF_RADIO->RXMATCH, pipe_info->pid)) {
    			interrupt_flags |= INT_RX_DATA_RECEIVED_MSK;
    			NVIC_SetPendingIRQ(ESB_EVT_IRQ);
    		}
    	}
    }
    
    static void on_radio_disabled_rx_ack(void)
    {
    	NRF_RADIO->SHORTS = radio_shorts_common |
    			    RADIO_SHORTS_DISABLED_TXEN_Msk;
    	update_rf_payload_format(esb_cfg.payload_length);
    
    	NRF_RADIO->PACKETPTR = (uint32_t)rx_payload_buffer;
    	on_radio_disabled = on_radio_disabled_rx;
    
    	esb_state = ESB_STATE_PRX;
    }
    
    /* Retrieve interrupt flags and reset them.
     *
     * @param[out] interrupts	Interrupt flags.
     */
    static void get_and_clear_irqs(uint32_t *interrupts)
    {
    	__ASSERT_NO_MSG(interrupts != NULL);
    
    	uint32_t key = irq_lock();
    
    	*interrupts = interrupt_flags;
    	interrupt_flags = 0;
    
    	irq_unlock(key);
    }
    
    static void radio_irq_handler(void)
    {
    	if (NRF_RADIO->EVENTS_READY &&
    	    (NRF_RADIO->INTENSET & RADIO_INTENSET_READY_Msk)) {
    		NRF_RADIO->EVENTS_READY = 0;
    		ESB_SYS_TIMER->TASKS_START;
    	}
    
    	if (NRF_RADIO->EVENTS_END &&
    	    (NRF_RADIO->INTENSET & RADIO_INTENSET_END_Msk)) {
    		NRF_RADIO->EVENTS_END = 0;
    		/* Call the correct on_radio_end function, depending on the
    		 * current protocol state.
    		 */
    		if (on_radio_end) {
    			on_radio_end();
    		}
    	}
    
    	if (NRF_RADIO->EVENTS_DISABLED &&
    	    (NRF_RADIO->INTENSET & RADIO_INTENSET_DISABLED_Msk)) {
    		NRF_RADIO->EVENTS_DISABLED = 0;
    		/* Call the correct on_radio_disable function, depending on the
    		 * current protocol state.
    		 */
    		if (on_radio_disabled) {
    			on_radio_disabled();
    		}
    	}
    }
    
    static void esb_evt_irq_handler(void)
    {
    	uint32_t interrupts;
    	struct esb_evt event;
    
    	event.tx_attempts = last_tx_attempts;
    
    	get_and_clear_irqs(&interrupts);
    	if (event_handler != NULL) {
    		if (interrupts & INT_TX_SUCCESS_MSK) {
    			event.evt_id = ESB_EVENT_TX_SUCCESS;
    			event_handler(&event);
    		}
    		if (interrupts & INT_TX_FAILED_MSK) {
    			event.evt_id = ESB_EVENT_TX_FAILED;
    			event_handler(&event);
    		}
    		if (interrupts & INT_RX_DATA_RECEIVED_MSK) {
    			event.evt_id = ESB_EVENT_RX_RECEIVED;
    			event_handler(&event);
    		}
    	}
    }
    
    ISR_DIRECT_DECLARE(RADIO_IRQHandler)
    {
    	radio_irq_handler();
    
    	ISR_DIRECT_PM();
    
    	return 1;
    }
    
    
    ISR_DIRECT_DECLARE(ESB_EVT_IRQHandler)
    {
    	esb_evt_irq_handler();
    
    	ISR_DIRECT_PM();
    
    	return 1;
    }
    
    ISR_DIRECT_DECLARE(ESB_SYS_TIMER_IRQHandler)
    {
    	ISR_DIRECT_PM();
    
    	return 1;
    }
    
    int esb_init(const struct esb_config *config)
    {
    	if (config == NULL) {
    		return -EINVAL;
    	}
    
    	if (esb_initialized) {
    		esb_disable();
    	}
    
    	event_handler = config->event_handler;
    
    	memcpy(&esb_cfg, config, sizeof(esb_cfg));
    
    	interrupt_flags = 0;
    
    	memset(rx_pipe_info, 0, sizeof(rx_pipe_info));
    	memset(pids, 0, sizeof(pids));
    
    	update_radio_parameters();
    
    	/* Configure radio address registers according to ESB default values */
    	NRF_RADIO->BASE0 = 0xE7E7E7E7;
    	NRF_RADIO->BASE1 = 0x43434343;
    	NRF_RADIO->PREFIX0 = 0x23C343E7;
    	NRF_RADIO->PREFIX1 = 0x13E363A3;
    
    	initialize_fifos();
    	sys_timer_init();
    	ppi_init();
    
    	NRF_RADIO->MODECNF0 = (NRF_RADIO->MODECNF0 & ~RADIO_MODECNF0_RU_Pos) | RADIO_MODECNF0_RU_Fast << RADIO_MODECNF0_RU_Pos;
    
    	IRQ_DIRECT_CONNECT(RADIO_IRQn, CONFIG_ESB_RADIO_IRQ_PRIORITY,
    			   RADIO_IRQHandler, 0);
    	IRQ_DIRECT_CONNECT(ESB_EVT_IRQ, CONFIG_ESB_EVENT_IRQ_PRIORITY,
    			   ESB_EVT_IRQHandler, 0);
    	IRQ_DIRECT_CONNECT(ESB_SYS_TIMER_IRQn, CONFIG_ESB_EVENT_IRQ_PRIORITY,
    			   ESB_SYS_TIMER_IRQHandler, 0);
    
    	irq_enable(RADIO_IRQn);
    	irq_enable(ESB_EVT_IRQ);
    	irq_enable(ESB_SYS_TIMER_IRQn);
    
    	esb_state = ESB_STATE_IDLE;
    	esb_initialized = true;
    
    #ifdef CONFIG_SOC_NRF52832
    	if ((NRF_FICR->INFO.VARIANT & 0x0000FF00) == 0x00004500) {
    		/* Check if the device is an nRF52832 Rev. 2. */
    		/* Workaround for nRF52832 rev 2 errata 182 */
    		*(volatile uint32_t *)0x4000173C |= (1 << 10);
    	}
    #endif
    
    	return 0;
    }
    
    int esb_suspend(void)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    
    	/*  Clear PPI */
    	nrfx_gppi_channels_disable(ppi_all_channels_mask);
    
    	esb_state = ESB_STATE_IDLE;
    
    	return 0;
    }
    
    void esb_disable(void)
    {
    	/*  Clear PPI */
    	nrfx_gppi_channels_disable(ppi_all_channels_mask);
    
    	esb_state = ESB_STATE_IDLE;
    	esb_initialized = false;
    
    	reset_fifos();
    
    	memset(rx_pipe_info, 0, sizeof(rx_pipe_info));
    	memset(pids, 0, sizeof(pids));
    
    	/*  Disable the interrupts used by ESB */
    	irq_disable(RADIO_IRQn);
    	irq_disable(ESB_SYS_TIMER_IRQn);
    	irq_disable(ESB_EVT_IRQ);
    
    	NRF_RADIO->SHORTS =
    	    RADIO_SHORTS_READY_START_Enabled << RADIO_SHORTS_READY_START_Pos |
    	    RADIO_SHORTS_END_DISABLE_Enabled << RADIO_SHORTS_END_DISABLE_Pos;
    }
    
    bool esb_is_idle(void)
    {
    	return (esb_state == ESB_STATE_IDLE);
    }
    
    static struct payload_wrap *find_free_payload_cont(void)
    {
    	for (int i = 0; i < CONFIG_ESB_TX_FIFO_SIZE; i++) {
    		if (!ack_pl_wrap[i].in_use)
    			return &ack_pl_wrap[i];
    	}
    	return 0;
    }
    
    int esb_write_payload(const struct esb_payload *payload)
    {
    	if (!esb_initialized) {
    		return -EACCES;
    	}
    	if (payload == NULL) {
    		return -EINVAL;
    	}
    	if (payload->length == 0 ||
    	    payload->length > CONFIG_ESB_MAX_PAYLOAD_LENGTH ||
    	    (esb_cfg.protocol == ESB_PROTOCOL_ESB &&
    	     payload->length > esb_cfg.payload_length)) {
    		return -EMSGSIZE;
    	}
    	if (tx_fifo.count >= CONFIG_ESB_TX_FIFO_SIZE) {
    		return -ENOMEM;
    	}
    	if (payload->pipe >= CONFIG_ESB_PIPE_COUNT) {
    		return -EINVAL;
    	}
    
    	uint32_t key = irq_lock();
    
    	if (esb_cfg.mode == ESB_MODE_PTX) {
    		memcpy(tx_fifo.payload[tx_fifo.back], payload,
    			sizeof(struct esb_payload));
    
    		pids[payload->pipe] = (pids[payload->pipe] + 1) % (PID_MAX + 1);
    		tx_fifo.payload[tx_fifo.back]->pid = pids[payload->pipe];
    
    		if (++tx_fifo.back >= CONFIG_ESB_TX_FIFO_SIZE) {
    			tx_fifo.back = 0;
    		}
    
    		tx_fifo.count++;
    	} else {
    		struct payload_wrap *new_ack_payload = find_free_payload_cont();
    
    		if (new_ack_payload != 0) {
    			new_ack_payload->in_use = true;
    			new_ack_payload->p_next = 0;
    			memcpy(new_ack_payload->p_payload, payload, sizeof(struct esb_payload));
    
    			pids[payload->pipe] = (pids[payload->pipe] + 1) % (PID_MAX + 1);
    			new_ack_payload->p_payload->pid = pids[payload->pipe];
    
    			if (ack_pl_wrap_pipe[payload->pipe] == 0) {
    				ack_pl_wrap_pipe[payload->pipe] = new_ack_payload;
    			} else {
    				struct payload_wrap *pl = ack_pl_wrap_pipe[payload->pipe];
    
    				while (pl->p_next != 0) {
    					pl = (struct payload_wrap *)pl->p_next;
    				}
    				pl->p_next = (struct payload_wrap *)new_ack_payload;
    			}
    			tx_fifo.count++;
    		}
    	}
    
    	irq_unlock(key);
    
    	if (esb_cfg.mode == ESB_MODE_PTX &&
    	    esb_cfg.tx_mode == ESB_TXMODE_AUTO &&
    	    esb_state == ESB_STATE_IDLE) {
    		start_tx_transaction();
    	}
    
    	return 0;
    }
    
    int esb_read_rx_payload(struct esb_payload *payload)
    {
    	if (!esb_initialized) {
    		return -EACCES;
    	}
    	if (payload == NULL) {
    		return -EINVAL;
    	}
    
    	if (rx_fifo.count == 0) {
    		return -ENODATA;
    	}
    
    	uint32_t key = irq_lock();
    
    	payload->length = rx_fifo.payload[rx_fifo.front]->length;
    	payload->pipe = rx_fifo.payload[rx_fifo.front]->pipe;
    	payload->rssi = rx_fifo.payload[rx_fifo.front]->rssi;
    	payload->pid = rx_fifo.payload[rx_fifo.front]->pid;
    	payload->noack = rx_fifo.payload[rx_fifo.front]->noack;
    	memcpy(payload->data, rx_fifo.payload[rx_fifo.front]->data,
    	       payload->length);
    
    	if (++rx_fifo.front >= CONFIG_ESB_RX_FIFO_SIZE) {
    		rx_fifo.front = 0;
    	}
    
    	rx_fifo.count--;
    
    	irq_unlock(key);
    
    	return 0;
    }
    
    int esb_start_tx(void)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    
    	if (tx_fifo.count == 0) {
    		return -ENODATA;
    	}
    
    	start_tx_transaction();
    
    	return 0;
    }
    
    int esb_start_rx(void)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    
    	NRF_RADIO->INTENCLR = 0xFFFFFFFF;
    	NRF_RADIO->EVENTS_DISABLED = 0;
    	on_radio_disabled = on_radio_disabled_rx;
    
    	NRF_RADIO->SHORTS = radio_shorts_common |
    			    RADIO_SHORTS_DISABLED_TXEN_Msk;
    	NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk;
    	esb_state = ESB_STATE_PRX;
    
    	NRF_RADIO->RXADDRESSES = esb_addr.rx_pipes_enabled;
    	NRF_RADIO->FREQUENCY = esb_addr.rf_channel;
    	NRF_RADIO->PACKETPTR = (uint32_t)rx_payload_buffer;
    
    	NVIC_ClearPendingIRQ(RADIO_IRQn);
    	irq_enable(RADIO_IRQn);
    
    	NRF_RADIO->EVENTS_ADDRESS = 0;
    	NRF_RADIO->EVENTS_PAYLOAD = 0;
    	NRF_RADIO->EVENTS_DISABLED = 0;
    
    	NRF_RADIO->TASKS_RXEN = 1;
    
    	return 0;
    }
    
    int esb_stop_rx(void)
    {
    	if (esb_state != ESB_STATE_PRX && esb_state != ESB_STATE_PRX_SEND_ACK) {
    		return -EINVAL;
    	}
    
    	NRF_RADIO->SHORTS = 0;
    	NRF_RADIO->INTENCLR = 0xFFFFFFFF;
    	on_radio_disabled = NULL;
    	NRF_RADIO->EVENTS_DISABLED = 0;
    	NRF_RADIO->TASKS_DISABLE = 1;
    	while (NRF_RADIO->EVENTS_DISABLED == 0) {
    		/* wait for register to settle */
    	}
    
    	esb_state = ESB_STATE_IDLE;
    
    	return 0;
    }
    
    int esb_flush_tx(void)
    {
    	if (!esb_initialized) {
    		return -EACCES;
    	}
    
    	uint32_t key = irq_lock();
    
    	tx_fifo.count = 0;
    	tx_fifo.back = 0;
    	tx_fifo.front = 0;
    
    	irq_unlock(key);
    
    	return 0;
    }
    
    int esb_pop_tx(void)
    {
    	if (!esb_initialized) {
    		return -EACCES;
    	}
    	if (tx_fifo.count == 0) {
    		return -ENODATA;
    	}
    
    	uint32_t key = irq_lock();
    
    	if (++tx_fifo.back >= CONFIG_ESB_TX_FIFO_SIZE) {
    		tx_fifo.back = 0;
    	}
    	tx_fifo.count--;
    
    	irq_unlock(key);
    
    	return 0;
    }
    
    int esb_flush_rx(void)
    {
    	if (!esb_initialized) {
    		return -EACCES;
    	}
    
    	uint32_t key = irq_lock();
    
    	rx_fifo.count = 0;
    	rx_fifo.back = 0;
    	rx_fifo.front = 0;
    
    	memset(rx_pipe_info, 0, sizeof(rx_pipe_info));
    
    	irq_unlock(key);
    
    	return 0;
    }
    
    int esb_set_address_length(uint8_t length)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    	if (!(length > 2 && length < 6)) {
    		return -EINVAL;
    	}
    
    	esb_addr.addr_length = length;
    
    	update_rf_payload_format(esb_cfg.payload_length);
    
    	return 0;
    }
    
    int esb_set_base_address_0(const uint8_t *addr)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    	if (addr == NULL) {
    		return -EINVAL;
    	}
    
    	memcpy(esb_addr.base_addr_p0, addr, sizeof(esb_addr.base_addr_p0));
    
    	update_radio_addresses(ADDR_UPDATE_MASK_BASE0);
    
    	return 0;
    }
    
    int esb_set_base_address_1(const uint8_t *addr)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    	if (addr == NULL) {
    		return -EINVAL;
    	}
    
    	memcpy(esb_addr.base_addr_p1, addr, sizeof(esb_addr.base_addr_p1));
    
    	update_radio_addresses(ADDR_UPDATE_MASK_BASE1);
    
    	return 0;
    }
    
    int esb_set_prefixes(const uint8_t *prefixes, uint8_t num_pipes)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    	if (prefixes == NULL) {
    		return -EINVAL;
    	}
    	if (!(num_pipes <= CONFIG_ESB_PIPE_COUNT)) {
    		return -EINVAL;
    	}
    
    	memcpy(esb_addr.pipe_prefixes, prefixes, num_pipes);
    	esb_addr.num_pipes = num_pipes;
    	esb_addr.rx_pipes_enabled = BIT_MASK_UINT_8(num_pipes);
    
    	update_radio_addresses(ADDR_UPDATE_MASK_PREFIX);
    
    	return 0;
    }
    
    int esb_update_prefix(uint8_t pipe, uint8_t prefix)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    	if (pipe >= CONFIG_ESB_PIPE_COUNT) {
    		return -EINVAL;
    	}
    
    	esb_addr.pipe_prefixes[pipe] = prefix;
    
    	update_radio_addresses(ADDR_UPDATE_MASK_PREFIX);
    
    	return 0;
    }
    
    int esb_enable_pipes(uint8_t enable_mask)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    	if ((enable_mask | BIT_MASK_UINT_8(CONFIG_ESB_PIPE_COUNT)) !=
    	    BIT_MASK_UINT_8(CONFIG_ESB_PIPE_COUNT)) {
    		return -EINVAL;
    	}
    
    	esb_addr.rx_pipes_enabled = enable_mask;
    
    	return 0;
    }
    
    int esb_set_rf_channel(uint32_t channel)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    	if (channel > 100) {
    		return -EINVAL;
    	}
    
    	esb_addr.rf_channel = channel;
    
    	return 0;
    }
    
    int esb_get_rf_channel(uint32_t *channel)
    {
    	if (channel == NULL) {
    		return -EINVAL;
    	}
    
    	*channel = esb_addr.rf_channel;
    
    	return 0;
    }
    
    int esb_set_tx_power(enum esb_tx_power tx_output_power)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    
    	if (esb_cfg.tx_output_power != tx_output_power) {
    		esb_cfg.tx_output_power = tx_output_power;
    		update_radio_tx_power();
    	}
    
    	return 0;
    }
    
    int esb_set_retransmit_delay(uint16_t delay)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    	if (delay < RETRANSMIT_DELAY_MIN) {
    		return -EINVAL;
    	}
    
    	esb_cfg.retransmit_delay = delay;
    
    	return 0;
    }
    
    int esb_set_retransmit_count(uint16_t count)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    
    	esb_cfg.retransmit_count = count;
    
    	return 0;
    }
    
    int esb_set_bitrate(enum esb_bitrate bitrate)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    
    	esb_cfg.bitrate = bitrate;
    
    	return update_radio_bitrate() ? 0 : -EINVAL;
    }
    
    int esb_reuse_pid(uint8_t pipe)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    	if (!(pipe < CONFIG_ESB_PIPE_COUNT)) {
    		return -EINVAL;
    	}
    
    	pids[pipe] = (pids[pipe] + PID_MAX) % (PID_MAX + 1);
    
    	return 0;
    }
    

    Whether or not the total time from uploading the payload to getting the sent interrupt is less than 100us I don't know since I haven't tested this particular use case. I am expecting you should get close, as long as you make sure to disable the ACK feature. 

    Best regards
    Torbjørn

  • Hello Torbjørn,

    Thank you for your answer.


    I will look into moving to nRF Connect SDK.
    Do you know for how long nRF5 SDK will be maintained ?

    By applying your modifications to the driver, i am able to go as low as  ΔT = 129.92 µs



    I only have 1 final question: why is this startup delay needed ?
    From my remains of wireless network classes I would guess it's a delay needed for wireless synchronization between emitter and receiver ?
    Is there any way I could transmit continuously, so that I would never need to re-sync ?

    Thank you for your support !

    Kind regards,
    Laurent

  • Hi Laurent

    lgs-wtk said:
    I only have 1 final question: why is this startup delay needed ?
    From my remains of wireless network classes I would guess it's a delay needed for wireless synchronization between emitter and receiver ?

    It takes some time to start the various analog components of the radio, such as the power amplifier, local oscillators etc. It is not a delay per se, it is simply the time it takes for the radio to go from idle mode to transmit or receive mode (the technical term is TX or RX rampup). 

    Synchronization of the TX and RX happens when the transmitter starts to send the preamble, after the TX rampup is complete. 

    lgs-wtk said:
    Is there any way I could transmit continuously, so that I would never need to re-sync ?

    The radio in the nRF52 does support staying in TX mode continuously, so if you don't care about achieving low current consumption this is an option, yes. 

    Unfortunately this would require considerably more change to the ESB library if you want to support it, since the state machine of the library is based around the flow between idle and TX/RX modes. 

    Best regards
    Torbjørn

  • Dear Torbjørn,

    Thanks a lot for your answer, I will look into modifying the state machine of the driver to transmit continuously.

    Have a nice day !

    Laurent

  • You welcome. The best of luck with your project Slight smile

  • Dear Torbjørn,

    Thanks a lot for your support.
    I have been able to modify the driver emission to transmit continuously. When a new option is enabled in the driver configuration, the radio peripheral do not switch back to DISABLED state in-between transmissions but remains in TXIDLE state.

    You will find below the code of the modified driver, based on nRF Connect v2.2.0.

    I have one remaining issue. Some received packets are missing. It's not a big deal as it is a 0.1% loss, but it happens every 100ms which is odd...

    Is there a heartbeat on the protocol or something like that that would make the receiver change state ?



    /*
     * Copyright (c) 2018 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
     */
    #ifndef __ESB_H
    #define __ESB_H
    
    #include <stdbool.h>
    #include <errno.h>
    
    #include <nrf.h>
    #include <hal/nrf_radio.h>
    
    #include <zephyr/sys/util.h>
    #include <zephyr/types.h>
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    /** @defgroup esb Enhanced ShockBurst
     * @{
     * @ingroup proprietary_api
     *
     * @brief Enhanced ShockBurst (ESB) is a basic protocol that supports two-way
     *        data packet communication including packet buffering, packet
     *        acknowledgment, and automatic retransmission of lost packets.
     */
    
    /** @brief Default radio parameters.
     *
     *  Roughly equal to the nRF24Lxx default parameters except for CRC,
     *  which is set to 16 bit, and protocol, which is set to DPL.
     */
    #define ESB_DEFAULT_CONFIG                                                     \
    	{                                                                      \
    		.protocol = ESB_PROTOCOL_ESB_DPL,                              \
    		.mode = ESB_MODE_PTX,					       \
    		.event_handler = 0,					       \
    		.bitrate = ESB_BITRATE_2MBPS,				       \
    		.crc = ESB_CRC_16BIT,					       \
    		.tx_output_power = ESB_TX_POWER_0DBM,			       \
    		.retransmit_delay = 600,				       \
    		.retransmit_count = 3,					       \
    		.tx_mode = ESB_TXMODE_AUTO,				       \
    		.payload_length = 32,					       \
    		.selective_auto_ack = false,                                    \
    		.never_disable_tx = false,                                    \
    		.optimize_txen_delay = false                                    \
    	}
    
    /** @brief Default legacy radio parameters.
     *
     *  Identical to the nRF24Lxx defaults.
     */
    #define ESB_LEGACY_CONFIG                                                      \
    	{                                                                      \
    		.protocol = ESB_PROTOCOL_ESB,				       \
    		.mode = ESB_MODE_PTX,					       \
    		.event_handler = 0,					       \
    		.bitrate = ESB_BITRATE_2MBPS,				       \
    		.crc = ESB_CRC_8BIT,					       \
    		.tx_output_power = ESB_TX_POWER_0DBM,			       \
    		.retransmit_delay = 600,				       \
    		.retransmit_count = 3,					       \
    		.tx_mode = ESB_TXMODE_AUTO,				       \
    		.payload_length = 32,					       \
    		.selective_auto_ack = false,                                    \
    		.never_disable_tx = false,                                    \
    		.optimize_txen_delay = false                                    \
    	}
    
    /** @brief Macro to create an initializer for a TX data packet.
     *
     *  This macro generates an initializer.
     *
     *  @param[in]   _pipe   The pipe to use for the data packet.
     *  @param[in]   ...     Comma separated list of character data to put in the
     *                       TX buffer. Supported values consist of 1 to 63
     *			 characters.
     *
     *  @return  Initializer that sets up the pipe, length, and byte array for
     *           content of the TX data.
     */
    #define ESB_CREATE_PAYLOAD(_pipe, ...)                                         \
    	{                                                                      \
    		.pipe = _pipe,                                                 \
    		.length = NUM_VA_ARGS_LESS_1(_pipe, __VA_ARGS__),	       \
    		.data = {						       \
    			__VA_ARGS__                                            \
    		}                                                              \
    	}
    
    /** @brief Enhanced ShockBurst protocols. */
    enum esb_protocol {
    	ESB_PROTOCOL_ESB,	/**< Fixed payload length. */
    	ESB_PROTOCOL_ESB_DPL	/**< Dynamic payload length. */
    };
    
    /** @brief Enhanced ShockBurst modes. */
    enum esb_mode {
    	ESB_MODE_PTX,	/**< Primary transmitter mode. */
    	ESB_MODE_PRX	/**< Primary receiver mode.    */
    };
    
    /** @brief Enhanced ShockBurst bitrate modes. */
    enum esb_bitrate {
    	/** 1 Mb radio mode. */
    	ESB_BITRATE_1MBPS = NRF_RADIO_MODE_NRF_1MBIT,
    	/** 2 Mb radio mode. */
    	ESB_BITRATE_2MBPS = NRF_RADIO_MODE_NRF_2MBIT,
    
    #if defined(RADIO_MODE_MODE_Nrf_250Kbit)
    	/** 250 Kb radio mode. */
    	ESB_BITRATE_250KBPS = NRF_RADIO_MODE_NRF_250KBIT,
    #endif /* defined(RADIO_MODE_MODE_Nrf_250Kbit) */
    
    	/** 1 Mb radio mode using @e Bluetooth low energy radio parameters. */
    	ESB_BITRATE_1MBPS_BLE = NRF_RADIO_MODE_BLE_1MBIT,
    
    #if defined(RADIO_MODE_MODE_Ble_2Mbit)
    	/** 2 Mb radio mode using @e Bluetooth low energy radio parameters. */
    	ESB_BITRATE_2MBPS_BLE = NRF_RADIO_MODE_BLE_2MBIT,
    #endif /* defined(RADIO_MODE_MODE_Ble_2Mbit) */
    };
    
    /** @brief Enhanced ShockBurst CRC modes. */
    enum esb_crc {
    	ESB_CRC_16BIT = RADIO_CRCCNF_LEN_Two,	/**< Use two-byte CRC. */
    	ESB_CRC_8BIT = RADIO_CRCCNF_LEN_One,	/**< Use one-byte CRC. */
    	ESB_CRC_OFF = RADIO_CRCCNF_LEN_Disabled /**< Disable CRC. */
    };
    
    /** @brief Enhanced ShockBurst radio transmission power modes. */
    enum esb_tx_power {
    #if defined(RADIO_TXPOWER_TXPOWER_Pos4dBm)
    	/** 4 dBm radio transmit power. */
    	ESB_TX_POWER_4DBM = NRF_RADIO_TXPOWER_POS4DBM,
    #endif /* defined(RADIO_TXPOWER_TXPOWER_Pos4dBm) */
    
    #if defined(RADIO_TXPOWER_TXPOWER_Pos3dBm)
    	/** 3 dBm radio transmit power. */
    	ESB_TX_POWER_3DBM = NRF_RADIO_TXPOWER_POS3DBM,
    #endif /* defined(RADIO_TXPOWER_TXPOWER_Pos3dBm) */
    
    	/** 0 dBm radio transmit power. */
    	ESB_TX_POWER_0DBM = NRF_RADIO_TXPOWER_0DBM,
    	/** -4 dBm radio transmit power. */
    	ESB_TX_POWER_NEG4DBM = NRF_RADIO_TXPOWER_NEG4DBM,
    	/** -8 dBm radio transmit power. */
    	ESB_TX_POWER_NEG8DBM = NRF_RADIO_TXPOWER_NEG8DBM,
    	/** -12 dBm radio transmit power. */
    	ESB_TX_POWER_NEG12DBM = NRF_RADIO_TXPOWER_NEG12DBM,
    	/** -16 dBm radio transmit power. */
    	ESB_TX_POWER_NEG16DBM = NRF_RADIO_TXPOWER_NEG16DBM,
    	/** -20 dBm radio transmit power. */
    	ESB_TX_POWER_NEG20DBM = NRF_RADIO_TXPOWER_NEG20DBM,
    	/** -30 dBm radio transmit power. */
    	ESB_TX_POWER_NEG30DBM = NRF_RADIO_TXPOWER_NEG30DBM,
    	/** -40 dBm radio transmit power. */
    #if defined(RADIO_TXPOWER_TXPOWER_Neg40dBm)
    	ESB_TX_POWER_NEG40DBM = NRF_RADIO_TXPOWER_NEG40DBM
    #endif /* defined(RADIO_TXPOWER_TXPOWER_Neg40dBm) */
    };
    
    /** @brief Enhanced ShockBurst transmission modes. */
    enum esb_tx_mode {
    	/** Automatic TX mode: When the TX FIFO contains packets and the
    	 * radio is idle, packets are sent automatically.
    	 */
    	ESB_TXMODE_AUTO,
    	/** Manual TX mode: Packets are not sent until @ref esb_start_tx
    	 * is called. This mode can be used to ensure consistent packet timing.
    	 */
    	ESB_TXMODE_MANUAL,
    	/** Manual start TX mode: Packets are not sent until
    	 * @ref esb_start_tx is called. Then, transmission continues
    	 * automatically until the TX FIFO is empty.
    	 */
    	ESB_TXMODE_MANUAL_START
    };
    
    /** @brief Enhanced ShockBurst event IDs. */
    enum esb_evt_id {
    	ESB_EVENT_TX_SUCCESS, /**< Event triggered on TX success. */
    	ESB_EVENT_TX_FAILED,  /**< Event triggered on TX failure. */
    	ESB_EVENT_RX_RECEIVED /**< Event triggered on RX received. */
    };
    
    /** @brief Enhanced ShockBurst payload.
     *
     *  The payload is used both for transmissions and for acknowledging a
     *  received packet with a payload.
     */
    struct esb_payload {
    	uint8_t length; /**< Length of the packet when not in DPL mode. */
    	uint8_t pipe;   /**< Pipe used for this payload. */
    	int8_t rssi;   /**< RSSI for the received packet. */
    	uint8_t noack;  /**< Flag indicating that this packet will not be
    		       *  acknowledged. Flag is ignored when selective auto
    		       *  ack is enabled.
    		       */
    	uint8_t pid;    /**< PID assigned during communication. */
    	uint8_t data[CONFIG_ESB_MAX_PAYLOAD_LENGTH]; /**< The payload data. */
    };
    
    /** @brief Enhanced ShockBurst event. */
    struct esb_evt {
    	enum esb_evt_id evt_id;	/**< Enhanced ShockBurst event ID. */
    	uint32_t tx_attempts;	/**< Number of TX retransmission attempts. */
    };
    
    /** @brief Event handler prototype. */
    typedef void (*esb_event_handler)(const struct esb_evt *event);
    
    /** @brief Main configuration structure for the module. */
    struct esb_config {
    	enum esb_protocol protocol;		/**< Protocol. */
    	enum esb_mode mode;			/**< Mode. */
    	esb_event_handler event_handler;	/**< Event handler. */
    	/* General RF parameters */
    	enum esb_bitrate bitrate;		/**< Bitrate mode. */
    	enum esb_crc crc;			/**< CRC mode. */
    	enum esb_tx_power tx_output_power;	/**< Radio TX power. */
    
    	uint16_t retransmit_delay; /**< The delay between each retransmission of
    				  *  unacknowledged packets.
    				  */
    	uint16_t retransmit_count; /**< The number of retransmission attempts
    				  *  before transmission fail.
    				  */
    
    	/* Control settings */
    	enum esb_tx_mode tx_mode;	/**< Transmission mode. */
    
    	uint8_t payload_length; /**< Length of the payload (maximum length depends
    			       *  on the platforms that are used on each side).
    			       */
    	bool selective_auto_ack; /**< Selective auto acknowledgement.
    				   *  When this feature is disabled, all packets
    				   *  will be acknowledged ignoring the noack
    				   *  field.
    				   */
    	bool never_disable_tx; 	/**< When this feature is enabled, 
    					* if mode is NRF_ESB_MODE_PTX and if packet 
    					* is not acknowledged, then radio peripheral will
    					* remain in TXIDLE state instead of TXDISABLE when
    					* transmission is pending. This reduce delay between
    					* consecutive transmissions but consumes more energy. */
    
    	bool optimize_txen_delay; 	/**<  When this feature is enabled, radio TXEN delay is 
    					* cut off from 130µs to 40 µs.
    					* The radio peripheral needs some time to start-up
    					* analogs components of the radio. On nRF51 and nRF24L series, 
    					* a hard coded 130 µs delay is implemented. If ESB connection 
    					* is achieved only between nRF52 devices, this delay can be cut off
    					* to 40 µs. */
    };
    
    /** @brief Initialize the Enhanced ShockBurst module.
     *
     *  @param  config	Parameters for initializing the module.
     *
     *  @return Zero on success or (negative) error code otherwise.
     */
    int esb_init(const struct esb_config *config);
    
    /** @brief Suspend the Enhanced ShockBurst module.
     *
     *  Calling this function stops ongoing communications without changing the
     *  queues.
     *
     * @retval 0 If successful.
     *           Otherwise, a (negative) error code is returned.
     */
    int esb_suspend(void);
    
    /** @brief Disable the Enhanced ShockBurst module.
     *
     *  Calling this function disables the Enhanced ShockBurst module immediately.
     *  Doing so might stop ongoing communications.
     *
     *  @note All queues are flushed by this function.
     *
     */
    void esb_disable(void);
    
    /** @brief Check if the Enhanced ShockBurst module is idle.
     *
     *  @return True if the module is idle, false otherwise.
     */
    bool esb_is_idle(void);
    
    /** @brief Write a payload for transmission or acknowledgement.
     *
     *  This function writes a payload that is added to the queue. When the module
     *  is in PTX mode, the payload is queued for a regular transmission. When the
     *  module is in PRX mode, the payload is queued for when a packet is received
     *  that requires an acknowledgement with payload.
     *
     *  @param[in]   payload     The payload.
     *
     * @retval 0 If successful.
     *           Otherwise, a (negative) error code is returned.
     */
    int esb_write_payload(const struct esb_payload *payload);
    
    /** @brief Read a payload.
     *
     *  @param[in,out] payload	The payload to be received.
     *
     * @retval 0 If successful.
     *           Otherwise, a (negative) error code is returned.
     */
    int esb_read_rx_payload(struct esb_payload *payload);
    
    /** @brief Start transmitting data.
     *
     * @retval 0 If successful.
     *           Otherwise, a (negative) error code is returned.
     */
    int esb_start_tx(void);
    
    /** @brief Start receiving data.
     *
     * @retval 0 If successful.
     *           Otherwise, a (negative) error code is returned.
     */
    int esb_start_rx(void);
    
    /** @brief Stop data reception.
     *
     * @retval 0 If successful.
     *           Otherwise, a (negative) error code is returned.
     */
    int esb_stop_rx(void);
    
    /** @brief Flush the TX buffer.
     *
     * This function clears the TX FIFO buffer.
     *
     * @retval 0 If successful.
     *           Otherwise, a (negative) error code is returned.
     */
    int esb_flush_tx(void);
    
    /** @brief Pop the first item from the TX buffer.
     *
     * @retval 0 If successful.
     *           Otherwise, a (negative) error code is returned.
     */
    int esb_pop_tx(void);
    
    /** @brief This function will check if space is remaining in Tx FIFO.
     *
     * @retval  True                            If the Tx FIFO is full
     *   		False                           If space left in Tx FIFO
     */
    bool esb_tx_full(void);
    
    /** @brief Flush the RX buffer.
     *
     * @retval 0 If successful.
     *           Otherwise, a (negative) error code is returned.
     */
    int esb_flush_rx(void);
    
    /** @brief Set the length of the address.
     *
     *  @param[in] length	Length of the ESB address (in bytes).
     *
     * @retval 0 If successful.
     *           Otherwise, a (negative) error code is returned.
     */
    int esb_set_address_length(uint8_t length);
    
    /** @brief Set the base address for pipe 0.
     *
     * @param[in] addr	Address.
     *
     * @retval 0 If successful.
     *           Otherwise, a (negative) error code is returned.
     */
    int esb_set_base_address_0(const uint8_t *addr);
    
    /** @brief Set the base address for pipe 1 to pipe 7.
     *
     *  @param[in] addr	Address.
     *
     * @retval 0 If successful.
     *           Otherwise, a (negative) error code is returned.
     */
    int esb_set_base_address_1(const uint8_t *addr);
    
    /** @brief Set the number of pipes and the pipe prefix addresses.
     *
     *  This function configures the number of available pipes, enables the pipes,
     *  and sets their prefix addresses.
     *
     *  @param[in] prefixes		Prefixes for each pipe.
     *  @param[in] num_pipes	Number of pipes. Must be less than or equal to
     *				@kconfig{CONFIG_ESB_PIPE_COUNT}.
     *
     * @retval 0 If successful.
     *           Otherwise, a (negative) error code is returned.
     */
    int esb_set_prefixes(const uint8_t *prefixes, uint8_t num_pipes);
    
    /** @brief Enable select pipes.
     *
     *  The @p enable_mask parameter must contain the same number of pipes as has
     *  been configured with @ref esb_set_prefixes. This number may not be
     *  greater than the number defined by @kconfig{CONFIG_ESB_PIPE_COUNT}
     *
     *  @param enable_mask	Bitfield mask to enable or disable pipes.
     *			Setting a bit to 0 disables the pipe.
     *			Setting a bit to 1 enables the pipe.
     *
     * @retval 0 If successful.
     *           Otherwise, a (negative) error code is returned.
     */
    int esb_enable_pipes(uint8_t enable_mask);
    
    /** @brief Update pipe prefix.
     *
     *  @param pipe		Pipe for which to set the prefix.
     *  @param prefix	Prefix to set for the pipe.
     *
     * @retval 0 If successful.
     *           Otherwise, a (negative) error code is returned.
     */
    int esb_update_prefix(uint8_t pipe, uint8_t prefix);
    
    /** @brief Set the channel to use for the radio.
     *
     *  The module must be in an idle state to call this function. As a PTX, the
     *  application must wait for an idle state and as a PRX, the application must
     *  stop RX before changing the channel. After changing the channel, operation
     *  can be resumed.
     *
     *  @param[in] channel	Channel to use for radio.
     *
     * @retval 0 If successful.
     *           Otherwise, a (negative) error code is returned.
     */
    int esb_set_rf_channel(uint32_t channel);
    
    /** @brief Get the current radio channel.
     *
     *  @param[in, out] channel	Channel number.
     *
     * @retval 0 If successful.
     *           Otherwise, a (negative) error code is returned.
     */
    int esb_get_rf_channel(uint32_t *channel);
    
    /** @brief Set the radio output power.
     *
     *  @param[in] tx_output_power	Output power.
     *
     * @retval 0 If successful.
     *           Otherwise, a (negative) error code is returned.
     */
    int esb_set_tx_power(enum esb_tx_power tx_output_power);
    
    /** @brief Set the packet retransmit delay.
     *
     *  @param[in] delay	Delay between retransmissions.
     *
     * @retval 0 If successful.
     *           Otherwise, a (negative) error code is returned.
     */
    int esb_set_retransmit_delay(uint16_t delay);
    
    /** @brief Set the number of retransmission attempts.
     *
     *  @param[in] count	Number of retransmissions.
     *
     * @retval 0 If successful.
     *           Otherwise, a (negative) error code is returned.
     */
    int esb_set_retransmit_count(uint16_t count);
    
    /** @brief Set the radio bitrate.
     *
     * @param[in] bitrate	Radio bitrate.
     *
     * @retval 0 If successful.
     *           Otherwise, a (negative) error code is returned.
     */
    int esb_set_bitrate(enum esb_bitrate bitrate);
    
    /** @brief Reuse a packet ID for a specific pipe.
     *
     *  The ESB protocol uses a 2-bit sequence number (packet ID) to identify
     *  retransmitted packets. By default, the packet ID is incremented for every
     *  uploaded packet. Use this function to prevent this and send two different
     *  packets with the same packet ID.
     *
     *  @param[in] pipe	Pipe.
     *
     * @retval 0 If successful.
     *           Otherwise, a (negative) error code is returned.
     */
    int esb_reuse_pid(uint8_t pipe);
    
    /** @} */
    
    #ifdef __cplusplus
    }
    #endif
    
    #endif /* ESB */
    



    /*
     * Copyright (c) 2018 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
     */
    #include <errno.h>
    #include <nrf.h>
    #include <esb.h>
    #include <stddef.h>
    #include <string.h>
    #include <nrf_erratas.h>
    
    #include <hal/nrf_radio.h>
    #include <hal/nrf_timer.h>
    #include <helpers/nrfx_gppi.h>
    #include <nrfx_timer.h>
    
    #include <zephyr/irq.h>
    #include <zephyr/kernel.h>
    #include <zephyr/sys/byteorder.h>
    #include <zephyr/drivers/gpio.h>
    
    /* Constants */
    
    /* 2 Mb RX wait for acknowledgment time-out value.
     * Smallest reliable value: 160.
     */
    #define RX_ACK_TIMEOUT_US_2MBPS 160
    /* 1 Mb RX wait for acknowledgment time-out value. */
    #define RX_ACK_TIMEOUT_US_1MBPS 300
    /* 250 Kb RX wait for acknowledgment time-out value. */
    #define RX_ACK_TIMEOUT_US_250KBPS 300
    /* 1 Mb RX wait for acknowledgment time-out (combined with BLE). */
    #define RX_ACK_TIMEOUT_US_1MBPS_BLE 300
    
    /* Minimum retransmit time */
    #define RETRANSMIT_DELAY_MIN 435
    
    /* Transmission time */
    #define TRANSMIT_DELAY_US 84
    
    /* Interrupt flags */
    /* Interrupt mask value for TX success. */
    #define INT_TX_SUCCESS_MSK BIT(0)
    /* Interrupt mask value for TX failure. */
    #define INT_TX_FAILED_MSK BIT(1)
    /* Interrupt mask value for RX_DR. */
    #define INT_RX_DATA_RECEIVED_MSK BIT(2)
    
    /* Mask value to signal updating BASE0 radio address. */
    #define ADDR_UPDATE_MASK_BASE0  BIT(0)
    /* Mask value to signal updating BASE1 radio address. */
    #define ADDR_UPDATE_MASK_BASE1  BIT(1)
    /* Mask value to signal updating radio prefixes. */
    #define ADDR_UPDATE_MASK_PREFIX BIT(2)
    
     /* The maximum value for PID. */
    #define PID_MAX 3
    
    #define BIT_MASK_UINT_8(x) (0xFF >> (8 - (x)))
    
    #define RADIO_BASE_FREQUENCY 2400UL
    
    #define RADIO_SHORTS_COMMON                                                              \
    	(NRF_RADIO_SHORT_READY_START_MASK | NRF_RADIO_SHORT_END_DISABLE_MASK |           \
    	NRF_RADIO_SHORT_ADDRESS_RSSISTART_MASK | NRF_RADIO_SHORT_DISABLED_RSSISTOP_MASK)
    
    #ifdef DEBUG_ESB_TX
    extern const struct gpio_dt_spec debug_output;
    #endif 
    
    /* Internal Enhanced ShockBurst module state. */
    enum esb_state {
    	ESB_STATE_IDLE,		/* Idle. */
    	ESB_STATE_PTX_TX,       /* Transmitting without acknowledgment. */
    	ESB_STATE_PTX_TX_ACK,   /* Transmitting with acknowledgment. */
    	ESB_STATE_PTX_RX_ACK,   /* Transmitting with acknowledgment and
    				 * reception of payload with the
    				 * acknowledgment response.
    				 */
    	ESB_STATE_PRX,		/* Receiving packets without ACK. */
    	ESB_STATE_PRX_SEND_ACK, /* Transmitting ACK in RX mode. */
    	ESB_STATE_PTX_TXIDLE,  /* Transmitter stage is idle but enabled */
    };
    
    /* Pipe info PID and CRC and acknowledgment payload. */
    struct pipe_info {
    	uint16_t crc;	  /* CRC of the last received packet.
    			   * Used to detect retransmits.
    			   */
    	uint8_t pid;	  /* Packet ID of the last received packet
    			   * Used to detect retransmits.
    			   */
    	bool ack_payload; /* State of the transmission of ACK payloads. */
    };
    
    /* Structure used by the PRX to organize ACK payloads for multiple pipes. */
    struct payload_wrap {
    	/* Pointer to the ACK payload. */
    	struct esb_payload  *p_payload;
    	/* Value used to determine if the current payload pointer is used. */
    	bool in_use;
    	/* Pointer to the next ACK payload queued on the same pipe. */
    	struct payload_wrap *p_next;
    };
    
    /* First-in, first-out queue of payloads to be transmitted. */
    struct payload_tx_fifo {
    	 /* Payload queue */
    	struct esb_payload *payload[CONFIG_ESB_TX_FIFO_SIZE];
    
    	uint32_t back;	/* Back of the queue (last in). */
    	uint32_t front;	/* Front of queue (first out). */
    	uint32_t count;	/* Number of elements in the queue. */
    };
    
    /* First-in, first-out queue of received payloads. */
    struct payload_rx_fifo {
    	 /* Payload queue */
    	struct esb_payload *payload[CONFIG_ESB_RX_FIFO_SIZE];
    
    	uint32_t back;	/* Back of the queue (last in). */
    	uint32_t front;	/* Front of queue (first out). */
    	uint32_t count;	/* Number of elements in the queue. */
    };
    
    /* Fixed radio PDU header definition. */
    struct esb_radio_fixed_pdu {
    	/* Packet ID of the last received packet. Used to detect retransmits. */
    	uint8_t pid:2;
    	uint8_t rfu:6;
    	uint8_t rfu1;
    } __packed;
    
    /* Dynamic length radio PDU header definition. */
    struct esb_radio_dynamic_pdu {
    	/* Payload length. */
    	uint8_t length:6;
    	uint8_t rfu0:2;
    
    	/* Disable acknowledge. */
    	uint8_t no_ack:1;
    
    	/* Packet ID of the last received packet. Used to detect retransmits. */
    	uint8_t pid:2;
    	uint8_t rfu1:5;
    } __packed;
    
    /* Radio PDU header definition. */
    union esb_radio_pdu_type {
    	/* Fixed PDU header. */
    	struct esb_radio_fixed_pdu fixed_pdu;
    
    	/* Dynamic PDU header. */
    	struct esb_radio_dynamic_pdu dpl_pdu;
    } __packed;
    
    /* Radio PDU definition. */
    struct esb_radio_pdu {
    	/* PDU header. */
    	union esb_radio_pdu_type type;
    
    	/* PDU data. */
    	uint8_t data[];
    } __packed;
    
    /* Enhanced ShockBurst address.
     *
     * Enhanced ShockBurst addresses consist of a base address and a prefix
     * that is unique for each pipe. See @ref esb_addressing in the ESB user
     * guide for more information.
     */
    struct esb_address {
    	uint8_t base_addr_p0[4];	/* Base address for pipe 0, in big endian. */
    	uint8_t base_addr_p1[4];   /* Base address for pipe 1-7, in big endian. */
    	uint8_t pipe_prefixes[8];	/* Address prefix for pipe 0 to 7. */
    	uint8_t num_pipes;		/* Number of pipes available. */
    	uint8_t addr_length;	/* Length of the address plus the prefix. */
    	uint8_t rx_pipes_enabled;	/* Bitfield for enabled pipes. */
    	uint8_t rf_channel;        /* Channel to use (between 0 and 100). */
    };
    
    /** The ESB event IRQ number when running on an nRF5 device. */
    #define ESB_EVT_IRQ        SWI0_IRQn
    /** The handler for @ref ESB_EVT_IRQ when running on an nRF5 device. */
    #define ESB_EVT_IRQHandler SWI0_IRQHandler
    
    #define ESB_TIMER_IRQ          NRFX_CONCAT_3(TIMER, CONFIG_ESB_SYS_TIMER_INSTANCE, _IRQn)
    #define ESB_TIMER_IRQ_HANDLER  NRFX_CONCAT_3(nrfx_timer_,		    \
    					     CONFIG_ESB_SYS_TIMER_INSTANCE, \
    					     _irq_handler)
    
    static nrfx_timer_t esb_timer = NRFX_TIMER_INSTANCE(CONFIG_ESB_SYS_TIMER_INSTANCE);
    
    static bool esb_initialized;
    static struct esb_config esb_cfg;
    static volatile enum esb_state esb_state = ESB_STATE_IDLE;
    
    /* Default address configuration for ESB.
     * Roughly equal to the nRF24Lxx defaults, except for the number of pipes,
     * because more pipes are supported.
     */
    __ALIGN(4)
    static struct esb_address esb_addr = {
    	.base_addr_p0 = {0xE7, 0xE7, 0xE7, 0xE7},
    	.base_addr_p1 = {0xC2, 0xC2, 0xC2, 0xC2},
    	.pipe_prefixes = {0xE7, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8},
    	.addr_length = 5,
    	.num_pipes = CONFIG_ESB_PIPE_COUNT,
    	.rf_channel = 2,
    	.rx_pipes_enabled = 0xFF
    };
    
    static esb_event_handler event_handler;
    static struct esb_payload *current_payload;
    
    /* FIFOs and buffers */
    static struct payload_tx_fifo tx_fifo;
    static struct payload_rx_fifo rx_fifo;
    
    static uint8_t tx_payload_buffer[CONFIG_ESB_MAX_PAYLOAD_LENGTH +
    				 sizeof(struct esb_radio_pdu)];
    static uint8_t rx_payload_buffer[CONFIG_ESB_MAX_PAYLOAD_LENGTH +
    				 sizeof(struct esb_radio_pdu)];
    
    /* Random access buffer variables for ACK payload handling */
    struct payload_wrap ack_pl_wrap[CONFIG_ESB_TX_FIFO_SIZE];
    struct payload_wrap *ack_pl_wrap_pipe[CONFIG_ESB_PIPE_COUNT];
    
    /* Run time variables */
    static uint8_t pids[CONFIG_ESB_PIPE_COUNT];
    static struct pipe_info rx_pipe_info[CONFIG_ESB_PIPE_COUNT];
    static volatile uint32_t interrupt_flags;
    static volatile uint32_t retransmits_remaining;
    static volatile uint32_t last_tx_attempts;
    static volatile uint32_t wait_for_ack_timeout_us;
    
    static uint32_t radio_shorts_common = RADIO_SHORTS_COMMON;
    
    static uint8_t ppi_ch_radio_address_timer_stop;
    static uint8_t ppi_ch_timer_compare0_radio_disable;
    static uint8_t ppi_ch_timer_compare1_radio_txen;
    static uint8_t ppi_ch_radio_end_timer_start;
    
    static uint32_t ppi_all_channels_mask;
    
    /* These function pointers are changed dynamically, depending on protocol
     * configuration and state. Note that they will be 0 initialized.
     */
    static void (*on_radio_disabled)(void);
    static void (*on_timer_compare1)(void);
    static void (*update_rf_payload_format)(uint32_t payload_length);
    
    /*  The following functions are assigned to the function pointers above. */
    static void on_radio_disabled_tx_noack(void);
    static void on_radio_disabled_tx(void);
    static void on_radio_disabled_tx_wait_for_ack(void);
    static void on_radio_disabled_rx(void);
    static void on_radio_disabled_rx_ack(void);
    static void on_radio_end_tx_noack(void);
    
    /*  Function to do bytewise bit-swap on an unsigned 32-bit value */
    static uint32_t bytewise_bit_swap(const uint8_t *input)
    {
    #if __CORTEX_M == (0x04U)
    	uint32_t inp = (*(uint32_t *)input);
    
    	return sys_cpu_to_be32((uint32_t)__RBIT(inp));
    #else
    	uint32_t inp = sys_cpu_to_le32(*(uint32_t *)input);
    
    	inp = (inp & 0xF0F0F0F0) >> 4 | (inp & 0x0F0F0F0F) << 4;
    	inp = (inp & 0xCCCCCCCC) >> 2 | (inp & 0x33333333) << 2;
    	inp = (inp & 0xAAAAAAAA) >> 1 | (inp & 0x55555555) << 1;
    	return inp;
    #endif
    }
    
    /* Convert a base address from nRF24L format to nRF5 format */
    static uint32_t addr_conv(const uint8_t *addr)
    {
    	return __REV(bytewise_bit_swap(addr));
    }
    
    static inline void apply_errata143_workaround(void)
    {
    	/* Workaround for Errata 143
    	 * Check if the most significant bytes of address 0 (including
    	 * prefix) match those of another address. It's recommended to
    	 * use a unique address 0 since this will avoid the 3dBm penalty
    	 * incurred from the workaround.
    	 */
    	uint32_t base_address_mask =
    		esb_addr.addr_length == 5 ? 0xFFFF0000 : 0xFF000000;
    
    	/* Load the two addresses before comparing them to ensure
    	 * defined ordering of volatile accesses.
    	 */
    	uint32_t addr0 = nrf_radio_base0_get(NRF_RADIO) & base_address_mask;
    	uint32_t addr1 = nrf_radio_base1_get(NRF_RADIO) & base_address_mask;
    
    	if (addr0 == addr1) {
    		uint32_t radio_prefix0 = nrf_radio_prefix0_get(NRF_RADIO);
    		uint32_t radio_prefix1 = nrf_radio_prefix1_get(NRF_RADIO);
    
    		uint8_t prefix0 = radio_prefix0 & RADIO_PREFIX0_AP0_Msk;
    		uint8_t prefix1 = (radio_prefix0 & RADIO_PREFIX0_AP1_Msk) >> RADIO_PREFIX0_AP1_Pos;
    		uint8_t prefix2 = (radio_prefix0 & RADIO_PREFIX0_AP2_Msk) >> RADIO_PREFIX0_AP2_Pos;
    		uint8_t prefix3 = (radio_prefix0 & RADIO_PREFIX0_AP3_Msk) >> RADIO_PREFIX0_AP3_Pos;
    		uint8_t prefix4 = radio_prefix1 & RADIO_PREFIX1_AP4_Msk;
    		uint8_t prefix5 = (radio_prefix1 & RADIO_PREFIX1_AP5_Msk) >> RADIO_PREFIX1_AP5_Pos;
    		uint8_t prefix6 = (radio_prefix1 & RADIO_PREFIX1_AP6_Msk) >> RADIO_PREFIX1_AP6_Pos;
    		uint8_t prefix7 = (radio_prefix1 & RADIO_PREFIX1_AP7_Msk) >> RADIO_PREFIX1_AP7_Pos;
    
    		if ((prefix0 == prefix1) || (prefix0 == prefix2) ||
    		    (prefix0 == prefix3) || (prefix0 == prefix4) ||
    		    (prefix0 == prefix5) || (prefix0 == prefix6) ||
    		    (prefix0 == prefix7)) {
    			/* This will cause a 3dBm sensitivity loss,
    			 * avoid using such address combinations if possible.
    			 */
    			*(volatile uint32_t *)0x40001774 =
    				((*(volatile uint32_t *)0x40001774) & 0xfffffffe) | 0x01000000;
    		}
    	}
    }
    
    static void update_rf_payload_format_esb_dpl(uint32_t payload_length)
    {
    	nrf_radio_packet_conf_t packet_config = { 0 };
    
    	packet_config.s0len = 0;
    	packet_config.s1len = 3;
    
    	/* Using 6 bits or 8 bits for length */
    	packet_config.lflen = (CONFIG_ESB_MAX_PAYLOAD_LENGTH <= 32) ? 6 : 8;
    	packet_config.whiteen = false;
    	packet_config.big_endian = true;
    	packet_config.balen = (esb_addr.addr_length - 1);
    	packet_config.statlen = 0;
    	packet_config.maxlen = CONFIG_ESB_MAX_PAYLOAD_LENGTH;
    
    	nrf_radio_packet_configure(NRF_RADIO, &packet_config);
    }
    
    static void update_rf_payload_format_esb(uint32_t payload_length)
    {
    	const nrf_radio_packet_conf_t packet_config = {
    		.s0len = 1,
    		.lflen = 0,
    		.s1len = 1,
    		.whiteen = false,
    		.big_endian = true,
    		.balen = (esb_addr.addr_length - 1),
    		.statlen = payload_length,
    		.maxlen = payload_length
    	};
    
    	nrf_radio_packet_configure(NRF_RADIO, &packet_config);
    }
    
    static void update_radio_addresses(uint8_t update_mask)
    {
    	if ((update_mask & ADDR_UPDATE_MASK_BASE0) != 0) {
    		nrf_radio_base0_set(NRF_RADIO, addr_conv(esb_addr.base_addr_p0));
    	}
    
    	if ((update_mask & ADDR_UPDATE_MASK_BASE1) != 0) {
    		nrf_radio_base1_set(NRF_RADIO, addr_conv(esb_addr.base_addr_p1));
    	}
    
    	if ((update_mask & ADDR_UPDATE_MASK_PREFIX) != 0) {
    		nrf_radio_prefix0_set(NRF_RADIO, bytewise_bit_swap(&esb_addr.pipe_prefixes[0]));
    		nrf_radio_prefix1_set(NRF_RADIO, bytewise_bit_swap(&esb_addr.pipe_prefixes[4]));
    	}
    
    	/* Workaround for Errata 143 */
    #if NRF52_ERRATA_143_ENABLE_WORKAROUND
    	if (nrf52_errata_143()) {
    		apply_errata143_workaround();
    	}
    #endif
    }
    
    static void update_radio_tx_power(void)
    {
    	nrf_radio_txpower_set(NRF_RADIO, esb_cfg.tx_output_power);
    }
    
    static bool update_radio_bitrate(void)
    {
    	nrf_radio_mode_set(NRF_RADIO, esb_cfg.bitrate);
    
    	switch (esb_cfg.bitrate) {
    	case ESB_BITRATE_2MBPS:
    
    #if defined(RADIO_MODE_MODE_Ble_2Mbit)
    	case ESB_BITRATE_2MBPS_BLE:
    #endif /* defined(RADIO_MODE_MODE_Ble_2Mbit) */
    
    		wait_for_ack_timeout_us = RX_ACK_TIMEOUT_US_2MBPS;
    		break;
    
    	case ESB_BITRATE_1MBPS:
    		wait_for_ack_timeout_us = RX_ACK_TIMEOUT_US_1MBPS;
    		break;
    
    #if defined(RADIO_MODE_MODE_Nrf_250Kbit)
    	case ESB_BITRATE_250KBPS:
    		wait_for_ack_timeout_us = RX_ACK_TIMEOUT_US_250KBPS;
    		break;
    #endif /* defined(RADIO_MODE_MODE_Nrf_250Kbit) */
    
    	case ESB_BITRATE_1MBPS_BLE:
    		wait_for_ack_timeout_us = RX_ACK_TIMEOUT_US_1MBPS_BLE;
    		break;
    
    	default:
    		/* Should not be reached */
    		return false;
    	}
    
    	return true;
    }
    
    static bool update_radio_protocol(void)
    {
    	switch (esb_cfg.protocol) {
    	case ESB_PROTOCOL_ESB_DPL:
    		update_rf_payload_format = update_rf_payload_format_esb_dpl;
    		break;
    
    	case ESB_PROTOCOL_ESB:
    		update_rf_payload_format = update_rf_payload_format_esb;
    		break;
    
    	default:
    		/* Should not be reached */
    		return false;
    	}
    	return true;
    }
    
    static bool update_radio_crc(void)
    {
    	switch (esb_cfg.crc) {
    	case ESB_CRC_16BIT:
    		nrf_radio_crcinit_set(NRF_RADIO, 0xFFFFUL); /* Initial value */
    		nrf_radio_crc_configure(NRF_RADIO, ESB_CRC_16BIT, NRF_RADIO_CRC_ADDR_INCLUDE,
    					0x11021UL); /* CRC poly: x^16+x^12^x^5+1 */
    		break;
    
    	case ESB_CRC_8BIT:
    		nrf_radio_crcinit_set(NRF_RADIO, 0xFFUL); /* Initial value */
    		nrf_radio_crc_configure(NRF_RADIO, ESB_CRC_8BIT, NRF_RADIO_CRC_ADDR_INCLUDE,
    					0x107UL); /* CRC poly: x^8+x^2^x^1+1 */
    		break;
    
    	case ESB_CRC_OFF:
    		nrf_radio_crcinit_set(NRF_RADIO, 0x00UL);
    		nrf_radio_crc_configure(NRF_RADIO, ESB_CRC_OFF, NRF_RADIO_CRC_ADDR_INCLUDE, 0x00UL);
    
    		break;
    
    	default:
    		return false;
    	}
    
    	return true;
    }
    
    static bool update_radio_parameters(void)
    {
    	bool params_valid = true;
    
    	update_radio_tx_power();
    	params_valid &= update_radio_bitrate();
    	params_valid &= update_radio_protocol();
    	params_valid &= update_radio_crc();
    	update_rf_payload_format(esb_cfg.payload_length);
    	params_valid &=
    	    (esb_cfg.retransmit_delay >= RETRANSMIT_DELAY_MIN);
    
    	return params_valid;
    }
    
    static void reset_fifos(void)
    {
    	tx_fifo.back = 0;
    	tx_fifo.front = 0;
    	tx_fifo.count = 0;
    
    	rx_fifo.back = 0;
    	rx_fifo.front = 0;
    	rx_fifo.count = 0;
    }
    
    static void initialize_fifos(void)
    {
    	static struct esb_payload rx_payload[CONFIG_ESB_RX_FIFO_SIZE];
    	static struct esb_payload tx_payload[CONFIG_ESB_TX_FIFO_SIZE];
    
    	reset_fifos();
    
    	for (size_t i = 0; i < CONFIG_ESB_TX_FIFO_SIZE; i++) {
    		tx_fifo.payload[i] = &tx_payload[i];
    	}
    
    	for (size_t i = 0; i < CONFIG_ESB_RX_FIFO_SIZE; i++) {
    		rx_fifo.payload[i] = &rx_payload[i];
    	}
    
    	for (size_t i = 0; i < CONFIG_ESB_TX_FIFO_SIZE; i++) {
    		ack_pl_wrap[i].p_payload = &tx_payload[i];
    		ack_pl_wrap[i].in_use = false;
    		ack_pl_wrap[i].p_next = 0;
    	}
    
    	for (size_t i = 0; i < CONFIG_ESB_PIPE_COUNT; i++) {
    		ack_pl_wrap_pipe[i] = 0;
    	}
    }
    
    static void tx_fifo_remove_last(void)
    {
    	if (tx_fifo.count == 0) {
    		return;
    	}
    
    	unsigned int key = irq_lock();
    
    	tx_fifo.count--;
    	if (++tx_fifo.front >= CONFIG_ESB_TX_FIFO_SIZE) {
    		tx_fifo.front = 0;
    	}
    
    	irq_unlock(key);
    }
    
    /*  Function to push the content of the rx_buffer to the RX FIFO.
     *
     *  The module will point the register NRF_RADIO->PACKETPTR to a buffer for
     *  receiving packets. After receiving a packet the module will call this
     *  function to copy the received data to the RX FIFO.
     *
     *  @param  pipe Pipe number to set for the packet.
     *  @param  pid  Packet ID.
     *
     *  @retval true   Operation successful.
     *  @retval false  Operation failed.
     */
    static bool rx_fifo_push_rfbuf(uint8_t pipe, uint8_t pid)
    {
    	struct esb_radio_pdu *rx_pdu = (struct esb_radio_pdu *)rx_payload_buffer;
    
    	if (rx_fifo.count >= CONFIG_ESB_RX_FIFO_SIZE) {
    		return false;
    	}
    
    	if (esb_cfg.protocol == ESB_PROTOCOL_ESB_DPL) {
    		if (rx_pdu->type.dpl_pdu.length > CONFIG_ESB_MAX_PAYLOAD_LENGTH) {
    			return false;
    		}
    
    		rx_fifo.payload[rx_fifo.back]->length = rx_pdu->type.dpl_pdu.length;
    	} else if (esb_cfg.mode == ESB_MODE_PTX) {
    		/* Received packet is an acknowledgment */
    		rx_fifo.payload[rx_fifo.back]->length = 0;
    	} else {
    		rx_fifo.payload[rx_fifo.back]->length = esb_cfg.payload_length;
    	}
    
    	memcpy(rx_fifo.payload[rx_fifo.back]->data, rx_pdu->data,
    	       rx_fifo.payload[rx_fifo.back]->length);
    
    	rx_fifo.payload[rx_fifo.back]->pipe = pipe;
    	rx_fifo.payload[rx_fifo.back]->rssi = nrf_radio_rssi_sample_get(NRF_RADIO);
    	rx_fifo.payload[rx_fifo.back]->pid = pid;
    	rx_fifo.payload[rx_fifo.back]->noack = !rx_pdu->type.dpl_pdu.no_ack;
    
    	if (++rx_fifo.back >= CONFIG_ESB_RX_FIFO_SIZE) {
    		rx_fifo.back = 0;
    	}
    	rx_fifo.count++;
    
    	return true;
    }
    
    static void esb_timer_handler(nrf_timer_event_t event_type, void *context)
    {
    	nrf_timer_event_clear(esb_timer.p_reg, NRF_TIMER_EVENT_COMPARE1);	
    	if(NULL != on_timer_compare1) {
    		on_timer_compare1();
    	}
    }
    
    static int sys_timer_init(void)
    {
    	nrfx_err_t nrfx_err;
    	const nrfx_timer_config_t config = {
    		.frequency = NRF_TIMER_FREQ_1MHz,
    		.mode = NRF_TIMER_MODE_TIMER,
    		.bit_width = NRF_TIMER_BIT_WIDTH_16,
    	};
    
    	nrfx_err = nrfx_timer_init(&esb_timer, &config, esb_timer_handler);
    	if (nrfx_err != NRFX_SUCCESS) {
    		return -EFAULT;
    	}
    
    	nrf_timer_shorts_set(esb_timer.p_reg,
    		(NRF_TIMER_SHORT_COMPARE1_STOP_MASK | NRF_TIMER_SHORT_COMPARE1_CLEAR_MASK));
    
    	return 0;
    }
    
    static void sys_timer_deinit(void)
    {
    	nrfx_timer_uninit(&esb_timer);
    }
    
    static int ppi_init(void)
    {
    	nrfx_err_t nrfx_err;
    
    	nrfx_err = nrfx_gppi_channel_alloc(&ppi_ch_radio_address_timer_stop);
    	if (nrfx_err != NRFX_SUCCESS) {
    		goto error;
    	}
    
    	nrfx_err = nrfx_gppi_channel_alloc(&ppi_ch_timer_compare0_radio_disable);
    	if (nrfx_err != NRFX_SUCCESS) {
    		goto error;
    	}
    
    	nrfx_err = nrfx_gppi_channel_alloc(&ppi_ch_timer_compare1_radio_txen);
    	if (nrfx_err != NRFX_SUCCESS) {
    		goto error;
    	}
    
    	nrfx_err = nrfx_gppi_channel_alloc(&ppi_ch_radio_end_timer_start);
    	if (nrfx_err != NRFX_SUCCESS) {
    		goto error;
    	}
    
    	nrfx_gppi_channel_endpoints_setup(ppi_ch_radio_address_timer_stop,
    			nrf_radio_event_address_get(NRF_RADIO, NRF_RADIO_EVENT_ADDRESS),
    			nrfx_timer_task_address_get(&esb_timer, NRF_TIMER_TASK_SHUTDOWN));
    
    	nrfx_gppi_channel_endpoints_setup(ppi_ch_timer_compare0_radio_disable,
    			nrfx_timer_event_address_get(&esb_timer, NRF_TIMER_EVENT_COMPARE0),
    			nrf_radio_task_address_get(NRF_RADIO, NRF_RADIO_TASK_DISABLE));
    
    	nrfx_gppi_channel_endpoints_setup(ppi_ch_timer_compare1_radio_txen,
    			nrfx_timer_event_address_get(&esb_timer, NRF_TIMER_EVENT_COMPARE1),
    			nrf_radio_task_address_get(NRF_RADIO, NRF_RADIO_TASK_TXEN));
    
    	nrfx_gppi_channel_endpoints_setup(ppi_ch_radio_end_timer_start,
    			nrf_radio_event_address_get(NRF_RADIO, NRF_RADIO_EVENT_END),
    			nrfx_timer_task_address_get(&esb_timer, NRF_TIMER_TASK_START));
    
    
    	ppi_all_channels_mask = (BIT(ppi_ch_radio_address_timer_stop) |
    				 BIT(ppi_ch_timer_compare0_radio_disable) |
    				 BIT(ppi_ch_timer_compare1_radio_txen));
    
    	return 0;
    
    error:
    	return -EFAULT;
    }
    
    static void ppi_deinit(void)
    {
    	nrfx_gppi_channels_disable(ppi_all_channels_mask);
    
    	nrfx_gppi_event_endpoint_clear(ppi_ch_radio_address_timer_stop,
    			nrf_radio_event_address_get(NRF_RADIO, NRF_RADIO_EVENT_ADDRESS));
    	nrfx_gppi_event_endpoint_clear(ppi_ch_timer_compare0_radio_disable,
    			nrfx_timer_event_address_get(&esb_timer, NRF_TIMER_EVENT_COMPARE0));
    	nrfx_gppi_event_endpoint_clear(ppi_ch_timer_compare1_radio_txen,
    			nrfx_timer_event_address_get(&esb_timer, NRF_TIMER_EVENT_COMPARE1));
    	nrfx_gppi_event_endpoint_clear(ppi_ch_radio_end_timer_start,
    			nrf_radio_event_address_get(NRF_RADIO, NRF_RADIO_EVENT_END));
    
    	nrfx_gppi_task_endpoint_clear(ppi_ch_radio_address_timer_stop,
    			nrfx_timer_task_address_get(&esb_timer, NRF_TIMER_TASK_SHUTDOWN));
    	nrfx_gppi_event_endpoint_clear(ppi_ch_timer_compare0_radio_disable,
    			nrf_radio_task_address_get(NRF_RADIO, NRF_RADIO_TASK_DISABLE));
    	nrfx_gppi_event_endpoint_clear(ppi_ch_timer_compare1_radio_txen,
    			nrf_radio_task_address_get(NRF_RADIO, NRF_RADIO_TASK_TXEN));
    	nrfx_gppi_task_endpoint_clear(ppi_ch_radio_end_timer_start,
    			nrfx_timer_task_address_get(&esb_timer, NRF_TIMER_TASK_START));
    
    	nrfx_gppi_channel_free(ppi_ch_radio_address_timer_stop);
    	nrfx_gppi_channel_free(ppi_ch_timer_compare0_radio_disable);
    	nrfx_gppi_channel_free(ppi_ch_timer_compare1_radio_txen);
    	nrfx_gppi_channel_free(ppi_ch_radio_end_timer_start);
    }
    
    static void start_tx_transaction(void)
    {
    	bool ack;
        bool isTxIdle = false;
    	struct esb_radio_pdu *pdu = (struct esb_radio_pdu *)tx_payload_buffer;
    	last_tx_attempts = 1;
    	/* Prepare the payload */
    	current_payload = tx_fifo.payload[tx_fifo.front];
    
    	switch (esb_cfg.protocol) {
    	case ESB_PROTOCOL_ESB:
    		memset(&pdu->type.fixed_pdu, 0, sizeof(pdu->type.fixed_pdu));
    		update_rf_payload_format(current_payload->length);
    
    		pdu->type.fixed_pdu.pid = current_payload->pid;
    
    		memcpy(pdu->data, current_payload->data, current_payload->length);
    
    		nrf_radio_shorts_set(NRF_RADIO,
    				     (radio_shorts_common | NRF_RADIO_SHORT_DISABLED_RXEN_MASK));
    		nrf_radio_int_enable(NRF_RADIO, NRF_RADIO_INT_DISABLED_MASK);
    
    		/* Configure the retransmit counter */
    		retransmits_remaining = esb_cfg.retransmit_count;
    		on_radio_disabled = on_radio_disabled_tx;
    		esb_state = ESB_STATE_PTX_TX_ACK;
    		break;
    
    	case ESB_PROTOCOL_ESB_DPL:
    		memset(&pdu->type.dpl_pdu, 0, sizeof(pdu->type.dpl_pdu));
    		ack = !current_payload->noack || !esb_cfg.selective_auto_ack;
    
    		pdu->type.dpl_pdu.length = current_payload->length;
    		pdu->type.dpl_pdu.pid = current_payload->pid;
    		pdu->type.dpl_pdu.no_ack = current_payload->noack ? 0x00 : 0x01;
    
    		memcpy(pdu->data, current_payload->data, current_payload->length);
    
    		/* Handling ack if noack is set to false or if
    		 * selective auto ack is turned off
    		 */
    		if (ack) {
    			nrf_radio_shorts_set(NRF_RADIO,
    				(radio_shorts_common | NRF_RADIO_SHORT_DISABLED_RXEN_MASK));
    
    			/* Configure the retransmit counter */
    			retransmits_remaining = esb_cfg.retransmit_count;
    			on_radio_disabled = on_radio_disabled_tx;
    			esb_state = ESB_STATE_PTX_TX_ACK;
    			nrf_radio_int_enable(NRF_RADIO, NRF_RADIO_INT_DISABLED_MASK);
    		} else if(esb_cfg.never_disable_tx) {
    			nrf_radio_shorts_set(NRF_RADIO, radio_shorts_common & ~RADIO_SHORTS_END_DISABLE_Msk);
    
    			// Configure timer to produce an ISR after retransmit_delay
    			nrfx_timer_clear(&esb_timer);
    			nrfx_timer_compare(&esb_timer, NRF_TIMER_CC_CHANNEL1, esb_cfg.retransmit_delay - TRANSMIT_DELAY_US, true);
    
    			// Configure PPI to start the timer when transmission ends
    			nrfx_gppi_channels_disable(ppi_all_channels_mask);
    			nrfx_gppi_channels_enable(BIT(ppi_ch_radio_end_timer_start));
    
    			on_timer_compare1 = on_radio_end_tx_noack;
    			on_radio_disabled = NULL;
                isTxIdle = (esb_state == ESB_STATE_PTX_TXIDLE || esb_state == ESB_STATE_PTX_TX) ? true : false;
    			esb_state = ESB_STATE_PTX_TX;
    		} else {
    			nrf_radio_shorts_set(NRF_RADIO, radio_shorts_common);
    
    			on_radio_disabled = on_radio_disabled_tx_noack;
    			esb_state = ESB_STATE_PTX_TX;
    			nrf_radio_int_enable(NRF_RADIO, NRF_RADIO_INT_DISABLED_MASK);
    		}
    
    		break;
    
    	default:
    		/* Should not be reached */
    		break;
    	}
    
    	nrf_radio_txaddress_set(NRF_RADIO, current_payload->pipe);
    	nrf_radio_rxaddresses_set(NRF_RADIO, BIT(current_payload->pipe));
    	nrf_radio_frequency_set(NRF_RADIO, (RADIO_BASE_FREQUENCY + esb_addr.rf_channel));
    
    	nrf_radio_packetptr_set(NRF_RADIO, pdu);
    
    	NVIC_ClearPendingIRQ(RADIO_IRQn);
    	irq_enable(RADIO_IRQn);
    
    	nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_ADDRESS);
    	nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_PAYLOAD);
    	nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_DISABLED);
    	nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_END);
    
    #ifdef DEBUG_ESB_TX
    	gpio_pin_set_dt(&debug_output, 1);
    #endif
    	// Trigger different radio event if radio is disabled or idle
    	if (isTxIdle) {
        	nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_START);
    	}
        else {
    		nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_TXEN);
    	}
    #ifdef DEBUG_ESB_TX
    	gpio_pin_set_dt(&debug_output, 0);
    #endif
    
    }
    
    static void on_radio_end_tx_noack(void)
    {
    	interrupt_flags |= INT_TX_SUCCESS_MSK;
    	tx_fifo_remove_last();
    
    	if (tx_fifo.count == 0) {
    		esb_state = ESB_STATE_PTX_TXIDLE;
    		NVIC_SetPendingIRQ(ESB_EVT_IRQ);
    	} else {
    		NVIC_SetPendingIRQ(ESB_EVT_IRQ);
    		start_tx_transaction();
    	}
    }
    
    static void on_radio_disabled_tx_noack(void)
    {
    	interrupt_flags |= INT_TX_SUCCESS_MSK;
    	tx_fifo_remove_last();
    
    	if (tx_fifo.count == 0) {
    		esb_state = ESB_STATE_IDLE;
    		NVIC_SetPendingIRQ(ESB_EVT_IRQ);
    	} else {
    		NVIC_SetPendingIRQ(ESB_EVT_IRQ);
    		start_tx_transaction();
    	}
    }
    
    static void on_radio_disabled_tx(void)
    {
    	/* Remove the DISABLED -> RXEN shortcut, to make sure the radio stays
    	 * disabled after the RX window
    	 */
    	nrf_radio_shorts_set(NRF_RADIO, radio_shorts_common);
    
    	/* Make sure the timer is started the next time the radio is ready,
    	 * and that it will disable the radio automatically if no packet is
    	 * received by the time defined in wait_for_ack_timeout_us
    	 */
    
    	nrfx_timer_compare(&esb_timer, NRF_TIMER_CC_CHANNEL0, wait_for_ack_timeout_us, false);
    	if(esb_cfg.optimize_txen_delay) {
    		nrfx_timer_compare(&esb_timer, NRF_TIMER_CC_CHANNEL1, (esb_cfg.retransmit_delay - 40),
    			   false);
    	} else {
    		nrfx_timer_compare(&esb_timer, NRF_TIMER_CC_CHANNEL1, (esb_cfg.retransmit_delay - 130),
    			   false);		
    	}
    
    
    	nrfx_timer_clear(&esb_timer);
    
    	nrf_timer_event_clear(esb_timer.p_reg, NRF_TIMER_EVENT_COMPARE0);
    	nrf_timer_event_clear(esb_timer.p_reg, NRF_TIMER_EVENT_COMPARE1);
    
    	nrf_timer_task_trigger(esb_timer.p_reg, NRF_TIMER_TASK_START);
    
    	nrfx_gppi_channels_enable(ppi_all_channels_mask);
    	nrfx_gppi_channels_disable(BIT(ppi_ch_timer_compare1_radio_txen));
    
    	nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_END);
    
    	if (esb_cfg.protocol == ESB_PROTOCOL_ESB) {
    		update_rf_payload_format(0);
    	}
    
    	nrf_radio_packetptr_set(NRF_RADIO, rx_payload_buffer);
    	on_radio_disabled = on_radio_disabled_tx_wait_for_ack;
    	esb_state = ESB_STATE_PTX_RX_ACK;
    }
    
    static void on_radio_disabled_tx_wait_for_ack(void)
    {
    	struct esb_radio_pdu *rx_pdu = (struct esb_radio_pdu *)rx_payload_buffer;
    	/* This marks the completion of a TX_RX sequence (TX with ACK) */
    
    	/* Make sure the timer will not deactivate the radio if a packet is
    	 * received.
    	 */
    	nrfx_gppi_channels_disable(ppi_all_channels_mask);
    
    	/* If the radio has received a packet and the CRC status is OK */
    	if (nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_END) &&
    	    nrf_radio_crc_status_check(NRF_RADIO)) {
    		interrupt_flags |= INT_TX_SUCCESS_MSK;
    		last_tx_attempts = esb_cfg.retransmit_count - retransmits_remaining + 1;
    
    		tx_fifo_remove_last();
    
    		if ((esb_cfg.protocol != ESB_PROTOCOL_ESB) && (rx_pdu->type.dpl_pdu.length > 0)) {
    			if (rx_fifo_push_rfbuf(
    				nrf_radio_txaddress_get(NRF_RADIO), rx_pdu->type.dpl_pdu.pid)) {
    				interrupt_flags |= INT_RX_DATA_RECEIVED_MSK;
    			}
    		}
    
    		if ((tx_fifo.count == 0) || (esb_cfg.tx_mode == ESB_TXMODE_MANUAL)) {
    			esb_state = ESB_STATE_IDLE;
    			NVIC_SetPendingIRQ(ESB_EVT_IRQ);
    		} else {
    			NVIC_SetPendingIRQ(ESB_EVT_IRQ);
    			start_tx_transaction();
    		}
    	} else {
    		if (retransmits_remaining-- == 0) {
    			nrf_timer_task_trigger(esb_timer.p_reg, NRF_TIMER_TASK_SHUTDOWN);
    
    			/* All retransmits are expended, and the TX operation is
    			 * suspended
    			 */
    			last_tx_attempts = esb_cfg.retransmit_count + 1;
    			interrupt_flags |= INT_TX_FAILED_MSK;
    
    			esb_state = ESB_STATE_IDLE;
    			NVIC_SetPendingIRQ(ESB_EVT_IRQ);
    		} else {
    			/* There are still more retransmits left, TX mode should
    			 * be entered again as soon as the system timer reaches
    			 * CC[1].
    			 */
    			nrf_radio_shorts_set(NRF_RADIO,
    				(radio_shorts_common | NRF_RADIO_SHORT_DISABLED_RXEN_MASK));
    			update_rf_payload_format(current_payload->length);
    
    			nrf_radio_packetptr_set(NRF_RADIO, tx_payload_buffer);
    
    			on_radio_disabled = on_radio_disabled_tx;
    			esb_state = ESB_STATE_PTX_TX_ACK;
    
    			nrfx_gppi_channels_enable(BIT(ppi_ch_timer_compare1_radio_txen));
    
    			nrf_timer_task_trigger(esb_timer.p_reg, NRF_TIMER_TASK_START);
    
    			if (nrf_timer_event_check(esb_timer.p_reg, NRF_TIMER_EVENT_COMPARE1)) {
    				nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_TXEN);
    			}
    		}
    	}
    }
    
    static void clear_events_restart_rx(void)
    {
    	nrf_radio_shorts_set(NRF_RADIO, radio_shorts_common);
    
    	update_rf_payload_format(esb_cfg.payload_length);
    
    	nrf_radio_packetptr_set(NRF_RADIO, rx_payload_buffer);
    	nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_DISABLED);
    	nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);
    
    	while (!nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_DISABLED)) {
    		/* wait for register to settle */
    	}
    
    	nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_DISABLED);
    	nrf_radio_shorts_set(NRF_RADIO, (radio_shorts_common | NRF_RADIO_SHORT_DISABLED_TXEN_MASK));
    
    	nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_RXEN);
    }
    
    static void on_radio_disabled_rx_dpl(bool retransmit_payload,
    				     struct pipe_info *pipe_info)
    {
    	struct esb_radio_pdu *tx_pdu = (struct esb_radio_pdu *)tx_payload_buffer;
    	struct esb_radio_pdu *rx_pdu = (struct esb_radio_pdu *)rx_payload_buffer;
    
    	uint32_t pipe = nrf_radio_rxmatch_get(NRF_RADIO);
    
    	if (tx_fifo.count > 0 && ack_pl_wrap_pipe[pipe] != 0) {
    		current_payload = ack_pl_wrap_pipe[pipe]->p_payload;
    
    		/* Pipe stays in ACK with payload until TX FIFO is empty */
    		/* Do not report TX success on first ack payload or retransmit */
    		if (pipe_info->ack_payload == true && !retransmit_payload) {
    			ack_pl_wrap_pipe[pipe]->in_use = false;
    			ack_pl_wrap_pipe[pipe] = ack_pl_wrap_pipe[pipe]->p_next;
    			tx_fifo.count--;
    			if (tx_fifo.count > 0 && ack_pl_wrap_pipe[pipe] != 0) {
    				current_payload = ack_pl_wrap_pipe[pipe]->p_payload;
    			} else {
    				current_payload = 0;
    			}
    
    			/* ACK payloads also require TX_DS */
    			/* (page 40 of the 'nRF24LE1_Product_Specification_rev1_6.pdf') */
    			interrupt_flags |= INT_TX_SUCCESS_MSK;
    		}
    
    		if (current_payload != 0) {
    			pipe_info->ack_payload = true;
    			update_rf_payload_format(current_payload->length);
    
    			tx_pdu->type.dpl_pdu.length = current_payload->length;
    			memcpy(tx_pdu->data, current_payload->data, current_payload->length);
    		} else {
    			pipe_info->ack_payload = false;
    			update_rf_payload_format(0);
    			tx_pdu->type.dpl_pdu.length = 0;
    		}
    	} else {
    		pipe_info->ack_payload = false;
    		update_rf_payload_format(0);
    		tx_pdu->type.dpl_pdu.length = 0;
    	}
    
    	tx_pdu->type.dpl_pdu.pid = rx_pdu->type.dpl_pdu.pid;
    	tx_pdu->type.dpl_pdu.no_ack = rx_pdu->type.dpl_pdu.no_ack;
    }
    
    static void on_radio_disabled_rx(void)
    {
    	bool retransmit_payload = false;
    	bool send_rx_event = true;
    	struct pipe_info *pipe_info;
    	struct esb_radio_pdu *rx_pdu = (struct esb_radio_pdu *)rx_payload_buffer;
    	struct esb_radio_pdu *tx_pdu = (struct esb_radio_pdu *)tx_payload_buffer;
    
    	if (!nrf_radio_crc_status_check(NRF_RADIO)) {
    		clear_events_restart_rx();
    		return;
    	}
    
    	if (rx_fifo.count >= CONFIG_ESB_RX_FIFO_SIZE) {
    		clear_events_restart_rx();
    		return;
    	}
    
    	pipe_info = &rx_pipe_info[nrf_radio_rxmatch_get(NRF_RADIO)];
    
    	if ((nrf_radio_rxcrc_get(NRF_RADIO) == pipe_info->crc) &&
    	    (rx_pdu->type.dpl_pdu.pid) == pipe_info->pid) {
    		retransmit_payload = true;
    		send_rx_event = false;
    	}
    
    	pipe_info->pid = rx_pdu->type.dpl_pdu.pid;
    	pipe_info->crc = nrf_radio_rxcrc_get(NRF_RADIO);
    
    	/* Check if an ack should be sent */
    	if ((esb_cfg.selective_auto_ack == false) || rx_pdu->type.dpl_pdu.no_ack) {
    		nrf_radio_shorts_set(NRF_RADIO,
    				     (radio_shorts_common | NRF_RADIO_SHORT_DISABLED_RXEN_MASK));
    
    		switch (esb_cfg.protocol) {
    		case ESB_PROTOCOL_ESB_DPL:
    			on_radio_disabled_rx_dpl(retransmit_payload, pipe_info);
    			break;
    
    		case ESB_PROTOCOL_ESB:
    			update_rf_payload_format(0);
    
    			tx_pdu->type.fixed_pdu.pid = rx_pdu->type.fixed_pdu.pid;
    			tx_pdu->type.fixed_pdu.rfu1 = 0;
    
    			break;
    		}
    
    		esb_state = ESB_STATE_PRX_SEND_ACK;
    
    		nrf_radio_txaddress_set(NRF_RADIO, nrf_radio_rxmatch_get(NRF_RADIO));
    		nrf_radio_packetptr_set(NRF_RADIO, tx_pdu);
    
    		on_radio_disabled = on_radio_disabled_rx_ack;
    	} else {
    		clear_events_restart_rx();
    	}
    
    	if (send_rx_event) {
    		/* Push the new packet to the RX buffer and trigger a received
    		 * event if the operation was
    		 * successful.
    		 */
    		if (rx_fifo_push_rfbuf(nrf_radio_rxmatch_get(NRF_RADIO), pipe_info->pid)) {
    			interrupt_flags |= INT_RX_DATA_RECEIVED_MSK;
    			NVIC_SetPendingIRQ(ESB_EVT_IRQ);
    		}
    	}
    }
    
    static void on_radio_disabled_rx_ack(void)
    {
    	nrf_radio_shorts_set(NRF_RADIO, (radio_shorts_common | NRF_RADIO_SHORT_DISABLED_TXEN_MASK));
    	update_rf_payload_format(esb_cfg.payload_length);
    
    	nrf_radio_packetptr_set(NRF_RADIO, rx_payload_buffer);
    	on_radio_disabled = on_radio_disabled_rx;
    
    	esb_state = ESB_STATE_PRX;
    }
    
    /* Retrieve interrupt flags and reset them.
     *
     * @param[out] interrupts	Interrupt flags.
     */
    static void get_and_clear_irqs(uint32_t *interrupts)
    {
    	__ASSERT_NO_MSG(interrupts != NULL);
    
    	unsigned int key = irq_lock();
    
    	*interrupts = interrupt_flags;
    	interrupt_flags = 0;
    
    	irq_unlock(key);
    }
    
    static void radio_irq_handler(void)
    {
    	if (nrf_radio_int_enable_check(NRF_RADIO, NRF_RADIO_INT_DISABLED_MASK) &&
    	    nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_DISABLED)) {
    		nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_DISABLED);
    		/* Call the correct on_radio_disable function, depending on the
    		 * current protocol state.
    		 */
    		if (on_radio_disabled) {
    			on_radio_disabled();
    		}
    	}
    }
    
    static void esb_evt_irq_handler(void)
    {
    	uint32_t interrupts;
    	struct esb_evt event;
    
    	event.tx_attempts = last_tx_attempts;
    
    	get_and_clear_irqs(&interrupts);
    	if (event_handler != NULL) {
    		if (interrupts & INT_TX_SUCCESS_MSK) {
    			event.evt_id = ESB_EVENT_TX_SUCCESS;
    			event_handler(&event);
    		}
    		if (interrupts & INT_TX_FAILED_MSK) {
    			event.evt_id = ESB_EVENT_TX_FAILED;
    			event_handler(&event);
    		}
    		if (interrupts & INT_RX_DATA_RECEIVED_MSK) {
    			event.evt_id = ESB_EVENT_RX_RECEIVED;
    			event_handler(&event);
    		}
    	}
    }
    
    #if IS_ENABLED(CONFIG_ESB_DYNAMIC_INTERRUPTS)
    
    void RADIO_IRQHandler(const void *args)
    {
    	ARG_UNUSED(args);
    	radio_irq_handler();
    	ISR_DIRECT_PM();
    }
    
    void ESB_EVT_IRQHandler(const void *args)
    {
    	ARG_UNUSED(args);
    	esb_evt_irq_handler();
    	ISR_DIRECT_PM();
    }
    
    void ESB_TIMER_IRQHandler(const void *args)
    {
    	ARG_UNUSED(args);
    	ESB_TIMER_IRQ_HANDLER();
    	ISR_DIRECT_PM();
    }
    
    #else /* !IS_ENABLED(CONFIG_ESB_DYNAMIC_INTERRUPTS) */
    
    ISR_DIRECT_DECLARE(RADIO_IRQHandler)
    {
    	radio_irq_handler();
    
    	ISR_DIRECT_PM();
    
    	return 1;
    }
    
    
    ISR_DIRECT_DECLARE(ESB_EVT_IRQHandler)
    {
    	esb_evt_irq_handler();
    
    	ISR_DIRECT_PM();
    
    	return 1;
    }
    
    ISR_DIRECT_DECLARE(ESB_SYS_TIMER_IRQHandler)
    {
    	ISR_DIRECT_PM();
    
    	return 1;
    }
    
    #endif /* IS_ENABLED(CONFIG_ESB_DYNAMIC_INTERRUPTS) */
    
    static void esb_irq_disable(void)
    {
    	irq_disable(RADIO_IRQn);
    	irq_disable(ESB_EVT_IRQ);
    	irq_disable(ESB_TIMER_IRQ);
    }
    
    int esb_init(const struct esb_config *config)
    {
    	int err;
    
    	if (!config) {
    		return -EINVAL;
    	}
    
    	if (esb_initialized) {
    		esb_disable();
    	}
    
    	event_handler = config->event_handler;
    
    	memcpy(&esb_cfg, config, sizeof(esb_cfg));
    
    	interrupt_flags = 0;
    
    	memset(rx_pipe_info, 0, sizeof(rx_pipe_info));
    	memset(pids, 0, sizeof(pids));
    
    	update_radio_parameters();
    
    	/* Configure radio address registers according to ESB default values */
    	nrf_radio_base0_set(NRF_RADIO, 0xE7E7E7E7);
    	nrf_radio_base1_set(NRF_RADIO, 0x43434343);
    	nrf_radio_prefix0_set(NRF_RADIO, 0x23C343E7);
    	nrf_radio_prefix1_set(NRF_RADIO, 0x13E363A3);
    
    	initialize_fifos();
    
    	err = sys_timer_init();
    	if (err) {
    		return err;
    	}
    
    	err = ppi_init();
    	if (err) {
    		return err;
    	}
    
    	if(esb_cfg.optimize_txen_delay) {
    		NRF_RADIO->MODECNF0 = (NRF_RADIO->MODECNF0 & ~RADIO_MODECNF0_RU_Pos) | RADIO_MODECNF0_RU_Fast << RADIO_MODECNF0_RU_Pos;
    	}
    
    	
    #if IS_ENABLED(CONFIG_ESB_DYNAMIC_INTERRUPTS)
    
    	/* Ensure IRQs are disabled before attaching. */
    	esb_irq_disable();
    
    	ARM_IRQ_DIRECT_DYNAMIC_CONNECT(RADIO_IRQn, CONFIG_ESB_RADIO_IRQ_PRIORITY,
    				       0, reschedule);
    	ARM_IRQ_DIRECT_DYNAMIC_CONNECT(ESB_EVT_IRQ, CONFIG_ESB_EVENT_IRQ_PRIORITY,
    				       0, reschedule);
    	ARM_IRQ_DIRECT_DYNAMIC_CONNECT(ESB_TIMER_IRQ, CONFIG_ESB_EVENT_IRQ_PRIORITY,
    				       0, reschedule);
    
    	irq_connect_dynamic(RADIO_IRQn, CONFIG_ESB_RADIO_IRQ_PRIORITY,
    			    RADIO_IRQHandler, NULL, 0);
    	irq_connect_dynamic(ESB_EVT_IRQ, CONFIG_ESB_EVENT_IRQ_PRIORITY,
    			    ESB_EVT_IRQHandler,  NULL, 0);
    	irq_connect_dynamic(ESB_TIMER_IRQ, CONFIG_ESB_EVENT_IRQ_PRIORITY,
    			    ESB_TIMER_IRQHandler, NULL, 0);
    
    #else /* !IS_ENABLED(CONFIG_ESB_DYNAMIC_INTERRUPTS) */
    
    	IRQ_DIRECT_CONNECT(RADIO_IRQn, CONFIG_ESB_RADIO_IRQ_PRIORITY,
    			   RADIO_IRQHandler, 0);
    	IRQ_DIRECT_CONNECT(ESB_EVT_IRQ, CONFIG_ESB_EVENT_IRQ_PRIORITY,
    			   ESB_EVT_IRQHandler, 0);
    	IRQ_DIRECT_CONNECT(ESB_TIMER_IRQ, CONFIG_ESB_EVENT_IRQ_PRIORITY,
    			   ESB_TIMER_IRQ_HANDLER, 0);
    
    #endif /* IS_ENABLED(CONFIG_ESB_DYNAMIC_INTERRUPTS) */
    
    	irq_enable(RADIO_IRQn);
    	irq_enable(ESB_EVT_IRQ);
    	irq_enable(ESB_TIMER_IRQ);
    
    	esb_state = ESB_STATE_IDLE;
    	esb_initialized = true;
    
    	if (nrf52_errata_182()) {
    		/* Check if the device is an nRF52832 Rev. 2. */
    		/* Workaround for nRF52832 rev 2 errata 182 */
    		*(volatile uint32_t *)0x4000173C |= (1 << 10);
    	}
    
    	return 0;
    }
    
    int esb_suspend(void)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    
    	/*  Clear PPI */
    	nrfx_gppi_channels_disable(ppi_all_channels_mask);
    
    	esb_state = ESB_STATE_IDLE;
    
    	return 0;
    }
    
    void esb_disable(void)
    {
    	sys_timer_deinit();
    	ppi_deinit();
    
    	esb_state = ESB_STATE_IDLE;
    	esb_initialized = false;
    
    	reset_fifos();
    
    	memset(rx_pipe_info, 0, sizeof(rx_pipe_info));
    	memset(pids, 0, sizeof(pids));
    
    	esb_irq_disable();
    }
    
    bool esb_is_idle(void)
    {
    	return (esb_state == ESB_STATE_IDLE);
    }
    
    static struct payload_wrap *find_free_payload_cont(void)
    {
    	for (int i = 0; i < CONFIG_ESB_TX_FIFO_SIZE; i++) {
    		if (!ack_pl_wrap[i].in_use) {
    			return &ack_pl_wrap[i];
    		}
    	}
    
    	return 0;
    }
    
    int esb_write_payload(const struct esb_payload *payload)
    {
    
    	if (!esb_initialized) {
    		return -EACCES;
    	}
    
    	if (payload == NULL) {
    		return -EINVAL;
    	}
    
    	if ((payload->length == 0) || (payload->length > CONFIG_ESB_MAX_PAYLOAD_LENGTH) ||
    	    ((esb_cfg.protocol == ESB_PROTOCOL_ESB) &&
    	     (payload->length > esb_cfg.payload_length))) {
    		return -EMSGSIZE;
    	}
    
    	if (tx_fifo.count >= CONFIG_ESB_TX_FIFO_SIZE) {
    		return -ENOMEM;
    	}
    
    	if (payload->pipe >= CONFIG_ESB_PIPE_COUNT) {
    		return -EINVAL;
    	}
    
    	unsigned int key = irq_lock();
    
    	if (esb_cfg.mode == ESB_MODE_PTX) {
    		memcpy(tx_fifo.payload[tx_fifo.back], payload, sizeof(struct esb_payload));
    
    		pids[payload->pipe] = (pids[payload->pipe] + 1) % (PID_MAX + 1);
    		tx_fifo.payload[tx_fifo.back]->pid = pids[payload->pipe];
    
    		if (++tx_fifo.back >= CONFIG_ESB_TX_FIFO_SIZE) {
    			tx_fifo.back = 0;
    		}
    
    		tx_fifo.count++;
    	} else {
    		struct payload_wrap *new_ack_payload = find_free_payload_cont();
    
    		if (new_ack_payload != 0) {
    			new_ack_payload->in_use = true;
    			new_ack_payload->p_next = 0;
    			memcpy(new_ack_payload->p_payload, payload, sizeof(struct esb_payload));
    
    			pids[payload->pipe] = (pids[payload->pipe] + 1) % (PID_MAX + 1);
    			new_ack_payload->p_payload->pid = pids[payload->pipe];
    
    			if (ack_pl_wrap_pipe[payload->pipe] == 0) {
    				ack_pl_wrap_pipe[payload->pipe] = new_ack_payload;
    			} else {
    				struct payload_wrap *pl = ack_pl_wrap_pipe[payload->pipe];
    
    				while (pl->p_next != 0) {
    					pl = (struct payload_wrap *)pl->p_next;
    				}
    				pl->p_next = (struct payload_wrap *)new_ack_payload;
    			}
    			tx_fifo.count++;
    		}
    	}
    
    	irq_unlock(key);
    
    	if (esb_cfg.mode == ESB_MODE_PTX &&
    	    esb_cfg.tx_mode == ESB_TXMODE_AUTO &&
    	    esb_state == ESB_STATE_IDLE) {
    		start_tx_transaction();
    	}
    
    	return 0;
    }
    
    int esb_read_rx_payload(struct esb_payload *payload)
    {
    	if (!esb_initialized) {
    		return -EACCES;
    	}
    	if (payload == NULL) {
    		return -EINVAL;
    	}
    
    	if (rx_fifo.count == 0) {
    		return -ENODATA;
    	}
    
    	unsigned int key = irq_lock();
    
    	payload->length = rx_fifo.payload[rx_fifo.front]->length;
    	payload->pipe = rx_fifo.payload[rx_fifo.front]->pipe;
    	payload->rssi = rx_fifo.payload[rx_fifo.front]->rssi;
    	payload->pid = rx_fifo.payload[rx_fifo.front]->pid;
    	payload->noack = rx_fifo.payload[rx_fifo.front]->noack;
    	memcpy(payload->data, rx_fifo.payload[rx_fifo.front]->data,
    	       payload->length);
    
    	if (++rx_fifo.front >= CONFIG_ESB_RX_FIFO_SIZE) {
    		rx_fifo.front = 0;
    	}
    
    	rx_fifo.count--;
    
    	irq_unlock(key);
    
    	return 0;
    }
    
    int esb_start_tx(void)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    
    	if (tx_fifo.count == 0) {
    		return -ENODATA;
    	}
    
    	start_tx_transaction();
    
    	return 0;
    }
    
    int esb_start_rx(void)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    
    	nrf_radio_int_disable(NRF_RADIO, 0xFFFFFFFF);
    	nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_DISABLED);
    
    	on_radio_disabled = on_radio_disabled_rx;
    
    	nrf_radio_shorts_set(NRF_RADIO, (radio_shorts_common | NRF_RADIO_SHORT_DISABLED_TXEN_MASK));
    	nrf_radio_int_enable(NRF_RADIO, NRF_RADIO_INT_DISABLED_MASK);
    
    	esb_state = ESB_STATE_PRX;
    
    	nrf_radio_rxaddresses_set(NRF_RADIO, esb_addr.rx_pipes_enabled);
    	nrf_radio_frequency_set(NRF_RADIO, (RADIO_BASE_FREQUENCY + esb_addr.rf_channel));
    	nrf_radio_packetptr_set(NRF_RADIO, rx_payload_buffer);
    
    	NVIC_ClearPendingIRQ(RADIO_IRQn);
    	irq_enable(RADIO_IRQn);
    
    	nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_ADDRESS);
    	nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_PAYLOAD);
    	nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_DISABLED);
    
    	nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_RXEN);
    
    	return 0;
    }
    
    int esb_stop_rx(void)
    {
    	if ((esb_state != ESB_STATE_PRX) && (esb_state != ESB_STATE_PRX_SEND_ACK)) {
    		return -EINVAL;
    	}
    
    	nrf_radio_shorts_disable(NRF_RADIO, 0xFFFFFFFF);
    	nrf_radio_int_disable(NRF_RADIO, 0xFFFFFFFF);
    
    	on_radio_disabled = NULL;
    
    	nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_DISABLED);
    	nrf_radio_task_trigger(NRF_RADIO, NRF_RADIO_TASK_DISABLE);
    
    	while (!nrf_radio_event_check(NRF_RADIO, NRF_RADIO_EVENT_DISABLED)) {
    		/* wait for register to settle */
    	}
    
    	nrf_radio_event_clear(NRF_RADIO, NRF_RADIO_EVENT_DISABLED);
    
    	esb_state = ESB_STATE_IDLE;
    
    	return 0;
    }
    
    int esb_flush_tx(void)
    {
    	if (!esb_initialized) {
    		return -EACCES;
    	}
    
    	unsigned int key = irq_lock();
    
    	tx_fifo.count = 0;
    	tx_fifo.back = 0;
    	tx_fifo.front = 0;
    
    	irq_unlock(key);
    
    	return 0;
    }
    
    int esb_pop_tx(void)
    {
    	if (!esb_initialized) {
    		return -EACCES;
    	}
    	if (tx_fifo.count == 0) {
    		return -ENODATA;
    	}
    
    	unsigned int key = irq_lock();
    
    	if (++tx_fifo.back >= CONFIG_ESB_TX_FIFO_SIZE) {
    		tx_fifo.back = 0;
    	}
    	tx_fifo.count--;
    
    	irq_unlock(key);
    
    	return 0;
    }
    
    bool esb_tx_full(void)
    {
        return tx_fifo.count >= CONFIG_ESB_TX_FIFO_SIZE;
    }
    
    int esb_flush_rx(void)
    {
    	if (!esb_initialized) {
    		return -EACCES;
    	}
    
    	unsigned int key = irq_lock();
    
    	rx_fifo.count = 0;
    	rx_fifo.back = 0;
    	rx_fifo.front = 0;
    
    	memset(rx_pipe_info, 0, sizeof(rx_pipe_info));
    
    	irq_unlock(key);
    
    	return 0;
    }
    
    int esb_set_address_length(uint8_t length)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    	if (!((length > 2) && (length < 6))) {
    		return -EINVAL;
    	}
    
    	esb_addr.addr_length = length;
    
    	update_rf_payload_format(esb_cfg.payload_length);
    
    	return 0;
    }
    
    int esb_set_base_address_0(const uint8_t *addr)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    	if (addr == NULL) {
    		return -EINVAL;
    	}
    
    	memcpy(esb_addr.base_addr_p0, addr, sizeof(esb_addr.base_addr_p0));
    
    	update_radio_addresses(ADDR_UPDATE_MASK_BASE0);
    
    	return 0;
    }
    
    int esb_set_base_address_1(const uint8_t *addr)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    	if (addr == NULL) {
    		return -EINVAL;
    	}
    
    	memcpy(esb_addr.base_addr_p1, addr, sizeof(esb_addr.base_addr_p1));
    
    	update_radio_addresses(ADDR_UPDATE_MASK_BASE1);
    
    	return 0;
    }
    
    int esb_set_prefixes(const uint8_t *prefixes, uint8_t num_pipes)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    	if (prefixes == NULL) {
    		return -EINVAL;
    	}
    	if (!(num_pipes <= CONFIG_ESB_PIPE_COUNT)) {
    		return -EINVAL;
    	}
    
    	memcpy(esb_addr.pipe_prefixes, prefixes, num_pipes);
    
    	esb_addr.num_pipes = num_pipes;
    	esb_addr.rx_pipes_enabled = BIT_MASK_UINT_8(num_pipes);
    
    	update_radio_addresses(ADDR_UPDATE_MASK_PREFIX);
    
    	return 0;
    }
    
    int esb_update_prefix(uint8_t pipe, uint8_t prefix)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    	if (pipe >= CONFIG_ESB_PIPE_COUNT) {
    		return -EINVAL;
    	}
    
    	esb_addr.pipe_prefixes[pipe] = prefix;
    
    	update_radio_addresses(ADDR_UPDATE_MASK_PREFIX);
    
    	return 0;
    }
    
    int esb_enable_pipes(uint8_t enable_mask)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    	if ((enable_mask | BIT_MASK_UINT_8(CONFIG_ESB_PIPE_COUNT)) !=
    	    BIT_MASK_UINT_8(CONFIG_ESB_PIPE_COUNT)) {
    		return -EINVAL;
    	}
    
    	esb_addr.rx_pipes_enabled = enable_mask;
    
    	return 0;
    }
    
    int esb_set_rf_channel(uint32_t channel)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    	if (channel > 100) {
    		return -EINVAL;
    	}
    
    	esb_addr.rf_channel = channel;
    
    	return 0;
    }
    
    int esb_get_rf_channel(uint32_t *channel)
    {
    	if (channel == NULL) {
    		return -EINVAL;
    	}
    
    	*channel = esb_addr.rf_channel;
    
    	return 0;
    }
    
    int esb_set_tx_power(enum esb_tx_power tx_output_power)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    
    	if (esb_cfg.tx_output_power != tx_output_power) {
    		esb_cfg.tx_output_power = tx_output_power;
    		update_radio_tx_power();
    	}
    
    	return 0;
    }
    
    int esb_set_retransmit_delay(uint16_t delay)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    	if (delay < RETRANSMIT_DELAY_MIN) {
    		return -EINVAL;
    	}
    
    	esb_cfg.retransmit_delay = delay;
    
    	return 0;
    }
    
    int esb_set_retransmit_count(uint16_t count)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    
    	esb_cfg.retransmit_count = count;
    
    	return 0;
    }
    
    int esb_set_bitrate(enum esb_bitrate bitrate)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    
    	esb_cfg.bitrate = bitrate;
    
    	return update_radio_bitrate() ? 0 : -EINVAL;
    }
    
    int esb_reuse_pid(uint8_t pipe)
    {
    	if (esb_state != ESB_STATE_IDLE) {
    		return -EBUSY;
    	}
    	if (!(pipe < CONFIG_ESB_PIPE_COUNT)) {
    		return -EINVAL;
    	}
    
    	pids[pipe] = (pids[pipe] + PID_MAX) % (PID_MAX + 1);
    
    	return 0;
    }
    


  • My bad, the above code is 100% functional.

    I was having radio interference from my laptop Bluetooth which was activated but not connected.
    It might have been polling regularly to find new devices...

    Do you have any suggestion to make ESB robust to this kind of perturbation ?

    Also, do you know how I can upstream the above code for the whole community to benefit from it ?

Reply
  • My bad, the above code is 100% functional.

    I was having radio interference from my laptop Bluetooth which was activated but not connected.
    It might have been polling regularly to find new devices...

    Do you have any suggestion to make ESB robust to this kind of perturbation ?

    Also, do you know how I can upstream the above code for the whole community to benefit from it ?

Children
  • Hi

    If your laptop is transmitting something in the 2.4GHz band while you are trying to receive something over ESB then most likely the ESB packet will be dropped, since a transmitter in close proximity to your receiver will saturate the receiver and block out the wanted signal. 

    There is not a lot you can do about this, normally wireless protocols handle issues like these by relying on acknowledgements and retransmissions to get the data through eventually (at the cost of latency and throughput). Frequency jumping schemes can also help to avoid interference, but if the transmitter is very close then changing to a different frequency in the same band is not enough. 

    A more practical approach is to try and keep the ESB dongle as far away from the WiFi/Bluetooth chipset in the PC as possible. 

    lgs-wtk said:
    Also, do you know how I can upstream the above code for the whole community to benefit from it ?

    The general Zephyr contribution guide lines can be found here.

    In your case you would be changing parts of the nrf repository rather than the Zephyr repository, which means at minimum you need to create a fork of the sdk-nrf repository on your private github account, create a new branch for your changes, commit the required changes to your branch, push it back to your fork, and then you can create a pull request to have these changes included in the sdk-nrf repository.  

    Best regards
    Torbjørn

Related