/* * Copyright (c) 2016, Freescale Semiconductor, Inc. * Copyright 2016-2017 NXP * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include "fsl_rtc.h" /******************************************************************************* * Definitions ******************************************************************************/ /* Component ID definition, used by tools. */ #ifndef FSL_COMPONENT_ID #define FSL_COMPONENT_ID "platform.drivers.lpc_rtc" #endif #define SECONDS_IN_A_DAY (86400U) #define SECONDS_IN_A_HOUR (3600U) #define SECONDS_IN_A_MINUTE (60U) #define DAYS_IN_A_YEAR (365U) #define YEAR_RANGE_START (1970U) #define YEAR_RANGE_END (2099U) /******************************************************************************* * Prototypes ******************************************************************************/ /*! * @brief Checks whether the date and time passed in is valid * * @param datetime Pointer to structure where the date and time details are stored * * @return Returns false if the date & time details are out of range; true if in range */ static bool RTC_CheckDatetimeFormat(const rtc_datetime_t *datetime); /*! * @brief Converts time data from datetime to seconds * * @param datetime Pointer to datetime structure where the date and time details are stored * * @return The result of the conversion in seconds */ static uint32_t RTC_ConvertDatetimeToSeconds(const rtc_datetime_t *datetime); /*! * @brief Converts time data from seconds to a datetime structure * * @param seconds Seconds value that needs to be converted to datetime format * @param datetime Pointer to the datetime structure where the result of the conversion is stored */ static void RTC_ConvertSecondsToDatetime(uint32_t seconds, rtc_datetime_t *datetime); /******************************************************************************* * Code ******************************************************************************/ static bool RTC_CheckDatetimeFormat(const rtc_datetime_t *datetime) { assert(datetime); /* Table of days in a month for a non leap year. First entry in the table is not used, * valid months start from 1 */ uint8_t daysPerMonth[] = {0U, 31U, 28U, 31U, 30U, 31U, 30U, 31U, 31U, 30U, 31U, 30U, 31U}; /* Check year, month, hour, minute, seconds */ if ((datetime->year < YEAR_RANGE_START) || (datetime->year > YEAR_RANGE_END) || (datetime->month > 12U) || (datetime->month < 1U) || (datetime->hour >= 24U) || (datetime->minute >= 60U) || (datetime->second >= 60U)) { /* If not correct then error*/ return false; } /* Adjust the days in February for a leap year */ if ((((datetime->year & 3U) == 0) && (datetime->year % 100 != 0)) || (datetime->year % 400 == 0)) { daysPerMonth[2] = 29U; } /* Check the validity of the day */ if ((datetime->day > daysPerMonth[datetime->month]) || (datetime->day < 1U)) { return false; } return true; } static uint32_t RTC_ConvertDatetimeToSeconds(const rtc_datetime_t *datetime) { assert(datetime); /* Number of days from begin of the non Leap-year*/ /* Number of days from begin of the non Leap-year*/ uint16_t monthDays[] = {0U, 0U, 31U, 59U, 90U, 120U, 151U, 181U, 212U, 243U, 273U, 304U, 334U}; uint32_t seconds; /* Compute number of days from 1970 till given year*/ seconds = (datetime->year - 1970U) * DAYS_IN_A_YEAR; /* Add leap year days */ seconds += ((datetime->year / 4) - (1970U / 4)); /* Add number of days till given month*/ seconds += monthDays[datetime->month]; /* Add days in given month. We subtract the current day as it is * represented in the hours, minutes and seconds field*/ seconds += (datetime->day - 1); /* For leap year if month less than or equal to Febraury, decrement day counter*/ if ((!(datetime->year & 3U)) && (datetime->month <= 2U)) { seconds--; } seconds = (seconds * SECONDS_IN_A_DAY) + (datetime->hour * SECONDS_IN_A_HOUR) + (datetime->minute * SECONDS_IN_A_MINUTE) + datetime->second; return seconds; } static void RTC_ConvertSecondsToDatetime(uint32_t seconds, rtc_datetime_t *datetime) { assert(datetime); uint32_t x; uint32_t secondsRemaining, days; uint16_t daysInYear; /* Table of days in a month for a non leap year. First entry in the table is not used, * valid months start from 1 */ uint8_t daysPerMonth[] = {0U, 31U, 28U, 31U, 30U, 31U, 30U, 31U, 31U, 30U, 31U, 30U, 31U}; /* Start with the seconds value that is passed in to be converted to date time format */ secondsRemaining = seconds; /* Calcuate the number of days, we add 1 for the current day which is represented in the * hours and seconds field */ days = secondsRemaining / SECONDS_IN_A_DAY + 1; /* Update seconds left*/ secondsRemaining = secondsRemaining % SECONDS_IN_A_DAY; /* Calculate the datetime hour, minute and second fields */ datetime->hour = secondsRemaining / SECONDS_IN_A_HOUR; secondsRemaining = secondsRemaining % SECONDS_IN_A_HOUR; datetime->minute = secondsRemaining / 60U; datetime->second = secondsRemaining % SECONDS_IN_A_MINUTE; /* Calculate year */ daysInYear = DAYS_IN_A_YEAR; datetime->year = YEAR_RANGE_START; while (days > daysInYear) { /* Decrease day count by a year and increment year by 1 */ days -= daysInYear; datetime->year++; /* Adjust the number of days for a leap year */ if (datetime->year & 3U) { daysInYear = DAYS_IN_A_YEAR; } else { daysInYear = DAYS_IN_A_YEAR + 1; } } /* Adjust the days in February for a leap year */ if (!(datetime->year & 3U)) { daysPerMonth[2] = 29U; } for (x = 1U; x <= 12U; x++) { if (days <= daysPerMonth[x]) { datetime->month = x; break; } else { days -= daysPerMonth[x]; } } datetime->day = days; } /*! * brief Ungates the RTC clock and enables the RTC oscillator. * * note This API should be called at the beginning of the application using the RTC driver. * * param base RTC peripheral base address */ void RTC_Init(RTC_Type *base) { #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) /* Enable the RTC peripheral clock */ CLOCK_EnableClock(kCLOCK_Rtc); #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ #if !(defined(FSL_FEATURE_RTC_HAS_NO_RESET) && FSL_FEATURE_RTC_HAS_NO_RESET) RESET_PeripheralReset(kRTC_RST_SHIFT_RSTn); #endif /* Make sure the reset bit is cleared */ base->CTRL &= ~RTC_CTRL_SWRESET_MASK; #if !(defined(FSL_FEATURE_RTC_HAS_NO_OSC_PD) && FSL_FEATURE_RTC_HAS_NO_OSC_PD) /* Make sure the RTC OSC is powered up */ base->CTRL &= ~RTC_CTRL_RTC_OSC_PD_MASK; #endif } /*! * brief Sets the RTC date and time according to the given time structure. * * The RTC counter must be stopped prior to calling this function as writes to the RTC * seconds register will fail if the RTC counter is running. * * param base RTC peripheral base address * param datetime Pointer to structure where the date and time details to set are stored * * return kStatus_Success: Success in setting the time and starting the RTC * kStatus_InvalidArgument: Error because the datetime format is incorrect */ status_t RTC_SetDatetime(RTC_Type *base, const rtc_datetime_t *datetime) { assert(datetime); /* Return error if the time provided is not valid */ if (!(RTC_CheckDatetimeFormat(datetime))) { return kStatus_InvalidArgument; } /* Set time in seconds */ base->COUNT = RTC_ConvertDatetimeToSeconds(datetime); return kStatus_Success; } /*! * brief Gets the RTC time and stores it in the given time structure. * * param base RTC peripheral base address * param datetime Pointer to structure where the date and time details are stored. */ void RTC_GetDatetime(RTC_Type *base, rtc_datetime_t *datetime) { assert(datetime); uint32_t seconds = 0; seconds = base->COUNT; RTC_ConvertSecondsToDatetime(seconds, datetime); } /*! * brief Sets the RTC alarm time * * The function checks whether the specified alarm time is greater than the present * time. If not, the function does not set the alarm and returns an error. * * param base RTC peripheral base address * param alarmTime Pointer to structure where the alarm time is stored. * * return kStatus_Success: success in setting the RTC alarm * kStatus_InvalidArgument: Error because the alarm datetime format is incorrect * kStatus_Fail: Error because the alarm time has already passed */ status_t RTC_SetAlarm(RTC_Type *base, const rtc_datetime_t *alarmTime) { assert(alarmTime); uint32_t alarmSeconds = 0; uint32_t currSeconds = 0; /* Return error if the alarm time provided is not valid */ if (!(RTC_CheckDatetimeFormat(alarmTime))) { return kStatus_InvalidArgument; } alarmSeconds = RTC_ConvertDatetimeToSeconds(alarmTime); /* Get the current time */ currSeconds = base->COUNT; /* Return error if the alarm time has passed */ if (alarmSeconds < currSeconds) { return kStatus_Fail; } /* Set alarm in seconds*/ base->MATCH = alarmSeconds; return kStatus_Success; } /*! * brief Returns the RTC alarm time. * * param base RTC peripheral base address * param datetime Pointer to structure where the alarm date and time details are stored. */ void RTC_GetAlarm(RTC_Type *base, rtc_datetime_t *datetime) { assert(datetime); uint32_t alarmSeconds = 0; /* Get alarm in seconds */ alarmSeconds = base->MATCH; RTC_ConvertSecondsToDatetime(alarmSeconds, datetime); }