takashi kadono / Mbed OS Nucleo_446

Dependencies:   ssd1331

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 (_suspended) {
00036         if (handler) {
00037             handler(&data);
00038         }
00039         core_util_critical_section_exit();
00040         return;
00041     }
00042 
00043     if (_pending_fire_now || _match_check(_intf->read())) {
00044         _timeout.detach();
00045         _pending_timeout = false;
00046         _pending_match = false;
00047         _pending_fire_now = false;
00048         if (handler) {
00049             handler(&data);
00050         }
00051     } else {
00052         // Spurious interrupt
00053         _intf->clear_interrupt();
00054     }
00055 
00056     core_util_critical_section_exit();
00057 }
00058 
00059 void LowPowerTickerWrapper::suspend()
00060 {
00061     core_util_critical_section_enter();
00062 
00063     // Wait until rescheduling is allowed
00064     while (!_set_interrupt_allowed) {
00065         timestamp_t current = _intf->read();
00066         if (((current - _last_actual_set_interrupt) & _mask) >= _min_count_between_writes) {
00067             _set_interrupt_allowed  = true;
00068         }
00069     }
00070 
00071     _reset();
00072     _suspended = true;
00073 
00074     core_util_critical_section_exit();
00075 }
00076 
00077 void LowPowerTickerWrapper::resume()
00078 {
00079     core_util_critical_section_enter();
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 (_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     _schedule_match(_last_set_interrupt);
00137 
00138     core_util_critical_section_exit();
00139 }
00140 
00141 void LowPowerTickerWrapper::disable_interrupt()
00142 {
00143     core_util_critical_section_enter();
00144 
00145     _intf->disable_interrupt();
00146 
00147     core_util_critical_section_exit();
00148 }
00149 
00150 void LowPowerTickerWrapper::clear_interrupt()
00151 {
00152     core_util_critical_section_enter();
00153 
00154     _intf->clear_interrupt();
00155 
00156     core_util_critical_section_exit();
00157 }
00158 
00159 void LowPowerTickerWrapper::fire_interrupt()
00160 {
00161     core_util_critical_section_enter();
00162 
00163     _pending_fire_now = 1;
00164     _intf->fire_interrupt();
00165 
00166     core_util_critical_section_exit();
00167 }
00168 
00169 const ticker_info_t *LowPowerTickerWrapper::get_info()
00170 {
00171 
00172     core_util_critical_section_enter();
00173 
00174     const ticker_info_t *info = _intf->get_info();
00175 
00176     core_util_critical_section_exit();
00177     return info;
00178 }
00179 
00180 void LowPowerTickerWrapper::_reset()
00181 {
00182     MBED_ASSERT(core_util_in_critical_section());
00183 
00184     _timeout.detach();
00185     _pending_timeout = false;
00186     _pending_match = false;
00187     _pending_fire_now = false;
00188     _set_interrupt_allowed = true;
00189     _cur_match_time = 0;
00190     _last_set_interrupt = 0;
00191     _last_actual_set_interrupt = 0;
00192 
00193     const ticker_info_t *info = _intf->get_info();
00194     if (info->bits >= 32) {
00195         _mask = 0xffffffff;
00196     } else {
00197         _mask = ((uint64_t)1 << info->bits) - 1;
00198     }
00199 
00200     // Round us_per_tick up
00201     _us_per_tick = (1000000 + info->frequency - 1) / info->frequency;
00202 }
00203 
00204 void LowPowerTickerWrapper::_timeout_handler()
00205 {
00206     core_util_critical_section_enter();
00207     _pending_timeout = false;
00208 
00209     timestamp_t current = _intf->read();
00210     /* Add extra check for '_last_set_interrupt == _cur_match_time'
00211      * 
00212      * When '_last_set_interrupt == _cur_match_time', _ticker_match_interval_passed sees it as
00213      * one-round interval rather than just-pass, so add extra check for it. In rare cases, we
00214      * may trap in _timeout_handler/_schedule_match loop. This check can break it.
00215      */
00216     if ((_last_set_interrupt == _cur_match_time) ||
00217         _ticker_match_interval_passed(_last_set_interrupt, current, _cur_match_time)) {
00218         _intf->fire_interrupt();
00219     } else {
00220         _schedule_match(current);
00221     }
00222 
00223     core_util_critical_section_exit();
00224 }
00225 
00226 bool LowPowerTickerWrapper::_match_check(timestamp_t current)
00227 {
00228     MBED_ASSERT(core_util_in_critical_section());
00229 
00230     if (!_pending_match) {
00231         return false;
00232     }
00233     /* Add extra check for '_last_set_interrupt == _cur_match_time' as above */
00234     return (_last_set_interrupt == _cur_match_time) ||
00235         _ticker_match_interval_passed(_last_set_interrupt, current, _cur_match_time);
00236 }
00237 
00238 uint32_t LowPowerTickerWrapper::_lp_ticks_to_us(uint32_t ticks)
00239 {
00240     MBED_ASSERT(core_util_in_critical_section());
00241 
00242     // Add 4 microseconds to round up the micro second ticker time (which has a frequency of at least 250KHz - 4us period)
00243     return _us_per_tick * ticks + 4;
00244 }
00245 
00246 void LowPowerTickerWrapper::_schedule_match(timestamp_t current)
00247 {
00248     MBED_ASSERT(core_util_in_critical_section());
00249 
00250     // Check if _intf->set_interrupt is allowed
00251     if (!_set_interrupt_allowed) {
00252         if (((current - _last_actual_set_interrupt) & _mask) >= _min_count_between_writes) {
00253             _set_interrupt_allowed  = true;
00254         }
00255     }
00256 
00257     uint32_t cycles_until_match = (_cur_match_time - current) & _mask;
00258     bool too_close = cycles_until_match < _min_count_until_match;
00259 
00260     if (!_set_interrupt_allowed) {
00261 
00262         // Can't use _intf->set_interrupt so use microsecond Timeout instead
00263 
00264         // Speed optimization - if a timer has already been scheduled
00265         // then don't schedule it again.
00266         if (!_pending_timeout) {
00267             uint32_t ticks = cycles_until_match < _min_count_until_match ? cycles_until_match : _min_count_until_match;
00268             _timeout.attach_us(mbed::callback(this, &LowPowerTickerWrapper::_timeout_handler), _lp_ticks_to_us(ticks));
00269             _pending_timeout = true;
00270         }
00271         return;
00272     }
00273 
00274     if (!too_close) {
00275 
00276         // Schedule LP ticker
00277         _intf->set_interrupt(_cur_match_time);
00278         current = _intf->read();
00279         _last_actual_set_interrupt = current;
00280         _set_interrupt_allowed  = false;
00281 
00282         // Check for overflow
00283         uint32_t new_cycles_until_match = (_cur_match_time - current) & _mask;
00284         if (new_cycles_until_match > cycles_until_match) {
00285             // Overflow so fire now
00286             _intf->fire_interrupt();
00287             return;
00288         }
00289 
00290         // Update variables with new time
00291         cycles_until_match = new_cycles_until_match;
00292         too_close = cycles_until_match < _min_count_until_match;
00293     }
00294 
00295     if (too_close) {
00296 
00297         // Low power ticker incremented to less than _min_count_until_match
00298         // so low power ticker may not fire. Use Timeout to ensure it does fire.
00299         uint32_t ticks = cycles_until_match < _min_count_until_match ? cycles_until_match : _min_count_until_match;
00300         _timeout.attach_us(mbed::callback(this, &LowPowerTickerWrapper::_timeout_handler), _lp_ticks_to_us(ticks));
00301         _pending_timeout = true;
00302         return;
00303     }
00304 }