Add RealTimeClock

pull/12425/head
Kevin Bracey 2020-02-25 14:34:14 +02:00
parent 0eff3340d2
commit f0ee31f119
3 changed files with 207 additions and 70 deletions

View File

@ -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[] = {

144
drivers/RealTimeClock.h Normal file
View File

@ -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
View File

@ -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"