From 15019ef88277a5267245abb1f077d876ff87d01a Mon Sep 17 00:00:00 2001 From: Jean-Philippe Brucker Date: Mon, 3 Aug 2015 16:42:03 +0100 Subject: [PATCH] Nordic: fallback to the internal LF clock in case Xtal doesn't start Since some Nordic platforms don't use an external clock source, they need to be built with the TARGET_NRF_LFCLK_RC flag. If they don't, SystemInit will wait indefinitely for an external clock to start. This patch adds a 1s timeout to the external oscillator initialisation, and falls back to using the internal RC one if no LFCLKSTARTED event was received in this interval. For most applications, this won't matter. When building for the wrong Nordic target, pin numbers will be mixed up as well and the application will break later on. But on targets that can only be updated using FOTA and don't have an interface chip, this patch avoid ending up with a bricked device after a slight mistake. Indeed, BLE DFU service is portable across all boards and will run even with a mixed-up target setup, allowing the user to re-install the right application. An example scenario is transferring an application on a standalone NRF51 chip, after spending some time prototyping on an mKIT, which tends to happen a bit too often... Signed-off-by: Jean-Philippe Brucker --- .../TARGET_MCU_NRF51822/system_nrf51.c | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/libraries/mbed/targets/cmsis/TARGET_NORDIC/TARGET_MCU_NRF51822/system_nrf51.c b/libraries/mbed/targets/cmsis/TARGET_NORDIC/TARGET_MCU_NRF51822/system_nrf51.c index dd4fc2bf4f..0e4dc2832e 100644 --- a/libraries/mbed/targets/cmsis/TARGET_NORDIC/TARGET_MCU_NRF51822/system_nrf51.c +++ b/libraries/mbed/targets/cmsis/TARGET_NORDIC/TARGET_MCU_NRF51822/system_nrf51.c @@ -34,6 +34,7 @@ #include #include #include "nrf.h" +#include "nrf_delay.h" #include "system_nrf51.h" /*lint ++flb "Enter library region" */ @@ -42,6 +43,7 @@ static bool is_manual_peripheral_setup_needed(void); static bool is_disabled_in_debug_needed(void); +static void init_clock(void); #if defined ( __CC_ARM ) @@ -82,8 +84,18 @@ void SystemInit(void) } // Start the external 32khz crystal oscillator. + init_clock(); +} + +void init_clock(void) +{ + /* For compatibility purpose, the default behaviour is to first attempt to initialise an + external clock, and after a timeout, use the internal RC one. To avoid this wait, boards that + don't have an external oscillator can set TARGET_NRF_LFCLK_RC directly. */ + int i = 0; + const uint32_t polling_period = 200; + const uint32_t timeout = 1000000; - /* for Nordic devices without an external LF crystal */ #if defined(TARGET_NRF_LFCLK_RC) NRF_CLOCK->LFCLKSRC = (CLOCK_LFCLKSRC_SRC_RC << CLOCK_LFCLKSRC_SRC_Pos); #else @@ -92,7 +104,26 @@ void SystemInit(void) NRF_CLOCK->EVENTS_LFCLKSTARTED = 0; NRF_CLOCK->TASKS_LFCLKSTART = 1; - // Wait for the external oscillator to start up. + /* Wait for the external oscillator to start up. + nRF51822 product specification (8.1.5) gives a typical value of 300ms for external clock + startup duration, and a maximum value of 1s. When using the internal RC source, typical delay + will be 390µs, so we use a polling period of 200µs. + + We can't use us_ticker at this point, so we have to rely on a less precise method for + measuring our timeout. Because of this, the actual timeout will be slightly longer than 1 + second, which isn't an issue at all, since this fallback should only be used as a safety net. + */ + for (i = 0; i < (timeout / polling_period); i++) { + if (NRF_CLOCK->EVENTS_LFCLKSTARTED != 0) + return; + nrf_delay_us(polling_period); + } + + /* Fallback to internal clock. Belt and braces, since the internal clock is used by default + whilst no external source is running. This is not only a sanity check, but it also allows + code down the road (e.g. ble initialisation) to directly know which clock is used. */ + NRF_CLOCK->LFCLKSRC = (CLOCK_LFCLKSRC_SRC_RC << CLOCK_LFCLKSRC_SRC_Pos); + NRF_CLOCK->TASKS_LFCLKSTART = 1; while (NRF_CLOCK->EVENTS_LFCLKSTARTED == 0) { // Do nothing. }