mbed library sources. Supersedes mbed-src.
Dependents: Nucleo_Hello_Encoder BLE_iBeaconScan AM1805_DEMO DISCO-F429ZI_ExportTemplate1 ... more
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 }
Generated on Tue Jul 12 2022 20:41:15 by 1.7.2