/* ---------------------------------------------------------------------------- * 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 : * * 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 *----------------------------------------------------------------------------*/ /* These headers were introduced in C99 by working group ISO/IEC JTC1/SC22/WG14. */ #include #include "rtc.h" #include #include #include /*---------------------------------------------------------------------------- * Exported functions *----------------------------------------------------------------------------*/ /** * \brief Sets the RTC in either 12 or 24 hour mode. * * \param mode Hour mode. */ void RTC_SetHourMode(uint32_t mode) { SANITY_CHECK((mode & 0xFFFFFFFE) == 0); TRACE_DEBUG("RTC_SetHourMode()\n\r"); RTC->RTC_MR = mode; } /** * \brief Gets the RTC mode. * * \return Hour mode. */ uint32_t RTC_GetHourMode( void ) { uint32_t hmode; TRACE_DEBUG("RTC_SetHourMode()\n\r"); hmode = RTC->RTC_MR; hmode &= 0xFFFFFFFE; return hmode; } /** * \brief Enables the selected interrupt sources of the RTC. * * \param sources Interrupt sources to enable. */ void RTC_EnableIt(uint32_t sources) { SANITY_CHECK((sources & ~0x1F) == 0); TRACE_DEBUG("RTC_EnableIt()\n\r"); RTC->RTC_IER = sources; } /** * \brief Disables the selected interrupt sources of the RTC. * * \param sources Interrupt sources to disable. */ void RTC_DisableIt(uint32_t sources) { SANITY_CHECK((sources & ~0x1F) == 0); TRACE_DEBUG("RTC_DisableIt()\n\r"); RTC->RTC_IDR = sources; } /** * \brief Sets the current time in the RTC. * * \param hour Current hour in 12 or 24 hour mode. * \param minute Current minute. * \param second Current second. * \return 0 sucess, 1 fail to set */ int RTC_SetTime(uint8_t hour, uint8_t minute, uint8_t second) { uint32_t time=0; uint8_t hour_bcd; uint8_t min_bcd; uint8_t sec_bcd; TRACE_DEBUG("RTC_SetTime(%02d:%02d:%02d)\n\r", hour, minute, second); /* if 12-hour mode, set AMPM bit */ if ((RTC->RTC_MR & RTC_MR_HRMOD) == RTC_MR_HRMOD) { if (hour > 12) { hour -= 12; time |= RTC_TIMR_AMPM; } } hour_bcd = (hour%10) | ((hour/10)<<4); min_bcd = (minute%10) | ((minute/10)<<4); sec_bcd = (second%10) | ((second/10)<<4); /* value overflow */ if((hour_bcd & (uint8_t)(~RTC_HOUR_BIT_LEN_MASK)) | (min_bcd & (uint8_t)(~RTC_MIN_BIT_LEN_MASK)) | (sec_bcd & (uint8_t)(~RTC_SEC_BIT_LEN_MASK))) return 1; time = sec_bcd | (min_bcd << 8) | (hour_bcd<<16); /* Set time */ while ((RTC->RTC_SR & RTC_SR_SEC) != RTC_SR_SEC); /* wait from previous set */ RTC->RTC_CR |= RTC_CR_UPDTIM; while ((RTC->RTC_SR & RTC_SR_ACKUPD) != RTC_SR_ACKUPD); RTC->RTC_SCCR = RTC_SCCR_ACKCLR; RTC->RTC_TIMR = time; RTC->RTC_CR &= ~RTC_CR_UPDTIM; RTC->RTC_SCCR |= RTC_SCCR_SECCLR; /* clear SECENV in SCCR */ return (int)(RTC->RTC_VER & RTC_VER_NVTIM); } /** * \brief Retrieves the current time as stored in the RTC in several variables. * * \param pHour If not null, current hour is stored in this variable. * \param pMinute If not null, current minute is stored in this variable. * \param pSecond If not null, current second is stored in this variable. */ void RTC_GetTime( uint8_t *pHour, uint8_t *pMinute, uint8_t *pSecond) { uint32_t time; TRACE_DEBUG("RTC_GetTime()\n\r"); /* Get current RTC time */ time = RTC->RTC_TIMR; while (time != RTC->RTC_TIMR) { time = RTC->RTC_TIMR; } /* Hour */ if (pHour) { *pHour = ((time & 0x00300000) >> 20) * 10 + ((time & 0x000F0000) >> 16); if ((time & RTC_TIMR_AMPM) == RTC_TIMR_AMPM) { *pHour += 12; } } /* Minute */ if (pMinute) { *pMinute = ((time & 0x00007000) >> 12) * 10 + ((time & 0x00000F00) >> 8); } /* Second */ if (pSecond) { *pSecond = ((time & 0x00000070) >> 4) * 10 + (time & 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 pHour If not null, the time alarm will hour-match this value. * \param pMinute If not null, the time alarm will minute-match this value. * \param pSecond If not null, the time alarm will second-match this value. * \return 0 success, 1 fail to set */ int RTC_SetTimeAlarm( uint8_t *pHour, uint8_t *pMinute, uint8_t *pSecond) { uint32_t alarm = 0; TRACE_DEBUG("RTC_SetTimeAlarm()\n\r"); /* Hour */ if (pHour) { alarm |= RTC_TIMALR_HOUREN | ((*pHour / 10) << 20) | ((*pHour % 10) << 16); } /* Minute */ if (pMinute) { alarm |= RTC_TIMALR_MINEN | ((*pMinute / 10) << 12) | ((*pMinute % 10) << 8); } /* Second */ if (pSecond) { alarm |= RTC_TIMALR_SECEN | ((*pSecond / 10) << 4) | (*pSecond % 10); } RTC->RTC_TIMALR = alarm; return (int)(RTC->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 pYear Current year (optional). * \param pMonth Current month (optional). * \param pDay Current day (optional). * \param pWeek Current day in current week (optional). */ void RTC_GetDate( unsigned short *pYear, uint8_t *pMonth, uint8_t *pDay, uint8_t *pWeek) { uint32_t date; /* Get current date (multiple reads are necessary to insure a stable value) */ do { date = RTC->RTC_CALR; } while (date != RTC->RTC_CALR); /* Retrieve year */ if (pYear) { *pYear = (((date >> 4) & 0x7) * 1000) + ((date & 0xF) * 100) + (((date >> 12) & 0xF) * 10) + ((date >> 8) & 0xF); } /* Retrieve month */ if (pMonth) { *pMonth = (((date >> 20) & 1) * 10) + ((date >> 16) & 0xF); } /* Retrieve day */ if (pDay) { *pDay = (((date >> 28) & 0x3) * 10) + ((date >> 24) & 0xF); } /* Retrieve week */ if (pWeek) { *pWeek = ((date >> 21) & 0x7); } } /** * \brief Sets the current year, month and day in the RTC. * Month, day and week values must be numbered starting from 1. * * \param year Current year. * \param month Current month. * \param day Current day. * \param week Day number in current week. * \return 0 success, 1 fail to set */ int RTC_SetDate( unsigned short year, uint8_t month, uint8_t day, uint8_t week) { uint32_t date; uint8_t cent_bcd; uint8_t year_bcd; uint8_t month_bcd; uint8_t day_bcd; uint8_t week_bcd; cent_bcd = ((year/100)%10) | ((year/1000)<<4); year_bcd = (year%10) | (((year/10)%10)<<4); month_bcd = ((month%10) | (month/10)<<4); day_bcd = ((day%10) | (day/10)<<4); week_bcd = ((week%10) | (week/10)<<4); /* value over flow */ if((cent_bcd & (uint8_t)(~RTC_CENT_BIT_LEN_MASK)) | (year_bcd & (uint8_t)(~RTC_YEAR_BIT_LEN_MASK)) | (month_bcd & (uint8_t)(~RTC_MONTH_BIT_LEN_MASK)) | (week_bcd & (uint8_t)(~RTC_WEEK_BIT_LEN_MASK)) | (day_bcd & (uint8_t)(~RTC_DATE_BIT_LEN_MASK))) return 1; /* Convert values to date register value */ date = cent_bcd | (year_bcd << 8) | (month_bcd << 16) | (week_bcd << 21) | (day_bcd << 24); /* Update calendar register */ while ((RTC->RTC_SR & RTC_SR_SEC) != RTC_SR_SEC); /* wait from previous set */ RTC->RTC_CR |= RTC_CR_UPDCAL; while ((RTC->RTC_SR & RTC_SR_ACKUPD) != RTC_SR_ACKUPD); RTC->RTC_SCCR = RTC_SCCR_ACKCLR; RTC->RTC_CALR = date; RTC->RTC_CR &= ~RTC_CR_UPDCAL; RTC->RTC_SCCR |= RTC_SCCR_SECCLR; /* clear SECENV in SCCR */ return (int)(RTC->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 pMonth If not null, the RTC alarm will month-match this value. * \param pDay If not null, the RTC alarm will day-match this value. * \return 0 success, 1 fail to set */ int RTC_SetDateAlarm(uint8_t *pMonth, uint8_t *pDay) { uint32_t alarm; alarm = ((pMonth) || (pDay)) ? (0) : (0x01010000); TRACE_DEBUG("RTC_SetDateAlarm()\n\r"); /* Compute alarm field value */ if (pMonth) { alarm |= RTC_CALALR_MTHEN | ((*pMonth / 10) << 20) | ((*pMonth % 10) << 16); } if (pDay) { alarm |= RTC_CALALR_DATEEN | ((*pDay / 10) << 28) | ((*pDay % 10) << 24); } /* Set alarm */ RTC->RTC_CALALR = alarm; return (int)(RTC->RTC_VER & RTC_VER_NVCALALR); } /** * \brief Clear flag bits of status clear command register in the RTC. * * \param mask Bits mask of cleared events */ void RTC_ClearSCCR(uint32_t mask) { /* Clear all flag bits in status clear command register */ mask &= RTC_SCCR_ACKCLR | RTC_SCCR_ALRCLR | RTC_SCCR_SECCLR | \ RTC_SCCR_TIMCLR | RTC_SCCR_CALCLR; RTC->RTC_SCCR = mask; } /** * \brief Get flag bits of status register in the RTC. * * \param mask Bits mask of Status Register * \return Status register & mask */ uint32_t RTC_GetSR(uint32_t mask) { uint32_t event; event = RTC->RTC_SR; return (event & mask); }