/** * \file * * \brief Real-Time Clock (RTC) driver for SAM. * * Copyright (c) 2011 - 2012 Atmel Corporation. All rights reserved. * * \asf_license_start * * \page License * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. The name of Atmel may not be used to endorse or promote products derived * from this software without specific prior written permission. * * 4. This software may only be redistributed and used in connection with an * Atmel microcontroller product. * * 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 * EXPRESSLY AND SPECIFICALLY 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. * * \asf_license_stop * */ #include "rtc.h" /// @cond 0 /**INDENT-OFF**/ #ifdef __cplusplus extern "C" { #endif /**INDENT-ON**/ /// @endcond /** * \defgroup sam_drivers_rtc_group Real-Time Clock (RTC) * * See \ref sam_rtc_quickstart. * * The RTC provides a full binary-coded decimal (BCD) clock that includes * century (19/20), year (with leap years), month, date, day, hour, minute * and second. * * @{ */ /* RTC Write Protect Key "RTC" in ASCII */ #define RTC_WP_KEY (0x525443) /* The BCD code shift value */ #define BCD_SHIFT 4 /* The BCD code mask value */ #define BCD_MASK 0xfu /* The BCD mul/div factor value */ #define BCD_FACTOR 10 /** * \brief Set the RTC hour mode. * * \param p_rtc Pointer to an RTC instance. * \param ul_mode 1 for 12-hour mode, 0 for 24-hour mode. */ void rtc_set_hour_mode(Rtc *p_rtc, uint32_t ul_mode) { if (ul_mode) { p_rtc->RTC_MR |= RTC_MR_HRMOD; } else { p_rtc->RTC_MR &= (~RTC_MR_HRMOD); } } /** * \brief Get the RTC hour mode. * * \param p_rtc Pointer to an RTC instance. * * \return 1 for 12-hour mode, 0 for 24-hour mode. */ uint32_t rtc_get_hour_mode(Rtc *p_rtc) { uint32_t ul_temp = p_rtc->RTC_MR; if (ul_temp & RTC_MR_HRMOD) { return 1; } else { return 0; } } /** * \brief Enable RTC interrupts. * * \param p_rtc Pointer to an RTC instance. * \param ul_sources Interrupts to be enabled. */ void rtc_enable_interrupt(Rtc *p_rtc, uint32_t ul_sources) { p_rtc->RTC_IER = ul_sources; } /** * \brief Disable RTC interrupts. * * \param p_rtc Pointer to an RTC instance. * \param ul_sources Interrupts to be disabled. */ void rtc_disable_interrupt(Rtc *p_rtc, uint32_t ul_sources) { p_rtc->RTC_IDR = ul_sources; } /** * \brief Read RTC interrupt mask. * * \param p_rtc Pointer to an RTC instance. * * \return The interrupt mask value. */ uint32_t rtc_get_interrupt_mask(Rtc *p_rtc) { return p_rtc->RTC_IMR; } /** * \brief Get the RTC time value. * * \param p_rtc Pointer to an RTC instance. * \param pul_hour Current hour, 24-hour mode. * \param pul_minute Current minute. * \param pul_second Current second. */ void rtc_get_time(Rtc *p_rtc, uint32_t *pul_hour, uint32_t *pul_minute, uint32_t *pul_second) { uint32_t ul_time; uint32_t ul_temp; /* Get the current RTC time (multiple reads are necessary to insure a stable value). */ ul_time = p_rtc->RTC_TIMR; while (ul_time != p_rtc->RTC_TIMR) { ul_time = p_rtc->RTC_TIMR; } /* Hour */ if (pul_hour) { ul_temp = (ul_time & RTC_TIMR_HOUR_Msk) >> RTC_TIMR_HOUR_Pos; *pul_hour = (ul_temp >> BCD_SHIFT) * BCD_FACTOR + (ul_temp & BCD_MASK); if ((ul_time & RTC_TIMR_AMPM) == RTC_TIMR_AMPM) { *pul_hour += 12; } } /* Minute */ if (pul_minute) { ul_temp = (ul_time & RTC_TIMR_MIN_Msk) >> RTC_TIMR_MIN_Pos; *pul_minute = (ul_temp >> BCD_SHIFT) * BCD_FACTOR + (ul_temp & BCD_MASK); } /* Second */ if (pul_second) { ul_temp = (ul_time & RTC_TIMR_SEC_Msk) >> RTC_TIMR_SEC_Pos; *pul_second = (ul_temp >> BCD_SHIFT) * BCD_FACTOR + (ul_temp & BCD_MASK); } } /** * \brief Set the RTC time value. * * \param p_rtc Pointer to an RTC instance. * \param ul_hour Current hour, 24-hour mode. * \param ul_minute Current minute. * \param ul_second Current second. * * \return 0 for OK, else invalid setting. */ uint32_t rtc_set_time(Rtc *p_rtc, uint32_t ul_hour, uint32_t ul_minute, uint32_t ul_second) { uint32_t ul_time = 0; /* If 12-hour mode, set AMPM bit */ if ((p_rtc->RTC_MR & RTC_MR_HRMOD) == RTC_MR_HRMOD) { if (ul_hour > 12) { ul_hour -= 12; ul_time |= RTC_TIMR_AMPM; } } /* Hour */ ul_time |= ((ul_hour / BCD_FACTOR) << (RTC_TIMR_HOUR_Pos + BCD_SHIFT)) | ((ul_hour % BCD_FACTOR) << RTC_TIMR_HOUR_Pos); /* Minute */ ul_time |= ((ul_minute / BCD_FACTOR) << (RTC_TIMR_MIN_Pos + BCD_SHIFT)) | ((ul_minute % BCD_FACTOR) << RTC_TIMR_MIN_Pos); /* Second */ ul_time |= ((ul_second / BCD_FACTOR) << (RTC_TIMR_SEC_Pos + BCD_SHIFT)) | ((ul_second % BCD_FACTOR) << RTC_TIMR_SEC_Pos); /* Update time register. Check the spec for the flow. */ p_rtc->RTC_CR |= RTC_CR_UPDTIM; while ((p_rtc->RTC_SR & RTC_SR_ACKUPD) != RTC_SR_ACKUPD); p_rtc->RTC_SCCR = RTC_SCCR_ACKCLR; p_rtc->RTC_TIMR = ul_time; p_rtc->RTC_CR &= (~RTC_CR_UPDTIM); p_rtc->RTC_SCCR |= RTC_SCCR_SECCLR; return (p_rtc->RTC_VER & RTC_VER_NVTIM); } /** * \brief Set the RTC alarm time value. * * \param p_rtc Pointer to an RTC instance. * \param ul_hour_flag 1 for setting, 0 for not setting. * \param ul_hour Alarm hour value, 24-hour mode. * \param ul_minute_flag 1 for setting, 0 for not setting. * \param ul_minute Alarm minute value. * \param ul_second_flag 1 for setting, 0 for not setting. * \param ul_second Alarm second value. * * \return 0 for OK, else invalid setting. */ uint32_t rtc_set_time_alarm(Rtc *p_rtc, uint32_t ul_hour_flag, uint32_t ul_hour, uint32_t ul_minute_flag, uint32_t ul_minute, uint32_t ul_second_flag, uint32_t ul_second) { uint32_t ul_alarm = 0; /* Hour alarm setting */ if (ul_hour_flag) { /* If 12-hour mode, set AMPM bit */ if ((p_rtc->RTC_MR & RTC_MR_HRMOD) == RTC_MR_HRMOD) { if (ul_hour > 12) { ul_hour -= 12; ul_alarm |= RTC_TIMR_AMPM; } } ul_alarm |= RTC_TIMALR_HOUREN | ((ul_hour / BCD_FACTOR) << (RTC_TIMR_HOUR_Pos + BCD_SHIFT)) | ((ul_hour % BCD_FACTOR) << RTC_TIMR_HOUR_Pos); } /* Minute alarm setting */ if (ul_minute_flag) { ul_alarm |= RTC_TIMALR_MINEN | ((ul_minute / BCD_FACTOR) << (RTC_TIMR_MIN_Pos + BCD_SHIFT)) | ((ul_minute % BCD_FACTOR) << RTC_TIMR_MIN_Pos); } /* Second alarm setting */ if (ul_second_flag) { ul_alarm |= RTC_TIMALR_SECEN | ((ul_second / BCD_FACTOR) << (RTC_TIMR_SEC_Pos + BCD_SHIFT)) | ((ul_second % BCD_FACTOR) << RTC_TIMR_SEC_Pos); } p_rtc->RTC_TIMALR = ul_alarm; return (p_rtc->RTC_VER & RTC_VER_NVTIMALR); } /** * \brief Get the RTC date value. * * \param p_rtc Pointer to an RTC instance. * \param pul_year Current year. * \param pul_month Current month. * \param pul_day Current day. * \param pul_week Current day in current week. */ void rtc_get_date(Rtc *p_rtc, uint32_t *pul_year, uint32_t *pul_month, uint32_t *pul_day, uint32_t *pul_week) { uint32_t ul_date; uint32_t ul_cent; uint32_t ul_temp; /* Get the current date (multiple reads are necessary to insure a stable value). */ ul_date = p_rtc->RTC_CALR; while (ul_date != p_rtc->RTC_CALR) { ul_date = p_rtc->RTC_CALR; } /* Retrieve year */ if (pul_year) { ul_temp = (ul_date & RTC_CALR_CENT_Msk) >> RTC_CALR_CENT_Pos; ul_cent = (ul_temp >> BCD_SHIFT) * BCD_FACTOR + (ul_temp & BCD_MASK); ul_temp = (ul_date & RTC_CALR_YEAR_Msk) >> RTC_CALR_YEAR_Pos; *pul_year = (ul_cent * BCD_FACTOR * BCD_FACTOR) + (ul_temp >> BCD_SHIFT) * BCD_FACTOR + (ul_temp & BCD_MASK); } /* Retrieve month */ if (pul_month) { ul_temp = (ul_date & RTC_CALR_MONTH_Msk) >> RTC_CALR_MONTH_Pos; *pul_month = (ul_temp >> BCD_SHIFT) * BCD_FACTOR + (ul_temp & BCD_MASK); } /* Retrieve day */ if (pul_day) { ul_temp = (ul_date & RTC_CALR_DATE_Msk) >> RTC_CALR_DATE_Pos; *pul_day = (ul_temp >> BCD_SHIFT) * BCD_FACTOR + (ul_temp & BCD_MASK); } /* Retrieve week */ if (pul_week) { *pul_week = ((ul_date & RTC_CALR_DAY_Msk) >> RTC_CALR_DAY_Pos); } } /** * \brief Set the RTC date. * * \param p_rtc Pointer to an RTC instance. * \param ul_year Current year. * \param ul_month Current month. * \param ul_day Current day. * \param ul_week Current day in current week. * * \return 0 for OK, else invalid setting. */ uint32_t rtc_set_date(Rtc *p_rtc, uint32_t ul_year, uint32_t ul_month, uint32_t ul_day, uint32_t ul_week) { uint32_t ul_date = 0; /* Cent */ ul_date |= ((ul_year / BCD_FACTOR / BCD_FACTOR / BCD_FACTOR) << (RTC_CALR_CENT_Pos + BCD_SHIFT) | ((ul_year / BCD_FACTOR / BCD_FACTOR) % BCD_FACTOR) << RTC_CALR_CENT_Pos); /* Year */ ul_date |= (((ul_year / BCD_FACTOR) % BCD_FACTOR) << (RTC_CALR_YEAR_Pos + BCD_SHIFT)) | ((ul_year % BCD_FACTOR) << RTC_CALR_YEAR_Pos); /* Month */ ul_date |= ((ul_month / BCD_FACTOR) << (RTC_CALR_MONTH_Pos + BCD_SHIFT)) | ((ul_month % BCD_FACTOR) << RTC_CALR_MONTH_Pos); /* Week */ ul_date |= (ul_week << RTC_CALR_DAY_Pos); /* Day */ ul_date |= ((ul_day / BCD_FACTOR) << (RTC_CALR_DATE_Pos + BCD_SHIFT)) | ((ul_day % BCD_FACTOR) << RTC_CALR_DATE_Pos); /* Update calendar register. Check the spec for the flow. */ p_rtc->RTC_CR |= RTC_CR_UPDCAL; while ((p_rtc->RTC_SR & RTC_SR_ACKUPD) != RTC_SR_ACKUPD); p_rtc->RTC_SCCR = RTC_SCCR_ACKCLR; p_rtc->RTC_CALR = ul_date; p_rtc->RTC_CR &= (~RTC_CR_UPDCAL); /* Clear SECENV in SCCR */ p_rtc->RTC_SCCR |= RTC_SCCR_SECCLR; return (p_rtc->RTC_VER & RTC_VER_NVCAL); } /** * \brief Set the RTC alarm date value. * * \param p_rtc Pointer to an RTC instance. * \param ul_month_flag 1 for setting, 0 for not setting. * \param ul_month Alarm month value. * \param ul_day_flag 1 for setting, 0 for not setting. * \param ul_day Alarm day value. * * \return 0 for OK, else invalid setting. */ uint32_t rtc_set_date_alarm(Rtc *p_rtc, uint32_t ul_month_flag, uint32_t ul_month, uint32_t ul_day_flag, uint32_t ul_day) { uint32_t ul_alarm = 0; /* Month alarm setting */ if (ul_month_flag) { ul_alarm |= RTC_CALALR_MTHEN | ((ul_month / BCD_FACTOR) << (RTC_CALR_MONTH_Pos + BCD_SHIFT)) | ((ul_month % BCD_FACTOR) << RTC_CALR_MONTH_Pos); } /* Day alarm setting */ if (ul_day_flag) { ul_alarm |= RTC_CALALR_DATEEN | ((ul_day / BCD_FACTOR) << (RTC_CALR_DATE_Pos + BCD_SHIFT)) | ((ul_day % BCD_FACTOR) << RTC_CALR_DATE_Pos); } /* Set alarm */ p_rtc->RTC_CALALR = ul_alarm; return (p_rtc->RTC_VER & RTC_VER_NVCALALR); } /** * \brief Clear the RTC time alarm setting. * * \param p_rtc Pointer to an RTC instance. */ void rtc_clear_time_alarm(Rtc *p_rtc) { p_rtc->RTC_TIMALR = 0; } /** * \brief Clear the RTC data alarm setting. * * \param p_rtc Pointer to an RTC instance. */ void rtc_clear_data_alarm(Rtc *p_rtc) { /* Need a valid value without enabling */ p_rtc->RTC_CALALR = RTC_CALALR_MONTH(0x01) | RTC_CALALR_DATE(0x01); } /** * \brief Get the RTC status. * * \param p_rtc Pointer to an RTC instance. * * \return Status of the RTC. */ uint32_t rtc_get_status(Rtc *p_rtc) { return (p_rtc->RTC_SR); } /** * \brief Set the RTC SCCR to clear status bits. * * \param p_rtc Pointer to an RTC instance. * \param ul_clear Some flag bits which will be cleared. */ void rtc_clear_status(Rtc *p_rtc, uint32_t ul_clear) { p_rtc->RTC_SCCR = ul_clear; } #if ((SAM3S8) || (SAM3SD8) || (SAM4S)) /** * \brief Set the RTC calendar mode. * * \param p_rtc Pointer to an RTC instance. * \param ul_mode 1 for Persian mode,0 for Gregorian mode. */ void rtc_set_calendar_mode(Rtc *p_rtc, uint32_t ul_mode) { if (ul_mode) { p_rtc->RTC_MR |= RTC_MR_PERSIAN; } else { p_rtc->RTC_MR &= (~RTC_MR_PERSIAN); } } /** * \brief Set the RTC calibration. * * \param p_rtc Pointer to an RTC instance. * \param ul_direction_ppm Positive/negative correction. * \param ul_correction Correction value. * \param ul_range_ppm Low/high range correction. */ void rtc_set_calibration(Rtc *p_rtc, uint32_t ul_direction_ppm, uint32_t ul_correction, uint32_t ul_range_ppm) { uint32_t ul_temp; ul_temp = p_rtc->RTC_MR; if (ul_direction_ppm) { ul_temp |= RTC_MR_NEGPPM; } else { ul_temp &= (~RTC_MR_NEGPPM); } ul_temp |= RTC_MR_CORRECTION(ul_correction); if (ul_range_ppm) { ul_temp |= RTC_MR_HIGHPPM; } else { ul_temp &= (~RTC_MR_HIGHPPM); } p_rtc->RTC_MR = ul_temp; } /** * \brief Set the RTC output waveform. * * \param p_rtc Pointer to an RTC instance. * \param ul_channel Output channel selection. * \param ul_value Output source selection value. */ void rtc_set_waveform(Rtc *p_rtc, uint32_t ul_channel, uint32_t ul_value) { if (ul_channel == 0) { switch (ul_value) { case 0: p_rtc->RTC_MR &= ~RTC_MR_OUT0_Msk; p_rtc->RTC_MR |= RTC_MR_OUT0_NO_WAVE; break; case 1: p_rtc->RTC_MR &= ~RTC_MR_OUT0_Msk; p_rtc->RTC_MR |= RTC_MR_OUT0_FREQ1HZ; break; case 2: p_rtc->RTC_MR &= ~RTC_MR_OUT0_Msk; p_rtc->RTC_MR |= RTC_MR_OUT0_FREQ32HZ; break; case 3: p_rtc->RTC_MR &= ~RTC_MR_OUT0_Msk; p_rtc->RTC_MR |= RTC_MR_OUT0_FREQ64HZ; break; case 4: p_rtc->RTC_MR &= ~RTC_MR_OUT0_Msk; p_rtc->RTC_MR |= RTC_MR_OUT0_FREQ512HZ; break; case 5: p_rtc->RTC_MR &= ~RTC_MR_OUT0_Msk; p_rtc->RTC_MR |= RTC_MR_OUT0_ALARM_TOGGLE; break; case 6: p_rtc->RTC_MR &= ~RTC_MR_OUT0_Msk; p_rtc->RTC_MR |= RTC_MR_OUT0_ALARM_FLAG; break; case 7: p_rtc->RTC_MR &= ~RTC_MR_OUT0_Msk; p_rtc->RTC_MR |= RTC_MR_OUT0_PROG_PULSE; break; default: break; } } else { switch (ul_value) { case 0: p_rtc->RTC_MR &= ~RTC_MR_OUT1_Msk; p_rtc->RTC_MR |= RTC_MR_OUT1_NO_WAVE; break; case 1: p_rtc->RTC_MR &= ~RTC_MR_OUT1_Msk; p_rtc->RTC_MR |= RTC_MR_OUT1_FREQ1HZ; break; case 2: p_rtc->RTC_MR &= ~RTC_MR_OUT1_Msk; p_rtc->RTC_MR |= RTC_MR_OUT1_FREQ32HZ; break; case 3: p_rtc->RTC_MR &= ~RTC_MR_OUT1_Msk; p_rtc->RTC_MR |= RTC_MR_OUT1_FREQ64HZ; break; case 4: p_rtc->RTC_MR &= ~RTC_MR_OUT1_Msk; p_rtc->RTC_MR |= RTC_MR_OUT1_FREQ512HZ; break; case 5: p_rtc->RTC_MR &= ~RTC_MR_OUT1_Msk; p_rtc->RTC_MR |= RTC_MR_OUT1_ALARM_TOGGLE; break; case 6: p_rtc->RTC_MR &= ~RTC_MR_OUT1_Msk; p_rtc->RTC_MR |= RTC_MR_OUT1_ALARM_FLAG; break; case 7: p_rtc->RTC_MR &= ~RTC_MR_OUT1_Msk; p_rtc->RTC_MR |= RTC_MR_OUT1_PROG_PULSE; break; default: break; } } } /** * \brief Set the pulse output waveform parameters. * * \param p_rtc Pointer to an RTC instance. * \param ul_time_high High duration of the output pulse. * \param ul_period Period of the output pulse. */ void rtc_set_pulse_parameter(Rtc *p_rtc, uint32_t ul_time_high, uint32_t ul_period) { uint32_t ul_temp; ul_temp = p_rtc->RTC_MR; ul_temp |= (RTC_MR_THIGH_Msk & ((ul_time_high) << RTC_MR_THIGH_Pos)); ul_temp |= (RTC_MR_TPERIOD_Msk & ((ul_period) << RTC_MR_TPERIOD_Pos)); p_rtc->RTC_MR = ul_temp; } #endif /* ((SAM3S8) || (SAM3SD8) || (SAM4S)) */ #if ((SAM3N) || (SAM3U) || (SAM3XA_SERIES)) /** * \brief Enable or disable write protection of RTC registers. * * \param p_rtc Pointer to an RTC instance. * \param ul_enable 1 to enable, 0 to disable. */ void rtc_set_writeprotect(Rtc *p_rtc, uint32_t ul_enable) { if (ul_enable) { p_rtc->RTC_WPMR = RTC_WPMR_WPKEY(RTC_WP_KEY) | RTC_WPMR_WPEN; } else { p_rtc->RTC_WPMR = RTC_WPMR_WPKEY(RTC_WP_KEY); } } #endif /* ((SAM3N) || (SAM3U) || (SAM3XA_SERIES)) */ //@} /// @cond 0 /**INDENT-OFF**/ #ifdef __cplusplus } #endif /**INDENT-ON**/ /// @endcond