Wouter van Kleunen
/
nrf52_esb
NRF52_esb
Revision 1:66f95e364222, committed 2021-02-04
- Comitter:
- wkleunen
- Date:
- Thu Feb 04 10:36:44 2021 +0000
- Parent:
- 0:a01a54c0dc90
- Commit message:
- Initial compile;
Changed in this revision
diff -r a01a54c0dc90 -r 66f95e364222 main.cpp --- a/main.cpp Mon Mar 23 04:09:41 2015 +0000 +++ b/main.cpp Thu Feb 04 10:36:44 2021 +0000 @@ -11,6 +11,8 @@ #include "nrf_delay.h" //#include "nrf_gpio.h" + + static uesb_payload_t tx_payload, rx_payload; void uesb_event_handler() @@ -26,7 +28,7 @@ if(rf_interrupts & UESB_INT_TX_FAILED_MSK) { - uesb_flush_tx(); + uesb_flush_tx(); } if(rf_interrupts & UESB_INT_RX_DR_MSK) @@ -36,7 +38,7 @@ NRF_GPIO->OUTSET = (uint32_t)((rx_payload.data[2] & 0x0F) << 8); } - uesb_get_tx_attempts(&tx_attempts); + uesb_get_tx_attempts(&tx_attempts); NRF_GPIO->OUTCLR = 0xFUL << 12; NRF_GPIO->OUTSET = (tx_attempts & 0x0F) << 12; } @@ -65,9 +67,9 @@ uesb_init(&uesb_config); - uesb_set_address(UESB_ADDRESS_PIPE0, rx_addr_p0); - uesb_set_address(UESB_ADDRESS_PIPE1, rx_addr_p1); - uesb_set_address(UESB_ADDRESS_PIPE2, &rx_addr_p2); + nrf_esb_set_base_address_0(rx_addr_p0); + nrf_esb_set_base_address_1(rx_addr_p1); + //nrf_esb_set_base_address_2(&rx_addr_p2); tx_payload.length = 8; tx_payload.pipe = 0;
diff -r a01a54c0dc90 -r 66f95e364222 mbed-os.lib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed-os.lib Thu Feb 04 10:36:44 2021 +0000 @@ -0,0 +1,1 @@ +https://github.com/ARMmbed/mbed-os/#bfde5aa1e74802771eaeacfa74789f71677325cb
diff -r a01a54c0dc90 -r 66f95e364222 mbed.bld --- a/mbed.bld Mon Mar 23 04:09:41 2015 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -http://mbed.org/users/mbed_official/code/mbed/builds/487b796308b0 \ No newline at end of file
diff -r a01a54c0dc90 -r 66f95e364222 micro_esb.c --- a/micro_esb.c Mon Mar 23 04:09:41 2015 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,871 +0,0 @@ -/* Copyright (c) 2014 Nordic Semiconductor. All Rights Reserved. - * - * The information contained herein is property of Nordic Semiconductor ASA. - * Terms and conditions of usage are described in detail in NORDIC - * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. - * - * Licensees are granted free, non-transferable use of the information. NO - * WARRANTY of ANY KIND is provided. This heading must NOT be removed from - * the file. - * - */ - -#include "micro_esb.h" -#include "uesb_error_codes.h" -//#include "nrf_gpio.h" -#include <string.h> - - -static uesb_event_handler_t m_event_handler; - -// RF parameters -static uesb_config_t m_config_local; - -// TX FIFO -static uesb_payload_t m_tx_fifo_payload[UESB_CORE_TX_FIFO_SIZE]; -static uesb_payload_tx_fifo_t m_tx_fifo; - -// RX FIFO -static uesb_payload_t m_rx_fifo_payload[UESB_CORE_RX_FIFO_SIZE]; -static uesb_payload_rx_fifo_t m_rx_fifo; - -static uint8_t m_tx_payload_buffer[UESB_CORE_MAX_PAYLOAD_LENGTH + 2]; -static uint8_t m_rx_payload_buffer[UESB_CORE_MAX_PAYLOAD_LENGTH + 2]; - -// Run time variables -static volatile uint32_t m_interrupt_flags = 0; -static uint32_t m_pid = 0; -static volatile uint32_t m_retransmits_remaining; -static volatile uint32_t m_last_tx_attempts; -static volatile uint8_t m_last_rx_packet_pid = 0xFF; -static volatile uint32_t m_last_rx_packet_crc = 0xFFFFFFFF; -static volatile uint32_t m_wait_for_ack_timeout_us; - -static uesb_payload_t *current_payload; - -static uesb_mainstate_t m_uesb_mainstate = UESB_STATE_UNINITIALIZED; - -// Constant parameters -#define RX_WAIT_FOR_ACK_TIMEOUT_US_2MBPS 48 // Smallest reliable value - 43 -#define RX_WAIT_FOR_ACK_TIMEOUT_US_1MBPS 64 // Smallest reliable value - 59 -#define RX_WAIT_FOR_ACK_TIMEOUT_US_250KBPS 250 - -// Macros -#define DISABLE_RF_IRQ NVIC_DisableIRQ(RADIO_IRQn) -#define ENABLE_RF_IRQ NVIC_EnableIRQ(RADIO_IRQn) - -#define RADIO_SHORTS_COMMON ( RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_DISABLE_Msk | \ - RADIO_SHORTS_ADDRESS_RSSISTART_Msk | RADIO_SHORTS_DISABLED_RSSISTOP_Msk ) - -// These function pointers are changed dynamically, depending on protocol configuration and state -static void (*on_radio_disabled)(void) = 0; -static void (*on_radio_end)(void) = 0; -static void (*update_rf_payload_format)(uint32_t payload_length) = 0; - -// The following functions are assigned to the function pointers above -static void on_radio_disabled_esb_dpl_tx_noack(void); -static void on_radio_disabled_esb_dpl_tx(void); -static void on_radio_disabled_esb_dpl_tx_wait_for_ack(void); -static void on_radio_disabled_esb_dpl_rx(void); -static void on_radio_disabled_esb_dpl_rx_ack(void); - -static void on_radio_end_sb_tx(void); -static void on_radio_end_sb_rx(void); - -static void update_rf_payload_format_esb_dpl(uint32_t payload_length) -{ -#if(UESB_CORE_MAX_PAYLOAD_LENGTH <= 32) - NRF_RADIO->PCNF0 = (0 << RADIO_PCNF0_S0LEN_Pos) | (6 << RADIO_PCNF0_LFLEN_Pos) | (3 << RADIO_PCNF0_S1LEN_Pos); -#else - 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) | - ((m_config_local.rf_addr_length - 1) << RADIO_PCNF1_BALEN_Pos) | - (0 << RADIO_PCNF1_STATLEN_Pos) | - (UESB_CORE_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) | - ((m_config_local.rf_addr_length - 1) << RADIO_PCNF1_BALEN_Pos) | - (payload_length << RADIO_PCNF1_STATLEN_Pos) | - (payload_length << RADIO_PCNF1_MAXLEN_Pos); -} - -static void update_rf_payload_format_sb(uint32_t payload_length) -{ - NRF_RADIO->PCNF0 = (0 << RADIO_PCNF0_S0LEN_Pos) | (0 << RADIO_PCNF0_LFLEN_Pos) | (0 << RADIO_PCNF0_S1LEN_Pos); - NRF_RADIO->PCNF1 = (RADIO_PCNF1_WHITEEN_Disabled << RADIO_PCNF1_WHITEEN_Pos) | - (RADIO_PCNF1_ENDIAN_Big << RADIO_PCNF1_ENDIAN_Pos) | - ((m_config_local.rf_addr_length - 1) << RADIO_PCNF1_BALEN_Pos) | - (payload_length << RADIO_PCNF1_STATLEN_Pos) | - (payload_length << RADIO_PCNF1_MAXLEN_Pos); -} - -// Function that swaps the bits within each byte in a uint32. Used to convert from nRF24L type addressing to nRF51 type addressing -static uint32_t bytewise_bit_swap(uint32_t inp) -{ - inp = (inp & 0xF0F0F0F0) >> 4 | (inp & 0x0F0F0F0F) << 4; - inp = (inp & 0xCCCCCCCC) >> 2 | (inp & 0x33333333) << 2; - return (inp & 0xAAAAAAAA) >> 1 | (inp & 0x55555555) << 1; -} - -static void update_radio_parameters() -{ - // Protocol - switch(m_config_local.protocol) - { - case UESB_PROTOCOL_ESB_DPL: - update_rf_payload_format = update_rf_payload_format_esb_dpl; - break; - case UESB_PROTOCOL_ESB: - update_rf_payload_format = update_rf_payload_format_esb; - break; - case UESB_PROTOCOL_SB: - update_rf_payload_format = update_rf_payload_format_sb; - on_radio_end = (m_config_local.mode == UESB_MODE_PTX ? on_radio_end_sb_tx : on_radio_end_sb_rx); - break; - } - // TX power - NRF_RADIO->TXPOWER = m_config_local.tx_output_power << RADIO_TXPOWER_TXPOWER_Pos; - - // RF bitrate - NRF_RADIO->MODE = m_config_local.bitrate << RADIO_MODE_MODE_Pos; - switch(m_config_local.bitrate) - { - case UESB_BITRATE_2MBPS: - m_wait_for_ack_timeout_us = RX_WAIT_FOR_ACK_TIMEOUT_US_2MBPS; - break; - case UESB_BITRATE_1MBPS: - m_wait_for_ack_timeout_us = RX_WAIT_FOR_ACK_TIMEOUT_US_1MBPS; - break; - case UESB_BITRATE_250KBPS: - m_wait_for_ack_timeout_us = RX_WAIT_FOR_ACK_TIMEOUT_US_250KBPS; - break; - } - - // CRC configuration - NRF_RADIO->CRCCNF = m_config_local.crc << RADIO_CRCCNF_LEN_Pos; - if(m_config_local.crc == RADIO_CRCCNF_LEN_Two) - { - NRF_RADIO->CRCINIT = 0xFFFFUL; // Initial value - NRF_RADIO->CRCPOLY = 0x11021UL; // CRC poly: x^16+x^12^x^5+1 - } - else if(m_config_local.crc == RADIO_CRCCNF_LEN_One) - { - NRF_RADIO->CRCINIT = 0xFFUL; // Initial value - NRF_RADIO->CRCPOLY = 0x107UL; // CRC poly: x^8+x^2^x^1+1 - } - - // Packet format - update_rf_payload_format(m_config_local.payload_length); - - // Radio address config - NRF_RADIO->PREFIX0 = bytewise_bit_swap(m_config_local.rx_address_p3 << 24 | m_config_local.rx_address_p2 << 16 | m_config_local.rx_address_p1[0] << 8 | m_config_local.rx_address_p0[0]); - NRF_RADIO->PREFIX1 = bytewise_bit_swap(m_config_local.rx_address_p7 << 24 | m_config_local.rx_address_p6 << 16 | m_config_local.rx_address_p5 << 8 | m_config_local.rx_address_p4); - NRF_RADIO->BASE0 = bytewise_bit_swap(m_config_local.rx_address_p0[1] << 24 | m_config_local.rx_address_p0[2] << 16 | m_config_local.rx_address_p0[3] << 8 | m_config_local.rx_address_p0[4]); - NRF_RADIO->BASE1 = bytewise_bit_swap(m_config_local.rx_address_p1[1] << 24 | m_config_local.rx_address_p1[2] << 16 | m_config_local.rx_address_p1[3] << 8 | m_config_local.rx_address_p1[4]); -} - -static void initialize_fifos() -{ - m_tx_fifo.entry_point = 0; - m_tx_fifo.exit_point = 0; - m_tx_fifo.count = 0; - for(int i = 0; i < UESB_CORE_TX_FIFO_SIZE; i++) - { - m_tx_fifo.payload_ptr[i] = &m_tx_fifo_payload[i]; - } - - m_rx_fifo.entry_point = 0; - m_rx_fifo.exit_point = 0; - m_rx_fifo.count = 0; - for(int i = 0; i < UESB_CORE_RX_FIFO_SIZE; i++) - { - m_rx_fifo.payload_ptr[i] = &m_rx_fifo_payload[i]; - } -} - -static void tx_fifo_remove_last() -{ - if(m_tx_fifo.count > 0) - { - DISABLE_RF_IRQ; - m_tx_fifo.count--; - m_tx_fifo.exit_point++; - if(m_tx_fifo.exit_point >= UESB_CORE_TX_FIFO_SIZE) m_tx_fifo.exit_point = 0; - ENABLE_RF_IRQ; - } -} - -static bool rx_fifo_push_rfbuf(uint8_t pipe) -{ - if(m_rx_fifo.count < UESB_CORE_RX_FIFO_SIZE) - { - if(m_config_local.protocol == UESB_PROTOCOL_ESB_DPL) - { - if(m_rx_payload_buffer[0] > UESB_CORE_MAX_PAYLOAD_LENGTH) return false; - m_rx_fifo.payload_ptr[m_rx_fifo.entry_point]->length = m_rx_payload_buffer[0]; - } - else - { - m_rx_fifo.payload_ptr[m_rx_fifo.entry_point]->length = m_config_local.payload_length; - } - if(m_config_local.protocol == UESB_PROTOCOL_SB) - { - memcpy(m_rx_fifo.payload_ptr[m_rx_fifo.entry_point]->data, &m_rx_payload_buffer[0], m_rx_fifo.payload_ptr[m_rx_fifo.entry_point]->length); - } - else - { - memcpy(m_rx_fifo.payload_ptr[m_rx_fifo.entry_point]->data, &m_rx_payload_buffer[2], m_rx_fifo.payload_ptr[m_rx_fifo.entry_point]->length); - } - m_rx_fifo.payload_ptr[m_rx_fifo.entry_point]->pipe = pipe; - m_rx_fifo.payload_ptr[m_rx_fifo.entry_point]->rssi = NRF_RADIO->RSSISAMPLE; - if(++m_rx_fifo.entry_point >= UESB_CORE_RX_FIFO_SIZE) m_rx_fifo.entry_point = 0; - m_rx_fifo.count++; - return true; - } - return false; -} - -static void sys_timer_init() -{ - // Configure the system timer with a 1 MHz base frequency - UESB_SYS_TIMER->PRESCALER = 4; - UESB_SYS_TIMER->BITMODE = TIMER_BITMODE_BITMODE_16Bit; - UESB_SYS_TIMER->SHORTS = TIMER_SHORTS_COMPARE1_CLEAR_Msk | TIMER_SHORTS_COMPARE1_STOP_Msk; -} - -static void ppi_init() -{ - NRF_PPI->CH[UESB_PPI_TIMER_START].EEP = (uint32_t)&NRF_RADIO->EVENTS_READY; - NRF_PPI->CH[UESB_PPI_TIMER_START].TEP = (uint32_t)&UESB_SYS_TIMER->TASKS_START; - NRF_PPI->CH[UESB_PPI_TIMER_STOP].EEP = (uint32_t)&NRF_RADIO->EVENTS_ADDRESS; - NRF_PPI->CH[UESB_PPI_TIMER_STOP].TEP = (uint32_t)&UESB_SYS_TIMER->TASKS_STOP; - NRF_PPI->CH[UESB_PPI_RX_TIMEOUT].EEP = (uint32_t)&UESB_SYS_TIMER->EVENTS_COMPARE[0]; - NRF_PPI->CH[UESB_PPI_RX_TIMEOUT].TEP = (uint32_t)&NRF_RADIO->TASKS_DISABLE; - NRF_PPI->CH[UESB_PPI_TX_START].EEP = (uint32_t)&UESB_SYS_TIMER->EVENTS_COMPARE[1]; - NRF_PPI->CH[UESB_PPI_TX_START].TEP = (uint32_t)&NRF_RADIO->TASKS_TXEN; -} - -uint32_t uesb_init(uesb_config_t *parameters) -{ - if(m_uesb_mainstate != UESB_STATE_UNINITIALIZED) return UESB_ERROR_ALREADY_INITIALIZED; - m_event_handler = parameters->event_handler; - memcpy(&m_config_local, parameters, sizeof(uesb_config_t)); - - m_interrupt_flags = 0; - m_pid = 0; - m_last_rx_packet_pid = 0xFF; - m_last_rx_packet_crc = 0xFFFFFFFF; - - update_radio_parameters(); - - initialize_fifos(); - - sys_timer_init(); - - ppi_init(); - - NVIC_SetPriority(RADIO_IRQn, m_config_local.radio_irq_priority & 0x03); - - //m_uesb_initialized = true; - m_uesb_mainstate = UESB_STATE_IDLE; - - return UESB_SUCCESS; -} - -uint32_t uesb_disable(void) -{ - if(m_uesb_mainstate != UESB_STATE_IDLE) return UESB_ERROR_NOT_IDLE; - NRF_PPI->CHENCLR = (1 << UESB_PPI_TIMER_START) | (1 << UESB_PPI_TIMER_STOP) | (1 << UESB_PPI_RX_TIMEOUT) | (1 << UESB_PPI_TX_START); - m_uesb_mainstate = UESB_STATE_UNINITIALIZED; - return UESB_SUCCESS; -} - -static void start_tx_transaction() -{ - bool ack; - m_last_tx_attempts = 1; - // Prepare the payload - current_payload = m_tx_fifo.payload_ptr[m_tx_fifo.exit_point]; - m_pid = (m_pid + 1) % 4; - switch(m_config_local.protocol) - { - case UESB_PROTOCOL_SB: - update_rf_payload_format(current_payload->length); - memcpy(&m_tx_payload_buffer[0], current_payload->data, current_payload->length); - NRF_RADIO->SHORTS = RADIO_SHORTS_READY_START_Msk; - NRF_RADIO->INTENSET = RADIO_INTENSET_END_Msk; - on_radio_disabled = on_radio_disabled_esb_dpl_tx_noack; - m_uesb_mainstate = UESB_STATE_PTX_TX; - break; - - case UESB_PROTOCOL_ESB: - update_rf_payload_format(current_payload->length); - m_tx_payload_buffer[0] = 0xCC | m_pid; - m_tx_payload_buffer[1] = 0; - memcpy(&m_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 - m_retransmits_remaining = m_config_local.retransmit_count; - on_radio_disabled = on_radio_disabled_esb_dpl_tx; - m_uesb_mainstate = UESB_STATE_PTX_TX_ACK; - break; - - case UESB_PROTOCOL_ESB_DPL: - ack = current_payload->noack == 0 || m_config_local.dynamic_ack_enabled == 0; - m_tx_payload_buffer[0] = current_payload->length; - m_tx_payload_buffer[1] = m_pid << 1 | ((current_payload->noack == 0 && m_config_local.dynamic_ack_enabled) ? 0x01 : 0x00); - memcpy(&m_tx_payload_buffer[2], current_payload->data, current_payload->length); - 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 - m_retransmits_remaining = m_config_local.retransmit_count; - on_radio_disabled = on_radio_disabled_esb_dpl_tx; - m_uesb_mainstate = UESB_STATE_PTX_TX_ACK; - } - else - { - NRF_RADIO->SHORTS = RADIO_SHORTS_COMMON; - NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk; - on_radio_disabled = on_radio_disabled_esb_dpl_tx_noack; - m_uesb_mainstate = UESB_STATE_PTX_TX; - } - break; - } - - NRF_RADIO->TXADDRESS = current_payload->pipe; - NRF_RADIO->RXADDRESSES = 1 << current_payload->pipe; - - NRF_RADIO->FREQUENCY = m_config_local.rf_channel; - - NRF_RADIO->PACKETPTR = (uint32_t)m_tx_payload_buffer; - - NVIC_ClearPendingIRQ(RADIO_IRQn); - NVIC_EnableIRQ(RADIO_IRQn); - - NRF_RADIO->EVENTS_ADDRESS = NRF_RADIO->EVENTS_PAYLOAD = NRF_RADIO->EVENTS_DISABLED = 0; - DEBUG_PIN_SET(DEBUGPIN4); - NRF_RADIO->TASKS_TXEN = 1; -} - -static uint32_t write_tx_payload(uesb_payload_t *payload, bool noack) // ~50us @ 61 bytes SB -{ - if(m_uesb_mainstate == UESB_STATE_UNINITIALIZED) return UESB_ERROR_NOT_INITIALIZED; - if(m_tx_fifo.count >= UESB_CORE_TX_FIFO_SIZE) return UESB_ERROR_TX_FIFO_FULL; - - DISABLE_RF_IRQ; - if(noack && m_config_local.dynamic_ack_enabled) payload->noack = 1; - else payload->noack = 0; - memcpy(m_tx_fifo.payload_ptr[m_tx_fifo.entry_point], payload, sizeof(uesb_payload_t)); - m_tx_fifo.entry_point++; - if(m_tx_fifo.entry_point >= UESB_CORE_TX_FIFO_SIZE) m_tx_fifo.entry_point = 0; - m_tx_fifo.count++; - ENABLE_RF_IRQ; - - if(m_config_local.tx_mode == UESB_TXMODE_AUTO && m_uesb_mainstate == UESB_STATE_IDLE) - { - start_tx_transaction(); - } - - return UESB_SUCCESS; -} - -uint32_t uesb_write_tx_payload(uesb_payload_t *payload) -{ - return write_tx_payload(payload, false); -} - -uint32_t uesb_write_tx_payload_noack(uesb_payload_t *payload) -{ - if(m_config_local.dynamic_ack_enabled == 0) return UESB_ERROR_DYN_ACK_NOT_ENABLED; - return write_tx_payload(payload, true); -} - -uint32_t uesb_write_ack_payload(uesb_payload_t *payload) -{ - if(m_uesb_mainstate == UESB_STATE_UNINITIALIZED) return UESB_ERROR_NOT_INITIALIZED; - if((m_uesb_mainstate != UESB_STATE_PRX) && - (m_uesb_mainstate != UESB_STATE_PRX_SEND_ACK) && - (m_uesb_mainstate != UESB_STATE_PRX_SEND_ACK_PAYLOAD)) - { - return UESB_ERROR_NOT_IN_RX_MODE; - } - if(m_tx_fifo.count >= UESB_CORE_TX_FIFO_SIZE) return UESB_ERROR_TX_FIFO_FULL; - - DISABLE_RF_IRQ; - memcpy(m_tx_fifo.payload_ptr[m_tx_fifo.entry_point], payload, sizeof(uesb_payload_t)); - m_tx_fifo.entry_point++; - if(m_tx_fifo.entry_point >= UESB_CORE_TX_FIFO_SIZE) m_tx_fifo.entry_point = 0; - m_tx_fifo.count++; - ENABLE_RF_IRQ; - - return UESB_SUCCESS; -} - -uint32_t uesb_read_rx_payload(uesb_payload_t *payload) -{ - if(m_uesb_mainstate == UESB_STATE_UNINITIALIZED) return UESB_ERROR_NOT_INITIALIZED; - if(m_rx_fifo.count == 0) return UESB_ERROR_RX_FIFO_EMPTY; - - DISABLE_RF_IRQ; - payload->length = m_rx_fifo.payload_ptr[m_rx_fifo.exit_point]->length; - payload->pipe = m_rx_fifo.payload_ptr[m_rx_fifo.exit_point]->pipe; - payload->rssi = m_rx_fifo.payload_ptr[m_rx_fifo.exit_point]->rssi; - memcpy(payload->data, m_rx_fifo.payload_ptr[m_rx_fifo.exit_point]->data, payload->length); - if(++m_rx_fifo.exit_point >= UESB_CORE_RX_FIFO_SIZE) m_rx_fifo.exit_point = 0; - m_rx_fifo.count--; - ENABLE_RF_IRQ; - - return UESB_SUCCESS; -} - -uint32_t uesb_start_tx() -{ - if(m_uesb_mainstate != UESB_STATE_IDLE) return UESB_ERROR_NOT_IDLE; - if(m_tx_fifo.count == 0) return UESB_ERROR_TX_FIFO_EMPTY; - start_tx_transaction(); - return UESB_SUCCESS; -} - -uint32_t uesb_start_rx(void) -{ - if(m_uesb_mainstate != UESB_STATE_IDLE) return UESB_ERROR_NOT_IDLE; - - NRF_RADIO->INTENCLR = 0xFFFFFFFF; - NRF_RADIO->EVENTS_DISABLED = 0; - on_radio_disabled = on_radio_disabled_esb_dpl_rx; - switch(m_config_local.protocol) - { - case UESB_PROTOCOL_SB: - NRF_RADIO->SHORTS = RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_START_Msk; - NRF_RADIO->INTENSET = RADIO_INTENSET_END_Msk; - m_uesb_mainstate = UESB_STATE_PRX; - break; - case UESB_PROTOCOL_ESB: - NRF_RADIO->SHORTS = RADIO_SHORTS_COMMON | RADIO_SHORTS_DISABLED_TXEN_Msk; - NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk; - m_uesb_mainstate = UESB_STATE_PRX; - break; - case UESB_PROTOCOL_ESB_DPL: - NRF_RADIO->SHORTS = RADIO_SHORTS_COMMON | RADIO_SHORTS_DISABLED_TXEN_Msk; - NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk; - m_uesb_mainstate = UESB_STATE_PRX; - break; - } - - NRF_RADIO->RXADDRESSES = m_config_local.rx_pipes_enabled; - - NRF_RADIO->FREQUENCY = m_config_local.rf_channel; - - NRF_RADIO->PACKETPTR = (uint32_t)m_rx_payload_buffer; - - NVIC_ClearPendingIRQ(RADIO_IRQn); - NVIC_EnableIRQ(RADIO_IRQn); - - NRF_RADIO->EVENTS_ADDRESS = NRF_RADIO->EVENTS_PAYLOAD = NRF_RADIO->EVENTS_DISABLED = 0; - NRF_RADIO->TASKS_RXEN = 1; - return UESB_SUCCESS; -} - -uint32_t uesb_stop_rx(void) -{ - if((m_uesb_mainstate == UESB_STATE_PRX) || (m_uesb_mainstate == UESB_STATE_PRX_SEND_ACK_PAYLOAD)) - { - 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_uesb_mainstate = UESB_STATE_IDLE; - return UESB_SUCCESS; - } - return UESB_ERROR_NOT_IN_RX_MODE; -} - -uint32_t uesb_get_tx_attempts(uint32_t *attempts) -{ - if(m_uesb_mainstate == UESB_STATE_UNINITIALIZED) return UESB_ERROR_NOT_INITIALIZED; - *attempts = m_last_tx_attempts; - return UESB_SUCCESS; -} - -uint32_t uesb_flush_tx(void) -{ - if(m_uesb_mainstate != UESB_STATE_IDLE) return UESB_ERROR_NOT_IDLE; - DISABLE_RF_IRQ; - m_tx_fifo.count = 0; - m_tx_fifo.entry_point = m_tx_fifo.exit_point = 0; - ENABLE_RF_IRQ; - return UESB_SUCCESS; -} - -uint32_t uesb_flush_rx(void) -{ - DISABLE_RF_IRQ; - m_rx_fifo.count = 0; - m_rx_fifo.entry_point = 0; - ENABLE_RF_IRQ; - return UESB_SUCCESS; -} - -uint32_t uesb_get_clear_interrupts(uint32_t *interrupts) -{ - DISABLE_RF_IRQ; - *interrupts = m_interrupt_flags; - m_interrupt_flags = 0; - ENABLE_RF_IRQ; - return UESB_SUCCESS; -} - -uint32_t uesb_set_address(uesb_address_type_t address, const uint8_t *data_ptr) -{ - if(m_uesb_mainstate != UESB_STATE_IDLE) return UESB_ERROR_NOT_IDLE; - switch(address) - { - case UESB_ADDRESS_PIPE0: - memcpy(m_config_local.rx_address_p0, data_ptr, m_config_local.rf_addr_length); - break; - case UESB_ADDRESS_PIPE1: - memcpy(m_config_local.rx_address_p1, data_ptr, m_config_local.rf_addr_length); - break; - case UESB_ADDRESS_PIPE2: - m_config_local.rx_address_p2 = *data_ptr; - break; - case UESB_ADDRESS_PIPE3: - m_config_local.rx_address_p3 = *data_ptr; - break; - case UESB_ADDRESS_PIPE4: - m_config_local.rx_address_p4 = *data_ptr; - break; - case UESB_ADDRESS_PIPE5: - m_config_local.rx_address_p5 = *data_ptr; - break; - case UESB_ADDRESS_PIPE6: - m_config_local.rx_address_p6 = *data_ptr; - break; - case UESB_ADDRESS_PIPE7: - m_config_local.rx_address_p7 = *data_ptr; - break; - default: - return UESB_ERROR_INVALID_PARAMETERS; - } - update_radio_parameters(); - return UESB_SUCCESS; -} - -uint32_t uesb_set_rf_channel(uint32_t channel) -{ - if(channel > 125) return UESB_ERROR_INVALID_PARAMETERS; - m_config_local.rf_channel = channel; - return UESB_SUCCESS; -} - -uint32_t uesb_set_tx_power(uesb_tx_power_t tx_output_power) -{ - if(m_uesb_mainstate != UESB_STATE_IDLE) return UESB_ERROR_NOT_IDLE; - if ( m_config_local.tx_output_power == tx_output_power ) return UESB_SUCCESS; - m_config_local.tx_output_power = tx_output_power; - update_radio_parameters(); - return UESB_SUCCESS; -} - -void RADIO_IRQHandler() -{ - if(NRF_RADIO->EVENTS_READY && (NRF_RADIO->INTENSET & RADIO_INTENSET_READY_Msk)) - { - NRF_RADIO->EVENTS_READY = 0; - - DEBUG_PIN_SET(DEBUGPIN1); - } - - if(NRF_RADIO->EVENTS_END && (NRF_RADIO->INTENSET & RADIO_INTENSET_END_Msk)) - { - NRF_RADIO->EVENTS_END = 0; - - DEBUG_PIN_SET(DEBUGPIN2); - - // 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; - - DEBUG_PIN_SET(DEBUGPIN3); - - // Call the correct on_radio_disable function, depending on the current protocol state - if(on_radio_disabled) - { - on_radio_disabled(); - } - } - - DEBUG_PIN_CLR(DEBUGPIN1); - DEBUG_PIN_CLR(DEBUGPIN2); - DEBUG_PIN_CLR(DEBUGPIN3); - DEBUG_PIN_CLR(DEBUGPIN4); -} - -static void on_radio_disabled_esb_dpl_tx_noack() -{ - m_interrupt_flags |= UESB_INT_TX_SUCCESS_MSK; - tx_fifo_remove_last(); - - if(m_tx_fifo.count == 0) - { - m_uesb_mainstate = UESB_STATE_IDLE; - if(m_event_handler != 0) m_event_handler(); - } - else - { - if(m_event_handler != 0) m_event_handler(); - start_tx_transaction(); - } -} - -static void on_radio_disabled_esb_dpl_tx() -{ - // 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 m_wait_for_ack_timeout_us - UESB_SYS_TIMER->CC[0] = m_wait_for_ack_timeout_us; - UESB_SYS_TIMER->CC[1] = m_config_local.retransmit_delay - 130; - UESB_SYS_TIMER->TASKS_CLEAR = 1; - UESB_SYS_TIMER->EVENTS_COMPARE[0] = 0; - UESB_SYS_TIMER->EVENTS_COMPARE[1] = 0; - NRF_PPI->CHENSET = (1 << UESB_PPI_TIMER_START) | (1 << UESB_PPI_RX_TIMEOUT) | (1 << UESB_PPI_TIMER_STOP); - NRF_PPI->CHENCLR = (1 << UESB_PPI_TX_START); - NRF_RADIO->EVENTS_END = 0; - if(m_config_local.protocol == UESB_PROTOCOL_ESB) - { - update_rf_payload_format(0); - } - NRF_RADIO->PACKETPTR = (uint32_t)m_rx_payload_buffer; - on_radio_disabled = on_radio_disabled_esb_dpl_tx_wait_for_ack; - m_uesb_mainstate = UESB_STATE_PTX_RX_ACK; -} - -static void on_radio_disabled_esb_dpl_tx_wait_for_ack() -{ - // 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 - NRF_PPI->CHENCLR = (1 << UESB_PPI_TIMER_START) | (1 << UESB_PPI_RX_TIMEOUT) | (1 << UESB_PPI_TIMER_STOP); - - // If the radio has received a packet and the CRC status is OK - if(NRF_RADIO->EVENTS_END && NRF_RADIO->CRCSTATUS != 0) - { - UESB_SYS_TIMER->TASKS_STOP = 1; - NRF_PPI->CHENCLR = (1 << UESB_PPI_TX_START); - m_interrupt_flags |= UESB_INT_TX_SUCCESS_MSK; - m_last_tx_attempts = m_config_local.retransmit_count - m_retransmits_remaining + 1; - tx_fifo_remove_last(); - if(m_rx_payload_buffer[0] > 0) - { - if(rx_fifo_push_rfbuf((uint8_t)NRF_RADIO->TXADDRESS)) - { - m_interrupt_flags |= UESB_INT_RX_DR_MSK; - } - } - - if((m_tx_fifo.count == 0) || (m_config_local.tx_mode == UESB_TXMODE_MANUAL)) - { - m_uesb_mainstate = UESB_STATE_IDLE; - if(m_event_handler != 0) m_event_handler(); - } - else - { - if(m_event_handler != 0) m_event_handler(); - start_tx_transaction(); - } - } - else - { - if(m_retransmits_remaining-- == 0) - { - UESB_SYS_TIMER->TASKS_STOP = 1; - NRF_PPI->CHENCLR = (1 << UESB_PPI_TX_START); - // All retransmits are expended, and the TX operation is suspended - m_last_tx_attempts = m_config_local.retransmit_count + 1; - m_interrupt_flags |= UESB_INT_TX_FAILED_MSK; - - m_uesb_mainstate = UESB_STATE_IDLE; - if(m_event_handler != 0) m_event_handler(); - } - else - { - // We still have more retransmits left, and we should enter TX mode 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)m_tx_payload_buffer; - on_radio_disabled = on_radio_disabled_esb_dpl_tx; - m_uesb_mainstate = UESB_STATE_PTX_TX_ACK; - UESB_SYS_TIMER->TASKS_START = 1; - NRF_PPI->CHENSET = (1 << UESB_PPI_TX_START); - if(UESB_SYS_TIMER->EVENTS_COMPARE[1]) - { - NRF_RADIO->TASKS_TXEN = 1; - } - } - } -} - -static void on_radio_disabled_esb_dpl_rx(void) -{ - bool send_ack = false; - bool set_rx_interrupt = false; - if(NRF_RADIO->CRCSTATUS != 0 && m_rx_fifo.count < UESB_CORE_RX_FIFO_SIZE) - { - send_ack = true; - } - if(send_ack) - { - NRF_RADIO->SHORTS = RADIO_SHORTS_COMMON | RADIO_SHORTS_DISABLED_RXEN_Msk; - - // For a packet to be considered new (and not a retransmit) the PID or the CRC has to be different - if(NRF_RADIO->RXCRC != m_last_rx_packet_crc || (m_rx_payload_buffer[1] >> 1) != m_last_rx_packet_pid) - { - if((m_uesb_mainstate == UESB_STATE_PRX_SEND_ACK_PAYLOAD) && (m_tx_fifo.count > 0)) - { - // It is assumed that the last ACK payload was recieved. - if(++m_tx_fifo.exit_point >= UESB_CORE_RX_FIFO_SIZE) m_tx_fifo.exit_point = 0; - m_tx_fifo.count--; - - // ACK payloads also require TX_DS (page 40 of the 'nRF24LE1_Product_Specification_rev1_6.pdf'). - m_interrupt_flags |= UESB_INT_TX_SUCCESS_MSK; - } - - set_rx_interrupt = true; - m_last_rx_packet_pid = m_rx_payload_buffer[1] >> 1; - m_last_rx_packet_crc = NRF_RADIO->RXCRC; - } - - if(m_config_local.protocol == UESB_PROTOCOL_ESB_DPL) - { - if(m_tx_fifo.count > 0) - { - current_payload = m_tx_fifo.payload_ptr[m_tx_fifo.exit_point]; - - update_rf_payload_format(current_payload->length); - m_tx_payload_buffer[0] = current_payload->length; - memcpy(&m_tx_payload_buffer[2], current_payload->data, current_payload->length); - - m_uesb_mainstate = UESB_STATE_PRX_SEND_ACK_PAYLOAD; - } - else - { - update_rf_payload_format(0); - m_tx_payload_buffer[0] = 0; - - m_uesb_mainstate = UESB_STATE_PRX_SEND_ACK; - } - - m_tx_payload_buffer[1] = m_rx_payload_buffer[1]; - } - else if(m_config_local.protocol == UESB_PROTOCOL_ESB) - { - m_tx_payload_buffer[0] = m_rx_payload_buffer[0]; - m_tx_payload_buffer[1] = 0; - - m_uesb_mainstate = UESB_STATE_PRX_SEND_ACK; - } - - NRF_RADIO->TXADDRESS = NRF_RADIO->RXMATCH; - NRF_RADIO->PACKETPTR = (uint32_t)m_tx_payload_buffer; - - on_radio_disabled = on_radio_disabled_esb_dpl_rx_ack; - } - else - { - NRF_RADIO->SHORTS = RADIO_SHORTS_COMMON; - update_rf_payload_format(m_config_local.payload_length); - NRF_RADIO->PACKETPTR = (uint32_t)m_rx_payload_buffer; - NRF_RADIO->EVENTS_DISABLED = 0; - NRF_RADIO->TASKS_DISABLE = 1; - while(NRF_RADIO->EVENTS_DISABLED == 0); - NRF_RADIO->EVENTS_DISABLED = 0; - NRF_RADIO->SHORTS = RADIO_SHORTS_COMMON | RADIO_SHORTS_DISABLED_TXEN_Msk; - NRF_RADIO->TASKS_RXEN = 1; - } - if(set_rx_interrupt) - { - rx_fifo_push_rfbuf(NRF_RADIO->RXMATCH); - m_interrupt_flags |= UESB_INT_RX_DR_MSK; - if(m_event_handler != 0) m_event_handler(); - } -} - -static void on_radio_disabled_esb_dpl_rx_ack(void) -{ - NRF_RADIO->SHORTS = RADIO_SHORTS_COMMON | RADIO_SHORTS_DISABLED_TXEN_Msk; - update_rf_payload_format(m_config_local.payload_length); - NRF_RADIO->PACKETPTR = (uint32_t)m_rx_payload_buffer; - on_radio_disabled = on_radio_disabled_esb_dpl_rx; - - if(m_uesb_mainstate == UESB_STATE_PRX_SEND_ACK) - { - // In the case of UESB_STATE_PRX_SEND_ACK_PAYLOAD the state will be updated when the next packet is received. - m_uesb_mainstate = UESB_STATE_PRX; - } -} - -static void on_radio_end_sb_tx(void) -{ - m_interrupt_flags |= UESB_INT_TX_SUCCESS_MSK; - tx_fifo_remove_last(); - if(m_config_local.tx_mode == UESB_TXMODE_MANUAL || m_tx_fifo.count == 0) - { - // No more packets to send. Disable the radio and set the state to idle. - NRF_RADIO->EVENTS_DISABLED = 0; - NRF_RADIO->TASKS_DISABLE = 1; - while(!NRF_RADIO->EVENTS_DISABLED); - NRF_RADIO->EVENTS_DISABLED = 0; - m_uesb_mainstate = UESB_STATE_IDLE; - if(m_event_handler != 0) m_event_handler(); - } - else - { - // Send another packet automatically without disabling the radio first. - current_payload = m_tx_fifo.payload_ptr[m_tx_fifo.exit_point]; - - update_rf_payload_format(current_payload->length); - memcpy(&m_tx_payload_buffer[0], current_payload->data, current_payload->length); - - NRF_RADIO->TXADDRESS = current_payload->pipe; - - NVIC_ClearPendingIRQ(RADIO_IRQn); - NVIC_EnableIRQ(RADIO_IRQn); - - NRF_RADIO->EVENTS_ADDRESS = NRF_RADIO->EVENTS_PAYLOAD = 0; - NRF_RADIO->TASKS_START = 1; - - } -} - -static void on_radio_end_sb_rx(void) -{ - if(NRF_RADIO->CRCSTATUS != 0 && rx_fifo_push_rfbuf(NRF_RADIO->RXMATCH)) - { - m_interrupt_flags |= UESB_INT_RX_DR_MSK; - if(m_event_handler != 0) m_event_handler(); - } -}
diff -r a01a54c0dc90 -r 66f95e364222 micro_esb.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/micro_esb.cpp Thu Feb 04 10:36:44 2021 +0000 @@ -0,0 +1,876 @@ +/* Copyright (c) 2014 Nordic Semiconductor. All Rights Reserved. + * + * The information contained herein is property of Nordic Semiconductor ASA. + * Terms and conditions of usage are described in detail in NORDIC + * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT. + * + * Licensees are granted free, non-transferable use of the information. NO + * WARRANTY of ANY KIND is provided. This heading must NOT be removed from + * the file. + * + */ + +#include "micro_esb.h" +#include "uesb_error_codes.h" +//#include "nrf_gpio.h" +#include <string.h> + + +static uesb_event_handler_t m_event_handler; + +// RF parameters +static uesb_config_t m_config_local; + +// TX FIFO +static uesb_payload_t m_tx_fifo_payload[UESB_CORE_TX_FIFO_SIZE]; +static uesb_payload_tx_fifo_t m_tx_fifo; + +// RX FIFO +static uesb_payload_t m_rx_fifo_payload[UESB_CORE_RX_FIFO_SIZE]; +static uesb_payload_rx_fifo_t m_rx_fifo; + +static uint8_t m_tx_payload_buffer[UESB_CORE_MAX_PAYLOAD_LENGTH + 2]; +static uint8_t m_rx_payload_buffer[UESB_CORE_MAX_PAYLOAD_LENGTH + 2]; + +// Run time variables +static volatile uint32_t m_interrupt_flags = 0; +static uint32_t m_pid = 0; +static volatile uint32_t m_retransmits_remaining; +static volatile uint32_t m_last_tx_attempts; +static volatile uint8_t m_last_rx_packet_pid = 0xFF; +static volatile uint32_t m_last_rx_packet_crc = 0xFFFFFFFF; +static volatile uint32_t m_wait_for_ack_timeout_us; + +static uesb_payload_t *current_payload; + +static uesb_mainstate_t m_uesb_mainstate = UESB_STATE_UNINITIALIZED; + +// Constant parameters +#define RX_WAIT_FOR_ACK_TIMEOUT_US_2MBPS 48 // Smallest reliable value - 43 +#define RX_WAIT_FOR_ACK_TIMEOUT_US_1MBPS 64 // Smallest reliable value - 59 +#define RX_WAIT_FOR_ACK_TIMEOUT_US_250KBPS 250 + +// Macros +#define DISABLE_RF_IRQ NVIC_DisableIRQ(RADIO_IRQn) +#define ENABLE_RF_IRQ NVIC_EnableIRQ(RADIO_IRQn) + +#define RADIO_SHORTS_COMMON ( RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_DISABLE_Msk | \ + RADIO_SHORTS_ADDRESS_RSSISTART_Msk | RADIO_SHORTS_DISABLED_RSSISTOP_Msk ) + +// These function pointers are changed dynamically, depending on protocol configuration and state +static void (*on_radio_disabled)(void) = 0; +static void (*on_radio_end)(void) = 0; +static void (*update_rf_payload_format)(uint32_t payload_length) = 0; + +// The following functions are assigned to the function pointers above +static void on_radio_disabled_esb_dpl_tx_noack(void); +static void on_radio_disabled_esb_dpl_tx(void); +static void on_radio_disabled_esb_dpl_tx_wait_for_ack(void); +static void on_radio_disabled_esb_dpl_rx(void); +static void on_radio_disabled_esb_dpl_rx_ack(void); + +static void on_radio_end_sb_tx(void); +static void on_radio_end_sb_rx(void); + +static void update_rf_payload_format_esb_dpl(uint32_t payload_length) +{ +#if(UESB_CORE_MAX_PAYLOAD_LENGTH <= 32) + NRF_RADIO->PCNF0 = (0 << RADIO_PCNF0_S0LEN_Pos) | (6 << RADIO_PCNF0_LFLEN_Pos) | (3 << RADIO_PCNF0_S1LEN_Pos); +#else + 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) | + ((m_config_local.rf_addr_length - 1) << RADIO_PCNF1_BALEN_Pos) | + (0 << RADIO_PCNF1_STATLEN_Pos) | + (UESB_CORE_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) | + ((m_config_local.rf_addr_length - 1) << RADIO_PCNF1_BALEN_Pos) | + (payload_length << RADIO_PCNF1_STATLEN_Pos) | + (payload_length << RADIO_PCNF1_MAXLEN_Pos); +} + +static void update_rf_payload_format_sb(uint32_t payload_length) +{ + NRF_RADIO->PCNF0 = (0 << RADIO_PCNF0_S0LEN_Pos) | (0 << RADIO_PCNF0_LFLEN_Pos) | (0 << RADIO_PCNF0_S1LEN_Pos); + NRF_RADIO->PCNF1 = (RADIO_PCNF1_WHITEEN_Disabled << RADIO_PCNF1_WHITEEN_Pos) | + (RADIO_PCNF1_ENDIAN_Big << RADIO_PCNF1_ENDIAN_Pos) | + ((m_config_local.rf_addr_length - 1) << RADIO_PCNF1_BALEN_Pos) | + (payload_length << RADIO_PCNF1_STATLEN_Pos) | + (payload_length << RADIO_PCNF1_MAXLEN_Pos); +} + +// Function that swaps the bits within each byte in a uint32. Used to convert from nRF24L type addressing to nRF51 type addressing +static uint32_t bytewise_bit_swap(uint32_t inp) +{ + inp = (inp & 0xF0F0F0F0) >> 4 | (inp & 0x0F0F0F0F) << 4; + inp = (inp & 0xCCCCCCCC) >> 2 | (inp & 0x33333333) << 2; + return (inp & 0xAAAAAAAA) >> 1 | (inp & 0x55555555) << 1; +} + +static void update_radio_parameters() +{ + // Protocol + switch(m_config_local.protocol) + { + case UESB_PROTOCOL_ESB_DPL: + update_rf_payload_format = update_rf_payload_format_esb_dpl; + break; + case UESB_PROTOCOL_ESB: + update_rf_payload_format = update_rf_payload_format_esb; + break; + case UESB_PROTOCOL_SB: + update_rf_payload_format = update_rf_payload_format_sb; + on_radio_end = (m_config_local.mode == UESB_MODE_PTX ? on_radio_end_sb_tx : on_radio_end_sb_rx); + break; + } + // TX power + NRF_RADIO->TXPOWER = m_config_local.tx_output_power << RADIO_TXPOWER_TXPOWER_Pos; + + // RF bitrate + NRF_RADIO->MODE = m_config_local.bitrate << RADIO_MODE_MODE_Pos; + switch(m_config_local.bitrate) + { + case UESB_BITRATE_2MBPS: + m_wait_for_ack_timeout_us = RX_WAIT_FOR_ACK_TIMEOUT_US_2MBPS; + break; + case UESB_BITRATE_1MBPS: + m_wait_for_ack_timeout_us = RX_WAIT_FOR_ACK_TIMEOUT_US_1MBPS; + break; + case UESB_BITRATE_250KBPS: + m_wait_for_ack_timeout_us = RX_WAIT_FOR_ACK_TIMEOUT_US_250KBPS; + break; + } + + // CRC configuration + NRF_RADIO->CRCCNF = m_config_local.crc << RADIO_CRCCNF_LEN_Pos; + if(m_config_local.crc == RADIO_CRCCNF_LEN_Two) + { + NRF_RADIO->CRCINIT = 0xFFFFUL; // Initial value + NRF_RADIO->CRCPOLY = 0x11021UL; // CRC poly: x^16+x^12^x^5+1 + } + else if(m_config_local.crc == RADIO_CRCCNF_LEN_One) + { + NRF_RADIO->CRCINIT = 0xFFUL; // Initial value + NRF_RADIO->CRCPOLY = 0x107UL; // CRC poly: x^8+x^2^x^1+1 + } + + // Packet format + update_rf_payload_format(m_config_local.payload_length); + + // Radio address config + NRF_RADIO->PREFIX0 = bytewise_bit_swap(m_config_local.rx_address_p3 << 24 | m_config_local.rx_address_p2 << 16 | m_config_local.rx_address_p1[0] << 8 | m_config_local.rx_address_p0[0]); + NRF_RADIO->PREFIX1 = bytewise_bit_swap(m_config_local.rx_address_p7 << 24 | m_config_local.rx_address_p6 << 16 | m_config_local.rx_address_p5 << 8 | m_config_local.rx_address_p4); + NRF_RADIO->BASE0 = bytewise_bit_swap(m_config_local.rx_address_p0[1] << 24 | m_config_local.rx_address_p0[2] << 16 | m_config_local.rx_address_p0[3] << 8 | m_config_local.rx_address_p0[4]); + NRF_RADIO->BASE1 = bytewise_bit_swap(m_config_local.rx_address_p1[1] << 24 | m_config_local.rx_address_p1[2] << 16 | m_config_local.rx_address_p1[3] << 8 | m_config_local.rx_address_p1[4]); +} + +static void initialize_fifos() +{ + m_tx_fifo.entry_point = 0; + m_tx_fifo.exit_point = 0; + m_tx_fifo.count = 0; + for(int i = 0; i < UESB_CORE_TX_FIFO_SIZE; i++) + { + m_tx_fifo.payload_ptr[i] = &m_tx_fifo_payload[i]; + } + + m_rx_fifo.entry_point = 0; + m_rx_fifo.exit_point = 0; + m_rx_fifo.count = 0; + for(int i = 0; i < UESB_CORE_RX_FIFO_SIZE; i++) + { + m_rx_fifo.payload_ptr[i] = &m_rx_fifo_payload[i]; + } +} + +static void tx_fifo_remove_last() +{ + if(m_tx_fifo.count > 0) + { + DISABLE_RF_IRQ; + m_tx_fifo.count--; + m_tx_fifo.exit_point++; + if(m_tx_fifo.exit_point >= UESB_CORE_TX_FIFO_SIZE) m_tx_fifo.exit_point = 0; + ENABLE_RF_IRQ; + } +} + +static bool rx_fifo_push_rfbuf(uint8_t pipe) +{ + if(m_rx_fifo.count < UESB_CORE_RX_FIFO_SIZE) + { + if(m_config_local.protocol == UESB_PROTOCOL_ESB_DPL) + { + if(m_rx_payload_buffer[0] > UESB_CORE_MAX_PAYLOAD_LENGTH) return false; + m_rx_fifo.payload_ptr[m_rx_fifo.entry_point]->length = m_rx_payload_buffer[0]; + } + else + { + m_rx_fifo.payload_ptr[m_rx_fifo.entry_point]->length = m_config_local.payload_length; + } + if(m_config_local.protocol == UESB_PROTOCOL_SB) + { + memcpy(m_rx_fifo.payload_ptr[m_rx_fifo.entry_point]->data, &m_rx_payload_buffer[0], m_rx_fifo.payload_ptr[m_rx_fifo.entry_point]->length); + } + else + { + memcpy(m_rx_fifo.payload_ptr[m_rx_fifo.entry_point]->data, &m_rx_payload_buffer[2], m_rx_fifo.payload_ptr[m_rx_fifo.entry_point]->length); + } + m_rx_fifo.payload_ptr[m_rx_fifo.entry_point]->pipe = pipe; + m_rx_fifo.payload_ptr[m_rx_fifo.entry_point]->rssi = NRF_RADIO->RSSISAMPLE; + if(++m_rx_fifo.entry_point >= UESB_CORE_RX_FIFO_SIZE) m_rx_fifo.entry_point = 0; + m_rx_fifo.count++; + return true; + } + return false; +} + +static void sys_timer_init() +{ + // Configure the system timer with a 1 MHz base frequency + UESB_SYS_TIMER->PRESCALER = 4; + UESB_SYS_TIMER->BITMODE = TIMER_BITMODE_BITMODE_16Bit; + UESB_SYS_TIMER->SHORTS = TIMER_SHORTS_COMPARE1_CLEAR_Msk | TIMER_SHORTS_COMPARE1_STOP_Msk; +} + +static void ppi_init() +{ + NRF_PPI->CH[UESB_PPI_TIMER_START].EEP = (uint32_t)&NRF_RADIO->EVENTS_READY; + NRF_PPI->CH[UESB_PPI_TIMER_START].TEP = (uint32_t)&UESB_SYS_TIMER->TASKS_START; + NRF_PPI->CH[UESB_PPI_TIMER_STOP].EEP = (uint32_t)&NRF_RADIO->EVENTS_ADDRESS; + NRF_PPI->CH[UESB_PPI_TIMER_STOP].TEP = (uint32_t)&UESB_SYS_TIMER->TASKS_STOP; + NRF_PPI->CH[UESB_PPI_RX_TIMEOUT].EEP = (uint32_t)&UESB_SYS_TIMER->EVENTS_COMPARE[0]; + NRF_PPI->CH[UESB_PPI_RX_TIMEOUT].TEP = (uint32_t)&NRF_RADIO->TASKS_DISABLE; + NRF_PPI->CH[UESB_PPI_TX_START].EEP = (uint32_t)&UESB_SYS_TIMER->EVENTS_COMPARE[1]; + NRF_PPI->CH[UESB_PPI_TX_START].TEP = (uint32_t)&NRF_RADIO->TASKS_TXEN; +} + +uint32_t uesb_read_rx_payload(uesb_payload_t *payload) +{ + if(m_uesb_mainstate == UESB_STATE_UNINITIALIZED) return UESB_ERROR_NOT_INITIALIZED; + if(m_rx_fifo.count == 0) return UESB_ERROR_RX_FIFO_EMPTY; + + DISABLE_RF_IRQ; + payload->length = m_rx_fifo.payload_ptr[m_rx_fifo.exit_point]->length; + payload->pipe = m_rx_fifo.payload_ptr[m_rx_fifo.exit_point]->pipe; + payload->rssi = m_rx_fifo.payload_ptr[m_rx_fifo.exit_point]->rssi; + memcpy(payload->data, m_rx_fifo.payload_ptr[m_rx_fifo.exit_point]->data, payload->length); + if(++m_rx_fifo.exit_point >= UESB_CORE_RX_FIFO_SIZE) m_rx_fifo.exit_point = 0; + m_rx_fifo.count--; + ENABLE_RF_IRQ; + + return UESB_SUCCESS; +} + + +uint32_t uesb_init(uesb_config_t *parameters) +{ + if(m_uesb_mainstate != UESB_STATE_UNINITIALIZED) return UESB_ERROR_ALREADY_INITIALIZED; + m_event_handler = parameters->event_handler; + memcpy(&m_config_local, parameters, sizeof(uesb_config_t)); + + m_interrupt_flags = 0; + m_pid = 0; + m_last_rx_packet_pid = 0xFF; + m_last_rx_packet_crc = 0xFFFFFFFF; + + update_radio_parameters(); + + initialize_fifos(); + + sys_timer_init(); + + ppi_init(); + + NVIC_SetPriority(RADIO_IRQn, m_config_local.radio_irq_priority & 0x03); + + //m_uesb_initialized = true; + m_uesb_mainstate = UESB_STATE_IDLE; + + return UESB_SUCCESS; +} + +uint32_t uesb_disable(void) +{ + if(m_uesb_mainstate != UESB_STATE_IDLE) return UESB_ERROR_NOT_IDLE; + NRF_PPI->CHENCLR = (1 << UESB_PPI_TIMER_START) | (1 << UESB_PPI_TIMER_STOP) | (1 << UESB_PPI_RX_TIMEOUT) | (1 << UESB_PPI_TX_START); + m_uesb_mainstate = UESB_STATE_UNINITIALIZED; + return UESB_SUCCESS; +} + +static void start_tx_transaction() +{ + bool ack; + m_last_tx_attempts = 1; + // Prepare the payload + current_payload = m_tx_fifo.payload_ptr[m_tx_fifo.exit_point]; + m_pid = (m_pid + 1) % 4; + switch(m_config_local.protocol) + { + case UESB_PROTOCOL_SB: + update_rf_payload_format(current_payload->length); + memcpy(&m_tx_payload_buffer[0], current_payload->data, current_payload->length); + NRF_RADIO->SHORTS = RADIO_SHORTS_READY_START_Msk; + NRF_RADIO->INTENSET = RADIO_INTENSET_END_Msk; + on_radio_disabled = on_radio_disabled_esb_dpl_tx_noack; + m_uesb_mainstate = UESB_STATE_PTX_TX; + break; + + case UESB_PROTOCOL_ESB: + update_rf_payload_format(current_payload->length); + m_tx_payload_buffer[0] = 0xCC | m_pid; + m_tx_payload_buffer[1] = 0; + memcpy(&m_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 + m_retransmits_remaining = m_config_local.retransmit_count; + on_radio_disabled = on_radio_disabled_esb_dpl_tx; + m_uesb_mainstate = UESB_STATE_PTX_TX_ACK; + break; + + case UESB_PROTOCOL_ESB_DPL: + ack = current_payload->noack == 0 || m_config_local.dynamic_ack_enabled == 0; + m_tx_payload_buffer[0] = current_payload->length; + m_tx_payload_buffer[1] = m_pid << 1 | ((current_payload->noack == 0 && m_config_local.dynamic_ack_enabled) ? 0x01 : 0x00); + memcpy(&m_tx_payload_buffer[2], current_payload->data, current_payload->length); + 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 + m_retransmits_remaining = m_config_local.retransmit_count; + on_radio_disabled = on_radio_disabled_esb_dpl_tx; + m_uesb_mainstate = UESB_STATE_PTX_TX_ACK; + } + else + { + NRF_RADIO->SHORTS = RADIO_SHORTS_COMMON; + NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk; + on_radio_disabled = on_radio_disabled_esb_dpl_tx_noack; + m_uesb_mainstate = UESB_STATE_PTX_TX; + } + break; + } + + NRF_RADIO->TXADDRESS = current_payload->pipe; + NRF_RADIO->RXADDRESSES = 1 << current_payload->pipe; + + NRF_RADIO->FREQUENCY = m_config_local.rf_channel; + + NRF_RADIO->PACKETPTR = (uint32_t)m_tx_payload_buffer; + + NVIC_ClearPendingIRQ(RADIO_IRQn); + NVIC_EnableIRQ(RADIO_IRQn); + + NRF_RADIO->EVENTS_ADDRESS = NRF_RADIO->EVENTS_PAYLOAD = NRF_RADIO->EVENTS_DISABLED = 0; + DEBUG_PIN_SET(DEBUGPIN4); + NRF_RADIO->TASKS_TXEN = 1; +} + +static uint32_t write_tx_payload(uesb_payload_t *payload, bool noack) // ~50us @ 61 bytes SB +{ + if(m_uesb_mainstate == UESB_STATE_UNINITIALIZED) return UESB_ERROR_NOT_INITIALIZED; + if(m_tx_fifo.count >= UESB_CORE_TX_FIFO_SIZE) return UESB_ERROR_TX_FIFO_FULL; + + DISABLE_RF_IRQ; + if(noack && m_config_local.dynamic_ack_enabled) payload->noack = 1; + else payload->noack = 0; + memcpy(m_tx_fifo.payload_ptr[m_tx_fifo.entry_point], payload, sizeof(uesb_payload_t)); + m_tx_fifo.entry_point++; + if(m_tx_fifo.entry_point >= UESB_CORE_TX_FIFO_SIZE) m_tx_fifo.entry_point = 0; + m_tx_fifo.count++; + ENABLE_RF_IRQ; + + if(m_config_local.tx_mode == UESB_TXMODE_AUTO && m_uesb_mainstate == UESB_STATE_IDLE) + { + start_tx_transaction(); + } + + return UESB_SUCCESS; +} + +uint32_t uesb_write_tx_payload(uesb_payload_t *payload) +{ + return write_tx_payload(payload, false); +} + +uint32_t uesb_write_tx_payload_noack(uesb_payload_t *payload) +{ + if(m_config_local.dynamic_ack_enabled == 0) return UESB_ERROR_DYN_ACK_NOT_ENABLED; + return write_tx_payload(payload, true); +} + +uint32_t uesb_write_ack_payload(uesb_payload_t *payload) +{ + if(m_uesb_mainstate == UESB_STATE_UNINITIALIZED) return UESB_ERROR_NOT_INITIALIZED; + if((m_uesb_mainstate != UESB_STATE_PRX) && + (m_uesb_mainstate != UESB_STATE_PRX_SEND_ACK) && + (m_uesb_mainstate != UESB_STATE_PRX_SEND_ACK_PAYLOAD)) + { + return UESB_ERROR_NOT_IN_RX_MODE; + } + if(m_tx_fifo.count >= UESB_CORE_TX_FIFO_SIZE) return UESB_ERROR_TX_FIFO_FULL; + + DISABLE_RF_IRQ; + memcpy(m_tx_fifo.payload_ptr[m_tx_fifo.entry_point], payload, sizeof(uesb_payload_t)); + m_tx_fifo.entry_point++; + if(m_tx_fifo.entry_point >= UESB_CORE_TX_FIFO_SIZE) m_tx_fifo.entry_point = 0; + m_tx_fifo.count++; + ENABLE_RF_IRQ; + + return UESB_SUCCESS; +} + + +uint32_t uesb_start_tx() +{ + if(m_uesb_mainstate != UESB_STATE_IDLE) return UESB_ERROR_NOT_IDLE; + if(m_tx_fifo.count == 0) return UESB_ERROR_TX_FIFO_EMPTY; + start_tx_transaction(); + return UESB_SUCCESS; +} + +uint32_t uesb_start_rx(void) +{ + if(m_uesb_mainstate != UESB_STATE_IDLE) return UESB_ERROR_NOT_IDLE; + + NRF_RADIO->INTENCLR = 0xFFFFFFFF; + NRF_RADIO->EVENTS_DISABLED = 0; + on_radio_disabled = on_radio_disabled_esb_dpl_rx; + switch(m_config_local.protocol) + { + case UESB_PROTOCOL_SB: + NRF_RADIO->SHORTS = RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_START_Msk; + NRF_RADIO->INTENSET = RADIO_INTENSET_END_Msk; + m_uesb_mainstate = UESB_STATE_PRX; + break; + case UESB_PROTOCOL_ESB: + NRF_RADIO->SHORTS = RADIO_SHORTS_COMMON | RADIO_SHORTS_DISABLED_TXEN_Msk; + NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk; + m_uesb_mainstate = UESB_STATE_PRX; + break; + case UESB_PROTOCOL_ESB_DPL: + NRF_RADIO->SHORTS = RADIO_SHORTS_COMMON | RADIO_SHORTS_DISABLED_TXEN_Msk; + NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk; + m_uesb_mainstate = UESB_STATE_PRX; + break; + } + + NRF_RADIO->RXADDRESSES = m_config_local.rx_pipes_enabled; + + NRF_RADIO->FREQUENCY = m_config_local.rf_channel; + + NRF_RADIO->PACKETPTR = (uint32_t)m_rx_payload_buffer; + + NVIC_ClearPendingIRQ(RADIO_IRQn); + NVIC_EnableIRQ(RADIO_IRQn); + + NRF_RADIO->EVENTS_ADDRESS = NRF_RADIO->EVENTS_PAYLOAD = NRF_RADIO->EVENTS_DISABLED = 0; + NRF_RADIO->TASKS_RXEN = 1; + return UESB_SUCCESS; +} + +uint32_t uesb_stop_rx(void) +{ + if((m_uesb_mainstate == UESB_STATE_PRX) || (m_uesb_mainstate == UESB_STATE_PRX_SEND_ACK_PAYLOAD)) + { + 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_uesb_mainstate = UESB_STATE_IDLE; + return UESB_SUCCESS; + } + return UESB_ERROR_NOT_IN_RX_MODE; +} + +uint32_t uesb_get_tx_attempts(uint32_t *attempts) +{ + if(m_uesb_mainstate == UESB_STATE_UNINITIALIZED) return UESB_ERROR_NOT_INITIALIZED; + *attempts = m_last_tx_attempts; + return UESB_SUCCESS; +} + +uint32_t uesb_flush_tx(void) +{ + if(m_uesb_mainstate != UESB_STATE_IDLE) return UESB_ERROR_NOT_IDLE; + DISABLE_RF_IRQ; + m_tx_fifo.count = 0; + m_tx_fifo.entry_point = m_tx_fifo.exit_point = 0; + ENABLE_RF_IRQ; + return UESB_SUCCESS; +} + +uint32_t uesb_flush_rx(void) +{ + DISABLE_RF_IRQ; + m_rx_fifo.count = 0; + m_rx_fifo.entry_point = 0; + ENABLE_RF_IRQ; + return UESB_SUCCESS; +} + +uint32_t uesb_get_clear_interrupts(uint32_t *interrupts) +{ + DISABLE_RF_IRQ; + *interrupts = m_interrupt_flags; + m_interrupt_flags = 0; + ENABLE_RF_IRQ; + return UESB_SUCCESS; +} + +uint32_t uesb_set_address(uesb_address_type_t address, const uint8_t *data_ptr) +{ + if(m_uesb_mainstate != UESB_STATE_IDLE) return UESB_ERROR_NOT_IDLE; + switch(address) + { + case UESB_ADDRESS_PIPE0: + memcpy(m_config_local.rx_address_p0, data_ptr, m_config_local.rf_addr_length); + break; + case UESB_ADDRESS_PIPE1: + memcpy(m_config_local.rx_address_p1, data_ptr, m_config_local.rf_addr_length); + break; + case UESB_ADDRESS_PIPE2: + m_config_local.rx_address_p2 = *data_ptr; + break; + case UESB_ADDRESS_PIPE3: + m_config_local.rx_address_p3 = *data_ptr; + break; + case UESB_ADDRESS_PIPE4: + m_config_local.rx_address_p4 = *data_ptr; + break; + case UESB_ADDRESS_PIPE5: + m_config_local.rx_address_p5 = *data_ptr; + break; + case UESB_ADDRESS_PIPE6: + m_config_local.rx_address_p6 = *data_ptr; + break; + case UESB_ADDRESS_PIPE7: + m_config_local.rx_address_p7 = *data_ptr; + break; + default: + return UESB_ERROR_INVALID_PARAMETERS; + } + update_radio_parameters(); + return UESB_SUCCESS; +} + +uint32_t uesb_set_rf_channel(uint32_t channel) +{ + if(channel > 125) return UESB_ERROR_INVALID_PARAMETERS; + m_config_local.rf_channel = channel; + return UESB_SUCCESS; +} + +uint32_t uesb_set_tx_power(uesb_tx_power_t tx_output_power) +{ + if(m_uesb_mainstate != UESB_STATE_IDLE) return UESB_ERROR_NOT_IDLE; + if ( m_config_local.tx_output_power == tx_output_power ) return UESB_SUCCESS; + m_config_local.tx_output_power = tx_output_power; + update_radio_parameters(); + return UESB_SUCCESS; +} + +/* + +void RADIO_IRQHandler() +{ + if(NRF_RADIO->EVENTS_READY && (NRF_RADIO->INTENSET & RADIO_INTENSET_READY_Msk)) + { + NRF_RADIO->EVENTS_READY = 0; + + DEBUG_PIN_SET(DEBUGPIN1); + } + + if(NRF_RADIO->EVENTS_END && (NRF_RADIO->INTENSET & RADIO_INTENSET_END_Msk)) + { + NRF_RADIO->EVENTS_END = 0; + + DEBUG_PIN_SET(DEBUGPIN2); + + // 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; + + DEBUG_PIN_SET(DEBUGPIN3); + + // Call the correct on_radio_disable function, depending on the current protocol state + if(on_radio_disabled) + { + on_radio_disabled(); + } + } + + DEBUG_PIN_CLR(DEBUGPIN1); + DEBUG_PIN_CLR(DEBUGPIN2); + DEBUG_PIN_CLR(DEBUGPIN3); + DEBUG_PIN_CLR(DEBUGPIN4); +} */ + +static void on_radio_disabled_esb_dpl_tx_noack() +{ + m_interrupt_flags |= UESB_INT_TX_SUCCESS_MSK; + tx_fifo_remove_last(); + + if(m_tx_fifo.count == 0) + { + m_uesb_mainstate = UESB_STATE_IDLE; + if(m_event_handler != 0) m_event_handler(); + } + else + { + if(m_event_handler != 0) m_event_handler(); + start_tx_transaction(); + } +} + +static void on_radio_disabled_esb_dpl_tx() +{ + // 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 m_wait_for_ack_timeout_us + UESB_SYS_TIMER->CC[0] = m_wait_for_ack_timeout_us; + UESB_SYS_TIMER->CC[1] = m_config_local.retransmit_delay - 130; + UESB_SYS_TIMER->TASKS_CLEAR = 1; + UESB_SYS_TIMER->EVENTS_COMPARE[0] = 0; + UESB_SYS_TIMER->EVENTS_COMPARE[1] = 0; + NRF_PPI->CHENSET = (1 << UESB_PPI_TIMER_START) | (1 << UESB_PPI_RX_TIMEOUT) | (1 << UESB_PPI_TIMER_STOP); + NRF_PPI->CHENCLR = (1 << UESB_PPI_TX_START); + NRF_RADIO->EVENTS_END = 0; + if(m_config_local.protocol == UESB_PROTOCOL_ESB) + { + update_rf_payload_format(0); + } + NRF_RADIO->PACKETPTR = (uint32_t)m_rx_payload_buffer; + on_radio_disabled = on_radio_disabled_esb_dpl_tx_wait_for_ack; + m_uesb_mainstate = UESB_STATE_PTX_RX_ACK; +} + +static void on_radio_disabled_esb_dpl_tx_wait_for_ack() +{ + // 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 + NRF_PPI->CHENCLR = (1 << UESB_PPI_TIMER_START) | (1 << UESB_PPI_RX_TIMEOUT) | (1 << UESB_PPI_TIMER_STOP); + + // If the radio has received a packet and the CRC status is OK + if(NRF_RADIO->EVENTS_END && NRF_RADIO->CRCSTATUS != 0) + { + UESB_SYS_TIMER->TASKS_STOP = 1; + NRF_PPI->CHENCLR = (1 << UESB_PPI_TX_START); + m_interrupt_flags |= UESB_INT_TX_SUCCESS_MSK; + m_last_tx_attempts = m_config_local.retransmit_count - m_retransmits_remaining + 1; + tx_fifo_remove_last(); + if(m_rx_payload_buffer[0] > 0) + { + if(rx_fifo_push_rfbuf((uint8_t)NRF_RADIO->TXADDRESS)) + { + m_interrupt_flags |= UESB_INT_RX_DR_MSK; + } + } + + if((m_tx_fifo.count == 0) || (m_config_local.tx_mode == UESB_TXMODE_MANUAL)) + { + m_uesb_mainstate = UESB_STATE_IDLE; + if(m_event_handler != 0) m_event_handler(); + } + else + { + if(m_event_handler != 0) m_event_handler(); + start_tx_transaction(); + } + } + else + { + if(m_retransmits_remaining-- == 0) + { + UESB_SYS_TIMER->TASKS_STOP = 1; + NRF_PPI->CHENCLR = (1 << UESB_PPI_TX_START); + // All retransmits are expended, and the TX operation is suspended + m_last_tx_attempts = m_config_local.retransmit_count + 1; + m_interrupt_flags |= UESB_INT_TX_FAILED_MSK; + + m_uesb_mainstate = UESB_STATE_IDLE; + if(m_event_handler != 0) m_event_handler(); + } + else + { + // We still have more retransmits left, and we should enter TX mode 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)m_tx_payload_buffer; + on_radio_disabled = on_radio_disabled_esb_dpl_tx; + m_uesb_mainstate = UESB_STATE_PTX_TX_ACK; + UESB_SYS_TIMER->TASKS_START = 1; + NRF_PPI->CHENSET = (1 << UESB_PPI_TX_START); + if(UESB_SYS_TIMER->EVENTS_COMPARE[1]) + { + NRF_RADIO->TASKS_TXEN = 1; + } + } + } +} + +static void on_radio_disabled_esb_dpl_rx(void) +{ + bool send_ack = false; + bool set_rx_interrupt = false; + if(NRF_RADIO->CRCSTATUS != 0 && m_rx_fifo.count < UESB_CORE_RX_FIFO_SIZE) + { + send_ack = true; + } + if(send_ack) + { + NRF_RADIO->SHORTS = RADIO_SHORTS_COMMON | RADIO_SHORTS_DISABLED_RXEN_Msk; + + // For a packet to be considered new (and not a retransmit) the PID or the CRC has to be different + if(NRF_RADIO->RXCRC != m_last_rx_packet_crc || (m_rx_payload_buffer[1] >> 1) != m_last_rx_packet_pid) + { + if((m_uesb_mainstate == UESB_STATE_PRX_SEND_ACK_PAYLOAD) && (m_tx_fifo.count > 0)) + { + // It is assumed that the last ACK payload was recieved. + if(++m_tx_fifo.exit_point >= UESB_CORE_RX_FIFO_SIZE) m_tx_fifo.exit_point = 0; + m_tx_fifo.count--; + + // ACK payloads also require TX_DS (page 40 of the 'nRF24LE1_Product_Specification_rev1_6.pdf'). + m_interrupt_flags |= UESB_INT_TX_SUCCESS_MSK; + } + + set_rx_interrupt = true; + m_last_rx_packet_pid = m_rx_payload_buffer[1] >> 1; + m_last_rx_packet_crc = NRF_RADIO->RXCRC; + } + + if(m_config_local.protocol == UESB_PROTOCOL_ESB_DPL) + { + if(m_tx_fifo.count > 0) + { + current_payload = m_tx_fifo.payload_ptr[m_tx_fifo.exit_point]; + + update_rf_payload_format(current_payload->length); + m_tx_payload_buffer[0] = current_payload->length; + memcpy(&m_tx_payload_buffer[2], current_payload->data, current_payload->length); + + m_uesb_mainstate = UESB_STATE_PRX_SEND_ACK_PAYLOAD; + } + else + { + update_rf_payload_format(0); + m_tx_payload_buffer[0] = 0; + + m_uesb_mainstate = UESB_STATE_PRX_SEND_ACK; + } + + m_tx_payload_buffer[1] = m_rx_payload_buffer[1]; + } + else if(m_config_local.protocol == UESB_PROTOCOL_ESB) + { + m_tx_payload_buffer[0] = m_rx_payload_buffer[0]; + m_tx_payload_buffer[1] = 0; + + m_uesb_mainstate = UESB_STATE_PRX_SEND_ACK; + } + + NRF_RADIO->TXADDRESS = NRF_RADIO->RXMATCH; + NRF_RADIO->PACKETPTR = (uint32_t)m_tx_payload_buffer; + + on_radio_disabled = on_radio_disabled_esb_dpl_rx_ack; + } + else + { + NRF_RADIO->SHORTS = RADIO_SHORTS_COMMON; + update_rf_payload_format(m_config_local.payload_length); + NRF_RADIO->PACKETPTR = (uint32_t)m_rx_payload_buffer; + NRF_RADIO->EVENTS_DISABLED = 0; + NRF_RADIO->TASKS_DISABLE = 1; + while(NRF_RADIO->EVENTS_DISABLED == 0); + NRF_RADIO->EVENTS_DISABLED = 0; + NRF_RADIO->SHORTS = RADIO_SHORTS_COMMON | RADIO_SHORTS_DISABLED_TXEN_Msk; + NRF_RADIO->TASKS_RXEN = 1; + } + if(set_rx_interrupt) + { + rx_fifo_push_rfbuf(NRF_RADIO->RXMATCH); + m_interrupt_flags |= UESB_INT_RX_DR_MSK; + if(m_event_handler != 0) m_event_handler(); + } +} + +static void on_radio_disabled_esb_dpl_rx_ack(void) +{ + NRF_RADIO->SHORTS = RADIO_SHORTS_COMMON | RADIO_SHORTS_DISABLED_TXEN_Msk; + update_rf_payload_format(m_config_local.payload_length); + NRF_RADIO->PACKETPTR = (uint32_t)m_rx_payload_buffer; + on_radio_disabled = on_radio_disabled_esb_dpl_rx; + + if(m_uesb_mainstate == UESB_STATE_PRX_SEND_ACK) + { + // In the case of UESB_STATE_PRX_SEND_ACK_PAYLOAD the state will be updated when the next packet is received. + m_uesb_mainstate = UESB_STATE_PRX; + } +} + +static void on_radio_end_sb_tx(void) +{ + m_interrupt_flags |= UESB_INT_TX_SUCCESS_MSK; + tx_fifo_remove_last(); + if(m_config_local.tx_mode == UESB_TXMODE_MANUAL || m_tx_fifo.count == 0) + { + // No more packets to send. Disable the radio and set the state to idle. + NRF_RADIO->EVENTS_DISABLED = 0; + NRF_RADIO->TASKS_DISABLE = 1; + while(!NRF_RADIO->EVENTS_DISABLED); + NRF_RADIO->EVENTS_DISABLED = 0; + m_uesb_mainstate = UESB_STATE_IDLE; + if(m_event_handler != 0) m_event_handler(); + } + else + { + // Send another packet automatically without disabling the radio first. + current_payload = m_tx_fifo.payload_ptr[m_tx_fifo.exit_point]; + + update_rf_payload_format(current_payload->length); + memcpy(&m_tx_payload_buffer[0], current_payload->data, current_payload->length); + + NRF_RADIO->TXADDRESS = current_payload->pipe; + + NVIC_ClearPendingIRQ(RADIO_IRQn); + NVIC_EnableIRQ(RADIO_IRQn); + + NRF_RADIO->EVENTS_ADDRESS = NRF_RADIO->EVENTS_PAYLOAD = 0; + NRF_RADIO->TASKS_START = 1; + + } +} + +static void on_radio_end_sb_rx(void) +{ + if(NRF_RADIO->CRCSTATUS != 0 && rx_fifo_push_rfbuf(NRF_RADIO->RXMATCH)) + { + m_interrupt_flags |= UESB_INT_RX_DR_MSK; + if(m_event_handler != 0) m_event_handler(); + } +} +
diff -r a01a54c0dc90 -r 66f95e364222 micro_esb.h --- a/micro_esb.h Mon Mar 23 04:09:41 2015 +0000 +++ b/micro_esb.h Thu Feb 04 10:36:44 2021 +0000 @@ -16,8 +16,9 @@ #include <stdbool.h> #include <stdint.h> //#include "nrf.h" -#include "nrf51.h" -#include "nrf51_bitfields.h" +//#include "nrf51.h" +//#include "nrf51_bitfields.h" +#include "nrf_esb.h" #define DEBUGPIN1 12 #define DEBUGPIN2 13 @@ -62,11 +63,17 @@ UESB_MODE_PRX // Primary receiver (CURRENTLY NOT IMPLEMENTED) } uesb_mode_t; -typedef enum { +/* typedef enum { UESB_BITRATE_2MBPS = RADIO_MODE_MODE_Nrf_2Mbit, UESB_BITRATE_1MBPS = RADIO_MODE_MODE_Nrf_1Mbit, UESB_BITRATE_250KBPS = RADIO_MODE_MODE_Nrf_250Kbit -} uesb_bitrate_t; +} uesb_bitrate_t; */ + +typedef enum { + UESB_BITRATE_2MBPS = NRF_ESB_BITRATE_1MBPS, + UESB_BITRATE_1MBPS = NRF_ESB_BITRATE_2MBPS, + UESB_BITRATE_250KBPS = NRF_ESB_BITRATE_250KBPS +} uesb_bitrate_t; typedef enum { UESB_CRC_16BIT = RADIO_CRCCNF_LEN_Two,
diff -r a01a54c0dc90 -r 66f95e364222 nrf_esb.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nrf_esb.c Thu Feb 04 10:36:44 2021 +0000 @@ -0,0 +1,1604 @@ +/** + * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "nrf_error.h" +#include "nrf_esb.h" +#include "nrf_esb_error_codes.h" +#include "nrf_gpio.h" +#include <string.h> +#include <stddef.h> +#include "sdk_common.h" +#include "sdk_macros.h" +#include "app_util.h" +#include "nrf_log.h" +#include "nrf_delay.h" + +#define BIT_MASK_UINT_8(x) (0xFF >> (8 - (x))) +#define NRF_ESB_PIPE_COUNT 8 + +// Constant parameters +#define RX_WAIT_FOR_ACK_TIMEOUT_US_2MBPS (48) /**< 2 Mb RX wait for acknowledgment time-out value. Smallest reliable value - 43. */ +#define RX_WAIT_FOR_ACK_TIMEOUT_US_1MBPS (64) /**< 1 Mb RX wait for acknowledgment time-out value. Smallest reliable value - 59. */ +#define RX_WAIT_FOR_ACK_TIMEOUT_US_250KBPS (250) /**< 250 Kb RX wait for acknowledgment time-out value. */ +#define RX_WAIT_FOR_ACK_TIMEOUT_US_1MBPS_BLE (73) /**< 1 Mb RX wait for acknowledgment time-out (combined with BLE). Smallest reliable value - 68.*/ + +// Interrupt flags +#define NRF_ESB_INT_TX_SUCCESS_MSK 0x01 /**< Interrupt mask value for TX success. */ +#define NRF_ESB_INT_TX_FAILED_MSK 0x02 /**< Interrupt mask value for TX failure. */ +#define NRF_ESB_INT_RX_DATA_RECEIVED_MSK 0x04 /**< Interrupt mask value for RX_DR. */ + +#define NRF_ESB_PID_RESET_VALUE 0xFF /**< Invalid PID value which is guaranteed to not collide with any valid PID value. */ +#define NRF_ESB_PID_MAX 3 /**< Maximum value for PID. */ +#define NRF_ESB_CRC_RESET_VALUE 0xFFFF /**< CRC reset value. */ + +// Internal Enhanced ShockBurst module state. +typedef enum { + NRF_ESB_STATE_IDLE, /**< Module idle. */ + NRF_ESB_STATE_PTX_TX, /**< Module transmitting without acknowledgment. */ + NRF_ESB_STATE_PTX_TX_ACK, /**< Module transmitting with acknowledgment. */ + NRF_ESB_STATE_PTX_RX_ACK, /**< Module transmitting with acknowledgment and reception of payload with the acknowledgment response. */ + NRF_ESB_STATE_PRX, /**< Module receiving packets without acknowledgment. */ + NRF_ESB_STATE_PRX_SEND_ACK, /**< Module transmitting acknowledgment in RX mode. */ +} nrf_esb_mainstate_t; + + +#define DISABLE_RF_IRQ() NVIC_DisableIRQ(RADIO_IRQn) +#define ENABLE_RF_IRQ() NVIC_EnableIRQ(RADIO_IRQn) + +#define _RADIO_SHORTS_COMMON ( RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_DISABLE_Msk | \ + RADIO_SHORTS_ADDRESS_RSSISTART_Msk | RADIO_SHORTS_DISABLED_RSSISTOP_Msk ) + +#define VERIFY_PAYLOAD_LENGTH(p) \ +do \ +{ \ + if (p->length == 0 || \ + p->length > NRF_ESB_MAX_PAYLOAD_LENGTH || \ + (m_config_local.protocol == NRF_ESB_PROTOCOL_ESB && \ + p->length > m_config_local.payload_length)) \ + { \ + return NRF_ERROR_INVALID_LENGTH; \ + } \ +}while (0) + + +/* @brief Structure holding pipe info PID and CRC and acknowledgment payload. */ +typedef struct +{ + uint16_t crc; /**< CRC value 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; /**< Flag indicating the state of the transmission of acknowledgment payloads. */ +} pipe_info_t; + + +/* @brief First-in, first-out queue of payloads to be transmitted. */ +typedef struct +{ + nrf_esb_payload_t * p_payload[NRF_ESB_TX_FIFO_SIZE]; /**< Pointer to the actual queue. */ + uint32_t entry_point; /**< Current start of queue. */ + uint32_t exit_point; /**< Current end of queue. */ + uint32_t count; /**< Current number of elements in the queue. */ +} nrf_esb_payload_tx_fifo_t; + + +/* @brief First-in, first-out queue of received payloads. */ +typedef struct +{ + nrf_esb_payload_t * p_payload[NRF_ESB_RX_FIFO_SIZE]; /**< Pointer to the actual queue. */ + uint32_t entry_point; /**< Current start of queue. */ + uint32_t exit_point; /**< Current end of queue. */ + uint32_t count; /**< Current number of elements in the queue. */ +} nrf_esb_payload_rx_fifo_t; + + +/**@brief 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. +*/ +typedef struct +{ + uint8_t base_addr_p0[4]; /**< Base address for pipe 0 encoded in big endian. */ + uint8_t base_addr_p1[4]; /**< Base address for pipe 1-7 encoded 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 including the prefix. */ + uint8_t rx_pipes_enabled; /**< Bitfield for enabled pipes. */ + uint8_t rf_channel; /**< Channel to use (must be between 0 and 100). */ +} nrf_esb_address_t; + + +// Module state +static bool m_esb_initialized = false; +static nrf_esb_mainstate_t m_nrf_esb_mainstate = NRF_ESB_STATE_IDLE; +static nrf_esb_payload_t * mp_current_payload; + +static nrf_esb_event_handler_t m_event_handler; + +// Address parameters +__ALIGN(4) static nrf_esb_address_t m_esb_addr = NRF_ESB_ADDR_DEFAULT; + +// RF parameters +static nrf_esb_config_t m_config_local; + +// TX FIFO +static nrf_esb_payload_t m_tx_fifo_payload[NRF_ESB_TX_FIFO_SIZE]; +static nrf_esb_payload_tx_fifo_t m_tx_fifo; + +// RX FIFO +static nrf_esb_payload_t m_rx_fifo_payload[NRF_ESB_RX_FIFO_SIZE]; +static nrf_esb_payload_rx_fifo_t m_rx_fifo; + +// Payload buffers +static uint8_t m_tx_payload_buffer[NRF_ESB_MAX_PAYLOAD_LENGTH + 2]; +static uint8_t m_rx_payload_buffer[NRF_ESB_MAX_PAYLOAD_LENGTH + 2]; + +// Run time variables +static volatile uint32_t m_interrupt_flags = 0; +static uint8_t m_pids[NRF_ESB_PIPE_COUNT]; +static pipe_info_t m_rx_pipe_info[NRF_ESB_PIPE_COUNT]; +static volatile uint32_t m_retransmits_remaining; +static volatile uint32_t m_last_tx_attempts; +static volatile uint32_t m_wait_for_ack_timeout_us; + +// nRF52 address workaround enable +#ifdef NRF52 +static bool m_address_hang_fix_enable = true; +#endif +static uint32_t m_radio_shorts_common = _RADIO_SHORTS_COMMON; + +// These function pointers are changed dynamically, depending on protocol configuration and state. +static void (*on_radio_disabled)(void) = 0; +static void (*on_radio_end)(void) = 0; +static void (*update_rf_payload_format)(uint32_t payload_length) = 0; + + +// 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); + + +#define NRF_ESB_ADDR_UPDATE_MASK_BASE0 (1 << 0) /*< Mask value to signal updating BASE0 radio address. */ +#define NRF_ESB_ADDR_UPDATE_MASK_BASE1 (1 << 1) /*< Mask value to signal updating BASE1 radio address. */ +#define NRF_ESB_ADDR_UPDATE_MASK_PREFIX (1 << 2) /*< Mask value to signal updating radio prefixes. */ + + +// Function to do bytewise bit-swap on an unsigned 32-bit value +static uint32_t bytewise_bit_swap(uint8_t const * p_inp) +{ + uint32_t inp = (*(uint32_t*)p_inp); +#if __CORTEX_M == (0x04U) + return __REV((uint32_t)__RBIT(inp)); //lint -esym(628, __rev) -esym(526, __rev) -esym(628, __rbit) -esym(526, __rbit) */ +#else + inp = (inp & 0xF0F0F0F0) >> 4 | (inp & 0x0F0F0F0F) << 4; + inp = (inp & 0xCCCCCCCC) >> 2 | (inp & 0x33333333) << 2; + inp = (inp & 0xAAAAAAAA) >> 1 | (inp & 0x55555555) << 1; + return inp; +#endif +} + + +// Internal function to convert base addresses from nRF24L type addressing to nRF51 type addressing +static uint32_t addr_conv(uint8_t const* p_addr) +{ + return __REV(bytewise_bit_swap(p_addr)); //lint -esym(628, __rev) -esym(526, __rev) */ +} + + +static ret_code_t apply_address_workarounds() +{ +#ifdef NRF52 + // Set up radio parameters. + NRF_RADIO->MODECNF0 = (NRF_RADIO->MODECNF0 & ~RADIO_MODECNF0_RU_Msk) | RADIO_MODECNF0_RU_Default << RADIO_MODECNF0_RU_Pos; + + // Workaround for nRF52832 Rev 1 Errata 102 and nRF52832 Rev 1 Errata 106. This will reduce sensitivity by 3dB. + *((volatile uint32_t *)0x40001774) = (*((volatile uint32_t *)0x40001774) & 0xFFFFFFFE) | 0x01000000; +#endif + return NRF_SUCCESS; +} + + +static void update_rf_payload_format_esb_dpl(uint32_t payload_length) +{ +#if (NRF_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) | + ((m_esb_addr.addr_length - 1) << RADIO_PCNF1_BALEN_Pos) | + (0 << RADIO_PCNF1_STATLEN_Pos) | + (NRF_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) | + ((m_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 & NRF_ESB_ADDR_UPDATE_MASK_BASE0) != 0) + { + NRF_RADIO->BASE0 = addr_conv(m_esb_addr.base_addr_p0); + } + + if ((update_mask & NRF_ESB_ADDR_UPDATE_MASK_BASE1) != 0) + { + NRF_RADIO->BASE1 = addr_conv(m_esb_addr.base_addr_p1); + } + + if ((update_mask & NRF_ESB_ADDR_UPDATE_MASK_PREFIX) != 0) + { + NRF_RADIO->PREFIX0 = bytewise_bit_swap(&m_esb_addr.pipe_prefixes[0]); + NRF_RADIO->PREFIX1 = bytewise_bit_swap(&m_esb_addr.pipe_prefixes[4]); + } +} + + +static void update_radio_tx_power() +{ + NRF_RADIO->TXPOWER = m_config_local.tx_output_power << RADIO_TXPOWER_TXPOWER_Pos; +} + + +static bool update_radio_bitrate() +{ + NRF_RADIO->MODE = m_config_local.bitrate << RADIO_MODE_MODE_Pos; + + switch (m_config_local.bitrate) + { + case NRF_ESB_BITRATE_2MBPS: +#ifdef NRF52 + case NRF_ESB_BITRATE_2MBPS_BLE: +#endif + m_wait_for_ack_timeout_us = RX_WAIT_FOR_ACK_TIMEOUT_US_2MBPS; + break; + + case NRF_ESB_BITRATE_1MBPS: + m_wait_for_ack_timeout_us = RX_WAIT_FOR_ACK_TIMEOUT_US_1MBPS; + break; + +#ifdef NRF51 + case NRF_ESB_BITRATE_250KBPS: + m_wait_for_ack_timeout_us = RX_WAIT_FOR_ACK_TIMEOUT_US_250KBPS; + break; +#endif + + case NRF_ESB_BITRATE_1MBPS_BLE: + m_wait_for_ack_timeout_us = RX_WAIT_FOR_ACK_TIMEOUT_US_1MBPS_BLE; + break; + + default: + // Should not be reached + return false; + } + return true; +} + + +static bool update_radio_protocol() +{ + switch (m_config_local.protocol) + { + case NRF_ESB_PROTOCOL_ESB_DPL: + update_rf_payload_format = update_rf_payload_format_esb_dpl; + break; + + case NRF_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() +{ + switch(m_config_local.crc) + { + case NRF_ESB_CRC_16BIT: + NRF_RADIO->CRCINIT = 0xFFFFUL; // Initial value + NRF_RADIO->CRCPOLY = 0x11021UL; // CRC poly: x^16+x^12^x^5+1 + break; + + case NRF_ESB_CRC_8BIT: + NRF_RADIO->CRCINIT = 0xFFUL; // Initial value + NRF_RADIO->CRCPOLY = 0x107UL; // CRC poly: x^8+x^2^x^1+1 + break; + + case NRF_ESB_CRC_OFF: + break; + + default: + return false; + } + NRF_RADIO->CRCCNF = m_config_local.crc << RADIO_CRCCNF_LEN_Pos; + return true; +} + + +static bool update_radio_parameters() +{ + 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(m_config_local.payload_length); + params_valid &= (m_config_local.retransmit_delay >= NRF_ESB_RETRANSMIT_DELAY_MIN); + return params_valid; +} + + +static void reset_fifos() +{ + m_tx_fifo.entry_point = 0; + m_tx_fifo.exit_point = 0; + m_tx_fifo.count = 0; + + m_rx_fifo.entry_point = 0; + m_rx_fifo.exit_point = 0; + m_rx_fifo.count = 0; +} + + +static void initialize_fifos() +{ + reset_fifos(); + + for (int i = 0; i < NRF_ESB_TX_FIFO_SIZE; i++) + { + m_tx_fifo.p_payload[i] = &m_tx_fifo_payload[i]; + } + + for (int i = 0; i < NRF_ESB_RX_FIFO_SIZE; i++) + { + m_rx_fifo.p_payload[i] = &m_rx_fifo_payload[i]; + } +} + + +static void tx_fifo_remove_last() +{ + if (m_tx_fifo.count > 0) + { + DISABLE_RF_IRQ(); + + m_tx_fifo.count--; + if (++m_tx_fifo.exit_point >= NRF_ESB_TX_FIFO_SIZE) + { + m_tx_fifo.exit_point = 0; + } + + ENABLE_RF_IRQ(); + } +} + +/** @brief 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 (m_rx_fifo.count < NRF_ESB_RX_FIFO_SIZE) + { + if (m_config_local.protocol == NRF_ESB_PROTOCOL_ESB_DPL) + { + if (m_rx_payload_buffer[0] > NRF_ESB_MAX_PAYLOAD_LENGTH) + { + return false; + } + + m_rx_fifo.p_payload[m_rx_fifo.entry_point]->length = m_rx_payload_buffer[0]; + } + else if (m_config_local.mode == NRF_ESB_MODE_PTX) + { + // Received packet is an acknowledgment + m_rx_fifo.p_payload[m_rx_fifo.entry_point]->length = 0; + } + else + { + m_rx_fifo.p_payload[m_rx_fifo.entry_point]->length = m_config_local.payload_length; + } + + memcpy(m_rx_fifo.p_payload[m_rx_fifo.entry_point]->data, &m_rx_payload_buffer[2], + m_rx_fifo.p_payload[m_rx_fifo.entry_point]->length); + + m_rx_fifo.p_payload[m_rx_fifo.entry_point]->pipe = pipe; + m_rx_fifo.p_payload[m_rx_fifo.entry_point]->rssi = NRF_RADIO->RSSISAMPLE; + m_rx_fifo.p_payload[m_rx_fifo.entry_point]->pid = pid; + m_rx_fifo.p_payload[m_rx_fifo.entry_point]->noack = !(m_rx_payload_buffer[1] & 0x01); + if (++m_rx_fifo.entry_point >= NRF_ESB_RX_FIFO_SIZE) + { + m_rx_fifo.entry_point = 0; + } + m_rx_fifo.count++; + + return true; + } + + return false; +} + + +static void sys_timer_init() +{ + // Configure the system timer with a 1 MHz base frequency + NRF_ESB_SYS_TIMER->PRESCALER = 4; + NRF_ESB_SYS_TIMER->BITMODE = TIMER_BITMODE_BITMODE_16Bit; + NRF_ESB_SYS_TIMER->SHORTS = TIMER_SHORTS_COMPARE1_CLEAR_Msk | TIMER_SHORTS_COMPARE1_STOP_Msk; +} + + +static void ppi_init() +{ + NRF_PPI->CH[NRF_ESB_PPI_TIMER_START].EEP = (uint32_t)&NRF_RADIO->EVENTS_READY; + NRF_PPI->CH[NRF_ESB_PPI_TIMER_START].TEP = (uint32_t)&NRF_ESB_SYS_TIMER->TASKS_START; + + NRF_PPI->CH[NRF_ESB_PPI_TIMER_STOP].EEP = (uint32_t)&NRF_RADIO->EVENTS_ADDRESS; + NRF_PPI->CH[NRF_ESB_PPI_TIMER_STOP].TEP = (uint32_t)&NRF_ESB_SYS_TIMER->TASKS_STOP; + + NRF_PPI->CH[NRF_ESB_PPI_RX_TIMEOUT].EEP = (uint32_t)&NRF_ESB_SYS_TIMER->EVENTS_COMPARE[0]; + NRF_PPI->CH[NRF_ESB_PPI_RX_TIMEOUT].TEP = (uint32_t)&NRF_RADIO->TASKS_DISABLE; + + NRF_PPI->CH[NRF_ESB_PPI_TX_START].EEP = (uint32_t)&NRF_ESB_SYS_TIMER->EVENTS_COMPARE[1]; + NRF_PPI->CH[NRF_ESB_PPI_TX_START].TEP = (uint32_t)&NRF_RADIO->TASKS_TXEN; +} + + +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: + update_rf_payload_format(mp_current_payload->length); + m_tx_payload_buffer[0] = mp_current_payload->pid; + 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; + + // 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; + 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; +} + + +static void on_radio_disabled_tx_noack() +{ + m_interrupt_flags |= NRF_ESB_INT_TX_SUCCESS_MSK; + tx_fifo_remove_last(); + + 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(); + } +} + + +static void on_radio_disabled_tx() +{ + // Remove the DISABLED -> RXEN shortcut, to make sure the radio stays + // disabled after the RX window + NRF_RADIO->SHORTS = m_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 m_wait_for_ack_timeout_us + NRF_ESB_SYS_TIMER->CC[0] = m_wait_for_ack_timeout_us; + NRF_ESB_SYS_TIMER->CC[1] = m_config_local.retransmit_delay - 130; + NRF_ESB_SYS_TIMER->TASKS_CLEAR = 1; + NRF_ESB_SYS_TIMER->EVENTS_COMPARE[0] = 0; + NRF_ESB_SYS_TIMER->EVENTS_COMPARE[1] = 0; + + NRF_PPI->CHENSET = (1 << NRF_ESB_PPI_TIMER_START) | + (1 << NRF_ESB_PPI_RX_TIMEOUT) | + (1 << NRF_ESB_PPI_TIMER_STOP); + NRF_PPI->CHENCLR = (1 << NRF_ESB_PPI_TX_START); + NRF_RADIO->EVENTS_END = 0; + + if (m_config_local.protocol == NRF_ESB_PROTOCOL_ESB) + { + update_rf_payload_format(0); + } + + NRF_RADIO->PACKETPTR = (uint32_t)m_rx_payload_buffer; + on_radio_disabled = on_radio_disabled_tx_wait_for_ack; + m_nrf_esb_mainstate = NRF_ESB_STATE_PTX_RX_ACK; +} + + +static void on_radio_disabled_tx_wait_for_ack() +{ + // 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 + NRF_PPI->CHENCLR = (1 << NRF_ESB_PPI_TIMER_START) | + (1 << NRF_ESB_PPI_RX_TIMEOUT) | + (1 << NRF_ESB_PPI_TIMER_STOP); + + // If the radio has received a packet and the CRC status is OK + if (NRF_RADIO->EVENTS_END && NRF_RADIO->CRCSTATUS != 0) + { + NRF_ESB_SYS_TIMER->TASKS_STOP = 1; + NRF_PPI->CHENCLR = (1 << NRF_ESB_PPI_TX_START); + m_interrupt_flags |= NRF_ESB_INT_TX_SUCCESS_MSK; + m_last_tx_attempts = m_config_local.retransmit_count - m_retransmits_remaining + 1; + + tx_fifo_remove_last(); + + if (m_config_local.protocol != NRF_ESB_PROTOCOL_ESB && m_rx_payload_buffer[0] > 0) + { + if (rx_fifo_push_rfbuf((uint8_t)NRF_RADIO->TXADDRESS, m_rx_payload_buffer[1] >> 1)) + { + m_interrupt_flags |= NRF_ESB_INT_RX_DATA_RECEIVED_MSK; + } + } + + if ((m_tx_fifo.count == 0) || (m_config_local.tx_mode == NRF_ESB_TXMODE_MANUAL)) + { + m_nrf_esb_mainstate = NRF_ESB_STATE_IDLE; + NVIC_SetPendingIRQ(ESB_EVT_IRQ); + } + else + { + NVIC_SetPendingIRQ(ESB_EVT_IRQ); + start_tx_transaction(); + } + } + else + { + if (m_retransmits_remaining-- == 0) + { + NRF_ESB_SYS_TIMER->TASKS_STOP = 1; + NRF_PPI->CHENCLR = (1 << NRF_ESB_PPI_TX_START); + // All retransmits are expended, and the TX operation is suspended + m_last_tx_attempts = m_config_local.retransmit_count + 1; + m_interrupt_flags |= NRF_ESB_INT_TX_FAILED_MSK; + + m_nrf_esb_mainstate = NRF_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 = m_radio_shorts_common | RADIO_SHORTS_DISABLED_RXEN_Msk; + update_rf_payload_format(mp_current_payload->length); + NRF_RADIO->PACKETPTR = (uint32_t)m_tx_payload_buffer; + on_radio_disabled = on_radio_disabled_tx; + m_nrf_esb_mainstate = NRF_ESB_STATE_PTX_TX_ACK; + NRF_ESB_SYS_TIMER->TASKS_START = 1; + NRF_PPI->CHENSET = (1 << NRF_ESB_PPI_TX_START); + if (NRF_ESB_SYS_TIMER->EVENTS_COMPARE[1]) + { + NRF_RADIO->TASKS_TXEN = 1; + } + } + } +} + +static void clear_events_restart_rx(void) +{ + NRF_RADIO->SHORTS = m_radio_shorts_common; + update_rf_payload_format(m_config_local.payload_length); + NRF_RADIO->PACKETPTR = (uint32_t)m_rx_payload_buffer; + NRF_RADIO->EVENTS_DISABLED = 0; + NRF_RADIO->TASKS_DISABLE = 1; + + while (NRF_RADIO->EVENTS_DISABLED == 0); + + NRF_RADIO->EVENTS_DISABLED = 0; + NRF_RADIO->SHORTS = m_radio_shorts_common | RADIO_SHORTS_DISABLED_TXEN_Msk; + + NRF_RADIO->TASKS_RXEN = 1; +} + +static void on_radio_disabled_rx(void) +{ + bool ack = false; + bool retransmit_payload = false; + bool send_rx_event = true; + pipe_info_t * p_pipe_info; + + if (NRF_RADIO->CRCSTATUS == 0) + { + clear_events_restart_rx(); + return; + } + + if (m_rx_fifo.count >= NRF_ESB_RX_FIFO_SIZE) + { + clear_events_restart_rx(); + return; + } + + p_pipe_info = &m_rx_pipe_info[NRF_RADIO->RXMATCH]; + if (NRF_RADIO->RXCRC == p_pipe_info->crc && + (m_rx_payload_buffer[1] >> 1) == p_pipe_info->pid + ) + { + retransmit_payload = true; + send_rx_event = false; + } + + p_pipe_info->pid = m_rx_payload_buffer[1] >> 1; + p_pipe_info->crc = NRF_RADIO->RXCRC; + + if ((m_config_local.selective_auto_ack == false) || ((m_rx_payload_buffer[1] & 0x01) == 1)) + { + ack = true; + } + + if (ack) + { + NRF_RADIO->SHORTS = m_radio_shorts_common | RADIO_SHORTS_DISABLED_RXEN_Msk; + + switch (m_config_local.protocol) + { + case NRF_ESB_PROTOCOL_ESB_DPL: + { + if (m_tx_fifo.count > 0 && + (m_tx_fifo.p_payload[m_tx_fifo.exit_point]->pipe == NRF_RADIO->RXMATCH) + ) + { + // Pipe stays in ACK with payload until TX FIFO is empty + // Do not report TX success on first ack payload or retransmit + if (p_pipe_info->ack_payload == true && !retransmit_payload) + { + if (++m_tx_fifo.exit_point >= NRF_ESB_TX_FIFO_SIZE) + { + m_tx_fifo.exit_point = 0; + } + + m_tx_fifo.count--; + + // ACK payloads also require TX_DS + // (page 40 of the 'nRF24LE1_Product_Specification_rev1_6.pdf'). + m_interrupt_flags |= NRF_ESB_INT_TX_SUCCESS_MSK; + } + + p_pipe_info->ack_payload = true; + + mp_current_payload = m_tx_fifo.p_payload[m_tx_fifo.exit_point]; + + update_rf_payload_format(mp_current_payload->length); + m_tx_payload_buffer[0] = mp_current_payload->length; + memcpy(&m_tx_payload_buffer[2], + mp_current_payload->data, + mp_current_payload->length); + } + else + { + p_pipe_info->ack_payload = false; + update_rf_payload_format(0); + m_tx_payload_buffer[0] = 0; + } + + m_tx_payload_buffer[1] = m_rx_payload_buffer[1]; + } + break; + + case NRF_ESB_PROTOCOL_ESB: + { + update_rf_payload_format(0); + m_tx_payload_buffer[0] = m_rx_payload_buffer[0]; + m_tx_payload_buffer[1] = 0; + } + break; + } + + m_nrf_esb_mainstate = NRF_ESB_STATE_PRX_SEND_ACK; + NRF_RADIO->TXADDRESS = NRF_RADIO->RXMATCH; + NRF_RADIO->PACKETPTR = (uint32_t)m_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, p_pipe_info->pid)) + { + m_interrupt_flags |= NRF_ESB_INT_RX_DATA_RECEIVED_MSK; + NVIC_SetPendingIRQ(ESB_EVT_IRQ); + } + } +} + + +static void on_radio_disabled_rx_ack(void) +{ + NRF_RADIO->SHORTS = m_radio_shorts_common | RADIO_SHORTS_DISABLED_TXEN_Msk; + update_rf_payload_format(m_config_local.payload_length); + + NRF_RADIO->PACKETPTR = (uint32_t)m_rx_payload_buffer; + on_radio_disabled = on_radio_disabled_rx; + + m_nrf_esb_mainstate = NRF_ESB_STATE_PRX; +} + + +/**@brief Function for clearing pending interrupts. + * + * @param[in,out] p_interrupts Pointer to the value that holds the current interrupts. + * + * @retval NRF_SUCCESS If the interrupts were cleared successfully. + * @retval NRF_ERROR_NULL If the required parameter was NULL. + * @retval NRF_INVALID_STATE If the module is not initialized. + */ +static uint32_t nrf_esb_get_clear_interrupts(uint32_t * p_interrupts) +{ + VERIFY_TRUE(m_esb_initialized, NRF_ERROR_INVALID_STATE); + VERIFY_PARAM_NOT_NULL(p_interrupts); + + DISABLE_RF_IRQ(); + + *p_interrupts = m_interrupt_flags; + m_interrupt_flags = 0; + + ENABLE_RF_IRQ(); + + return NRF_SUCCESS; +} + + +/* +void RADIO_IRQHandler() +{ + if (NRF_RADIO->EVENTS_READY && (NRF_RADIO->INTENSET & RADIO_INTENSET_READY_Msk)) + { + NRF_RADIO->EVENTS_READY = 0; + DEBUG_PIN_SET(DEBUGPIN1); + } + + if (NRF_RADIO->EVENTS_END && (NRF_RADIO->INTENSET & RADIO_INTENSET_END_Msk)) + { + NRF_RADIO->EVENTS_END = 0; + DEBUG_PIN_SET(DEBUGPIN2); + + // 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; + DEBUG_PIN_SET(DEBUGPIN3); + + // Call the correct on_radio_disable function, depending on the current protocol state + if (on_radio_disabled) + { + on_radio_disabled(); + } + } + + DEBUG_PIN_CLR(DEBUGPIN1); + DEBUG_PIN_CLR(DEBUGPIN2); + DEBUG_PIN_CLR(DEBUGPIN3); + DEBUG_PIN_CLR(DEBUGPIN4); +} */ + + +uint32_t nrf_esb_init(nrf_esb_config_t const * p_config) +{ + uint32_t err_code; + + VERIFY_PARAM_NOT_NULL(p_config); + + if (m_esb_initialized) + { + err_code = nrf_esb_disable(); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + } + + m_event_handler = p_config->event_handler; + + memcpy(&m_config_local, p_config, sizeof(nrf_esb_config_t)); + + m_interrupt_flags = 0; + + memset(m_rx_pipe_info, 0, sizeof(m_rx_pipe_info)); + memset(m_pids, 0, sizeof(m_pids)); + + VERIFY_TRUE(update_radio_parameters(), NRF_ERROR_INVALID_PARAM); + + // 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(); + + NVIC_SetPriority(RADIO_IRQn, m_config_local.radio_irq_priority & ESB_IRQ_PRIORITY_MSK); + NVIC_SetPriority(ESB_EVT_IRQ, m_config_local.event_irq_priority & ESB_IRQ_PRIORITY_MSK); + NVIC_EnableIRQ(ESB_EVT_IRQ); + +#ifdef NRF52 + if(m_address_hang_fix_enable) + { + // Setup a timeout timer to start on an ADDRESS match, and stop on a BCMATCH event. + // If the BCMATCH event never occurs the CC[0] event will fire, and the timer interrupt will disable the radio to recover. + m_radio_shorts_common |= RADIO_SHORTS_ADDRESS_BCSTART_Msk; + NRF_RADIO->BCC = 2; + NRF_ESB_BUGFIX_TIMER->BITMODE = TIMER_BITMODE_BITMODE_32Bit << TIMER_BITMODE_BITMODE_Pos; + NRF_ESB_BUGFIX_TIMER->PRESCALER = 4; + NRF_ESB_BUGFIX_TIMER->CC[0] = 5; + NRF_ESB_BUGFIX_TIMER->SHORTS = TIMER_SHORTS_COMPARE0_STOP_Msk | TIMER_SHORTS_COMPARE0_CLEAR_Msk; + NRF_ESB_BUGFIX_TIMER->MODE = TIMER_MODE_MODE_Timer << TIMER_MODE_MODE_Pos; + NRF_ESB_BUGFIX_TIMER->INTENSET = TIMER_INTENSET_COMPARE0_Msk; + NRF_ESB_BUGFIX_TIMER->TASKS_CLEAR = 1; + NVIC_SetPriority(NRF_ESB_BUGFIX_TIMER_IRQn, 5); + NVIC_EnableIRQ(NRF_ESB_BUGFIX_TIMER_IRQn); + + NRF_PPI->CH[NRF_ESB_PPI_BUGFIX1].EEP = (uint32_t)&NRF_RADIO->EVENTS_ADDRESS; + NRF_PPI->CH[NRF_ESB_PPI_BUGFIX1].TEP = (uint32_t)&NRF_ESB_BUGFIX_TIMER->TASKS_START; + + NRF_PPI->CH[NRF_ESB_PPI_BUGFIX2].EEP = (uint32_t)&NRF_RADIO->EVENTS_BCMATCH; + NRF_PPI->CH[NRF_ESB_PPI_BUGFIX2].TEP = (uint32_t)&NRF_ESB_BUGFIX_TIMER->TASKS_STOP; + + NRF_PPI->CH[NRF_ESB_PPI_BUGFIX3].EEP = (uint32_t)&NRF_RADIO->EVENTS_BCMATCH; + NRF_PPI->CH[NRF_ESB_PPI_BUGFIX3].TEP = (uint32_t)&NRF_ESB_BUGFIX_TIMER->TASKS_CLEAR; + + NRF_PPI->CHENSET = (1 << NRF_ESB_PPI_BUGFIX1) | (1 << NRF_ESB_PPI_BUGFIX2) | (1 << NRF_ESB_PPI_BUGFIX3); + } +#endif + + m_nrf_esb_mainstate = NRF_ESB_STATE_IDLE; + m_esb_initialized = true; + + return NRF_SUCCESS; +} + + +uint32_t nrf_esb_suspend(void) +{ + VERIFY_TRUE(m_nrf_esb_mainstate == NRF_ESB_STATE_IDLE, NRF_ERROR_BUSY); + + // Clear PPI + NRF_PPI->CHENCLR = (1 << NRF_ESB_PPI_TIMER_START) | + (1 << NRF_ESB_PPI_TIMER_STOP) | + (1 << NRF_ESB_PPI_RX_TIMEOUT) | + (1 << NRF_ESB_PPI_TX_START); + + m_nrf_esb_mainstate = NRF_ESB_STATE_IDLE; + + return NRF_SUCCESS; +} + + +uint32_t nrf_esb_disable(void) +{ + // Clear PPI + NRF_PPI->CHENCLR = (1 << NRF_ESB_PPI_TIMER_START) | + (1 << NRF_ESB_PPI_TIMER_STOP) | + (1 << NRF_ESB_PPI_RX_TIMEOUT) | + (1 << NRF_ESB_PPI_TX_START); + + m_nrf_esb_mainstate = NRF_ESB_STATE_IDLE; + + reset_fifos(); + + memset(m_rx_pipe_info, 0, sizeof(m_rx_pipe_info)); + memset(m_pids, 0, sizeof(m_pids)); + + // Disable the radio + NVIC_DisableIRQ(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; + + return NRF_SUCCESS; +} + + +bool nrf_esb_is_idle(void) +{ + return m_nrf_esb_mainstate == NRF_ESB_STATE_IDLE; +} + + +void ESB_EVT_IRQHandler(void) +{ + ret_code_t err_code; + uint32_t interrupts; + nrf_esb_evt_t event; + + event.tx_attempts = m_last_tx_attempts; + + err_code = nrf_esb_get_clear_interrupts(&interrupts); + if (err_code == NRF_SUCCESS && m_event_handler != 0) + { + if (interrupts & NRF_ESB_INT_TX_SUCCESS_MSK) + { + event.evt_id = NRF_ESB_EVENT_TX_SUCCESS; + m_event_handler(&event); + } + if (interrupts & NRF_ESB_INT_TX_FAILED_MSK) + { + event.evt_id = NRF_ESB_EVENT_TX_FAILED; + m_event_handler(&event); + } + if (interrupts & NRF_ESB_INT_RX_DATA_RECEIVED_MSK) + { + event.evt_id = NRF_ESB_EVENT_RX_RECEIVED; + m_event_handler(&event); + } + } +} + +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); + 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(); + + 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++; + + 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) + { + start_tx_transaction(); + } + + return NRF_SUCCESS; +} + + +uint32_t nrf_esb_read_rx_payload(nrf_esb_payload_t * p_payload) +{ + VERIFY_TRUE(m_esb_initialized, NRF_ERROR_INVALID_STATE); + VERIFY_PARAM_NOT_NULL(p_payload); + + if (m_rx_fifo.count == 0) + { + return NRF_ERROR_NOT_FOUND; + } + + DISABLE_RF_IRQ(); + + p_payload->length = m_rx_fifo.p_payload[m_rx_fifo.exit_point]->length; + p_payload->pipe = m_rx_fifo.p_payload[m_rx_fifo.exit_point]->pipe; + p_payload->rssi = m_rx_fifo.p_payload[m_rx_fifo.exit_point]->rssi; + p_payload->pid = m_rx_fifo.p_payload[m_rx_fifo.exit_point]->pid; + p_payload->noack = m_rx_fifo.p_payload[m_rx_fifo.exit_point]->noack; + memcpy(p_payload->data, m_rx_fifo.p_payload[m_rx_fifo.exit_point]->data, p_payload->length); + + if (++m_rx_fifo.exit_point >= NRF_ESB_RX_FIFO_SIZE) + { + m_rx_fifo.exit_point = 0; + } + + m_rx_fifo.count--; + + ENABLE_RF_IRQ(); + + return NRF_SUCCESS; +} + + +uint32_t nrf_esb_start_tx(void) +{ + VERIFY_TRUE(m_nrf_esb_mainstate == NRF_ESB_STATE_IDLE, NRF_ERROR_BUSY); + + if (m_tx_fifo.count == 0) + { + return NRF_ERROR_BUFFER_EMPTY; + } + + start_tx_transaction(); + + return NRF_SUCCESS; +} + + +uint32_t nrf_esb_start_rx(void) +{ + VERIFY_TRUE(m_nrf_esb_mainstate == NRF_ESB_STATE_IDLE, NRF_ERROR_BUSY); + + NRF_RADIO->INTENCLR = 0xFFFFFFFF; + NRF_RADIO->EVENTS_DISABLED = 0; + on_radio_disabled = on_radio_disabled_rx; + + NRF_RADIO->SHORTS = m_radio_shorts_common | RADIO_SHORTS_DISABLED_TXEN_Msk; + NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk; + m_nrf_esb_mainstate = NRF_ESB_STATE_PRX; + + NRF_RADIO->RXADDRESSES = m_esb_addr.rx_pipes_enabled; + NRF_RADIO->FREQUENCY = m_esb_addr.rf_channel; + NRF_RADIO->PACKETPTR = (uint32_t)m_rx_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; + + NRF_RADIO->TASKS_RXEN = 1; + + return NRF_SUCCESS; +} + + +uint32_t nrf_esb_stop_rx(void) +{ + if (m_nrf_esb_mainstate == NRF_ESB_STATE_PRX) + { + 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; + + return NRF_SUCCESS; + } + + return NRF_ESB_ERROR_NOT_IN_RX_MODE; +} + + +uint32_t nrf_esb_flush_tx(void) +{ + VERIFY_TRUE(m_esb_initialized, NRF_ERROR_INVALID_STATE); + + DISABLE_RF_IRQ(); + + m_tx_fifo.count = 0; + m_tx_fifo.entry_point = 0; + m_tx_fifo.exit_point = 0; + + ENABLE_RF_IRQ(); + + return NRF_SUCCESS; +} + + +uint32_t nrf_esb_pop_tx(void) +{ + VERIFY_TRUE(m_esb_initialized, NRF_ERROR_INVALID_STATE); + VERIFY_TRUE(m_tx_fifo.count > 0, NRF_ERROR_BUFFER_EMPTY); + + DISABLE_RF_IRQ(); + + if (++m_tx_fifo.entry_point >= NRF_ESB_TX_FIFO_SIZE) + { + m_tx_fifo.entry_point = 0; + } + m_tx_fifo.count--; + + ENABLE_RF_IRQ(); + + return NRF_SUCCESS; +} + + +uint32_t nrf_esb_flush_rx(void) +{ + VERIFY_TRUE(m_esb_initialized, NRF_ERROR_INVALID_STATE); + + DISABLE_RF_IRQ(); + + m_rx_fifo.count = 0; + m_rx_fifo.entry_point = 0; + m_rx_fifo.exit_point = 0; + + memset(m_rx_pipe_info, 0, sizeof(m_rx_pipe_info)); + + ENABLE_RF_IRQ(); + + return NRF_SUCCESS; +} + + +uint32_t nrf_esb_set_address_length(uint8_t length) +{ + VERIFY_TRUE(m_nrf_esb_mainstate == NRF_ESB_STATE_IDLE, NRF_ERROR_BUSY); + VERIFY_TRUE(length > 2 && length < 6, NRF_ERROR_INVALID_PARAM); + + /* + Workaround for nRF52832 Rev 1 Errata 107 + Check if pipe 0 or pipe 1-7 has a 'zero address'. + Avoid using access addresses in the following pattern (where X is don't care): + ADDRLEN=5 + BASE0 = 0x0000XXXX, PREFIX0 = 0xXXXXXX00 + BASE1 = 0x0000XXXX, PREFIX0 = 0xXXXX00XX + BASE1 = 0x0000XXXX, PREFIX0 = 0xXX00XXXX + BASE1 = 0x0000XXXX, PREFIX0 = 0x00XXXXXX + BASE1 = 0x0000XXXX, PREFIX1 = 0xXXXXXX00 + BASE1 = 0x0000XXXX, PREFIX1 = 0xXXXX00XX + BASE1 = 0x0000XXXX, PREFIX1 = 0xXX00XXXX + BASE1 = 0x0000XXXX, PREFIX1 = 0x00XXXXXX + + ADDRLEN=4 + BASE0 = 0x00XXXXXX, PREFIX0 = 0xXXXXXX00 + BASE1 = 0x00XXXXXX, PREFIX0 = 0xXXXX00XX + BASE1 = 0x00XXXXXX, PREFIX0 = 0xXX00XXXX + BASE1 = 0x00XXXXXX, PREFIX0 = 0x00XXXXXX + BASE1 = 0x00XXXXXX, PREFIX1 = 0xXXXXXX00 + BASE1 = 0x00XXXXXX, PREFIX1 = 0xXXXX00XX + BASE1 = 0x00XXXXXX, PREFIX1 = 0xXX00XXXX + BASE1 = 0x00XXXXXX, PREFIX1 = 0x00XXXXXX + */ + uint32_t base_address_mask = length == 5 ? 0xFFFF0000 : 0xFF000000; + if((NRF_RADIO->BASE0 & base_address_mask) == 0 && (NRF_RADIO->PREFIX0 & 0x000000FF) == 0) + { + return NRF_ERROR_INVALID_PARAM; + } + if((NRF_RADIO->BASE1 & base_address_mask) == 0 && ((NRF_RADIO->PREFIX0 & 0x0000FF00) == 0 ||(NRF_RADIO->PREFIX0 & 0x00FF0000) == 0 || (NRF_RADIO->PREFIX0 & 0xFF000000) == 0 || + (NRF_RADIO->PREFIX1 & 0xFF000000) == 0 || (NRF_RADIO->PREFIX1 & 0x00FF0000) == 0 ||(NRF_RADIO->PREFIX1 & 0x0000FF00) == 0 || (NRF_RADIO->PREFIX1 & 0x000000FF) == 0)) + { + return NRF_ERROR_INVALID_PARAM; + } + + m_esb_addr.addr_length = length; + + update_rf_payload_format(m_config_local.payload_length); + + return NRF_SUCCESS; +} + + +uint32_t nrf_esb_set_base_address_0(uint8_t const * p_addr) +{ + VERIFY_TRUE(m_nrf_esb_mainstate == NRF_ESB_STATE_IDLE, NRF_ERROR_BUSY); + VERIFY_PARAM_NOT_NULL(p_addr); + + /* + Workaround for nRF52832 Rev 1 Errata 107 + Check if pipe 0 or pipe 1-7 has a 'zero address'. + Avoid using access addresses in the following pattern (where X is don't care): + ADDRLEN=5 + BASE0 = 0x0000XXXX, PREFIX0 = 0xXXXXXX00 + BASE1 = 0x0000XXXX, PREFIX0 = 0xXXXX00XX + BASE1 = 0x0000XXXX, PREFIX0 = 0xXX00XXXX + BASE1 = 0x0000XXXX, PREFIX0 = 0x00XXXXXX + BASE1 = 0x0000XXXX, PREFIX1 = 0xXXXXXX00 + BASE1 = 0x0000XXXX, PREFIX1 = 0xXXXX00XX + BASE1 = 0x0000XXXX, PREFIX1 = 0xXX00XXXX + BASE1 = 0x0000XXXX, PREFIX1 = 0x00XXXXXX + + ADDRLEN=4 + BASE0 = 0x00XXXXXX, PREFIX0 = 0xXXXXXX00 + BASE1 = 0x00XXXXXX, PREFIX0 = 0xXXXX00XX + BASE1 = 0x00XXXXXX, PREFIX0 = 0xXX00XXXX + BASE1 = 0x00XXXXXX, PREFIX0 = 0x00XXXXXX + BASE1 = 0x00XXXXXX, PREFIX1 = 0xXXXXXX00 + BASE1 = 0x00XXXXXX, PREFIX1 = 0xXXXX00XX + BASE1 = 0x00XXXXXX, PREFIX1 = 0xXX00XXXX + BASE1 = 0x00XXXXXX, PREFIX1 = 0x00XXXXXX + */ + uint32_t base_address_mask = m_esb_addr.addr_length == 5 ? 0xFFFF0000 : 0xFF000000; + if((addr_conv(p_addr) & base_address_mask) == 0 && (NRF_RADIO->PREFIX0 & 0x000000FF) == 0) + { + return NRF_ERROR_INVALID_PARAM; + } + + memcpy(m_esb_addr.base_addr_p0, p_addr, 4); + + update_radio_addresses(NRF_ESB_ADDR_UPDATE_MASK_BASE0); + + return apply_address_workarounds(); +} + + +uint32_t nrf_esb_set_base_address_1(uint8_t const * p_addr) +{ + VERIFY_TRUE(m_nrf_esb_mainstate == NRF_ESB_STATE_IDLE, NRF_ERROR_BUSY); + VERIFY_PARAM_NOT_NULL(p_addr); + + /* + Workaround for nRF52832 Rev 1 Errata 107 + Check if pipe 0 or pipe 1-7 has a 'zero address'. + Avoid using access addresses in the following pattern (where X is don't care): + ADDRLEN=5 + BASE0 = 0x0000XXXX, PREFIX0 = 0xXXXXXX00 + BASE1 = 0x0000XXXX, PREFIX0 = 0xXXXX00XX + BASE1 = 0x0000XXXX, PREFIX0 = 0xXX00XXXX + BASE1 = 0x0000XXXX, PREFIX0 = 0x00XXXXXX + BASE1 = 0x0000XXXX, PREFIX1 = 0xXXXXXX00 + BASE1 = 0x0000XXXX, PREFIX1 = 0xXXXX00XX + BASE1 = 0x0000XXXX, PREFIX1 = 0xXX00XXXX + BASE1 = 0x0000XXXX, PREFIX1 = 0x00XXXXXX + + ADDRLEN=4 + BASE0 = 0x00XXXXXX, PREFIX0 = 0xXXXXXX00 + BASE1 = 0x00XXXXXX, PREFIX0 = 0xXXXX00XX + BASE1 = 0x00XXXXXX, PREFIX0 = 0xXX00XXXX + BASE1 = 0x00XXXXXX, PREFIX0 = 0x00XXXXXX + BASE1 = 0x00XXXXXX, PREFIX1 = 0xXXXXXX00 + BASE1 = 0x00XXXXXX, PREFIX1 = 0xXXXX00XX + BASE1 = 0x00XXXXXX, PREFIX1 = 0xXX00XXXX + BASE1 = 0x00XXXXXX, PREFIX1 = 0x00XXXXXX + */ + uint32_t base_address_mask = m_esb_addr.addr_length == 5 ? 0xFFFF0000 : 0xFF000000; + if((addr_conv(p_addr) & base_address_mask) == 0 && ((NRF_RADIO->PREFIX0 & 0x0000FF00) == 0 ||(NRF_RADIO->PREFIX0 & 0x00FF0000) == 0 || (NRF_RADIO->PREFIX0 & 0xFF000000) == 0 || + (NRF_RADIO->PREFIX1 & 0xFF000000) == 0 || (NRF_RADIO->PREFIX1 & 0x00FF0000) == 0 ||(NRF_RADIO->PREFIX1 & 0x0000FF00) == 0 || (NRF_RADIO->PREFIX1 & 0x000000FF) == 0)) + { + return NRF_ERROR_INVALID_PARAM; + } + + memcpy(m_esb_addr.base_addr_p1, p_addr, 4); + + update_radio_addresses(NRF_ESB_ADDR_UPDATE_MASK_BASE1); + + return apply_address_workarounds(); +} + + +uint32_t nrf_esb_set_prefixes(uint8_t const * p_prefixes, uint8_t num_pipes) +{ + VERIFY_TRUE(m_nrf_esb_mainstate == NRF_ESB_STATE_IDLE, NRF_ERROR_BUSY); + VERIFY_PARAM_NOT_NULL(p_prefixes); + VERIFY_TRUE(num_pipes < 9, NRF_ERROR_INVALID_PARAM); + + /* + Workaround for nRF52832 Rev 1 Errata 107 + Check if pipe 0 or pipe 1-7 has a 'zero address'. + Avoid using access addresses in the following pattern (where X is don't care): + ADDRLEN=5 + BASE0 = 0x0000XXXX, PREFIX0 = 0xXXXXXX00 + BASE1 = 0x0000XXXX, PREFIX0 = 0xXXXX00XX + BASE1 = 0x0000XXXX, PREFIX0 = 0xXX00XXXX + BASE1 = 0x0000XXXX, PREFIX0 = 0x00XXXXXX + BASE1 = 0x0000XXXX, PREFIX1 = 0xXXXXXX00 + BASE1 = 0x0000XXXX, PREFIX1 = 0xXXXX00XX + BASE1 = 0x0000XXXX, PREFIX1 = 0xXX00XXXX + BASE1 = 0x0000XXXX, PREFIX1 = 0x00XXXXXX + + ADDRLEN=4 + BASE0 = 0x00XXXXXX, PREFIX0 = 0xXXXXXX00 + BASE1 = 0x00XXXXXX, PREFIX0 = 0xXXXX00XX + BASE1 = 0x00XXXXXX, PREFIX0 = 0xXX00XXXX + BASE1 = 0x00XXXXXX, PREFIX0 = 0x00XXXXXX + BASE1 = 0x00XXXXXX, PREFIX1 = 0xXXXXXX00 + BASE1 = 0x00XXXXXX, PREFIX1 = 0xXXXX00XX + BASE1 = 0x00XXXXXX, PREFIX1 = 0xXX00XXXX + BASE1 = 0x00XXXXXX, PREFIX1 = 0x00XXXXXX + */ + uint32_t base_address_mask = m_esb_addr.addr_length == 5 ? 0xFFFF0000 : 0xFF000000; + if(num_pipes >= 1 && (NRF_RADIO->BASE0 & base_address_mask) == 0 && p_prefixes[0] == 0) + { + return NRF_ERROR_INVALID_PARAM; + } + + if((NRF_RADIO->BASE1 & base_address_mask) == 0) + { + for (uint8_t i = 1; i < num_pipes; i++) + { + if (p_prefixes[i] == 0) + { + return NRF_ERROR_INVALID_PARAM; + } + } + } + + memcpy(m_esb_addr.pipe_prefixes, p_prefixes, num_pipes); + m_esb_addr.num_pipes = num_pipes; + m_esb_addr.rx_pipes_enabled = BIT_MASK_UINT_8(num_pipes); + + update_radio_addresses(NRF_ESB_ADDR_UPDATE_MASK_PREFIX); + + return apply_address_workarounds(); +} + + +uint32_t nrf_esb_update_prefix(uint8_t pipe, uint8_t prefix) +{ + VERIFY_TRUE(m_nrf_esb_mainstate == NRF_ESB_STATE_IDLE, NRF_ERROR_BUSY); + VERIFY_TRUE(pipe < 8, NRF_ERROR_INVALID_PARAM); + + /* + Workaround for nRF52832 Rev 1 Errata 107 + Check if pipe 0 or pipe 1-7 has a 'zero address'. + Avoid using access addresses in the following pattern (where X is don't care): + ADDRLEN=5 + BASE0 = 0x0000XXXX, PREFIX0 = 0xXXXXXX00 + BASE1 = 0x0000XXXX, PREFIX0 = 0xXXXX00XX + BASE1 = 0x0000XXXX, PREFIX0 = 0xXX00XXXX + BASE1 = 0x0000XXXX, PREFIX0 = 0x00XXXXXX + BASE1 = 0x0000XXXX, PREFIX1 = 0xXXXXXX00 + BASE1 = 0x0000XXXX, PREFIX1 = 0xXXXX00XX + BASE1 = 0x0000XXXX, PREFIX1 = 0xXX00XXXX + BASE1 = 0x0000XXXX, PREFIX1 = 0x00XXXXXX + + ADDRLEN=4 + BASE0 = 0x00XXXXXX, PREFIX0 = 0xXXXXXX00 + BASE1 = 0x00XXXXXX, PREFIX0 = 0xXXXX00XX + BASE1 = 0x00XXXXXX, PREFIX0 = 0xXX00XXXX + BASE1 = 0x00XXXXXX, PREFIX0 = 0x00XXXXXX + BASE1 = 0x00XXXXXX, PREFIX1 = 0xXXXXXX00 + BASE1 = 0x00XXXXXX, PREFIX1 = 0xXXXX00XX + BASE1 = 0x00XXXXXX, PREFIX1 = 0xXX00XXXX + BASE1 = 0x00XXXXXX, PREFIX1 = 0x00XXXXXX + */ + uint32_t base_address_mask = m_esb_addr.addr_length == 5 ? 0xFFFF0000 : 0xFF000000; + if (pipe == 0) + { + if((NRF_RADIO->BASE0 & base_address_mask) == 0 && prefix == 0) + { + return NRF_ERROR_INVALID_PARAM; + } + } + else{ + if((NRF_RADIO->BASE1 & base_address_mask) == 0 && prefix == 0) + { + return NRF_ERROR_INVALID_PARAM; + } + } + + m_esb_addr.pipe_prefixes[pipe] = prefix; + + update_radio_addresses(NRF_ESB_ADDR_UPDATE_MASK_PREFIX); + + return apply_address_workarounds(); +} + + +uint32_t nrf_esb_enable_pipes(uint8_t enable_mask) +{ + VERIFY_TRUE(m_nrf_esb_mainstate == NRF_ESB_STATE_IDLE, NRF_ERROR_BUSY); + + m_esb_addr.rx_pipes_enabled = enable_mask; + + return apply_address_workarounds(); +} + + +uint32_t nrf_esb_set_rf_channel(uint32_t channel) +{ + VERIFY_TRUE(m_nrf_esb_mainstate == NRF_ESB_STATE_IDLE, NRF_ERROR_BUSY); + VERIFY_TRUE(channel <= 100, NRF_ERROR_INVALID_PARAM); + + m_esb_addr.rf_channel = channel; + + return NRF_SUCCESS; +} + + +uint32_t nrf_esb_get_rf_channel(uint32_t * p_channel) +{ + VERIFY_PARAM_NOT_NULL(p_channel); + + *p_channel = m_esb_addr.rf_channel; + + return NRF_SUCCESS; +} + + +uint32_t nrf_esb_set_tx_power(nrf_esb_tx_power_t tx_output_power) +{ + VERIFY_TRUE(m_nrf_esb_mainstate == NRF_ESB_STATE_IDLE, NRF_ERROR_BUSY); + + if ( m_config_local.tx_output_power != tx_output_power ) + { + m_config_local.tx_output_power = tx_output_power; + update_radio_tx_power(); + } + + return NRF_SUCCESS; +} + + +uint32_t nrf_esb_set_retransmit_delay(uint16_t delay) +{ + VERIFY_TRUE(m_nrf_esb_mainstate == NRF_ESB_STATE_IDLE, NRF_ERROR_BUSY); + VERIFY_TRUE(delay >= NRF_ESB_RETRANSMIT_DELAY_MIN, NRF_ERROR_INVALID_PARAM); + + m_config_local.retransmit_delay = delay; + return NRF_SUCCESS; +} + + +uint32_t nrf_esb_set_retransmit_count(uint16_t count) +{ + VERIFY_TRUE(m_nrf_esb_mainstate == NRF_ESB_STATE_IDLE, NRF_ERROR_BUSY); + + m_config_local.retransmit_count = count; + return NRF_SUCCESS; +} + + +uint32_t nrf_esb_set_bitrate(nrf_esb_bitrate_t bitrate) +{ + VERIFY_TRUE(m_nrf_esb_mainstate == NRF_ESB_STATE_IDLE, NRF_ERROR_BUSY); + + m_config_local.bitrate = bitrate; + return update_radio_bitrate() ? NRF_SUCCESS : NRF_ERROR_INVALID_PARAM; +} + + +uint32_t nrf_esb_reuse_pid(uint8_t pipe) +{ + VERIFY_TRUE(m_nrf_esb_mainstate == NRF_ESB_STATE_IDLE, NRF_ERROR_BUSY); + VERIFY_TRUE(pipe < 8, NRF_ERROR_INVALID_PARAM); + + m_pids[pipe] = (m_pids[pipe] + NRF_ESB_PID_MAX) % (NRF_ESB_PID_MAX + 1); + return NRF_SUCCESS; +} + + +// Handler for +#ifdef NRF52 +void NRF_ESB_BUGFIX_TIMER_IRQHandler(void) +{ + if(NRF_ESB_BUGFIX_TIMER->EVENTS_COMPARE[0]) + { + NRF_ESB_BUGFIX_TIMER->EVENTS_COMPARE[0] = 0; + + // If the timeout timer fires and we are in the PTX receive ACK state, disable the radio + if(m_nrf_esb_mainstate == NRF_ESB_STATE_PTX_RX_ACK) + { + NRF_RADIO->TASKS_DISABLE = 1; + } + } +} +#endif \ No newline at end of file
diff -r a01a54c0dc90 -r 66f95e364222 nrf_esb.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nrf_esb.h Thu Feb 04 10:36:44 2021 +0000 @@ -0,0 +1,609 @@ +/** + * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef __NRF_ESB_H +#define __NRF_ESB_H + +#include <stdbool.h> +#include <stdint.h> +#include "nrf.h" +#include "app_util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @defgroup nrf_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. + */ + +/** @name Debug pins + * @{ + * @brief If NRF_ESB_DEBUG is defined, these GPIO pins can be used for debug timing. + */ + +#define DEBUGPIN1 12 //!< If NRF_ESB_DEBUG is defined, this GPIO pin is set with every radio interrupt. +#define DEBUGPIN2 13 //!< If NRF_ESB_DEBUG is defined, this GPIO pin is set with every NRF_RADIO->EVENTS_END. +#define DEBUGPIN3 14 //!< If NRF_ESB_DEBUG is defined, this GPIO pin is set with every NRF_RADIO->EVENTS_DISABLED. +#define DEBUGPIN4 15 //!< If NRF_ESB_DEBUG is defined, this GPIO pin is set when the radio is set to start transmission. + + +#ifdef NRF_ESB_DEBUG +#define DEBUG_PIN_SET(a) (NRF_GPIO->OUTSET = (1 << (a))) //!< Used internally to set debug pins. +#define DEBUG_PIN_CLR(a) (NRF_GPIO->OUTCLR = (1 << (a))) //!< Used internally to clear debug pins. +#else +#define DEBUG_PIN_SET(a) //!< Used internally to set debug pins. +#define DEBUG_PIN_CLR(a) //!< Used internally to clear debug pins. +#endif + + /** @} */ + +#define NRF_ESB_RETRANSMIT_DELAY_MIN 135 + +// Hardcoded parameters - change if necessary +#ifndef NRF_ESB_MAX_PAYLOAD_LENGTH +#define NRF_ESB_MAX_PAYLOAD_LENGTH 32 //!< The maximum size of the payload. Valid values are 1 to 252. +#endif + +#define NRF_ESB_TX_FIFO_SIZE 8 //!< The size of the transmission first-in, first-out buffer. +#define NRF_ESB_RX_FIFO_SIZE 8 //!< The size of the reception first-in, first-out buffer. + +// 252 is the largest possible payload size according to the nRF5 architecture. +STATIC_ASSERT(NRF_ESB_MAX_PAYLOAD_LENGTH <= 252); + +#define NRF_ESB_SYS_TIMER NRF_TIMER2 //!< The timer that is used by the module. +#define NRF_ESB_SYS_TIMER_IRQ_Handler TIMER2_IRQHandler //!< The handler that is used by @ref NRF_ESB_SYS_TIMER. + +#define NRF_ESB_PPI_TIMER_START 10 //!< The PPI channel used for starting the timer. +#define NRF_ESB_PPI_TIMER_STOP 11 //!< The PPI channel used for stopping the timer. +#define NRF_ESB_PPI_RX_TIMEOUT 12 //!< The PPI channel used for RX time-out. +#define NRF_ESB_PPI_TX_START 13 //!< The PPI channel used for starting TX. + +/**@cond NO_DOXYGEN */ + +// nRF52 address fix timer and PPI defines +#ifdef NRF52 +#define NRF_ESB_PPI_BUGFIX1 9 +#define NRF_ESB_PPI_BUGFIX2 8 +#define NRF_ESB_PPI_BUGFIX3 7 + +#define NRF_ESB_BUGFIX_TIMER NRF_TIMER3 +#define NRF_ESB_BUGFIX_TIMER_IRQn TIMER3_IRQn +#define NRF_ESB_BUGFIX_TIMER_IRQHandler TIMER3_IRQHandler +#endif + +/** @endcond */ + +// Interrupt flags +#define NRF_ESB_INT_TX_SUCCESS_MSK 0x01 //!< The flag used to indicate a success since the last event. +#define NRF_ESB_INT_TX_FAILED_MSK 0x02 //!< The flag used to indicate a failure since the last event. +#define NRF_ESB_INT_RX_DR_MSK 0x04 //!< The flag used to indicate that a packet was received since the last event. + +#define NRF_ESB_PID_RESET_VALUE 0xFF //!< Invalid PID value that is guaranteed to not collide with any valid PID value. +#define NRF_ESB_PID_MAX 3 //!< The maximum value for PID. +#define NRF_ESB_CRC_RESET_VALUE 0xFFFF //!< The CRC reset value. + +#define ESB_EVT_IRQ SWI0_IRQn //!< The ESB event IRQ number when running on an nRF5 device. +#define ESB_EVT_IRQHandler SWI0_IRQHandler //!< The handler for @ref ESB_EVT_IRQ when running on an nRF5 device. + +#if defined(NRF52) +#define ESB_IRQ_PRIORITY_MSK 0x07 //!< The mask used to enforce a valid IRQ priority. +#else +#define ESB_IRQ_PRIORITY_MSK 0x03 //!< The mask used to enforce a valid IRQ priority. +#endif + +/** @brief Default address configuration for ESB. + * @details Roughly equal to the nRF24Lxx default (except for the number of pipes, because more pipes are supported). */ +#define NRF_ESB_ADDR_DEFAULT \ +{ \ + .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 = 8, \ + .rf_channel = 2, \ + .rx_pipes_enabled = 0xFF \ +} + + +/** @brief Default radio parameters. + * @details Roughly equal to the nRF24Lxx default parameters (except for CRC, which is set to 16 bit, and protocol, which is set to DPL). */ +#define NRF_ESB_DEFAULT_CONFIG {.protocol = NRF_ESB_PROTOCOL_ESB_DPL, \ + .mode = NRF_ESB_MODE_PTX, \ + .event_handler = 0, \ + .bitrate = NRF_ESB_BITRATE_2MBPS, \ + .crc = NRF_ESB_CRC_16BIT, \ + .tx_output_power = NRF_ESB_TX_POWER_0DBM, \ + .retransmit_delay = 250, \ + .retransmit_count = 3, \ + .tx_mode = NRF_ESB_TXMODE_AUTO, \ + .radio_irq_priority = 1, \ + .event_irq_priority = 2, \ + .payload_length = 32, \ + .selective_auto_ack = false \ +} + + +/** @brief Default legacy radio parameters. Identical to the nRF24Lxx defaults. */ +#define NRF_ESB_LEGACY_CONFIG {.protocol = NRF_ESB_PROTOCOL_ESB, \ + .mode = NRF_ESB_MODE_PTX, \ + .event_handler = 0, \ + .bitrate = NRF_ESB_BITRATE_2MBPS, \ + .crc = NRF_ESB_CRC_8BIT, \ + .tx_output_power = NRF_ESB_TX_POWER_0DBM, \ + .retransmit_delay = 600, \ + .retransmit_count = 3, \ + .tx_mode = NRF_ESB_TXMODE_AUTO, \ + .radio_irq_priority = 1, \ + .event_irq_priority = 2, \ + .payload_length = 32, \ + .selective_auto_ack = false \ +} + + +/** @brief Macro to create an initializer for a TX data packet. + * + * @details This macro generates an initializer. Using the initializer is more efficient + * than setting the individual parameters dynamically. + * + * @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 NRF_ESB_CREATE_PAYLOAD(_pipe, ...) \ + {.pipe = _pipe, .length = NUM_VA_ARGS(__VA_ARGS__), .data = {__VA_ARGS__}}; \ + STATIC_ASSERT(NUM_VA_ARGS(__VA_ARGS__) > 0 && NUM_VA_ARGS(__VA_ARGS__) <= 63) + + +/**@brief Enhanced ShockBurst protocols. */ +typedef enum { + NRF_ESB_PROTOCOL_ESB, /**< Enhanced ShockBurst with fixed payload length. */ + NRF_ESB_PROTOCOL_ESB_DPL /**< Enhanced ShockBurst with dynamic payload length. */ +} nrf_esb_protocol_t; + + +/**@brief Enhanced ShockBurst modes. */ +typedef enum { + NRF_ESB_MODE_PTX, /**< Primary transmitter mode. */ + NRF_ESB_MODE_PRX /**< Primary receiver mode. */ +} nrf_esb_mode_t; + + +/**@brief Enhanced ShockBurst bitrate modes. */ +typedef enum { + NRF_ESB_BITRATE_2MBPS = RADIO_MODE_MODE_Nrf_2Mbit, /**< 2 Mb radio mode. */ + NRF_ESB_BITRATE_1MBPS = RADIO_MODE_MODE_Nrf_1Mbit, /**< 1 Mb radio mode. */ + NRF_ESB_BITRATE_250KBPS = RADIO_MODE_MODE_Nrf_250Kbit, /**< 250 Kb radio mode. */ + NRF_ESB_BITRATE_1MBPS_BLE = RADIO_MODE_MODE_Ble_1Mbit, /**< 1 Mb radio mode using @e Bluetooth low energy radio parameters. */ +#if defined(NRF52) + NRF_ESB_BITRATE_2MBPS_BLE = 4 /**< 2 Mb radio mode using @e Bluetooth low energy radio parameters. */ +#endif +} nrf_esb_bitrate_t; + + +/**@brief Enhanced ShockBurst CRC modes. */ +typedef enum { + NRF_ESB_CRC_16BIT = RADIO_CRCCNF_LEN_Two, /**< Use two-byte CRC. */ + NRF_ESB_CRC_8BIT = RADIO_CRCCNF_LEN_One, /**< Use one-byte CRC. */ + NRF_ESB_CRC_OFF = RADIO_CRCCNF_LEN_Disabled /**< Disable CRC. */ +} nrf_esb_crc_t; + + +/**@brief Enhanced ShockBurst radio transmission power modes. */ +typedef enum { + NRF_ESB_TX_POWER_4DBM = RADIO_TXPOWER_TXPOWER_Pos4dBm, /**< 4 dBm radio transmit power. */ +#if defined(NRF52) + NRF_ESB_TX_POWER_3DBM = RADIO_TXPOWER_TXPOWER_Pos3dBm, /**< 3 dBm radio transmit power. */ +#endif + NRF_ESB_TX_POWER_0DBM = RADIO_TXPOWER_TXPOWER_0dBm, /**< 0 dBm radio transmit power. */ + NRF_ESB_TX_POWER_NEG4DBM = RADIO_TXPOWER_TXPOWER_Neg4dBm, /**< -4 dBm radio transmit power. */ + NRF_ESB_TX_POWER_NEG8DBM = RADIO_TXPOWER_TXPOWER_Neg8dBm, /**< -8 dBm radio transmit power. */ + NRF_ESB_TX_POWER_NEG12DBM = RADIO_TXPOWER_TXPOWER_Neg12dBm, /**< -12 dBm radio transmit power. */ + NRF_ESB_TX_POWER_NEG16DBM = RADIO_TXPOWER_TXPOWER_Neg16dBm, /**< -16 dBm radio transmit power. */ + NRF_ESB_TX_POWER_NEG20DBM = RADIO_TXPOWER_TXPOWER_Neg20dBm, /**< -20 dBm radio transmit power. */ + NRF_ESB_TX_POWER_NEG30DBM = RADIO_TXPOWER_TXPOWER_Neg30dBm /**< -30 dBm radio transmit power. */ +} nrf_esb_tx_power_t; + + +/**@brief Enhanced ShockBurst transmission modes. */ +typedef enum { + NRF_ESB_TXMODE_AUTO, /**< Automatic TX mode: When the TX FIFO contains packets and the radio is idle, packets are sent automatically. */ + NRF_ESB_TXMODE_MANUAL, /**< Manual TX mode: Packets are not sent until @ref nrf_esb_start_tx is called. This mode can be used to ensure consistent packet timing. */ + NRF_ESB_TXMODE_MANUAL_START /**< Manual start TX mode: Packets are not sent until @ref nrf_esb_start_tx is called. Then, transmission continues automatically until the TX FIFO is empty. */ +} nrf_esb_tx_mode_t; + + +/**@brief Enhanced ShockBurst event IDs used to indicate the type of the event. */ +typedef enum +{ + NRF_ESB_EVENT_TX_SUCCESS, /**< Event triggered on TX success. */ + NRF_ESB_EVENT_TX_FAILED, /**< Event triggered on TX failure. */ + NRF_ESB_EVENT_RX_RECEIVED /**< Event triggered on RX received. */ +} nrf_esb_evt_id_t; + + +/**@brief Enhanced ShockBurst payload. + * + * @details The payload is used both for transmissions and for acknowledging a + * received packet with a payload. +*/ +typedef struct +{ + uint8_t length; //!< Length of the packet (maximum value is @ref NRF_ESB_MAX_PAYLOAD_LENGTH). + 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 acknowledgement. + uint8_t pid; //!< PID assigned during communication. + uint8_t data[NRF_ESB_MAX_PAYLOAD_LENGTH]; //!< The payload data. +} nrf_esb_payload_t; + + +/**@brief Enhanced ShockBurst event. */ +typedef struct +{ + nrf_esb_evt_id_t evt_id; //!< Enhanced ShockBurst event ID. + uint32_t tx_attempts; //!< Number of TX retransmission attempts. +} nrf_esb_evt_t; + + +/**@brief Definition of the event handler for the module. */ +typedef void (* nrf_esb_event_handler_t)(nrf_esb_evt_t const * p_event); + + +/**@brief Main configuration structure for the module. */ +typedef struct +{ + nrf_esb_protocol_t protocol; //!< Enhanced ShockBurst protocol. + nrf_esb_mode_t mode; //!< Enhanced ShockBurst mode. + nrf_esb_event_handler_t event_handler; //!< Enhanced ShockBurst event handler. + + // General RF parameters + nrf_esb_bitrate_t bitrate; //!< Enhanced ShockBurst bitrate mode. + nrf_esb_crc_t crc; //!< Enhanced ShockBurst CRC mode. + + nrf_esb_tx_power_t tx_output_power; //!< Enhanced ShockBurst radio transmission power mode. + + 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 + nrf_esb_tx_mode_t tx_mode; //!< Enhanced ShockBurst transmission mode. + + uint8_t radio_irq_priority; //!< nRF radio interrupt priority. + uint8_t event_irq_priority; //!< ESB event interrupt priority. + uint8_t payload_length; //!< Length of the payload (maximum length depends on the platforms that are used on each side). + + bool selective_auto_ack; //!< Enable or disable selective auto acknowledgement. +} nrf_esb_config_t; + + +/**@brief Function for initializing the Enhanced ShockBurst module. + * + * @param p_config Parameters for initializing the module. + * + * @retval NRF_SUCCESS If initialization was successful. + * @retval NRF_ERROR_NULL If the @p p_config argument was NULL. + * @retval NRF_ERROR_BUSY If the function failed because the radio is busy. + */ +uint32_t nrf_esb_init(nrf_esb_config_t const * p_config); + + +/**@brief Function for suspending the Enhanced ShockBurst module. + * + * Calling this function stops ongoing communications without changing the queues. + * + * @retval NRF_SUCCESS If Enhanced ShockBurst was suspended. + * @retval NRF_ERROR_BUSY If the function failed because the radio is busy. + */ +uint32_t nrf_esb_suspend(void); + + +/**@brief Function for disabling 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. + * + * @retval NRF_SUCCESS If Enhanced ShockBurst was disabled. + */ +uint32_t nrf_esb_disable(void); + + +/**@brief Function for checking if the Enhanced ShockBurst module is idle. + * + * @retval true If the module is idle. + * @retval false If the module is busy. + */ +bool nrf_esb_is_idle(void); + + +/**@brief Function for writing 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] p_payload Pointer to the structure that contains information and state of the payload. + * + * @retval NRF_SUCCESS If the payload was successfully queued for writing. + * @retval NRF_ERROR_NULL If the required parameter was NULL. + * @retval NRF_INVALID_STATE If the module is not initialized. + * @retval NRF_ERROR_NOT_SUPPORTED If @p p_payload->noack was false, but selective acknowledgement is not enabled. + * @retval NRF_ERROR_NO_MEM If the TX FIFO is full. + * @retval NRF_ERROR_INVALID_LENGTH If the payload length was invalid (zero or larger than the allowed maximum). + */ +uint32_t nrf_esb_write_payload(nrf_esb_payload_t const * p_payload); + + +/**@brief Function for reading an RX payload. + * + * @param[in,out] p_payload Pointer to the structure that contains information and state of the payload. + * + * @retval NRF_SUCCESS If the data was read successfully. + * @retval NRF_ERROR_NULL If the required parameter was NULL. + * @retval NRF_INVALID_STATE If the module is not initialized. + */ +uint32_t nrf_esb_read_rx_payload(nrf_esb_payload_t * p_payload); + + +/**@brief Function for starting transmission. + * + * @retval NRF_SUCCESS If the TX started successfully. + * @retval NRF_ERROR_BUFFER_EMPTY If the TX did not start because the FIFO buffer is empty. + * @retval NRF_ERROR_BUSY If the function failed because the radio is busy. + */ +uint32_t nrf_esb_start_tx(void); + + +/**@brief Function for starting to transmit data from the FIFO buffer. + * + * @retval NRF_SUCCESS If the transmission was started successfully. + * @retval NRF_ERROR_BUSY If the function failed because the radio is busy. + */ +uint32_t nrf_esb_start_rx(void); + + +/** @brief Function for stopping data reception. + * + * @retval NRF_SUCCESS If data reception was stopped successfully. + * @retval NRF_ESB_ERROR_NOT_IN_RX_MODE If the function failed because the module is not in RX mode. + */ +uint32_t nrf_esb_stop_rx(void); + + +/**@brief Function for removing remaining items from the TX buffer. + * + * This function clears the TX FIFO buffer. + * + * @retval NRF_SUCCESS If pending items in the TX buffer were successfully cleared. + * @retval NRF_INVALID_STATE If the module is not initialized. + */ +uint32_t nrf_esb_flush_tx(void); + + +/**@brief Function for removing the first item from the TX buffer. + * + * @retval NRF_SUCCESS If the operation completed successfully. + * @retval NRF_INVALID_STATE If the module is not initialized. + * @retval NRF_ERROR_BUFFER_EMPTY If there are no items in the queue to remove. + */ +uint32_t nrf_esb_pop_tx(void); + + +/**@brief Function for removing remaining items from the RX buffer. + * + * @retval NRF_SUCCESS If the pending items in the RX buffer were successfully cleared. + * @retval NRF_INVALID_STATE If the module is not initialized. + */ +uint32_t nrf_esb_flush_rx(void); + + +/**@brief Function for setting the length of the address. + * + * @param[in] length Length of the ESB address (in bytes). + * + * @retval NRF_SUCCESS If the address length was set successfully. + * @retval NRF_ERROR_INVALID_PARAM If the address length was invalid. + * @retval NRF_ERROR_BUSY If the function failed because the radio is busy. + */ +uint32_t nrf_esb_set_address_length(uint8_t length); + + +/**@brief Function for setting the base address for pipe 0. + * + * @param[in] p_addr Pointer to the address data. + * + * @retval NRF_SUCCESS If the base address was set successfully. + * @retval NRF_ERROR_BUSY If the function failed because the radio is busy. + * @retval NRF_ERROR_INVALID_PARAM If the function failed because the address given was too close to a zero address. + * @retval NRF_ERROR_NULL If the required parameter was NULL. + */ +uint32_t nrf_esb_set_base_address_0(uint8_t const * p_addr); + + +/**@brief Function for setting the base address for pipe 1 to pipe 7. + * + * @param[in] p_addr Pointer to the address data. + * + * @retval NRF_SUCCESS If the base address was set successfully. + * @retval NRF_ERROR_BUSY If the function failed because the radio is busy. + * @retval NRF_ERROR_INVALID_PARAM If the function failed because the address given was too close to a zero address. + * @retval NRF_ERROR_NULL If the required parameter was NULL. + */ +uint32_t nrf_esb_set_base_address_1(uint8_t const * p_addr); + + +/**@brief Function for setting 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] p_prefixes Pointer to a char array that contains the prefix for each pipe. + * @param[in] num_pipes Number of pipes. + * + * @retval NRF_SUCCESS If the prefix addresses were set successfully. + * @retval NRF_ERROR_BUSY If the function failed because the radio is busy. + * @retval NRF_ERROR_NULL If a required parameter was NULL. + * @retval NRF_ERROR_INVALID_PARAM If an invalid number of pipes was given or if the address given was too close to a zero address. + */ +uint32_t nrf_esb_set_prefixes(uint8_t const * p_prefixes, uint8_t num_pipes); + + +/**@brief Function for enabling pipes. + * + * The @p enable_mask parameter must contain the same number of pipes as has been configured + * with @ref nrf_esb_set_prefixes. + * + * @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 NRF_SUCCESS If the pipes were enabled and disabled successfully. + * @retval NRF_ERROR_BUSY If the function failed because the radio is busy. + * @retval NRF_ERROR_INVALID_PARAM If the function failed because the address given was too close to a zero address. + */ +uint32_t nrf_esb_enable_pipes(uint8_t enable_mask); + + +/**@brief Function for updating the prefix for a pipe. + * + * @param pipe Pipe for which to set the prefix. + * @param prefix Prefix to set for the pipe. + * + * @retval NRF_SUCCESS If the operation completed successfully. + * @retval NRF_ERROR_BUSY If the function failed because the radio is busy. + * @retval NRF_ERROR_INVALID_PARAM If the given pipe number was invalid or if the address given was too close to a zero address. + */ +uint32_t nrf_esb_update_prefix(uint8_t pipe, uint8_t prefix); + + +/** @brief Function for setting 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 NRF_SUCCESS If the operation completed successfully. + * @retval NRF_INVALID_STATE If the module is not initialized. + * @retval NRF_ERROR_BUSY If the module was not in idle state. + * @retval NRF_ERROR_INVALID_PARAM If the channel is invalid (larger than 100). + */ +uint32_t nrf_esb_set_rf_channel(uint32_t channel); + + +/**@brief Function for getting the current radio channel. + * + * @param[in, out] p_channel Pointer to the channel data. + * + * @retval NRF_SUCCESS If the operation completed successfully. + * @retval NRF_ERROR_NULL If the required parameter was NULL. + */ +uint32_t nrf_esb_get_rf_channel(uint32_t * p_channel); + + +/**@brief Function for setting the radio output power. + * + * @param[in] tx_output_power Output power. + * + * @retval NRF_SUCCESS If the operation completed successfully. + * @retval NRF_ERROR_BUSY If the function failed because the radio is busy. + */ +uint32_t nrf_esb_set_tx_power(nrf_esb_tx_power_t tx_output_power); + + +/**@brief Function for setting the packet retransmit delay. + * + * @param[in] delay Delay between retransmissions. + * + * @retval NRF_SUCCESS If the operation completed successfully. + * @retval NRF_ERROR_BUSY If the function failed because the radio is busy. + */ +uint32_t nrf_esb_set_retransmit_delay(uint16_t delay); + + +/**@brief Function for setting the number of retransmission attempts. + * + * @param[in] count Number of retransmissions. + * + * @retval NRF_SUCCESS If the operation completed successfully. + * @retval NRF_ERROR_BUSY If the function failed because the radio is busy. + */ +uint32_t nrf_esb_set_retransmit_count(uint16_t count); + + +/**@brief Function for setting the radio bitrate. + * + * @param[in] bitrate Radio bitrate. + * + * @retval NRF_SUCCESS If the operation completed successfully. + * @retval NRF_ERROR_BUSY If the function failed because the radio is busy. + */ +uint32_t nrf_esb_set_bitrate(nrf_esb_bitrate_t bitrate); + + +/**@brief Function for reusing 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 NRF_SUCCESS If the operation completed successfully. + * @retval NRF_ERROR_BUSY If the function failed because the radio is busy. + */ +uint32_t nrf_esb_reuse_pid(uint8_t pipe); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif // NRF_ESB \ No newline at end of file
diff -r a01a54c0dc90 -r 66f95e364222 nrf_esb_error_codes.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nrf_esb_error_codes.h Thu Feb 04 10:36:44 2021 +0000 @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef __NRF_ESB_ERROR_CODES_H__ +#define __NRF_ESB_ERROR_CODES_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define NRF_ERROR_BUFFER_EMPTY (0x0100) + +#define NRF_ESB_ERROR_NOT_IN_RX_MODE (0x0101) + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file
diff -r a01a54c0dc90 -r 66f95e364222 nrf_esb_resources.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nrf_esb_resources.h Thu Feb 04 10:36:44 2021 +0000 @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef NRF_ESB_RESOURCES_H__ +#define NRF_ESB_RESOURCES_H__ + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup nrf_esb_resources ESB resources + * @{ + * @ingroup nrf_esb + */ + +#ifndef ESB_ALTERNATIVE_RESOURCES + #define ESB_PPI_CHANNELS_USED 0x00000007uL /**< PPI channels used by ESB (not available to the application). */ + #define ESB_TIMERS_USED 0x00000004uL /**< Timers used by ESB. */ + #define ESB_SWI_USED 0x00000001uL /**< Software interrupts used by ESB. */ +#else + #define ESB_PPI_CHANNELS_USED 0x00000700uL /**< PPI channels used by ESB (not available to the application). */ + #define ESB_TIMERS_USED 0x00000001uL /**< Timers used by ESB. */ + #define ESB_SWI_USED 0x00000002uL /**< Software interrupts used by ESB. */ +#endif + +/** @} */ + + +#ifdef __cplusplus +} +#endif + +#endif /* NRF_ESB_RESOURCES_H__ */ \ No newline at end of file