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.
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 }
Generated on Tue Aug 9 2022 00:37:10 by
1.7.2