mbed library sources. Supersedes mbed-src.

Dependents:   Nucleo_Hello_Encoder BLE_iBeaconScan AM1805_DEMO DISCO-F429ZI_ExportTemplate1 ... more

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers LowPowerTickerWrapper.cpp Source File

LowPowerTickerWrapper.cpp

00001 /* mbed Microcontroller Library
00002  * Copyright (c) 2018 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 #include "hal/LowPowerTickerWrapper.h"
00018 #include "platform/Callback.h"
00019 
00020 LowPowerTickerWrapper::LowPowerTickerWrapper(const ticker_data_t *data, const ticker_interface_t *interface, uint32_t min_cycles_between_writes, uint32_t min_cycles_until_match)
00021     : _intf(data->interface), _min_count_between_writes(min_cycles_between_writes + 1), _min_count_until_match(min_cycles_until_match + 1), _suspended(false)
00022 {
00023     core_util_critical_section_enter();
00024 
00025     this->data.interface = interface;
00026     this->data.queue = data->queue;
00027     _reset();
00028 
00029     core_util_critical_section_exit();
00030 }
00031 
00032 void LowPowerTickerWrapper::irq_handler(ticker_irq_handler_type handler)
00033 {
00034     core_util_critical_section_enter();
00035 
00036     // This code no longer filters out early interrupts. Instead it
00037     // passes them through to the next layer and ignores further interrupts
00038     // until the next call to set_interrrupt or fire_interrupt (when not suspended).
00039     // This is to ensure that the device doesn't get stuck in sleep due to an
00040     // early low power ticker interrupt that was ignored.
00041     if (_pending_fire_now || _pending_match || _suspended) {
00042         _timeout.detach();
00043         _pending_timeout = false;
00044         _pending_match = false;
00045         _pending_fire_now = false;
00046         if (handler) {
00047             handler(&data);
00048         }
00049     } else {
00050         // Spurious interrupt
00051         _intf->clear_interrupt();
00052     }
00053 
00054     core_util_critical_section_exit();
00055 }
00056 
00057 void LowPowerTickerWrapper::suspend()
00058 {
00059     core_util_critical_section_enter();
00060 
00061     // Wait until rescheduling is allowed
00062     while (!_set_interrupt_allowed) {
00063         timestamp_t current = _intf->read();
00064         if (((current - _last_actual_set_interrupt) & _mask) >= _min_count_between_writes) {
00065             _set_interrupt_allowed  = true;
00066         }
00067     }
00068 
00069     _reset();
00070     _suspended = true;
00071 
00072     core_util_critical_section_exit();
00073 }
00074 
00075 void LowPowerTickerWrapper::resume()
00076 {
00077     core_util_critical_section_enter();
00078 
00079     // Wait until rescheduling is allowed
00080     while (!_set_interrupt_allowed) {
00081         timestamp_t current = _intf->read();
00082         if (((current - _last_actual_set_interrupt) & _mask) >= _min_count_between_writes) {
00083             _set_interrupt_allowed  = true;
00084         }
00085     }
00086 
00087     _suspended = false;
00088 
00089     core_util_critical_section_exit();
00090 }
00091 
00092 bool LowPowerTickerWrapper::timeout_pending()
00093 {
00094     core_util_critical_section_enter();
00095 
00096     bool pending = _pending_timeout;
00097 
00098     core_util_critical_section_exit();
00099     return pending;
00100 }
00101 
00102 void LowPowerTickerWrapper::init()
00103 {
00104     core_util_critical_section_enter();
00105 
00106     _reset();
00107     _intf->init();
00108 
00109     core_util_critical_section_exit();
00110 }
00111 
00112 void LowPowerTickerWrapper::free()
00113 {
00114     core_util_critical_section_enter();
00115 
00116     _reset();
00117     _intf->free();
00118 
00119     core_util_critical_section_exit();
00120 }
00121 
00122 uint32_t LowPowerTickerWrapper::read()
00123 {
00124     core_util_critical_section_enter();
00125 
00126     timestamp_t current = _intf->read();
00127     if (!_suspended && _match_check(current)) {
00128         _intf->fire_interrupt();
00129     }
00130 
00131     core_util_critical_section_exit();
00132     return current;
00133 }
00134 
00135 void LowPowerTickerWrapper::set_interrupt(timestamp_t timestamp)
00136 {
00137     core_util_critical_section_enter();
00138 
00139     _last_set_interrupt = _intf->read();
00140     _cur_match_time = timestamp;
00141     _pending_match = true;
00142     if (!_suspended) {
00143         _schedule_match(_last_set_interrupt);
00144     } else {
00145         _intf->set_interrupt(timestamp);
00146         _last_actual_set_interrupt = _last_set_interrupt;
00147         _set_interrupt_allowed = false;
00148     }
00149 
00150     core_util_critical_section_exit();
00151 }
00152 
00153 void LowPowerTickerWrapper::disable_interrupt()
00154 {
00155     core_util_critical_section_enter();
00156 
00157     _intf->disable_interrupt();
00158 
00159     core_util_critical_section_exit();
00160 }
00161 
00162 void LowPowerTickerWrapper::clear_interrupt()
00163 {
00164     core_util_critical_section_enter();
00165 
00166     _intf->clear_interrupt();
00167 
00168     core_util_critical_section_exit();
00169 }
00170 
00171 void LowPowerTickerWrapper::fire_interrupt()
00172 {
00173     core_util_critical_section_enter();
00174 
00175     _pending_fire_now = 1;
00176     _intf->fire_interrupt();
00177 
00178     core_util_critical_section_exit();
00179 }
00180 
00181 const ticker_info_t *LowPowerTickerWrapper::get_info()
00182 {
00183 
00184     core_util_critical_section_enter();
00185 
00186     const ticker_info_t *info = _intf->get_info();
00187 
00188     core_util_critical_section_exit();
00189     return info;
00190 }
00191 
00192 void LowPowerTickerWrapper::_reset()
00193 {
00194     MBED_ASSERT(core_util_in_critical_section());
00195 
00196     _timeout.detach();
00197     _pending_timeout = false;
00198     _pending_match = false;
00199     _pending_fire_now = false;
00200     _set_interrupt_allowed = true;
00201     _cur_match_time = 0;
00202     _last_set_interrupt = 0;
00203     _last_actual_set_interrupt = 0;
00204 
00205     const ticker_info_t *info = _intf->get_info();
00206     if (info->bits >= 32) {
00207         _mask = 0xffffffff;
00208     } else {
00209         _mask = ((uint64_t)1 << info->bits) - 1;
00210     }
00211 
00212     // Round us_per_tick up
00213     _us_per_tick = (1000000 + info->frequency - 1) / info->frequency;
00214 }
00215 
00216 void LowPowerTickerWrapper::_timeout_handler()
00217 {
00218     core_util_critical_section_enter();
00219     _pending_timeout = false;
00220 
00221     timestamp_t current = _intf->read();
00222     /* Add extra check for '_last_set_interrupt == _cur_match_time'
00223      *
00224      * When '_last_set_interrupt == _cur_match_time', _ticker_match_interval_passed sees it as
00225      * one-round interval rather than just-pass, so add extra check for it. In rare cases, we
00226      * may trap in _timeout_handler/_schedule_match loop. This check can break it.
00227      */
00228     if ((_last_set_interrupt == _cur_match_time) ||
00229             _ticker_match_interval_passed(_last_set_interrupt, current, _cur_match_time)) {
00230         _intf->fire_interrupt();
00231     } else {
00232         _schedule_match(current);
00233     }
00234 
00235     core_util_critical_section_exit();
00236 }
00237 
00238 bool LowPowerTickerWrapper::_match_check(timestamp_t current)
00239 {
00240     MBED_ASSERT(core_util_in_critical_section());
00241 
00242     if (!_pending_match) {
00243         return false;
00244     }
00245     /* Add extra check for '_last_set_interrupt == _cur_match_time' as above */
00246     return (_last_set_interrupt == _cur_match_time) ||
00247            _ticker_match_interval_passed(_last_set_interrupt, current, _cur_match_time);
00248 }
00249 
00250 uint32_t LowPowerTickerWrapper::_lp_ticks_to_us(uint32_t ticks)
00251 {
00252     MBED_ASSERT(core_util_in_critical_section());
00253 
00254     // Add 4 microseconds to round up the micro second ticker time (which has a frequency of at least 250KHz - 4us period)
00255     return _us_per_tick * ticks + 4;
00256 }
00257 
00258 void LowPowerTickerWrapper::_schedule_match(timestamp_t current)
00259 {
00260     MBED_ASSERT(core_util_in_critical_section());
00261 
00262     // Check if _intf->set_interrupt is allowed
00263     if (!_set_interrupt_allowed) {
00264         if (((current - _last_actual_set_interrupt) & _mask) >= _min_count_between_writes) {
00265             _set_interrupt_allowed  = true;
00266         }
00267     }
00268 
00269     uint32_t cycles_until_match = (_cur_match_time - current) & _mask;
00270     bool too_close = cycles_until_match < _min_count_until_match;
00271 
00272     if (!_set_interrupt_allowed) {
00273 
00274         // Can't use _intf->set_interrupt so use microsecond Timeout instead
00275 
00276         // Speed optimization - if a timer has already been scheduled
00277         // then don't schedule it again.
00278         if (!_pending_timeout) {
00279             uint32_t ticks = cycles_until_match < _min_count_until_match ? cycles_until_match : _min_count_until_match;
00280             _timeout.attach_us(mbed::callback(this, &LowPowerTickerWrapper::_timeout_handler), _lp_ticks_to_us(ticks));
00281             _pending_timeout = true;
00282         }
00283         return;
00284     }
00285 
00286     if (!too_close) {
00287 
00288         // Schedule LP ticker
00289         _intf->set_interrupt(_cur_match_time);
00290         current = _intf->read();
00291         _last_actual_set_interrupt = current;
00292         _set_interrupt_allowed = false;
00293 
00294         // Check for overflow
00295         uint32_t new_cycles_until_match = (_cur_match_time - current) & _mask;
00296         if (new_cycles_until_match > cycles_until_match) {
00297             // Overflow so fire now
00298             _intf->fire_interrupt();
00299             return;
00300         }
00301 
00302         // Update variables with new time
00303         cycles_until_match = new_cycles_until_match;
00304         too_close = cycles_until_match < _min_count_until_match;
00305     }
00306 
00307     if (too_close) {
00308 
00309         // Low power ticker incremented to less than _min_count_until_match
00310         // so low power ticker may not fire. Use Timeout to ensure it does fire.
00311         uint32_t ticks = cycles_until_match < _min_count_until_match ? cycles_until_match : _min_count_until_match;
00312         _timeout.attach_us(mbed::callback(this, &LowPowerTickerWrapper::_timeout_handler), _lp_ticks_to_us(ticks));
00313         _pending_timeout = true;
00314         return;
00315     }
00316 }