/* ---------------------------------------------------------------------------- * SAM Software Package License * ---------------------------------------------------------------------------- * Copyright (c) 2011, 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 : * * 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 #include /*---------------------------------------------------------------------------- * 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 ; 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); 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); 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 ; /* 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 ; /* 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 ; /* 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); /* 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) ; }