Hello,
I am using nRF5 SDK v17.0.2 as my development environment for this project. My goal is to retransmit the same command as quickly (and as many times) as possible in a 1ms window. Currently, I am able to retransmit ~200us apart despite sending the nrf_esb_write_payload()
command once every 10us.
For reference, here is my ESB config:
uint32_t esb_init( void ) { uint32_t err_code; uint8_t base_addr_0[4] = {0xB6,0xC7,0xD8,0xE9}; uint8_t base_addr_1[4] = {0xB6,0xC7,0xD8,0xE9}; uint8_t addr_prefix[8] = {0xA5,0xA5, 0xC3,0xC4, 0xC5,0xC6, 0xC7,0xC8 }; nrf_esb_config_t nrf_esb_config = NRF_ESB_LEGACY_CONFIG; nrf_esb_config.protocol = NRF_ESB_PROTOCOL_ESB;//_DPL; nrf_esb_config.bitrate = NRF_ESB_BITRATE_1MBPS; nrf_esb_config.crc = NRF_ESB_CRC_16BIT; nrf_esb_config.event_handler = nrf_esb_event_handler; nrf_esb_config.mode = NRF_ESB_MODE_PTX; nrf_esb_config.selective_auto_ack = true; nrf_esb_config.payload_length = 1; nrf_esb_config.retransmit_count = 3;
The event to send the radio command is triggered via a button on the device:
else if( pin == PIN_IN_1) { int i; for(i = 0; i < MAX_RETRIES; ++i){ nrf_esb_write_payload(&tx_payload); nrf_delay_us(10); } nrf_delay_us(100); nrf_esb_stop_tx(); }
I made two changes to nrf_esb_write_payload():
- If the TX FIFO buffer is full, clear it and proceed.
- Allow the device to call
start_tx_transaction();
at the end of the function ifm_retries_active
is TRUE.m_retries_active
is a flag I used to tell the device to retransmit while keeping it in TX mode instead of changing back to IDLE.
uint32_t nrf_esb_write_payload(nrf_esb_payload_t const * p_payload) { VERIFY_TRUE(m_esb_initialized, NRF_ERROR_INVALID_STATE); VERIFY_PARAM_NOT_NULL(p_payload); VERIFY_PAYLOAD_LENGTH(p_payload); if(m_tx_fifo.count >= NRF_ESB_TX_FIFO_SIZE){ nrf_esb_flush_tx(); } VERIFY_FALSE(m_tx_fifo.count >= NRF_ESB_TX_FIFO_SIZE, NRF_ERROR_NO_MEM); VERIFY_TRUE(p_payload->pipe < NRF_ESB_PIPE_COUNT, NRF_ERROR_INVALID_PARAM); DISABLE_RF_IRQ(); if (m_config_local.mode == NRF_ESB_MODE_PTX) { memcpy(m_tx_fifo.p_payload[m_tx_fifo.entry_point], p_payload, sizeof(nrf_esb_payload_t)); m_pids[p_payload->pipe] = (m_pids[p_payload->pipe] + 1) % (NRF_ESB_PID_MAX + 1); m_tx_fifo.p_payload[m_tx_fifo.entry_point]->pid = m_pids[p_payload->pipe]; if (++m_tx_fifo.entry_point >= NRF_ESB_TX_FIFO_SIZE) { m_tx_fifo.entry_point = 0; } m_tx_fifo.count++; } else { nrf_esb_payload_random_access_buf_wrapper_t *new_ack_payload; if((new_ack_payload = find_free_payload_cont()) != 0) { new_ack_payload->in_use = true; new_ack_payload->p_next = 0; memcpy(new_ack_payload->p_payload, p_payload, sizeof(nrf_esb_payload_t)); m_pids[p_payload->pipe] = (m_pids[p_payload->pipe] + 1) % (NRF_ESB_PID_MAX + 1); new_ack_payload->p_payload->pid = m_pids[p_payload->pipe]; if(m_ack_pl_container_entry_point_pr_pipe[p_payload->pipe] == 0) { m_ack_pl_container_entry_point_pr_pipe[p_payload->pipe] = new_ack_payload; } else { nrf_esb_payload_random_access_buf_wrapper_t *list_iterator = m_ack_pl_container_entry_point_pr_pipe[p_payload->pipe]; while(list_iterator->p_next != 0) { list_iterator = (nrf_esb_payload_random_access_buf_wrapper_t *)list_iterator->p_next; } list_iterator->p_next = (struct nrf_esb_payload_random_access_buf_wrapper_t *)new_ack_payload; } m_tx_fifo.count++; } } ENABLE_RF_IRQ(); if ((m_config_local.mode == NRF_ESB_MODE_PTX && m_config_local.tx_mode == NRF_ESB_TXMODE_AUTO && m_nrf_esb_mainstate == NRF_ESB_STATE_IDLE) || m_retries_active) { start_tx_transaction(); } return NRF_SUCCESS; }
In start_tx_transaction()
, I adjusted the m_tx_payload_buffer[0]
to what was needed for my receiver and since I am using the NRF_ESB_PROTOCOL_ESB
protocol, and am not expecting acknowledgments, I set on_radio_disabled = on_radio_disabled_tx_noack;
and m_nrf_esb_mainstate = NRF_ESB_STATE_PTX_TX;
static void start_tx_transaction() { bool ack; m_last_tx_attempts = 1; // Prepare the payload mp_current_payload = m_tx_fifo.p_payload[m_tx_fifo.exit_point]; switch (m_config_local.protocol) { case NRF_ESB_PROTOCOL_ESB: // SEGGER_RTT_printf(0, "NRF_ESB_PROTOCOL_ESB\n"); update_rf_payload_format(mp_current_payload->length); // m_tx_payload_buffer[0] = mp_current_payload->pid; m_tx_payload_buffer[0] = 3; m_tx_payload_buffer[1] = 0; memcpy(&m_tx_payload_buffer[2], mp_current_payload->data, mp_current_payload->length); NRF_RADIO->SHORTS = m_radio_shorts_common | RADIO_SHORTS_DISABLED_RXEN_Msk; NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk | RADIO_INTENSET_READY_Msk; // SEGGER_RTT_printf(0, "7_1. start_tx_transaction %x\n", mp_current_payload->pid); // Configure the retransmit counter m_retransmits_remaining = m_config_local.retransmit_count; on_radio_disabled = on_radio_disabled_tx_noack; m_nrf_esb_mainstate = NRF_ESB_STATE_PTX_TX; break; case NRF_ESB_PROTOCOL_ESB_DPL: ack = !mp_current_payload->noack || !m_config_local.selective_auto_ack; m_tx_payload_buffer[0] = mp_current_payload->length; m_tx_payload_buffer[1] = mp_current_payload->pid << 1; m_tx_payload_buffer[1] |= mp_current_payload->noack ? 0x00 : 0x01; memcpy(&m_tx_payload_buffer[2], mp_current_payload->data, mp_current_payload->length); // Handling ack if noack is set to false or if selective auto ack is turned off if (ack) { NRF_RADIO->SHORTS = m_radio_shorts_common | RADIO_SHORTS_DISABLED_RXEN_Msk; NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk | RADIO_INTENSET_READY_Msk; // Configure the retransmit counter m_retransmits_remaining = m_config_local.retransmit_count; on_radio_disabled = on_radio_disabled_tx; m_nrf_esb_mainstate = NRF_ESB_STATE_PTX_TX_ACK; } else { NRF_RADIO->SHORTS = m_radio_shorts_common; NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk; on_radio_disabled = on_radio_disabled_tx_noack; m_nrf_esb_mainstate = NRF_ESB_STATE_PTX_TX; } break; default: // Should not be reached break; } NRF_RADIO->TXADDRESS = mp_current_payload->pipe; NRF_RADIO->RXADDRESSES = 1 << mp_current_payload->pipe; NRF_RADIO->FREQUENCY = m_esb_addr.rf_channel; NRF_RADIO->PACKETPTR = (uint32_t)m_tx_payload_buffer; NVIC_ClearPendingIRQ(RADIO_IRQn); NVIC_EnableIRQ(RADIO_IRQn); NRF_RADIO->EVENTS_ADDRESS = 0; NRF_RADIO->EVENTS_PAYLOAD = 0; NRF_RADIO->EVENTS_DISABLED = 0; DEBUG_PIN_SET(DEBUGPIN4); NRF_RADIO->TASKS_TXEN = 1; }
I edited on_radio_disabled_tx_noack()
pretty heavily so please see below for reference. The idea is for m_retries_active
(mentioned earlier) to be TRUE until m_retransmits_remaining_noack == 0
at which point, the device should disable TX mode and return to its IDLE state.
static void on_radio_disabled_tx_noack() { m_interrupt_flags |= NRF_ESB_INT_TX_SUCCESS_MSK; (void) nrf_esb_skip_tx(); m_retries_active = true; if (m_tx_fifo.count == 0) { m_nrf_esb_mainstate = NRF_ESB_STATE_IDLE; NVIC_SetPendingIRQ(ESB_EVT_IRQ); } //else //{ //NVIC_SetPendingIRQ(ESB_EVT_IRQ); //start_tx_transaction(); //redundant? //} NRF_RADIO->TASKS_STOP = 1; if (--m_retransmits_remaining_noack == 0) { m_retransmits_remaining_noack = MAX_RETRIES; m_retries_active = false; nrf_esb_flush_tx(); // these are taken from nrf_esb_stop_rx to reset radio for IRQs 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); m_nrf_esb_mainstate = NRF_ESB_STATE_IDLE; } }
In summary, I know the retries are working because the receiver will see a delay of 220us if it picks up the first transmit and ignore the rest. However, it appears that several transmits cannot be received (or are never sent) because the receiver only gets subsequent transmissions at 420us, 620us, etc. If MAX_RETRIES
or the delay after nrf_esb_write_payload()
is too small, then the receiver either gets the first transaction or none at all. In previous iterations of this design, I have used an NRF24L01+ and, using the CE, have been able to produce much small delays between TX events to the receiver.
- Is there a way to minimize the delay between successful TX transactions?
- Based on Figure 54 (below left) and Section 6.12.16.8 (below middle) of https://infocenter.nordicsemi.com/pdf/nRF52820_PS_v1.3.pdf, I believe I'm running into an issue where the radio goes from TX to TXDISABLE to DISABLED to TXRU to TXIDLE to TX which causes these ~200us delays. However, I would like the device to operate as it does in Figure 57 (below right). What am I missing to make this happen?
Thank you for your help in advance. Please let me know if I can provide more information.