mirror of https://github.com/ARMmbed/mbed-os.git
Merge pull request #6536 from c1728p9/sync_lp_ticker_support
Add handling for synchronized low power tickerspull/6677/head
commit
06eefcb45a
|
@ -17,6 +17,8 @@
|
|||
|
||||
#if DEVICE_LOWPOWERTIMER
|
||||
|
||||
void lp_ticker_set_interrupt_wrapper(timestamp_t timestamp);
|
||||
|
||||
static ticker_event_queue_t events = { 0 };
|
||||
|
||||
static ticker_irq_handler_type irq_handler = ticker_irq_handler;
|
||||
|
@ -26,7 +28,11 @@ static const ticker_interface_t lp_interface = {
|
|||
.read = lp_ticker_read,
|
||||
.disable_interrupt = lp_ticker_disable_interrupt,
|
||||
.clear_interrupt = lp_ticker_clear_interrupt,
|
||||
#if LOWPOWERTIMER_DELAY_TICKS > 0
|
||||
.set_interrupt = lp_ticker_set_interrupt_wrapper,
|
||||
#else
|
||||
.set_interrupt = lp_ticker_set_interrupt,
|
||||
#endif
|
||||
.fire_interrupt = lp_ticker_fire_interrupt,
|
||||
.get_info = lp_ticker_get_info,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,156 @@
|
|||
/* 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/lp_ticker_api.h"
|
||||
|
||||
#if DEVICE_LOWPOWERTIMER && (LOWPOWERTIMER_DELAY_TICKS > 0)
|
||||
|
||||
#include "Timeout.h"
|
||||
#include "mbed_critical.h"
|
||||
|
||||
static const timestamp_t min_delta = LOWPOWERTIMER_DELAY_TICKS;
|
||||
|
||||
static bool init = false;
|
||||
static bool pending = false;
|
||||
static bool timeout_pending = false;
|
||||
static timestamp_t last_set_interrupt = 0;
|
||||
static timestamp_t last_request = 0;
|
||||
static timestamp_t next = 0;
|
||||
|
||||
static timestamp_t mask;
|
||||
static timestamp_t reschedule_us;
|
||||
|
||||
// Do not use SingletonPtr since this must be initialized in a critical section
|
||||
static mbed::Timeout *timeout;
|
||||
static uint64_t timeout_data[sizeof(mbed::Timeout) / 8];
|
||||
|
||||
/**
|
||||
* Initialize variables
|
||||
*/
|
||||
static void init_local()
|
||||
{
|
||||
MBED_ASSERT(core_util_in_critical_section());
|
||||
|
||||
const ticker_info_t* info = lp_ticker_get_info();
|
||||
if (info->bits >= 32) {
|
||||
mask = 0xffffffff;
|
||||
} else {
|
||||
mask = ((uint64_t)1 << info->bits) - 1;
|
||||
}
|
||||
|
||||
// Round us_per_tick up
|
||||
timestamp_t us_per_tick = (1000000 + info->frequency - 1) / info->frequency;
|
||||
|
||||
// Add 1 tick to the min delta for the case where the clock transitions after you read it
|
||||
// Add 4 microseconds to round up the micro second ticker time (which has a frequency of at least 250KHz - 4us period)
|
||||
reschedule_us = (min_delta + 1) * us_per_tick + 4;
|
||||
|
||||
timeout = new (timeout_data) mbed::Timeout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Call lp_ticker_set_interrupt with a value that is guaranteed to fire
|
||||
*
|
||||
* Assumptions
|
||||
* -Only one low power clock tick can pass from the last read (last_read)
|
||||
* -The closest an interrupt can fire is max_delta + 1
|
||||
*
|
||||
* @param last_read The last value read from lp_ticker_read
|
||||
* @param timestamp The timestamp to trigger the interrupt at
|
||||
*/
|
||||
static void set_interrupt_safe(timestamp_t last_read, timestamp_t timestamp)
|
||||
{
|
||||
MBED_ASSERT(core_util_in_critical_section());
|
||||
uint32_t delta = (timestamp - last_read) & mask;
|
||||
if (delta < min_delta + 2) {
|
||||
timestamp = (last_read + min_delta + 2) & mask;
|
||||
}
|
||||
lp_ticker_set_interrupt(timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the low power ticker match time when hardware is ready
|
||||
*
|
||||
* This event is scheduled to set the lp timer after the previous write
|
||||
* has taken effect and it is safe to write a new value without blocking.
|
||||
* If the time has already passed then this function fires and interrupt
|
||||
* immediately.
|
||||
*/
|
||||
static void set_interrupt_later()
|
||||
{
|
||||
core_util_critical_section_enter();
|
||||
|
||||
timestamp_t current = lp_ticker_read();
|
||||
if (_ticker_match_interval_passed(last_request, current, next)) {
|
||||
lp_ticker_fire_interrupt();
|
||||
} else {
|
||||
set_interrupt_safe(current, next);
|
||||
last_set_interrupt = lp_ticker_read();
|
||||
}
|
||||
timeout_pending = false;
|
||||
|
||||
core_util_critical_section_exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around lp_ticker_set_interrupt to prevent blocking
|
||||
*
|
||||
* Problems this function is solving:
|
||||
* 1. Interrupt may not fire if set earlier than LOWPOWERTIMER_DELAY_TICKS low power clock cycles
|
||||
* 2. Setting the interrupt back-to-back will block
|
||||
*
|
||||
* This wrapper function prevents lp_ticker_set_interrupt from being called
|
||||
* back-to-back and blocking while the first write is in progress. This function
|
||||
* avoids that problem by scheduling a timeout event if the lp ticker is in the
|
||||
* middle of a write operation.
|
||||
*
|
||||
* @param timestamp Time to call ticker irq
|
||||
* @note this is a utility function and it's not required part of HAL implementation
|
||||
*/
|
||||
extern "C" void lp_ticker_set_interrupt_wrapper(timestamp_t timestamp)
|
||||
{
|
||||
core_util_critical_section_enter();
|
||||
|
||||
if (!init) {
|
||||
init_local();
|
||||
init = true;
|
||||
}
|
||||
|
||||
timestamp_t current = lp_ticker_read();
|
||||
if (pending) {
|
||||
// Check if pending should be cleared
|
||||
if (((current - last_set_interrupt) & mask) >= min_delta) {
|
||||
pending = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (pending || timeout_pending) {
|
||||
next = timestamp;
|
||||
last_request = current;
|
||||
if (!timeout_pending) {
|
||||
timeout->attach_us(set_interrupt_later, reschedule_us);
|
||||
timeout_pending = true;
|
||||
}
|
||||
} else {
|
||||
// Schedule immediately if nothing is pending
|
||||
set_interrupt_safe(current, timestamp);
|
||||
last_set_interrupt = lp_ticker_read();
|
||||
pending = true;
|
||||
}
|
||||
|
||||
core_util_critical_section_exit();
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue