/* ---------------------------------------------------------------------------- | |
* ATMEL Microcontroller Software Support | |
* ---------------------------------------------------------------------------- | |
* Copyright (c) 2009, Atmel Corporation | |
* | |
* All rights reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions are met: | |
* | |
* - Redistributions of source code must retain the above copyright notice, | |
* this list of conditions and the disclaimer below. | |
* | |
* Atmel's name may not be used to endorse or promote products derived from | |
* this software without specific prior written permission. | |
* | |
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR | |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE | |
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, | |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, | |
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | |
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
* ---------------------------------------------------------------------------- | |
*/ | |
/** \addtogroup rtc_module Working with RTC | |
* The RTC driver provides the interface to configure and use the RTC | |
* peripheral. | |
* | |
* It manages date, time, and alarms.\n | |
* This timer is clocked by the 32kHz system clock, and is not impacted by | |
* power management settings (PMC). To be accurate, it is better to use an | |
* external 32kHz crystal instead of the internal 32kHz RC.\n | |
* | |
* It uses BCD format, and time can be set in AM/PM or 24h mode through a | |
* configuration bit in the mode register.\n | |
* | |
* To update date or time, the user has to follow these few steps : | |
* <ul> | |
* <li>Set UPDTIM and/or UPDCAL bit(s) in RTC_CR,</li> | |
* <li>Polling or IRQ on the ACKUPD bit of RTC_CR,</li> | |
* <li>Clear ACKUPD bit in RTC_SCCR,</li> | |
* <li>Update Time and/or Calendar values in RTC_TIMR/RTC_CALR (BCD format),</li> | |
* <li>Clear UPDTIM and/or UPDCAL bit in RTC_CR.</li> | |
* </ul> | |
* An alarm can be set to happen on month, date, hours, minutes or seconds, | |
* by setting the proper "Enable" bit of each of these fields in the Time and | |
* Calendar registers. | |
* This allows a large number of configurations to be available for the user. | |
* Alarm occurence can be detected even by polling or interrupt. | |
* | |
* A check of the validity of the date and time format and values written by the user is automatically done. | |
* Errors are reported through the Valid Entry Register. | |
* | |
* For more accurate information, please look at the RTC section of the | |
* Datasheet. | |
* | |
* Related files :\n | |
* \ref rtc.c\n | |
* \ref rtc.h.\n | |
*/ | |
/*@{*/ | |
/*@}*/ | |
/** | |
* \file | |
* | |
* Implementation of Real Time Clock (RTC) controller. | |
* | |
*/ | |
/*---------------------------------------------------------------------------- | |
* Headers | |
*----------------------------------------------------------------------------*/ | |
#include "chip.h" | |
#include <stdint.h> | |
#include <assert.h> | |
/*---------------------------------------------------------------------------- | |
* Exported functions | |
*----------------------------------------------------------------------------*/ | |
/** | |
* \brief Sets the RTC in either 12 or 24 hour mode. | |
* | |
* \param mode Hour mode. | |
*/ | |
extern void RTC_SetHourMode( Rtc* pRtc, uint32_t dwMode ) | |
{ | |
assert((dwMode & 0xFFFFFFFE) == 0); | |
pRtc->RTC_MR = dwMode ; | |
} | |
/** | |
* \brief Gets the RTC mode. | |
* | |
* \return Hour mode. | |
*/ | |
extern uint32_t RTC_GetHourMode( Rtc* pRtc ) | |
{ | |
uint32_t dwMode ; | |
TRACE_DEBUG( "RTC_SetHourMode()\n\r" ) ; | |
dwMode = pRtc->RTC_MR; | |
dwMode &= 0xFFFFFFFE; | |
return dwMode ; | |
} | |
/** | |
* \brief Enables the selected interrupt sources of the RTC. | |
* | |
* \param sources Interrupt sources to enable. | |
*/ | |
extern void RTC_EnableIt( Rtc* pRtc, uint32_t dwSources ) | |
{ | |
assert((dwSources & (uint32_t)(~0x1F)) == 0); | |
TRACE_DEBUG( "RTC_EnableIt()\n\r" ) ; | |
pRtc->RTC_IER = dwSources ; | |
} | |
/** | |
* \brief Disables the selected interrupt sources of the RTC. | |
* | |
* \param sources Interrupt sources to disable. | |
*/ | |
extern void RTC_DisableIt( Rtc* pRtc, uint32_t dwSources ) | |
{ | |
assert((dwSources & (uint32_t)(~0x1F)) == 0); | |
TRACE_DEBUG( "RTC_DisableIt()\n\r" ) ; | |
pRtc->RTC_IDR = dwSources ; | |
} | |
/** | |
* \brief Sets the current time in the RTC. | |
* | |
* \note In successive update operations, the user must wait at least one second | |
* after resetting the UPDTIM/UPDCAL bit in the RTC_CR before setting these | |
* bits again. Please look at the RTC section of the datasheet for detail. | |
* | |
* \param ucHour Current hour in 12 or 24 hour mode. | |
* \param ucMinute Current minute. | |
* \param ucSecond Current second. | |
* | |
* \return 0 sucess, 1 fail to set | |
*/ | |
extern int RTC_SetTime( Rtc* pRtc, uint8_t ucHour, uint8_t ucMinute, uint8_t ucSecond ) | |
{ | |
uint32_t dwTime=0 ; | |
uint8_t ucHour_bcd ; | |
uint8_t ucMin_bcd ; | |
uint8_t ucSec_bcd ; | |
TRACE_DEBUG( "RTC_SetTime(%02d:%02d:%02d)\n\r", ucHour, ucMinute, ucSecond ) ; | |
/* if 12-hour mode, set AMPM bit */ | |
if ( (pRtc->RTC_MR & RTC_MR_HRMOD) == RTC_MR_HRMOD ) | |
{ | |
if ( ucHour > 12 ) | |
{ | |
ucHour -= 12 ; | |
dwTime |= RTC_TIMR_AMPM ; | |
} | |
} | |
ucHour_bcd = (ucHour%10) | ((ucHour/10)<<4) ; | |
ucMin_bcd = (ucMinute%10) | ((ucMinute/10)<<4) ; | |
ucSec_bcd = (ucSecond%10) | ((ucSecond/10)<<4) ; | |
/* value overflow */ | |
if ( (ucHour_bcd & (uint8_t)(~RTC_HOUR_BIT_LEN_MASK)) | | |
(ucMin_bcd & (uint8_t)(~RTC_MIN_BIT_LEN_MASK)) | | |
(ucSec_bcd & (uint8_t)(~RTC_SEC_BIT_LEN_MASK))) | |
{ | |
return 1 ; | |
} | |
dwTime = ucSec_bcd | (ucMin_bcd << 8) | (ucHour_bcd<<16) ; | |
pRtc->RTC_CR |= RTC_CR_UPDTIM ; | |
while ((pRtc->RTC_SR & RTC_SR_ACKUPD) != RTC_SR_ACKUPD) ; | |
pRtc->RTC_SCCR = RTC_SCCR_ACKCLR ; | |
pRtc->RTC_TIMR = dwTime ; | |
pRtc->RTC_CR &= (uint32_t)(~RTC_CR_UPDTIM) ; | |
pRtc->RTC_SCCR |= RTC_SCCR_SECCLR ; | |
return (int)(pRtc->RTC_VER & RTC_VER_NVTIM) ; | |
} | |
/** | |
* \brief Retrieves the current time as stored in the RTC in several variables. | |
* | |
* \param pucHour If not null, current hour is stored in this variable. | |
* \param pucMinute If not null, current minute is stored in this variable. | |
* \param pucSecond If not null, current second is stored in this variable. | |
*/ | |
extern void RTC_GetTime( Rtc* pRtc, uint8_t *pucHour, uint8_t *pucMinute, uint8_t *pucSecond ) | |
{ | |
uint32_t dwTime ; | |
TRACE_DEBUG( "RTC_GetTime()\n\r" ) ; | |
/* Get current RTC time */ | |
dwTime = pRtc->RTC_TIMR ; | |
while ( dwTime != pRtc->RTC_TIMR ) | |
{ | |
dwTime = pRtc->RTC_TIMR ; | |
} | |
/* Hour */ | |
if ( pucHour ) | |
{ | |
*pucHour = ((dwTime & 0x00300000) >> 20) * 10 | |
+ ((dwTime & 0x000F0000) >> 16); | |
if ( (dwTime & RTC_TIMR_AMPM) == RTC_TIMR_AMPM ) | |
{ | |
*pucHour += 12 ; | |
} | |
} | |
/* Minute */ | |
if ( pucMinute ) | |
{ | |
*pucMinute = ((dwTime & 0x00007000) >> 12) * 10 | |
+ ((dwTime & 0x00000F00) >> 8); | |
} | |
/* Second */ | |
if ( pucSecond ) | |
{ | |
*pucSecond = ((dwTime & 0x00000070) >> 4) * 10 | |
+ (dwTime & 0x0000000F); | |
} | |
} | |
/** | |
* \brief Sets a time alarm on the RTC. | |
* The match is performed only on the provided variables; | |
* Setting all pointers to 0 disables the time alarm. | |
* | |
* \note In AM/PM mode, the hour value must have bit #7 set for PM, cleared for | |
* AM (as expected in the time registers). | |
* | |
* \param pucHour If not null, the time alarm will hour-match this value. | |
* \param pucMinute If not null, the time alarm will minute-match this value. | |
* \param pucSecond If not null, the time alarm will second-match this value. | |
* | |
* \return 0 success, 1 fail to set | |
*/ | |
extern int RTC_SetTimeAlarm( Rtc* pRtc, uint8_t *pucHour, uint8_t *pucMinute, uint8_t *pucSecond ) | |
{ | |
uint32_t dwAlarm=0 ; | |
TRACE_DEBUG( "RTC_SetTimeAlarm()\n\r" ) ; | |
/* Hour */ | |
if ( pucHour ) | |
{ | |
dwAlarm |= RTC_TIMALR_HOUREN | ((*pucHour / 10) << 20) | ((*pucHour % 10) << 16); | |
} | |
/* Minute */ | |
if ( pucMinute ) | |
{ | |
dwAlarm |= RTC_TIMALR_MINEN | ((*pucMinute / 10) << 12) | ((*pucMinute % 10) << 8); | |
} | |
/* Second */ | |
if ( pucSecond ) | |
{ | |
dwAlarm |= RTC_TIMALR_SECEN | ((*pucSecond / 10) << 4) | (*pucSecond % 10); | |
} | |
pRtc->RTC_TIMALR = dwAlarm ; | |
return (int)(pRtc->RTC_VER & RTC_VER_NVTIMALR) ; | |
} | |
/** | |
* \brief Retrieves the current year, month and day from the RTC. | |
* Month, day and week values are numbered starting at 1. | |
* | |
* \param pYwear Current year (optional). | |
* \param pucMonth Current month (optional). | |
* \param pucDay Current day (optional). | |
* \param pucWeek Current day in current week (optional). | |
*/ | |
extern void RTC_GetDate( Rtc* pRtc, uint16_t *pwYear, uint8_t *pucMonth, uint8_t *pucDay, uint8_t *pucWeek ) | |
{ | |
uint32_t dwDate ; | |
/* Get current date (multiple reads are necessary to insure a stable value) */ | |
do | |
{ | |
dwDate = pRtc->RTC_CALR ; | |
} | |
while ( dwDate != pRtc->RTC_CALR ) ; | |
/* Retrieve year */ | |
if ( pwYear ) | |
{ | |
*pwYear = (((dwDate >> 4) & 0x7) * 1000) | |
+ ((dwDate & 0xF) * 100) | |
+ (((dwDate >> 12) & 0xF) * 10) | |
+ ((dwDate >> 8) & 0xF); | |
} | |
/* Retrieve month */ | |
if ( pucMonth ) | |
{ | |
*pucMonth = (((dwDate >> 20) & 1) * 10) + ((dwDate >> 16) & 0xF); | |
} | |
/* Retrieve day */ | |
if ( pucDay ) | |
{ | |
*pucDay = (((dwDate >> 28) & 0x3) * 10) + ((dwDate >> 24) & 0xF); | |
} | |
/* Retrieve week */ | |
if ( pucWeek ) | |
{ | |
*pucWeek = ((dwDate >> 21) & 0x7); | |
} | |
} | |
/** | |
* \brief Sets the current year, month and day in the RTC. | |
* Month, day and week values must be numbered starting from 1. | |
* | |
* \note In successive update operations, the user must wait at least one second | |
* after resetting the UPDTIM/UPDCAL bit in the RTC_CR before setting these | |
* bits again. Please look at the RTC section of the datasheet for detail. | |
* | |
* \param wYear Current year. | |
* \param ucMonth Current month. | |
* \param ucDay Current day. | |
* \param ucWeek Day number in current week. | |
* | |
* \return 0 success, 1 fail to set | |
*/ | |
extern int RTC_SetDate( Rtc* pRtc, uint16_t wYear, uint8_t ucMonth, uint8_t ucDay, uint8_t ucWeek ) | |
{ | |
uint32_t wDate ; | |
uint8_t ucCent_bcd ; | |
uint8_t ucYear_bcd ; | |
uint8_t ucMonth_bcd ; | |
uint8_t ucDay_bcd ; | |
uint8_t ucWeek_bcd ; | |
ucCent_bcd = ((wYear/100)%10) | ((wYear/1000)<<4); | |
ucYear_bcd = (wYear%10) | (((wYear/10)%10)<<4); | |
ucMonth_bcd = ((ucMonth%10) | (ucMonth/10)<<4); | |
ucDay_bcd = ((ucDay%10) | (ucDay/10)<<4); | |
ucWeek_bcd = ((ucWeek%10) | (ucWeek/10)<<4); | |
/* value over flow */ | |
if ( (ucCent_bcd & (uint8_t)(~RTC_CENT_BIT_LEN_MASK)) | | |
(ucYear_bcd & (uint8_t)(~RTC_YEAR_BIT_LEN_MASK)) | | |
(ucMonth_bcd & (uint8_t)(~RTC_MONTH_BIT_LEN_MASK)) | | |
(ucWeek_bcd & (uint8_t)(~RTC_WEEK_BIT_LEN_MASK)) | | |
(ucDay_bcd & (uint8_t)(~RTC_DATE_BIT_LEN_MASK)) | |
) | |
{ | |
return 1 ; | |
} | |
/* Convert values to date register value */ | |
wDate = ucCent_bcd | | |
(ucYear_bcd << 8) | | |
(ucMonth_bcd << 16) | | |
(ucWeek_bcd << 21) | | |
(ucDay_bcd << 24); | |
/* Update calendar register */ | |
pRtc->RTC_CR |= RTC_CR_UPDCAL ; | |
while ((pRtc->RTC_SR & RTC_SR_ACKUPD) != RTC_SR_ACKUPD) ; | |
pRtc->RTC_SCCR = RTC_SCCR_ACKCLR; | |
pRtc->RTC_CALR = wDate ; | |
pRtc->RTC_CR &= (uint32_t)(~RTC_CR_UPDCAL) ; | |
pRtc->RTC_SCCR |= RTC_SCCR_SECCLR; /* clear SECENV in SCCR */ | |
return (int)(pRtc->RTC_VER & RTC_VER_NVCAL) ; | |
} | |
/** | |
* \brief Sets a date alarm in the RTC. | |
* The alarm will match only the provided values; | |
* Passing a null-pointer disables the corresponding field match. | |
* | |
* \param pucMonth If not null, the RTC alarm will month-match this value. | |
* \param pucDay If not null, the RTC alarm will day-match this value. | |
* | |
* \return 0 success, 1 fail to set | |
*/ | |
extern int RTC_SetDateAlarm( Rtc* pRtc, uint8_t *pucMonth, uint8_t *pucDay ) | |
{ | |
uint32_t dwAlarm ; | |
dwAlarm = ((pucMonth) || (pucDay)) ? (0) : (0x01010000); | |
TRACE_DEBUG( "RTC_SetDateAlarm()\n\r" ) ; | |
/* Compute alarm field value */ | |
if ( pucMonth ) | |
{ | |
dwAlarm |= RTC_CALALR_MTHEN | ((*pucMonth / 10) << 20) | ((*pucMonth % 10) << 16); | |
} | |
if ( pucDay ) | |
{ | |
dwAlarm |= RTC_CALALR_DATEEN | ((*pucDay / 10) << 28) | ((*pucDay % 10) << 24); | |
} | |
/* Set alarm */ | |
pRtc->RTC_CALALR = dwAlarm ; | |
return (int)(pRtc->RTC_VER & RTC_VER_NVCALALR) ; | |
} | |
/** | |
* \brief Clear flag bits of status clear command register in the RTC. | |
* | |
* \param mask Bits mask of cleared events | |
*/ | |
extern void RTC_ClearSCCR( Rtc* pRtc, uint32_t dwMask ) | |
{ | |
/* Clear all flag bits in status clear command register */ | |
dwMask &= RTC_SCCR_ACKCLR | RTC_SCCR_ALRCLR | RTC_SCCR_SECCLR | RTC_SCCR_TIMCLR | RTC_SCCR_CALCLR ; | |
pRtc->RTC_SCCR = dwMask ; | |
} | |
/** | |
* \brief Get flag bits of status register in the RTC. | |
* | |
* \param mask Bits mask of Status Register | |
* | |
* \return Status register & mask | |
*/ | |
extern uint32_t RTC_GetSR( Rtc* pRtc, uint32_t dwMask ) | |
{ | |
uint32_t dwEvent ; | |
dwEvent = pRtc->RTC_SR ; | |
return (dwEvent & dwMask) ; | |
} | |