source: rtems/bsps/arm/atsam/contrib/libraries/libchip/source/rtc.c @ 5d4a81f5

5
Last change on this file since 5d4a81f5 was 5d4a81f5, checked in by Sebastian Huber <sebastian.huber@…>, on 06/12/19 at 10:51:20

bsp/atsam: Fix RTC_SetTimeAlarm()

Set the alarm time according to the note in the datasheet.

  • Property mode set to 100644
File size: 13.6 KB
Line 
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 */
97extern 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 */
109extern 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 */
126extern 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 */
140extern 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 */
162extern 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 */
211extern 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 */
260extern 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 */
302extern 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 */
348extern 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 */
404extern 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 */
434extern 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 */
450extern 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
Note: See TracBrowser for help on using the repository browser.