mirror of https://github.com/ARMmbed/mbed-os.git
Merge pull request #7524 from c1728p9/tickless_fix
Fixes for tickless and LPTICKER_DELAY_TICKSpull/7843/head
commit
e02466a77a
|
@ -29,90 +29,90 @@ using namespace utest::v1;
|
|||
|
||||
void deep_sleep_lock_lock_test()
|
||||
{
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep());
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check());
|
||||
{
|
||||
// Check basic usage works
|
||||
DeepSleepLock lock;
|
||||
TEST_ASSERT_EQUAL(false, sleep_manager_can_deep_sleep());
|
||||
TEST_ASSERT_EQUAL(false, sleep_manager_can_deep_sleep_test_check());
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep());
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check());
|
||||
{
|
||||
// Check that unlock and lock change can deep sleep as expected
|
||||
DeepSleepLock lock;
|
||||
TEST_ASSERT_EQUAL(false, sleep_manager_can_deep_sleep());
|
||||
TEST_ASSERT_EQUAL(false, sleep_manager_can_deep_sleep_test_check());
|
||||
lock.unlock();
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep());
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check());
|
||||
lock.lock();
|
||||
TEST_ASSERT_EQUAL(false, sleep_manager_can_deep_sleep());
|
||||
TEST_ASSERT_EQUAL(false, sleep_manager_can_deep_sleep_test_check());
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep());
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check());
|
||||
{
|
||||
// Check that unlock releases sleep based on count
|
||||
DeepSleepLock lock;
|
||||
lock.lock();
|
||||
lock.lock();
|
||||
lock.unlock();
|
||||
TEST_ASSERT_EQUAL(false, sleep_manager_can_deep_sleep());
|
||||
TEST_ASSERT_EQUAL(false, sleep_manager_can_deep_sleep_test_check());
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep());
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check());
|
||||
{
|
||||
// Check that unbalanced locks do not leave deep sleep locked
|
||||
DeepSleepLock lock;
|
||||
lock.lock();
|
||||
TEST_ASSERT_EQUAL(false, sleep_manager_can_deep_sleep());
|
||||
TEST_ASSERT_EQUAL(false, sleep_manager_can_deep_sleep_test_check());
|
||||
}
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep());
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check());
|
||||
|
||||
}
|
||||
|
||||
void timer_lock_test()
|
||||
{
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep());
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check());
|
||||
{
|
||||
// Just creating a timer object does not lock sleep
|
||||
Timer timer;
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep());
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check());
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep());
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check());
|
||||
{
|
||||
// Starting a timer does lock sleep
|
||||
Timer timer;
|
||||
timer.start();
|
||||
TEST_ASSERT_EQUAL(false, sleep_manager_can_deep_sleep());
|
||||
TEST_ASSERT_EQUAL(false, sleep_manager_can_deep_sleep_test_check());
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep());
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check());
|
||||
{
|
||||
// Stopping a timer after starting it allows sleep
|
||||
Timer timer;
|
||||
timer.start();
|
||||
timer.stop();
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep());
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check());
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep());
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check());
|
||||
{
|
||||
// Starting a timer multiple times still lets you sleep
|
||||
Timer timer;
|
||||
timer.start();
|
||||
timer.start();
|
||||
}
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep());
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check());
|
||||
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep());
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check());
|
||||
{
|
||||
// Stopping a timer multiple times still lets you sleep
|
||||
Timer timer;
|
||||
timer.start();
|
||||
timer.stop();
|
||||
timer.stop();
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep());
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check());
|
||||
}
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep());
|
||||
TEST_ASSERT_EQUAL(true, sleep_manager_can_deep_sleep_test_check());
|
||||
}
|
||||
|
||||
Case cases[] = {
|
||||
|
|
|
@ -263,7 +263,7 @@ void test_sleep(void)
|
|||
timer.start();
|
||||
timeout.attach_callback(mbed::callback(sem_callback, &sem), delay_us);
|
||||
|
||||
bool deep_sleep_allowed = sleep_manager_can_deep_sleep();
|
||||
bool deep_sleep_allowed = sleep_manager_can_deep_sleep_test_check();
|
||||
TEST_ASSERT_FALSE_MESSAGE(deep_sleep_allowed, "Deep sleep should be disallowed");
|
||||
while (sem.wait(0) != 1) {
|
||||
sleep();
|
||||
|
@ -322,7 +322,7 @@ void test_deepsleep(void)
|
|||
timer.start();
|
||||
timeout.attach_callback(mbed::callback(sem_callback, &sem), delay_us);
|
||||
|
||||
bool deep_sleep_allowed = sleep_manager_can_deep_sleep();
|
||||
bool deep_sleep_allowed = sleep_manager_can_deep_sleep_test_check();
|
||||
TEST_ASSERT_TRUE_MESSAGE(deep_sleep_allowed, "Deep sleep should be allowed");
|
||||
while (sem.wait(0) != 1) {
|
||||
sleep();
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "ticker_api_tests.h"
|
||||
#include "hal/us_ticker_api.h"
|
||||
#include "hal/lp_ticker_api.h"
|
||||
#include "hal/mbed_lp_ticker_wrapper.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -496,7 +497,15 @@ utest::v1::status_t us_ticker_setup(const Case *const source, const size_t index
|
|||
{
|
||||
intf = get_us_ticker_data()->interface;
|
||||
|
||||
OS_Tick_Disable();
|
||||
/* OS, common ticker and low power ticker wrapper
|
||||
* may make use of us ticker so suspend them for this test */
|
||||
osKernelSuspend();
|
||||
#if DEVICE_LPTICKER && (LPTICKER_DELAY_TICKS > 0)
|
||||
/* Suspend the lp ticker wrapper since it makes use of the us ticker */
|
||||
ticker_suspend(get_lp_ticker_data());
|
||||
lp_ticker_wrapper_suspend();
|
||||
#endif
|
||||
ticker_suspend(get_us_ticker_data());
|
||||
|
||||
intf->init();
|
||||
|
||||
|
@ -515,7 +524,12 @@ utest::v1::status_t us_ticker_teardown(const Case *const source, const size_t pa
|
|||
|
||||
prev_irq_handler = NULL;
|
||||
|
||||
OS_Tick_Enable();
|
||||
ticker_resume(get_us_ticker_data());
|
||||
#if DEVICE_LPTICKER && (LPTICKER_DELAY_TICKS > 0)
|
||||
lp_ticker_wrapper_resume();
|
||||
ticker_resume(get_lp_ticker_data());
|
||||
#endif
|
||||
osKernelResume(0);
|
||||
|
||||
return greentea_case_teardown_handler(source, passed, failed, reason);
|
||||
}
|
||||
|
@ -525,7 +539,9 @@ utest::v1::status_t lp_ticker_setup(const Case *const source, const size_t index
|
|||
{
|
||||
intf = get_lp_ticker_data()->interface;
|
||||
|
||||
OS_Tick_Disable();
|
||||
/* OS and common ticker may make use of lp ticker so suspend them for this test */
|
||||
osKernelSuspend();
|
||||
ticker_suspend(get_lp_ticker_data());
|
||||
|
||||
intf->init();
|
||||
|
||||
|
@ -544,7 +560,8 @@ utest::v1::status_t lp_ticker_teardown(const Case *const source, const size_t pa
|
|||
|
||||
prev_irq_handler = NULL;
|
||||
|
||||
OS_Tick_Enable();
|
||||
ticker_resume(get_lp_ticker_data());
|
||||
osKernelResume(0);
|
||||
|
||||
return greentea_case_teardown_handler(source, passed, failed, reason);
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "ticker_api_test_freq.h"
|
||||
#include "hal/us_ticker_api.h"
|
||||
#include "hal/lp_ticker_api.h"
|
||||
#include "hal/mbed_lp_ticker_wrapper.h"
|
||||
|
||||
#if !DEVICE_USTICKER
|
||||
#error [NOT_SUPPORTED] test not supported
|
||||
|
@ -38,25 +39,51 @@
|
|||
using namespace utest::v1;
|
||||
|
||||
const ticker_interface_t *intf;
|
||||
|
||||
static volatile unsigned int overflowCounter;
|
||||
uint32_t intf_mask;
|
||||
uint32_t intf_last_tick;
|
||||
uint32_t intf_elapsed_ticks;
|
||||
ticker_irq_handler_type prev_handler;
|
||||
|
||||
uint32_t ticks_to_us(uint32_t ticks, uint32_t freq)
|
||||
{
|
||||
return (uint32_t)((uint64_t)ticks * US_PER_S / freq);
|
||||
}
|
||||
|
||||
void elapsed_ticks_reset()
|
||||
{
|
||||
core_util_critical_section_enter();
|
||||
|
||||
const uint32_t ticker_bits = intf->get_info()->bits;
|
||||
|
||||
intf_mask = (1 << ticker_bits) - 1;
|
||||
intf_last_tick = intf->read();
|
||||
intf_elapsed_ticks = 0;
|
||||
|
||||
core_util_critical_section_exit();
|
||||
}
|
||||
|
||||
uint32_t elapsed_ticks_update()
|
||||
{
|
||||
core_util_critical_section_enter();
|
||||
|
||||
const uint32_t current_tick = intf->read();
|
||||
intf_elapsed_ticks += (current_tick - intf_last_tick) & intf_mask;
|
||||
intf_last_tick = current_tick;
|
||||
|
||||
/* Schedule next interrupt half way to overflow */
|
||||
uint32_t next = (current_tick + intf_mask / 2) & intf_mask;
|
||||
intf->set_interrupt(next);
|
||||
|
||||
uint32_t elapsed = intf_elapsed_ticks;
|
||||
|
||||
core_util_critical_section_exit();
|
||||
return elapsed;
|
||||
}
|
||||
|
||||
void ticker_event_handler_stub(const ticker_data_t *const ticker)
|
||||
{
|
||||
if (ticker == get_us_ticker_data()) {
|
||||
us_ticker_clear_interrupt();
|
||||
} else {
|
||||
#if DEVICE_LPTICKER
|
||||
lp_ticker_clear_interrupt();
|
||||
#endif
|
||||
}
|
||||
|
||||
overflowCounter++;
|
||||
intf->clear_interrupt();
|
||||
elapsed_ticks_update();
|
||||
}
|
||||
|
||||
/* Test that the ticker is operating at the frequency it specifies. */
|
||||
|
@ -66,13 +93,13 @@ void ticker_frequency_test()
|
|||
char _value[128] = { };
|
||||
int expected_key = 1;
|
||||
const uint32_t ticker_freq = intf->get_info()->frequency;
|
||||
const uint32_t ticker_bits = intf->get_info()->bits;
|
||||
const uint32_t ticker_max = (1 << ticker_bits) - 1;
|
||||
|
||||
intf->init();
|
||||
|
||||
elapsed_ticks_reset();
|
||||
|
||||
/* Detect overflow for tickers with lower counters width. */
|
||||
intf->set_interrupt(0);
|
||||
elapsed_ticks_update();
|
||||
|
||||
greentea_send_kv("timing_drift_check_start", 0);
|
||||
|
||||
|
@ -82,9 +109,7 @@ void ticker_frequency_test()
|
|||
expected_key = strcmp(_key, "base_time");
|
||||
} while (expected_key);
|
||||
|
||||
overflowCounter = 0;
|
||||
|
||||
const uint32_t begin_ticks = intf->read();
|
||||
const uint32_t begin_ticks = elapsed_ticks_update();
|
||||
|
||||
/* Assume that there was no overflow at this point - we are just after init. */
|
||||
greentea_send_kv(_key, ticks_to_us(begin_ticks, ticker_freq));
|
||||
|
@ -92,9 +117,9 @@ void ticker_frequency_test()
|
|||
/* Wait for 2nd signal from host. */
|
||||
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
|
||||
|
||||
const uint32_t end_ticks = intf->read();
|
||||
const uint32_t end_ticks = elapsed_ticks_update();
|
||||
|
||||
greentea_send_kv(_key, ticks_to_us(end_ticks + overflowCounter * ticker_max, ticker_freq));
|
||||
greentea_send_kv(_key, ticks_to_us(end_ticks, ticker_freq));
|
||||
|
||||
/* Get the results from host. */
|
||||
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
|
||||
|
@ -106,43 +131,74 @@ void ticker_frequency_test()
|
|||
|
||||
utest::v1::status_t us_ticker_case_setup_handler_t(const Case *const source, const size_t index_of_case)
|
||||
{
|
||||
#if DEVICE_LPTICKER && (LPTICKER_DELAY_TICKS > 0)
|
||||
/* Suspend the lp ticker wrapper since it makes use of the us ticker */
|
||||
ticker_suspend(get_lp_ticker_data());
|
||||
lp_ticker_wrapper_suspend();
|
||||
#endif
|
||||
ticker_suspend(get_us_ticker_data());
|
||||
intf = get_us_ticker_data()->interface;
|
||||
set_us_ticker_irq_handler(ticker_event_handler_stub);
|
||||
prev_handler = set_us_ticker_irq_handler(ticker_event_handler_stub);
|
||||
return greentea_case_setup_handler(source, index_of_case);
|
||||
}
|
||||
|
||||
utest::v1::status_t us_ticker_case_teardown_handler_t(const Case *const source, const size_t passed, const size_t failed,
|
||||
const failure_t reason)
|
||||
{
|
||||
set_us_ticker_irq_handler(prev_handler);
|
||||
ticker_resume(get_us_ticker_data());
|
||||
#if DEVICE_LPTICKER && (LPTICKER_DELAY_TICKS > 0)
|
||||
lp_ticker_wrapper_resume();
|
||||
ticker_resume(get_lp_ticker_data());
|
||||
#endif
|
||||
return greentea_case_teardown_handler(source, passed, failed, reason);
|
||||
}
|
||||
|
||||
#if DEVICE_LPTICKER
|
||||
utest::v1::status_t lp_ticker_case_setup_handler_t(const Case *const source, const size_t index_of_case)
|
||||
{
|
||||
ticker_suspend(get_lp_ticker_data());
|
||||
intf = get_lp_ticker_data()->interface;
|
||||
set_lp_ticker_irq_handler(ticker_event_handler_stub);
|
||||
prev_handler = set_lp_ticker_irq_handler(ticker_event_handler_stub);
|
||||
return greentea_case_setup_handler(source, index_of_case);
|
||||
}
|
||||
#endif
|
||||
|
||||
utest::v1::status_t ticker_case_teardown_handler_t(const Case *const source, const size_t passed, const size_t failed,
|
||||
const failure_t reason)
|
||||
utest::v1::status_t lp_ticker_case_teardown_handler_t(const Case *const source, const size_t passed, const size_t failed,
|
||||
const failure_t reason)
|
||||
{
|
||||
set_lp_ticker_irq_handler(prev_handler);
|
||||
ticker_resume(get_lp_ticker_data());
|
||||
return greentea_case_teardown_handler(source, passed, failed, reason);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Test cases
|
||||
Case cases[] = {
|
||||
Case("Microsecond ticker frequency test", us_ticker_case_setup_handler_t, ticker_frequency_test,
|
||||
ticker_case_teardown_handler_t),
|
||||
us_ticker_case_teardown_handler_t),
|
||||
#if DEVICE_LPTICKER
|
||||
Case("Low power ticker frequency test", lp_ticker_case_setup_handler_t, ticker_frequency_test,
|
||||
ticker_case_teardown_handler_t),
|
||||
lp_ticker_case_teardown_handler_t),
|
||||
#endif
|
||||
};
|
||||
|
||||
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
|
||||
{
|
||||
/* Suspend RTOS Kernel so the timers are not in use. */
|
||||
osKernelSuspend();
|
||||
|
||||
GREENTEA_SETUP(120, "timing_drift_auto");
|
||||
return greentea_test_setup_handler(number_of_cases);
|
||||
}
|
||||
|
||||
Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);
|
||||
void greentea_test_teardown(const size_t passed, const size_t failed, const failure_t failure)
|
||||
{
|
||||
osKernelResume(0);
|
||||
|
||||
greentea_test_teardown_handler(passed, failed, failure);
|
||||
}
|
||||
|
||||
Specification specification(greentea_test_setup, cases, greentea_test_teardown);
|
||||
|
||||
int main()
|
||||
{
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "rtos.h"
|
||||
#include "lp_ticker_api_tests.h"
|
||||
#include "hal/lp_ticker_api.h"
|
||||
#include "hal/mbed_lp_ticker_wrapper.h"
|
||||
|
||||
#if !DEVICE_LPTICKER
|
||||
#error [NOT_SUPPORTED] Low power timer not supported for this target
|
||||
|
@ -29,6 +30,8 @@ using namespace utest::v1;
|
|||
|
||||
volatile int intFlag = 0;
|
||||
|
||||
ticker_irq_handler_type prev_handler;
|
||||
|
||||
#define US_PER_MS 1000
|
||||
|
||||
#define TICKER_GLITCH_TEST_TICKS 1000
|
||||
|
@ -113,8 +116,6 @@ void lp_ticker_deepsleep_test()
|
|||
{
|
||||
intFlag = 0;
|
||||
|
||||
set_lp_ticker_irq_handler(ticker_event_handler_stub);
|
||||
|
||||
lp_ticker_init();
|
||||
|
||||
/* Give some time Green Tea to finish UART transmission before entering
|
||||
|
@ -130,7 +131,7 @@ void lp_ticker_deepsleep_test()
|
|||
* tick_count + TICKER_INT_VAL. */
|
||||
lp_ticker_set_interrupt(tick_count + TICKER_INT_VAL);
|
||||
|
||||
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep());
|
||||
TEST_ASSERT_TRUE(sleep_manager_can_deep_sleep_test_check());
|
||||
|
||||
while (!intFlag) {
|
||||
sleep();
|
||||
|
@ -157,6 +158,32 @@ void lp_ticker_glitch_test()
|
|||
}
|
||||
}
|
||||
|
||||
#if DEVICE_LPTICKER
|
||||
utest::v1::status_t lp_ticker_deepsleep_test_setup_handler(const Case *const source, const size_t index_of_case)
|
||||
{
|
||||
/* disable everything using the lp ticker for this test */
|
||||
osKernelSuspend();
|
||||
ticker_suspend(get_lp_ticker_data());
|
||||
#if DEVICE_LPTICKER && (LPTICKER_DELAY_TICKS > 0)
|
||||
lp_ticker_wrapper_suspend();
|
||||
#endif
|
||||
prev_handler = set_lp_ticker_irq_handler(ticker_event_handler_stub);
|
||||
return greentea_case_setup_handler(source, index_of_case);
|
||||
}
|
||||
|
||||
utest::v1::status_t lp_ticker_deepsleep_test_teardown_handler(const Case *const source, const size_t passed, const size_t failed,
|
||||
const failure_t reason)
|
||||
{
|
||||
set_lp_ticker_irq_handler(prev_handler);
|
||||
#if DEVICE_LPTICKER && (LPTICKER_DELAY_TICKS > 0)
|
||||
lp_ticker_wrapper_resume();
|
||||
#endif
|
||||
ticker_resume(get_lp_ticker_data());
|
||||
osKernelResume(0);
|
||||
return greentea_case_teardown_handler(source, passed, failed, reason);
|
||||
}
|
||||
#endif
|
||||
|
||||
utest::v1::status_t test_setup(const size_t number_of_cases)
|
||||
{
|
||||
GREENTEA_SETUP(20, "default_auto");
|
||||
|
@ -166,7 +193,7 @@ utest::v1::status_t test_setup(const size_t number_of_cases)
|
|||
Case cases[] = {
|
||||
Case("lp ticker info test", lp_ticker_info_test),
|
||||
#if DEVICE_SLEEP
|
||||
Case("lp ticker sleep test", lp_ticker_deepsleep_test),
|
||||
Case("lp ticker sleep test", lp_ticker_deepsleep_test_setup_handler, lp_ticker_deepsleep_test, lp_ticker_deepsleep_test_teardown_handler),
|
||||
#endif
|
||||
Case("lp ticker glitch test", lp_ticker_glitch_test)
|
||||
};
|
||||
|
|
|
@ -74,7 +74,7 @@ void rtc_sleep_test_support(bool deepsleep_mode)
|
|||
|
||||
timeout.attach(callback, DELAY_4S);
|
||||
|
||||
TEST_ASSERT(sleep_manager_can_deep_sleep() == deepsleep_mode);
|
||||
TEST_ASSERT(sleep_manager_can_deep_sleep_test_check() == deepsleep_mode);
|
||||
|
||||
while (!expired) {
|
||||
sleep();
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "utest/utest.h"
|
||||
#include "unity/unity.h"
|
||||
#include "greentea-client/test_env.h"
|
||||
#include "mbed_lp_ticker_wrapper.h"
|
||||
|
||||
#include "sleep_api_tests.h"
|
||||
|
||||
|
@ -40,7 +41,7 @@
|
|||
*
|
||||
* This should be replaced with a better function that checks if the
|
||||
* hardware buffers are empty. However, such an API does not exist now,
|
||||
* so we'll use the wait_ms() function for now.
|
||||
* so we'll use the busy_wait_ms() function for now.
|
||||
*/
|
||||
#define SERIAL_FLUSH_TIME_MS 20
|
||||
|
||||
|
@ -103,6 +104,20 @@ bool compare_timestamps(unsigned int delta_ticks, unsigned int ticker_width, uns
|
|||
}
|
||||
}
|
||||
|
||||
void busy_wait_ms(int ms)
|
||||
{
|
||||
const ticker_info_t *info = us_ticker_get_info();
|
||||
uint32_t mask = (1 << info->bits) - 1;
|
||||
int delay = (int)((uint64_t)ms * info->frequency / 1000);
|
||||
|
||||
uint32_t prev = us_ticker_read();
|
||||
while (delay > 0) {
|
||||
uint32_t next = us_ticker_read();
|
||||
delay -= (next - prev) & mask;
|
||||
prev = next;
|
||||
}
|
||||
}
|
||||
|
||||
void us_ticker_isr(const ticker_data_t *const ticker_data)
|
||||
{
|
||||
us_ticker_clear_interrupt();
|
||||
|
@ -125,17 +140,6 @@ void sleep_usticker_test()
|
|||
|
||||
const ticker_irq_handler_type us_ticker_irq_handler_org = set_us_ticker_irq_handler(us_ticker_isr);
|
||||
|
||||
// call ticker_read_us to initialize ticker upper layer
|
||||
// prevents subsequent scheduling of max_delta interrupt during ticker initialization while test execution
|
||||
// (e.g when ticker_read_us is called)
|
||||
ticker_read_us(ticker);
|
||||
#ifdef DEVICE_LPTICKER
|
||||
// call ticker_read_us to initialize lp_ticker
|
||||
// prevents scheduling interrupt during ticker initialization (in lp_ticker_init) while test execution
|
||||
// (e.g when ticker_read_us is called for lp_ticker, see MBED_CPU_STATS_ENABLED)
|
||||
ticker_read_us(get_lp_ticker_data());
|
||||
#endif
|
||||
|
||||
/* Test only sleep functionality. */
|
||||
sleep_manager_lock_deep_sleep();
|
||||
TEST_ASSERT_FALSE_MESSAGE(sleep_manager_can_deep_sleep(), "deep sleep should be locked");
|
||||
|
@ -173,17 +177,12 @@ void deepsleep_lpticker_test()
|
|||
const unsigned int ticker_freq = ticker->interface->get_info()->frequency;
|
||||
const unsigned int ticker_width = ticker->interface->get_info()->bits;
|
||||
|
||||
// call ticker_read_us to initialize ticker upper layer
|
||||
// prevents subsequent scheduling of max_delta interrupt during ticker initialization while test execution
|
||||
// (e.g when ticker_read_us is called)
|
||||
ticker_read_us(ticker);
|
||||
|
||||
const ticker_irq_handler_type lp_ticker_irq_handler_org = set_lp_ticker_irq_handler(lp_ticker_isr);
|
||||
|
||||
/* Give some time Green Tea to finish UART transmission before entering
|
||||
* deep-sleep mode.
|
||||
*/
|
||||
wait_ms(SERIAL_FLUSH_TIME_MS);
|
||||
busy_wait_ms(SERIAL_FLUSH_TIME_MS);
|
||||
|
||||
TEST_ASSERT_TRUE_MESSAGE(sleep_manager_can_deep_sleep(), "deep sleep should not be locked");
|
||||
|
||||
|
@ -218,7 +217,7 @@ void deepsleep_high_speed_clocks_turned_off_test()
|
|||
/* Give some time Green Tea to finish UART transmission before entering
|
||||
* deep-sleep mode.
|
||||
*/
|
||||
wait_ms(SERIAL_FLUSH_TIME_MS);
|
||||
busy_wait_ms(SERIAL_FLUSH_TIME_MS);
|
||||
|
||||
TEST_ASSERT_TRUE_MESSAGE(sleep_manager_can_deep_sleep(), "deep sleep should not be locked");
|
||||
|
||||
|
@ -256,15 +255,38 @@ utest::v1::status_t greentea_failure_handler(const Case *const source, const fai
|
|||
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
|
||||
{
|
||||
GREENTEA_SETUP(60, "default_auto");
|
||||
/* Suspend RTOS Kernel to enable sleep modes. */
|
||||
osKernelSuspend();
|
||||
#if DEVICE_LPTICKER
|
||||
ticker_suspend(get_lp_ticker_data());
|
||||
#if DEVICE_LPTICKER && (LPTICKER_DELAY_TICKS > 0)
|
||||
lp_ticker_wrapper_suspend();
|
||||
#endif
|
||||
#endif
|
||||
ticker_suspend(get_us_ticker_data());
|
||||
|
||||
us_ticker_init();
|
||||
#if DEVICE_LPTICKER
|
||||
lp_ticker_init();
|
||||
#endif
|
||||
/* Suspend RTOS Kernel to enable sleep modes. */
|
||||
osKernelSuspend();
|
||||
|
||||
return greentea_test_setup_handler(number_of_cases);
|
||||
}
|
||||
|
||||
void greentea_test_teardown(const size_t passed, const size_t failed, const failure_t failure)
|
||||
{
|
||||
ticker_resume(get_us_ticker_data());
|
||||
#if DEVICE_LPTICKER
|
||||
#if DEVICE_LPTICKER && (LPTICKER_DELAY_TICKS > 0)
|
||||
lp_ticker_wrapper_resume();
|
||||
#endif
|
||||
ticker_resume(get_lp_ticker_data());
|
||||
#endif
|
||||
osKernelResume(0);
|
||||
|
||||
greentea_test_teardown_handler(passed, failed, failure);
|
||||
}
|
||||
|
||||
Case cases[] = {
|
||||
Case("sleep - source of wake-up - us ticker", sleep_usticker_test, greentea_failure_handler),
|
||||
#if DEVICE_LPTICKER
|
||||
|
@ -273,7 +295,7 @@ Case cases[] = {
|
|||
#endif
|
||||
};
|
||||
|
||||
Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);
|
||||
Specification specification(greentea_test_setup, cases, greentea_test_teardown);
|
||||
|
||||
int main()
|
||||
{
|
||||
|
|
|
@ -25,15 +25,15 @@ using namespace utest::v1;
|
|||
|
||||
void sleep_manager_deepsleep_counter_test()
|
||||
{
|
||||
bool deep_sleep_allowed = sleep_manager_can_deep_sleep();
|
||||
bool deep_sleep_allowed = sleep_manager_can_deep_sleep_test_check();
|
||||
TEST_ASSERT_TRUE(deep_sleep_allowed);
|
||||
|
||||
sleep_manager_lock_deep_sleep();
|
||||
deep_sleep_allowed = sleep_manager_can_deep_sleep();
|
||||
deep_sleep_allowed = sleep_manager_can_deep_sleep_test_check();
|
||||
TEST_ASSERT_FALSE(deep_sleep_allowed);
|
||||
|
||||
sleep_manager_unlock_deep_sleep();
|
||||
deep_sleep_allowed = sleep_manager_can_deep_sleep();
|
||||
deep_sleep_allowed = sleep_manager_can_deep_sleep_test_check();
|
||||
TEST_ASSERT_TRUE(deep_sleep_allowed);
|
||||
}
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ void sleep_manager_multithread_test()
|
|||
t2.join();
|
||||
}
|
||||
|
||||
bool deep_sleep_allowed = sleep_manager_can_deep_sleep();
|
||||
bool deep_sleep_allowed = sleep_manager_can_deep_sleep_test_check();
|
||||
TEST_ASSERT_TRUE_MESSAGE(deep_sleep_allowed, "Deep sleep should be allowed");
|
||||
}
|
||||
|
||||
|
@ -83,7 +83,7 @@ void sleep_manager_irq_test()
|
|||
timer.stop();
|
||||
}
|
||||
|
||||
bool deep_sleep_allowed = sleep_manager_can_deep_sleep();
|
||||
bool deep_sleep_allowed = sleep_manager_can_deep_sleep_test_check();
|
||||
TEST_ASSERT_TRUE_MESSAGE(deep_sleep_allowed, "Deep sleep should be allowed");
|
||||
}
|
||||
|
||||
|
|
|
@ -2268,6 +2268,82 @@ static void test_match_interval_passed_table()
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that suspend and resume work as expected
|
||||
*
|
||||
* Check the following
|
||||
* -time does not change while suspended
|
||||
* -restricted interface functions are not called
|
||||
* -scheduling resumes correctly
|
||||
*/
|
||||
static void test_suspend_resume()
|
||||
{
|
||||
ticker_set_handler(&ticker_stub, NULL);
|
||||
|
||||
interface_stub.timestamp = 1000;
|
||||
us_timestamp_t start = ticker_read_us(&ticker_stub);
|
||||
TEST_ASSERT_EQUAL(1000, start);
|
||||
|
||||
/* Reset call count */
|
||||
interface_stub.init_call = 0;
|
||||
interface_stub.read_call = 0;
|
||||
interface_stub.set_interrupt_call = 0;
|
||||
|
||||
/* Suspend the ticker */
|
||||
ticker_suspend(&ticker_stub);
|
||||
const timestamp_t suspend_time = queue_stub.present_time;
|
||||
|
||||
|
||||
/* Simulate time passing */
|
||||
interface_stub.timestamp = 1500;
|
||||
us_timestamp_t next = ticker_read_us(&ticker_stub);
|
||||
|
||||
/* Time should not have passed and no calls to interface should have been made */
|
||||
TEST_ASSERT_EQUAL(start, next);
|
||||
TEST_ASSERT_EQUAL(0, interface_stub.init_call);
|
||||
TEST_ASSERT_EQUAL(0, interface_stub.read_call);
|
||||
TEST_ASSERT_EQUAL(0, interface_stub.disable_interrupt_call);
|
||||
TEST_ASSERT_EQUAL(0, interface_stub.clear_interrupt_call);
|
||||
TEST_ASSERT_EQUAL(0, interface_stub.set_interrupt_call);
|
||||
TEST_ASSERT_EQUAL(0, interface_stub.fire_interrupt_call);
|
||||
|
||||
|
||||
/* Simulate a reinit (time reset to 0) */
|
||||
interface_stub.timestamp = 0;
|
||||
next = ticker_read_us(&ticker_stub);
|
||||
|
||||
/* Time should not have passed and no calls to interface should have been made */
|
||||
TEST_ASSERT_EQUAL(start, next);
|
||||
TEST_ASSERT_EQUAL(0, interface_stub.init_call);
|
||||
TEST_ASSERT_EQUAL(0, interface_stub.read_call);
|
||||
TEST_ASSERT_EQUAL(0, interface_stub.disable_interrupt_call);
|
||||
TEST_ASSERT_EQUAL(0, interface_stub.clear_interrupt_call);
|
||||
TEST_ASSERT_EQUAL(0, interface_stub.set_interrupt_call);
|
||||
TEST_ASSERT_EQUAL(0, interface_stub.fire_interrupt_call);
|
||||
|
||||
|
||||
/* Insert an event in the past and future */
|
||||
ticker_event_t event_past = { 0 };
|
||||
const timestamp_t event_past_timestamp = suspend_time - 10;
|
||||
ticker_insert_event_us(&ticker_stub, &event_past, event_past_timestamp, 0);
|
||||
|
||||
ticker_event_t event_future = { 0 };
|
||||
const timestamp_t event_future_timestamp = suspend_time + 10;
|
||||
ticker_insert_event_us(&ticker_stub, &event_future, event_future_timestamp, 0);
|
||||
|
||||
TEST_ASSERT_EQUAL(0, interface_stub.init_call);
|
||||
TEST_ASSERT_EQUAL(0, interface_stub.read_call);
|
||||
TEST_ASSERT_EQUAL(0, interface_stub.disable_interrupt_call);
|
||||
TEST_ASSERT_EQUAL(0, interface_stub.clear_interrupt_call);
|
||||
TEST_ASSERT_EQUAL(0, interface_stub.set_interrupt_call);
|
||||
TEST_ASSERT_EQUAL(0, interface_stub.fire_interrupt_call);
|
||||
|
||||
/* Resume and verify everything starts again */
|
||||
ticker_resume(&ticker_stub);
|
||||
TEST_ASSERT_EQUAL(suspend_time, queue_stub.present_time);
|
||||
TEST_ASSERT_EQUAL(1, interface_stub.fire_interrupt_call);
|
||||
}
|
||||
|
||||
static const case_t cases[] = {
|
||||
MAKE_TEST_CASE("ticker initialization", test_ticker_initialization),
|
||||
MAKE_TEST_CASE(
|
||||
|
@ -2376,6 +2452,10 @@ static const case_t cases[] = {
|
|||
MAKE_TEST_CASE(
|
||||
"test_match_interval_passed_table",
|
||||
test_match_interval_passed_table
|
||||
),
|
||||
MAKE_TEST_CASE(
|
||||
"test_suspend_resume",
|
||||
test_suspend_resume
|
||||
)
|
||||
};
|
||||
|
||||
|
|
|
@ -313,7 +313,7 @@ void test_deepsleep(void)
|
|||
|
||||
lptimer.start();
|
||||
st.schedule_tick(TEST_TICKS);
|
||||
TEST_ASSERT_TRUE_MESSAGE(sleep_manager_can_deep_sleep(), "Deep sleep should be allowed");
|
||||
TEST_ASSERT_TRUE_MESSAGE(sleep_manager_can_deep_sleep_test_check(), "Deep sleep should be allowed");
|
||||
while (st.sem_wait(0) != 1) {
|
||||
sleep();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,295 @@
|
|||
/* 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 (_suspended) {
|
||||
if (handler) {
|
||||
handler(&data);
|
||||
}
|
||||
core_util_critical_section_exit();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_pending_fire_now || _match_check(_intf->read())) {
|
||||
_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();
|
||||
|
||||
_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 (_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;
|
||||
_schedule_match(_last_set_interrupt);
|
||||
|
||||
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();
|
||||
if (_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;
|
||||
}
|
||||
return _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 - _last_set_interrupt) & _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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,239 @@
|
|||
|
||||
/** \addtogroup hal */
|
||||
/** @{*/
|
||||
/* 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.
|
||||
*/
|
||||
#ifndef MBED_LOW_POWER_TICKER_WRAPPER_H
|
||||
#define MBED_LOW_POWER_TICKER_WRAPPER_H
|
||||
|
||||
#include "device.h"
|
||||
|
||||
#include "hal/ticker_api.h"
|
||||
#include "hal/us_ticker_api.h"
|
||||
#include "drivers/Timeout.h"
|
||||
|
||||
|
||||
class LowPowerTickerWrapper {
|
||||
public:
|
||||
|
||||
|
||||
/**
|
||||
* Create a new wrapped low power ticker object
|
||||
*
|
||||
* @param data Low power ticker data to wrap
|
||||
* @param interface new ticker interface functions
|
||||
* @param min_cycles_between_writes The number of whole low power clock periods
|
||||
* which must complete before subsequent calls to set_interrupt
|
||||
* @param min_cycles_until_match The minimum number of whole low power clock periods
|
||||
* from the current time for which the match timestamp passed to set_interrupt is
|
||||
* guaranteed to fire.
|
||||
*
|
||||
* N = min_cycles_between_writes
|
||||
*
|
||||
* 0 1 N - 1 N N + 1 N + 2 N + 3
|
||||
* |-------|------...------|-------|-------|-------|-------|
|
||||
* ^ ^
|
||||
* | |
|
||||
* set_interrupt Next set_interrupt allowed
|
||||
*
|
||||
* N = min_cycles_until_match
|
||||
*
|
||||
* 0 1 N - 1 N N + 1 N + 2 N + 3
|
||||
* |-------|------...------|-------|-------|-------|-------|
|
||||
* ^ ^
|
||||
* | |
|
||||
* set_interrupt Earliest match timestamp allowed
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
LowPowerTickerWrapper(const ticker_data_t *data, const ticker_interface_t *interface, uint32_t min_cycles_between_writes, uint32_t min_cycles_until_match);
|
||||
|
||||
/**
|
||||
* Interrupt handler called by the underlying driver/hardware
|
||||
*
|
||||
* @param handler The callback which would normally be called by the underlying driver/hardware
|
||||
*/
|
||||
void irq_handler(ticker_irq_handler_type handler);
|
||||
|
||||
/**
|
||||
* Suspend wrapper operation and pass through interrupts.
|
||||
*
|
||||
* This stops to wrapper layer from using the microsecond ticker.
|
||||
* This should be called before using the low power ticker APIs directly.
|
||||
*/
|
||||
void suspend();
|
||||
|
||||
/**
|
||||
* Resume wrapper operation and filter interrupts normally
|
||||
*/
|
||||
void resume();
|
||||
|
||||
/**
|
||||
* Check if a Timeout object is being used
|
||||
*
|
||||
* @return true if Timeout is used for scheduling false otherwise
|
||||
*/
|
||||
bool timeout_pending();
|
||||
|
||||
/*
|
||||
* Implementation of ticker_init
|
||||
*/
|
||||
void init();
|
||||
|
||||
/*
|
||||
* Implementation of free
|
||||
*/
|
||||
void free();
|
||||
|
||||
/*
|
||||
* Implementation of read
|
||||
*/
|
||||
uint32_t read();
|
||||
|
||||
/*
|
||||
* Implementation of set_interrupt
|
||||
*/
|
||||
void set_interrupt(timestamp_t timestamp);
|
||||
|
||||
/*
|
||||
* Implementation of disable_interrupt
|
||||
*/
|
||||
void disable_interrupt();
|
||||
|
||||
/*
|
||||
* Implementation of clear_interrupt
|
||||
*/
|
||||
void clear_interrupt();
|
||||
|
||||
/*
|
||||
* Implementation of fire_interrupt
|
||||
*/
|
||||
void fire_interrupt();
|
||||
|
||||
/*
|
||||
* Implementation of get_info
|
||||
*/
|
||||
const ticker_info_t *get_info();
|
||||
|
||||
ticker_data_t data;
|
||||
|
||||
private:
|
||||
mbed::Timeout _timeout;
|
||||
const ticker_interface_t *const _intf;
|
||||
|
||||
/*
|
||||
* The number of low power clock cycles which must pass between subsequent
|
||||
* calls to intf->set_interrupt
|
||||
*/
|
||||
const uint32_t _min_count_between_writes;
|
||||
|
||||
/*
|
||||
* The minimum number of low power clock cycles in the future that
|
||||
* a match value can be set to and still fire
|
||||
*/
|
||||
const uint32_t _min_count_until_match;
|
||||
|
||||
/*
|
||||
* Flag to indicate if the timer is suspended
|
||||
*/
|
||||
bool _suspended;
|
||||
|
||||
/*
|
||||
* _cur_match_time is valid and Timeout is scheduled to fire
|
||||
*/
|
||||
bool _pending_timeout;
|
||||
|
||||
/*
|
||||
* set_interrupt has been called and _cur_match_time is valid
|
||||
*/
|
||||
bool _pending_match;
|
||||
|
||||
/*
|
||||
* The function LowPowerTickerWrapper::fire_interrupt has been called
|
||||
* and an interrupt is expected.
|
||||
*/
|
||||
bool _pending_fire_now;
|
||||
|
||||
/*
|
||||
* It is safe to call intf->set_interrupt
|
||||
*/
|
||||
bool _set_interrupt_allowed;
|
||||
|
||||
/*
|
||||
* Last value written by LowPowerTickerWrapper::set_interrupt
|
||||
*/
|
||||
timestamp_t _cur_match_time;
|
||||
|
||||
/*
|
||||
* Time of last call to LowPowerTickerWrapper::set_interrupt
|
||||
*/
|
||||
uint32_t _last_set_interrupt;
|
||||
|
||||
/*
|
||||
* Time of last call to intf->set_interrupt
|
||||
*/
|
||||
uint32_t _last_actual_set_interrupt;
|
||||
|
||||
/*
|
||||
* Mask of valid bits from intf->read()
|
||||
*/
|
||||
uint32_t _mask;
|
||||
|
||||
/*
|
||||
* Microsecond per low power tick (rounded up)
|
||||
*/
|
||||
uint32_t _us_per_tick;
|
||||
|
||||
|
||||
void _reset();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
void _timeout_handler();
|
||||
|
||||
/*
|
||||
* Check match time has passed
|
||||
*/
|
||||
bool _match_check(timestamp_t current);
|
||||
|
||||
/*
|
||||
* Convert low power ticks to approximate microseconds
|
||||
*
|
||||
* This value is always larger or equal to exact value.
|
||||
*/
|
||||
uint32_t _lp_ticks_to_us(uint32_t);
|
||||
|
||||
/*
|
||||
* Schedule a match interrupt to fire at the correct time
|
||||
*
|
||||
* @param current The current low power ticker time
|
||||
*/
|
||||
void _schedule_match(timestamp_t current);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/** @}*/
|
||||
|
||||
|
|
@ -14,11 +14,10 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
#include "hal/lp_ticker_api.h"
|
||||
#include "hal/mbed_lp_ticker_wrapper.h"
|
||||
|
||||
#if DEVICE_LPTICKER
|
||||
|
||||
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;
|
||||
|
@ -28,11 +27,7 @@ static const ticker_interface_t lp_interface = {
|
|||
.read = lp_ticker_read,
|
||||
.disable_interrupt = lp_ticker_disable_interrupt,
|
||||
.clear_interrupt = lp_ticker_clear_interrupt,
|
||||
#if LPTICKER_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,
|
||||
.free = lp_ticker_free,
|
||||
|
@ -45,7 +40,11 @@ static const ticker_data_t lp_data = {
|
|||
|
||||
const ticker_data_t *get_lp_ticker_data(void)
|
||||
{
|
||||
#if LPTICKER_DELAY_TICKS > 0
|
||||
return get_lp_ticker_wrapper_data(&lp_data);
|
||||
#else
|
||||
return &lp_data;
|
||||
#endif
|
||||
}
|
||||
|
||||
ticker_irq_handler_type set_lp_ticker_irq_handler(ticker_irq_handler_type ticker_irq_handler)
|
||||
|
@ -59,9 +58,13 @@ ticker_irq_handler_type set_lp_ticker_irq_handler(ticker_irq_handler_type ticker
|
|||
|
||||
void lp_ticker_irq_handler(void)
|
||||
{
|
||||
#if LPTICKER_DELAY_TICKS > 0
|
||||
lp_ticker_wrapper_irq_handler(irq_handler);
|
||||
#else
|
||||
if (irq_handler) {
|
||||
irq_handler(&lp_data);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -13,144 +13,115 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include "hal/lp_ticker_api.h"
|
||||
#include "hal/mbed_lp_ticker_wrapper.h"
|
||||
|
||||
#if DEVICE_LPTICKER && (LPTICKER_DELAY_TICKS > 0)
|
||||
|
||||
#include "Timeout.h"
|
||||
#include "mbed_critical.h"
|
||||
|
||||
static const timestamp_t min_delta = LPTICKER_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;
|
||||
#include "hal/LowPowerTickerWrapper.h"
|
||||
#include "platform/mbed_critical.h"
|
||||
|
||||
// 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];
|
||||
static LowPowerTickerWrapper *ticker_wrapper;
|
||||
static uint64_t ticker_wrapper_data[(sizeof(LowPowerTickerWrapper) + 7) / 8];
|
||||
static bool init = false;
|
||||
|
||||
/**
|
||||
* Initialize variables
|
||||
*/
|
||||
static void init_local()
|
||||
static void lp_ticker_wrapper_init()
|
||||
{
|
||||
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();
|
||||
ticker_wrapper->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
static uint32_t lp_ticker_wrapper_read()
|
||||
{
|
||||
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);
|
||||
return ticker_wrapper->read();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
static void lp_ticker_wrapper_set_interrupt(timestamp_t timestamp)
|
||||
{
|
||||
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();
|
||||
ticker_wrapper->set_interrupt(timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around lp_ticker_set_interrupt to prevent blocking
|
||||
*
|
||||
* Problems this function is solving:
|
||||
* 1. Interrupt may not fire if set earlier than LPTICKER_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)
|
||||
static void lp_ticker_wrapper_disable_interrupt()
|
||||
{
|
||||
ticker_wrapper->disable_interrupt();
|
||||
}
|
||||
|
||||
static void lp_ticker_wrapper_clear_interrupt()
|
||||
{
|
||||
ticker_wrapper->clear_interrupt();
|
||||
}
|
||||
|
||||
static void lp_ticker_wrapper_fire_interrupt()
|
||||
{
|
||||
ticker_wrapper->fire_interrupt();
|
||||
}
|
||||
|
||||
static const ticker_info_t *lp_ticker_wrapper_get_info()
|
||||
{
|
||||
return ticker_wrapper->get_info();
|
||||
}
|
||||
|
||||
static void lp_ticker_wrapper_free()
|
||||
{
|
||||
ticker_wrapper->free();
|
||||
}
|
||||
|
||||
static const ticker_interface_t lp_interface = {
|
||||
lp_ticker_wrapper_init,
|
||||
lp_ticker_wrapper_read,
|
||||
lp_ticker_wrapper_disable_interrupt,
|
||||
lp_ticker_wrapper_clear_interrupt,
|
||||
lp_ticker_wrapper_set_interrupt,
|
||||
lp_ticker_wrapper_fire_interrupt,
|
||||
lp_ticker_wrapper_free,
|
||||
lp_ticker_wrapper_get_info
|
||||
};
|
||||
|
||||
void lp_ticker_wrapper_irq_handler(ticker_irq_handler_type handler)
|
||||
{
|
||||
core_util_critical_section_enter();
|
||||
|
||||
if (!init) {
|
||||
init_local();
|
||||
init = true;
|
||||
// Force ticker to initialize
|
||||
get_lp_ticker_data();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
ticker_wrapper->irq_handler(handler);
|
||||
|
||||
core_util_critical_section_exit();
|
||||
}
|
||||
|
||||
const ticker_data_t *get_lp_ticker_wrapper_data(const ticker_data_t *data)
|
||||
{
|
||||
core_util_critical_section_enter();
|
||||
|
||||
if (!init) {
|
||||
ticker_wrapper = new (ticker_wrapper_data) LowPowerTickerWrapper(data, &lp_interface, LPTICKER_DELAY_TICKS, LPTICKER_DELAY_TICKS);
|
||||
init = true;
|
||||
}
|
||||
|
||||
core_util_critical_section_exit();
|
||||
|
||||
return &ticker_wrapper->data;
|
||||
}
|
||||
|
||||
void lp_ticker_wrapper_suspend()
|
||||
{
|
||||
if (!init) {
|
||||
// Force ticker to initialize
|
||||
get_lp_ticker_data();
|
||||
}
|
||||
|
||||
ticker_wrapper->suspend();
|
||||
}
|
||||
|
||||
void lp_ticker_wrapper_resume()
|
||||
{
|
||||
if (!init) {
|
||||
// Force ticker to initialize
|
||||
get_lp_ticker_data();
|
||||
}
|
||||
|
||||
ticker_wrapper->resume();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
|
||||
/** \addtogroup hal */
|
||||
/** @{*/
|
||||
/* 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.
|
||||
*/
|
||||
#ifndef MBED_LP_TICKER_WRAPPER_H
|
||||
#define MBED_LP_TICKER_WRAPPER_H
|
||||
|
||||
#include "device.h"
|
||||
|
||||
#if DEVICE_LPTICKER
|
||||
|
||||
#include "hal/ticker_api.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void (*ticker_irq_handler_type)(const ticker_data_t *const);
|
||||
|
||||
/**
|
||||
* Interrupt handler for the wrapped lp ticker
|
||||
*
|
||||
* @param handler the function which would normally be called by the
|
||||
* lp ticker handler when it is not wrapped
|
||||
*/
|
||||
void lp_ticker_wrapper_irq_handler(ticker_irq_handler_type handler);
|
||||
|
||||
/**
|
||||
* Get wrapped lp ticker data
|
||||
*
|
||||
* @param data hardware low power ticker object
|
||||
* @return wrapped low power ticker object
|
||||
*/
|
||||
const ticker_data_t *get_lp_ticker_wrapper_data(const ticker_data_t *data);
|
||||
|
||||
/**
|
||||
* Suspend the wrapper layer code
|
||||
*
|
||||
* Pass through all interrupts to the low power ticker and stop using
|
||||
* the microsecond ticker.
|
||||
*/
|
||||
void lp_ticker_wrapper_suspend(void);
|
||||
|
||||
/**
|
||||
* Resume the wrapper layer code
|
||||
*
|
||||
* Resume operation of the wrapper layer. Interrupts will be filtered
|
||||
* as normal and the microsecond timer will be used for interrupts scheduled
|
||||
* too quickly back-to-back.
|
||||
*/
|
||||
void lp_ticker_wrapper_resume(void);
|
||||
|
||||
/**@}*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/** @}*/
|
|
@ -21,6 +21,7 @@
|
|||
#include "mbed_error.h"
|
||||
#include "mbed_debug.h"
|
||||
#include "mbed_stats.h"
|
||||
#include "us_ticker_api.h"
|
||||
#include "lp_ticker_api.h"
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
|
@ -183,6 +184,19 @@ bool sleep_manager_can_deep_sleep(void)
|
|||
return deep_sleep_lock == 0 ? true : false;
|
||||
}
|
||||
|
||||
bool sleep_manager_can_deep_sleep_test_check()
|
||||
{
|
||||
const uint32_t check_time_us = 2000;
|
||||
const ticker_data_t *const ticker = get_us_ticker_data();
|
||||
uint32_t start = ticker_read(ticker);
|
||||
while ((ticker_read(ticker) - start) < check_time_us) {
|
||||
if (sleep_manager_can_deep_sleep()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void sleep_manager_sleep_auto(void)
|
||||
{
|
||||
#ifdef MBED_SLEEP_TRACING_ENABLED
|
||||
|
|
|
@ -32,6 +32,9 @@ static void initialize(const ticker_data_t *ticker)
|
|||
if (ticker->queue->initialized) {
|
||||
return;
|
||||
}
|
||||
if (ticker->queue->suspended) {
|
||||
return;
|
||||
}
|
||||
|
||||
ticker->interface->init();
|
||||
|
||||
|
@ -70,6 +73,7 @@ static void initialize(const ticker_data_t *ticker)
|
|||
ticker->queue->max_delta_us = max_delta_us;
|
||||
ticker->queue->present_time = 0;
|
||||
ticker->queue->dispatching = false;
|
||||
ticker->queue->suspended = false;
|
||||
ticker->queue->initialized = true;
|
||||
|
||||
update_present_time(ticker);
|
||||
|
@ -121,6 +125,9 @@ static us_timestamp_t convert_timestamp(us_timestamp_t ref, timestamp_t timestam
|
|||
static void update_present_time(const ticker_data_t *const ticker)
|
||||
{
|
||||
ticker_event_queue_t *queue = ticker->queue;
|
||||
if (queue->suspended) {
|
||||
return;
|
||||
}
|
||||
uint32_t ticker_time = ticker->interface->read();
|
||||
if (ticker_time == ticker->queue->tick_last_read) {
|
||||
// No work to do
|
||||
|
@ -230,7 +237,7 @@ int _ticker_match_interval_passed(timestamp_t prev_tick, timestamp_t cur_tick, t
|
|||
static void schedule_interrupt(const ticker_data_t *const ticker)
|
||||
{
|
||||
ticker_event_queue_t *queue = ticker->queue;
|
||||
if (ticker->queue->dispatching) {
|
||||
if (queue->suspended || ticker->queue->dispatching) {
|
||||
// Don't schedule the next interrupt until dispatching is
|
||||
// finished. This prevents repeated calls to interface->set_interrupt
|
||||
return;
|
||||
|
@ -282,6 +289,10 @@ void ticker_irq_handler(const ticker_data_t *const ticker)
|
|||
core_util_critical_section_enter();
|
||||
|
||||
ticker->interface->clear_interrupt();
|
||||
if (ticker->queue->suspended) {
|
||||
core_util_critical_section_exit();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Go through all the pending TimerEvents */
|
||||
ticker->queue->dispatching = true;
|
||||
|
@ -427,3 +438,29 @@ int ticker_get_next_timestamp(const ticker_data_t *const data, timestamp_t *time
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ticker_suspend(const ticker_data_t *const ticker)
|
||||
{
|
||||
core_util_critical_section_enter();
|
||||
|
||||
ticker->queue->suspended = true;
|
||||
|
||||
core_util_critical_section_exit();
|
||||
}
|
||||
|
||||
void ticker_resume(const ticker_data_t *const ticker)
|
||||
{
|
||||
core_util_critical_section_enter();
|
||||
|
||||
ticker->queue->suspended = false;
|
||||
if (ticker->queue->initialized) {
|
||||
ticker->queue->tick_last_read = ticker->interface->read();
|
||||
|
||||
update_present_time(ticker);
|
||||
schedule_interrupt(ticker);
|
||||
} else {
|
||||
initialize(ticker);
|
||||
}
|
||||
|
||||
core_util_critical_section_exit();
|
||||
}
|
||||
|
|
|
@ -82,6 +82,7 @@ typedef struct {
|
|||
us_timestamp_t present_time; /**< Store the timestamp used for present time */
|
||||
bool initialized; /**< Indicate if the instance is initialized */
|
||||
bool dispatching; /**< The function ticker_irq_handler is dispatching */
|
||||
bool suspended; /**< Indicate if the instance is suspended */
|
||||
uint8_t frequency_shifts; /**< If frequency is a value of 2^n, this is n, otherwise 0 */
|
||||
} ticker_event_queue_t;
|
||||
|
||||
|
@ -184,6 +185,27 @@ us_timestamp_t ticker_read_us(const ticker_data_t *const ticker);
|
|||
*/
|
||||
int ticker_get_next_timestamp(const ticker_data_t *const ticker, timestamp_t *timestamp);
|
||||
|
||||
/** Suspend this ticker
|
||||
*
|
||||
* When suspended reads will always return the same time and no
|
||||
* events will be dispatched. When suspended the common layer
|
||||
* will only ever call the interface function clear_interrupt()
|
||||
* and that is only if ticker_irq_handler is called.
|
||||
*
|
||||
*
|
||||
* @param ticker The ticker object.
|
||||
*/
|
||||
void ticker_suspend(const ticker_data_t *const ticker);
|
||||
|
||||
/** Resume this ticker
|
||||
*
|
||||
* When resumed the ticker will ignore any time that has passed
|
||||
* and continue counting up where it left off.
|
||||
*
|
||||
* @param ticker The ticker object.
|
||||
*/
|
||||
void ticker_resume(const ticker_data_t *const ticker);
|
||||
|
||||
/* Private functions
|
||||
*
|
||||
* @cond PRIVATE
|
||||
|
|
|
@ -122,6 +122,17 @@ void sleep_manager_unlock_deep_sleep_internal(void);
|
|||
*/
|
||||
bool sleep_manager_can_deep_sleep(void);
|
||||
|
||||
/** Check if the target can deep sleep within a period of time
|
||||
*
|
||||
* This function in intended for use in testing. The amount
|
||||
* of time this functions waits for deeps sleep to be available
|
||||
* is currently 2ms. This may change in the future depending
|
||||
* on testing requirements.
|
||||
*
|
||||
* @return true if a target can go to deepsleep, false otherwise
|
||||
*/
|
||||
bool sleep_manager_can_deep_sleep_test_check(void);
|
||||
|
||||
/** Enter auto selected sleep mode. It chooses the sleep or deeepsleep modes based
|
||||
* on the deepsleep locking counter
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue