1 | /* ---------------------------------------------------------------------------- */ |
---|
2 | /* Atmel Microcontroller Software Support */ |
---|
3 | /* SAM Software Package License */ |
---|
4 | /* ---------------------------------------------------------------------------- */ |
---|
5 | /* Copyright (c) 2015, Atmel Corporation */ |
---|
6 | /* */ |
---|
7 | /* All rights reserved. */ |
---|
8 | /* */ |
---|
9 | /* Redistribution and use in source and binary forms, with or without */ |
---|
10 | /* modification, are permitted provided that the following condition is met: */ |
---|
11 | /* */ |
---|
12 | /* - Redistributions of source code must retain the above copyright notice, */ |
---|
13 | /* this list of conditions and the disclaimer below. */ |
---|
14 | /* */ |
---|
15 | /* Atmel's name may not be used to endorse or promote products derived from */ |
---|
16 | /* this software without specific prior written permission. */ |
---|
17 | /* */ |
---|
18 | /* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR */ |
---|
19 | /* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF */ |
---|
20 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE */ |
---|
21 | /* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, */ |
---|
22 | /* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */ |
---|
23 | /* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, */ |
---|
24 | /* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF */ |
---|
25 | /* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING */ |
---|
26 | /* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, */ |
---|
27 | /* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ |
---|
28 | /* ---------------------------------------------------------------------------- */ |
---|
29 | |
---|
30 | /** \addtogroup rtc_module Working with RTC |
---|
31 | * \ingroup peripherals_module |
---|
32 | * The RTC driver provides the interface to configure and use the RTC |
---|
33 | * peripheral. |
---|
34 | * |
---|
35 | * It manages date, time, and alarms.\n |
---|
36 | * This timer is clocked by the 32kHz system clock, and is not impacted by |
---|
37 | * power management settings (PMC). To be accurate, it is better to use an |
---|
38 | * external 32kHz crystal instead of the internal 32kHz RC.\n |
---|
39 | * |
---|
40 | * It uses BCD format, and time can be set in AM/PM or 24h mode through a |
---|
41 | * configuration bit in the mode register.\n |
---|
42 | * |
---|
43 | * To update date or time, the user has to follow these few steps : |
---|
44 | * <ul> |
---|
45 | * <li>Set UPDTIM and/or UPDCAL bit(s) in RTC_CR,</li> |
---|
46 | * <li>Polling or IRQ on the ACKUPD bit of RTC_CR,</li> |
---|
47 | * <li>Clear ACKUPD bit in RTC_SCCR,</li> |
---|
48 | * <li>Update Time and/or Calendar values in RTC_TIMR/RTC_CALR (BCD format),</li> |
---|
49 | * <li>Clear UPDTIM and/or UPDCAL bit in RTC_CR.</li> |
---|
50 | * </ul> |
---|
51 | * An alarm can be set to happen on month, date, hours, minutes or seconds, |
---|
52 | * by setting the proper "Enable" bit of each of these fields in the Time and |
---|
53 | * Calendar registers. |
---|
54 | * This allows a large number of configurations to be available for the user. |
---|
55 | * Alarm occurrence can be detected even by polling or interrupt. |
---|
56 | * |
---|
57 | * A check of the validity of the date and time format and values written by |
---|
58 | * the user is automatically done. |
---|
59 | * Errors are reported through the Valid Entry Register. |
---|
60 | * |
---|
61 | * For more accurate information, please look at the RTC section of the |
---|
62 | * Datasheet. |
---|
63 | * |
---|
64 | * Related files :\n |
---|
65 | * \ref rtc.c\n |
---|
66 | * \ref rtc.h.\n |
---|
67 | */ |
---|
68 | /*@{*/ |
---|
69 | /*@}*/ |
---|
70 | |
---|
71 | |
---|
72 | /** |
---|
73 | * \file |
---|
74 | * |
---|
75 | * Implementation of Real Time Clock (RTC) controller. |
---|
76 | * |
---|
77 | */ |
---|
78 | |
---|
79 | /*---------------------------------------------------------------------------- |
---|
80 | * Headers |
---|
81 | *----------------------------------------------------------------------------*/ |
---|
82 | |
---|
83 | #include "chip.h" |
---|
84 | |
---|
85 | #include <stdint.h> |
---|
86 | #include <assert.h> |
---|
87 | |
---|
88 | /*---------------------------------------------------------------------------- |
---|
89 | * Exported functions |
---|
90 | *----------------------------------------------------------------------------*/ |
---|
91 | |
---|
92 | /** |
---|
93 | * \brief Sets the RTC in either 12 or 24 hour mode. |
---|
94 | * |
---|
95 | * \param mode Hour mode. |
---|
96 | */ |
---|
97 | extern void RTC_SetHourMode(Rtc *pRtc, uint32_t dwMode) |
---|
98 | { |
---|
99 | assert((dwMode & 0xFFFFFFFE) == 0); |
---|
100 | |
---|
101 | pRtc->RTC_MR = dwMode; |
---|
102 | } |
---|
103 | |
---|
104 | /** |
---|
105 | * \brief Gets the RTC mode. |
---|
106 | * |
---|
107 | * \return Hour mode. |
---|
108 | */ |
---|
109 | extern uint32_t RTC_GetHourMode(Rtc *pRtc) |
---|
110 | { |
---|
111 | uint32_t dwMode; |
---|
112 | |
---|
113 | TRACE_DEBUG("RTC_SetHourMode()\n\r"); |
---|
114 | |
---|
115 | dwMode = pRtc->RTC_MR; |
---|
116 | dwMode &= 0xFFFFFFFE; |
---|
117 | |
---|
118 | return dwMode; |
---|
119 | } |
---|
120 | |
---|
121 | /** |
---|
122 | * \brief Enables the selected interrupt sources of the RTC. |
---|
123 | * |
---|
124 | * \param sources Interrupt sources to enable. |
---|
125 | */ |
---|
126 | extern void RTC_EnableIt(Rtc *pRtc, uint32_t dwSources) |
---|
127 | { |
---|
128 | assert((dwSources & (uint32_t)(~0x1F)) == 0); |
---|
129 | |
---|
130 | TRACE_DEBUG("RTC_EnableIt()\n\r"); |
---|
131 | |
---|
132 | pRtc->RTC_IER = dwSources; |
---|
133 | } |
---|
134 | |
---|
135 | /** |
---|
136 | * \brief Disables the selected interrupt sources of the RTC. |
---|
137 | * |
---|
138 | * \param sources Interrupt sources to disable. |
---|
139 | */ |
---|
140 | extern void RTC_DisableIt(Rtc *pRtc, uint32_t dwSources) |
---|
141 | { |
---|
142 | assert((dwSources & (uint32_t)(~0x1F)) == 0); |
---|
143 | |
---|
144 | TRACE_DEBUG("RTC_DisableIt()\n\r"); |
---|
145 | |
---|
146 | pRtc->RTC_IDR = dwSources; |
---|
147 | } |
---|
148 | |
---|
149 | /** |
---|
150 | * \brief Sets the current time in the RTC. |
---|
151 | * |
---|
152 | * \note In successive update operations, the user must wait at least one second |
---|
153 | * after resetting the UPDTIM/UPDCAL bit in the RTC_CR before setting these |
---|
154 | * bits again. Please look at the RTC section of the datasheet for detail. |
---|
155 | * |
---|
156 | * \param ucHour Current hour in 12 or 24 hour mode. |
---|
157 | * \param ucMinute Current minute. |
---|
158 | * \param ucSecond Current second. |
---|
159 | * |
---|
160 | * \return 0 success, 1 fail to set |
---|
161 | */ |
---|
162 | extern int RTC_SetTime(Rtc *pRtc, uint8_t ucHour, uint8_t ucMinute, |
---|
163 | uint8_t ucSecond) |
---|
164 | { |
---|
165 | uint32_t dwTime = 0; |
---|
166 | uint8_t ucHour_bcd; |
---|
167 | uint8_t ucMin_bcd; |
---|
168 | uint8_t ucSec_bcd; |
---|
169 | |
---|
170 | TRACE_DEBUG("RTC_SetTime(%02d:%02d:%02d)\n\r", ucHour, ucMinute, ucSecond); |
---|
171 | |
---|
172 | /* if 12-hour mode, set AMPM bit */ |
---|
173 | if ((pRtc->RTC_MR & RTC_MR_HRMOD) == RTC_MR_HRMOD) { |
---|
174 | if (ucHour > 12) { |
---|
175 | ucHour -= 12; |
---|
176 | dwTime |= RTC_TIMR_AMPM; |
---|
177 | } |
---|
178 | } |
---|
179 | |
---|
180 | ucHour_bcd = (ucHour % 10) | ((ucHour / 10) << 4); |
---|
181 | ucMin_bcd = (ucMinute % 10) | ((ucMinute / 10) << 4); |
---|
182 | ucSec_bcd = (ucSecond % 10) | ((ucSecond / 10) << 4); |
---|
183 | |
---|
184 | /* value overflow */ |
---|
185 | if ((ucHour_bcd & (uint8_t)(~RTC_HOUR_BIT_LEN_MASK)) | |
---|
186 | (ucMin_bcd & (uint8_t)(~RTC_MIN_BIT_LEN_MASK)) | |
---|
187 | (ucSec_bcd & (uint8_t)(~RTC_SEC_BIT_LEN_MASK))) |
---|
188 | return 1; |
---|
189 | |
---|
190 | dwTime = ucSec_bcd | (ucMin_bcd << 8) | (ucHour_bcd << 16); |
---|
191 | |
---|
192 | pRtc->RTC_CR |= RTC_CR_UPDTIM; |
---|
193 | |
---|
194 | while ((pRtc->RTC_SR & RTC_SR_ACKUPD) != RTC_SR_ACKUPD); |
---|
195 | |
---|
196 | pRtc->RTC_SCCR = RTC_SCCR_ACKCLR; |
---|
197 | pRtc->RTC_TIMR = dwTime; |
---|
198 | pRtc->RTC_CR &= (uint32_t)(~RTC_CR_UPDTIM); |
---|
199 | pRtc->RTC_SCCR |= RTC_SCCR_SECCLR; |
---|
200 | |
---|
201 | return (int)(pRtc->RTC_VER & RTC_VER_NVTIM); |
---|
202 | } |
---|
203 | |
---|
204 | /** |
---|
205 | * \brief Retrieves the current time as stored in the RTC in several variables. |
---|
206 | * |
---|
207 | * \param pucHour If not null, current hour is stored in this variable. |
---|
208 | * \param pucMinute If not null, current minute is stored in this variable. |
---|
209 | * \param pucSecond If not null, current second is stored in this variable. |
---|
210 | */ |
---|
211 | extern void RTC_GetTime(Rtc *pRtc, uint8_t *pucHour, |
---|
212 | uint8_t *pucMinute, uint8_t *pucSecond) |
---|
213 | { |
---|
214 | uint32_t dwTime; |
---|
215 | |
---|
216 | TRACE_DEBUG("RTC_GetTime()\n\r"); |
---|
217 | |
---|
218 | /* Get current RTC time */ |
---|
219 | dwTime = pRtc->RTC_TIMR; |
---|
220 | |
---|
221 | while (dwTime != pRtc->RTC_TIMR) |
---|
222 | dwTime = pRtc->RTC_TIMR; |
---|
223 | |
---|
224 | /* Hour */ |
---|
225 | if (pucHour) { |
---|
226 | *pucHour = ((dwTime & 0x00300000) >> 20) * 10 |
---|
227 | + ((dwTime & 0x000F0000) >> 16); |
---|
228 | |
---|
229 | if ((dwTime & RTC_TIMR_AMPM) == RTC_TIMR_AMPM) |
---|
230 | *pucHour += 12; |
---|
231 | } |
---|
232 | |
---|
233 | /* Minute */ |
---|
234 | if (pucMinute) { |
---|
235 | *pucMinute = ((dwTime & 0x00007000) >> 12) * 10 |
---|
236 | + ((dwTime & 0x00000F00) >> 8); |
---|
237 | } |
---|
238 | |
---|
239 | /* Second */ |
---|
240 | if (pucSecond) { |
---|
241 | *pucSecond = ((dwTime & 0x00000070) >> 4) * 10 |
---|
242 | + (dwTime & 0x0000000F); |
---|
243 | } |
---|
244 | } |
---|
245 | |
---|
246 | /** |
---|
247 | * \brief Sets a time alarm on the RTC. |
---|
248 | * The match is performed only on the provided variables; |
---|
249 | * Setting all pointers to 0 disables the time alarm. |
---|
250 | * |
---|
251 | * \note In AM/PM mode, the hour value must have bit #7 set for PM, cleared for |
---|
252 | * AM (as expected in the time registers). |
---|
253 | * |
---|
254 | * \param pucHour If not null, the time alarm will hour-match this value. |
---|
255 | * \param pucMinute If not null, the time alarm will minute-match this value. |
---|
256 | * \param pucSecond If not null, the time alarm will second-match this value. |
---|
257 | * |
---|
258 | * \return 0 success, 1 fail to set |
---|
259 | */ |
---|
260 | extern int RTC_SetTimeAlarm(Rtc *pRtc, uint8_t *pucHour, |
---|
261 | uint8_t *pucMinute, uint8_t *pucSecond) |
---|
262 | { |
---|
263 | uint32_t dwAlarm = 0; |
---|
264 | uint32_t dwAlarmEnable = 0; |
---|
265 | |
---|
266 | TRACE_DEBUG("RTC_SetTimeAlarm()\n\r"); |
---|
267 | |
---|
268 | /* Hour */ |
---|
269 | if (pucHour) { |
---|
270 | dwAlarm |= ((*pucHour / 10) << 20) | (( *pucHour % 10) << 16); |
---|
271 | dwAlarmEnable |= RTC_TIMALR_HOUREN; |
---|
272 | } |
---|
273 | |
---|
274 | /* Minute */ |
---|
275 | if (pucMinute) { |
---|
276 | dwAlarm |= ((*pucMinute / 10) << 12) | ((*pucMinute % 10) << 8); |
---|
277 | dwAlarmEnable |= RTC_TIMALR_MINEN; |
---|
278 | } |
---|
279 | |
---|
280 | /* Second */ |
---|
281 | if (pucSecond) { |
---|
282 | dwAlarm |= ((*pucSecond / 10) << 4) | (*pucSecond % 10); |
---|
283 | dwAlarmEnable |= RTC_TIMALR_SECEN; |
---|
284 | } |
---|
285 | |
---|
286 | pRtc->RTC_TIMALR = 0; |
---|
287 | pRtc->RTC_TIMALR = dwAlarm; |
---|
288 | pRtc->RTC_TIMALR = dwAlarm | dwAlarmEnable; |
---|
289 | |
---|
290 | return (int)(pRtc->RTC_VER & RTC_VER_NVTIMALR); |
---|
291 | } |
---|
292 | |
---|
293 | /** |
---|
294 | * \brief Retrieves the current year, month and day from the RTC. |
---|
295 | * Month, day and week values are numbered starting at 1. |
---|
296 | * |
---|
297 | * \param pYwear Current year (optional). |
---|
298 | * \param pucMonth Current month (optional). |
---|
299 | * \param pucDay Current day (optional). |
---|
300 | * \param pucWeek Current day in current week (optional). |
---|
301 | */ |
---|
302 | extern void RTC_GetDate(Rtc *pRtc, uint16_t *pwYear, uint8_t *pucMonth, |
---|
303 | uint8_t *pucDay, uint8_t *pucWeek) |
---|
304 | { |
---|
305 | uint32_t dwDate; |
---|
306 | |
---|
307 | /* Get current date (multiple reads are necessary to insure a stable value) */ |
---|
308 | do { |
---|
309 | dwDate = pRtc->RTC_CALR; |
---|
310 | } while (dwDate != pRtc->RTC_CALR); |
---|
311 | |
---|
312 | /* Retrieve year */ |
---|
313 | if (pwYear) { |
---|
314 | *pwYear = (((dwDate >> 4) & 0x7) * 1000) |
---|
315 | + ((dwDate & 0xF) * 100) |
---|
316 | + (((dwDate >> 12) & 0xF) * 10) |
---|
317 | + ((dwDate >> 8) & 0xF); |
---|
318 | } |
---|
319 | |
---|
320 | /* Retrieve month */ |
---|
321 | if (pucMonth) |
---|
322 | *pucMonth = (((dwDate >> 20) & 1) * 10) + ((dwDate >> 16) & 0xF); |
---|
323 | |
---|
324 | /* Retrieve day */ |
---|
325 | if (pucDay) |
---|
326 | *pucDay = (((dwDate >> 28) & 0x3) * 10) + ((dwDate >> 24) & 0xF); |
---|
327 | |
---|
328 | /* Retrieve week */ |
---|
329 | if (pucWeek) |
---|
330 | *pucWeek = ((dwDate >> 21) & 0x7); |
---|
331 | } |
---|
332 | |
---|
333 | /** |
---|
334 | * \brief Sets the current year, month and day in the RTC. |
---|
335 | * Month, day and week values must be numbered starting from 1. |
---|
336 | * |
---|
337 | * \note In successive update operations, the user must wait at least one second |
---|
338 | * after resetting the UPDTIM/UPDCAL bit in the RTC_CR before setting these |
---|
339 | * bits again. Please look at the RTC section of the datasheet for detail. |
---|
340 | * |
---|
341 | * \param wYear Current year. |
---|
342 | * \param ucMonth Current month. |
---|
343 | * \param ucDay Current day. |
---|
344 | * \param ucWeek Day number in current week. |
---|
345 | * |
---|
346 | * \return 0 success, 1 fail to set |
---|
347 | */ |
---|
348 | extern int RTC_SetDate(Rtc *pRtc, uint16_t wYear, uint8_t ucMonth, |
---|
349 | uint8_t ucDay, uint8_t ucWeek) |
---|
350 | { |
---|
351 | uint32_t wDate; |
---|
352 | uint8_t ucCent_bcd; |
---|
353 | uint8_t ucYear_bcd; |
---|
354 | uint8_t ucMonth_bcd; |
---|
355 | uint8_t ucDay_bcd; |
---|
356 | uint8_t ucWeek_bcd; |
---|
357 | |
---|
358 | ucCent_bcd = ((wYear / 100) % 10) | ((wYear / 1000) << 4); |
---|
359 | ucYear_bcd = (wYear % 10) | (((wYear / 10) % 10) << 4); |
---|
360 | ucMonth_bcd = ((ucMonth % 10) | (ucMonth / 10) << 4); |
---|
361 | ucDay_bcd = ((ucDay % 10) | (ucDay / 10) << 4); |
---|
362 | ucWeek_bcd = ((ucWeek % 10) | (ucWeek / 10) << 4); |
---|
363 | |
---|
364 | /* value over flow */ |
---|
365 | if ((ucCent_bcd & (uint8_t)(~RTC_CENT_BIT_LEN_MASK)) | |
---|
366 | (ucYear_bcd & (uint8_t)(~RTC_YEAR_BIT_LEN_MASK)) | |
---|
367 | (ucMonth_bcd & (uint8_t)(~RTC_MONTH_BIT_LEN_MASK)) | |
---|
368 | (ucWeek_bcd & (uint8_t)(~RTC_WEEK_BIT_LEN_MASK)) | |
---|
369 | (ucDay_bcd & (uint8_t)(~RTC_DATE_BIT_LEN_MASK)) |
---|
370 | ) |
---|
371 | return 1; |
---|
372 | |
---|
373 | |
---|
374 | /* Convert values to date register value */ |
---|
375 | wDate = ucCent_bcd | |
---|
376 | (ucYear_bcd << 8) | |
---|
377 | (ucMonth_bcd << 16) | |
---|
378 | (ucWeek_bcd << 21) | |
---|
379 | (ucDay_bcd << 24); |
---|
380 | |
---|
381 | /* Update calendar register */ |
---|
382 | pRtc->RTC_CR |= RTC_CR_UPDCAL; |
---|
383 | |
---|
384 | while ((pRtc->RTC_SR & RTC_SR_ACKUPD) != RTC_SR_ACKUPD); |
---|
385 | |
---|
386 | pRtc->RTC_SCCR = RTC_SCCR_ACKCLR; |
---|
387 | pRtc->RTC_CALR = wDate; |
---|
388 | pRtc->RTC_CR &= (uint32_t)(~RTC_CR_UPDCAL); |
---|
389 | pRtc->RTC_SCCR |= RTC_SCCR_SECCLR; /* clear SECENV in SCCR */ |
---|
390 | |
---|
391 | return (int)(pRtc->RTC_VER & RTC_VER_NVCAL); |
---|
392 | } |
---|
393 | |
---|
394 | /** |
---|
395 | * \brief Sets a date alarm in the RTC. |
---|
396 | * The alarm will match only the provided values; |
---|
397 | * Passing a null-pointer disables the corresponding field match. |
---|
398 | * |
---|
399 | * \param pucMonth If not null, the RTC alarm will month-match this value. |
---|
400 | * \param pucDay If not null, the RTC alarm will day-match this value. |
---|
401 | * |
---|
402 | * \return 0 success, 1 fail to set |
---|
403 | */ |
---|
404 | extern int RTC_SetDateAlarm(Rtc *pRtc, uint8_t *pucMonth, uint8_t *pucDay) |
---|
405 | { |
---|
406 | uint32_t dwAlarm; |
---|
407 | |
---|
408 | dwAlarm = ((pucMonth) || (pucDay)) ? (0) : (0x01010000); |
---|
409 | |
---|
410 | TRACE_DEBUG("RTC_SetDateAlarm()\n\r"); |
---|
411 | |
---|
412 | /* Compute alarm field value */ |
---|
413 | if (pucMonth) { |
---|
414 | dwAlarm |= RTC_CALALR_MTHEN | ((*pucMonth / 10) << 20) |
---|
415 | | ((*pucMonth % 10) << 16); |
---|
416 | } |
---|
417 | |
---|
418 | if (pucDay) { |
---|
419 | dwAlarm |= RTC_CALALR_DATEEN | ((*pucDay / 10) << 28) |
---|
420 | | ((*pucDay % 10) << 24); |
---|
421 | } |
---|
422 | |
---|
423 | /* Set alarm */ |
---|
424 | pRtc->RTC_CALALR = dwAlarm; |
---|
425 | |
---|
426 | return (int)(pRtc->RTC_VER & RTC_VER_NVCALALR); |
---|
427 | } |
---|
428 | |
---|
429 | /** |
---|
430 | * \brief Clear flag bits of status clear command register in the RTC. |
---|
431 | * |
---|
432 | * \param mask Bits mask of cleared events |
---|
433 | */ |
---|
434 | extern void RTC_ClearSCCR(Rtc *pRtc, uint32_t dwMask) |
---|
435 | { |
---|
436 | /* Clear all flag bits in status clear command register */ |
---|
437 | dwMask &= RTC_SCCR_ACKCLR | RTC_SCCR_ALRCLR | RTC_SCCR_SECCLR |
---|
438 | | RTC_SCCR_TIMCLR | RTC_SCCR_CALCLR; |
---|
439 | |
---|
440 | pRtc->RTC_SCCR = dwMask; |
---|
441 | } |
---|
442 | |
---|
443 | /** |
---|
444 | * \brief Get flag bits of status register in the RTC. |
---|
445 | * |
---|
446 | * \param mask Bits mask of Status Register |
---|
447 | * |
---|
448 | * \return Status register & mask |
---|
449 | */ |
---|
450 | extern uint32_t RTC_GetSR(Rtc *pRtc, uint32_t dwMask) |
---|
451 | { |
---|
452 | uint32_t dwEvent; |
---|
453 | |
---|
454 | dwEvent = pRtc->RTC_SR; |
---|
455 | |
---|
456 | return (dwEvent & dwMask); |
---|
457 | } |
---|
458 | |
---|