remove usage of mktime/localtime in favor of dedicated functions.

The use of mktime was causing a fault when called in interrupt handler because on GCC it lock the mutex protecting the environment, To overcome this issue, this patch add dedicated routine to convert a time_t into a tm and vice versa.
In the process mktime has been optimized and is now an order of magnitude faster than the routines present in the C library.
pull/4499/head
Vincent Coubard 2017-06-07 21:59:17 -05:00 committed by Russ Butler
parent 96288e3eef
commit f880e44145
12 changed files with 599 additions and 80 deletions

View File

@ -0,0 +1,241 @@
/* 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 rtc_api.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(&copy);
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 ||
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"
);
}
}
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);
}

158
platform/mbed_mktime.c Normal file
View File

@ -0,0 +1,158 @@
/* 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;
// 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;
}
}
// 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;
}

84
platform/mbed_mktime.h Normal file
View File

@ -0,0 +1,84 @@
/** \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 <time.h>
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* Compute if a year is a leap year or not.
* year 0 is translated into year 1900 CE.
*
* @note - For use by the HAL only
*/
bool _rtc_is_leap_year(int year);
/*
* Thread safe (partial) replacement for mktime.
* This function is tailored around the RTC specification and is not a full
* replacement for mktime.
* The fields from tm used are:
* - tm_sec
* - tm_min
* - tm_hour
* - tm_mday
* - tm_mon
* - tm_year
* Other fields are ignored and won't be normalized by the call.
* If the time in input is less than UNIX epoch (1st january of 1970 at 00:00:00),
* then this function consider the input as invalid and will return time_t(-1).
* Values in output range from 0 to INT_MAX.
* Leap seconds are not supported.
*
* @note - For use by the HAL only
*/
time_t _rtc_mktime(const struct tm* time);
/*
* Thread safe (partial) replacement for localtime.
* This function is tailored around the RTC specification and is not a full
* replacement for localtime.
* The tm fields filled by this function are:
* - tm_sec
* - tm_min
* - tm_hour
* - tm_mday
* - tm_mon
* - tm_year
* The time in input shall be in the range [0, INT32_MAX] otherwise the function
* will return false and the structure time_info in input will remain untouch.
*
* @note - For use by the HAL only
*/
bool _rtc_localtime(time_t timestamp, struct tm* time_info);
#ifdef __cplusplus
}
#endif
#endif /* MBED_MKTIME_H */
/** @}*/

View File

@ -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 (st_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);
}
}

View File

@ -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 (st_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.

View File

@ -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 (st_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.

View File

@ -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 (st_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);

View File

@ -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 (st_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);

View File

@ -16,6 +16,7 @@
* Ported to NXP LPC43XX by Micromint USA <support@micromint.com>
*/
#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 (st_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);

View File

@ -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 (st_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;

View File

@ -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 (st_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;

View File

@ -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 (st_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;