RTC auf true
Diff: hal/LowPowerTickerWrapper.cpp
- Revision:
- 0:38ceb79fef03
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hal/LowPowerTickerWrapper.cpp Wed Nov 28 15:10:15 2018 +0000 @@ -0,0 +1,310 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "hal/LowPowerTickerWrapper.h" +#include "platform/Callback.h" + +LowPowerTickerWrapper::LowPowerTickerWrapper(const ticker_data_t *data, const ticker_interface_t *interface, uint32_t min_cycles_between_writes, uint32_t min_cycles_until_match) + : _intf(data->interface), _min_count_between_writes(min_cycles_between_writes + 1), _min_count_until_match(min_cycles_until_match + 1), _suspended(false) +{ + core_util_critical_section_enter(); + + this->data.interface = interface; + this->data.queue = data->queue; + _reset(); + + core_util_critical_section_exit(); +} + +void LowPowerTickerWrapper::irq_handler(ticker_irq_handler_type handler) +{ + core_util_critical_section_enter(); + + if (_pending_fire_now || _match_check(_intf->read()) || _suspended) { + _timeout.detach(); + _pending_timeout = false; + _pending_match = false; + _pending_fire_now = false; + if (handler) { + handler(&data); + } + } else { + // Spurious interrupt + _intf->clear_interrupt(); + } + + core_util_critical_section_exit(); +} + +void LowPowerTickerWrapper::suspend() +{ + core_util_critical_section_enter(); + + // Wait until rescheduling is allowed + while (!_set_interrupt_allowed) { + timestamp_t current = _intf->read(); + if (((current - _last_actual_set_interrupt) & _mask) >= _min_count_between_writes) { + _set_interrupt_allowed = true; + } + } + + _reset(); + _suspended = true; + + core_util_critical_section_exit(); +} + +void LowPowerTickerWrapper::resume() +{ + core_util_critical_section_enter(); + + // Wait until rescheduling is allowed + while (!_set_interrupt_allowed) { + timestamp_t current = _intf->read(); + if (((current - _last_actual_set_interrupt) & _mask) >= _min_count_between_writes) { + _set_interrupt_allowed = true; + } + } + + _suspended = false; + + core_util_critical_section_exit(); +} + +bool LowPowerTickerWrapper::timeout_pending() +{ + core_util_critical_section_enter(); + + bool pending = _pending_timeout; + + core_util_critical_section_exit(); + return pending; +} + +void LowPowerTickerWrapper::init() +{ + core_util_critical_section_enter(); + + _reset(); + _intf->init(); + + core_util_critical_section_exit(); +} + +void LowPowerTickerWrapper::free() +{ + core_util_critical_section_enter(); + + _reset(); + _intf->free(); + + core_util_critical_section_exit(); +} + +uint32_t LowPowerTickerWrapper::read() +{ + core_util_critical_section_enter(); + + timestamp_t current = _intf->read(); + if (!_suspended && _match_check(current)) { + _intf->fire_interrupt(); + } + + core_util_critical_section_exit(); + return current; +} + +void LowPowerTickerWrapper::set_interrupt(timestamp_t timestamp) +{ + core_util_critical_section_enter(); + + _last_set_interrupt = _intf->read(); + _cur_match_time = timestamp; + _pending_match = true; + if (!_suspended) { + _schedule_match(_last_set_interrupt); + } else { + _intf->set_interrupt(timestamp); + _last_actual_set_interrupt = _last_set_interrupt; + _set_interrupt_allowed = false; + } + + core_util_critical_section_exit(); +} + +void LowPowerTickerWrapper::disable_interrupt() +{ + core_util_critical_section_enter(); + + _intf->disable_interrupt(); + + core_util_critical_section_exit(); +} + +void LowPowerTickerWrapper::clear_interrupt() +{ + core_util_critical_section_enter(); + + _intf->clear_interrupt(); + + core_util_critical_section_exit(); +} + +void LowPowerTickerWrapper::fire_interrupt() +{ + core_util_critical_section_enter(); + + _pending_fire_now = 1; + _intf->fire_interrupt(); + + core_util_critical_section_exit(); +} + +const ticker_info_t *LowPowerTickerWrapper::get_info() +{ + + core_util_critical_section_enter(); + + const ticker_info_t *info = _intf->get_info(); + + core_util_critical_section_exit(); + return info; +} + +void LowPowerTickerWrapper::_reset() +{ + MBED_ASSERT(core_util_in_critical_section()); + + _timeout.detach(); + _pending_timeout = false; + _pending_match = false; + _pending_fire_now = false; + _set_interrupt_allowed = true; + _cur_match_time = 0; + _last_set_interrupt = 0; + _last_actual_set_interrupt = 0; + + const ticker_info_t *info = _intf->get_info(); + if (info->bits >= 32) { + _mask = 0xffffffff; + } else { + _mask = ((uint64_t)1 << info->bits) - 1; + } + + // Round us_per_tick up + _us_per_tick = (1000000 + info->frequency - 1) / info->frequency; +} + +void LowPowerTickerWrapper::_timeout_handler() +{ + core_util_critical_section_enter(); + _pending_timeout = false; + + timestamp_t current = _intf->read(); + /* Add extra check for '_last_set_interrupt == _cur_match_time' + * + * When '_last_set_interrupt == _cur_match_time', _ticker_match_interval_passed sees it as + * one-round interval rather than just-pass, so add extra check for it. In rare cases, we + * may trap in _timeout_handler/_schedule_match loop. This check can break it. + */ + if ((_last_set_interrupt == _cur_match_time) || + _ticker_match_interval_passed(_last_set_interrupt, current, _cur_match_time)) { + _intf->fire_interrupt(); + } else { + _schedule_match(current); + } + + core_util_critical_section_exit(); +} + +bool LowPowerTickerWrapper::_match_check(timestamp_t current) +{ + MBED_ASSERT(core_util_in_critical_section()); + + if (!_pending_match) { + return false; + } + /* Add extra check for '_last_set_interrupt == _cur_match_time' as above */ + return (_last_set_interrupt == _cur_match_time) || + _ticker_match_interval_passed(_last_set_interrupt, current, _cur_match_time); +} + +uint32_t LowPowerTickerWrapper::_lp_ticks_to_us(uint32_t ticks) +{ + MBED_ASSERT(core_util_in_critical_section()); + + // Add 4 microseconds to round up the micro second ticker time (which has a frequency of at least 250KHz - 4us period) + return _us_per_tick * ticks + 4; +} + +void LowPowerTickerWrapper::_schedule_match(timestamp_t current) +{ + MBED_ASSERT(core_util_in_critical_section()); + + // Check if _intf->set_interrupt is allowed + if (!_set_interrupt_allowed) { + if (((current - _last_actual_set_interrupt) & _mask) >= _min_count_between_writes) { + _set_interrupt_allowed = true; + } + } + + uint32_t cycles_until_match = (_cur_match_time - current) & _mask; + bool too_close = cycles_until_match < _min_count_until_match; + + if (!_set_interrupt_allowed) { + + // Can't use _intf->set_interrupt so use microsecond Timeout instead + + // Speed optimization - if a timer has already been scheduled + // then don't schedule it again. + if (!_pending_timeout) { + uint32_t ticks = cycles_until_match < _min_count_until_match ? cycles_until_match : _min_count_until_match; + _timeout.attach_us(mbed::callback(this, &LowPowerTickerWrapper::_timeout_handler), _lp_ticks_to_us(ticks)); + _pending_timeout = true; + } + return; + } + + if (!too_close) { + + // Schedule LP ticker + _intf->set_interrupt(_cur_match_time); + current = _intf->read(); + _last_actual_set_interrupt = current; + _set_interrupt_allowed = false; + + // Check for overflow + uint32_t new_cycles_until_match = (_cur_match_time - current) & _mask; + if (new_cycles_until_match > cycles_until_match) { + // Overflow so fire now + _intf->fire_interrupt(); + return; + } + + // Update variables with new time + cycles_until_match = new_cycles_until_match; + too_close = cycles_until_match < _min_count_until_match; + } + + if (too_close) { + + // Low power ticker incremented to less than _min_count_until_match + // so low power ticker may not fire. Use Timeout to ensure it does fire. + uint32_t ticks = cycles_until_match < _min_count_until_match ? cycles_until_match : _min_count_until_match; + _timeout.attach_us(mbed::callback(this, &LowPowerTickerWrapper::_timeout_handler), _lp_ticks_to_us(ticks)); + _pending_timeout = true; + return; + } +}