mbed-os

Fork of mbed-os by erkin yucel

Committer:
elessair
Date:
Sun Oct 23 15:10:02 2016 +0000
Revision:
0:f269e3021894
Initial commit

Who changed what in which revision?

UserRevisionLine numberNew contents of line
elessair 0:f269e3021894 1 /*
elessair 0:f269e3021894 2 * Copyright (c) 2013 Nordic Semiconductor ASA
elessair 0:f269e3021894 3 * All rights reserved.
elessair 0:f269e3021894 4 *
elessair 0:f269e3021894 5 * Redistribution and use in source and binary forms, with or without modification,
elessair 0:f269e3021894 6 * are permitted provided that the following conditions are met:
elessair 0:f269e3021894 7 *
elessair 0:f269e3021894 8 * 1. Redistributions of source code must retain the above copyright notice, this list
elessair 0:f269e3021894 9 * of conditions and the following disclaimer.
elessair 0:f269e3021894 10 *
elessair 0:f269e3021894 11 * 2. Redistributions in binary form, except as embedded into a Nordic Semiconductor ASA
elessair 0:f269e3021894 12 * integrated circuit in a product or a software update for such product, must reproduce
elessair 0:f269e3021894 13 * the above copyright notice, this list of conditions and the following disclaimer in
elessair 0:f269e3021894 14 * the documentation and/or other materials provided with the distribution.
elessair 0:f269e3021894 15 *
elessair 0:f269e3021894 16 * 3. Neither the name of Nordic Semiconductor ASA nor the names of its contributors may be
elessair 0:f269e3021894 17 * used to endorse or promote products derived from this software without specific prior
elessair 0:f269e3021894 18 * written permission.
elessair 0:f269e3021894 19 *
elessair 0:f269e3021894 20 * 4. This software, with or without modification, must only be used with a
elessair 0:f269e3021894 21 * Nordic Semiconductor ASA integrated circuit.
elessair 0:f269e3021894 22 *
elessair 0:f269e3021894 23 * 5. Any software provided in binary or object form under this license must not be reverse
elessair 0:f269e3021894 24 * engineered, decompiled, modified and/or disassembled.
elessair 0:f269e3021894 25 *
elessair 0:f269e3021894 26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
elessair 0:f269e3021894 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
elessair 0:f269e3021894 28 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
elessair 0:f269e3021894 29 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
elessair 0:f269e3021894 30 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
elessair 0:f269e3021894 31 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
elessair 0:f269e3021894 32 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
elessair 0:f269e3021894 33 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
elessair 0:f269e3021894 34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
elessair 0:f269e3021894 35 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
elessair 0:f269e3021894 36 *
elessair 0:f269e3021894 37 */
elessair 0:f269e3021894 38
elessair 0:f269e3021894 39 #include "us_ticker_api.h"
elessair 0:f269e3021894 40 #include "common_rtc.h"
elessair 0:f269e3021894 41 #include "app_util.h"
elessair 0:f269e3021894 42 #include "nrf_drv_common.h"
elessair 0:f269e3021894 43 #include "lp_ticker_api.h"
elessair 0:f269e3021894 44
elessair 0:f269e3021894 45
elessair 0:f269e3021894 46 //------------------------------------------------------------------------------
elessair 0:f269e3021894 47 // Common stuff used also by lp_ticker and rtc_api (see "common_rtc.h").
elessair 0:f269e3021894 48 //
elessair 0:f269e3021894 49 #include "app_util_platform.h"
elessair 0:f269e3021894 50
elessair 0:f269e3021894 51 bool m_common_rtc_enabled = false;
elessair 0:f269e3021894 52 uint32_t volatile m_common_rtc_overflows = 0;
elessair 0:f269e3021894 53
elessair 0:f269e3021894 54 #if defined(TARGET_MCU_NRF51822)
elessair 0:f269e3021894 55 void common_rtc_irq_handler(void)
elessair 0:f269e3021894 56 #else
elessair 0:f269e3021894 57 void COMMON_RTC_IRQ_HANDLER(void)
elessair 0:f269e3021894 58 #endif
elessair 0:f269e3021894 59 {
elessair 0:f269e3021894 60 if (nrf_rtc_event_pending(COMMON_RTC_INSTANCE, US_TICKER_EVENT)) {
elessair 0:f269e3021894 61 us_ticker_irq_handler();
elessair 0:f269e3021894 62 }
elessair 0:f269e3021894 63
elessair 0:f269e3021894 64 #if DEVICE_LOWPOWERTIMER
elessair 0:f269e3021894 65 if (nrf_rtc_event_pending(COMMON_RTC_INSTANCE, LP_TICKER_EVENT)) {
elessair 0:f269e3021894 66
elessair 0:f269e3021894 67 lp_ticker_irq_handler();
elessair 0:f269e3021894 68 }
elessair 0:f269e3021894 69 #endif
elessair 0:f269e3021894 70
elessair 0:f269e3021894 71 if (nrf_rtc_event_pending(COMMON_RTC_INSTANCE, NRF_RTC_EVENT_OVERFLOW)) {
elessair 0:f269e3021894 72 nrf_rtc_event_clear(COMMON_RTC_INSTANCE, NRF_RTC_EVENT_OVERFLOW);
elessair 0:f269e3021894 73 // Don't disable this event. It shall occur periodically.
elessair 0:f269e3021894 74
elessair 0:f269e3021894 75 ++m_common_rtc_overflows;
elessair 0:f269e3021894 76 }
elessair 0:f269e3021894 77 }
elessair 0:f269e3021894 78
elessair 0:f269e3021894 79 void common_rtc_init(void)
elessair 0:f269e3021894 80 {
elessair 0:f269e3021894 81 if (m_common_rtc_enabled) {
elessair 0:f269e3021894 82 return;
elessair 0:f269e3021894 83 }
elessair 0:f269e3021894 84
elessair 0:f269e3021894 85 // RTC is driven by the low frequency (32.768 kHz) clock, a proper request
elessair 0:f269e3021894 86 // must be made to have it running.
elessair 0:f269e3021894 87 // Currently this clock is started in 'SystemInit' (see "system_nrf51.c"
elessair 0:f269e3021894 88 // or "system_nrf52.c", respectively).
elessair 0:f269e3021894 89
elessair 0:f269e3021894 90 nrf_rtc_prescaler_set(COMMON_RTC_INSTANCE, 0);
elessair 0:f269e3021894 91
elessair 0:f269e3021894 92 nrf_rtc_event_clear(COMMON_RTC_INSTANCE, US_TICKER_EVENT);
elessair 0:f269e3021894 93 #if defined(TARGET_MCU_NRF51822)
elessair 0:f269e3021894 94 nrf_rtc_event_clear(COMMON_RTC_INSTANCE, OS_TICK_EVENT);
elessair 0:f269e3021894 95 #endif
elessair 0:f269e3021894 96 #if DEVICE_LOWPOWERTIMER
elessair 0:f269e3021894 97 nrf_rtc_event_clear(COMMON_RTC_INSTANCE, LP_TICKER_EVENT);
elessair 0:f269e3021894 98 #endif
elessair 0:f269e3021894 99 nrf_rtc_event_clear(COMMON_RTC_INSTANCE, NRF_RTC_EVENT_OVERFLOW);
elessair 0:f269e3021894 100
elessair 0:f269e3021894 101 // Interrupts on all related events are enabled permanently. Particular
elessair 0:f269e3021894 102 // events will be enabled or disabled as needed (such approach is more
elessair 0:f269e3021894 103 // energy efficient).
elessair 0:f269e3021894 104 nrf_rtc_int_enable(COMMON_RTC_INSTANCE,
elessair 0:f269e3021894 105 #if defined(TARGET_MCU_NRF51822)
elessair 0:f269e3021894 106 OS_TICK_INT_MASK |
elessair 0:f269e3021894 107 #endif
elessair 0:f269e3021894 108 #if DEVICE_LOWPOWERTIMER
elessair 0:f269e3021894 109 LP_TICKER_INT_MASK |
elessair 0:f269e3021894 110 #endif
elessair 0:f269e3021894 111 US_TICKER_INT_MASK |
elessair 0:f269e3021894 112 NRF_RTC_INT_OVERFLOW_MASK);
elessair 0:f269e3021894 113
elessair 0:f269e3021894 114 // This event is enabled permanently, since overflow indications are needed
elessair 0:f269e3021894 115 // continuously.
elessair 0:f269e3021894 116 nrf_rtc_event_enable(COMMON_RTC_INSTANCE, NRF_RTC_INT_OVERFLOW_MASK);
elessair 0:f269e3021894 117 // All other relevant events are initially disabled.
elessair 0:f269e3021894 118 nrf_rtc_event_disable(COMMON_RTC_INSTANCE,
elessair 0:f269e3021894 119 #if defined(TARGET_MCU_NRF51822)
elessair 0:f269e3021894 120 OS_TICK_INT_MASK |
elessair 0:f269e3021894 121 #endif
elessair 0:f269e3021894 122 #if DEVICE_LOWPOWERTIMER
elessair 0:f269e3021894 123 LP_TICKER_INT_MASK |
elessair 0:f269e3021894 124 #endif
elessair 0:f269e3021894 125 US_TICKER_INT_MASK);
elessair 0:f269e3021894 126
elessair 0:f269e3021894 127 nrf_drv_common_irq_enable(nrf_drv_get_IRQn(COMMON_RTC_INSTANCE),
elessair 0:f269e3021894 128 APP_IRQ_PRIORITY_LOW);
elessair 0:f269e3021894 129
elessair 0:f269e3021894 130 nrf_rtc_task_trigger(COMMON_RTC_INSTANCE, NRF_RTC_TASK_START);
elessair 0:f269e3021894 131
elessair 0:f269e3021894 132 m_common_rtc_enabled = true;
elessair 0:f269e3021894 133 }
elessair 0:f269e3021894 134
elessair 0:f269e3021894 135 uint32_t common_rtc_32bit_ticks_get(void)
elessair 0:f269e3021894 136 {
elessair 0:f269e3021894 137 uint32_t ticks = nrf_rtc_counter_get(COMMON_RTC_INSTANCE);
elessair 0:f269e3021894 138 // The counter used for time measurements is less than 32 bit wide,
elessair 0:f269e3021894 139 // so its value is complemented with the number of registered overflows
elessair 0:f269e3021894 140 // of the counter.
elessair 0:f269e3021894 141 ticks += (m_common_rtc_overflows << RTC_COUNTER_BITS);
elessair 0:f269e3021894 142 return ticks;
elessair 0:f269e3021894 143 }
elessair 0:f269e3021894 144
elessair 0:f269e3021894 145 uint64_t common_rtc_64bit_us_get(void)
elessair 0:f269e3021894 146 {
elessair 0:f269e3021894 147 uint32_t ticks = common_rtc_32bit_ticks_get();
elessair 0:f269e3021894 148 // [ticks -> microseconds]
elessair 0:f269e3021894 149 return ROUNDED_DIV(((uint64_t)ticks) * 1000000, RTC_INPUT_FREQ);
elessair 0:f269e3021894 150 }
elessair 0:f269e3021894 151
elessair 0:f269e3021894 152 void common_rtc_set_interrupt(uint32_t us_timestamp, uint32_t cc_channel,
elessair 0:f269e3021894 153 uint32_t int_mask)
elessair 0:f269e3021894 154 {
elessair 0:f269e3021894 155 // The internal counter is clocked with a frequency that cannot be easily
elessair 0:f269e3021894 156 // multiplied to 1 MHz, therefore besides the translation of values
elessair 0:f269e3021894 157 // (microsecond <-> ticks) a special care of overflows handling must be
elessair 0:f269e3021894 158 // taken. Here the 32-bit timestamp value is complemented with information
elessair 0:f269e3021894 159 // about current the system up time of (ticks + number of overflows of tick
elessair 0:f269e3021894 160 // counter on upper bits, converted to microseconds), and such 64-bit value
elessair 0:f269e3021894 161 // is then translated to counter ticks. Finally, the lower 24 bits of thus
elessair 0:f269e3021894 162 // calculated value is written to the counter compare register to prepare
elessair 0:f269e3021894 163 // the interrupt generation.
elessair 0:f269e3021894 164 uint64_t current_time64 = common_rtc_64bit_us_get();
elessair 0:f269e3021894 165 // [add upper 32 bits from the current time to the timestamp value]
elessair 0:f269e3021894 166 uint64_t timestamp64 = us_timestamp +
elessair 0:f269e3021894 167 (current_time64 & ~(uint64_t)0xFFFFFFFF);
elessair 0:f269e3021894 168 // [if the original timestamp value happens to be after the 32 bit counter
elessair 0:f269e3021894 169 // of microsends overflows, correct the upper 32 bits accordingly]
elessair 0:f269e3021894 170 if (us_timestamp < (uint32_t)(current_time64 & 0xFFFFFFFF)) {
elessair 0:f269e3021894 171 timestamp64 += ((uint64_t)1 << 32);
elessair 0:f269e3021894 172 }
elessair 0:f269e3021894 173 // [microseconds -> ticks, always round the result up to avoid too early
elessair 0:f269e3021894 174 // interrupt generation]
elessair 0:f269e3021894 175 uint32_t compare_value =
elessair 0:f269e3021894 176 (uint32_t)CEIL_DIV((timestamp64) * RTC_INPUT_FREQ, 1000000);
elessair 0:f269e3021894 177
elessair 0:f269e3021894 178 // The COMPARE event occurs when the value in compare register is N and
elessair 0:f269e3021894 179 // the counter value changes from N-1 to N. Therefore, the minimal safe
elessair 0:f269e3021894 180 // difference between the compare value to be set and the current counter
elessair 0:f269e3021894 181 // value is 2 ticks. This guarantees that the compare trigger is properly
elessair 0:f269e3021894 182 // setup before the compare condition occurs.
elessair 0:f269e3021894 183 uint32_t closest_safe_compare = common_rtc_32bit_ticks_get() + 2;
elessair 0:f269e3021894 184 if ((int)(compare_value - closest_safe_compare) <= 0) {
elessair 0:f269e3021894 185 compare_value = closest_safe_compare;
elessair 0:f269e3021894 186 }
elessair 0:f269e3021894 187
elessair 0:f269e3021894 188 nrf_rtc_cc_set(COMMON_RTC_INSTANCE, cc_channel, RTC_WRAP(compare_value));
elessair 0:f269e3021894 189 nrf_rtc_event_enable(COMMON_RTC_INSTANCE, int_mask);
elessair 0:f269e3021894 190 }
elessair 0:f269e3021894 191 //------------------------------------------------------------------------------
elessair 0:f269e3021894 192
elessair 0:f269e3021894 193
elessair 0:f269e3021894 194 void us_ticker_init(void)
elessair 0:f269e3021894 195 {
elessair 0:f269e3021894 196 common_rtc_init();
elessair 0:f269e3021894 197 }
elessair 0:f269e3021894 198
elessair 0:f269e3021894 199 uint32_t us_ticker_read()
elessair 0:f269e3021894 200 {
elessair 0:f269e3021894 201 us_ticker_init();
elessair 0:f269e3021894 202 return (uint32_t)common_rtc_64bit_us_get();
elessair 0:f269e3021894 203 }
elessair 0:f269e3021894 204
elessair 0:f269e3021894 205 void us_ticker_set_interrupt(timestamp_t timestamp)
elessair 0:f269e3021894 206 {
elessair 0:f269e3021894 207 common_rtc_set_interrupt(timestamp,
elessair 0:f269e3021894 208 US_TICKER_CC_CHANNEL, US_TICKER_INT_MASK);
elessair 0:f269e3021894 209 }
elessair 0:f269e3021894 210
elessair 0:f269e3021894 211 void us_ticker_disable_interrupt(void)
elessair 0:f269e3021894 212 {
elessair 0:f269e3021894 213 nrf_rtc_event_disable(COMMON_RTC_INSTANCE, US_TICKER_INT_MASK);
elessair 0:f269e3021894 214 }
elessair 0:f269e3021894 215
elessair 0:f269e3021894 216 void us_ticker_clear_interrupt(void)
elessair 0:f269e3021894 217 {
elessair 0:f269e3021894 218 nrf_rtc_event_clear(COMMON_RTC_INSTANCE, US_TICKER_EVENT);
elessair 0:f269e3021894 219 }
elessair 0:f269e3021894 220
elessair 0:f269e3021894 221
elessair 0:f269e3021894 222 // Since there is no SysTick on NRF51, the RTC1 channel 1 is used as an
elessair 0:f269e3021894 223 // alternative source of RTOS ticks.
elessair 0:f269e3021894 224 #if defined(TARGET_MCU_NRF51822)
elessair 0:f269e3021894 225
elessair 0:f269e3021894 226 #include "toolchain.h"
elessair 0:f269e3021894 227
elessair 0:f269e3021894 228
elessair 0:f269e3021894 229 #define MAX_RTC_COUNTER_VAL ((1uL << RTC_COUNTER_BITS) - 1)
elessair 0:f269e3021894 230
elessair 0:f269e3021894 231 /**
elessair 0:f269e3021894 232 * The value previously set in the capture compare register of channel 1
elessair 0:f269e3021894 233 */
elessair 0:f269e3021894 234 static uint32_t previous_tick_cc_value = 0;
elessair 0:f269e3021894 235
elessair 0:f269e3021894 236 /*
elessair 0:f269e3021894 237 RTX provide the following definitions which are used by the tick code:
elessair 0:f269e3021894 238 * os_trv: The number (minus 1) of clock cycle between two tick.
elessair 0:f269e3021894 239 * os_clockrate: Time duration between two ticks (in us).
elessair 0:f269e3021894 240 * OS_Tick_Handler: The function which handle a tick event.
elessair 0:f269e3021894 241 This function is special because it never returns.
elessair 0:f269e3021894 242 Those definitions are used by the code which handle the os tick.
elessair 0:f269e3021894 243 To allow compilation of us_ticker programs without RTOS, those symbols are
elessair 0:f269e3021894 244 exported from this module as weak ones.
elessair 0:f269e3021894 245 */
elessair 0:f269e3021894 246 MBED_WEAK uint32_t const os_trv;
elessair 0:f269e3021894 247 MBED_WEAK uint32_t const os_clockrate;
elessair 0:f269e3021894 248 MBED_WEAK void OS_Tick_Handler() { }
elessair 0:f269e3021894 249
elessair 0:f269e3021894 250
elessair 0:f269e3021894 251 #if defined (__CC_ARM) /* ARMCC Compiler */
elessair 0:f269e3021894 252
elessair 0:f269e3021894 253 __asm void COMMON_RTC_IRQ_HANDLER(void)
elessair 0:f269e3021894 254 {
elessair 0:f269e3021894 255 IMPORT OS_Tick_Handler
elessair 0:f269e3021894 256 IMPORT common_rtc_irq_handler
elessair 0:f269e3021894 257
elessair 0:f269e3021894 258 /**
elessair 0:f269e3021894 259 * Chanel 1 of RTC1 is used by RTX as a systick.
elessair 0:f269e3021894 260 * If the compare event on channel 1 is set, then branch to OS_Tick_Handler.
elessair 0:f269e3021894 261 * Otherwise, just execute common_rtc_irq_handler.
elessair 0:f269e3021894 262 * This function has to be written in assembly and tagged as naked because OS_Tick_Handler
elessair 0:f269e3021894 263 * will never return.
elessair 0:f269e3021894 264 * A c function would put lr on the stack before calling OS_Tick_Handler and this value
elessair 0:f269e3021894 265 * would never been dequeued.
elessair 0:f269e3021894 266 *
elessair 0:f269e3021894 267 * \code
elessair 0:f269e3021894 268 * void COMMON_RTC_IRQ_HANDLER(void) {
elessair 0:f269e3021894 269 if(NRF_RTC1->EVENTS_COMPARE[1]) {
elessair 0:f269e3021894 270 // never return...
elessair 0:f269e3021894 271 OS_Tick_Handler();
elessair 0:f269e3021894 272 } else {
elessair 0:f269e3021894 273 common_rtc_irq_handler();
elessair 0:f269e3021894 274 }
elessair 0:f269e3021894 275 }
elessair 0:f269e3021894 276 * \endcode
elessair 0:f269e3021894 277 */
elessair 0:f269e3021894 278 ldr r0,=0x40011144
elessair 0:f269e3021894 279 ldr r1, [r0, #0]
elessair 0:f269e3021894 280 cmp r1, #0
elessair 0:f269e3021894 281 beq US_TICKER_HANDLER
elessair 0:f269e3021894 282 bl OS_Tick_Handler
elessair 0:f269e3021894 283 US_TICKER_HANDLER
elessair 0:f269e3021894 284 push {r3, lr}
elessair 0:f269e3021894 285 bl common_rtc_irq_handler
elessair 0:f269e3021894 286 pop {r3, pc}
elessair 0:f269e3021894 287 ; ALIGN ;
elessair 0:f269e3021894 288 }
elessair 0:f269e3021894 289
elessair 0:f269e3021894 290 #elif defined (__GNUC__) /* GNU Compiler */
elessair 0:f269e3021894 291
elessair 0:f269e3021894 292 __attribute__((naked)) void COMMON_RTC_IRQ_HANDLER(void)
elessair 0:f269e3021894 293 {
elessair 0:f269e3021894 294 /**
elessair 0:f269e3021894 295 * Chanel 1 of RTC1 is used by RTX as a systick.
elessair 0:f269e3021894 296 * If the compare event on channel 1 is set, then branch to OS_Tick_Handler.
elessair 0:f269e3021894 297 * Otherwise, just execute common_rtc_irq_handler.
elessair 0:f269e3021894 298 * This function has to be written in assembly and tagged as naked because OS_Tick_Handler
elessair 0:f269e3021894 299 * will never return.
elessair 0:f269e3021894 300 * A c function would put lr on the stack before calling OS_Tick_Handler and this value
elessair 0:f269e3021894 301 * would never been dequeued.
elessair 0:f269e3021894 302 *
elessair 0:f269e3021894 303 * \code
elessair 0:f269e3021894 304 * void COMMON_RTC_IRQ_HANDLER(void) {
elessair 0:f269e3021894 305 if(NRF_RTC1->EVENTS_COMPARE[1]) {
elessair 0:f269e3021894 306 // never return...
elessair 0:f269e3021894 307 OS_Tick_Handler();
elessair 0:f269e3021894 308 } else {
elessair 0:f269e3021894 309 common_rtc_irq_handler();
elessair 0:f269e3021894 310 }
elessair 0:f269e3021894 311 }
elessair 0:f269e3021894 312 * \endcode
elessair 0:f269e3021894 313 */
elessair 0:f269e3021894 314 __asm__ (
elessair 0:f269e3021894 315 "ldr r0,=0x40011144\n"
elessair 0:f269e3021894 316 "ldr r1, [r0, #0]\n"
elessair 0:f269e3021894 317 "cmp r1, #0\n"
elessair 0:f269e3021894 318 "beq US_TICKER_HANDLER\n"
elessair 0:f269e3021894 319 "bl OS_Tick_Handler\n"
elessair 0:f269e3021894 320 "US_TICKER_HANDLER:\n"
elessair 0:f269e3021894 321 "push {r3, lr}\n"
elessair 0:f269e3021894 322 "bl common_rtc_irq_handler\n"
elessair 0:f269e3021894 323 "pop {r3, pc}\n"
elessair 0:f269e3021894 324 "nop"
elessair 0:f269e3021894 325 );
elessair 0:f269e3021894 326 }
elessair 0:f269e3021894 327
elessair 0:f269e3021894 328 #elif defined (__ICCARM__)//IAR
elessair 0:f269e3021894 329 void common_rtc_irq_handler(void);
elessair 0:f269e3021894 330
elessair 0:f269e3021894 331 __stackless __task void COMMON_RTC_IRQ_HANDLER(void)
elessair 0:f269e3021894 332 {
elessair 0:f269e3021894 333 uint32_t temp;
elessair 0:f269e3021894 334
elessair 0:f269e3021894 335 __asm volatile(
elessair 0:f269e3021894 336 " ldr %[temp], [%[reg2check]] \n"
elessair 0:f269e3021894 337 " cmp %[temp], #0 \n"
elessair 0:f269e3021894 338 " beq 1f \n"
elessair 0:f269e3021894 339 " bl.w OS_Tick_Handler \n"
elessair 0:f269e3021894 340 "1: \n"
elessair 0:f269e3021894 341 " push {r3, lr}\n"
elessair 0:f269e3021894 342 " blx %[rtc_irq] \n"
elessair 0:f269e3021894 343 " pop {r3, pc}\n"
elessair 0:f269e3021894 344
elessair 0:f269e3021894 345 : /* Outputs */
elessair 0:f269e3021894 346 [temp] "=&r"(temp)
elessair 0:f269e3021894 347 : /* Inputs */
elessair 0:f269e3021894 348 [reg2check] "r"(0x40011144),
elessair 0:f269e3021894 349 [rtc_irq] "r"(common_rtc_irq_handler)
elessair 0:f269e3021894 350 : /* Clobbers */
elessair 0:f269e3021894 351 "cc"
elessair 0:f269e3021894 352 );
elessair 0:f269e3021894 353 (void)temp;
elessair 0:f269e3021894 354 }
elessair 0:f269e3021894 355
elessair 0:f269e3021894 356
elessair 0:f269e3021894 357 #else
elessair 0:f269e3021894 358
elessair 0:f269e3021894 359 #error Compiler not supported.
elessair 0:f269e3021894 360 #error Provide a definition of COMMON_RTC_IRQ_HANDLER.
elessair 0:f269e3021894 361
elessair 0:f269e3021894 362 /*
elessair 0:f269e3021894 363 * Chanel 1 of RTC1 is used by RTX as a systick.
elessair 0:f269e3021894 364 * If the compare event on channel 1 is set, then branch to OS_Tick_Handler.
elessair 0:f269e3021894 365 * Otherwise, just execute common_rtc_irq_handler.
elessair 0:f269e3021894 366 * This function has to be written in assembly and tagged as naked because OS_Tick_Handler
elessair 0:f269e3021894 367 * will never return.
elessair 0:f269e3021894 368 * A c function would put lr on the stack before calling OS_Tick_Handler and this value
elessair 0:f269e3021894 369 * will never been dequeued. After a certain time a stack overflow will happen.
elessair 0:f269e3021894 370 *
elessair 0:f269e3021894 371 * \code
elessair 0:f269e3021894 372 * void COMMON_RTC_IRQ_HANDLER(void) {
elessair 0:f269e3021894 373 if(NRF_RTC1->EVENTS_COMPARE[1]) {
elessair 0:f269e3021894 374 // never return...
elessair 0:f269e3021894 375 OS_Tick_Handler();
elessair 0:f269e3021894 376 } else {
elessair 0:f269e3021894 377 common_rtc_irq_handler();
elessair 0:f269e3021894 378 }
elessair 0:f269e3021894 379 }
elessair 0:f269e3021894 380 * \endcode
elessair 0:f269e3021894 381 */
elessair 0:f269e3021894 382
elessair 0:f269e3021894 383 #endif
elessair 0:f269e3021894 384
elessair 0:f269e3021894 385 /**
elessair 0:f269e3021894 386 * Return the next number of clock cycle needed for the next tick.
elessair 0:f269e3021894 387 * @note This function has been carrefuly optimized for a systick occuring every 1000us.
elessair 0:f269e3021894 388 */
elessair 0:f269e3021894 389 static uint32_t get_next_tick_cc_delta() {
elessair 0:f269e3021894 390 uint32_t delta = 0;
elessair 0:f269e3021894 391
elessair 0:f269e3021894 392 if (os_clockrate != 1000) {
elessair 0:f269e3021894 393 // In RTX, by default SYSTICK is is used.
elessair 0:f269e3021894 394 // A tick event is generated every os_trv + 1 clock cycles of the system timer.
elessair 0:f269e3021894 395 delta = os_trv + 1;
elessair 0:f269e3021894 396 } else {
elessair 0:f269e3021894 397 // If the clockrate is set to 1000us then 1000 tick should happen every second.
elessair 0:f269e3021894 398 // Unfortunatelly, when clockrate is set to 1000, os_trv is equal to 31.
elessair 0:f269e3021894 399 // If (os_trv + 1) is used as the delta value between two ticks, 1000 ticks will be
elessair 0:f269e3021894 400 // generated in 32000 clock cycle instead of 32768 clock cycles.
elessair 0:f269e3021894 401 // As a result, if a user schedule an OS timer to start in 100s, the timer will start
elessair 0:f269e3021894 402 // instead after 97.656s
elessair 0:f269e3021894 403 // The code below fix this issue, a clock rate of 1000s will generate 1000 ticks in 32768
elessair 0:f269e3021894 404 // clock cycles.
elessair 0:f269e3021894 405 // The strategy is simple, for 1000 ticks:
elessair 0:f269e3021894 406 // * 768 ticks will occur 33 clock cycles after the previous tick
elessair 0:f269e3021894 407 // * 232 ticks will occur 32 clock cycles after the previous tick
elessair 0:f269e3021894 408 // By default every delta is equal to 33.
elessair 0:f269e3021894 409 // Every five ticks (20%, 200 delta in one second), the delta is equal to 32
elessair 0:f269e3021894 410 // The remaining (32) deltas equal to 32 are distributed using primes numbers.
elessair 0:f269e3021894 411 static uint32_t counter = 0;
elessair 0:f269e3021894 412 if ((counter % 5) == 0 || (counter % 31) == 0 || (counter % 139) == 0 || (counter == 503)) {
elessair 0:f269e3021894 413 delta = 32;
elessair 0:f269e3021894 414 } else {
elessair 0:f269e3021894 415 delta = 33;
elessair 0:f269e3021894 416 }
elessair 0:f269e3021894 417 ++counter;
elessair 0:f269e3021894 418 if (counter == 1000) {
elessair 0:f269e3021894 419 counter = 0;
elessair 0:f269e3021894 420 }
elessair 0:f269e3021894 421 }
elessair 0:f269e3021894 422 return delta;
elessair 0:f269e3021894 423 }
elessair 0:f269e3021894 424
elessair 0:f269e3021894 425 static inline void clear_tick_interrupt() {
elessair 0:f269e3021894 426 nrf_rtc_event_clear(COMMON_RTC_INSTANCE, OS_TICK_EVENT);
elessair 0:f269e3021894 427 nrf_rtc_event_disable(COMMON_RTC_INSTANCE, OS_TICK_INT_MASK);
elessair 0:f269e3021894 428 }
elessair 0:f269e3021894 429
elessair 0:f269e3021894 430 /**
elessair 0:f269e3021894 431 * Indicate if a value is included in a range which can be wrapped.
elessair 0:f269e3021894 432 * @param begin start of the range
elessair 0:f269e3021894 433 * @param end end of the range
elessair 0:f269e3021894 434 * @param val value to check
elessair 0:f269e3021894 435 * @return true if the value is included in the range and false otherwise.
elessair 0:f269e3021894 436 */
elessair 0:f269e3021894 437 static inline bool is_in_wrapped_range(uint32_t begin, uint32_t end, uint32_t val) {
elessair 0:f269e3021894 438 // regular case, begin < end
elessair 0:f269e3021894 439 // return true if begin <= val < end
elessair 0:f269e3021894 440 if (begin < end) {
elessair 0:f269e3021894 441 if (begin <= val && val < end) {
elessair 0:f269e3021894 442 return true;
elessair 0:f269e3021894 443 } else {
elessair 0:f269e3021894 444 return false;
elessair 0:f269e3021894 445 }
elessair 0:f269e3021894 446 } else {
elessair 0:f269e3021894 447 // In this case end < begin because it has wrap around the limits
elessair 0:f269e3021894 448 // return false if end < val < begin
elessair 0:f269e3021894 449 if (end < val && val < begin) {
elessair 0:f269e3021894 450 return false;
elessair 0:f269e3021894 451 } else {
elessair 0:f269e3021894 452 return true;
elessair 0:f269e3021894 453 }
elessair 0:f269e3021894 454 }
elessair 0:f269e3021894 455
elessair 0:f269e3021894 456 }
elessair 0:f269e3021894 457
elessair 0:f269e3021894 458 /**
elessair 0:f269e3021894 459 * Register the next tick.
elessair 0:f269e3021894 460 */
elessair 0:f269e3021894 461 static void register_next_tick() {
elessair 0:f269e3021894 462 previous_tick_cc_value = nrf_rtc_cc_get(COMMON_RTC_INSTANCE, OS_TICK_CC_CHANNEL);
elessair 0:f269e3021894 463 uint32_t delta = get_next_tick_cc_delta();
elessair 0:f269e3021894 464 uint32_t new_compare_value = (previous_tick_cc_value + delta) & MAX_RTC_COUNTER_VAL;
elessair 0:f269e3021894 465
elessair 0:f269e3021894 466 // Disable irq directly for few cycles,
elessair 0:f269e3021894 467 // Validation of the new CC value against the COUNTER,
elessair 0:f269e3021894 468 // Setting the new CC value and enabling CC IRQ should be an atomic operation
elessair 0:f269e3021894 469 // Otherwise, there is a possibility to set an invalid CC value because
elessair 0:f269e3021894 470 // the RTC1 keeps running.
elessair 0:f269e3021894 471 // This code is very short 20-38 cycles in the worst case, it shouldn't
elessair 0:f269e3021894 472 // disturb softdevice.
elessair 0:f269e3021894 473 __disable_irq();
elessair 0:f269e3021894 474 uint32_t current_counter = nrf_rtc_counter_get(COMMON_RTC_INSTANCE);
elessair 0:f269e3021894 475
elessair 0:f269e3021894 476 // If an overflow occur, set the next tick in COUNTER + delta clock cycles
elessair 0:f269e3021894 477 if (is_in_wrapped_range(previous_tick_cc_value, new_compare_value, current_counter + 1) == false) {
elessair 0:f269e3021894 478 new_compare_value = current_counter + delta;
elessair 0:f269e3021894 479 }
elessair 0:f269e3021894 480 nrf_rtc_cc_set(COMMON_RTC_INSTANCE, OS_TICK_CC_CHANNEL, new_compare_value);
elessair 0:f269e3021894 481 // Enable generation of the compare event for the value set above (this
elessair 0:f269e3021894 482 // event will trigger the interrupt).
elessair 0:f269e3021894 483 nrf_rtc_event_enable(COMMON_RTC_INSTANCE, OS_TICK_INT_MASK);
elessair 0:f269e3021894 484 __enable_irq();
elessair 0:f269e3021894 485 }
elessair 0:f269e3021894 486
elessair 0:f269e3021894 487 /**
elessair 0:f269e3021894 488 * Initialize alternative hardware timer as RTX kernel timer
elessair 0:f269e3021894 489 * This function is directly called by RTX.
elessair 0:f269e3021894 490 * @note this function shouldn't be called directly.
elessair 0:f269e3021894 491 * @return IRQ number of the alternative hardware timer
elessair 0:f269e3021894 492 */
elessair 0:f269e3021894 493 int os_tick_init (void)
elessair 0:f269e3021894 494 {
elessair 0:f269e3021894 495 common_rtc_init();
elessair 0:f269e3021894 496
elessair 0:f269e3021894 497 nrf_rtc_cc_set(COMMON_RTC_INSTANCE, OS_TICK_CC_CHANNEL, 0);
elessair 0:f269e3021894 498 register_next_tick();
elessair 0:f269e3021894 499
elessair 0:f269e3021894 500 return nrf_drv_get_IRQn(COMMON_RTC_INSTANCE);
elessair 0:f269e3021894 501 }
elessair 0:f269e3021894 502
elessair 0:f269e3021894 503 /**
elessair 0:f269e3021894 504 * Acknowledge the tick interrupt.
elessair 0:f269e3021894 505 * This function is called by the function OS_Tick_Handler of RTX.
elessair 0:f269e3021894 506 * @note this function shouldn't be called directly.
elessair 0:f269e3021894 507 */
elessair 0:f269e3021894 508 void os_tick_irqack(void)
elessair 0:f269e3021894 509 {
elessair 0:f269e3021894 510 clear_tick_interrupt();
elessair 0:f269e3021894 511 register_next_tick();
elessair 0:f269e3021894 512 }
elessair 0:f269e3021894 513
elessair 0:f269e3021894 514 /**
elessair 0:f269e3021894 515 * Returns the overflow flag of the alternative hardware timer.
elessair 0:f269e3021894 516 * @note This function is exposed by RTX kernel.
elessair 0:f269e3021894 517 * @return 1 if the timer has overflowed and 0 otherwise.
elessair 0:f269e3021894 518 */
elessair 0:f269e3021894 519 uint32_t os_tick_ovf(void) {
elessair 0:f269e3021894 520 uint32_t current_counter = nrf_rtc_counter_get(COMMON_RTC_INSTANCE);
elessair 0:f269e3021894 521 uint32_t next_tick_cc_value = nrf_rtc_cc_get(COMMON_RTC_INSTANCE, OS_TICK_CC_CHANNEL);
elessair 0:f269e3021894 522
elessair 0:f269e3021894 523 return is_in_wrapped_range(previous_tick_cc_value, next_tick_cc_value, current_counter) ? 0 : 1;
elessair 0:f269e3021894 524 }
elessair 0:f269e3021894 525
elessair 0:f269e3021894 526 /**
elessair 0:f269e3021894 527 * Return the value of the alternative hardware timer.
elessair 0:f269e3021894 528 * @note The documentation is not very clear about what is expected as a result,
elessair 0:f269e3021894 529 * is it an ascending counter, a descending one ?
elessair 0:f269e3021894 530 * None of this is specified.
elessair 0:f269e3021894 531 * The default systick is a descending counter and this function return values in
elessair 0:f269e3021894 532 * descending order, even if the internal counter used is an ascending one.
elessair 0:f269e3021894 533 * @return the value of the alternative hardware timer.
elessair 0:f269e3021894 534 */
elessair 0:f269e3021894 535 uint32_t os_tick_val(void) {
elessair 0:f269e3021894 536 uint32_t current_counter = nrf_rtc_counter_get(COMMON_RTC_INSTANCE);
elessair 0:f269e3021894 537 uint32_t next_tick_cc_value = nrf_rtc_cc_get(COMMON_RTC_INSTANCE, OS_TICK_CC_CHANNEL);
elessair 0:f269e3021894 538
elessair 0:f269e3021894 539 // do not use os_tick_ovf because its counter value can be different
elessair 0:f269e3021894 540 if(is_in_wrapped_range(previous_tick_cc_value, next_tick_cc_value, current_counter)) {
elessair 0:f269e3021894 541 if (next_tick_cc_value > previous_tick_cc_value) {
elessair 0:f269e3021894 542 return next_tick_cc_value - current_counter;
elessair 0:f269e3021894 543 } else if(current_counter <= next_tick_cc_value) {
elessair 0:f269e3021894 544 return next_tick_cc_value - current_counter;
elessair 0:f269e3021894 545 } else {
elessair 0:f269e3021894 546 return next_tick_cc_value + (MAX_RTC_COUNTER_VAL - current_counter);
elessair 0:f269e3021894 547 }
elessair 0:f269e3021894 548 } else {
elessair 0:f269e3021894 549 // use (os_trv + 1) has the base step, can be totally inacurate ...
elessair 0:f269e3021894 550 uint32_t clock_cycles_by_tick = os_trv + 1;
elessair 0:f269e3021894 551
elessair 0:f269e3021894 552 // if current counter has wrap arround, add the limit to it.
elessair 0:f269e3021894 553 if (current_counter < next_tick_cc_value) {
elessair 0:f269e3021894 554 current_counter = current_counter + MAX_RTC_COUNTER_VAL;
elessair 0:f269e3021894 555 }
elessair 0:f269e3021894 556
elessair 0:f269e3021894 557 return clock_cycles_by_tick - ((current_counter - next_tick_cc_value) % clock_cycles_by_tick);
elessair 0:f269e3021894 558 }
elessair 0:f269e3021894 559
elessair 0:f269e3021894 560 }
elessair 0:f269e3021894 561
elessair 0:f269e3021894 562 #endif // defined(TARGET_MCU_NRF51822)