diff --git a/TESTS/mbed_hal/rtc_time/main.cpp b/TESTS/mbed_hal/rtc_time/main.cpp new file mode 100644 index 00000000000..8082519f2f7 --- /dev/null +++ b/TESTS/mbed_hal/rtc_time/main.cpp @@ -0,0 +1,245 @@ +/* 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. + */ + +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" + +#include "mbed.h" +#include "mbed_mktime.h" + +using namespace utest::v1; + +/* + * regular is_leap_year, see platform/mbed_mktime.c for the optimized version + */ +bool is_leap_year(int year) { + year = 1900 + year; + if (year % 4) { + return false; + } else if (year % 100) { + return true; + } else if (year % 400) { + return false; + } + return true; +} + +/* + * Test the optimized version of _rtc_is_leap_year against the generic version. + */ +void test_is_leap_year() { + for (int i = 70; i < 138; ++i) { + bool expected = is_leap_year(i); + bool actual_value = _rtc_is_leap_year(i); + + if (expected != actual_value) { + printf ("leap year failed with i = %d\r\n", i); + } + TEST_ASSERT_EQUAL(expected, actual_value); + } +} + +struct tm make_time_info(int year, int month, int day, int hours, int minutes, int seconds) { + struct tm timeinfo; + timeinfo.tm_year = year; + timeinfo.tm_mon = month; + timeinfo.tm_mday = day; + timeinfo.tm_hour = hours; + timeinfo.tm_min = minutes; + timeinfo.tm_sec = seconds; + return timeinfo; +} + +/* + * test out of range values for _rtc_mktime. + * The function operates from the 1st of january 1970 at 00:00:00 to the 19th + * of january 2038 at 03:14:07. + */ +void test_mk_time_out_of_range() { + tm invalid_lower_bound = make_time_info( + 69, + 11, + 31, + 23, + 59, + 59 + ); + + tm valid_lower_bound = make_time_info( + 70, + 0, + 1, + 0, + 0, + 0 + ); + + tm valid_upper_bound = make_time_info( + 138, + 0, + 19, + 3, + 14, + 7 + ); + + tm invalid_upper_bound = make_time_info( + 138, + 0, + 19, + 3, + 14, + 8 + ); + + TEST_ASSERT_EQUAL_INT(((time_t) -1), _rtc_mktime(&invalid_lower_bound)); + TEST_ASSERT_EQUAL_INT(((time_t) 0), _rtc_mktime(&valid_lower_bound)); + TEST_ASSERT_EQUAL_INT(((time_t) INT_MAX), _rtc_mktime(&valid_upper_bound)); + TEST_ASSERT_EQUAL_INT(((time_t) -1), _rtc_mktime(&invalid_upper_bound)); +} + +/* + * test mktime over a large set of values + */ +void test_mk_time() { + for (size_t year = 70; year < 137; ++year) { + for (size_t month = 0; month < 12; ++month) { + for (size_t day = 1; day < 32; ++day) { + if (month == 1 && is_leap_year(year) && day == 29) { + break; + } else if(month == 1 && !is_leap_year(year) && day == 28) { + break; + } else if ( + day == 31 && + (month == 3 || month == 5 || month == 8 || month == 10) + ) { + break; + } + + for (size_t hour = 0; hour < 24; ++hour) { + tm time_info = make_time_info( + year, + month, + day, + hour, + hour % 2 ? 59 : 0, + hour % 2 ? 59 : 0 + ); + + time_t expected = mktime(&time_info); + time_t actual_value = _rtc_mktime(&time_info); + + char msg[128] = ""; + if (expected != actual_value) { + snprintf( + msg, sizeof(msg), + "year = %d, month = %d, day = %d, diff = %ld", + year, month, day, expected - actual_value + ); + } + + TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected, actual_value, msg); + } + } + } + } +} + +/* + * test value out of range for localtime + */ +void test_local_time_limit() { + struct tm dummy_value; + TEST_ASSERT_FALSE(_rtc_localtime((time_t) -1, &dummy_value)); + TEST_ASSERT_FALSE(_rtc_localtime((time_t) INT_MIN, &dummy_value)); +} + +/* + * test _rtc_localtime over a large set of values. + */ +void test_local_time() { + for (uint32_t i = 0; i < INT_MAX; i += 3451) { + time_t copy = (time_t) i; + struct tm* expected = localtime(©); + struct tm actual_value; + bool result = _rtc_localtime((time_t) i, &actual_value); + + if ( + expected->tm_sec != actual_value.tm_sec || + expected->tm_min != actual_value.tm_min || + expected->tm_hour != actual_value.tm_hour || + expected->tm_mday != actual_value.tm_mday || + expected->tm_mon != actual_value.tm_mon || + expected->tm_year != actual_value.tm_year || + expected->tm_wday != actual_value.tm_wday || + expected->tm_yday != actual_value.tm_yday || + result == false + ) { + printf("error: i = %lu\r\n", i); + } + + TEST_ASSERT_TRUE(result); + TEST_ASSERT_EQUAL_UINT32_MESSAGE( + expected->tm_sec, actual_value.tm_sec, "invalid seconds" + ); + TEST_ASSERT_EQUAL_UINT32_MESSAGE( + expected->tm_min, actual_value.tm_min, "invalid minutes" + ); + TEST_ASSERT_EQUAL_UINT32_MESSAGE( + expected->tm_hour, actual_value.tm_hour, "invalid hours" + ); + TEST_ASSERT_EQUAL_UINT32_MESSAGE( + expected->tm_mday, actual_value.tm_mday, "invalid day" + ); + TEST_ASSERT_EQUAL_UINT32_MESSAGE( + expected->tm_mon, actual_value.tm_mon, "invalid month" + ); + TEST_ASSERT_EQUAL_UINT32_MESSAGE( + expected->tm_year, actual_value.tm_year, "invalid year" + ); + TEST_ASSERT_EQUAL_UINT32_MESSAGE( + expected->tm_wday, actual_value.tm_wday, "invalid weekday" + ); + TEST_ASSERT_EQUAL_UINT32_MESSAGE( + expected->tm_yday, actual_value.tm_yday, "invalid year day" + ); + } +} + +utest::v1::status_t greentea_failure_handler(const Case *const source, const failure_t reason) { + greentea_case_failure_abort_handler(source, reason); + return STATUS_CONTINUE; +} + +Case cases[] = { + Case("test is leap year", test_is_leap_year, greentea_failure_handler), + Case("test mk time out of range values", test_mk_time_out_of_range, greentea_failure_handler), + Case("mk time", test_mk_time, greentea_failure_handler), + Case("test local time", test_local_time, greentea_failure_handler), + Case("test local time limits", test_local_time_limit, greentea_failure_handler), +}; + +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) { + GREENTEA_SETUP(1200, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); + +int main() { + return Harness::run(specification); +} diff --git a/platform/mbed_mktime.c b/platform/mbed_mktime.c new file mode 100644 index 00000000000..49b4a4a6102 --- /dev/null +++ b/platform/mbed_mktime.c @@ -0,0 +1,165 @@ +/* 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. + */ + +#include "mbed_mktime.h" + +/* + * time constants + */ +#define SECONDS_BY_MINUTES 60 +#define MINUTES_BY_HOUR 60 +#define SECONDS_BY_HOUR (SECONDS_BY_MINUTES * MINUTES_BY_HOUR) +#define HOURS_BY_DAY 24 +#define SECONDS_BY_DAY (SECONDS_BY_HOUR * HOURS_BY_DAY) + +/* + * 2 dimensional array containing the number of seconds elapsed before a given + * month. + * The second index map to the month while the first map to the type of year: + * - 0: non leap year + * - 1: leap year + */ +static const uint32_t seconds_before_month[2][12] = { + { + 0, + 31 * SECONDS_BY_DAY, + (31 + 28) * SECONDS_BY_DAY, + (31 + 28 + 31) * SECONDS_BY_DAY, + (31 + 28 + 31 + 30) * SECONDS_BY_DAY, + (31 + 28 + 31 + 30 + 31) * SECONDS_BY_DAY, + (31 + 28 + 31 + 30 + 31 + 30) * SECONDS_BY_DAY, + (31 + 28 + 31 + 30 + 31 + 30 + 31) * SECONDS_BY_DAY, + (31 + 28 + 31 + 30 + 31 + 30 + 31 + 31) * SECONDS_BY_DAY, + (31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30) * SECONDS_BY_DAY, + (31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31) * SECONDS_BY_DAY, + (31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30) * SECONDS_BY_DAY, + }, + { + 0, + 31 * SECONDS_BY_DAY, + (31 + 29) * SECONDS_BY_DAY, + (31 + 29 + 31) * SECONDS_BY_DAY, + (31 + 29 + 31 + 30) * SECONDS_BY_DAY, + (31 + 29 + 31 + 30 + 31) * SECONDS_BY_DAY, + (31 + 29 + 31 + 30 + 31 + 30) * SECONDS_BY_DAY, + (31 + 29 + 31 + 30 + 31 + 30 + 31) * SECONDS_BY_DAY, + (31 + 29 + 31 + 30 + 31 + 30 + 31 + 31) * SECONDS_BY_DAY, + (31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30) * SECONDS_BY_DAY, + (31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31) * SECONDS_BY_DAY, + (31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30) * SECONDS_BY_DAY, + } +}; + +bool _rtc_is_leap_year(int year) { + /* + * since in practice, the value manipulated by this algorithm lie in the + * range [70 : 138], the algorith can be reduced to: year % 4. + * The algorithm valid over the full range of value is: + + year = 1900 + year; + if (year % 4) { + return false; + } else if (year % 100) { + return true; + } else if (year % 400) { + return false; + } + return true; + + */ + return (year) % 4 ? false : true; +} + +time_t _rtc_mktime(const struct tm* time) { + // partial check for the upper bound of the range + // normalization might happen at the end of the function + // this solution is faster than checking if the input is after the 19th of + // january 2038 at 03:14:07. + if ((time->tm_year < 70) || (time->tm_year > 138)) { + return ((time_t) -1); + } + + uint32_t result = time->tm_sec; + result += time->tm_min * SECONDS_BY_MINUTES; + result += time->tm_hour * SECONDS_BY_HOUR; + result += (time->tm_mday - 1) * SECONDS_BY_DAY; + result += seconds_before_month[_rtc_is_leap_year(time->tm_year)][time->tm_mon]; + + if (time->tm_year > 70) { + // valid in the range [70:138] + uint32_t count_of_leap_days = ((time->tm_year - 1) / 4) - (70 / 4); + result += (((time->tm_year - 70) * 365) + count_of_leap_days) * SECONDS_BY_DAY; + } + + if (result > INT32_MAX) { + return -1; + } + + return result; +} + +bool _rtc_localtime(time_t timestamp, struct tm* time_info) { + if (((int32_t) timestamp) < 0) { + return false; + } + + time_info->tm_sec = timestamp % 60; + timestamp = timestamp / 60; // timestamp in minutes + time_info->tm_min = timestamp % 60; + timestamp = timestamp / 60; // timestamp in hours + time_info->tm_hour = timestamp % 24; + timestamp = timestamp / 24; // timestamp in days; + + // compute the weekday + // The 1st of January 1970 was a Thursday which is equal to 4 in the weekday + // representation ranging from [0:6] + time_info->tm_wday = (timestamp + 4) % 7; + + // years start at 70 + time_info->tm_year = 70; + while (true) { + if (_rtc_is_leap_year(time_info->tm_year) && timestamp >= 366) { + ++time_info->tm_year; + timestamp -= 366; + } else if (!_rtc_is_leap_year(time_info->tm_year) && timestamp >= 365) { + ++time_info->tm_year; + timestamp -= 365; + } else { + // the remaining days are less than a years + break; + } + } + + time_info->tm_yday = timestamp; + + // convert days into seconds and find the current month + timestamp *= SECONDS_BY_DAY; + time_info->tm_mon = 11; + bool leap = _rtc_is_leap_year(time_info->tm_year); + for (uint32_t i = 0; i < 12; ++i) { + if ((uint32_t) timestamp < seconds_before_month[leap][i]) { + time_info->tm_mon = i - 1; + break; + } + } + + // remove month from timestamp and compute the number of days. + // note: unlike other fields, days are not 0 indexed. + timestamp -= seconds_before_month[leap][time_info->tm_mon]; + time_info->tm_mday = (timestamp / SECONDS_BY_DAY) + 1; + + return true; +} diff --git a/platform/mbed_mktime.h b/platform/mbed_mktime.h new file mode 100644 index 00000000000..b28525224c4 --- /dev/null +++ b/platform/mbed_mktime.h @@ -0,0 +1,98 @@ + +/** \addtogroup platform */ +/** @{*/ +/* 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_MKTIME_H +#define MBED_MKTIME_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Compute if a year is a leap year or not. + * + * @param year The year to test it shall be in the range [70:138]. Year 0 is + * translated into year 1900 CE. + * @return true if the year in input is a leap year and false otherwise. + * @note - For use by the HAL only + */ +bool _rtc_is_leap_year(int year); + +/* Convert a calendar time into time since UNIX epoch as a time_t. + * + * This function is a thread safe (partial) replacement for mktime. It is + * tailored around RTC peripherals needs and is not by any mean a complete + * replacement of mktime. + * + * @param calendar_time The calendar time to convert into a time_t since epoch. + * The fields from tm used for the computation are: + * - tm_sec + * - tm_min + * - tm_hour + * - tm_mday + * - tm_mon + * - tm_year + * Other fields are ignored and won't be renormalized by a call to this function. + * A valid calendar time is comprised between the 1st january of 1970 at + * 00:00:00 and the 19th of january 2038 at 03:14:07. + * + * @return The calendar time as seconds since UNIX epoch if the input is in the + * valid range. Otherwise ((time_t) -1). + * + * @note Leap seconds are not supported. + * @note Values in output range from 0 to INT_MAX. + * @note - For use by the HAL only + */ +time_t _rtc_mktime(const struct tm* calendar_time); + +/* Convert a given time in seconds since epoch into calendar time. + * + * This function is a thread safe (partial) replacement for localtime. It is + * tailored around RTC peripherals specification and is not by any means a + * complete of localtime. + * + * @param timestamp The time (in seconds) to convert into calendar time. Valid + * input are in the range [0 : INT32_MAX]. + * @param calendar_time Pointer to the object which will contain the result of + * the conversion. The tm fields filled by this function are: + * - tm_sec + * - tm_min + * - tm_hour + * - tm_mday + * - tm_mon + * - tm_year + * - tm_wday + * - tm_yday + * The object remains untouched if the time in input is invalid. + * @return true if the conversion was successful, false otherwise. + * + * @note - For use by the HAL only + */ +bool _rtc_localtime(time_t timestamp, struct tm* calendar_time); + +#ifdef __cplusplus +} +#endif + +#endif /* MBED_MKTIME_H */ + +/** @}*/ diff --git a/targets/TARGET_Atmel/TARGET_SAM_CortexM4/rtc_api.c b/targets/TARGET_Atmel/TARGET_SAM_CortexM4/rtc_api.c index 663f3164b70..7a8ca2608eb 100644 --- a/targets/TARGET_Atmel/TARGET_SAM_CortexM4/rtc_api.c +++ b/targets/TARGET_Atmel/TARGET_SAM_CortexM4/rtc_api.c @@ -18,6 +18,7 @@ #include "cmsis.h" #include "sysclk.h" #include "rtc.h" +#include "mbed_mktime.h" static int rtc_inited = 0; @@ -70,7 +71,7 @@ time_t rtc_read(void) timeinfo.tm_year = (ul_year - 1900); /* Convert to timestamp */ - time_t t = mktime(&timeinfo); + time_t t = _rtc_mktime(&timeinfo); return t; } @@ -80,19 +81,22 @@ void rtc_write(time_t t) /* Initialize the RTC is not yet initialized */ rtc_init(); } - struct tm *timeinfo = localtime(&t); + struct tm timeinfo; + if (_rtc_localtime(t, &timeinfo) == false) { + return; + } uint32_t ul_hour, ul_minute, ul_second; uint32_t ul_year, ul_month, ul_day, ul_week; - ul_second = timeinfo->tm_sec; - ul_minute = timeinfo->tm_min; - ul_hour = timeinfo->tm_hour; - ul_day = timeinfo->tm_mday; - ul_week = timeinfo->tm_wday; - ul_month = timeinfo->tm_mon; - ul_year = timeinfo->tm_year; + ul_second = timeinfo.tm_sec; + ul_minute = timeinfo.tm_min; + ul_hour = timeinfo.tm_hour; + ul_day = timeinfo.tm_mday; + ul_week = timeinfo.tm_wday; + ul_month = timeinfo.tm_mon; + ul_year = timeinfo.tm_year; /* Set the RTC */ rtc_set_time(RTC, ul_hour, ul_minute, ul_second); rtc_set_date(RTC, ul_year, ul_month, ul_day, ul_week); -} \ No newline at end of file +} diff --git a/targets/TARGET_NUVOTON/TARGET_M451/rtc_api.c b/targets/TARGET_NUVOTON/TARGET_M451/rtc_api.c index d114b7cff12..906604c1a8d 100644 --- a/targets/TARGET_NUVOTON/TARGET_M451/rtc_api.c +++ b/targets/TARGET_NUVOTON/TARGET_M451/rtc_api.c @@ -22,6 +22,7 @@ #include "mbed_error.h" #include "nu_modutil.h" #include "nu_miscutil.h" +#include "mbed_mktime.h" #define YEAR0 1900 //#define EPOCH_YR 1970 @@ -86,7 +87,7 @@ time_t rtc_read(void) timeinfo.tm_sec = rtc_datetime.u32Second; // Convert to timestamp - time_t t = mktime(&timeinfo); + time_t t = _rtc_mktime(&timeinfo); return t; } @@ -98,18 +99,21 @@ void rtc_write(time_t t) } // Convert timestamp to struct tm - struct tm *timeinfo = localtime(&t); + struct tm timeinfo; + if (_rtc_localtime(t, &timeinfo) == false) { + return; + } S_RTC_TIME_DATA_T rtc_datetime; // Convert S_RTC_TIME_DATA_T to struct tm - rtc_datetime.u32Year = timeinfo->tm_year + YEAR0; - rtc_datetime.u32Month = timeinfo->tm_mon + 1; - rtc_datetime.u32Day = timeinfo->tm_mday; - rtc_datetime.u32DayOfWeek = timeinfo->tm_wday; - rtc_datetime.u32Hour = timeinfo->tm_hour; - rtc_datetime.u32Minute = timeinfo->tm_min; - rtc_datetime.u32Second = timeinfo->tm_sec; + rtc_datetime.u32Year = timeinfo.tm_year + YEAR0; + rtc_datetime.u32Month = timeinfo.tm_mon + 1; + rtc_datetime.u32Day = timeinfo.tm_mday; + rtc_datetime.u32DayOfWeek = timeinfo.tm_wday; + rtc_datetime.u32Hour = timeinfo.tm_hour; + rtc_datetime.u32Minute = timeinfo.tm_min; + rtc_datetime.u32Second = timeinfo.tm_sec; rtc_datetime.u32TimeScale = RTC_CLOCK_24; // NOTE: Timing issue with write to RTC registers. This delay is empirical, not rational. diff --git a/targets/TARGET_NUVOTON/TARGET_NUC472/rtc_api.c b/targets/TARGET_NUVOTON/TARGET_NUC472/rtc_api.c index d114b7cff12..906604c1a8d 100644 --- a/targets/TARGET_NUVOTON/TARGET_NUC472/rtc_api.c +++ b/targets/TARGET_NUVOTON/TARGET_NUC472/rtc_api.c @@ -22,6 +22,7 @@ #include "mbed_error.h" #include "nu_modutil.h" #include "nu_miscutil.h" +#include "mbed_mktime.h" #define YEAR0 1900 //#define EPOCH_YR 1970 @@ -86,7 +87,7 @@ time_t rtc_read(void) timeinfo.tm_sec = rtc_datetime.u32Second; // Convert to timestamp - time_t t = mktime(&timeinfo); + time_t t = _rtc_mktime(&timeinfo); return t; } @@ -98,18 +99,21 @@ void rtc_write(time_t t) } // Convert timestamp to struct tm - struct tm *timeinfo = localtime(&t); + struct tm timeinfo; + if (_rtc_localtime(t, &timeinfo) == false) { + return; + } S_RTC_TIME_DATA_T rtc_datetime; // Convert S_RTC_TIME_DATA_T to struct tm - rtc_datetime.u32Year = timeinfo->tm_year + YEAR0; - rtc_datetime.u32Month = timeinfo->tm_mon + 1; - rtc_datetime.u32Day = timeinfo->tm_mday; - rtc_datetime.u32DayOfWeek = timeinfo->tm_wday; - rtc_datetime.u32Hour = timeinfo->tm_hour; - rtc_datetime.u32Minute = timeinfo->tm_min; - rtc_datetime.u32Second = timeinfo->tm_sec; + rtc_datetime.u32Year = timeinfo.tm_year + YEAR0; + rtc_datetime.u32Month = timeinfo.tm_mon + 1; + rtc_datetime.u32Day = timeinfo.tm_mday; + rtc_datetime.u32DayOfWeek = timeinfo.tm_wday; + rtc_datetime.u32Hour = timeinfo.tm_hour; + rtc_datetime.u32Minute = timeinfo.tm_min; + rtc_datetime.u32Second = timeinfo.tm_sec; rtc_datetime.u32TimeScale = RTC_CLOCK_24; // NOTE: Timing issue with write to RTC registers. This delay is empirical, not rational. diff --git a/targets/TARGET_NXP/TARGET_LPC176X/rtc_api.c b/targets/TARGET_NXP/TARGET_LPC176X/rtc_api.c index ccf76f12110..1db7b3c9f52 100644 --- a/targets/TARGET_NXP/TARGET_LPC176X/rtc_api.c +++ b/targets/TARGET_NXP/TARGET_LPC176X/rtc_api.c @@ -14,6 +14,7 @@ * limitations under the License. */ #include "rtc_api.h" +#include "mbed_mktime.h" // ensure rtc is running (unchanged if already running) @@ -88,25 +89,28 @@ time_t rtc_read(void) { timeinfo.tm_year = LPC_RTC->YEAR - 1900; // Convert to timestamp - time_t t = mktime(&timeinfo); + time_t t = _rtc_mktime(&timeinfo); return t; } void rtc_write(time_t t) { // Convert the time in to a tm - struct tm *timeinfo = localtime(&t); + struct tm timeinfo; + if (_rtc_localtime(t, &timeinfo) == false) { + return; + } // Pause clock, and clear counter register (clears us count) LPC_RTC->CCR |= 2; // Set the RTC - LPC_RTC->SEC = timeinfo->tm_sec; - LPC_RTC->MIN = timeinfo->tm_min; - LPC_RTC->HOUR = timeinfo->tm_hour; - LPC_RTC->DOM = timeinfo->tm_mday; - LPC_RTC->MONTH = timeinfo->tm_mon + 1; - LPC_RTC->YEAR = timeinfo->tm_year + 1900; + LPC_RTC->SEC = timeinfo.tm_sec; + LPC_RTC->MIN = timeinfo.tm_min; + LPC_RTC->HOUR = timeinfo.tm_hour; + LPC_RTC->DOM = timeinfo.tm_mday; + LPC_RTC->MONTH = timeinfo.tm_mon + 1; + LPC_RTC->YEAR = timeinfo.tm_year + 1900; // Restart clock LPC_RTC->CCR &= ~((uint32_t)2); diff --git a/targets/TARGET_NXP/TARGET_LPC408X/rtc_api.c b/targets/TARGET_NXP/TARGET_LPC408X/rtc_api.c index c75bf263d8a..5785e512d0c 100644 --- a/targets/TARGET_NXP/TARGET_LPC408X/rtc_api.c +++ b/targets/TARGET_NXP/TARGET_LPC408X/rtc_api.c @@ -14,6 +14,7 @@ * limitations under the License. */ #include "rtc_api.h" +#include "mbed_mktime.h" // ensure rtc is running (unchanged if already running) @@ -87,25 +88,28 @@ time_t rtc_read(void) { timeinfo.tm_year = LPC_RTC->YEAR - 1900; // Convert to timestamp - time_t t = mktime(&timeinfo); + time_t t = _rtc_mktime(&timeinfo); return t; } void rtc_write(time_t t) { // Convert the time in to a tm - struct tm *timeinfo = localtime(&t); + struct tm timeinfo; + if (_rtc_localtime(t, &timeinfo) == false) { + return; + } // Pause clock, and clear counter register (clears us count) LPC_RTC->CCR |= 2; // Set the RTC - LPC_RTC->SEC = timeinfo->tm_sec; - LPC_RTC->MIN = timeinfo->tm_min; - LPC_RTC->HOUR = timeinfo->tm_hour; - LPC_RTC->DOM = timeinfo->tm_mday; - LPC_RTC->MONTH = timeinfo->tm_mon + 1; - LPC_RTC->YEAR = timeinfo->tm_year + 1900; + LPC_RTC->SEC = timeinfo.tm_sec; + LPC_RTC->MIN = timeinfo.tm_min; + LPC_RTC->HOUR = timeinfo.tm_hour; + LPC_RTC->DOM = timeinfo.tm_mday; + LPC_RTC->MONTH = timeinfo.tm_mon + 1; + LPC_RTC->YEAR = timeinfo.tm_year + 1900; // Restart clock LPC_RTC->CCR &= ~((uint32_t)2); diff --git a/targets/TARGET_NXP/TARGET_LPC43XX/rtc_api.c b/targets/TARGET_NXP/TARGET_LPC43XX/rtc_api.c index aea7e86c134..52f68c25a19 100644 --- a/targets/TARGET_NXP/TARGET_LPC43XX/rtc_api.c +++ b/targets/TARGET_NXP/TARGET_LPC43XX/rtc_api.c @@ -16,6 +16,7 @@ * Ported to NXP LPC43XX by Micromint USA */ #include "rtc_api.h" +#include "mbed_mktime.h" // ensure rtc is running (unchanged if already running) @@ -101,27 +102,30 @@ time_t rtc_read(void) { timeinfo.tm_year = LPC_RTC->TIME[RTC_TIMETYPE_YEAR] - 1900; // Convert to timestamp - time_t t = mktime(&timeinfo); + time_t t = _rtc_mktime(&timeinfo); return t; } void rtc_write(time_t t) { // Convert the time in to a tm - struct tm *timeinfo = localtime(&t); + struct tm timeinfo; + if (_rtc_localtime(t, &timeinfo) == false) { + return; + } // Pause clock, and clear counter register (clears us count) LPC_RTC->CCR |= 2; // Set the RTC - LPC_RTC->TIME[RTC_TIMETYPE_SECOND] = timeinfo->tm_sec; - LPC_RTC->TIME[RTC_TIMETYPE_MINUTE] = timeinfo->tm_min; - LPC_RTC->TIME[RTC_TIMETYPE_HOUR] = timeinfo->tm_hour; - LPC_RTC->TIME[RTC_TIMETYPE_DAYOFMONTH] = timeinfo->tm_mday; - LPC_RTC->TIME[RTC_TIMETYPE_DAYOFWEEK] = timeinfo->tm_wday; - LPC_RTC->TIME[RTC_TIMETYPE_DAYOFYEAR] = timeinfo->tm_yday; - LPC_RTC->TIME[RTC_TIMETYPE_MONTH] = timeinfo->tm_mon + 1; - LPC_RTC->TIME[RTC_TIMETYPE_YEAR] = timeinfo->tm_year + 1900; + LPC_RTC->TIME[RTC_TIMETYPE_SECOND] = timeinfo.tm_sec; + LPC_RTC->TIME[RTC_TIMETYPE_MINUTE] = timeinfo.tm_min; + LPC_RTC->TIME[RTC_TIMETYPE_HOUR] = timeinfo.tm_hour; + LPC_RTC->TIME[RTC_TIMETYPE_DAYOFMONTH] = timeinfo.tm_mday; + LPC_RTC->TIME[RTC_TIMETYPE_DAYOFWEEK] = timeinfo.tm_wday; + LPC_RTC->TIME[RTC_TIMETYPE_DAYOFYEAR] = timeinfo.tm_yday; + LPC_RTC->TIME[RTC_TIMETYPE_MONTH] = timeinfo.tm_mon + 1; + LPC_RTC->TIME[RTC_TIMETYPE_YEAR] = timeinfo.tm_year + 1900; // Restart clock LPC_RTC->CCR &= ~((uint32_t)2); diff --git a/targets/TARGET_RENESAS/TARGET_RZ_A1H/rtc_api.c b/targets/TARGET_RENESAS/TARGET_RZ_A1H/rtc_api.c index e9845172755..f526a221c39 100644 --- a/targets/TARGET_RENESAS/TARGET_RZ_A1H/rtc_api.c +++ b/targets/TARGET_RENESAS/TARGET_RZ_A1H/rtc_api.c @@ -21,6 +21,7 @@ #include "rtc_api.h" #include "rtc_iodefine.h" +#include "mbed_mktime.h" #define RCR1_VAL_ON (0x08u) // AIE = 1 @@ -213,7 +214,7 @@ time_t rtc_read(void) { if (err == 0) { // Convert to timestamp - t = mktime(&timeinfo); + t = _rtc_mktime(&timeinfo); } else { // Error t = TIME_ERROR_VAL; @@ -303,7 +304,10 @@ static int rtc_dec16_to_hex(uint16_t dec_val, uint16_t offset, int *hex_val) { */ void rtc_write(time_t t) { - struct tm *timeinfo = localtime(&t); + struct tm timeinfo; + if (_rtc_localtime(t, &timeinfo) == false) { + return; + } volatile uint16_t dummy_read; if (rtc_isenabled() != 0) { @@ -314,12 +318,12 @@ void rtc_write(time_t t) { dummy_read = (uint16_t)RTC.RCR2; dummy_read = (uint16_t)RTC.RCR2; - RTC.RSECCNT = rtc_hex8_to_dec(timeinfo->tm_sec); - RTC.RMINCNT = rtc_hex8_to_dec(timeinfo->tm_min); - RTC.RHRCNT = rtc_hex8_to_dec(timeinfo->tm_hour); - RTC.RDAYCNT = rtc_hex8_to_dec(timeinfo->tm_mday); - RTC.RMONCNT = rtc_hex8_to_dec(timeinfo->tm_mon + 1); - RTC.RYRCNT = rtc_hex16_to_dec(timeinfo->tm_year + 1900); + RTC.RSECCNT = rtc_hex8_to_dec(timeinfo.tm_sec); + RTC.RMINCNT = rtc_hex8_to_dec(timeinfo.tm_min); + RTC.RHRCNT = rtc_hex8_to_dec(timeinfo.tm_hour); + RTC.RDAYCNT = rtc_hex8_to_dec(timeinfo.tm_mday); + RTC.RMONCNT = rtc_hex8_to_dec(timeinfo.tm_mon + 1); + RTC.RYRCNT = rtc_hex16_to_dec(timeinfo.tm_year + 1900); dummy_read = (uint16_t)RTC.RYRCNT; dummy_read = (uint16_t)RTC.RYRCNT; diff --git a/targets/TARGET_RENESAS/TARGET_VK_RZ_A1H/rtc_api.c b/targets/TARGET_RENESAS/TARGET_VK_RZ_A1H/rtc_api.c index 1a5d8930d8c..8754a5d2575 100644 --- a/targets/TARGET_RENESAS/TARGET_VK_RZ_A1H/rtc_api.c +++ b/targets/TARGET_RENESAS/TARGET_VK_RZ_A1H/rtc_api.c @@ -25,6 +25,7 @@ #include "rtc_api.h" #include "rtc_iodefine.h" +#include "mbed_mktime.h" #define RCR1_VAL_ON (0x08u) // AIE = 1 @@ -247,7 +248,7 @@ time_t rtc_read(void) { if (err == 0) { // Convert to timestamp - t = mktime(&timeinfo); + t = _rtc_mktime(&timeinfo); } else { // Error t = TIME_ERROR_VAL; @@ -337,7 +338,10 @@ static int rtc_dec16_to_hex(uint16_t dec_val, uint16_t offset, int *hex_val) { */ void rtc_write(time_t t) { - struct tm *timeinfo = localtime(&t); + struct tm timeinfo; + if (_rtc_localtime(t, &timeinfo) == false) { + return; + } volatile uint16_t dummy_read; if (rtc_isenabled() != 0) { @@ -348,12 +352,12 @@ void rtc_write(time_t t) { dummy_read = (uint16_t)RTC.RCR2; dummy_read = (uint16_t)RTC.RCR2; - RTC.RSECCNT = rtc_hex8_to_dec(timeinfo->tm_sec); - RTC.RMINCNT = rtc_hex8_to_dec(timeinfo->tm_min); - RTC.RHRCNT = rtc_hex8_to_dec(timeinfo->tm_hour); - RTC.RDAYCNT = rtc_hex8_to_dec(timeinfo->tm_mday); - RTC.RMONCNT = rtc_hex8_to_dec(timeinfo->tm_mon + 1); - RTC.RYRCNT = rtc_hex16_to_dec(timeinfo->tm_year + 1900); + RTC.RSECCNT = rtc_hex8_to_dec(timeinfo.tm_sec); + RTC.RMINCNT = rtc_hex8_to_dec(timeinfo.tm_min); + RTC.RHRCNT = rtc_hex8_to_dec(timeinfo.tm_hour); + RTC.RDAYCNT = rtc_hex8_to_dec(timeinfo.tm_mday); + RTC.RMONCNT = rtc_hex8_to_dec(timeinfo.tm_mon + 1); + RTC.RYRCNT = rtc_hex16_to_dec(timeinfo.tm_year + 1900); dummy_read = (uint16_t)RTC.RYRCNT; dummy_read = (uint16_t)RTC.RYRCNT; diff --git a/targets/TARGET_Realtek/TARGET_AMEBA/rtc_api.c b/targets/TARGET_Realtek/TARGET_AMEBA/rtc_api.c index a15d1c44bf2..f2d44075d55 100644 --- a/targets/TARGET_Realtek/TARGET_AMEBA/rtc_api.c +++ b/targets/TARGET_Realtek/TARGET_AMEBA/rtc_api.c @@ -19,54 +19,17 @@ #if DEVICE_RTC #include #include "timer_api.h" // software-RTC: use a g-timer for the tick of the RTC +#include "mbed_mktime.h" #define SW_RTC_TIMER_ID TIMER4 static gtimer_t sw_rtc; -static struct tm rtc_timeinfo; static int sw_rtc_en=0; - -static const u8 dim[14] = { - 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 28 }; - -static inline bool is_leap_year(unsigned int year) -{ - return (!(year % 4) && (year % 100)) || !(year % 400); -} - - -static u8 days_in_month (u8 month, u8 year) -{ - u8 ret = dim [ month - 1 ]; - if (ret == 0) - ret = is_leap_year (year) ? 29 : 28; - return ret; -} +static time_t rtc_time; void sw_rtc_tick_handler(uint32_t id) { - if(++rtc_timeinfo.tm_sec > 59) { // Increment seconds, check for overflow - rtc_timeinfo.tm_sec = 0; // Reset seconds - if(++rtc_timeinfo.tm_min > 59) { // Increment minutes, check for overflow - rtc_timeinfo.tm_min = 0; // Reset minutes - if(++rtc_timeinfo.tm_hour > 23) { // Increment hours, check for overflow - rtc_timeinfo.tm_hour = 0; // Reset hours - ++rtc_timeinfo.tm_yday; // Increment day of year - if(++rtc_timeinfo.tm_wday > 6) // Increment day of week, check for overflow - rtc_timeinfo.tm_wday = 0; // Reset day of week - // Increment day of month, check for overflow - if(++rtc_timeinfo.tm_mday > - days_in_month(rtc_timeinfo.tm_mon, rtc_timeinfo.tm_year + 1900)) { - rtc_timeinfo.tm_mday = 1; // Reset day of month - if(++rtc_timeinfo.tm_mon > 11) { // Increment month, check for overflow - rtc_timeinfo.tm_mon = 0; // Reset month - rtc_timeinfo.tm_yday = 0; // Reset day of year - ++rtc_timeinfo.tm_year; // Increment year - } // - year - } // - month - } // - day - } // - hour - } + rtc_time++; } void rtc_init(void) @@ -92,35 +55,15 @@ int rtc_isenabled(void) time_t rtc_read(void) { - time_t t; - - // Convert to timestamp - t = mktime(&rtc_timeinfo); - - return t; + return rtc_time; } void rtc_write(time_t t) { - // Convert the time in to a tm - struct tm *timeinfo = localtime(&t); - - if (timeinfo == NULL) { - // Error - return; - } - gtimer_stop(&sw_rtc); // Set the RTC - rtc_timeinfo.tm_sec = timeinfo->tm_sec; - rtc_timeinfo.tm_min = timeinfo->tm_min; - rtc_timeinfo.tm_hour = timeinfo->tm_hour; - rtc_timeinfo.tm_mday = timeinfo->tm_mday; - rtc_timeinfo.tm_wday = timeinfo->tm_wday; - rtc_timeinfo.tm_yday = timeinfo->tm_yday; - rtc_timeinfo.tm_mon = timeinfo->tm_mon; - rtc_timeinfo.tm_year = timeinfo->tm_year; + rtc_time = t; gtimer_start(&sw_rtc); } diff --git a/targets/TARGET_STM/rtc_api.c b/targets/TARGET_STM/rtc_api.c index 66255db0caa..c9e9341cf8d 100644 --- a/targets/TARGET_STM/rtc_api.c +++ b/targets/TARGET_STM/rtc_api.c @@ -32,6 +32,7 @@ #include "rtc_api.h" #include "rtc_api_hal.h" #include "mbed_error.h" +#include "mbed_mktime.h" static RTC_HandleTypeDef RtcHandle; @@ -239,7 +240,7 @@ time_t rtc_read(void) timeinfo.tm_isdst = -1; // Convert to timestamp - time_t t = mktime(&timeinfo); + time_t t = _rtc_mktime(&timeinfo); return t; } @@ -252,20 +253,23 @@ void rtc_write(time_t t) RtcHandle.Instance = RTC; // Convert the time into a tm - struct tm *timeinfo = localtime(&t); + struct tm timeinfo; + if (_rtc_localtime(t, &timeinfo) == false) { + return; + } // Fill RTC structures - if (timeinfo->tm_wday == 0) { + if (timeinfo.tm_wday == 0) { dateStruct.WeekDay = 7; } else { - dateStruct.WeekDay = timeinfo->tm_wday; + dateStruct.WeekDay = timeinfo.tm_wday; } - dateStruct.Month = timeinfo->tm_mon + 1; - dateStruct.Date = timeinfo->tm_mday; - dateStruct.Year = timeinfo->tm_year - 68; - timeStruct.Hours = timeinfo->tm_hour; - timeStruct.Minutes = timeinfo->tm_min; - timeStruct.Seconds = timeinfo->tm_sec; + dateStruct.Month = timeinfo.tm_mon + 1; + dateStruct.Date = timeinfo.tm_mday; + dateStruct.Year = timeinfo.tm_year - 68; + timeStruct.Hours = timeinfo.tm_hour; + timeStruct.Minutes = timeinfo.tm_min; + timeStruct.Seconds = timeinfo.tm_sec; #if !(TARGET_STM32F1) timeStruct.TimeFormat = RTC_HOURFORMAT_24;