MYNEWT-313: Low power modifications There were a number of changes with this commit: 1) Porting the nrf51 code which uses an RTC timer for the OS timer tick to the nrf52. The nrf52 used to use a higher power timer for its os tick. 2) Changing OS_TICKS_PER_SEC on the nrf51 and nrf52 to 128 (from 1000). 3) Modified blinky as it was doing a os_time_delay(1000) instead of os_time_delay(OS_TICKS_PER_SEC).
Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/commit/9b008aa8 Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/tree/9b008aa8 Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/diff/9b008aa8 Branch: refs/heads/master Commit: 9b008aa849eea2f6dae5f44cc1d523add450b887 Parents: 127d425 Author: William San Filippo <wi...@runtime.io> Authored: Tue Jul 5 14:59:42 2016 -0700 Committer: William San Filippo <wi...@runtime.io> Committed: Tue Jul 5 15:02:17 2016 -0700 ---------------------------------------------------------------------- apps/blinky/src/main.c | 4 +- hw/bsp/bmd300eval/pkg.yml | 2 +- hw/bsp/nrf51dk/pkg.yml | 4 +- hw/bsp/nrf52dk/pkg.yml | 2 +- hw/mcu/nordic/nrf51xxx/include/mcu/cortex_m0.h | 4 +- hw/mcu/nordic/nrf52xxx/include/mcu/cortex_m4.h | 6 +- hw/mcu/nordic/nrf52xxx/src/hal_os_tick.c | 179 ++++++++++++++++++-- 7 files changed, 174 insertions(+), 27 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/9b008aa8/apps/blinky/src/main.c ---------------------------------------------------------------------- diff --git a/apps/blinky/src/main.c b/apps/blinky/src/main.c index 3d4ff9e..24d0311 100755 --- a/apps/blinky/src/main.c +++ b/apps/blinky/src/main.c @@ -6,7 +6,7 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, @@ -89,7 +89,7 @@ task1_handler(void *arg) ++g_task1_loops; /* Wait one second */ - os_time_delay(1000); + os_time_delay(OS_TICKS_PER_SEC); /* Toggle the LED */ hal_gpio_toggle(g_led_pin); http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/9b008aa8/hw/bsp/bmd300eval/pkg.yml ---------------------------------------------------------------------- diff --git a/hw/bsp/bmd300eval/pkg.yml b/hw/bsp/bmd300eval/pkg.yml index ccaf641..60d88ff 100644 --- a/hw/bsp/bmd300eval/pkg.yml +++ b/hw/bsp/bmd300eval/pkg.yml @@ -32,7 +32,7 @@ pkg.linkerscript: "bmd300eval.ld" pkg.linkerscript.bootloader.OVERWRITE: "boot-bmd300eval.ld" pkg.downloadscript: bmd300eval_download.sh pkg.debugscript: bmd300eval_debug.sh -pkg.cflags: -DNRF52 +pkg.cflags: -DNRF52 -DBSP_HAS_32768_XTAL pkg.deps: - hw/mcu/nordic/nrf52xxx - libs/baselibc http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/9b008aa8/hw/bsp/nrf51dk/pkg.yml ---------------------------------------------------------------------- diff --git a/hw/bsp/nrf51dk/pkg.yml b/hw/bsp/nrf51dk/pkg.yml index 60fb6e6..4a1a93b 100644 --- a/hw/bsp/nrf51dk/pkg.yml +++ b/hw/bsp/nrf51dk/pkg.yml @@ -6,7 +6,7 @@ # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at -# +# # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, @@ -32,7 +32,7 @@ pkg.linkerscript: "nrf51dk.ld" pkg.linkerscript.bootloader.OVERWRITE: "boot-nrf51dk.ld" pkg.downloadscript: nrf51dk_download.sh pkg.debugscript: nrf51dk_debug.sh -pkg.cflags: -DNRF51 +pkg.cflags: -DNRF51 -DBSP_HAS_32768_XTAL pkg.deps: - hw/mcu/nordic/nrf51xxx - libs/baselibc http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/9b008aa8/hw/bsp/nrf52dk/pkg.yml ---------------------------------------------------------------------- diff --git a/hw/bsp/nrf52dk/pkg.yml b/hw/bsp/nrf52dk/pkg.yml index 617cea7..d350e70 100644 --- a/hw/bsp/nrf52dk/pkg.yml +++ b/hw/bsp/nrf52dk/pkg.yml @@ -32,7 +32,7 @@ pkg.linkerscript: "nrf52dk.ld" pkg.linkerscript.bootloader.OVERWRITE: "boot-nrf52dk.ld" pkg.downloadscript: nrf52dk_download.sh pkg.debugscript: nrf52dk_debug.sh -pkg.cflags: -DNRF52 +pkg.cflags: -DNRF52 -DBSP_HAS_32768_XTAL pkg.deps: - hw/mcu/nordic/nrf52xxx - libs/baselibc http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/9b008aa8/hw/mcu/nordic/nrf51xxx/include/mcu/cortex_m0.h ---------------------------------------------------------------------- diff --git a/hw/mcu/nordic/nrf51xxx/include/mcu/cortex_m0.h b/hw/mcu/nordic/nrf51xxx/include/mcu/cortex_m0.h index b8c91a9..3ae4253 100644 --- a/hw/mcu/nordic/nrf51xxx/include/mcu/cortex_m0.h +++ b/hw/mcu/nordic/nrf51xxx/include/mcu/cortex_m0.h @@ -6,7 +6,7 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, @@ -27,6 +27,6 @@ * clocked at 32768Hz. The tick frequency is chosen such that it divides * cleanly into 32768 to avoid a systemic bias in the actual tick frequency. */ -#define OS_TICKS_PER_SEC (1024) +#define OS_TICKS_PER_SEC (128) #endif /* __MCU_CORTEX_M0_H__ */ http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/9b008aa8/hw/mcu/nordic/nrf52xxx/include/mcu/cortex_m4.h ---------------------------------------------------------------------- diff --git a/hw/mcu/nordic/nrf52xxx/include/mcu/cortex_m4.h b/hw/mcu/nordic/nrf52xxx/include/mcu/cortex_m4.h index d7661c7..bb1c14e 100644 --- a/hw/mcu/nordic/nrf52xxx/include/mcu/cortex_m4.h +++ b/hw/mcu/nordic/nrf52xxx/include/mcu/cortex_m4.h @@ -6,7 +6,7 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, @@ -22,6 +22,10 @@ #include "mcu/nrf52.h" +#if defined(BSP_HAS_32768_XTAL) +#define OS_TICKS_PER_SEC (128) +#else #define OS_TICKS_PER_SEC (1000) +#endif #endif /* __MCU_CORTEX_M4_H__ */ http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/9b008aa8/hw/mcu/nordic/nrf52xxx/src/hal_os_tick.c ---------------------------------------------------------------------- diff --git a/hw/mcu/nordic/nrf52xxx/src/hal_os_tick.c b/hw/mcu/nordic/nrf52xxx/src/hal_os_tick.c index ba3f0b3..454d2a1 100644 --- a/hw/mcu/nordic/nrf52xxx/src/hal_os_tick.c +++ b/hw/mcu/nordic/nrf52xxx/src/hal_os_tick.c @@ -6,7 +6,7 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, @@ -22,16 +22,58 @@ #include <mcu/nrf52_bitfields.h> #include <bsp/cmsis_nvic.h> +#if defined(BSP_HAS_32768_XTAL) +#define RTC_FREQ 32768 /* in Hz */ +#define OS_TICK_TIMER NRF_RTC1 +#define OS_TICK_IRQ RTC1_IRQn +#define OS_TICK_CMPREG 3 /* generate timer interrupt */ +#define OS_TICK_PRESCALER 1 /* prescaler to generate timer freq */ +#define TIMER_LT(__t1, __t2) ((((__t1) - (__t2)) & 0xffffff) > 0x800000) +#define RTC_COMPARE_INT_MASK(ccreg) (1UL << ((ccreg) + 16)) +#else #define OS_TICK_TIMER NRF_TIMER1 #define OS_TICK_IRQ TIMER1_IRQn #define OS_TICK_CMPREG 0 /* generate timer interrupt */ #define OS_TICK_COUNTER 1 /* capture current timer value */ #define OS_TICK_PRESCALER 4 /* prescaler to generate 1MHz timer freq */ #define TIMER_LT(__t1, __t2) ((int32_t)((__t1) - (__t2)) < 0) +#endif + +struct hal_os_tick +{ + int ticks_per_ostick; + os_time_t max_idle_ticks; + uint32_t lastocmp; +}; + +struct hal_os_tick g_hal_os_tick; + +/* + * Implement (x - y) where the range of both 'x' and 'y' is limited to 24-bits. + * + * For example: + * + * sub24(0, 0xffffff) = 1 + * sub24(0xffffff, 0xfffffe) = 1 + * sub24(0xffffff, 0) = -1 + * sub24(0x7fffff, 0) = 8388607 + * sub24(0x800000, 0) = -8388608 + */ +static inline int +sub24(uint32_t x, uint32_t y) +{ + int result; + + assert(x <= 0xffffff); + assert(y <= 0xffffff); -static int timer_ticks_per_ostick; -static os_time_t nrf52_max_idle_ticks; -static uint32_t lastocmp; + result = x - y; + if (result & 0x800000) { + return (result | 0xff800000); + } else { + return (result & 0x007fffff); + } +} static inline uint32_t nrf52_os_tick_counter(void) @@ -42,11 +84,15 @@ nrf52_os_tick_counter(void) */ OS_ASSERT_CRITICAL(); +#if defined(BSP_HAS_32768_XTAL) + return OS_TICK_TIMER->COUNTER; +#else /* * Capture the current timer value and return it. */ OS_TICK_TIMER->TASKS_CAPTURE[OS_TICK_COUNTER] = 1; return (OS_TICK_TIMER->CC[OS_TICK_COUNTER]); +#endif } static inline void @@ -56,12 +102,33 @@ nrf52_os_tick_set_ocmp(uint32_t ocmp) OS_ASSERT_CRITICAL(); while (1) { +#if defined(BSP_HAS_32768_XTAL) + int delta; + + ocmp &= 0xffffff; + OS_TICK_TIMER->CC[OS_TICK_CMPREG] = ocmp; + counter = nrf52_os_tick_counter(); + /* + * From nRF52 Product specification + * + * - If Counter is 'N' writing (N) or (N + 1) to CC register + * may not trigger a compare event. + * + * - If Counter is 'N' writing (N + 2) to CC register is guaranteed + * to trigger a compare event at 'N + 2'. + */ + delta = sub24(ocmp, counter); + if (delta > 2) { + break; + } +#else OS_TICK_TIMER->CC[OS_TICK_CMPREG] = ocmp; counter = nrf52_os_tick_counter(); if (TIMER_LT(counter, ocmp)) { break; } - ocmp += timer_ticks_per_ostick; +#endif + ocmp += g_hal_os_tick.ticks_per_ostick; } } @@ -74,21 +141,35 @@ nrf52_timer_handler(void) OS_ENTER_CRITICAL(sr); - /* - * Calculate elapsed ticks and advance OS time. - */ + /* Calculate elapsed ticks and advance OS time. */ +#if defined(BSP_HAS_32768_XTAL) + int delta; + counter = nrf52_os_tick_counter(); - ticks = (counter - lastocmp) / timer_ticks_per_ostick; + delta = sub24(counter, g_hal_os_tick.lastocmp); + ticks = delta / g_hal_os_tick.ticks_per_ostick; os_time_advance(ticks); /* Clear timer interrupt */ OS_TICK_TIMER->EVENTS_COMPARE[OS_TICK_CMPREG] = 0; /* Update the time associated with the most recent tick */ - lastocmp += ticks * timer_ticks_per_ostick; + g_hal_os_tick.lastocmp = (g_hal_os_tick.lastocmp + + (ticks * g_hal_os_tick.ticks_per_ostick)) & 0xffffff; +#else + counter = nrf52_os_tick_counter(); + ticks = (counter - g_hal_os_tick.lastocmp) / g_hal_os_tick.ticks_per_ostick; + os_time_advance(ticks); + + /* Clear timer interrupt */ + OS_TICK_TIMER->EVENTS_COMPARE[OS_TICK_CMPREG] = 0; + + /* Update the time associated with the most recent tick */ + g_hal_os_tick.lastocmp += ticks * g_hal_os_tick.ticks_per_ostick; +#endif /* Update the output compare to interrupt at the next tick */ - nrf52_os_tick_set_ocmp(lastocmp + timer_ticks_per_ostick); + nrf52_os_tick_set_ocmp(g_hal_os_tick.lastocmp + g_hal_os_tick.ticks_per_ostick); OS_EXIT_CRITICAL(sr); } @@ -104,10 +185,10 @@ os_tick_idle(os_time_t ticks) /* * Enter tickless regime during long idle durations. */ - if (ticks > nrf52_max_idle_ticks) { - ticks = nrf52_max_idle_ticks; + if (ticks > g_hal_os_tick.max_idle_ticks) { + ticks = g_hal_os_tick.max_idle_ticks; } - ocmp = lastocmp + ticks * timer_ticks_per_ostick; + ocmp = g_hal_os_tick.lastocmp + (ticks*g_hal_os_tick.ticks_per_ostick); nrf52_os_tick_set_ocmp(ocmp); } @@ -123,18 +204,79 @@ os_tick_idle(os_time_t ticks) } } +#if defined(BSP_HAS_32768_XTAL) +void +os_tick_init(uint32_t os_ticks_per_sec, int prio) +{ + uint32_t sr; + uint32_t mask; + + assert(RTC_FREQ % os_ticks_per_sec == 0); + + g_hal_os_tick.lastocmp = 0; + g_hal_os_tick.ticks_per_ostick = RTC_FREQ / os_ticks_per_sec; + + /* + * The maximum number of OS ticks allowed to elapse during idle is + * limited to 1/4th the number of timer ticks before the 24-bit counter + * rolls over. + */ + g_hal_os_tick.max_idle_ticks = (1UL << 22) / g_hal_os_tick.ticks_per_ostick; + + /* Turn on the LFCLK */ + NRF_CLOCK->TASKS_LFCLKSTOP = 1; + NRF_CLOCK->EVENTS_LFCLKSTARTED = 0; + NRF_CLOCK->LFCLKSRC = CLOCK_LFCLKSRC_SRC_Xtal; + NRF_CLOCK->TASKS_LFCLKSTART = 1; + + /* Wait here till started! */ + mask = CLOCK_LFCLKSTAT_STATE_Msk | CLOCK_LFCLKSTAT_SRC_Xtal; + while (1) { + if (NRF_CLOCK->EVENTS_LFCLKSTARTED) { + if ((NRF_CLOCK->LFCLKSTAT & mask) == mask) { + break; + } + } + } + + /* disable interrupts */ + OS_ENTER_CRITICAL(sr); + + /* Set isr in vector table and enable interrupt */ + NVIC_SetPriority(OS_TICK_IRQ, prio); + NVIC_SetVector(OS_TICK_IRQ, (uint32_t)nrf52_timer_handler); + NVIC_EnableIRQ(OS_TICK_IRQ); + + /* + * Program the OS_TICK_TIMER to operate at 32KHz and trigger an output + * compare interrupt at a rate of 'os_ticks_per_sec'. + */ + OS_TICK_TIMER->TASKS_STOP = 1; + OS_TICK_TIMER->TASKS_CLEAR = 1; + + OS_TICK_TIMER->EVTENCLR = 0xffffffff; + OS_TICK_TIMER->INTENCLR = 0xffffffff; + OS_TICK_TIMER->INTENSET = RTC_COMPARE_INT_MASK(OS_TICK_CMPREG); + + OS_TICK_TIMER->EVENTS_COMPARE[OS_TICK_CMPREG] = 0; + OS_TICK_TIMER->CC[OS_TICK_CMPREG] = g_hal_os_tick.ticks_per_ostick; + + OS_TICK_TIMER->TASKS_START = 1; + + OS_EXIT_CRITICAL(sr); +} +#else void os_tick_init(uint32_t os_ticks_per_sec, int prio) { - lastocmp = 0; - timer_ticks_per_ostick = 1000000 / os_ticks_per_sec; + g_hal_os_tick.ticks_per_ostick = 1000000 / os_ticks_per_sec; /* * The maximum number of timer ticks allowed to elapse during idle is * limited to 1/4th the number of timer ticks before the 32-bit counter * rolls over. */ - nrf52_max_idle_ticks = (1UL << 30) / timer_ticks_per_ostick; + g_hal_os_tick.max_idle_ticks = (1UL << 30) / g_hal_os_tick.ticks_per_ostick; /* * Program OS_TICK_TIMER to operate at 1MHz and trigger an output @@ -146,7 +288,7 @@ os_tick_init(uint32_t os_ticks_per_sec, int prio) OS_TICK_TIMER->BITMODE = TIMER_BITMODE_BITMODE_32Bit; OS_TICK_TIMER->PRESCALER = OS_TICK_PRESCALER; - OS_TICK_TIMER->CC[OS_TICK_CMPREG] = timer_ticks_per_ostick; + OS_TICK_TIMER->CC[OS_TICK_CMPREG] = g_hal_os_tick.ticks_per_ostick; OS_TICK_TIMER->INTENSET = TIMER_COMPARE_INT_MASK(OS_TICK_CMPREG); OS_TICK_TIMER->EVENTS_COMPARE[OS_TICK_CMPREG] = 0; @@ -156,3 +298,4 @@ os_tick_init(uint32_t os_ticks_per_sec, int prio) OS_TICK_TIMER->TASKS_START = 1; /* start the os tick timer */ } +#endif