Skip to content

Nuvoton: Rework RTC #6049

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 15, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 121 additions & 61 deletions targets/TARGET_NUVOTON/TARGET_M451/rtc_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "rtc_api.h"

#if DEVICE_RTC
Expand All @@ -24,8 +24,65 @@
#include "nu_miscutil.h"
#include "mbed_mktime.h"

#define YEAR0 1900
//#define EPOCH_YR 1970
/* Micro seconds per second */
#define NU_US_PER_SEC 1000000
/* Timer clock per second
*
* NOTE: This dependents on real hardware.
*/
#define NU_RTCCLK_PER_SEC ((CLK->CLKSEL3 & CLK_CLKSEL3_SC0SEL_Msk) ? __LIRC : __LXT)

/* Strategy for implementation of RTC HAL
*
* H/W RTC just supports year range 2000~2099, which cannot fully cover POSIX time (starting since 2970)
* and date time of struct TM (starting since 1900).
*
* To conquer the difficulty, we don't use H/W RTC to keep real date time. Instead, we use it to keep
* elapsed time in seconds since one reference time point. The strategy would be:
*
* 1. Choose DATETIME_HWRTC_ORIGIN (00:00:00 UTC, Saturday, 1 January 2000) as reference time point of H/W RTC.
* 2. t_hwrtc_origin = DATETIME_HWRTC_ORIGIN in POSIX time
* 3. t_hwrtc_elapsed = t_hwrtc_origin + elapsed time since t_hwrtc_origin
* 4. t_write = POSIX time set by rtc_write().
* 5. t_present = rtc_read() = t_write + (t_hwrtc_elapsed - t_hwrtc_origin)
*
* 1900
* |---------------------------------------------------------------------------------|
* 1970 t_write t_present
* |---------|-------|-----------------|---------------------------------------------|
*
* 2000
* |-----------------|---------------------------------------------------------------|
* t_hwrtc_origin t_hwrtc_elapsed
*
*/
/* Start year of struct TM*/
#define NU_TM_YEAR0 1900
/* Start year of POSIX time (set_time()/time()) */
#define NU_POSIX_YEAR0 1970
/* Start year of H/W RTC */
#define NU_HWRTC_YEAR0 2000

/* RTC H/W origin time: 00:00:00 UTC, Saturday, 1 January 2000 */
static const S_RTC_TIME_DATA_T DATETIME_HWRTC_ORIGIN = {
2000, /* Year value, range between 2000 ~ 2099 */
1, /* Month value, range between 1 ~ 12 */
1, /* Day value, range between 1 ~ 31 */
RTC_SATURDAY, /* Day of the week */
0, /* Hour value, range between 0 ~ 23 */
0, /* Minute value, range between 0 ~ 59 */
0, /* Second value, range between 0 ~ 59 */
RTC_CLOCK_24, /* 12-Hour (RTC_CLOCK_12) / 24-Hour (RTC_CLOCK_24) */
0 /* RTC_AM / RTC_PM (used only for 12-Hour) */
};
/* t_hwrtc_origin initialized or not? */
static bool t_hwrtc_origin_inited = 0;
/* POSIX time of DATETIME_HWRTC_ORIGIN (since 00:00:00 UTC, Thursday, 1 January 1970) */
static time_t t_hwrtc_origin = 0;
/* POSIX time set by rtc_write() */
static time_t t_write = 0;
/* Convert date time from H/W RTC to struct TM */
static void rtc_convert_datetime_hwrtc_to_tm(struct tm *datetime_tm, const S_RTC_TIME_DATA_T *datetime_hwrtc);

static const struct nu_modinit_s rtc_modinit = {RTC_0, RTC_MODULE, 0, 0, 0, RTC_IRQn, NULL};

Expand All @@ -34,8 +91,11 @@ void rtc_init(void)
if (rtc_isenabled()) {
return;
}

RTC_Open(NULL);

/* POSIX time origin (00:00:00 UTC, Thursday, 1 January 1970) */
rtc_write(0);
}

void rtc_free(void)
Expand All @@ -50,56 +110,47 @@ int rtc_isenabled(void)
// Enable IP clock
CLK_EnableModuleClock(rtc_modinit.clkidx);
}

// NOTE: Check RTC Init Active flag to support crossing reset cycle.
return !! (RTC->INIT & RTC_INIT_ACTIVE_Msk);
}

/*
struct tm
tm_sec seconds after the minute 0-61
tm_min minutes after the hour 0-59
tm_hour hours since midnight 0-23
tm_mday day of the month 1-31
tm_mon months since January 0-11
tm_year years since 1900
tm_wday days since Sunday 0-6
tm_yday days since January 1 0-365
tm_isdst Daylight Saving Time flag
*/

time_t rtc_read(void)
{
// NOTE: After boot, RTC time registers are not synced immediately, about 1 sec latency.
// RTC time got (through RTC_GetDateAndTime()) in this sec would be last-synced and incorrect.
/* NOTE: After boot, RTC time registers are not synced immediately, about 1 sec latency.
* RTC time got (through RTC_GetDateAndTime()) in this sec would be last-synced and incorrect.
*/
if (! rtc_isenabled()) {
rtc_init();
}

S_RTC_TIME_DATA_T rtc_datetime;
RTC_GetDateAndTime(&rtc_datetime);

struct tm timeinfo;

// Convert struct tm to S_RTC_TIME_DATA_T
timeinfo.tm_year = rtc_datetime.u32Year - YEAR0;
timeinfo.tm_mon = rtc_datetime.u32Month - 1;
timeinfo.tm_mday = rtc_datetime.u32Day;
timeinfo.tm_wday = rtc_datetime.u32DayOfWeek;
timeinfo.tm_hour = rtc_datetime.u32Hour;
if (rtc_datetime.u32TimeScale == RTC_CLOCK_12 && rtc_datetime.u32AmPm == RTC_PM) {
timeinfo.tm_hour += 12;

/* Used for intermediary between date time of H/W RTC and POSIX time */
struct tm datetime_tm;

if (! t_hwrtc_origin_inited) {
t_hwrtc_origin_inited = 1;

/* Convert date time from H/W RTC to struct TM */
rtc_convert_datetime_hwrtc_to_tm(&datetime_tm, &DATETIME_HWRTC_ORIGIN);
/* Convert date time of struct TM to POSIX time */
if (! _rtc_maketime(&datetime_tm, &t_hwrtc_origin, RTC_FULL_LEAP_YEAR_SUPPORT)) {
return 0;
}
}
timeinfo.tm_min = rtc_datetime.u32Minute;
timeinfo.tm_sec = rtc_datetime.u32Second;

// Convert to timestamp
time_t t;
if (_rtc_maketime(&timeinfo, &t, RTC_FULL_LEAP_YEAR_SUPPORT) == false) {
S_RTC_TIME_DATA_T hwrtc_datetime_2K_present;
RTC_GetDateAndTime(&hwrtc_datetime_2K_present);
/* Convert date time from H/W RTC to struct TM */
rtc_convert_datetime_hwrtc_to_tm(&datetime_tm, &hwrtc_datetime_2K_present);
/* Convert date time of struct TM to POSIX time */
time_t t_hwrtc_elapsed;
if (! _rtc_maketime(&datetime_tm, &t_hwrtc_elapsed, RTC_FULL_LEAP_YEAR_SUPPORT)) {
return 0;
}

return t;
/* Present time in POSIX time */
time_t t_present = t_write + (t_hwrtc_elapsed - t_hwrtc_origin);
return t_present;
}

void rtc_write(time_t t)
Expand All @@ -108,28 +159,37 @@ void rtc_write(time_t t)
rtc_init();
}

// Convert timestamp to struct tm
struct tm timeinfo;
if (_rtc_localtime(t, &timeinfo, RTC_FULL_LEAP_YEAR_SUPPORT) == false) {
return;
}
t_write = t;

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.u32TimeScale = RTC_CLOCK_24;

// NOTE: Timing issue with write to RTC registers. This delay is empirical, not rational.
RTC_SetDateAndTime(&rtc_datetime);
//nu_nop(6000);
wait_us(100);
RTC_SetDateAndTime((S_RTC_TIME_DATA_T *) &DATETIME_HWRTC_ORIGIN);
/* NOTE: When engine is clocked by low power clock source (LXT/LIRC), we need to wait for 3 engine clocks. */
wait_us((NU_US_PER_SEC / NU_RTCCLK_PER_SEC) * 3);
}

/*
struct tm
tm_sec seconds after the minute 0-61
tm_min minutes after the hour 0-59
tm_hour hours since midnight 0-23
tm_mday day of the month 1-31
tm_mon months since January 0-11
tm_year years since 1900
tm_wday days since Sunday 0-6
tm_yday days since January 1 0-365
tm_isdst Daylight Saving Time flag
*/
static void rtc_convert_datetime_hwrtc_to_tm(struct tm *datetime_tm, const S_RTC_TIME_DATA_T *datetime_hwrtc)
{
datetime_tm->tm_year = datetime_hwrtc->u32Year - NU_TM_YEAR0;
datetime_tm->tm_mon = datetime_hwrtc->u32Month - 1;
datetime_tm->tm_mday = datetime_hwrtc->u32Day;
datetime_tm->tm_wday = datetime_hwrtc->u32DayOfWeek;
datetime_tm->tm_hour = datetime_hwrtc->u32Hour;
if (datetime_hwrtc->u32TimeScale == RTC_CLOCK_12 && datetime_hwrtc->u32AmPm == RTC_PM) {
datetime_tm->tm_hour += 12;
}
datetime_tm->tm_min = datetime_hwrtc->u32Minute;
datetime_tm->tm_sec = datetime_hwrtc->u32Second;
}

#endif
Loading