diff --git a/connectivity/drivers/ble/FEATURE_BLE/TARGET_NORDIC/TARGET_NORDIC_CORDIO/TARGET_NRF5x/CMakeLists.txt b/connectivity/drivers/ble/FEATURE_BLE/TARGET_NORDIC/TARGET_NORDIC_CORDIO/TARGET_NRF5x/CMakeLists.txt index 29996451c0..ce5a88f10a 100644 --- a/connectivity/drivers/ble/FEATURE_BLE/TARGET_NORDIC/TARGET_NORDIC_CORDIO/TARGET_NRF5x/CMakeLists.txt +++ b/connectivity/drivers/ble/FEATURE_BLE/TARGET_NORDIC/TARGET_NORDIC_CORDIO/TARGET_NRF5x/CMakeLists.txt @@ -17,6 +17,7 @@ target_sources(mbed-ble-cordio stack/sources/pal_bb_ble_rf.c stack/sources/pal_cfg.c stack/sources/pal_crypto.c + stack/sources/pal_rtc.c stack/sources/pal_timer.c ) diff --git a/connectivity/drivers/ble/FEATURE_BLE/TARGET_NORDIC/TARGET_NORDIC_CORDIO/TARGET_NRF5x/NRFCordioHCIDriver.cpp b/connectivity/drivers/ble/FEATURE_BLE/TARGET_NORDIC/TARGET_NORDIC_CORDIO/TARGET_NRF5x/NRFCordioHCIDriver.cpp index 6e74440d23..19808e70f3 100644 --- a/connectivity/drivers/ble/FEATURE_BLE/TARGET_NORDIC/TARGET_NORDIC_CORDIO/TARGET_NRF5x/NRFCordioHCIDriver.cpp +++ b/connectivity/drivers/ble/FEATURE_BLE/TARGET_NORDIC/TARGET_NORDIC_CORDIO/TARGET_NRF5x/NRFCordioHCIDriver.cpp @@ -202,6 +202,10 @@ NRFCordioHCIDriver::~NRFCordioHCIDriver() // Stop timer NRF_TIMER0->TASKS_STOP = 1; NRF_TIMER0->TASKS_CLEAR = 1; + NRF_TIMER0->TASKS_SHUTDOWN = 1; + + // Stop rtc + NRF_RTC1->TASKS_STOP = 1; NVIC_ClearPendingIRQ(RADIO_IRQn); NVIC_ClearPendingIRQ(TIMER0_IRQn); @@ -233,6 +237,7 @@ ble::buf_pool_desc_t NRFCordioHCIDriver::get_buffer_pool_description() void NRFCordioHCIDriver::do_initialize() { if(_is_init) { + NRF_RTC1->TASKS_START = 1; return; } @@ -260,7 +265,13 @@ void NRFCordioHCIDriver::do_initialize() { } - + if(NRF_CLOCK->LFCLKSRC == CLOCK_LFCLKSRC_SRC_RC) { + NRF_CLOCK->EVENTS_DONE = 0; + NRF_CLOCK->TASKS_CAL = 1; + while (NRF_CLOCK->EVENTS_DONE == 0) + { + } + } // mbed-os target uses IRQ Handler names with _v added at the end // (TIMER0_IRQHandler_v and TIMER2_IRQHandler_v) so we need to register these manually @@ -313,7 +324,13 @@ void NRFCordioHCIDriver::do_initialize() void NRFCordioHCIDriver::do_terminate() { + // Stop timer + NRF_TIMER0->TASKS_STOP = 1; + NRF_TIMER0->TASKS_CLEAR = 1; + NRF_TIMER0->TASKS_SHUTDOWN = 1; + // Stop rtc + NRF_RTC1->TASKS_STOP = 1; } void NRFCordioHCIDriver::start_reset_sequence() diff --git a/connectivity/drivers/ble/FEATURE_BLE/TARGET_NORDIC/TARGET_NORDIC_CORDIO/TARGET_NRF5x/stack/sources/pal_bb_ble.c b/connectivity/drivers/ble/FEATURE_BLE/TARGET_NORDIC/TARGET_NORDIC_CORDIO/TARGET_NRF5x/stack/sources/pal_bb_ble.c index f14748d3f7..f55e4bd53a 100644 --- a/connectivity/drivers/ble/FEATURE_BLE/TARGET_NORDIC/TARGET_NORDIC_CORDIO/TARGET_NRF5x/stack/sources/pal_bb_ble.c +++ b/connectivity/drivers/ble/FEATURE_BLE/TARGET_NORDIC/TARGET_NORDIC_CORDIO/TARGET_NRF5x/stack/sources/pal_bb_ble.c @@ -527,6 +527,7 @@ void PalBbBleLowPower(void) #if (USE_RTC_BB_CLK) NRF_TIMER0->TASKS_STOP = 1; NRF_TIMER0->TASKS_CLEAR = 1; + NRF_TIMER0->TASKS_SHUTDOWN = 1; #endif } diff --git a/connectivity/drivers/ble/FEATURE_BLE/TARGET_NORDIC/TARGET_NORDIC_CORDIO/TARGET_NRF5x/stack/sources/pal_rtc.c b/connectivity/drivers/ble/FEATURE_BLE/TARGET_NORDIC/TARGET_NORDIC_CORDIO/TARGET_NRF5x/stack/sources/pal_rtc.c index fea87a0cb8..20d3db4ad9 100644 --- a/connectivity/drivers/ble/FEATURE_BLE/TARGET_NORDIC/TARGET_NORDIC_CORDIO/TARGET_NRF5x/stack/sources/pal_rtc.c +++ b/connectivity/drivers/ble/FEATURE_BLE/TARGET_NORDIC/TARGET_NORDIC_CORDIO/TARGET_NRF5x/stack/sources/pal_rtc.c @@ -231,9 +231,6 @@ void PalRtcInit(void) PalRtcDisableCompareIrq(channelId); } - /* Configure low-frequency clock. */ - NRF_CLOCK->LFCLKSRC = (CLOCK_LFCLKSRC_SRC_Xtal << CLOCK_LFCLKSRC_SRC_Pos); - NRF_CLOCK->EVENTS_LFCLKSTARTED = 0; NRF_CLOCK->TASKS_LFCLKSTART = 1; while (NRF_CLOCK->EVENTS_LFCLKSTARTED == 0) { } diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/CMakeLists.txt b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/CMakeLists.txt index 765e1808ca..33e8e8dfb2 100644 --- a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/CMakeLists.txt +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/CMakeLists.txt @@ -33,3 +33,9 @@ target_sources(mbed-core us_ticker.c watchdog_api.c ) + + +target_link_libraries(mbed-core + INTERFACE + mbed-ble-cordio +) \ No newline at end of file diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/common_rtc.c b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/common_rtc.c index 858813c75f..a15f8a8d18 100644 --- a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/common_rtc.c +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/common_rtc.c @@ -100,9 +100,6 @@ void COMMON_RTC_IRQ_HANDLER(void) __STATIC_INLINE void errata_20(void) { #if defined(NRF52_PAN_20) - if (COMMON_RTC_INSTANCE != NRF_RTC1) { - return; - } if (!NRF_HAL_SD_IS_ENABLED()) { NRF_CLOCK->EVENTS_LFCLKSTARTED = 0; @@ -112,12 +109,10 @@ __STATIC_INLINE void errata_20(void) { } } - NRF_RTC1->TASKS_STOP = 0; + COMMON_RTC_INSTANCE->TASKS_STOP = 0; #endif } -void RTC1_IRQHandler(void); - void common_rtc_init(void) { if (m_common_rtc_enabled) { @@ -132,9 +127,7 @@ void common_rtc_init(void) nrf_rtc_task_trigger(COMMON_RTC_INSTANCE, NRF_RTC_TASK_STOP); - if (COMMON_RTC_INSTANCE == NRF_RTC1) { - NVIC_SetVector(RTC1_IRQn, (uint32_t)RTC1_IRQHandler); - } + NVIC_SetVector(COMMON_RTC_IRQN, (uint32_t)COMMON_RTC_IRQ_HANDLER); /* RTC is driven by the low frequency (32.768 kHz) clock, a proper request * must be made to have it running. @@ -160,12 +153,12 @@ void common_rtc_init(void) nrf_rtc_event_disable(COMMON_RTC_INSTANCE, NRF_RTC_INT_OVERFLOW_MASK #if defined(TARGET_MCU_NRF51822) - | OS_TICK_INT_MASK + | OS_TICK_INT_MASK #endif #if DEVICE_LPTICKER - | LP_TICKER_INT_MASK + | LP_TICKER_INT_MASK #endif - ); + ); /* This interrupt is enabled permanently, since overflow indications are needed * continuously. diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/common_rtc.h b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/common_rtc.h index e07c59bdc2..e48eed5e11 100644 --- a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/common_rtc.h +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/common_rtc.h @@ -28,6 +28,7 @@ // of NRF51) as an alternative tick source for RTOS. #define COMMON_RTC_INSTANCE NRF_RTC0 #define COMMON_RTC_IRQ_HANDLER RTC0_IRQHandler +#define COMMON_RTC_IRQN RTC0_IRQn #define OS_TICK_CC_CHANNEL 0 #define LP_TICKER_CC_CHANNEL 1 diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/sleep.c b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/sleep.c index 3d11c54177..e4a566f397 100644 --- a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/sleep.c +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/sleep.c @@ -20,9 +20,11 @@ #include "nrf_soc.h" #include "nrf_timer.h" #include "us_ticker.h" +#if BB_CLK_RATE_HZ == 32768 && FEATURE_BLE == 1 #include "bb_api.h" #include "pal_timer.h" #include "pal_rtc.h" +#endif #if defined(SOFTDEVICE_PRESENT) #include "nrf_sdh.h" @@ -41,14 +43,25 @@ extern bool us_ticker_initialized; void hal_sleep(void) { - /* Clock management for low power mode. */ -#if BB_CLK_RATE_HZ == 32768 + // Trigger an event when an interrupt is pending. This allows to wake up + // the processor from disabled interrupts. + SCB->SCR |= SCB_SCR_SEVONPEND_Msk; + +#if defined(NRF52) || defined(NRF52840_XXAA) + /* Clear exceptions and PendingIRQ from the FPU unit */ + __set_FPSCR(__get_FPSCR() & ~(FPU_EXCEPTION_MASK)); + (void)__get_FPSCR(); + NVIC_ClearPendingIRQ(FPU_IRQn); +#endif + + /* Clock management for low power mode. */ +#if BB_CLK_RATE_HZ == 32768 && FEATURE_BLE == 1 uint32_t rtcNow = NRF_RTC1->COUNTER; if ((BbGetCurrentBod() == NULL)) { if ((PalTimerGetState() == PAL_TIMER_STATE_BUSY && - ((NRF_RTC1->CC[3] - rtcNow) & PAL_MAX_RTC_COUNTER_VAL) > PAL_HFCLK_OSC_SETTLE_TICKS) || + ((NRF_RTC1->CC[3] - rtcNow) & PAL_MAX_RTC_COUNTER_VAL) > PAL_HFCLK_OSC_SETTLE_TICKS) || (PalTimerGetState() == PAL_TIMER_STATE_READY)) { /* disable HFCLK */ @@ -60,7 +73,33 @@ void hal_sleep(void) #endif NRF_POWER->TASKS_LOWPWR = 1; - __WFI(); + + // Note: it is not sufficient to just use WFE here, since the internal + // event register may be already set from an event that occurred in the + // past (like an SVC call to the SoftDevice) and in such case WFE will + // just clear the register and continue execution. + // Therefore, the strategy here is to first clear the event register + // by using SEV/WFE pair, and then execute WFE again, unless there is + // a pending interrupt. + + // Set an event and wake up whatsoever, this will clear the event + // register from all previous events set (SVC call included) + __SEV(); + __WFE(); + + // Test if there is an interrupt pending (mask reserved regions) + if (SCB->ICSR & (SCB_ICSR_RESERVED_BITS_MASK)) + { + // Ok, there is an interrut pending, no need to go to sleep + return; + } + else + { + // next event will wakeup the CPU + // If an interrupt occured between the test of SCB->ICSR and this + // instruction, WFE will just not put the CPU to sleep + __WFE(); + } } void hal_deepsleep(void)