diff --git a/MyConfig.h b/MyConfig.h index ce3dcbe..5c7bea8 100644 --- a/MyConfig.h +++ b/MyConfig.h @@ -503,10 +503,16 @@ #endif /** - * @def MY_NRF5_ESB_REVERSE_ACK - * @brief Switch to SI24R1 or faked nRF24L01+ compatible ACK mode. + * @def MY_NRF5_ESB_REVERSE_ACK_TX + * @brief Switch to SI24R1 or faked nRF24L01+ compatible ACK mode. ACK bit is reversed on TX side. */ -//#define MY_NRF5_ESB_REVERSE_ACK +//#define MY_NRF5_ESB_REVERSE_ACK_TX + +/** + * @def MY_NRF5_ESB_REVERSE_ACK_RX + * @brief Switch to SI24R1 or faked nRF24L01+ compatible ACK mode. ACK bit is reversed on RX side. + */ +//#define MY_NRF5_ESB_REVERSE_ACK_RX /** @}*/ // End of NRF5SettingGrpPub group /** @@ -1865,7 +1871,8 @@ // NRF5_ESB #define MY_NRF5_ESB_ENABLE_ENCRYPTION #define MY_DEBUG_VERBOSE_NRF5_ESB -#define MY_NRF5_ESB_REVERSE_ACK +#define MY_NRF5_ESB_REVERSE_ACK_RX +#define MY_NRF5_ESB_REVERSE_ACK_TX // RFM69 #define MY_IS_RFM69HW #define MY_RFM69_NEW_DRIVER diff --git a/MySensors.h b/MySensors.h index 4289d2f..e60234d 100644 --- a/MySensors.h +++ b/MySensors.h @@ -61,7 +61,7 @@ #include "hal/architecture/MyHwSAMD.cpp" #elif defined(ARDUINO_ARCH_STM32F1) #include "hal/architecture/MyHwSTM32F1.cpp" -#elif defined(ARDUINO_ARCH_NRF5) +#elif defined(ARDUINO_ARCH_NRF5) || defined(ARDUINO_ARCH_NRF52) #include "drivers/NVM/VirtualPage.cpp" #include "drivers/NVM/NVRAM.cpp" #include "hal/architecture/MyHwNRF5.cpp" @@ -291,7 +291,7 @@ MY_DEFAULT_RX_LED_PIN in your sketch instead to enable LEDs // POWER PIN #ifndef DOXYGEN -#if defined(MY_RF24_POWER_PIN) || defined(MY_RFM69_POWER_PIN) || defined(MY_RFM95_POWER_PIN) +#if defined(MY_RF24_POWER_PIN) || defined(MY_RFM69_POWER_PIN) || defined(MY_RFM95_POWER_PIN) || defined(MY_RADIO_NRF5_ESB) #define RADIO_CAN_POWER_OFF (true) #else #define RADIO_CAN_POWER_OFF (false) @@ -384,8 +384,6 @@ MY_DEFAULT_RX_LED_PIN in your sketch instead to enable LEDs #if !defined(MY_CORE_ONLY) #if defined(ARDUINO_ARCH_ESP8266) #include "hal/architecture/MyMainESP8266.cpp" -#elif defined(ARDUINO_ARCH_NRF5) -#include "hal/architecture/MyMainNRF5.cpp" #elif defined(__linux__) #include "hal/architecture/MyMainLinux.cpp" #elif defined(ARDUINO_ARCH_STM32F1) diff --git a/drivers/NRF5/Radio.h b/drivers/NRF5/Radio.h index 0422190..dc38fd5 100644 --- a/drivers/NRF5/Radio.h +++ b/drivers/NRF5/Radio.h @@ -23,14 +23,10 @@ #ifndef __NRF_RADIO_H__ #define __NRF_RADIO_H__ -#if !defined(ARDUINO) +#if !defined(ARDUINO_ARCH_NRF5) #error "NRF5 Radio is not supported for this platform." #endif -#if defined(NRF5_SOFTDEVICE) -#warning "NRF5 SoftDevice cannot be used with NRF5 Radio." -#endif - #include #include #include diff --git a/drivers/NRF5/Radio_ESB.cpp b/drivers/NRF5/Radio_ESB.cpp index 202864a..dfa6e2e 100644 --- a/drivers/NRF5/Radio_ESB.cpp +++ b/drivers/NRF5/Radio_ESB.cpp @@ -70,6 +70,12 @@ static bool NRF5_ESB_initialize() // Power on radio unit NRF_RADIO->POWER = 1; + // Disable shorts + NRF_RADIO->SHORTS = 0; + + // Disable radio + NRF_RADIO->TASKS_DISABLE = 1; + // Enable radio interrupt NVIC_SetPriority(RADIO_IRQn, 1); NVIC_ClearPendingIRQ(RADIO_IRQn); @@ -80,10 +86,16 @@ static bool NRF5_ESB_initialize() NVIC_ClearPendingIRQ(NRF5_RADIO_TIMER_IRQN); NVIC_EnableIRQ(NRF5_RADIO_TIMER_IRQN); - // Clear events - NRF_RADIO->EVENTS_END = 0; - NRF_RADIO->EVENTS_READY = 0; + // Clear all events + NRF_RADIO->EVENTS_ADDRESS = 0; NRF_RADIO->EVENTS_BCMATCH = 0; + NRF_RADIO->EVENTS_DEVMATCH = 0; + NRF_RADIO->EVENTS_DEVMISS = 0; + NRF_RADIO->EVENTS_DISABLED = 0; + NRF_RADIO->EVENTS_END = 0; + NRF_RADIO->EVENTS_PAYLOAD = 0; + NRF_RADIO->EVENTS_READY = 0; + NRF_RADIO->EVENTS_RSSIEND = 0; // Disable all interrupts NRF_RADIO->INTENCLR = (uint32_t)~0; @@ -161,9 +173,16 @@ static bool NRF5_ESB_initialize() TIMER_SHORTS_COMPARE1_CLEAR_Msk | TIMER_SHORTS_COMPARE1_STOP_Msk; // Reset timer NRF5_RADIO_TIMER->TASKS_CLEAR = 1; + // Reset compare events - NRF5_RADIO_TIMER->EVENTS_COMPARE[0] = 0; - NRF5_RADIO_TIMER->EVENTS_COMPARE[1] = 0; +#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_COMPARE0_Enabled << TIMER_INTENSET_COMPARE0_Pos; NRF5_RADIO_TIMER->INTENSET = TIMER_INTENSET_COMPARE1_Enabled << TIMER_INTENSET_COMPARE1_Pos; @@ -179,6 +198,12 @@ static bool NRF5_ESB_initialize() // Set internal variables radio_disabled = true; +#ifdef MY_DEBUG_VERBOSE_NRF5_ESB + intcntr_bcmatch=0; + intcntr_ready=0; + intcntr_end=0; +#endif + return true; } @@ -209,19 +234,24 @@ static void NRF5_ESB_powerDown() static void NRF5_ESB_powerUp() { - // Not needed + NRF5_ESB_initialize(); } static void NRF5_ESB_sleep() { NRF5_RADIO_DEBUG(PSTR("NRF5:SLP\n")); - NRF5_ESB_powerDown(); + + // Disable shorts + NRF_RADIO->SHORTS = 0; + + // Disable radio + NRF_RADIO->TASKS_DISABLE = 1; } static void NRF5_ESB_standBy() { NRF5_RADIO_DEBUG(PSTR("NRF5:SBY\n")); - NRF5_ESB_initialize(); + NRF5_ESB_startListening(); } static bool NRF5_ESB_sanityCheck() @@ -274,27 +304,27 @@ static bool NRF5_ESB_isDataAvailable() static uint8_t NRF5_ESB_readMessage(void *data) { + uint8_t ret = 0; + // get content from rx buffer NRF5_ESB_Packet *buffer = rx_circular_buffer.getBack(); // Nothing to read? - if (buffer == NULL) { - return 0; - } + if (buffer != NULL) { + // copy content + memcpy(data, buffer->data, buffer->len); + ret = buffer->len; + rssi_rx = 0-buffer->rssi; - // copy content - memcpy(data, buffer->data, buffer->len); - uint8_t ret = buffer->len; - rssi_rx = 0-buffer->rssi; - - // Debug message + // Debug message #ifdef MY_DEBUG_VERBOSE_NRF5_ESB - NRF5_RADIO_DEBUG(PSTR("NRF5:RX:LEN=%" PRIu8 ",NOACK=%" PRIu8 ",PID=%" PRIu8 ",RSSI=%" PRIi16 ",RX=%" - PRIu32 "\n"), - buffer->len, buffer->noack, buffer->pid, rssi_rx, buffer->rxmatch); + NRF5_RADIO_DEBUG(PSTR("NRF5:RX:LEN=%" PRIu8 ",NOACK=%" PRIu8 ",PID=%" PRIu8 ",RSSI=%" PRIi16 ",RX=%" + PRIu32 "\n"), + buffer->len, buffer->noack, buffer->pid, rssi_rx, buffer->rxmatch); #endif - // release buffer - rx_circular_buffer.popBack(); + // release buffer + rx_circular_buffer.popBack(); + } // Check if radio was disabled by buffer end if (radio_disabled == true) { @@ -399,7 +429,7 @@ static bool NRF5_ESB_sendMessage(uint8_t recipient, const void *buf, uint8_t len // build metadata tx_buffer.len = len; -#ifndef MY_NRF5_ESB_REVERSE_ACK +#ifndef MY_NRF5_ESB_REVERSE_ACK_TX tx_buffer.noack = noACK || recipient==BROADCAST_ADDRESS; #else // reverse the noack bit @@ -408,13 +438,13 @@ static bool NRF5_ESB_sendMessage(uint8_t recipient, const void *buf, uint8_t len tx_buffer.pid++; // Calculate number of retries - tx_retries = ((recipient == BROADCAST_ADDRESS)?(NRF5_ESB_BC):(NRF5_ESB_ARC)); + if (recipient == BROADCAST_ADDRESS) { + tx_retries = NRF5_ESB_BC_ARC; + } else { + tx_retries = ((noACK == false)?(NRF5_ESB_ARC_ACK):(NRF5_ESB_ARC_NOACK)); + } int8_t tx_retries_start = tx_retries; - - // This line disables sending a broadcast packet multiple times - // ack_received = (recipient == BROADCAST_ADDRESS); - // This line force to send an boradcast packet multiple times like NRF24 - ack_received = tx_buffer.noack; + ack_received = false; // configure TX address NRF_RADIO->PREFIX1 = (NRF_RADIO->PREFIX1 & NRF5_ESB_TX_ADDR_MSK) | @@ -442,9 +472,6 @@ static bool NRF5_ESB_sendMessage(uint8_t recipient, const void *buf, uint8_t len #endif } - // Enable listening on Node and BC address - NRF_RADIO->RXADDRESSES = (1 << NRF5_ESB_NODE_ADDR) | (1 << NRF5_ESB_BC_ADDR); - // Calculate RSSI if (rssi_tx == INVALID_RSSI) { // calculate pseudo-RSSI based on retransmission counter (ARC) @@ -452,12 +479,17 @@ static bool NRF5_ESB_sendMessage(uint8_t recipient, const void *buf, uint8_t len // 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 ",WAKE=%" PRIu32 "\n"), events_end_tx, ack_received, tx_retries_start - tx_retries, rssi_tx, wakeups); #endif + return ack_received; }; @@ -486,7 +518,7 @@ static uint8_t reverse_byte(uint8_t address) return address; } -// Calculate time to transmit an byte in us as bit shift -> 2^X +// Calculate time to transmit an byte in µs as bit shift -> 2^X static inline uint8_t NRF5_ESB_byte_time() { if ((MY_NRF5_ESB_MODE == NRF5_1MBPS) or @@ -510,14 +542,16 @@ extern "C" { */ if (NRF_RADIO->EVENTS_BCMATCH == 1) { NRF_RESET_EVENT(NRF_RADIO->EVENTS_BCMATCH); - +#ifdef MY_DEBUG_VERBOSE_NRF5_ESB + intcntr_bcmatch++; +#endif // Disable bitcounter NRF_RADIO->TASKS_BCSTOP = 1; // In RX mode -> prepare ACK or RX if (NRF_RADIO->STATE == RADIO_STATE_STATE_Rx) { // ACK only for node address and unset noack bit -#ifndef MY_NRF5_ESB_REVERSE_ACK +#ifndef MY_NRF5_ESB_REVERSE_ACK_RX if ((NRF_RADIO->RXMATCH != NRF5_ESB_NODE_ADDR) || (rx_buffer->noack)) { #else if ((NRF_RADIO->RXMATCH != NRF5_ESB_NODE_ADDR) || (!rx_buffer->noack)) { @@ -561,6 +595,9 @@ extern "C" { */ if (NRF_RADIO->EVENTS_READY == 1) { NRF_RESET_EVENT(NRF_RADIO->EVENTS_READY); +#ifdef MY_DEBUG_VERBOSE_NRF5_ESB + intcntr_ready++; +#endif // Fetch a new buffer rx_buffer = rx_circular_buffer.getFront(); @@ -591,6 +628,9 @@ extern "C" { */ if (NRF_RADIO->EVENTS_END == 1) { NRF_RESET_EVENT(NRF_RADIO->EVENTS_END); +#ifdef MY_DEBUG_VERBOSE_NRF5_ESB + intcntr_end++; +#endif // Enable ACK bitcounter for next packet NRF_RADIO->BCC = NRF5_ESB_BITCOUNTER; @@ -619,7 +659,7 @@ extern "C" { // Prepare ACK package ack_buffer.pid++; ack_buffer.len=1; // data[0] is set some lines before -#ifndef MY_NRF5_ESB_REVERSE_ACK +#ifndef MY_NRF5_ESB_REVERSE_ACK_TX ack_buffer.noack = 1; #else ack_buffer.noack = 0; diff --git a/drivers/NRF5/Radio_ESB.h b/drivers/NRF5/Radio_ESB.h index 87a8bb5..940f9c3 100644 --- a/drivers/NRF5/Radio_ESB.h +++ b/drivers/NRF5/Radio_ESB.h @@ -25,6 +25,11 @@ #include "Radio.h" #include +// Check SoftDevice presence +#if defined(SOFTDEVICE_PRESENT) && !defined(FORCE_RADIO_ESB_WITH_SD) +#error "NRF5 SoftDevice cannot be used with NRF5 Radio." +#endif + // Check maximum messae length #if MAX_MESSAGE_LENGTH > (32) #error "Unsupported message length. (MAX_MESSAGE_LENGTH)" @@ -49,11 +54,14 @@ // auto retry delay in us, don't set this value < 1500us@250kbit #define NRF5_ESB_ARD (1500) -// auto retry count -#define NRF5_ESB_ARC (15) +// auto retry count with noACK is false +#define NRF5_ESB_ARC_ACK (15) + +// auto retry count with noACK is true +#define NRF5_ESB_ARC_NOACK (15) // How often broadcast messages are send -#define NRF5_ESB_BC (1) +#define NRF5_ESB_BC_ARC (15) // Node address index #define NRF5_ESB_NODE_ADDR (0) @@ -154,4 +162,10 @@ typedef struct nrf5_radio_packet_s { #endif } NRF5_ESB_Packet; +#ifdef MY_DEBUG_VERBOSE_NRF5_ESB +static uint32_t intcntr_bcmatch; +static uint32_t intcntr_ready; +static uint32_t intcntr_end; +#endif + #endif // __NRF5_H__ diff --git a/drivers/NRF5/nrf5_wiring_digital.c b/drivers/NRF5/nrf5_wiring_digital.c index 9285d13..3ab490f 100644 --- a/drivers/NRF5/nrf5_wiring_digital.c +++ b/drivers/NRF5/nrf5_wiring_digital.c @@ -32,7 +32,13 @@ void nrf5_pinMode(uint32_t ulPin, uint32_t ulMode) return; } +#ifdef ARDUINO_ARCH_NRF52 + // Arduino: https://github.com/arduino-org/arduino-core-nrf52 + ulPin = g_APinDescription[ulPin].ulPin; +#else + // Sandeep Mistry: https://github.com/sandeepmistry/arduino-nRF5 ulPin = g_ADigitalPinMap[ulPin]; +#endif // Set pin mode according to chapter '22.6.3 I/O Pin Configuration' switch (ulMode) { diff --git a/hal/architecture/MyHwNRF5.cpp b/hal/architecture/MyHwNRF5.cpp index 25ae921..00afe48 100644 --- a/hal/architecture/MyHwNRF5.cpp +++ b/hal/architecture/MyHwNRF5.cpp @@ -66,6 +66,25 @@ void hwWriteConfig(int adr, uint8_t value) bool hwInit() { + // Clock is manged by sleep modes. Radio depends on HFCLK. + // Force to start HFCLK + NRF_CLOCK->EVENTS_HFCLKSTARTED = 0; + NRF_CLOCK->TASKS_HFCLKSTART = 1; + while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) + ; + + // Enable low latency sleep mode + NRF_POWER->TASKS_CONSTLAT = 1; + + // Enable cache on >= NRF52 +#ifndef NRF51 + NRF_NVMC->ICACHECNF = NVMC_ICACHECNF_CACHEEN_Msk; +#endif + + // Suspend UART + NRF_UART0->TASKS_STOPRX = 1; + NRF_UART0->TASKS_STOPTX = 1; + NRF_UART0->TASKS_SUSPEND = 1; #ifdef MY_DISABLED_SERIAL // Disable UART, when not configured @@ -471,7 +490,13 @@ uint16_t hwCPUVoltage() uint16_t hwCPUFrequency() { - return F_CPU / 100000UL; +#if defined(VARIANT_MCK) + return (VARIANT_MCK) / 100000UL; +#elif defined(F_CPU) + return (F_CPU) / 100000UL; +#else + return 16; +#endif } uint16_t hwFreeMem() diff --git a/hal/architecture/MyHwNRF5.h b/hal/architecture/MyHwNRF5.h index 07c7590..dc56859 100644 --- a/hal/architecture/MyHwNRF5.h +++ b/hal/architecture/MyHwNRF5.h @@ -29,12 +29,17 @@ #endif // Define NRF5_SOFTDEVICE when SoftDevice found -#if defined(S110) || defined(S130) || defined(S132) -#ifndef NRF5_SOFTDEVICE -#define NRF5_SOFTDEVICE +#if defined(S110) || defined(S130) || defined(S132) || defined(S140) +#ifndef SOFTDEVICE_PRESENT +#define SOFTDEVICE_PRESENT #endif #endif +// Define ARDUINO_ARCH_NRF5, if not defined +#ifndef ARDUINO_ARCH_NRF5 +#define ARDUINO_ARCH_NRF5 +#endif + #include "drivers/NRF5/nrf5_wiring_digital.c" #include "drivers/NRF5/wdt.h" #include "drivers/NVM/NVRAM.h" @@ -42,8 +47,19 @@ #include #include +// mapping +#ifndef strncpy_P +#define strncpy_P strncpy +#endif +#ifndef snprintf_P #define snprintf_P(s, f, ...) snprintf((s), (f), __VA_ARGS__) +#endif +#ifndef vsnprintf_P +#define vsnprintf_P vsnprintf +#endif +#ifndef printf_P #define printf_P printf +#endif #define MIN(a,b) min(a,b) #define MAX(a,b) max(a,b) @@ -139,4 +155,4 @@ typedef struct { #define MY_CRITICAL_SECTION #endif /* DOXYGEN || CPPCHECK */ -#endif // #ifdef ARDUINO_ARCH_NRF5 +#endif // #ifndef MyHwNRF5_h diff --git a/hal/architecture/MyMainNRF5.cpp b/hal/architecture/MyMainNRF5.cpp deleted file mode 100644 index aa5cd46..0000000 --- a/hal/architecture/MyMainNRF5.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/** - * The MySensors Arduino library handles the wireless radio link and protocol - * between your home built sensors/actuators and HA controller of choice. - * The sensors forms 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. - * - * Created by Henrik Ekblad - * Copyright (C) 2013-2017 Sensnology AB - * Full contributor list: - * https://github.com/mysensors/Arduino/graphs/contributors - * - * Documentation: http://www.mysensors.org - * Support Forum: http://forum.mysensors.org - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - */ - -// Initialize library and handle sketch functions like we want to - -extern "C" void __libc_init_array(void); - -// Weak empty variant initialization function. -// May be redefined by variant files. -void initVariant() __attribute__((weak)); -void initVariant() {} - -int main(void) -{ - // Suspend UART - NRF_UART0->TASKS_STOPRX = 1; - NRF_UART0->TASKS_STOPTX = 1; - NRF_UART0->TASKS_SUSPEND = 1; - - // Clock is manged by sleep modes. Radio depends on HFCLK. - // Force to start HFCLK - NRF_CLOCK->EVENTS_HFCLKSTARTED = 0; - NRF_CLOCK->TASKS_HFCLKSTART = 1; - while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0) - ; - - // Enable low latency sleep mode - NRF_POWER->TASKS_CONSTLAT = 1; - - // Enable cache on >= NRF52 -#ifndef NRF51 - NRF_NVMC->ICACHECNF = NVMC_ICACHECNF_CACHEEN_Msk; -#endif - - init(); - initVariant(); // arduino-nRF5 specific - delay(1); - - _begin(); // Startup MySensors library - - for (;;) { - _process(); // Process incoming data - if (loop) { - loop(); // Call sketch loop - } - if (serialEventRun) { - serialEventRun(); - } - } - return 0; -}