1 /*
2 * Copyright 2017-2020 NXP
3 * All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 */
7
8 #include "fsl_rtc.h"
9
10 /*******************************************************************************
11 * Definitions
12 ******************************************************************************/
13
14 /* Component ID definition, used by tools. */
15 #ifndef FSL_COMPONENT_ID
16 #define FSL_COMPONENT_ID "platform.drivers.rtc_1"
17 #endif
18
19 #define SECONDS_IN_A_DAY (86400U)
20 #define SECONDS_IN_A_HOUR (3600U)
21 #define SECONDS_IN_A_MINUTE (60U)
22 #define DAYS_IN_A_YEAR (365U)
23 #define YEAR_RANGE_START (1970U)
24 #define YEAR_RANGE_END (2099U)
25
26 /*******************************************************************************
27 * Variables
28 ******************************************************************************/
29 static volatile uint32_t s_CurrentTimeSeconds;
30 static volatile uint32_t s_AlarmTimeSeconds;
31 static rtc_alarm_callback_t s_RtcAlarmCallback;
32
33 /*******************************************************************************
34 * Prototypes
35 ******************************************************************************/
36 /*!
37 * @brief Checks whether the date and time passed in is valid
38 *
39 * @param datetime Pointer to structure where the date and time details are stored
40 *
41 * @return Returns false if the date & time details are out of range; true if in range
42 */
43 static bool RTC_CheckDatetimeFormat(const rtc_datetime_t *datetime);
44
45 /*!
46 * @brief Converts time data from datetime to seconds
47 *
48 * @param datetime Pointer to datetime structure where the date and time details are stored
49 *
50 * @return The result of the conversion in seconds
51 */
52 static uint32_t RTC_ConvertDatetimeToSeconds(const rtc_datetime_t *datetime);
53
54 /*!
55 * @brief Converts time data from seconds to a datetime structure
56 *
57 * @param seconds Seconds value that needs to be converted to datetime format
58 * @param datetime Pointer to the datetime structure where the result of the conversion is stored
59 */
60 static void RTC_ConvertSecondsToDatetime(uint32_t seconds, rtc_datetime_t *datetime);
61
62 /*******************************************************************************
63 * Code
64 ******************************************************************************/
RTC_CheckDatetimeFormat(const rtc_datetime_t * datetime)65 static bool RTC_CheckDatetimeFormat(const rtc_datetime_t *datetime)
66 {
67 assert(datetime);
68
69 /* Table of days in a month for a non leap year. First entry in the table is not used,
70 * valid months start from 1
71 */
72 uint8_t daysPerMonth[] = {0U, 31U, 28U, 31U, 30U, 31U, 30U, 31U, 31U, 30U, 31U, 30U, 31U};
73
74 /* Check year, month, hour, minute, seconds */
75 if ((datetime->year < YEAR_RANGE_START) || (datetime->year > YEAR_RANGE_END) || (datetime->month > 12U) ||
76 (datetime->month < 1U) || (datetime->hour >= 24U) || (datetime->minute >= 60U) || (datetime->second >= 60U))
77 {
78 /* If not correct then error*/
79 return false;
80 }
81
82 /* Adjust the days in February for a leap year */
83 if ((((datetime->year & 3U) == 0U) && (datetime->year % 100U != 0U)) || (datetime->year % 400U == 0U))
84 {
85 daysPerMonth[2] = 29U;
86 }
87
88 /* Check the validity of the day */
89 if ((datetime->day > daysPerMonth[datetime->month]) || (datetime->day < 1U))
90 {
91 return false;
92 }
93
94 return true;
95 }
96
RTC_ConvertDatetimeToSeconds(const rtc_datetime_t * datetime)97 static uint32_t RTC_ConvertDatetimeToSeconds(const rtc_datetime_t *datetime)
98 {
99 assert(datetime);
100
101 /* Number of days from begin of the non Leap-year*/
102 /* Number of days from begin of the non Leap-year*/
103 uint16_t monthDays[] = {0U, 0U, 31U, 59U, 90U, 120U, 151U, 181U, 212U, 243U, 273U, 304U, 334U};
104 uint32_t seconds;
105
106 /* Compute number of days from 1970 till given year*/
107 seconds = ((uint32_t)datetime->year - 1970U) * DAYS_IN_A_YEAR;
108 /* Add leap year days */
109 seconds += (((uint32_t)datetime->year / 4U) - (1970U / 4U));
110 /* Add number of days till given month*/
111 seconds += monthDays[datetime->month];
112 /* Add days in given month. We subtract the current day as it is
113 * represented in the hours, minutes and seconds field*/
114 seconds += ((uint32_t)datetime->day - 1U);
115 /* For leap year if month less than or equal to Febraury, decrement day counter*/
116 if (((datetime->year & 3U) == 0x00U) && (datetime->month <= 2U))
117 {
118 seconds--;
119 }
120
121 seconds = (seconds * SECONDS_IN_A_DAY) + (datetime->hour * SECONDS_IN_A_HOUR) +
122 (datetime->minute * SECONDS_IN_A_MINUTE) + datetime->second;
123
124 return seconds;
125 }
126
RTC_ConvertSecondsToDatetime(uint32_t seconds,rtc_datetime_t * datetime)127 static void RTC_ConvertSecondsToDatetime(uint32_t seconds, rtc_datetime_t *datetime)
128 {
129 assert(datetime);
130
131 uint8_t i;
132 uint16_t daysInYear;
133 uint32_t secondsRemaining;
134 uint32_t days;
135 /* Table of days in a month for a non leap year. First entry in the table is not used,
136 * valid months start from 1
137 */
138 uint8_t daysPerMonth[] = {0U, 31U, 28U, 31U, 30U, 31U, 30U, 31U, 31U, 30U, 31U, 30U, 31U};
139
140 /* Start with the seconds value that is passed in to be converted to date time format */
141 secondsRemaining = seconds;
142
143 /* Calcuate the number of days, we add 1 for the current day which is represented in the
144 * hours and seconds field
145 */
146 days = secondsRemaining / SECONDS_IN_A_DAY + 1U;
147
148 /* Update seconds left*/
149 secondsRemaining = secondsRemaining % SECONDS_IN_A_DAY;
150
151 /* Calculate the datetime hour, minute and second fields */
152 datetime->hour = (uint8_t)(secondsRemaining / SECONDS_IN_A_HOUR);
153 secondsRemaining = secondsRemaining % SECONDS_IN_A_HOUR;
154 datetime->minute = (uint8_t)(secondsRemaining / 60U);
155 datetime->second = (uint8_t)(secondsRemaining % SECONDS_IN_A_MINUTE);
156
157 /* Calculate year */
158 daysInYear = DAYS_IN_A_YEAR;
159 datetime->year = YEAR_RANGE_START;
160 while (days > daysInYear)
161 {
162 /* Decrease day count by a year and increment year by 1 */
163 days -= daysInYear;
164 datetime->year++;
165
166 /* Adjust the number of days for a leap year */
167 if ((datetime->year & 3U) != 0x00U)
168 {
169 daysInYear = DAYS_IN_A_YEAR;
170 }
171 else
172 {
173 daysInYear = DAYS_IN_A_YEAR + 1U;
174 }
175 }
176
177 /* Adjust the days in February for a leap year */
178 if ((datetime->year & 3U) == 0x00U)
179 {
180 daysPerMonth[2] = 29U;
181 }
182
183 for (i = 1U; i <= 12U; i++)
184 {
185 if (days <= daysPerMonth[i])
186 {
187 datetime->month = i;
188 break;
189 }
190 else
191 {
192 days -= daysPerMonth[i];
193 }
194 }
195
196 datetime->day = (uint8_t)days;
197 }
198
199 /*!
200 * brief Ungates the RTC clock and configures the peripheral for basic operation.
201 *
202 * note This API should be called at the beginning of the application using the RTC driver.
203 *
204 * param base RTC peripheral base address
205 */
RTC_Init(RTC_Type * base,const rtc_config_t * config)206 void RTC_Init(RTC_Type *base, const rtc_config_t *config)
207 {
208 assert(config);
209
210 uint32_t srcClock_Hz = 0;
211 #if defined(RTC_CLOCKS)
212 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
213 CLOCK_EnableClock(kCLOCK_Rtc0);
214 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
215 #endif /* RTC_CLOCKS */
216
217 /* Clear Real-Time Interrupt Flag to 0 */
218 base->SC |= RTC_SC_RTIF_MASK;
219
220 /* Select Real-Time Clock Source and Clock Prescaler */
221 base->SC &= ~(RTC_SC_RTCLKS_MASK | RTC_SC_RTCPS_MASK);
222 base->SC |= RTC_SC_RTCLKS(config->clockSource) | RTC_SC_RTCPS(config->prescaler);
223
224 /* Get RTC clock frequency */
225 if (config->clockSource == kRTC_ExternalClock)
226 {
227 srcClock_Hz = CLOCK_GetFreq(kCLOCK_Osc0ErClk);
228 }
229
230 if (config->clockSource == kRTC_LPOCLK)
231 {
232 srcClock_Hz = CLOCK_GetFreq(kCLOCK_LpoClk);
233 }
234
235 if (config->clockSource == kRTC_ICSIRCLK)
236 {
237 srcClock_Hz = CLOCK_GetFreq(kCLOCK_ICSInternalRefClk);
238 }
239
240 if (config->clockSource == kRTC_BusClock)
241 {
242 srcClock_Hz = CLOCK_GetFreq(kCLOCK_BusClk);
243 }
244
245 /* Set RTC module value */
246 RTC_SetModuloValue(
247 base,
248 (uint32_t)(USEC_TO_COUNT(config->time_us, (uint64_t)srcClock_Hz / (uint64_t)RTC_GetDivideValue(base)) - 1U));
249 }
250
251 /*!
252 * brief Stops the timer and gate the RTC clock.
253 *
254 * param base RTC peripheral base address
255 */
RTC_Deinit(RTC_Type * base)256 void RTC_Deinit(RTC_Type *base)
257 {
258 #if defined(RTC_CLOCKS)
259 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
260 /* Gate the module clock */
261 CLOCK_DisableClock(kCLOCK_Rtc0);
262 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
263 #endif /* RTC_CLOCKS */
264 }
265
266 /*!
267 * brief Fills in the RTC config struct with the default settings.
268 *
269 * The default values are as follows.
270 * code
271 * config->clockSource = kRTC_BusClock;
272 * config->prescaler = kRTC_ClockDivide_16_2048;
273 * config->time_us = 1000000U;
274 * endcode
275 * param config Pointer to the user's RTC configuration structure.
276 */
RTC_GetDefaultConfig(rtc_config_t * config)277 void RTC_GetDefaultConfig(rtc_config_t *config)
278 {
279 assert(config != NULL);
280
281 /* Initializes the configure structure to zero. */
282 (void)memset(config, 0, sizeof(*config));
283
284 config->clockSource = kRTC_BusClock;
285
286 config->prescaler = kRTC_ClockDivide_16_2048;
287
288 /* Configure RTC to interrupt every 1000000us */
289 config->time_us = 1000000U;
290 }
291
292 /*!
293 * brief Get the RTC Divide value.
294 *
295 * note This API should be called after selecting clock source and clock prescaler.
296 *
297 * param base RTC peripheral base address
298 * return The Divider value. The Divider value depends on clock source and clock prescaler
299 */
RTC_GetDivideValue(RTC_Type * base)300 uint32_t RTC_GetDivideValue(RTC_Type *base)
301 {
302 uint32_t clocks, divide, prescale;
303
304 /* Get RTCLKS and RTCPS configuration value */
305 clocks = (uint32_t)((base->SC & RTC_SC_RTCLKS_MASK) >> RTC_SC_RTCLKS_SHIFT);
306 prescale = (uint32_t)((base->SC & RTC_SC_RTCPS_MASK) >> RTC_SC_RTCPS_SHIFT);
307
308 assert(prescale > 0UL);
309
310 if ((clocks & 1UL) == 0UL)
311 {
312 divide = 1UL << (prescale - 1UL);
313 }
314 if ((clocks & 1UL) == 1UL)
315 {
316 if (prescale < 6UL)
317 {
318 divide = 1UL << (prescale + 6UL);
319 }
320 else if (prescale == 6UL)
321 {
322 divide = 100UL;
323 }
324 else
325 {
326 divide = 1000UL;
327 }
328 }
329 return divide;
330 }
331
332 /*!
333 * brief Sets the RTC date and time according to the given time structure.
334 *
335 * param datetime Pointer to the structure where the date and time details are stored.
336 *
337 * return kStatus_Success: Success in setting the time and starting the RTC
338 * kStatus_InvalidArgument: Error because the datetime format is incorrect
339 */
RTC_SetDatetime(rtc_datetime_t * datetime)340 status_t RTC_SetDatetime(rtc_datetime_t *datetime)
341 {
342 assert(datetime != NULL);
343
344 status_t status = kStatus_Success;
345
346 /* Return error if the time provided is not valid */
347 if (!(RTC_CheckDatetimeFormat(datetime)))
348 {
349 status = kStatus_InvalidArgument;
350 }
351 else
352 {
353 /* Set current time seconds */
354 s_CurrentTimeSeconds = RTC_ConvertDatetimeToSeconds(datetime);
355 }
356
357 return status;
358 }
359
360 /*!
361 * brief Gets the RTC time and stores it in the given time structure.
362 *
363 * param datetime Pointer to the structure where the date and time details are stored.
364 */
RTC_GetDatetime(rtc_datetime_t * datetime)365 void RTC_GetDatetime(rtc_datetime_t *datetime)
366 {
367 assert(datetime);
368
369 /* Get current data time */
370 RTC_ConvertSecondsToDatetime(s_CurrentTimeSeconds, datetime);
371 }
372
373 /*!
374 * brief Sets the RTC alarm time.
375 *
376 * param second Second value. User input the number of second.
377 * After seconds user input, alarm occurs.
378 */
RTC_SetAlarm(uint32_t second)379 void RTC_SetAlarm(uint32_t second)
380 {
381 /* Set alarm time seconds */
382 s_AlarmTimeSeconds = second + s_CurrentTimeSeconds;
383 }
384
385 /*!
386 * brief Returns the RTC alarm time.
387 *
388 * param datetime Pointer to the structure where the alarm date and time details are stored.
389 */
RTC_GetAlarm(rtc_datetime_t * datetime)390 void RTC_GetAlarm(rtc_datetime_t *datetime)
391 {
392 assert(datetime);
393
394 /* Get alarm data time */
395 RTC_ConvertSecondsToDatetime(s_AlarmTimeSeconds, datetime);
396 }
397
398 /*!
399 * brief Set the RTC alarm callback.
400 *
401 * param callback The callback function.
402 */
RTC_SetAlarmCallback(rtc_alarm_callback_t callback)403 void RTC_SetAlarmCallback(rtc_alarm_callback_t callback)
404 {
405 s_RtcAlarmCallback = callback;
406 }
407
408 void RTC_DriverIRQHandler(void);
RTC_DriverIRQHandler(void)409 void RTC_DriverIRQHandler(void)
410 {
411 uint32_t alarmTimeSeconds = s_AlarmTimeSeconds; /* Fix the order of volatile accesses undefined issue. */
412
413 if ((RTC_GetInterruptFlags(RTC) & (uint32_t)kRTC_InterruptFlag) != 0U)
414 {
415 s_CurrentTimeSeconds++;
416 /* Clear second interrupt flag */
417 RTC_ClearInterruptFlags(RTC, (uint32_t)kRTC_InterruptFlag);
418 }
419
420 if (alarmTimeSeconds == s_CurrentTimeSeconds)
421 {
422 if (s_RtcAlarmCallback != NULL)
423 {
424 s_RtcAlarmCallback();
425 }
426 }
427 }
428