Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: TYBLE16_simple_data_logger TYBLE16_MP3_Air
SysTimer.cpp
00001 /* mbed Microcontroller Library 00002 * Copyright (c) 2006-2019 ARM Limited 00003 * SPDX-License-Identifier: Apache-2.0 00004 * 00005 * Licensed under the Apache License, Version 2.0 (the "License"); 00006 * you may not use this file except in compliance with the License. 00007 * You may obtain a copy of the License at 00008 * 00009 * http://www.apache.org/licenses/LICENSE-2.0 00010 * 00011 * Unless required by applicable law or agreed to in writing, software 00012 * distributed under the License is distributed on an "AS IS" BASIS, 00013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00014 * See the License for the specific language governing permissions and 00015 * limitations under the License. 00016 */ 00017 00018 #include "hal/us_ticker_api.h" 00019 #include "hal/lp_ticker_api.h" 00020 #include "mbed_atomic.h" 00021 #include "mbed_critical.h" 00022 #include "mbed_assert.h" 00023 #include "platform/mbed_power_mgmt.h" 00024 #include "platform/CriticalSectionLock.h" 00025 #include "platform/source/SysTimer.h" 00026 extern "C" { 00027 #if MBED_CONF_RTOS_PRESENT 00028 #include "rtx_lib.h" 00029 #endif 00030 } 00031 00032 #if (defined(NO_SYSTICK)) 00033 /** 00034 * Return an IRQ number that can be used in the absence of SysTick 00035 * 00036 * @return Free IRQ number that can be used 00037 */ 00038 extern "C" IRQn_Type mbed_get_m0_tick_irqn(void); 00039 #endif 00040 00041 #if defined(TARGET_CORTEX_A) 00042 extern "C" IRQn_ID_t mbed_get_a9_tick_irqn(void); 00043 #endif 00044 00045 namespace mbed { 00046 namespace internal { 00047 00048 template<uint32_t US_IN_TICK, bool IRQ> 00049 SysTimer<US_IN_TICK, IRQ>::SysTimer() : 00050 #if DEVICE_LPTICKER 00051 TimerEvent(get_lp_ticker_data()), 00052 #else 00053 TimerEvent(get_us_ticker_data()), 00054 #endif 00055 _time_us(ticker_read_us(_ticker_data)), 00056 _tick(0), 00057 _unacknowledged_ticks(0), 00058 _wake_time_set(false), 00059 _wake_time_passed(false), 00060 _wake_early(false), 00061 _ticking(false), 00062 _deep_sleep_locked(false) 00063 { 00064 } 00065 00066 template<uint32_t US_IN_TICK, bool IRQ> 00067 SysTimer<US_IN_TICK, IRQ>::SysTimer(const ticker_data_t *data) : 00068 TimerEvent(data), 00069 _time_us(ticker_read_us(_ticker_data)), 00070 _tick(0), 00071 _unacknowledged_ticks(0), 00072 _wake_time_set(false), 00073 _wake_time_passed(false), 00074 _wake_early(false), 00075 _ticking(false), 00076 _deep_sleep_locked(false) 00077 { 00078 } 00079 00080 template<uint32_t US_IN_TICK, bool IRQ> 00081 SysTimer<US_IN_TICK, IRQ>::~SysTimer() 00082 { 00083 cancel_tick(); 00084 cancel_wake(); 00085 } 00086 00087 template<uint32_t US_IN_TICK, bool IRQ> 00088 void SysTimer<US_IN_TICK, IRQ>::set_wake_time(uint64_t at) 00089 { 00090 // SysTimer must not be active - we must be in suspend state 00091 MBED_ASSERT(!_ticking); 00092 00093 // There is a potential race here, when called from outside 00094 // a critical section. See function documentation for notes on 00095 // handling it. 00096 if (core_util_atomic_load_bool (&_wake_time_set)) { 00097 return; 00098 } 00099 00100 // Analyse the timers 00101 if (update_and_get_tick() >= at) { 00102 _wake_time_passed = true; 00103 return; 00104 } 00105 00106 uint64_t ticks_to_sleep = at - _tick; 00107 uint64_t wake_time = at * US_IN_TICK; 00108 00109 /* Set this first, before attaching the interrupt that can unset it */ 00110 _wake_time_set = true; 00111 _wake_time_passed = false; 00112 00113 if (!_deep_sleep_locked && !_ticker_data->interface->runs_in_deep_sleep) { 00114 _deep_sleep_locked = true; 00115 sleep_manager_lock_deep_sleep(); 00116 } 00117 /* Consider whether we will need early or precise wake-up */ 00118 if (MBED_CONF_TARGET_DEEP_SLEEP_LATENCY > 0 && 00119 ticks_to_sleep > MBED_CONF_TARGET_DEEP_SLEEP_LATENCY && 00120 !_deep_sleep_locked) { 00121 /* If there is deep sleep latency, but we still have enough time, 00122 * and we haven't blocked deep sleep ourselves, 00123 * allow for that latency by requesting early wake-up. 00124 * Actual sleep may or may not be deep, depending on other actors. 00125 */ 00126 _wake_early = true; 00127 insert_absolute(wake_time - MBED_CONF_TARGET_DEEP_SLEEP_LATENCY * US_IN_TICK); 00128 } else { 00129 /* Otherwise, set up to wake at the precise time. 00130 * If there is a deep sleep latency, ensure that we're holding the lock so the sleep 00131 * is shallow. (If there is no deep sleep latency, we're fine with it being deep). 00132 */ 00133 _wake_early = false; 00134 if (MBED_CONF_TARGET_DEEP_SLEEP_LATENCY > 0 && !_deep_sleep_locked) { 00135 _deep_sleep_locked = true; 00136 sleep_manager_lock_deep_sleep(); 00137 } 00138 insert_absolute(wake_time); 00139 } 00140 } 00141 00142 template<uint32_t US_IN_TICK, bool IRQ> 00143 void SysTimer<US_IN_TICK, IRQ>::cancel_wake() 00144 { 00145 MBED_ASSERT(!_ticking); 00146 // Remove ensures serialized access to SysTimer by stopping timer interrupt 00147 remove(); 00148 00149 _wake_time_set = false; 00150 _wake_time_passed = false; 00151 00152 if (_deep_sleep_locked) { 00153 _deep_sleep_locked = false; 00154 sleep_manager_unlock_deep_sleep(); 00155 } 00156 } 00157 00158 template<uint32_t US_IN_TICK, bool IRQ> 00159 uint64_t SysTimer<US_IN_TICK, IRQ>::_elapsed_ticks() const 00160 { 00161 uint64_t elapsed_us = ticker_read_us(_ticker_data) - _time_us; 00162 if (elapsed_us < US_IN_TICK) { 00163 return 0; 00164 } else if (elapsed_us < 2 * US_IN_TICK) { 00165 return 1; 00166 } else if (elapsed_us <= 0xFFFFFFFF) { 00167 // Fast common case avoiding 64-bit division 00168 return (uint32_t) elapsed_us / US_IN_TICK; 00169 } else { 00170 return elapsed_us / US_IN_TICK; 00171 } 00172 } 00173 00174 template<uint32_t US_IN_TICK, bool IRQ> 00175 void SysTimer<US_IN_TICK, IRQ>::start_tick() 00176 { 00177 _ticking = true; 00178 if (_unacknowledged_ticks > 0) { 00179 _set_irq_pending(); 00180 } 00181 _schedule_tick(); 00182 } 00183 00184 template<uint32_t US_IN_TICK, bool IRQ> 00185 void SysTimer<US_IN_TICK, IRQ>::_schedule_tick() 00186 { 00187 insert_absolute(_time_us + US_IN_TICK); 00188 } 00189 00190 template<uint32_t US_IN_TICK, bool IRQ> 00191 void SysTimer<US_IN_TICK, IRQ>::acknowledge_tick() 00192 { 00193 // Try to avoid missed ticks if OS's IRQ level is not keeping 00194 // up with our handler. 00195 // 8-bit counter to save space, and also make sure it we don't 00196 // try TOO hard to resync if something goes really awry - 00197 // resync will reset if the count hits 256. 00198 if (core_util_atomic_decr_u8(&_unacknowledged_ticks, 1) > 0) { 00199 _set_irq_pending(); 00200 } 00201 } 00202 00203 template<uint32_t US_IN_TICK, bool IRQ> 00204 void SysTimer<US_IN_TICK, IRQ>::cancel_tick() 00205 { 00206 // Underlying call is interrupt safe 00207 00208 remove(); 00209 _ticking = false; 00210 00211 _clear_irq_pending(); 00212 } 00213 00214 template<uint32_t US_IN_TICK, bool IRQ> 00215 uint64_t SysTimer<US_IN_TICK, IRQ>::get_tick() const 00216 { 00217 // Atomic is necessary as this can be called from any foreground context, 00218 // while IRQ can update it. 00219 return core_util_atomic_load_u64 (&_tick); 00220 } 00221 00222 template<uint32_t US_IN_TICK, bool IRQ> 00223 uint64_t SysTimer<US_IN_TICK, IRQ>::update_and_get_tick() 00224 { 00225 MBED_ASSERT(!_ticking && !_wake_time_set); 00226 // Can only be used when no interrupts are scheduled 00227 // Update counters to reflect elapsed time 00228 uint64_t elapsed_ticks = _elapsed_ticks(); 00229 _unacknowledged_ticks = 0; 00230 _time_us += elapsed_ticks * US_IN_TICK; 00231 _tick += elapsed_ticks; 00232 00233 return _tick; 00234 } 00235 00236 template<uint32_t US_IN_TICK, bool IRQ> 00237 us_timestamp_t SysTimer<US_IN_TICK, IRQ>::get_time() const 00238 { 00239 // Underlying call is interrupt safe 00240 00241 return ticker_read_us(_ticker_data); 00242 } 00243 00244 template<uint32_t US_IN_TICK, bool IRQ> 00245 us_timestamp_t SysTimer<US_IN_TICK, IRQ>::get_time_since_tick() const 00246 { 00247 // Underlying call is interrupt safe, and _time_us is not updated by IRQ 00248 00249 return get_time() - _time_us; 00250 } 00251 00252 #if (defined(NO_SYSTICK)) 00253 template<uint32_t US_IN_TICK, bool IRQ> 00254 IRQn_Type SysTimer<US_IN_TICK, IRQ>::get_irq_number() 00255 { 00256 return mbed_get_m0_tick_irqn(); 00257 } 00258 #elif (TARGET_CORTEX_M) 00259 template<uint32_t US_IN_TICK, bool IRQ> 00260 IRQn_Type SysTimer<US_IN_TICK, IRQ>::get_irq_number() 00261 { 00262 return SysTick_IRQn; 00263 } 00264 #elif (TARGET_CORTEX_A) 00265 template<uint32_t US_IN_TICK, bool IRQ> 00266 IRQn_ID_t SysTimer<US_IN_TICK, IRQ>::get_irq_number() 00267 { 00268 return mbed_get_a9_tick_irqn(); 00269 } 00270 #endif 00271 00272 template<uint32_t US_IN_TICK, bool IRQ> 00273 void SysTimer<US_IN_TICK, IRQ>::_set_irq_pending() 00274 { 00275 // Protected function synchronized externally 00276 if (!IRQ) { 00277 return; 00278 } 00279 #if (defined(NO_SYSTICK)) 00280 NVIC_SetPendingIRQ(mbed_get_m0_tick_irqn()); 00281 #elif (TARGET_CORTEX_M) 00282 SCB->ICSR = SCB_ICSR_PENDSTSET_Msk; 00283 #else 00284 IRQ_SetPending(mbed_get_a9_tick_irqn()); 00285 #endif 00286 } 00287 00288 template<uint32_t US_IN_TICK, bool IRQ> 00289 void SysTimer<US_IN_TICK, IRQ>::_clear_irq_pending() 00290 { 00291 // Protected function synchronized externally 00292 if (!IRQ) { 00293 return; 00294 } 00295 #if (defined(NO_SYSTICK)) 00296 NVIC_ClearPendingIRQ(mbed_get_m0_tick_irqn()); 00297 #elif (TARGET_CORTEX_M) 00298 SCB->ICSR = SCB_ICSR_PENDSTCLR_Msk; 00299 #else 00300 IRQ_ClearPending(mbed_get_a9_tick_irqn()); 00301 #endif 00302 } 00303 00304 template<uint32_t US_IN_TICK, bool IRQ> 00305 void SysTimer<US_IN_TICK, IRQ>::_increment_tick() 00306 { 00307 // Protected function synchronized externally 00308 00309 _tick++; 00310 _time_us += US_IN_TICK; 00311 } 00312 00313 template<uint32_t US_IN_TICK, bool IRQ> 00314 void SysTimer<US_IN_TICK, IRQ>::handler() 00315 { 00316 /* To reduce IRQ latency problems, we do not re-arm in the interrupt handler */ 00317 if (_wake_time_set) { 00318 _wake_time_set = false; 00319 if (!_wake_early) { 00320 _wake_time_passed = true; 00321 } 00322 /* If this was an early interrupt, user has the responsibility to check and 00323 * note the combination of (!set, !passed), and re-arm the wake timer if 00324 * necessary. 00325 */ 00326 } else if (_ticking) { 00327 _unacknowledged_ticks++; 00328 _set_irq_pending(); 00329 _increment_tick(); 00330 // We do this now, rather than in acknowledgement, as we get it "for free" 00331 // here - because we're in the ticker handler, the programming gets deferred 00332 // until end of dispatch, and the ticker would likely be rescheduling 00333 // anyway after dispatch. 00334 00335 _schedule_tick(); 00336 } 00337 } 00338 00339 #if MBED_CONF_RTOS_PRESENT 00340 /* Whatever the OS wants (in case it isn't 1ms) */ 00341 MBED_STATIC_ASSERT(1000000 % OS_TICK_FREQ == 0, "OS_TICK_FREQ must be a divisor of 1000000 for correct tick calculations"); 00342 #define OS_TICK_US (1000000 / OS_TICK_FREQ) 00343 #if OS_TICK_US != 1000 00344 template class SysTimer<OS_TICK_US>; 00345 #endif 00346 #endif 00347 00348 /* Standard 1ms SysTimer */ 00349 template class SysTimer<1000>; 00350 00351 /* Standard 1ms SysTimer that doesn't set interrupts, used for Greentea tests */ 00352 template class SysTimer<1000, false>; 00353 00354 /* Slowed-down SysTimer that doesn't set interrupts, used for Greentea tests */ 00355 template class SysTimer<42000, false>; 00356 00357 } // namespace internal 00358 } // namespace mbed
Generated on Tue Jul 12 2022 13:54:55 by
1.7.2