Kenji Arai / TYBLE16_mbedlized_os5_several_examples_1st

Dependencies:   nRF51_Vdd TextLCD BME280

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