1 /*
2 * Copyright 2021 MindMotion Microelectronics Co., Ltd.
3 * All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 */
7
8 #include "hal_rtc.h"
9
10 /* Initialize RTC. */
RTC_Init(void)11 void RTC_Init(void)
12 {
13 /* Open access rights. */
14 RCC->BDCR |= RCC_BDCR_DBP_MASK;
15
16 /* Reset BKP. */
17 RCC->BDCR |= RCC_BDCR_BDRST_MASK;
18 RCC->BDCR &= ~RCC_BDCR_BDRST_MASK;
19
20 /* Enable clock source. */
21 RCC->BDCR |= RCC_BDCR_RTCSEL(1u);
22 RCC->BDCR &= ~RCC_BDCR_LSEON_MASK;
23 RCC->BDCR |= RCC_BDCR_LSEON_MASK;
24
25 /* Clear register synchronization status. */
26 RTC->CRL &= ~RTC_CRL_RSF_MASK;
27 }
28
29 /* Enable or disable access to RTC and backing registers. */
RTC_EnableAccess(bool enable)30 void RTC_EnableAccess(bool enable)
31 {
32 if (enable)
33 {
34 RCC->BDCR |= RCC_BDCR_DBP_MASK;
35 }
36 else
37 {
38 RCC->BDCR &= ~RCC_BDCR_DBP_MASK;
39 }
40 }
41
42 /* Enable backup domain software reset, the register of BKP is reset by the backup domain, not reset during power reset and system reset. */
RTC_EnableReset(bool enable)43 void RTC_EnableReset(bool enable)
44 {
45 if (enable)
46 {
47 RCC->BDCR |= RCC_BDCR_BDRST_MASK;
48 }
49 else
50 {
51 RCC->BDCR &= ~RCC_BDCR_BDRST_MASK;
52 }
53 }
54
55 /* Get the current status flags of the RTC module. */
RTC_GetStatus(void)56 uint32_t RTC_GetStatus(void)
57 {
58 return RTC->CRL;
59 }
60
61 /* Clear the status flag of the RTC module. */
RTC_ClearStatus(uint32_t status)62 void RTC_ClearStatus(uint32_t status)
63 {
64 RTC->CRL &= ~status;
65 }
66
67 /* Get RTC clock source status. */
RTC_GetClockStatus(void)68 uint32_t RTC_GetClockStatus(void)
69 {
70 return RCC->BDCR;
71 }
72
73 /* Enable RTC clock. */
RTC_Enable(bool enable)74 void RTC_Enable(bool enable)
75 {
76 if (enable)
77 {
78 RCC->BDCR |= RCC_BDCR_RTCEN_MASK;
79 }
80 else
81 {
82 RCC->BDCR &= ~RCC_BDCR_RTCEN_MASK;
83 }
84 }
85
86 /* Enable or disable the configuration mode, enable to enter configuration mode, this is a precondition for registers to write data. */
RTC_EnableConf(bool enable)87 void RTC_EnableConf(bool enable)
88 {
89 if (enable)
90 {
91 RTC->CRL |= RTC_CRL_CNF_MASK;
92 }
93 else
94 {
95 RTC->CRL &= ~RTC_CRL_CNF_MASK;
96 }
97 }
98
99 /* Setup prescaler register, open configration module before put data into RTC register. */
RTC_PutPrescalerData(uint32_t div)100 void RTC_PutPrescalerData(uint32_t div)
101 {
102 RTC->CRL |= RTC_CRL_CNF_MASK; /* Enable the configuration mode. */
103 RTC->PRLH = div >> 16u; /* Setup the upper 16-bit value of prescaler. */
104 RTC->PRLL = div; /* Setup the lower 16-bit value of prescaler. */
105 RTC->CRL &= ~RTC_CRL_CNF_MASK; /* Disable the configuration mode. */
106 }
107
108 /* Setup counter register, open configration module before put data into RTC register. */
RTC_PutCounterData(uint32_t cnt)109 void RTC_PutCounterData(uint32_t cnt)
110 {
111 RTC->CRL |= RTC_CRL_CNF_MASK; /* Enable the configuration mode. */
112 RTC->CNTH = cnt >> 16u; /* Setup the upper 16-bit value of counter. */
113 RTC->CNTL = cnt; /* Setup the lower 16-bit value of counter. */
114 RTC->CRL &= ~RTC_CRL_CNF_MASK; /* Disable the configuration mode. */
115 }
116
117 /* Setup alarm register, open configration module before put data into RTC register. */
RTC_PutAlarmData(uint32_t alarm)118 void RTC_PutAlarmData(uint32_t alarm)
119 {
120 RTC->CRL |= RTC_CRL_CNF_MASK; /* Enable the configuration mode. */
121 RTC->ALRH = alarm >> 16u; /* Setup the upper 16-bit value of alarm counter. */
122 RTC->ALRL = alarm; /* Setup the lower 16-bit value of alarm counter. */
123 RTC->CRL &= ~RTC_CRL_CNF_MASK; /* Disable the configuration mode. */
124 }
125
126 /* Get the data from counter of I2C module. */
RTC_GetCounterData(void)127 uint32_t RTC_GetCounterData(void)
128 {
129 return ( (RTC->CNTH << 16u) | RTC->CNTL );
130 }
131
132 /* Get alarm count value which is used to alarm interrupt. */
RTC_GetAlarmData(void)133 uint32_t RTC_GetAlarmData(void)
134 {
135 return ( (RTC->ALRH << 16u) | RTC->ALRL);
136 }
137
138 /* Enable RTC interrupt of RTC module. */
RTC_EnableInterrupts(uint32_t interrupts,bool enable)139 void RTC_EnableInterrupts(uint32_t interrupts, bool enable)
140 {
141 if (enable)
142 {
143 RTC->CRH |= interrupts;
144 }
145 else
146 {
147 RTC->CRH &= ~interrupts;
148 }
149 }
150
151 /* Get the interrupts status flags of the RTC module. */
RTC_GetInterruptStatus(void)152 uint32_t RTC_GetInterruptStatus(void)
153 {
154 return RTC->CRL;
155 }
156
157 /* Clear the status of RTC interrupt. */
RTC_ClearInterruptStatus(uint32_t interrupts)158 void RTC_ClearInterruptStatus(uint32_t interrupts)
159 {
160 RTC->CRL &= ~interrupts;
161 }
162
163 /* Get RTC interrupt enable status. */
RTC_GetEnabledInterrupts(void)164 uint32_t RTC_GetEnabledInterrupts(void)
165 {
166 return RTC->CRH;
167 }
168
169 /* Judging whether the current year is a leap year, an ordinary leap year or a century leap year. */
RTC_JudgeLeapYear(uint16_t years)170 bool RTC_JudgeLeapYear(uint16_t years)
171 {
172 if (years % 4u == 0u)
173 {
174 if (years % 100u == 0u)
175 {
176 if (years % 400u == 0u)
177 {
178 return true; /* Century leap year. */
179 }
180 else
181 {
182 return false;
183 }
184 }
185 else
186 {
187 return true; /* Ordinary leap year. */
188 }
189 }
190 else
191 {
192 return false;
193 }
194 }
195
196 /* Month correction table, used for calculation of month. */
197 const uint8_t month_table[12u] = {31u, 28u, 31u, 30u, 31u, 30u, 31u, 31u, 30u, 31u, 30u, 31u};
198
199 /* Setup initialization time. */
RTC_SetTimeBlocking(RTC_Init_Type * init,RTC_Time_Type * time)200 bool RTC_SetTimeBlocking(RTC_Init_Type * init, RTC_Time_Type * time)
201 {
202 RTC_PutPrescalerData(init->Div); /* Setup prescaler. */
203 while ( (0u == (RTC_GetStatus() & RTC_STATUS_OPERATION) ) && (0u != (time->WaitTime--) ) ) /* Wait for write operation finish, only after the end of the previous write operation can new write be performed. */
204 {
205 }
206
207 /* Calculate the total number of seconds of the current configuration time. */
208 uint32_t seccnt = 0u;
209 if ( (init->Years < init->LYears) || (init->Years > init->HYears) ) /* Exceeding the specified year. */
210 {
211 return false;
212 }
213 /* Calculate the number of seconds from the lowest years to the current setup years. */
214 for (uint16_t years = init->LYears; years < init->Years; years++)
215 {
216 if ( RTC_JudgeLeapYear(years) )
217 {
218 seccnt += 31622400u; /* The number of seconds in leap year is 31622400. */
219 }
220 else
221 {
222 seccnt += 31536000u; /* The number of seconds in normal year is 31622400. */
223 }
224 }
225
226 /* Add up the seconds of the previous month. */
227 init->Months -= 1u; /* The month count starts from 0 instead of 1, so current months - 1. */
228 for (uint16_t months = 0u; months < init->Months; months++)
229 {
230 seccnt += (uint32_t)month_table[months] * 86400u; /* Calculate the number of seconds of months, the total number of seconds in a day is 86400. */
231 if ( ( RTC_JudgeLeapYear(init->Years) ) && (months == 1u) ) /* The time is in a leap year and february, add the number of seconds in one day. */
232 {
233 seccnt += 86400u; /* The number of seconds in day is 86400. */
234 }
235 }
236 /* Add up the seconds of the previous date. */
237 seccnt += (uint32_t)(init->Days - 1u) * 86400u; /* The day set for initialization is less than 24 hours, which needs to be subtracted by one day. */
238 seccnt += (uint32_t)(init->Hours) * 3600u; /* There are 3600 seconds in a hour. */
239 seccnt += (uint32_t)(init->Mins) * 60u; /* There are 60 seconds in a minute. */
240 seccnt += (init->Secs);
241
242
243 while ( (0u == (RTC_GetStatus() & RTC_STATUS_OPERATION)) && (0u != (time->WaitTime--) ) ) /* Wait for write operation finish, only after the end of the previous write operation can new write be performed. */
244 {
245 }
246
247 /* Configrate counter value. */
248 RTC_PutCounterData(seccnt); /* Put data into counter. */
249 while ( ( 0u == (RTC_GetStatus() & RTC_STATUS_OPERATION) ) && (0u != (time->WaitTime--) ) ) /* Wait for write operation finish. */
250 {
251 }
252 if (0u == time->WaitTime) /* Timeout. */
253 {
254 return false;
255 }
256
257 return true;
258 }
259
260 /* Calculate and get current time. */
RTC_CalcTimeBlocking(RTC_Init_Type * init,RTC_Time_Type * time)261 void RTC_CalcTimeBlocking(RTC_Init_Type * init, RTC_Time_Type * time)
262 {
263 while ( 0u == (RTC_GetStatus() & RTC_STATUS_SYNC) && (0u != time->WaitTime) ) /* Wait for register synchronization, only register synchronization can read RTC register. */
264 {
265 time->WaitTime--;
266 }
267 uint32_t count = RTC_GetCounterData(); /* Get current seconds count. */
268
269 /* Calculated in days. */
270 uint16_t years = init->LYears;
271 uint32_t days = count / 86400u;
272
273 for (; days >= 365u; days -= 365u)
274 {
275 if ( RTC_JudgeLeapYear(years) ) /* Determine whether it is a leap year. */
276 {
277 if (days >= 366u)
278 {
279 days -= 1u;
280 }
281 else
282 {
283 break;
284 }
285 }
286 years++;
287 }
288 init->Years = years; /* Get current years. */
289
290 uint16_t months = 0u;
291 for (; days >= 28u; days -= 28u)
292 {
293 if ( ( true == RTC_JudgeLeapYear(init->Years) ) && (months == 1u) ) /* The time is February of leap year. */
294 {
295 if (days >= 29u)
296 {
297 days -= 1u;
298 }
299 else
300 {
301 break;
302 }
303 }
304 else
305 {
306 if (days >= month_table[months]) /* Reach the maximum number of days in the current month. */
307 {
308 days = days - month_table[months] + 28u;
309 }
310 else
311 {
312 break;
313 }
314 }
315 months++;
316 }
317 init->Months = months + 1u; /* Get current months. */
318 init->Days = days + 1u; /* Get current days. */
319 init->Hours = ( count % 86400u) / 3600u; /* Get current hours. */
320 init->Mins = ((count % 86400u) % 3600u) / 60u; /* Get current minutes. */
321 init->Secs = ((count % 86400u) % 3600u) % 60u; /* Get current seconds. */
322 }
323
324 /* Setup the alarm response time. */
RTC_SetAlarmBlocking(RTC_Time_Type * time)325 bool RTC_SetAlarmBlocking(RTC_Time_Type * time)
326 {
327 RTC_EnableInterrupts(RTC_INT_ALARM, true); /* Enable alarm interrupt. */
328 while ( 0u == (RTC_GetStatus() & RTC_STATUS_SYNC) && (0u != time->WaitTime) ) /* Wait for register synchronization, only register synchronization can read RTC register. */
329 {
330 time->WaitTime--;
331 }
332 uint32_t value = RTC_GetCounterData(); /* Get the current total number of seconds. */
333 RTC_PutAlarmData(value + time->AlarmTime); /* Set alarm respond time. */
334 while ( 0u == (RTC_GetStatus() & RTC_STATUS_OPERATION) && (0u != time->WaitTime) ) /* Wait for write operation finish. */
335 {
336 time->WaitTime--;
337 }
338 if (0u == time->WaitTime)
339 {
340 return false;
341 }
342 return true;
343 }
344
345 /* RTC interrupt request handler. */
RTC_TimeHandler(RTC_Init_Type * init,RTC_Time_Type * time,uint32_t interrupts)346 void RTC_TimeHandler(RTC_Init_Type * init, RTC_Time_Type * time, uint32_t interrupts)
347 {
348 /* Seconds interrupt. */
349 if ( ( 0u == (interrupts & RTC_INT_ALARM) ) && ( 1u == (interrupts & RTC_INT_SEC) ) )
350 {
351 RTC_CalcTimeBlocking(init, time); /* Get current time. */
352 if (time->SecDoneCallback)
353 {
354 (*(time->SecDoneCallback))((void *)time); /* Callback when seconds interrupt done. */
355 }
356 }
357
358 /* Alarm interrupt. */
359 if (0u != (interrupts & RTC_INT_ALARM) ) /* When the alarm count value is the same as the current count value, the alarm clock interrupt is generated. */
360 {
361 RTC_CalcTimeBlocking(init, time); /* Get current time. */
362 RTC_ClearInterruptStatus(RTC_INT_ALARM); /* Clear alarm interrupt status flag. */
363 if (time->AlarmDoneCallback)
364 {
365 (*(time->AlarmDoneCallback))((void *)time); /* Callback when alarm interrupt done. */
366 }
367 }
368 RTC_ClearInterruptStatus(RTC_INT_SEC | RTC_INT_OVERFLOW); /* Clear seconds interrupt status flag and overflow status flag. */
369 }
370
371 /* EOF. */
372
373