/** ****************************************************************************** * @file STM32RTC.cpp * @author Frederic Pillon * @brief Provides a RTC interface for Arduino * ****************************************************************************** * @attention * *

© COPYRIGHT(c) 2020 STMicroelectronics

* * 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. Neither the name of STMicroelectronics nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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. * ****************************************************************************** */ #include "STM32RTC.h" #define EPOCH_TIME_OFF 946684800 // This is 1st January 2000, 00:00:00 in epoch time #define EPOCH_TIME_YEAR_OFF 100 // years since 1900 // Initialize static variable bool STM32RTC::_timeSet = false; /** * @brief initializes the RTC * @param format: hour format: HOUR_12 or HOUR_24(default) * @retval None */ void STM32RTC::begin(Hour_Format format) { begin(false, format); } /** * @brief initializes the RTC * @param resetTime: if true reconfigures the RTC * @param format: hour format: HOUR_12 or HOUR_24(default) * @retval None */ void STM32RTC::begin(bool resetTime, Hour_Format format) { bool reinit; _format = format; reinit = RTC_init((format == HOUR_12) ? HOUR_FORMAT_12 : HOUR_FORMAT_24, (_mode == MODE_MIX) ? ::MODE_BINARY_MIX : ((_mode == MODE_BIN) ? ::MODE_BINARY_ONLY : ::MODE_BINARY_NONE), (_clockSource == LSE_CLOCK) ? ::LSE_CLOCK : (_clockSource == HSE_CLOCK) ? ::HSE_CLOCK : ::LSI_CLOCK , resetTime); _timeSet = !reinit; syncTime(); syncDate(); syncAlarmTime(); if (!IS_RTC_DATE(_alarmDay)) { // Use current time to init alarm members, // specially in case _alarmDay is 0 (reset value) which is an invalid value _alarmDay = _day; _alarmHours = _hours; _alarmMinutes = _minutes; _alarmSeconds = _seconds; _alarmSubSeconds = _subSeconds; _alarmPeriod = _hoursPeriod; } #ifdef RTC_ALARM_B syncAlarmTime(ALARM_B); if (!IS_RTC_DATE(_alarmBDay)) { // Use current time to init alarm members, // specially in case _alarmDay is 0 (reset value) which is an invalid value _alarmBDay = _day; _alarmBHours = _hours; _alarmBMinutes = _minutes; _alarmBSeconds = _seconds; _alarmBSubSeconds = _subSeconds; _alarmBPeriod = _hoursPeriod; } #endif } /** * @brief Deinitialize and stop the RTC * @param None * @retval None */ void STM32RTC::end(void) { RTC_DeInit(true); _timeSet = false; } /** * @brief get the RTC clock source. * @retval clock source: LSI_CLOCK, LSE_CLOCK or HSE_CLOCK */ STM32RTC::Source_Clock STM32RTC::getClockSource(void) { return _clockSource; } /** * @brief set the RTC clock source and user (a)synchronous prescalers values. * @note By default LSI clock is selected. This method must be called before begin(). * @param source: clock source: LSI_CLOCK, LSE_CLOCK or HSE_CLOCK * @param predivA: Asynchronous prescaler value. * @note Reset value: RTC_AUTO_1_SECOND for STM32F1xx series, else (PREDIVA_MAX + 1) * @param predivS: Synchronous prescaler value. * @note Reset value: (PREDIVS_MAX + 1), not used for STM32F1xx series. * @retval None */ void STM32RTC::setClockSource(Source_Clock source, uint32_t predivA, uint32_t predivS) { if (IS_CLOCK_SOURCE(source)) { _clockSource = source; RTC_SetClockSource((_clockSource == LSE_CLOCK) ? ::LSE_CLOCK : (_clockSource == HSE_CLOCK) ? ::HSE_CLOCK : ::LSI_CLOCK); } RTC_setPrediv(predivA, predivS); } /** * @brief get the Binary Mode. * @retval mode: MODE_BCD, MODE_BIN or MODE_MIX */ STM32RTC::Binary_Mode STM32RTC::getBinaryMode(void) { return _mode; } /** * @brief set the Binary Mode. By default MODE_BCD is selected. This * method must be called before begin(). * @param mode: the RTC mode: MODE_BCD, MODE_BIN or MODE_MIX * @retval None */ void STM32RTC::setBinaryMode(Binary_Mode mode) { #if defined(RTC_BINARY_NONE) _mode = mode; #else #warning "only BCD mode is supported" UNUSED(mode); _mode = MODE_BCD; #endif /* RTC_BINARY_NONE */ } /** * @brief get user (a)synchronous prescaler values if set else computed * ones for the current clock source. * @param predivA: pointer to the current Asynchronous prescaler value * @param predivS: pointer to the current Synchronous prescaler value, * not used for STM32F1xx series. * @retval None */ void STM32RTC::getPrediv(uint32_t *predivA, uint32_t *predivS) { if ((predivA != nullptr) #if !defined(STM32F1xx) && (predivS != nullptr) #endif /* STM32F1xx */ ) { RTC_getPrediv(predivA, predivS); } } /** * @brief set user (a)synchronous prescalers values. * @note This method must be called before begin(). * @param predivA: Asynchronous prescaler value. * @note Reset value: RTC_AUTO_1_SECOND for STM32F1xx series, else (PREDIVA_MAX + 1) * @param predivS: Synchronous prescaler value. * @note Reset value: (PREDIVS_MAX + 1), not used for STM32F1xx series. * @retval None */ void STM32RTC::setPrediv(uint32_t predivA, uint32_t predivS) { setClockSource(_clockSource, predivA, predivS); } /** * @brief enable the RTC alarm. * @param match: Alarm_Match configuration * @param name: optional (default: ALARM_A) * ALARM_A or ALARM_B if exists * @retval None */ void STM32RTC::enableAlarm(Alarm_Match match, Alarm name) { #ifdef RTC_ALARM_B if (name == ALARM_B) { _alarmBMatch = match; } else #else UNUSED(name); #endif { _alarmMatch = match; } switch (match) { case MATCH_OFF: #ifdef RTC_ALARM_B if (name == ALARM_B) { RTC_StopAlarm(::ALARM_B); } else #endif { RTC_StopAlarm(::ALARM_A); } break; case MATCH_SUBSEC: /* force _alarmday to 0 to go to the right alarm config in MIX mode */ #ifdef RTC_ALARM_B if (name == ALARM_B) { RTC_StartAlarm(::ALARM_B, 0, 0, 0, 0, _alarmBSubSeconds, (_alarmBPeriod == AM) ? HOUR_AM : HOUR_PM, static_cast(31UL)); } else #endif { RTC_StartAlarm(::ALARM_A, 0, 0, 0, 0, _alarmSubSeconds, (_alarmPeriod == AM) ? HOUR_AM : HOUR_PM, static_cast(31UL)); } break; case MATCH_YYMMDDHHMMSS://kept for compatibility case MATCH_MMDDHHMMSS: //kept for compatibility case MATCH_DHHMMSS: case MATCH_HHMMSS: case MATCH_MMSS: case MATCH_SS: #ifdef RTC_ALARM_B if (name == ALARM_B) { RTC_StartAlarm(::ALARM_B, _alarmBDay, _alarmBHours, _alarmBMinutes, _alarmBSeconds, _alarmBSubSeconds, (_alarmBPeriod == AM) ? HOUR_AM : HOUR_PM, static_cast(_alarmBMatch)); } else #endif { RTC_StartAlarm(::ALARM_A, _alarmDay, _alarmHours, _alarmMinutes, _alarmSeconds, _alarmSubSeconds, (_alarmPeriod == AM) ? HOUR_AM : HOUR_PM, static_cast(_alarmMatch)); } break; default: break; } } /** * @brief disable the RTC alarm. * @param name: optional (default: ALARM_A) * ALARM_A or ALARM_B if exists * @retval None */ void STM32RTC::disableAlarm(Alarm name) { RTC_StopAlarm(static_cast(name)); } /** * @brief attach a callback to the RTC alarm interrupt. * @param callback: pointer to the callback * @param name: optional (default: ALARM_A) * ALARM_A or ALARM_B if exists * @retval None */ void STM32RTC::attachInterrupt(voidFuncPtr callback, Alarm name) { attachInterrupt(callback, nullptr, name); } /** * @brief attach a callback to the RTC alarm interrupt. * @param callback: pointer to the callback * @param data: pointer to callback argument if any (default: nullptr) * @param name: optional (default: ALARM_A) * ALARM_A or ALARM_B if exists * @retval None */ void STM32RTC::attachInterrupt(voidFuncPtr callback, void *data, Alarm name) { attachAlarmCallback(callback, data, static_cast(name)); } /** * @brief detach the RTC alarm callback. * @param name: optional (default: ALARM_A) * ALARM_A or ALARM_B if exists * @retval None */ void STM32RTC::detachInterrupt(Alarm name) { detachAlarmCallback(static_cast(name)); } #ifdef ONESECOND_IRQn /** * @brief attach a callback to the RTC Seconds interrupt. * @param callback: pointer to the callback * @retval None */ void STM32RTC::attachSecondsInterrupt(voidFuncPtr callback) { attachSecondsIrqCallback(callback); } /** * @brief detach the RTC Seconds callback. * @retval None */ void STM32RTC::detachSecondsInterrupt(void) { detachSecondsIrqCallback(); } #endif /* ONESECOND_IRQn */ // Kept for compatibility. Use STM32LowPower library. void STM32RTC::standbyMode(void) { } /* * Get Functions */ /** * @brief get RTC subseconds. * @retval return the current milliseconds from the RTC. */ uint32_t STM32RTC::getSubSeconds(void) { syncTime(); return _subSeconds; } /** * @brief get RTC seconds. * @retval return the current seconds from the RTC. */ uint8_t STM32RTC::getSeconds(void) { syncTime(); return _seconds; } /** * @brief get RTC minutes. * @retval return the current minutes from the RTC. */ uint8_t STM32RTC::getMinutes(void) { syncTime(); return _minutes; } /** * @brief get RTC hours. * @param format: optional (default: nullptr) * pointer to the current hour period set in the RTC: AM or PM * @retval return the current hours from the RTC. */ uint8_t STM32RTC::getHours(AM_PM *period) { syncTime(); if (period != nullptr) { *period = _hoursPeriod; } return _hours; } /** * @brief get RTC time. * @param hours: pointer to the current hours * @param minutes: pointer to the current minutes * @param seconds: pointer to the current seconds * @param subSeconds: pointer to the current subSeconds (in milliseconds) * @param period: optional (default: nullptr) * pointer to the current hour period set in the RTC: AM or PM * @retval none */ void STM32RTC::getTime(uint8_t *hours, uint8_t *minutes, uint8_t *seconds, uint32_t *subSeconds, AM_PM *period) { syncTime(); if (hours != nullptr) { *hours = _hours; } if (minutes != nullptr) { *minutes = _minutes; } if (seconds != nullptr) { *seconds = _seconds; } if (subSeconds != nullptr) { *subSeconds = _subSeconds; } if (period != nullptr) { *period = _hoursPeriod; } } /** * @brief get RTC week day. * @retval return the current week day from the RTC. */ uint8_t STM32RTC::getWeekDay(void) { syncDate(); return _wday; } /** * @brief get RTC day. * @retval return the current day from the RTC. */ uint8_t STM32RTC::getDay(void) { syncDate(); return _day; } /** * @brief get RTC month. * @retval return the current month from the RTC. */ uint8_t STM32RTC::getMonth(void) { syncDate(); return _month; } /** * @brief get RTC year. * @retval return the current year from the RTC. */ uint8_t STM32RTC::getYear(void) { syncDate(); return _year; } /** * @brief get RTC time. * @param weekDay: pointer to the current weekDay * @param day: pointer to the current day * @param month: pointer to the current month * @param year: pointer to the current year * @retval none */ void STM32RTC::getDate(uint8_t *weekDay, uint8_t *day, uint8_t *month, uint8_t *year) { syncDate(); if (weekDay != nullptr) { *weekDay = _wday; } if (day != nullptr) { *day = _day; } if (month != nullptr) { *month = _month; } if (year != nullptr) { *year = _year; } } /** * @brief get RTC alarm subsecond. * @param name: optional (default: ALARM_A) * ALARM_A or ALARM_B if exists * @retval return the current alarm subsecond. */ uint32_t STM32RTC::getAlarmSubSeconds(Alarm name) { uint32_t alarmSubSeconds = 0; syncAlarmTime(name); #ifdef RTC_ALARM_B if (name == ALARM_B) { alarmSubSeconds = _alarmBSubSeconds; } else #endif { alarmSubSeconds = _alarmSubSeconds; } return alarmSubSeconds; } /** * @brief get RTC alarm second. * @param name: optional (default: ALARM_A) * ALARM_A or ALARM_B if exists * @retval return the current alarm second. */ uint8_t STM32RTC::getAlarmSeconds(Alarm name) { uint8_t alarmSeconds = 0; syncAlarmTime(name); #ifdef RTC_ALARM_B if (name == ALARM_B) { alarmSeconds = _alarmBSeconds; } else #endif { alarmSeconds = _alarmSeconds; } return alarmSeconds; } /** * @brief get RTC alarm minute. * @param name: optional (default: ALARM_A) * ALARM_A or ALARM_B if exists * @retval return the current alarm minute. */ uint8_t STM32RTC::getAlarmMinutes(Alarm name) { uint8_t alarmMinutes = 0; syncAlarmTime(name); #ifdef RTC_ALARM_B if (name == ALARM_B) { alarmMinutes = _alarmBMinutes; } else #endif { alarmMinutes = _alarmMinutes; } return alarmMinutes; } /** * @brief get RTC alarm hour. * @param name: optional (default: ALARM_A) * ALARM_A or ALARM_B if exists * @retval return the current alarm hour. */ uint8_t STM32RTC::getAlarmHours(Alarm name) { return getAlarmHours(nullptr, name); } /** * @brief get RTC alarm hour. * @param format: optional (default: nullptr) * pointer to the current hour format set in the RTC: AM or PM * @param name: optional (default: ALARM_A) * ALARM_A or ALARM_B if exists * @retval return the current alarm hour. */ uint8_t STM32RTC::getAlarmHours(AM_PM *period, Alarm name) { uint8_t alarmHours = 0; syncAlarmTime(name); #ifdef RTC_ALARM_B if (name == ALARM_B) { if (period != nullptr) { *period = _alarmBPeriod; } alarmHours = _alarmBHours; } else #endif { if (period != nullptr) { *period = _alarmPeriod; } alarmHours = _alarmHours; } return alarmHours; } /** * @brief get RTC alarm day. * @param name: optional (default: ALARM_A) * ALARM_A or ALARM_B if exists * @retval return the current alarm day. */ uint8_t STM32RTC::getAlarmDay(Alarm name) { uint8_t alarmDay = 0; syncAlarmTime(name); #ifdef RTC_ALARM_B if (name == ALARM_B) { alarmDay = _alarmBDay; } else #endif { alarmDay = _alarmDay; } return alarmDay; } /** * @brief get RTC alarm month. * @NOTE This function is kept for compatibility but the STM32 RTC * can't assign a month to an alarm. See board datasheet. * @retval always returns 0 */ uint8_t STM32RTC::getAlarmMonth(void) { return 0; } /** * @brief get RTC alarm year. * @NOTE This function is kept for compatibility but the STM32 RTC * can't assign a year to an alarm. See board datasheet. * @retval always returns 0 */ uint8_t STM32RTC::getAlarmYear(void) { return 0; } /* * Set Functions */ /** * @brief set RTC subseconds. * @param subseconds: 0-999 milliseconds * @retval none */ void STM32RTC::setSubSeconds(uint32_t subSeconds) { syncTime(); if (subSeconds < 1000) { _subSeconds = subSeconds; } RTC_SetTime(_hours, _minutes, _seconds, _subSeconds, (_hoursPeriod == AM) ? HOUR_AM : HOUR_PM); _timeSet = true; } /** * @brief set RTC seconds. * @param seconds: 0-59 * @retval none */ void STM32RTC::setSeconds(uint8_t seconds) { syncTime(); if (seconds < 60) { _seconds = seconds; } RTC_SetTime(_hours, _minutes, _seconds, _subSeconds, (_hoursPeriod == AM) ? HOUR_AM : HOUR_PM); _timeSet = true; } /** * @brief set RTC minutes. * @param minutes: 0-59 * @retval none */ void STM32RTC::setMinutes(uint8_t minutes) { syncTime(); if (minutes < 60) { _minutes = minutes; } RTC_SetTime(_hours, _minutes, _seconds, _subSeconds, (_hoursPeriod == AM) ? HOUR_AM : HOUR_PM); _timeSet = true; } /** * @brief set RTC hours. * @param hours: 0-23 * @param period: hour format AM or PM (optional) * @retval none */ void STM32RTC::setHours(uint8_t hours, AM_PM period) { syncTime(); if (hours < 24) { _hours = hours; } if (_format == HOUR_12) { _hoursPeriod = period; } RTC_SetTime(_hours, _minutes, _seconds, _subSeconds, (_hoursPeriod == AM) ? HOUR_AM : HOUR_PM); _timeSet = true; } /** * @brief set RTC time. * @param hours: 0-23 * @param minutes: 0-59 * @param seconds: 0-59 * @param subSeconds: 0-999 (optional) * @param period: hour format AM or PM (optional) * @retval none */ void STM32RTC::setTime(uint8_t hours, uint8_t minutes, uint8_t seconds, uint32_t subSeconds, AM_PM period) { syncTime(); if (subSeconds < 1000) { _subSeconds = subSeconds; } if (seconds < 60) { _seconds = seconds; } if (minutes < 60) { _minutes = minutes; } if (hours < 24) { _hours = hours; } if (_format == HOUR_12) { _hoursPeriod = period; } RTC_SetTime(_hours, _minutes, _seconds, _subSeconds, (_hoursPeriod == AM) ? HOUR_AM : HOUR_PM); _timeSet = true; } /** * @brief set RTC week day. * @param week day: 1-7 (Monday first) * @retval none */ void STM32RTC::setWeekDay(uint8_t weekDay) { syncDate(); if ((weekDay >= 1) && (weekDay <= 7)) { _wday = weekDay; } RTC_SetDate(_year, _month, _day, _wday); _timeSet = true; } /** * @brief set RTC day. * @param day: 1-31 * @retval none */ void STM32RTC::setDay(uint8_t day) { syncDate(); if ((day >= 1) && (day <= 31)) { _day = day; } RTC_SetDate(_year, _month, _day, _wday); _timeSet = true; } /** * @brief set RTC month. * @param month: 1-12 * @retval none */ void STM32RTC::setMonth(uint8_t month) { syncDate(); if ((month >= 1) && (month <= 12)) { _month = month; } RTC_SetDate(_year, _month, _day, _wday); _timeSet = true; } /** * @brief set RTC year. * @param year: 0-99 * @retval none */ void STM32RTC::setYear(uint8_t year) { syncDate(); if (year < 100) { _year = year; } RTC_SetDate(_year, _month, _day, _wday); _timeSet = true; } /** * @brief set RTC calendar. * @param day: 1-31 * @param month: 1-12 * @param year: 0-99 * @retval none */ void STM32RTC::setDate(uint8_t day, uint8_t month, uint8_t year) { syncDate(); if ((day >= 1) && (day <= 31)) { _day = day; } if ((month >= 1) && (month <= 12)) { _month = month; } if (year < 100) { _year = year; } RTC_SetDate(_year, _month, _day, _wday); _timeSet = true; } /** * @brief set RTC calendar. * @param weekDay: 1-7 (Monday first) * @param day: 1-31 * @param month: 1-12 * @param year: 0-99 * @retval none */ void STM32RTC::setDate(uint8_t weekDay, uint8_t day, uint8_t month, uint8_t year) { syncDate(); if ((weekDay >= 1) && (weekDay <= 7)) { _wday = weekDay; } if ((day >= 1) && (day <= 31)) { _day = day; } if ((month >= 1) && (month <= 12)) { _month = month; } if (year < 100) { _year = year; } RTC_SetDate(_year, _month, _day, _wday); _timeSet = true; } /** * @brief set RTC alarm subseconds. * @param subseconds: 0-999 (in ms) or 32bit nb of milliseconds in BIN mode * @param name: optional (default: ALARM_A) * ALARM_A or ALARM_B if exists * @retval none */ void STM32RTC::setAlarmSubSeconds(uint32_t subSeconds, Alarm name) { #ifndef RTC_ALARM_B UNUSED(name); #endif if (_mode == MODE_BIN) { #ifdef RTC_ALARM_B if (name == ALARM_B) { _alarmBSubSeconds = subSeconds; } else #endif { _alarmSubSeconds = subSeconds; } } else if (subSeconds < 1000) { #ifdef RTC_ALARM_B if (name == ALARM_B) { _alarmBSubSeconds = subSeconds; } else #endif { _alarmSubSeconds = subSeconds; } } } /** * @brief set RTC alarm second. * @param seconds: 0-59 * @param name: optional (default: ALARM_A) * ALARM_A or ALARM_B if exists * @retval none */ void STM32RTC::setAlarmSeconds(uint8_t seconds, Alarm name) { if (seconds < 60) { #ifdef RTC_ALARM_B if (name == ALARM_B) { _alarmBSeconds = seconds; } else #else UNUSED(name); #endif { _alarmSeconds = seconds; } } } /** * @brief set RTC alarm minute. * @param minutes: 0-59 * @param name: optional (default: ALARM_A) * ALARM_A or ALARM_B if exists * @retval none */ void STM32RTC::setAlarmMinutes(uint8_t minutes, Alarm name) { if (minutes < 60) { #ifdef RTC_ALARM_B if (name == ALARM_B) { _alarmBMinutes = minutes; } else #else UNUSED(name); #endif { _alarmMinutes = minutes; } } } /** * @brief set RTC alarm hour. * @param hour: 0-23 or 0-12 * @param name: optional (default: ALARM_A) * ALARM_A or ALARM_B if exists * @retval none */ void STM32RTC::setAlarmHours(uint8_t hours, Alarm name) { setAlarmHours(hours, AM, name); } /** * @brief set RTC alarm hour. * @param hour: 0-23 or 0-12 * @param period: hour format AM or PM (optional) * @param name: optional (default: ALARM_A) * ALARM_A or ALARM_B if exists * @retval none */ void STM32RTC::setAlarmHours(uint8_t hours, AM_PM period, Alarm name) { if (hours < 24) { #ifdef RTC_ALARM_B if (name == ALARM_B) { _alarmBHours = hours; if (_format == HOUR_12) { _alarmBPeriod = period; } } else #else UNUSED(name); #endif { _alarmHours = hours; if (_format == HOUR_12) { _alarmPeriod = period; } } } } /** * @brief set RTC alarm time. * @param subSeconds: 0-999 ms or 32bit nb of milliseconds in BIN mode * @param name: ALARM_A or ALARM_B if exists * @retval none */ void STM32RTC::setAlarmTime(uint32_t subSeconds, Alarm name) { setAlarmTime(0, 0, 0, subSeconds, AM, name); } /** * @brief set RTC alarm time. * @param hours: 0-23 * @param minutes: 0-59 * @param seconds: 0-59 * @param name: ALARM_A or ALARM_B if exists * @retval none */ void STM32RTC::setAlarmTime(uint8_t hours, uint8_t minutes, uint8_t seconds, Alarm name) { setAlarmTime(hours, minutes, seconds, 0, AM, name); } /** * @brief set RTC alarm time. * @param hours: 0-23 * @param minutes: 0-59 * @param seconds: 0-59 * @param subSeconds: 0-999 ms or 32bit nb of milliseconds in BIN mode * @param name: ALARM_A or ALARM_B if exists * @retval none */ void STM32RTC::setAlarmTime(uint8_t hours, uint8_t minutes, uint8_t seconds, uint32_t subSeconds, Alarm name) { setAlarmTime(hours, minutes, seconds, subSeconds, AM, name); } /** * @brief set RTC alarm time. * @param hours: 0-23 (not used in BIN mode) * @param minutes: 0-59 (not used in BIN mode) * @param seconds: 0-59 (not used in BIN mode) * @param subSeconds: 0-999 ms (optional) or 32bit nb of milliseconds in BIN mode * @param period: hour format AM or PM (optional) * @param name: optional (default: ALARM_A) * ALARM_A or ALARM_B if exists * @retval none */ void STM32RTC::setAlarmTime(uint8_t hours, uint8_t minutes, uint8_t seconds, uint32_t subSeconds, AM_PM period, Alarm name) { setAlarmHours(hours, period, name); setAlarmMinutes(minutes, name); setAlarmSeconds(seconds, name); setAlarmSubSeconds(subSeconds, name); } /** * @brief set RTC alarm day. * @param day: 1-31 * @param name: optional (default: ALARM_A) * ALARM_A or ALARM_B if exists * @retval none */ void STM32RTC::setAlarmDay(uint8_t day, Alarm name) { if ((day >= 1) && (day <= 31)) { #ifdef RTC_ALARM_B if (name == ALARM_B) { _alarmBDay = day; } #else UNUSED(name); #endif { _alarmDay = day; } } } /** * @brief set RTC alarm month. * @NOTE This function is kept for compatibility but the STM32 RTC * can't assign a month to an alarm. See board datasheet. * @param month is ignored. */ void STM32RTC::setAlarmMonth(uint8_t month) { UNUSED(month); } /** * @brief set RTC alarm year. * @NOTE This function is kept for compatibility but the STM32 RTC * can't assign a year to an alarm. See board datasheet. * @param year is ignored. */ void STM32RTC::setAlarmYear(uint8_t year) { UNUSED(year); } /** * @brief set RTC alarm date. * @NOTE Parameters month and year are ignored because the STM32 RTC can't * assign a month or year to an alarm. See board datasheet. * @param day: 1-31 * @param month is ignored * @param year is ignored * @param name: optional (default: ALARM_A) * ALARM_A or ALARM_B if exists */ void STM32RTC::setAlarmDate(uint8_t day, uint8_t month, uint8_t year, Alarm name) { UNUSED(month); UNUSED(year); setAlarmDay(day, name); } /** * @brief get epoch time * @param subSeconds: optional pointer to where to store subseconds of the epoch in ms * @retval epoch time in seconds */ time_t STM32RTC::getEpoch(uint32_t *subSeconds) { struct tm tm; syncDate(); syncTime(); tm.tm_isdst = -1; /* * mktime ignores the values supplied by the caller in the * tm_wday and tm_yday fields */ tm.tm_yday = 0; tm.tm_wday = 0; tm.tm_year = _year + EPOCH_TIME_YEAR_OFF; tm.tm_mon = _month - 1; tm.tm_mday = _day; tm.tm_hour = _hours; tm.tm_min = _minutes; tm.tm_sec = _seconds; if (subSeconds != nullptr) { *subSeconds = _subSeconds; } return mktime(&tm); } /** * @brief get epoch time since 1st January 2000, 00:00:00 * @retval epoch time in seconds */ time_t STM32RTC::getY2kEpoch(void) { return (getEpoch() - EPOCH_TIME_OFF); } /** * @brief get alarm epoch time * @param name: ALARM_A or ALARM_B if exists * @retval epoch time in seconds */ time_t STM32RTC::getAlarmEpoch(Alarm name) { return getAlarmEpoch(nullptr, name); } /** * @brief get alarm epoch time * @param subSeconds: optional pointer to where to store subseconds of the epoch in ms * @param name: optional (default: ALARM_A) * ALARM_A or ALARM_B if exists * @retval epoch time in seconds */ time_t STM32RTC::getAlarmEpoch(uint32_t *subSeconds, Alarm name) { struct tm tm; tm.tm_isdst = -1; /* * mktime ignores the values supplied by the caller in the * tm_wday and tm_yday fields */ tm.tm_yday = 0; tm.tm_wday = 0; tm.tm_year = _year + EPOCH_TIME_YEAR_OFF; tm.tm_mon = _month - 1; syncAlarmTime(name); #ifdef RTC_ALARM_B if (name == ALARM_B) { tm.tm_mday = _alarmBDay; tm.tm_hour = _alarmBHours; tm.tm_min = _alarmBMinutes; tm.tm_sec = _alarmBSeconds; if (subSeconds != nullptr) { *subSeconds = _alarmBSubSeconds; } } else #endif { tm.tm_mday = _alarmDay; tm.tm_hour = _alarmHours; tm.tm_min = _alarmMinutes; tm.tm_sec = _alarmSeconds; if (subSeconds != nullptr) { *subSeconds = _alarmSubSeconds; } } return mktime(&tm); } /** * @brief set RTC alarm from epoch time * @param epoch time in seconds * @param Alarm_Match match enum * @param name: optional (default: ALARM_A) * ALARM_A or ALARM_B if exists */ void STM32RTC::setAlarmEpoch(time_t ts, Alarm_Match match, Alarm name) { setAlarmEpoch(ts, match, 0, name); } /** * @brief set RTC alarm from epoch time * @param epoch time in seconds * @param Alarm_Match match enum * @param subSeconds optional subSeconds in ms (default: 0) * @param name: optional (default: ALARM_A) * ALARM_A or ALARM_B if exists */ void STM32RTC::setAlarmEpoch(time_t ts, Alarm_Match match, uint32_t subSeconds, Alarm name) { if (ts < EPOCH_TIME_OFF) { ts = EPOCH_TIME_OFF; } time_t t = ts; struct tm *tmp = gmtime(&t); setAlarmDay(tmp->tm_mday, name); setAlarmHours(tmp->tm_hour, name); setAlarmMinutes(tmp->tm_min, name); setAlarmSeconds(tmp->tm_sec, name); setAlarmSubSeconds(subSeconds, name); enableAlarm(match, name); } /** * @brief set RTC time from epoch time * @param epoch time in seconds * @param subSeconds subSeconds in ms */ void STM32RTC::setEpoch(time_t ts, uint32_t subSeconds) { if (ts < EPOCH_TIME_OFF) { ts = EPOCH_TIME_OFF; } time_t t = ts; struct tm *tmp = gmtime(&t); _year = tmp->tm_year - EPOCH_TIME_YEAR_OFF; _month = tmp->tm_mon + 1; _day = tmp->tm_mday; if (tmp->tm_wday == 0) { _wday = RTC_WEEKDAY_SUNDAY; } else { _wday = tmp->tm_wday; } _hours = tmp->tm_hour; _minutes = tmp->tm_min; _seconds = tmp->tm_sec; _subSeconds = subSeconds; RTC_SetDate(_year, _month, _day, _wday); RTC_SetTime(_hours, _minutes, _seconds, _subSeconds, (_hoursPeriod == AM) ? HOUR_AM : HOUR_PM); _timeSet = true; } /** * @brief set RTC time from epoch time since 1st January 2000, 00:00:00 * @param epoch time in seconds */ void STM32RTC::setY2kEpoch(time_t ts) { setEpoch(ts + EPOCH_TIME_OFF); } /** * @brief configure RTC source clock for low power * @param none */ void STM32RTC::configForLowPower(Source_Clock source) { #if defined(HAL_PWR_MODULE_ENABLED) #ifdef __HAL_RCC_RTCAPB_CLKAM_ENABLE __HAL_RCC_RTCAPB_CLKAM_ENABLE(); #endif setClockSource(source); begin(); if (!isTimeSet()) { // Set arbitrary time for Lowpower; if not already set setTime(12, 0, 0, 0, AM); } #endif } /** * @brief Check whether RTC alarm is set * @param name: optional (default: ALARM_A) * ALARM_A or ALARM_B if exists * @retval True if Alarm is set */ bool STM32RTC::isAlarmEnabled(Alarm name) { return RTC_IsAlarmSet(static_cast(name)); } /** * @brief synchronise the time from the current RTC one * @param none */ void STM32RTC::syncTime(void) { hourAM_PM_t p = HOUR_AM; RTC_GetTime(&_hours, &_minutes, &_seconds, &_subSeconds, &p); _hoursPeriod = (p == HOUR_AM) ? AM : PM; } /** * @brief synchronise the time from the current RTC one * @param none */ void STM32RTC::syncDate(void) { RTC_GetDate(&_year, &_month, &_day, &_wday); #if defined(STM32F1xx) RTC_StoreDate(); #endif /* STM32F1xx */ } /** * @brief synchronise the specified alarm time from the current RTC one * @param name: optional (default: ALARM_A) * ALARM_A or ALARM_B if exists */ void STM32RTC::syncAlarmTime(Alarm name) { hourAM_PM_t p = HOUR_AM; uint8_t match; #ifdef RTC_ALARM_B if (name == ALARM_B) { RTC_GetAlarm(::ALARM_B, &_alarmBDay, &_alarmBHours, &_alarmBMinutes, &_alarmBSeconds, &_alarmBSubSeconds, &p, &match); _alarmBPeriod = (p == HOUR_AM) ? AM : PM; } else #else UNUSED(name); #endif { RTC_GetAlarm(::ALARM_A, &_alarmDay, &_alarmHours, &_alarmMinutes, &_alarmSeconds, &_alarmSubSeconds, &p, &match); _alarmPeriod = (p == HOUR_AM) ? AM : PM; } switch (static_cast(match)) { case MATCH_OFF: case MATCH_YYMMDDHHMMSS://kept for compatibility case MATCH_MMDDHHMMSS: //kept for compatibility case MATCH_DHHMMSS: case MATCH_HHMMSS: case MATCH_MMSS: case MATCH_SS: #ifdef RTC_ALARM_B if (name == ALARM_B) { _alarmBMatch = static_cast(match); } else #endif { _alarmMatch = static_cast(match); } break; default: #ifdef RTC_ALARM_B if (name == ALARM_B) { _alarmBMatch = MATCH_OFF; } else #endif { _alarmMatch = MATCH_OFF; } break; } }