mirror of https://github.com/ARMmbed/mbed-os.git
Add RealTimeClock
parent
0eff3340d2
commit
f0ee31f119
|
@ -25,23 +25,20 @@
|
||||||
#include "rtc_test.h"
|
#include "rtc_test.h"
|
||||||
|
|
||||||
#include "mbed.h"
|
#include "mbed.h"
|
||||||
|
#include "drivers/RealTimeClock.h"
|
||||||
#include "rtc_api.h"
|
#include "rtc_api.h"
|
||||||
|
|
||||||
using namespace utest::v1;
|
using namespace utest::v1;
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
static const uint32_t WAIT_TIME = 4;
|
static constexpr auto WAIT_TIME = 4s;
|
||||||
static const uint32_t WAIT_TOLERANCE = 1;
|
static constexpr auto WAIT_TOLERANCE = 1s;
|
||||||
|
|
||||||
#define US_PER_SEC 1000000
|
|
||||||
#define ACCURACY_FACTOR 10
|
|
||||||
|
|
||||||
static const uint32_t DELAY_4S = 4;
|
|
||||||
static const uint32_t DELAY_10S = 10;
|
|
||||||
static const uint32_t RTC_TOLERANCE = 1;
|
|
||||||
static const uint32_t TOLERANCE_ACCURACY_US = (DELAY_10S *US_PER_SEC / ACCURACY_FACTOR);
|
|
||||||
|
|
||||||
#if DEVICE_LPTICKER
|
#if DEVICE_LPTICKER
|
||||||
volatile bool expired;
|
mstd::atomic_bool expired;
|
||||||
|
|
||||||
|
#define TEST_ASSERT_DURATION_WITHIN(delta, expected, actual) \
|
||||||
|
TEST_ASSERT_INT64_WITHIN(microseconds(delta).count(), microseconds(expected).count(), microseconds(actual).count())
|
||||||
|
|
||||||
void set_flag_true(void)
|
void set_flag_true(void)
|
||||||
{
|
{
|
||||||
|
@ -53,7 +50,7 @@ void set_flag_true(void)
|
||||||
void rtc_sleep_test_support(bool deepsleep_mode)
|
void rtc_sleep_test_support(bool deepsleep_mode)
|
||||||
{
|
{
|
||||||
LowPowerTimeout timeout;
|
LowPowerTimeout timeout;
|
||||||
const uint32_t start = 100;
|
const auto start = RealTimeClock::time_point(100s);
|
||||||
expired = false;
|
expired = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -63,17 +60,17 @@ void rtc_sleep_test_support(bool deepsleep_mode)
|
||||||
* hardware buffers are empty. However, such an API does not exist now,
|
* hardware buffers are empty. However, such an API does not exist now,
|
||||||
* so we'll use the ThisThread::sleep_for() function for now.
|
* so we'll use the ThisThread::sleep_for() function for now.
|
||||||
*/
|
*/
|
||||||
ThisThread::sleep_for(10);
|
ThisThread::sleep_for(10ms);
|
||||||
|
|
||||||
rtc_init();
|
RealTimeClock::init();
|
||||||
|
|
||||||
if (deepsleep_mode == false) {
|
if (deepsleep_mode == false) {
|
||||||
sleep_manager_lock_deep_sleep();
|
sleep_manager_lock_deep_sleep();
|
||||||
}
|
}
|
||||||
|
|
||||||
rtc_write(start);
|
RealTimeClock::write(start);
|
||||||
|
|
||||||
timeout.attach(set_flag_true, DELAY_4S);
|
timeout.attach(set_flag_true, 4s);
|
||||||
|
|
||||||
TEST_ASSERT(sleep_manager_can_deep_sleep_test_check() == deepsleep_mode);
|
TEST_ASSERT(sleep_manager_can_deep_sleep_test_check() == deepsleep_mode);
|
||||||
|
|
||||||
|
@ -81,9 +78,9 @@ void rtc_sleep_test_support(bool deepsleep_mode)
|
||||||
sleep();
|
sleep();
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint32_t stop = rtc_read();
|
const auto stop = RealTimeClock::now();
|
||||||
|
|
||||||
TEST_ASSERT_UINT32_WITHIN(RTC_TOLERANCE, DELAY_4S, stop - start);
|
TEST_ASSERT_DURATION_WITHIN(1s, 4s, stop - start);
|
||||||
|
|
||||||
timeout.detach();
|
timeout.detach();
|
||||||
|
|
||||||
|
@ -91,7 +88,7 @@ void rtc_sleep_test_support(bool deepsleep_mode)
|
||||||
sleep_manager_unlock_deep_sleep();
|
sleep_manager_unlock_deep_sleep();
|
||||||
}
|
}
|
||||||
|
|
||||||
rtc_free();
|
RealTimeClock::free();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -99,10 +96,10 @@ void rtc_sleep_test_support(bool deepsleep_mode)
|
||||||
void rtc_init_test()
|
void rtc_init_test()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
rtc_init();
|
RealTimeClock::init();
|
||||||
}
|
}
|
||||||
|
|
||||||
rtc_free();
|
RealTimeClock::free();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEVICE_LPTICKER
|
#if DEVICE_LPTICKER
|
||||||
|
@ -121,58 +118,57 @@ void rtc_sleep_test()
|
||||||
/* Test that the RTC keeps counting even after ::rtc_free has been called. */
|
/* Test that the RTC keeps counting even after ::rtc_free has been called. */
|
||||||
void rtc_persist_test()
|
void rtc_persist_test()
|
||||||
{
|
{
|
||||||
const uint32_t start = 100;
|
const auto start = RealTimeClock::time_point(100s);
|
||||||
rtc_init();
|
RealTimeClock::init();
|
||||||
rtc_write(start);
|
RealTimeClock::write(start);
|
||||||
rtc_free();
|
RealTimeClock::free();
|
||||||
|
|
||||||
ThisThread::sleep_for(WAIT_TIME * 1000);
|
ThisThread::sleep_for(WAIT_TIME);
|
||||||
|
|
||||||
rtc_init();
|
RealTimeClock::init();
|
||||||
const uint32_t stop = rtc_read();
|
const auto stop = RealTimeClock::now();
|
||||||
const int enabled = rtc_isenabled();
|
const bool enabled = RealTimeClock::isenabled();
|
||||||
rtc_free();
|
RealTimeClock::free();
|
||||||
|
|
||||||
TEST_ASSERT_TRUE(enabled);
|
TEST_ASSERT_TRUE(enabled);
|
||||||
TEST_ASSERT_UINT32_WITHIN(WAIT_TOLERANCE, WAIT_TIME, stop - start);
|
TEST_ASSERT_DURATION_WITHIN(WAIT_TOLERANCE, WAIT_TIME, stop - start);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Test time does not glitch backwards due to an incorrectly implemented ripple counter driver. */
|
/* Test time does not glitch backwards due to an incorrectly implemented ripple counter driver. */
|
||||||
void rtc_glitch_test()
|
void rtc_glitch_test()
|
||||||
{
|
{
|
||||||
const uint32_t start = 0xffffe;
|
const auto start = RealTimeClock::time_point(0xffffes);
|
||||||
rtc_init();
|
RealTimeClock::init();
|
||||||
|
|
||||||
rtc_write(start);
|
RealTimeClock::write(start);
|
||||||
uint32_t last = start;
|
auto last = start;
|
||||||
while (last < start + 4) {
|
while (last < start + 4s) {
|
||||||
const uint32_t cur = rtc_read();
|
const auto cur = RealTimeClock::now();
|
||||||
TEST_ASSERT(cur >= last);
|
TEST_ASSERT(cur >= last);
|
||||||
last = cur;
|
last = cur;
|
||||||
}
|
}
|
||||||
|
|
||||||
rtc_free();
|
RealTimeClock::free();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Test that the RTC correctly handles different time values. */
|
/* Test that the RTC correctly handles different time values. */
|
||||||
void rtc_range_test()
|
void rtc_range_test()
|
||||||
{
|
{
|
||||||
static const uint32_t starts[] = {
|
static const RealTimeClock::time_point starts[] {
|
||||||
0x00000000,
|
0x00000000s,
|
||||||
0xEFFFFFFF,
|
0xEFFFFFFFs,
|
||||||
0x00001000,
|
0x00001000s,
|
||||||
0x00010000,
|
0x00010000s,
|
||||||
};
|
};
|
||||||
|
|
||||||
rtc_init();
|
RealTimeClock::init();
|
||||||
for (uint32_t i = 0; i < sizeof(starts) / sizeof(starts[0]); i++) {
|
for (const auto &start : starts) {
|
||||||
const uint32_t start = starts[i];
|
RealTimeClock::write(start);
|
||||||
rtc_write(start);
|
ThisThread::sleep_for(WAIT_TIME);
|
||||||
ThisThread::sleep_for(WAIT_TIME * 1000);
|
const auto stop = RealTimeClock::now();
|
||||||
const uint32_t stop = rtc_read();
|
TEST_ASSERT_DURATION_WITHIN(WAIT_TOLERANCE, WAIT_TIME, stop - start);
|
||||||
TEST_ASSERT_UINT32_WITHIN(WAIT_TOLERANCE, WAIT_TIME, stop - start);
|
|
||||||
}
|
}
|
||||||
rtc_free();
|
RealTimeClock::free();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Test that the RTC accuracy is at least 10%. */
|
/* Test that the RTC accuracy is at least 10%. */
|
||||||
|
@ -180,44 +176,40 @@ void rtc_accuracy_test()
|
||||||
{
|
{
|
||||||
Timer timer1;
|
Timer timer1;
|
||||||
|
|
||||||
const uint32_t start = 100;
|
const auto start = RealTimeClock::time_point(100s);
|
||||||
rtc_init();
|
RealTimeClock::init();
|
||||||
rtc_write(start);
|
RealTimeClock::write(start);
|
||||||
|
|
||||||
timer1.start();
|
timer1.start();
|
||||||
while (rtc_read() < (start + DELAY_10S)) {
|
while (RealTimeClock::now() < (start + 10s)) {
|
||||||
/* Just wait. */
|
/* Just wait. */
|
||||||
}
|
}
|
||||||
timer1.stop();
|
timer1.stop();
|
||||||
|
|
||||||
/* RTC accuracy is at least 10%. */
|
/* RTC accuracy is at least 10%. */
|
||||||
TEST_ASSERT_INT32_WITHIN(TOLERANCE_ACCURACY_US, DELAY_10S * US_PER_SEC, timer1.read_us());
|
TEST_ASSERT_DURATION_WITHIN(1s, 10s, timer1.read_duration());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Test that ::rtc_write/::rtc_read functions provides availability to set/get RTC time. */
|
/* Test that ::rtc_write/::rtc_read functions provides availability to set/get RTC time. */
|
||||||
void rtc_write_read_test()
|
void rtc_write_read_test()
|
||||||
{
|
{
|
||||||
static const uint32_t rtc_init_val = 100;
|
RealTimeClock::init();
|
||||||
|
|
||||||
rtc_init();
|
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
const uint32_t init_val = (rtc_init_val + i * rtc_init_val);
|
|
||||||
|
|
||||||
|
for (auto init_val = RealTimeClock::time_point(100s); init_val < RealTimeClock::time_point(400s); init_val += 100s) {
|
||||||
core_util_critical_section_enter();
|
core_util_critical_section_enter();
|
||||||
|
|
||||||
rtc_write(init_val);
|
RealTimeClock::write(init_val);
|
||||||
const uint32_t read_val = rtc_read();
|
const auto read_val = RealTimeClock::now();
|
||||||
|
|
||||||
core_util_critical_section_exit();
|
core_util_critical_section_exit();
|
||||||
|
|
||||||
/* No tolerance is provided since we should have 1 second to
|
/* No tolerance is provided since we should have 1 second to
|
||||||
* execute this case after the RTC time is set.
|
* execute this case after the RTC time is set.
|
||||||
*/
|
*/
|
||||||
TEST_ASSERT_EQUAL_UINT32(init_val, read_val);
|
TEST_ASSERT(init_val == read_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
rtc_free();
|
RealTimeClock::free();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Test that ::is_enabled function returns 1 if the RTC is counting and the time has been set. */
|
/* Test that ::is_enabled function returns 1 if the RTC is counting and the time has been set. */
|
||||||
|
@ -228,10 +220,10 @@ void rtc_enabled_test()
|
||||||
* and RTC time is set.
|
* and RTC time is set.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
rtc_init();
|
RealTimeClock::init();
|
||||||
rtc_write(0);
|
RealTimeClock::write(RealTimeClock::time_point(0));
|
||||||
TEST_ASSERT_EQUAL_INT(1, rtc_isenabled());
|
TEST_ASSERT_TRUE(RealTimeClock::isenabled());
|
||||||
rtc_free();
|
RealTimeClock::free();
|
||||||
}
|
}
|
||||||
|
|
||||||
Case cases[] = {
|
Case cases[] = {
|
||||||
|
|
|
@ -0,0 +1,144 @@
|
||||||
|
/* mbed Microcontroller Library
|
||||||
|
* Copyright (c) 2006-2019 ARM Limited
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* 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_REALTIMECLOCK_H
|
||||||
|
#define MBED_REALTIMECLOCK_H
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include "hal/rtc_api.h"
|
||||||
|
|
||||||
|
namespace mbed {
|
||||||
|
/**
|
||||||
|
* \defgroup drivers_RealTimeClock RealTimeClock class
|
||||||
|
* \ingroup drivers-public-api-ticker
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of a C++11 Clock representing the HAL real-time clock.
|
||||||
|
*
|
||||||
|
* This is intended to be usable as a substitute for @c std::chrono::system_clock,
|
||||||
|
* but is provided as a separate class, due to inconsistencies in retargetting
|
||||||
|
* support between toolchains.
|
||||||
|
*
|
||||||
|
* This is only a thin wrapper around the HAL RTC API, and as such it requires the
|
||||||
|
* same use of initialization.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class RealTimeClock {
|
||||||
|
public:
|
||||||
|
using duration = std::chrono::seconds;
|
||||||
|
using rep = duration::rep;
|
||||||
|
using period = duration::period;
|
||||||
|
using time_point = std::chrono::time_point<RealTimeClock>;
|
||||||
|
static const bool is_steady = false;
|
||||||
|
|
||||||
|
/** Initialize the RTC peripheral
|
||||||
|
*
|
||||||
|
* Power up the RTC in preparation for access. This function must be called
|
||||||
|
* before any other RealTimeClock methods are called. This does not change the state
|
||||||
|
* of the RTC. It just enables access to it.
|
||||||
|
*
|
||||||
|
* @see ::rtc_init
|
||||||
|
*/
|
||||||
|
static void init()
|
||||||
|
{
|
||||||
|
return rtc_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Deinitialize RTC
|
||||||
|
*
|
||||||
|
* Powerdown the RTC in preparation for sleep, powerdown or reset. That should only
|
||||||
|
* affect the CPU domain and not the time keeping logic.
|
||||||
|
* After this function is called no other RealTimeClock methods should be called
|
||||||
|
* except for RealTimeClock::init.
|
||||||
|
*
|
||||||
|
* @see ::rtc_free
|
||||||
|
*/
|
||||||
|
static void free()
|
||||||
|
{
|
||||||
|
return rtc_free();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check if the RTC has the time set and is counting
|
||||||
|
*
|
||||||
|
* @retval false The time reported by the RTC is not valid
|
||||||
|
* @retval true The time has been set and the RTC is counting
|
||||||
|
*
|
||||||
|
* @see ::rtc_isenabled
|
||||||
|
*/
|
||||||
|
static bool isenabled() noexcept
|
||||||
|
{
|
||||||
|
return bool(rtc_isenabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get the current time from the RTC peripheral
|
||||||
|
*
|
||||||
|
* @return The current time in seconds
|
||||||
|
*
|
||||||
|
* @see ::rtc_read
|
||||||
|
*/
|
||||||
|
static time_point now() noexcept
|
||||||
|
{
|
||||||
|
return from_time_t(rtc_read());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Write the current time in seconds to the RTC peripheral
|
||||||
|
*
|
||||||
|
* @param t The current time to be set in seconds.
|
||||||
|
*
|
||||||
|
* @see ::rtc_write
|
||||||
|
*/
|
||||||
|
static void write(time_point t) noexcept
|
||||||
|
{
|
||||||
|
rtc_write(to_time_t(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convert a C time_t to C++ Chrono time_point
|
||||||
|
*
|
||||||
|
* @param t a @c time_t object
|
||||||
|
* @return a RealTimeClock::time_point object that represents the same point in time as @p t.
|
||||||
|
*/
|
||||||
|
static time_point from_time_t(time_t t) noexcept
|
||||||
|
{
|
||||||
|
return time_point{std::chrono::duration_cast<duration>(std::chrono::duration<time_t>{t})};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convert a C++ Chrono time_point to a C time_t
|
||||||
|
*
|
||||||
|
* @param t a RealTimeClock::time_point object
|
||||||
|
* @return a @c time_t object that represents the same point in time as @p t.
|
||||||
|
*/
|
||||||
|
static time_t to_time_t(const time_point &t) noexcept
|
||||||
|
{
|
||||||
|
return std::chrono::duration_cast<std::chrono::duration<time_t>>(t.time_since_epoch()).count();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Lock the clock to ensure it stays running; dummy for API compatibility with HighResClock */
|
||||||
|
static void lock()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Unlock the clock, allowing it to stop during power saving; dummy for API compatibility with HighResClock */
|
||||||
|
static void unlock()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @}*/
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif /* MBED_TICKERCLOCK_H */
|
1
mbed.h
1
mbed.h
|
@ -84,6 +84,7 @@
|
||||||
#include "drivers/LowPowerTimeout.h"
|
#include "drivers/LowPowerTimeout.h"
|
||||||
#include "drivers/LowPowerTicker.h"
|
#include "drivers/LowPowerTicker.h"
|
||||||
#include "drivers/LowPowerTimer.h"
|
#include "drivers/LowPowerTimer.h"
|
||||||
|
#include "drivers/RealTimeClock.h"
|
||||||
#include "platform/LocalFileSystem.h"
|
#include "platform/LocalFileSystem.h"
|
||||||
#include "drivers/InterruptIn.h"
|
#include "drivers/InterruptIn.h"
|
||||||
#include "platform/mbed_wait_api.h"
|
#include "platform/mbed_wait_api.h"
|
||||||
|
|
Loading…
Reference in New Issue