mirror of https://github.com/ARMmbed/mbed-os.git
Add documentation and test the HAL RTC API
Add requirements, tests, an example implementation and additional function documentation to the HAL RTC API.pull/7009/head
parent
a0a9626420
commit
1f97f11373
|
@ -0,0 +1,119 @@
|
|||
"""
|
||||
mbed SDK
|
||||
Copyright (c) 2017-2017 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.
|
||||
"""
|
||||
from __future__ import print_function
|
||||
|
||||
from mbed_host_tests import BaseHostTest
|
||||
from time import sleep
|
||||
|
||||
|
||||
class RtcResetTest(BaseHostTest):
|
||||
"""This test checks that a device's RTC keeps count through a reset
|
||||
|
||||
It does this by setting the RTC's time, triggering a reset,
|
||||
delaying and then reading the RTC's time again to ensure
|
||||
that the RTC is still counting.
|
||||
"""
|
||||
|
||||
"""Start of the RTC"""
|
||||
START_TIME = 50000
|
||||
START_TIME_TOLERANCE = 10
|
||||
"""Time to delay after sending reset"""
|
||||
DELAY_TIME = 4.0
|
||||
DELAY_TOLERANCE = 1.0
|
||||
VALUE_PLACEHOLDER = "0"
|
||||
|
||||
def setup(self):
|
||||
"""Register callbacks required for the test"""
|
||||
self._error = False
|
||||
generator = self.rtc_reset_test()
|
||||
generator.next()
|
||||
|
||||
def run_gen(key, value, time):
|
||||
"""Run the generator, and fail testing if the iterator stops"""
|
||||
if self._error:
|
||||
return
|
||||
try:
|
||||
print("Calling generator")
|
||||
generator.send((key, value, time))
|
||||
except StopIteration:
|
||||
self._error = True
|
||||
|
||||
for resp in ("start", "read"):
|
||||
self.register_callback(resp, run_gen)
|
||||
|
||||
def teardown(self):
|
||||
"""No work to do here"""
|
||||
pass
|
||||
|
||||
def rtc_reset_test(self):
|
||||
"""Generator for running the reset test
|
||||
|
||||
This function calls yield to wait for the next event from
|
||||
the device. If the device gives the wrong response, then the
|
||||
generator terminates by returing which raises a StopIteration
|
||||
exception and fails the test.
|
||||
"""
|
||||
|
||||
# Wait for start token
|
||||
key, value, time = yield
|
||||
if key != "start":
|
||||
return
|
||||
|
||||
# Initialize, and set the time
|
||||
self.send_kv("init", self.VALUE_PLACEHOLDER)
|
||||
self.send_kv("write", str(self.START_TIME))
|
||||
self.send_kv("read", self.VALUE_PLACEHOLDER)
|
||||
key, value, time = yield
|
||||
if key != "read":
|
||||
return
|
||||
dev_time_start = int(value)
|
||||
|
||||
# Unitialize, and reset
|
||||
self.send_kv("free", self.VALUE_PLACEHOLDER)
|
||||
self.send_kv("reset", self.VALUE_PLACEHOLDER)
|
||||
sleep(self.DELAY_TIME)
|
||||
|
||||
# Restart the test, and send the sync token
|
||||
self.send_kv("__sync", "00000000-0000-000000000-000000000000")
|
||||
key, value, time = yield
|
||||
if key != "start":
|
||||
return
|
||||
|
||||
# Initialize, and read the time
|
||||
self.send_kv("init", self.VALUE_PLACEHOLDER)
|
||||
self.send_kv("read", self.VALUE_PLACEHOLDER)
|
||||
key, value, time = yield
|
||||
if key != "read":
|
||||
return
|
||||
dev_time_end = int(value)
|
||||
|
||||
# Check result
|
||||
elapsed = dev_time_end - dev_time_start
|
||||
start_time_valid = (self.START_TIME <= dev_time_start <
|
||||
self.START_TIME + self.START_TIME_TOLERANCE)
|
||||
elapsed_time_valid = elapsed >= self.DELAY_TIME - self.DELAY_TOLERANCE
|
||||
passed = start_time_valid and elapsed_time_valid
|
||||
if not start_time_valid:
|
||||
self.log("FAIL: Expected start time of %i got %i" %
|
||||
(self.START_TIME, dev_time_start))
|
||||
elif not passed:
|
||||
self.log("FAIL: Delayed for %fs but device "
|
||||
"reported elapsed time of %fs" %
|
||||
(self.DELAY_TIME, elapsed))
|
||||
self.send_kv("exit", "pass" if passed else "fail")
|
||||
yield # No more events expected
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017 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.
|
||||
*/
|
||||
|
||||
#if !DEVICE_RTC
|
||||
#error [NOT_SUPPORTED] RTC API not supported for this target
|
||||
#endif
|
||||
|
||||
#include "utest/utest.h"
|
||||
#include "unity/unity.h"
|
||||
#include "greentea-client/test_env.h"
|
||||
#include "rtc_test.h"
|
||||
|
||||
#include "mbed.h"
|
||||
#include "rtc_api.h"
|
||||
|
||||
|
||||
using namespace utest::v1;
|
||||
|
||||
static const uint32_t WAIT_TIME = 4;
|
||||
static const uint32_t WAIT_TOLERANCE = 1;
|
||||
|
||||
|
||||
void rtc_init_test()
|
||||
{
|
||||
for (int i = 0; i < 10; i++) {
|
||||
rtc_init();
|
||||
}
|
||||
}
|
||||
|
||||
void rtc_sleep_test()
|
||||
{
|
||||
const uint32_t start = 100;
|
||||
rtc_init();
|
||||
|
||||
rtc_write(start);
|
||||
wait(WAIT_TIME);
|
||||
const uint32_t stop = rtc_read();
|
||||
|
||||
rtc_free();
|
||||
|
||||
TEST_ASSERT_UINT32_WITHIN(WAIT_TOLERANCE, WAIT_TIME, stop - start);
|
||||
}
|
||||
|
||||
void rtc_persist_test()
|
||||
{
|
||||
const uint32_t start = 100;
|
||||
rtc_init();
|
||||
rtc_write(start);
|
||||
rtc_free();
|
||||
|
||||
wait(WAIT_TIME);
|
||||
|
||||
rtc_init();
|
||||
const uint32_t stop = rtc_read();
|
||||
const int enabled = rtc_isenabled();
|
||||
rtc_free();
|
||||
|
||||
TEST_ASSERT_TRUE(enabled);
|
||||
TEST_ASSERT_UINT32_WITHIN(WAIT_TOLERANCE, WAIT_TIME, stop - start);
|
||||
}
|
||||
|
||||
void rtc_glitch_test()
|
||||
{
|
||||
const uint32_t start = 0xffffe;
|
||||
rtc_init();
|
||||
|
||||
rtc_write(start);
|
||||
uint32_t last = start;
|
||||
while (last < start + 4) {
|
||||
const uint32_t cur = rtc_read();
|
||||
TEST_ASSERT(cur >= last);
|
||||
last = cur;
|
||||
}
|
||||
|
||||
rtc_free();
|
||||
}
|
||||
|
||||
void rtc_range_test()
|
||||
{
|
||||
static const uint32_t starts[] = {
|
||||
0x00000000,
|
||||
0xEFFFFFFF,
|
||||
0x00001000,
|
||||
};
|
||||
|
||||
rtc_init();
|
||||
for (uint32_t i = 0; i < sizeof(starts) / sizeof(starts[0]); i++) {
|
||||
const uint32_t start = starts[i];
|
||||
rtc_write(start);
|
||||
wait(WAIT_TIME);
|
||||
const uint32_t stop = rtc_read();
|
||||
TEST_ASSERT_UINT32_WITHIN(WAIT_TOLERANCE, WAIT_TIME, stop - start);
|
||||
}
|
||||
rtc_free();
|
||||
}
|
||||
|
||||
Case cases[] = {
|
||||
Case("RTC - init", rtc_init_test),
|
||||
Case("RTC - sleep", rtc_sleep_test),
|
||||
Case("RTC - persist", rtc_persist_test),
|
||||
Case("RTC - glitch", rtc_glitch_test),
|
||||
Case("RTC - range", rtc_range_test),
|
||||
};
|
||||
|
||||
utest::v1::status_t greentea_test_setup(const size_t number_of_cases) {
|
||||
GREENTEA_SETUP(30, "default_auto");
|
||||
return greentea_test_setup_handler(number_of_cases);
|
||||
}
|
||||
|
||||
Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);
|
||||
|
||||
int main() {
|
||||
Harness::run(specification);
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/** \addtogroup hal_rtc_tests
|
||||
* @{
|
||||
*/
|
||||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017-2017 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_RTC_TEST_H
|
||||
#define MBED_RTC_TEST_H
|
||||
|
||||
#if DEVICE_RTC
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Test that ::rtc_init can be called multiple times
|
||||
*
|
||||
*/
|
||||
void rtc_init_test(void);
|
||||
|
||||
/** Test that the RTC keeps counting in the various sleep modes
|
||||
*
|
||||
*/
|
||||
void rtc_sleep_test(void);
|
||||
|
||||
/** Test that the RTC keeps counting even after ::rtc_free has been called
|
||||
*
|
||||
*/
|
||||
void rtc_persist_test(void);
|
||||
|
||||
/** Test time does not glitch backwards due to an incorrectly implemented ripple counter driver
|
||||
*
|
||||
*/
|
||||
void rtc_glitch_test(void);
|
||||
|
||||
/** Test that the RTC correctly handles large time values
|
||||
*
|
||||
*/
|
||||
void rtc_range_test(void);
|
||||
|
||||
/**@}*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/** @}*/
|
|
@ -0,0 +1,98 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017 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.
|
||||
*/
|
||||
|
||||
#if !DEVICE_RTC
|
||||
#error [NOT_SUPPORTED] RTC API not supported for this target
|
||||
#endif
|
||||
|
||||
#include "utest/utest.h"
|
||||
#include "unity/unity.h"
|
||||
#include "greentea-client/test_env.h"
|
||||
#include "rtc_reset_test.h"
|
||||
|
||||
#include "mbed.h"
|
||||
#include "rtc_api.h"
|
||||
|
||||
|
||||
typedef enum {
|
||||
CMD_STATUS_PASS,
|
||||
CMD_STATUS_FAIL,
|
||||
CMD_STATUS_CONTINUE,
|
||||
CMD_STATUS_ERROR
|
||||
} cmd_status_t;
|
||||
|
||||
static cmd_status_t handle_command(const char *key, const char *value)
|
||||
{
|
||||
if (strcmp(key, "init") == 0) {
|
||||
rtc_init();
|
||||
return CMD_STATUS_CONTINUE;
|
||||
|
||||
} else if (strcmp(key, "free") == 0) {
|
||||
rtc_free();
|
||||
return CMD_STATUS_CONTINUE;
|
||||
|
||||
} else if (strcmp(key, "read") == 0) {
|
||||
static char time_buf[64];
|
||||
memset(time_buf, 0, sizeof(time_buf));
|
||||
sprintf(time_buf, "%lu", (uint32_t)rtc_read());
|
||||
greentea_send_kv("read", time_buf);
|
||||
return CMD_STATUS_CONTINUE;
|
||||
|
||||
} else if (strcmp(key, "write") == 0) {
|
||||
uint32_t time;
|
||||
sscanf(value, "%lu", &time);
|
||||
rtc_write(time);
|
||||
return CMD_STATUS_CONTINUE;
|
||||
|
||||
} else if (strcmp(key, "reset") == 0) {
|
||||
NVIC_SystemReset();
|
||||
// Code shouldn't read this due to the reset
|
||||
return CMD_STATUS_ERROR;
|
||||
|
||||
} else if (strcmp(key, "exit") == 0) {
|
||||
return strcmp(value, "pass") == 0 ? CMD_STATUS_PASS : CMD_STATUS_FAIL;
|
||||
|
||||
} else {
|
||||
return CMD_STATUS_ERROR;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void rtc_reset_test()
|
||||
{
|
||||
GREENTEA_SETUP(60, "rtc_reset");
|
||||
|
||||
static char _key[10 + 1] = {};
|
||||
static char _value[128 + 1] = {};
|
||||
|
||||
greentea_send_kv("start", 1);
|
||||
|
||||
// Handshake with host
|
||||
cmd_status_t cmd_status = CMD_STATUS_CONTINUE;
|
||||
while (CMD_STATUS_CONTINUE == cmd_status) {
|
||||
memset(_key, 0, sizeof(_key));
|
||||
memset(_value, 0, sizeof(_value));
|
||||
greentea_parse_kv(_key, _value, sizeof(_key) - 1, sizeof(_value) - 1);
|
||||
cmd_status = handle_command(_key, _value);
|
||||
}
|
||||
|
||||
GREENTEA_TESTSUITE_RESULT(CMD_STATUS_PASS == cmd_status);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
rtc_reset_test();
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017-2017 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_RTC_TEST_H
|
||||
#define MBED_RTC_TEST_H
|
||||
|
||||
#if DEVICE_RTC
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Test that the RTC does not stop counting after a software reset
|
||||
* \ingroup hal_rtc_tests
|
||||
*/
|
||||
void rtc_reset_test();
|
||||
|
||||
/**@}*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/** @}*/
|
124
hal/rtc_api.h
124
hal/rtc_api.h
|
@ -28,37 +28,145 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
/**
|
||||
* \defgroup hal_rtc RTC hal functions
|
||||
* \defgroup hal_rtc RTC hal
|
||||
*
|
||||
* The RTC hal provides a low level interface to the Real Time Counter (RTC) of a
|
||||
* target.
|
||||
*
|
||||
* # Defined behavior
|
||||
* * The function ::rtc_init is safe to call repeatedly - Verified by test ::rtc_init_test
|
||||
* * RTC accuracy is at least 10% - Not verified
|
||||
* * Init/free doesn't stop RTC from counting - Verified by test ::rtc_persist_test
|
||||
* * Software reset doesn't stop RTC from counting - Verified by ::rtc_reset_test
|
||||
* * Sleep modes don't stop RTC from counting - Verified by ::rtc_sleep_test
|
||||
* * Shutdown mode doesn't stop RTC from counting - Not verified
|
||||
*
|
||||
* # Undefined behavior
|
||||
* * Calling any function other than ::rtc_init before the initialization of the RTC
|
||||
*
|
||||
* # Potential bugs
|
||||
* * Incorrect overflow handling - Verified by ::rtc_range_test
|
||||
* * Glitches due to ripple counter - Verified by ::rtc_glitch_test
|
||||
*
|
||||
* @see hal_rtc_tests
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \defgroup hal_rtc_tests RTC hal tests
|
||||
* The RTC test validate proper implementation of the RTC hal.
|
||||
*
|
||||
* To run the RTC hal tests use the command:
|
||||
*
|
||||
* mbed test -t <toolchain> -m <target> -n tests-mbed_hal-rtc*
|
||||
*/
|
||||
|
||||
|
||||
/** Initialize the RTC peripheral
|
||||
*
|
||||
* Powerup the RTC in perpetration for access. This function must be called
|
||||
* before any other RTC functions ares called. This does not change the state
|
||||
* of the RTC. It just enables access to it.
|
||||
*
|
||||
* @note This function is safe to call repeatedly - Tested by ::rtc_init_test
|
||||
*
|
||||
* Pseudo Code:
|
||||
* @code
|
||||
* void rtc_init()
|
||||
* {
|
||||
* // Enable clock gate so processor can read RTC registers
|
||||
* POWER_CTRL |= POWER_CTRL_RTC_Msk;
|
||||
*
|
||||
* // See if the RTC is already setup
|
||||
* if (!(RTC_STATUS & RTC_STATUS_COUNTING_Msk)) {
|
||||
*
|
||||
* // Setup the RTC clock source
|
||||
* RTC_CTRL |= RTC_CTRL_CLK32_Msk;
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
void rtc_init(void);
|
||||
|
||||
/** Deinitialize RTC
|
||||
*
|
||||
* TODO: The function is not used by rtc api in mbed-drivers.
|
||||
* Powerdown the RTC in preparation for sleep, powerdown or reset.
|
||||
* After this function is called no other RTC functions should be called
|
||||
* except for ::rtc_init.
|
||||
*
|
||||
* @note This function does not stop the RTC from counting - Tested by ::rtc_persist_test
|
||||
*
|
||||
* Pseudo Code:
|
||||
* @code
|
||||
* void rtc_free()
|
||||
* {
|
||||
* // Disable clock gate since processor no longer needs to read RTC registers
|
||||
* POWER_CTRL &= ~POWER_CTRL_RTC_Msk;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
void rtc_free(void);
|
||||
|
||||
/** Get the RTC enable status
|
||||
/** Check if the RTC has the time set and is counting
|
||||
*
|
||||
* @retval 0 disabled
|
||||
* @retval 1 enabled
|
||||
* @retval 0 The time reported by the RTC is not valid
|
||||
* @retval 1 The time has been set the RTC is counting
|
||||
*
|
||||
* Pseudo Code:
|
||||
* @code
|
||||
* int rtc_isenabled()
|
||||
* {
|
||||
* if (RTC_STATUS & RTC_STATUS_COUNTING_Msk) {
|
||||
* return 1;
|
||||
* } else {
|
||||
* return 0;
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
int rtc_isenabled(void);
|
||||
|
||||
/** Get the current time from the RTC peripheral
|
||||
*
|
||||
* @return The current time
|
||||
* @return The current time in seconds
|
||||
*
|
||||
* @note Some RTCs are not synchronized with the main clock. If
|
||||
* this is the case with your RTC then you must read the RTC time
|
||||
* in a loop to prevent reading the wrong time due to a glitch.
|
||||
* The test ::rtc_glitch_test is intended to catch this bug.
|
||||
*
|
||||
* Example implementation for an unsynchronized ripple counter:
|
||||
* @code
|
||||
* time_t rtc_read()
|
||||
* {
|
||||
* uint32_t val;
|
||||
* uint32_t last_val;
|
||||
*
|
||||
* // Loop until the same value is read twice
|
||||
* val = RTC_SECONDS;
|
||||
* do {
|
||||
* last_val = val;
|
||||
* val = RTC_SECONDS;
|
||||
* } while (last_val != val);
|
||||
*
|
||||
* return (time_t)val;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
time_t rtc_read(void);
|
||||
|
||||
/** Set the current time to the RTC peripheral
|
||||
/** Write the current time in seconds to the RTC peripheral
|
||||
*
|
||||
* @param t The current time to be set
|
||||
* @param t The current time to be set in seconds.
|
||||
*
|
||||
* Pseudo Code:
|
||||
* @code
|
||||
* void rtc_write(time_t t)
|
||||
* {
|
||||
* RTC_SECONDS = t;
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
void rtc_write(time_t t);
|
||||
|
||||
|
|
Loading…
Reference in New Issue