From f72c822caa265c1864221312283a0ccb8b3a88f8 Mon Sep 17 00:00:00 2001 From: d00616 Date: Sun, 22 Nov 2020 21:29:35 +0100 Subject: [PATCH] NRF5 ESB redesign - simplified logic - add collision handling - timer controlled handling nRF52822 PANs --- hal/transport/NRF5_ESB/driver/Radio_ESB.cpp | 592 ++++++++++---------- hal/transport/NRF5_ESB/driver/Radio_ESB.h | 44 +- 2 files changed, 323 insertions(+), 313 deletions(-) diff --git a/hal/transport/NRF5_ESB/driver/Radio_ESB.cpp b/hal/transport/NRF5_ESB/driver/Radio_ESB.cpp index 538b53d16..e9f297572 100644 --- a/hal/transport/NRF5_ESB/driver/Radio_ESB.cpp +++ b/hal/transport/NRF5_ESB/driver/Radio_ESB.cpp @@ -3,11 +3,10 @@ * between your home built sensors/actuators and HA controller of choice. * The sensors formrs a self healing radio network with optional repeaters. Each * repeater and gateway builds a routing tables in EEPROM which keeps track of - * the - * network topology allowing messages to be routed to nodes. + * the network topology allowing messages to be routed to nodes. * * Created by Frank Holtz - * Copyright (C) 2017 Frank Holtz + * Copyright (C) 2017-2020 Frank Holtz * Full contributor list: * https://github.com/mysensors/MySensors/graphs/contributors * @@ -23,33 +22,33 @@ #include "Radio_ESB.h" #include "hal/architecture/NRF5/MyHwNRF5.h" #include "drivers/CircularBuffer/CircularBuffer.h" -#include // internal functions static uint8_t reverse_byte(uint8_t address); -inline void _stopTimer(); -inline void _stopACK(); +static void _stopTimer(); +#ifdef MY_DEBUG_VERBOSE_NRF5_ESB +void print_radio_stats(); +#endif // RX Buffer static NRF5_ESB_Packet rx_circular_buffer_buffer[MY_NRF5_ESB_RX_BUFFER_SIZE]; -// Poiter to rx circular buffer -static NRF5_ESB_Packet rx_buffer; +// Poiter to the active buffer +static NRF5_ESB_Packet rx_tx_buffer; // Circular buffer static CircularBuffer rx_circular_buffer(rx_circular_buffer_buffer, MY_NRF5_ESB_RX_BUFFER_SIZE); // Dedect duplicate packages for every pipe available static volatile uint32_t package_ids[8]; -// TX Buffer -static NRF5_ESB_Packet tx_buffer; -// remaining TX retries -static volatile int8_t tx_retries; -// PID number for ACK -static volatile int8_t ack_pid; // Flag for ack received static volatile bool ack_received; -// Flag for end TX event -static volatile bool events_end_tx; +// Flag for timeout event +static volatile bool events_timeout; +// Flag for address received for emergency timer +static volatile bool events_address; + +// TX PID (2 bits) +uint8_t tx_pid = 0; // Last RSSI sample provided by NRF5_ESB_readMessage static volatile int16_t rssi_rx; // Last RSSI sample by last package @@ -59,6 +58,7 @@ static uint8_t node_address = 0; // TX power level static int8_t tx_power_level = (MY_NRF5_ESB_PA_LEVEL << RADIO_TXPOWER_TXPOWER_Pos); + // Initialize radio unit static bool NRF5_ESB_initialize() { @@ -71,6 +71,10 @@ static bool NRF5_ESB_initialize() // Power on radio unit NRF_RADIO->POWER = 1; +#ifdef NRF52 + // Fix PAN#182 + *(volatile uint32_t *) 0x4000173C |= (1 << 10); +#endif // Disable shorts NRF_RADIO->SHORTS = 0; @@ -88,7 +92,7 @@ static bool NRF5_ESB_initialize() NVIC_ClearPendingIRQ(NRF5_RADIO_TIMER_IRQN); NVIC_EnableIRQ(NRF5_RADIO_TIMER_IRQN); - // Clear all events + // Clear all radio events NRF_RADIO->EVENTS_ADDRESS = 0; NRF_RADIO->EVENTS_BCMATCH = 0; NRF_RADIO->EVENTS_DEVMATCH = 0; @@ -102,8 +106,8 @@ static bool NRF5_ESB_initialize() // Disable all interrupts NRF_RADIO->INTENCLR = (uint32_t)~0; - // Select interrupt events - NRF_RADIO->INTENSET = RADIO_INTENSET_END_Msk | RADIO_INTENSET_BCMATCH_Msk; + // Select interrupt events (End of packet and bitcounter event for ACK management) + NRF_RADIO->INTENSET = NRF5_EBS_RADIO_INT_RX; // Configure radio parameters: tx power NRF_RADIO->TXPOWER = tx_power_level; @@ -114,12 +118,6 @@ static bool NRF5_ESB_initialize() // Configure radio parameters: data rate NRF_RADIO->MODE = MY_NRF5_ESB_MODE; -#ifdef NRF52 - // Configure nRF52 specific mode register - NRF_RADIO->MODECNF0 = (RADIO_MODECNF0_RU_Default << RADIO_MODECNF0_RU_Pos) | - (RADIO_MODECNF0_DTX_Center << RADIO_MODECNF0_DTX_Pos); -#endif - // Configure radio parameters: CRC16 NRF_RADIO->CRCCNF = (RADIO_CRCCNF_LEN_Two << RADIO_CRCCNF_LEN_Pos); NRF_RADIO->CRCINIT = 0xFFFFUL; @@ -158,44 +156,46 @@ static bool NRF5_ESB_initialize() (RADIO_PCNF1_ENDIAN_Big << RADIO_PCNF1_ENDIAN_Pos) | // Big endian (RADIO_PCNF1_WHITEEN_Disabled << RADIO_PCNF1_WHITEEN_Pos); // Disable whitening - // HINT: Fast ramp up can enabled here. Needs more code on other lines - // Fast ramp up isn't supported by nRF24 and NRF51 series. +#ifdef RADIO_MODECNF0_RU_Default + // Configure nRF52 specific mode register + NRF_RADIO->MODECNF0 = (RADIO_MODECNF0_RU_Default << RADIO_MODECNF0_RU_Pos) | + (RADIO_MODECNF0_DTX_Center << RADIO_MODECNF0_DTX_Pos); +#endif - // Set bitcounter to trigger interrupt after ACK bit - NRF_RADIO->BCC = NRF5_ESB_BITCOUNTER; +#ifdef NRF52 + // Fix PAN#102 and PAN#106 + //*((volatile uint32_t *)0x40001774) = (*((volatile uint32_t *)0x40001774) & 0xFFFFFFFE) | 0x01000000; +#endif #ifdef NRF51 // Enable timer NRF5_RADIO_TIMER->POWER = 1; #endif + + // Configure DMA target address + NRF_RADIO->PACKETPTR = (uint32_t)&rx_tx_buffer; + // Stop timer, if running _stopTimer(); + + // Reset address event flag used for emergency reset timer + events_address = false; + // Prepare timer running at 1 MHz/1us NRF5_RADIO_TIMER->PRESCALER = 4; // Timer mode NRF5_RADIO_TIMER->MODE = TIMER_MODE_MODE_Timer; // in 16 Bit mode NRF5_RADIO_TIMER->BITMODE = TIMER_BITMODE_BITMODE_16Bit << TIMER_BITMODE_BITMODE_Pos; - // Stop timer when CC0 reached + // Stop timer when CC3 reached NRF5_RADIO_TIMER->SHORTS = TIMER_SHORTS_COMPARE3_CLEAR_Msk | TIMER_SHORTS_COMPARE3_STOP_Msk; - // Reset timer - NRF5_RADIO_TIMER->TASKS_CLEAR = 1; - - // Reset compare events -#ifdef NRF51 - for (uint8_t i=0; i<4; i++) { -#else - for (uint8_t i=0; i<6; i++) { -#endif - NRF5_RADIO_TIMER->EVENTS_COMPARE[i] = 0; - } - // Enable interrupt - NRF5_RADIO_TIMER->INTENSET = TIMER_INTENSET_COMPARE1_Enabled << TIMER_INTENSET_COMPARE1_Pos; + NRF5_RADIO_TIMER->INTENCLR = (uint32_t)~0;; + NRF5_RADIO_TIMER->INTENSET = TIMER_INTENSET_COMPARE3_Enabled << TIMER_INTENSET_COMPARE3_Pos; + #ifdef MY_DEBUG_VERBOSE_NRF5_ESB - intcntr_bcmatch=0; intcntr_ready=0; intcntr_end=0; #endif @@ -277,15 +277,7 @@ static void NRF5_ESB_startListening() NRF5_ESB_initialize(); } -#ifdef NRF52 - // Fix PAN#102 and PAN#106 - *((volatile uint32_t *)0x40001774) = (*((volatile uint32_t *)0x40001774) & 0xFFFFFFFE) | 0x01000000; -#endif - - // Enable Ready interrupt - NRF_RADIO->INTENSET = RADIO_INTENSET_READY_Msk; - - // Enable RX when ready, Enable RX after disabling task + // Bring radio into RX mode NRF_RADIO->SHORTS = NRF5_ESB_SHORTS_RX; // Switch to RX @@ -328,96 +320,15 @@ static uint8_t NRF5_ESB_readMessage(void *data) return ret; } -void NRF5_ESB_endtx(); -void NRF5_ESB_starttx() -{ - if (tx_retries > 0) { - // Prevent radio to write into TX memory while receiving - if (NRF_RADIO->PACKETPTR != (uint32_t)&tx_buffer) { - // Disable shorts - NRF_RADIO->SHORTS = 0; - // Disable radio - NRF_RADIO->TASKS_DISABLE = 1; - } - - // Mark TX as unfinised - events_end_tx = false; - - // Configure TX address to address at index NRF5_ESB_TX_ADDR - NRF_RADIO->TXADDRESS = NRF5_ESB_TX_ADDR; - - // Enable TX when ready, Enable TX after disabling task - NRF_RADIO->SHORTS = NRF5_ESB_SHORTS_TX; - - // reset timer - NRF_RESET_EVENT(NRF5_RADIO_TIMER->EVENTS_COMPARE[3]); - _stopTimer(); - NRF5_RADIO_TIMER->TASKS_CLEAR = 1; - // Set retransmit time - NRF5_RADIO_TIMER->CC[3] = NRF5_ESB_ARD - NRF5_ESB_RAMP_UP_TIME; - // Set radio disable time to ACK_WAIT time - NRF5_RADIO_TIMER->CC[1] = NRF5_ESB_ACK_WAIT; - - /** Configure PPI (Programmable peripheral interconnect) */ - // Start timer on END event - NRF_PPI->CH[NRF5_ESB_PPI_TIMER_START].EEP = (uint32_t)&NRF_RADIO->EVENTS_END; - NRF_PPI->CH[NRF5_ESB_PPI_TIMER_START].TEP = (uint32_t)&NRF5_RADIO_TIMER->TASKS_START; -#ifdef NRF52 - NRF_PPI->FORK[NRF5_ESB_PPI_TIMER_START].TEP = 0; -#endif - -#ifndef NRF5_ESB_USE_PREDEFINED_PPI - // Disable Radio after CC[1] - NRF_PPI->CH[NRF5_ESB_PPI_TIMER_RADIO_DISABLE].EEP = (uint32_t)&NRF5_RADIO_TIMER->EVENTS_COMPARE[1]; - NRF_PPI->CH[NRF5_ESB_PPI_TIMER_RADIO_DISABLE].TEP = (uint32_t)&NRF_RADIO->TASKS_DISABLE; -#ifdef NRF52 - NRF_PPI->CH[NRF5_ESB_PPI_TIMER_RADIO_DISABLE].TEP = 0; -#endif -#endif - - // Set PPI - NRF_PPI->CHENSET = NRF5_ESB_PPI_BITS; - - // Disable Ready interrupt - NRF_RADIO->INTENCLR = RADIO_INTENSET_READY_Msk; - - // Set buffer - NRF_RADIO->PACKETPTR = (uint32_t)&tx_buffer; - - // Switch to TX - if (NRF_RADIO->STATE == RADIO_STATE_STATE_Disabled) { - NRF_RADIO->TASKS_TXEN = 1; - } else { - NRF_RADIO->TASKS_DISABLE = 1; - } - } else { - // finised TX - NRF5_ESB_endtx(); - } - tx_retries--; -} - -void NRF5_ESB_endtx() -{ - // Clear PPI - NRF_PPI->CHENCLR = NRF5_ESB_PPI_BITS; - // Enable Ready interrupt - NRF_RADIO->INTENSET = RADIO_INTENSET_READY_Msk; - // Stop Timer - _stopTimer(); - // Mark TX as end - events_end_tx = true; - // Debug output -#ifdef NRF5_ESB_DEBUG_INT_TX_END - NRF5_RADIO_DEBUG(PSTR("NRF5:INT:ENDTX\n")); -#endif -} - static bool NRF5_ESB_sendMessage(uint8_t recipient, const void *buf, uint8_t len, const bool noACK) { + // Increment TX PID + tx_pid++; + NRF5_RADIO_DEBUG(PSTR("NRF5:SND:TO=%" PRIu8 ",LEN=%" PRIu8 ",PID=%" PRIu8 ",NOACK=%" PRIu8 "\n"), - recipient, len, tx_buffer.pid, - tx_buffer.noack); // send message + recipient, len, tx_pid, + noACK); // send message + // Check if radio is initialized if (NRF_RADIO->POWER == 0) { NRF5_ESB_initialize(); @@ -428,20 +339,8 @@ static bool NRF5_ESB_sendMessage(uint8_t recipient, const void *buf, uint8_t len len = MAX_MESSAGE_SIZE; } - // copy data to tx_buffer - memcpy(&tx_buffer.data[0], buf, len); - - // build metadata - tx_buffer.len = len; -#ifndef MY_NRF5_ESB_REVERSE_ACK_TX - tx_buffer.noack = noACK || recipient==BROADCAST_ADDRESS; -#else - // reverse the noack bit - tx_buffer.noack = !(noACK || recipient==BROADCAST_ADDRESS); -#endif - tx_buffer.pid++; - // Calculate number of retries + int8_t tx_retries; if (recipient == BROADCAST_ADDRESS) { tx_retries = NRF5_ESB_BC_ARC; } else { @@ -449,51 +348,122 @@ static bool NRF5_ESB_sendMessage(uint8_t recipient, const void *buf, uint8_t len } int8_t tx_retries_start = tx_retries; ack_received = false; +#ifdef MY_DEBUG_VERBOSE_NRF5_ESB + int wakeups=0; + intcntr_addrmatch = 0; + intcntr_end = 0; + intcntr_disabled = 0; + intcntr_timer_cc3 = 0; +#endif - // configure TX address - NRF_RADIO->PREFIX1 = (NRF_RADIO->PREFIX1 & NRF5_ESB_TX_ADDR_MSK) | - (reverse_byte(recipient) << (NRF5_ESB_TX_ADDR - 4)); - - // Enable listening on Node, BC and TX address - NRF_RADIO->RXADDRESSES = (1 << NRF5_ESB_NODE_ADDR) | (1 << NRF5_ESB_BC_ADDR) | - (1 << NRF5_ESB_TX_ADDR); + /** Loop until tx_retries == 0 + * Because the radio is in RX state between TX + * the radio is initialized with each iteration + */ + while ((tx_retries > 0) && (ack_received == false)) { + tx_retries--; + /** Wait until RX is end, if any activity + * Wait a maximum of 10ms + */ + uint8_t max_wait = 10; + while ((events_address) && (max_wait>0)) { + // Sleep until next interrupt + hwSleep(1); + max_wait--; + } - // Set RSSI to invalid - rssi_tx = INVALID_RSSI; + // Stop RX + NRF_RADIO->SHORTS = 0; + NRF_RADIO->TASKS_DISABLE = 1; - NRF5_ESB_starttx(); + /** Prepare Buffer + **/ + // copy data to tx_buffer + memcpy(&rx_tx_buffer.data[0], buf, len); - // Wait for end of transmission -#ifdef MY_DEBUG_VERBOSE_NRF5_ESB - uint32_t wakeups = 0; + // build metadata + rx_tx_buffer.len = len; +#ifndef MY_NRF5_ESB_REVERSE_ACK_TX + rx_tx_buffer.noack = noACK || recipient==BROADCAST_ADDRESS; +#else + // reverse the noack bit + rx_tx_buffer.noack = !(noACK || recipient==BROADCAST_ADDRESS); #endif - while (events_end_tx == false) { - // Power off CPU until next interrupt - hwSleep(); - // hwWaitForInterrupt(); + rx_tx_buffer.pid = tx_pid; + + // Set RSSI to invalid + rssi_tx = INVALID_RSSI; + + // Reset event flags + events_timeout = false; + + /** Configure radio parameters + **/ + // configure TX address + NRF_RADIO->PREFIX1 = (NRF_RADIO->PREFIX1 & NRF5_ESB_TX_ADDR_MSK) | + (reverse_byte(recipient) << (NRF5_ESB_TX_ADDR - 4)); + // Enable listening on Node, BC and TX address + NRF_RADIO->RXADDRESSES = (1 << NRF5_ESB_NODE_ADDR) | (1 << NRF5_ESB_BC_ADDR) | + (1 << NRF5_ESB_TX_ADDR); + // Configure TX address to address at index NRF5_ESB_TX_ADDR + NRF_RADIO->TXADDRESS = NRF5_ESB_TX_ADDR; + // Enable TX when ready, Enable TX after disabling task + NRF_RADIO->SHORTS = NRF5_ESB_SHORTS_TX; + // Disable all interrupts + NRF_RADIO->INTENCLR = (uint32_t)~0; + // Select interrupt events for TX + NRF_RADIO->INTENSET = NRF5_EBS_RADIO_INT_TX; + + /** Configure timer + * CC[3] is used for retransmit and timeout + **/ + // reset timer + _stopTimer(); + + if (tx_retries > 0) { + // Set retransmit time with a litte jitter + NRF5_RADIO_TIMER->CC[3] = NRF5_ESB_ARD + NRF5_ESB_MAX_PACKET_TIME + (tx_pid << + NRF5_ESB_byte_time()); + } else { + // Wait for ACK with a litte jitter + NRF5_RADIO_TIMER->CC[3] = NRF5_ESB_ARD + NRF5_ESB_ACK_WAIT + NRF5_ESB_MAX_PACKET_TIME + + (tx_pid << NRF5_ESB_byte_time());; + } + + /** Start TX and timer + **/ + NRF_RADIO->TASKS_TXEN = 1; + //NRF5_RADIO_TIMER->TASKS_START = 1; + + /** wait until ACK is seen or timeout + **/ + // Wait for end of transmission + while ((ack_received == false) && (events_timeout==false)) { + // Power off CPU until next interrupt + hwSleep(); #ifdef MY_DEBUG_VERBOSE_NRF5_ESB - wakeups++; + wakeups++; #endif + } } + // reset timer + _stopTimer(); + // Calculate RSSI - if (rssi_tx == INVALID_RSSI) { + if ((ack_received == true) && (rssi_tx == INVALID_RSSI)) { // calculate pseudo-RSSI based on retransmission counter (ARC) // min -104dBm at 250kBps // Arbitrary definition: ARC 0 == -29, ARC 15 = -104 rssi_tx = (-29 - (8 * (tx_retries_start - tx_retries))); } - // Enable listening on Node and BC address - NRF_RADIO->RXADDRESSES = (1 << NRF5_ESB_NODE_ADDR) | (1 << NRF5_ESB_BC_ADDR); - #ifdef MY_DEBUG_VERBOSE_NRF5_ESB - NRF5_RADIO_DEBUG(PSTR("NRF5:SND:END=%" PRIu8 ",ACK=%" PRIu8 ",RTRY=%" PRIi8 ",RSSI=%" PRIi16 + NRF5_RADIO_DEBUG(PSTR("NRF5:SND:ACK=%" PRIu8 ",RTRY=%" PRIi8 ",RSSI=%" PRIi16 ",WAKE=%" PRIu32 "\n"), - events_end_tx, ack_received, tx_retries_start - tx_retries, rssi_tx, wakeups); + ack_received, tx_retries_start - tx_retries, rssi_tx, wakeups); + print_radio_stats(); #endif - - return ack_received; }; @@ -511,6 +481,29 @@ static int16_t NRF5_ESB_getReceivingRSSI() * Internal helper functions */ +#ifdef MY_DEBUG_VERBOSE_NRF5_ESB +// Print interrupt counter statistics +void print_radio_stats() +{ + NRF5_RADIO_DEBUG(PSTR("NRF5:INTCTR:ADDRM=%" PRIu32 ",END=%" PRIu32 ",DISABLED=%" PRIu32 ",TMRC3=%" + PRIu32 "\n"), + intcntr_addrmatch, intcntr_end, intcntr_disabled, intcntr_timer_cc3); + intcntr_addrmatch = 0; + intcntr_end = 0; + intcntr_disabled = 0; + intcntr_timer_cc3 = 0; + + NRF5_RADIO_DEBUG(PSTR("NRF5:REGISTERS:STATE=0x%" PRIX32 ",SHORTS=0x%" PRIX32 ",INTENSET=0x%" PRIX32 + ",POWER=0x%" PRIX32 "\n"), + NRF_RADIO->STATE, NRF_RADIO->SHORTS,NRF_RADIO->INTENSET, NRF_RADIO->POWER); + NRF5_RADIO_DEBUG(PSTR("NRF5:ADDR:PREFIX0=0x04%" PRIX32 ",PREFIX1=0x04%" PRIX32 + ",TXADDRESS=0x%" PRIX32 ",RXADDRESSES=0x%" PRIX32 ",RXMATCH=0x%" PRIX32 "\n"), + NRF_RADIO->PREFIX0, NRF_RADIO->PREFIX1,NRF_RADIO->TXADDRESS, + NRF_RADIO->RXADDRESSES, NRF_RADIO->RXMATCH); +} +#endif + + // Reverse a byte for address static uint8_t reverse_byte(uint8_t address) { @@ -522,20 +515,31 @@ static uint8_t reverse_byte(uint8_t address) return address; } -inline void _stopTimer() + +static void _stopTimer() { // Stop timer NRF5_RADIO_TIMER->TASKS_STOP = 1; // NRF52 PAN#78 NRF5_RADIO_TIMER->TASKS_SHUTDOWN = 1; + // Reset timer + NRF5_RADIO_TIMER->TASKS_CLEAR = 1; + // Reset compare events +#ifdef NRF51 + for (uint8_t i=0; i<4; i++) { +#else + for (uint8_t i=0; i<6; i++) { +#endif + NRF_RESET_EVENT(NRF5_RADIO_TIMER->EVENTS_COMPARE[i]); + } + // Maximum time between ADDRESS and END event + NRF5_RADIO_TIMER->CC[3] = NRF5_ESB_MAX_PACKET_TIME; } +// Switch back to RX without ACK inline void _stopACK() { - // Enable RX when ready, Enable RX after disabling task NRF_RADIO->SHORTS = NRF5_ESB_SHORTS_RX; - - // Start disabling radio -> switch to rx by shorts NRF_RADIO->TASKS_DISABLE = 1; } @@ -556,137 +560,127 @@ extern "C" { /** Radio Interrupt handler */ void RADIO_IRQHandler() { - /** Bitcounter event is used to switch between RX/TX - * In RX mode, when an ACK required packet is received, switch to TX, - * elsewhere start RX again. - * In TX mode switch always to RX. - */ - if (NRF_RADIO->EVENTS_BCMATCH == 1) { - NRF_RESET_EVENT(NRF_RADIO->EVENTS_BCMATCH); + // Address is send or received + if (NRF_RADIO->EVENTS_ADDRESS == 1) { + NRF_RESET_EVENT(NRF_RADIO->EVENTS_ADDRESS); + // Start emergency timer to dedect missed END-Events (nRF52832 PAN#102) or ACKs + NRF5_RADIO_TIMER->TASKS_START = 1; + // Set flag for timer interrupt + events_address = true; #ifdef MY_DEBUG_VERBOSE_NRF5_ESB - intcntr_bcmatch++; + intcntr_addrmatch++; #endif - // Disable bitcounter - NRF_RADIO->TASKS_BCSTOP = 1; - - // In RX mode -> prepare ACK or RX - if (NRF_RADIO->STATE == RADIO_STATE_STATE_Rx) { - // Send ACK only for node address, don't care about the ACK bit to handle bad nRF24 clones + /** What to do after RX/TX END? + * The radio is switching to RX after each RX or TX packet + * expect a packet was addressed to the node address. + **/ + if (NRF_RADIO->STATE >= RADIO_STATE_STATE_TxRu) { + // TX-Mode -> switch to RX + NRF_RADIO->SHORTS = NRF5_ESB_SHORTS_TX_RX; + // Disable all interrupts + NRF_RADIO->INTENCLR = (uint32_t)~0; + // Select interrupt events for RX + NRF_RADIO->INTENSET = NRF5_EBS_RADIO_INT_RX; + } else { + /* Regular or ACK packet + * After an RX packet, the radio is keept in RX mode + */ if (NRF_RADIO->RXMATCH == NRF5_ESB_NODE_ADDR) { - // Send ACK after END, an empty packet is provided in READY event + // Switch to TX for ACK after END. + // noACK flag is ignored for compatibility with NRF24 clones NRF_RADIO->SHORTS = NRF5_ESB_SHORTS_RX_TX; } else { - // No ACK -> Start RX after END + // Switch to RX after END NRF_RADIO->SHORTS = NRF5_ESB_SHORTS_RX; } - - // Handle incoming ACK packet - if (NRF_RADIO->RXMATCH == NRF5_ESB_TX_ADDR) { - /** Calculate time to switch radio off - * This is an ACK packet, the radio is disabled by Timer - * event after CC[1], calculate the time switching of the - * radio. - */ - // Read current timer value - NRF5_RADIO_TIMER->TASKS_CAPTURE[1] = 1; - - // Set Timer compare register 0 to end of packet (len+CRC) - NRF5_RADIO_TIMER->CC[1] += ((rx_buffer.len + 3) << NRF5_ESB_byte_time()); - } - } else { - // Current mode is TX: - // After TX the Radio has to be always in RX mode to - // receive ACK or start implicit listen mode after send. - NRF_RADIO->SHORTS = NRF5_ESB_SHORTS_TX_RX; - // HINT: Fast ramp up can enabled here. Needs more code on other lines + // Stop listening on TX_ADDR. Enable listening on node and broadcast address + NRF_RADIO->RXADDRESSES = (1 << NRF5_ESB_NODE_ADDR) | (1 << NRF5_ESB_BC_ADDR); } - } - /** Ready event is generated before RX starts - * An free rx buffer is allocated or radio is disabled on failures - */ - if (NRF_RADIO->EVENTS_READY == 1) { - NRF_RESET_EVENT(NRF_RADIO->EVENTS_READY); -#ifdef MY_DEBUG_VERBOSE_NRF5_ESB - intcntr_ready++; -#endif - // Configure DMA target address - NRF_RADIO->PACKETPTR = (uint32_t)&rx_buffer; - - /* Don't care about if next packet RX or ACK, - * prepare current rx_buffer to send an ACK */ - - // Set outgoing address to node address for ACK packages + // Reset TX address to node address for ACK NRF_RADIO->TXADDRESS = NRF5_ESB_NODE_ADDR; } - /** This event is generated after TX or RX finised + /** This event generated only after RX ends */ if (NRF_RADIO->EVENTS_END == 1) { + // Reset END-Event NRF_RESET_EVENT(NRF_RADIO->EVENTS_END); + // Reset emergency timer reset flag + events_address = false; + #ifdef MY_DEBUG_VERBOSE_NRF5_ESB intcntr_end++; #endif - - // Enable ACK bitcounter for next packet - NRF_RADIO->BCC = NRF5_ESB_BITCOUNTER; - - // End of RX packet - if ((NRF_RADIO->STATE == RADIO_STATE_STATE_Rx) or - (NRF_RADIO->STATE == RADIO_STATE_STATE_RxIdle) or - (NRF_RADIO->STATE == RADIO_STATE_STATE_RxDisable) or - (NRF_RADIO->STATE == RADIO_STATE_STATE_TxRu)) { - if (NRF_RADIO->CRCSTATUS) { - // Ensure no ACK package is received - if (NRF_RADIO->RXMATCH != NRF5_ESB_TX_ADDR) { - // calculate a package id - uint32_t pkgid = rx_buffer.pid << 16 | NRF_RADIO->RXCRC; - if (pkgid != package_ids[NRF_RADIO->RXMATCH]) { - // correct package -> store id to dedect duplicates - package_ids[NRF_RADIO->RXMATCH] = pkgid; - rx_buffer.rssi = NRF_RADIO->RSSISAMPLE; + // Check CRC + if (NRF_RADIO->CRCSTATUS == 0) { + // discard RX data + _stopACK(); + } else { #ifdef MY_DEBUG_VERBOSE_NRF5_ESB - // Store debug data - rx_buffer.rxmatch = NRF_RADIO->RXMATCH; -#endif - // Push data to buffer - if (rx_circular_buffer.pushFront(&rx_buffer)) { - // Prepare ACK package - rx_buffer.data[0]=rx_buffer.rssi; - rx_buffer.len=1; // data[0] is set some lines before -#ifndef MY_NRF5_ESB_REVERSE_ACK_TX - rx_buffer.noack = 1; -#else - rx_buffer.noack = 0; + // Store debug data + rx_tx_buffer.rxmatch = NRF_RADIO->RXMATCH; #endif - } else { - // Buffer is full - // Stop ACK - _stopACK(); - // Increment pkgid allowing receive the package again - package_ids[NRF_RADIO->RXMATCH]++; - } - } - } else { - // ACK package received, ducplicates are accepted - - // rssi value in ACK included? - if (rx_buffer.len == 1) { - rssi_tx = 0-rx_buffer.data[0]; + // Store RSSI sample + rx_tx_buffer.rssi = NRF_RADIO->RSSISAMPLE; + + // Addressed to RX or TX address? + if ((NRF_RADIO->RXMATCH == NRF5_ESB_NODE_ADDR) || + (NRF_RADIO->RXMATCH == NRF5_ESB_BC_ADDR)) { + /** ignore doubled packages + * calculate a package id from PID and CRC + **/ + uint32_t pkgid = rx_tx_buffer.pid << 16 | NRF_RADIO->RXCRC; + if (pkgid != package_ids[NRF_RADIO->RXMATCH]) { + // Add package to rx buffer + if (rx_circular_buffer.pushFront(&rx_tx_buffer)) { + // correct package -> store id to dedect duplicates + package_ids[NRF_RADIO->RXMATCH] = pkgid; + } else { + /** buffer has reached capacity -> stop ACK + switch back to RX + **/ + _stopACK(); } + } + } else { + /** ACK to TX address received? + * An ACK paket can have a payload of + * 0, 1 or 2 bytes. Longer ACKs are ignored + * The RSSI sample is the first byte + * The second byte can be used for protocol extensions + **/ + if (rx_tx_buffer.len <= 2) { // notify TX process ack_received = true; - // End TX - NRF5_ESB_endtx(); + // rssi value in ACK included? + if (rx_tx_buffer.len > 1) { + rssi_tx = 0-rx_tx_buffer.data[0]; + } } - } else { - /** Invalid CRC -> Switch back to RX, Stop sending ACK */ - _stopACK(); } - } else { - // TX end + + /** prepare ACK + * The ACK is send, when the radio has to switch to TX + * otherwise the buffer content is ignored + **/ + rx_tx_buffer.data[0]=rx_tx_buffer.rssi; + rx_tx_buffer.len=1; +#ifndef MY_NRF5_ESB_REVERSE_ACK_TX + rx_tx_buffer.noack = 1; +#else + rx_tx_buffer.noack = 0; +#endif } } + // Handle Disabled event (used in NRF5_ESB_sendMessage) + if (NRF_RADIO->EVENTS_DISABLED == 1) { + NRF_RESET_EVENT(NRF_RADIO->EVENTS_DISABLED); + events_address = false; +#ifdef MY_DEBUG_VERBOSE_NRF5_ESB + intcntr_disabled++; +#endif + } } /** Timer Interrupt Handler @@ -695,16 +689,24 @@ extern "C" { */ void NRF5_RADIO_TIMER_IRQ_HANDLER() { + // TX Timeout (ARD, ACK) if (NRF5_RADIO_TIMER->EVENTS_COMPARE[3] == 1) { - _stopTimer(); - NRF_RESET_EVENT(NRF5_RADIO_TIMER->EVENTS_COMPARE[1]); - if (ack_received == false) { - // missing ACK, start TX again - NRF5_ESB_starttx(); - } else { - // finised TX - NRF5_ESB_endtx(); + NRF_RESET_EVENT(NRF5_RADIO_TIMER->EVENTS_COMPARE[3]); + + // Timeout after address event? + if (events_address) { + // Force switch radio to RX after disable + NRF_RADIO->SHORTS = NRF5_ESB_SHORTS_RX; + // Disable Radio + NRF_RADIO->TASKS_DISABLE = 1; } + // Set timeout event flag + events_timeout = true; + // Reset address flag + events_address = false; +#ifdef MY_DEBUG_VERBOSE_NRF5_ESB + intcntr_timer_cc3++; +#endif } } } // extern "C" diff --git a/hal/transport/NRF5_ESB/driver/Radio_ESB.h b/hal/transport/NRF5_ESB/driver/Radio_ESB.h index bcd37640b..55f9d43b1 100644 --- a/hal/transport/NRF5_ESB/driver/Radio_ESB.h +++ b/hal/transport/NRF5_ESB/driver/Radio_ESB.h @@ -46,11 +46,16 @@ #define NRF5_ESB_ACK_WAIT \ ((NRF5_ESB_RAMP_UP_TIME << 1) + (9 << NRF5_ESB_byte_time())) +/** Maximum time to transmit a complete packet after address event + **/ +#define NRF5_ESB_MAX_PACKET_TIME \ + ((MAX_MESSAGE_SIZE+sizeof(nrf5_radio_packet_s)) << NRF5_ESB_byte_time()) + // auto retry delay in us, don't set this value < 1500us@250kbit #define NRF5_ESB_ARD (1500) // auto retry count with noACK is false -#define NRF5_ESB_ARC_ACK (15) +#define NRF5_ESB_ARC_ACK (3) // auto retry count with noACK is true #define NRF5_ESB_ARC_NOACK (3) @@ -66,30 +71,36 @@ #define NRF5_ESB_TX_ADDR (4) #define NRF5_ESB_TX_ADDR_MSK (0xffffff00UL) -// BC address index +// Broadcast address index #define NRF5_ESB_BC_ADDR (7) #define NRF5_ESB_BC_ADDR_MSK (0xffffffffUL) +// Interrupt mask RX +#define NRF5_EBS_RADIO_INT_RX (RADIO_INTENSET_ADDRESS_Msk | RADIO_INTENSET_END_Msk | RADIO_INTENSET_DISABLED_Msk) + +// Interrupt mask TX +#define NRF5_EBS_RADIO_INT_TX (RADIO_INTENSET_ADDRESS_Msk | RADIO_INTENSET_DISABLED_Msk) + // Shorts for RX mode #define NRF5_ESB_SHORTS_RX \ - (RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_START_Msk | \ - RADIO_SHORTS_DISABLED_RXEN_Msk | RADIO_SHORTS_ADDRESS_BCSTART_Msk | \ + (RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_START_Msk | \ + RADIO_SHORTS_DISABLED_RXEN_Msk | \ RADIO_SHORTS_ADDRESS_RSSISTART_Msk | RADIO_SHORTS_DISABLED_RSSISTOP_Msk) // Shorts for TX mode #define NRF5_ESB_SHORTS_TX \ - (RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_START_Msk | \ - RADIO_SHORTS_DISABLED_TXEN_Msk | RADIO_SHORTS_ADDRESS_BCSTART_Msk) + (RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_START_Msk | \ + RADIO_SHORTS_DISABLED_TXEN_Msk) // Shorts to switch from RX to TX #define NRF5_ESB_SHORTS_RX_TX \ - (RADIO_SHORTS_END_DISABLE_Msk | RADIO_SHORTS_DISABLED_TXEN_Msk | \ - RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_ADDRESS_BCSTART_Msk) + (RADIO_SHORTS_END_DISABLE_Msk | RADIO_SHORTS_DISABLED_TXEN_Msk | \ + RADIO_SHORTS_READY_START_Msk) // Shorts to switch from TX to RX #define NRF5_ESB_SHORTS_TX_RX \ - (RADIO_SHORTS_END_DISABLE_Msk | RADIO_SHORTS_DISABLED_RXEN_Msk | \ - RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_ADDRESS_BCSTART_Msk | \ + (RADIO_SHORTS_END_DISABLE_Msk | RADIO_SHORTS_DISABLED_RXEN_Msk | \ + RADIO_SHORTS_READY_START_Msk | \ RADIO_SHORTS_ADDRESS_RSSISTART_Msk | RADIO_SHORTS_DISABLED_RSSISTOP_Msk) // PPI Channels for TX @@ -103,15 +114,10 @@ #define NRF5_ESB_PPI_TIMER_START 15 #define NRF5_ESB_PPI_TIMER_RADIO_DISABLE 22 #endif -#define NRF5_ESB_PPI_BITS \ - ((1 << NRF5_ESB_PPI_TIMER_START) | \ +#define NRF5_ESB_PPI_BITS \ + ((1 << NRF5_ESB_PPI_TIMER_START) | \ (1 << NRF5_ESB_PPI_TIMER_RADIO_DISABLE)) -/** Bitcounter for Packet Control Field length - * 6 Bits address length + 3 Bits S1 (NOACK + PID) - */ -#define NRF5_ESB_BITCOUNTER (9) - /** ramp up time * Time to activate radio TX or RX mode */ @@ -166,9 +172,11 @@ typedef struct nrf5_radio_packet_s { } NRF5_ESB_Packet; #ifdef MY_DEBUG_VERBOSE_NRF5_ESB -static uint32_t intcntr_bcmatch; +static uint32_t intcntr_addrmatch; static uint32_t intcntr_ready; static uint32_t intcntr_end; +static uint32_t intcntr_disabled; +static uint32_t intcntr_timer_cc3; #endif #endif // __NRF5_H__