From 718e96aff64d4ed98e0399e3df6cba55740307be Mon Sep 17 00:00:00 2001 From: Frederic Pillon Date: Fri, 13 Jan 2023 09:53:47 +0100 Subject: [PATCH 1/7] chore(keywords): add missing items Signed-off-by: Frederic Pillon --- keywords.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/keywords.txt b/keywords.txt index 6718cc2..e62ecbd 100644 --- a/keywords.txt +++ b/keywords.txt @@ -70,6 +70,7 @@ detachSecondsInterrupt KEYWORD2 getClockSource KEYWORD2 setClockSource KEYWORD2 +isAlarmEnabled KEYWORD2 isConfigured KEYWORD2 isTimeSet KEYWORD2 From d5484bbf5a968ee870de38334111dd4382df0e15 Mon Sep 17 00:00:00 2001 From: Frederic Pillon Date: Thu, 12 Jan 2023 16:53:34 +0100 Subject: [PATCH 2/7] feat: add second alarm (B) support Fixes #74 Signed-off-by: Frederic Pillon Co-authored-by: hariton_batkov --- README.md | 19 +- .../advancedRTCAlarm/advancedRTCAlarm.ino | 22 + keywords.txt | 2 + src/STM32RTC.cpp | 411 +++++++++++++++--- src/STM32RTC.h | 70 ++- src/rtc.c | 148 +++++-- src/rtc.h | 19 +- 7 files changed, 564 insertions(+), 127 deletions(-) diff --git a/README.md b/README.md index 11024db..dc9fdd6 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ _SubSeconds alarm management_ * **`void setAlarmTime(uint8_t hours, uint8_t minutes, uint8_t seconds, uint32_t subSeconds = 0, AM_PM period = AM)`** * **`uint32_t getEpoch(uint32_t *subSeconds = nullptr)`** * **`void setEpoch(uint32_t ts, uint32_t subSeconds = 0)`** - * **`void setAlarmEpoch(uint32_t ts, Alarm_Match match = MATCH_DHHMMSS, uint32_t subSeconds = 0)`** + * **`void setAlarmEpoch(uint32_t ts, Alarm_Match match = MATCH_DHHMMSS, uint32_t subSeconds = 0)`** _Library version management_ @@ -105,7 +105,7 @@ _Library version management_ _One-Second interrupt_ STM32 RTC includes a one-second interrupt for generating a periodic interrupt signal. - - This feature is native on the stm32F1xx and mapped on the existing WakeUp interrupt on other stm32 mcus. + - This feature is native on the stm32F1xx and mapped on the existing WakeUp interrupt on other stm32 mcus. - It is not available on some stm32F0 devices. * **new API:** @@ -138,6 +138,21 @@ To know if a time has already been set use: } ``` +### Since STM32RTC version higher than 1.3.4 +_Second alarm (Alarm B)_ + +Some STM32 RTC have a second alarm named `RTC_ALARM_B`. +It is possible to use it thanks all alarm API with an extra argument: + - `STM32RTC::ALARM_A` + - `STM32RTC::ALARM_B` + +```C++ + rtc.attachInterrupt(myCallback, &myCallbackdata, STM32RTC::ALARM_B); + rtc.setAlarmDay(day, STM32RTC::ALARM_B); + rtc.setAlarmTime(hours, minutes, seconds + 5, 567, STM32RTC::ALARM_B); + rtc.enableAlarm(rtc.MATCH_DHHMMSS, STM32RTC::ALARM_B); +``` + Refer to the Arduino RTC documentation for the other functions http://arduino.cc/en/Reference/RTC diff --git a/examples/advancedRTCAlarm/advancedRTCAlarm.ino b/examples/advancedRTCAlarm/advancedRTCAlarm.ino index 4871d14..ff52c9e 100644 --- a/examples/advancedRTCAlarm/advancedRTCAlarm.ino +++ b/examples/advancedRTCAlarm/advancedRTCAlarm.ino @@ -5,6 +5,9 @@ It uses the optional 'data' alarm callback parameters to reload alarm with 'atime' offset indefinitely. + If a second alarm (B) is available, it is configured + to trigger each second. + Creation 25 May 2018 by Frederic Pillon for STMicroelectronics Modified 03 Jul 2020 @@ -22,6 +25,9 @@ STM32RTC& rtc = STM32RTC::getInstance(); /* Declare it volatile since it's incremented inside an interrupt */ volatile int alarmMatch_counter = 0; +#ifdef RTC_ALARM_B +volatile int alarmBMatch_counter = 0; +#endif /* Change this value to set alarm match offset in millisecond */ /* Note that STM32F1xx does not manage subsecond only second */ @@ -54,6 +60,13 @@ void setup() rtc.setAlarmDay(day); rtc.setAlarmTime(16, 0, 10, 567); rtc.enableAlarm(rtc.MATCH_DHHMMSS); + +#ifdef RTC_ALARM_B + rtc.attachInterrupt(alarmBMatch, STM32RTC::ALARM_B); + rtc.setAlarmDay(day, STM32RTC::ALARM_B); + rtc.setAlarmTime(16, 0, 11, 567, STM32RTC::ALARM_B); + rtc.enableAlarm(rtc.MATCH_DHHMMSS, STM32RTC::ALARM_B); +#endif } void loop() @@ -97,3 +110,12 @@ void alarmMatch(void *data) Serial.printf("Alarm Match %i\n", ++alarmMatch_counter); rtc.setAlarmEpoch(epoc + sec, STM32RTC::MATCH_SS, epoc_ms); } + +#ifdef RTC_ALARM_B +void alarmBMatch(void *data) +{ + (void)data; + Serial.printf("Alarm B Match %i\n", ++alarmBMatch_counter); + rtc.setAlarmEpoch(rtc.getEpoch() + 2, STM32RTC::MATCH_SS, STM32RTC::ALARM_B); +} +#endif diff --git a/keywords.txt b/keywords.txt index e62ecbd..d20756d 100644 --- a/keywords.txt +++ b/keywords.txt @@ -97,3 +97,5 @@ PM LITERAL1 LSE_CLOCK LITERAL1 LSI_CLOCK LITERAL1 HSE_CLOCK LITERAL1 +ALARM_A LITERAL1 +ALARM_B LITERAL1 diff --git a/src/STM32RTC.cpp b/src/STM32RTC.cpp index 6727ab8..b24a3d0 100644 --- a/src/STM32RTC.cpp +++ b/src/STM32RTC.cpp @@ -64,7 +64,6 @@ void STM32RTC::begin(bool resetTime, Hour_Format format) { bool reinit; - _format = format; reinit = RTC_init((format == HOUR_12) ? HOUR_FORMAT_12 : HOUR_FORMAT_24, (_clockSource == LSE_CLOCK) ? ::LSE_CLOCK : @@ -85,6 +84,18 @@ void STM32RTC::begin(bool resetTime, Hour_Format format) _alarmSubSeconds = _subSeconds; _alarmPeriod = _hoursPeriod; } +#ifdef RTC_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 } /** @@ -180,14 +191,32 @@ void STM32RTC::setPrediv(int8_t predivA, int16_t 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) +void STM32RTC::enableAlarm(Alarm_Match match, Alarm name) { - _alarmMatch = match; +#ifdef RTC_ALARM_B + if (name == ALARM_B) { + _alarmBMatch = match; + } else +#else + UNUSED(name); +#endif + { + _alarmMatch = match; + } switch (match) { case MATCH_OFF: - RTC_StopAlarm(); +#ifdef RTC_ALARM_B + if (name == ALARM_B) { + RTC_StopAlarm(::ALARM_B); + } else +#endif + { + RTC_StopAlarm(::ALARM_A); + } break; case MATCH_YYMMDDHHMMSS://kept for compatibility case MATCH_MMDDHHMMSS: //kept for compatibility @@ -195,9 +224,18 @@ void STM32RTC::enableAlarm(Alarm_Match match) case MATCH_HHMMSS: case MATCH_MMSS: case MATCH_SS: - RTC_StartAlarm(_alarmDay, _alarmHours, _alarmMinutes, _alarmSeconds, - _alarmSubSeconds, (_alarmPeriod == AM) ? HOUR_AM : HOUR_PM, - static_cast(_alarmMatch)); +#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; @@ -206,30 +244,50 @@ void STM32RTC::enableAlarm(Alarm_Match match) /** * @brief disable the RTC alarm. + * @param name: optional (default: ALARM_A) + * ALARM_A or ALARM_B if exists * @retval None */ -void STM32RTC::disableAlarm(void) +void STM32RTC::disableAlarm(Alarm name) { - RTC_StopAlarm(); + 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, void *data) +void STM32RTC::attachInterrupt(voidFuncPtr callback, Alarm name) { - attachAlarmCallback(callback, data); + 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(void) +void STM32RTC::detachInterrupt(Alarm name) { - detachAlarmCallback(); + detachAlarmCallback(static_cast(name)); } #ifdef ONESECOND_IRQn @@ -405,57 +463,126 @@ void STM32RTC::getDate(uint8_t *weekDay, uint8_t *day, uint8_t *month, uint8_t * /** * @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(void) +uint32_t STM32RTC::getAlarmSubSeconds(Alarm name) { - syncAlarmTime(); - return _alarmSubSeconds; + 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(void) +uint8_t STM32RTC::getAlarmSeconds(Alarm name) { - syncAlarmTime(); - return _alarmSeconds; + 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(void) +uint8_t STM32RTC::getAlarmMinutes(Alarm name) { - syncAlarmTime(); - return _alarmMinutes; + 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) +uint8_t STM32RTC::getAlarmHours(AM_PM *period, Alarm name) { - syncAlarmTime(); - if (period != nullptr) { - *period = _alarmPeriod; + 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; + 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(void) +uint8_t STM32RTC::getAlarmDay(Alarm name) { - syncAlarmTime(); - return _alarmDay; + uint8_t alarmDay = 0; + syncAlarmTime(name); +#ifdef RTC_ALARM_B + if (name == ALARM_B) { + alarmDay = _alarmBDay; + } else +#endif + { + alarmDay = _alarmDay; + } + return alarmDay; } /** @@ -692,55 +819,141 @@ void STM32RTC::setDate(uint8_t weekDay, uint8_t day, uint8_t month, uint8_t year /** * @brief set RTC alarm subseconds. * @param subseconds: 0-999 (in ms) + * @param name: optional (default: ALARM_A) + * ALARM_A or ALARM_B if exists * @retval none */ -void STM32RTC::setAlarmSubSeconds(uint32_t subSeconds) +void STM32RTC::setAlarmSubSeconds(uint32_t subSeconds, Alarm name) { if (subSeconds < 1000) { - _alarmSubSeconds = subSeconds; +#ifdef RTC_ALARM_B + if (name == ALARM_B) { + _alarmBSubSeconds = subSeconds; + } +#else + UNUSED(name); +#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) +void STM32RTC::setAlarmSeconds(uint8_t seconds, Alarm name) { if (seconds < 60) { - _alarmSeconds = seconds; +#ifdef RTC_ALARM_B + if (name == ALARM_B) { + _alarmBSeconds = seconds; + } +#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) +void STM32RTC::setAlarmMinutes(uint8_t minutes, Alarm name) { if (minutes < 60) { - _alarmMinutes = minutes; +#ifdef RTC_ALARM_B + if (name == ALARM_B) { + _alarmBMinutes = minutes; + } +#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) +void STM32RTC::setAlarmHours(uint8_t hours, AM_PM period, Alarm name) { if (hours < 24) { - _alarmHours = hours; - } - if (_format == HOUR_12) { - _alarmPeriod = period; +#ifdef RTC_ALARM_B + if (name == ALARM_B) { + _alarmBHours = hours; + if (_format == HOUR_12) { + _alarmBPeriod = period; + } + } +#else + UNUSED(name); +#endif + { + _alarmHours = hours; + if (_format == HOUR_12) { + _alarmPeriod = period; + } + } } } +/** + * @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 + * @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 @@ -748,25 +961,38 @@ void STM32RTC::setAlarmHours(uint8_t hours, AM_PM period) * @param seconds: 0-59 * @param subSeconds: 0-999 (optional) * @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) +void STM32RTC::setAlarmTime(uint8_t hours, uint8_t minutes, uint8_t seconds, uint32_t subSeconds, AM_PM period, Alarm name) { - setAlarmHours(hours, period); - setAlarmMinutes(minutes); - setAlarmSeconds(seconds); - setAlarmSubSeconds(subSeconds); + 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) +void STM32RTC::setAlarmDay(uint8_t day, Alarm name) { if ((day >= 1) && (day <= 31)) { - _alarmDay = day; +#ifdef RTC_ALARM_B + if (name == ALARM_B) { + _alarmBDay = day; + } +#else + UNUSED(name); +#endif + { + _alarmDay = day; + } } } @@ -799,13 +1025,15 @@ void STM32RTC::setAlarmYear(uint8_t year) * @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) +void STM32RTC::setAlarmDate(uint8_t day, uint8_t month, uint8_t year, Alarm name) { UNUSED(month); UNUSED(year); - setAlarmDay(day); + setAlarmDay(day, name); } /** @@ -853,9 +1081,23 @@ time_t STM32RTC::getY2kEpoch(void) * @brief set RTC alarm from epoch time * @param epoch time in seconds * @param Alarm_Match match enum - * @param subSeconds subSeconds in ms + * @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) +void STM32RTC::setAlarmEpoch(time_t ts, Alarm_Match match, uint32_t subSeconds, Alarm name) { if (ts < EPOCH_TIME_OFF) { ts = EPOCH_TIME_OFF; @@ -864,12 +1106,12 @@ void STM32RTC::setAlarmEpoch(time_t ts, Alarm_Match match, uint32_t subSeconds) time_t t = ts; struct tm *tmp = gmtime(&t); - setAlarmDay(tmp->tm_mday); - setAlarmHours(tmp->tm_hour); - setAlarmMinutes(tmp->tm_min); - setAlarmSeconds(tmp->tm_sec); - setAlarmSubSeconds(subSeconds); - enableAlarm(match); + 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); } /** @@ -934,6 +1176,17 @@ void STM32RTC::configForLowPower(Source_Clock source) #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 @@ -958,16 +1211,28 @@ void STM32RTC::syncDate(void) } /** - * @brief synchronise the alarm time from the current RTC one - * @param none + * @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(void) +void STM32RTC::syncAlarmTime(Alarm name) { hourAM_PM_t p = HOUR_AM; uint8_t match; - RTC_GetAlarm(&_alarmDay, &_alarmHours, &_alarmMinutes, &_alarmSeconds, - &_alarmSubSeconds, &p, &match); - _alarmPeriod = (p == HOUR_AM) ? AM : PM; +#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 + 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 @@ -976,10 +1241,24 @@ void STM32RTC::syncAlarmTime(void) case MATCH_HHMMSS: case MATCH_MMSS: case MATCH_SS: - _alarmMatch = static_cast(match); +#ifdef RTC_ALARM_B + if (name == ALARM_B) { + _alarmBMatch = static_cast(match); + } else +#endif + { + _alarmMatch = static_cast(match); + } break; default: - _alarmMatch = MATCH_OFF; +#ifdef RTC_ALARM_B + if (name == ALARM_B) { + _alarmBMatch = MATCH_OFF; + } else +#endif + { + _alarmMatch = MATCH_OFF; + } break; } } diff --git a/src/STM32RTC.h b/src/STM32RTC.h index 504b479..06e639d 100644 --- a/src/STM32RTC.h +++ b/src/STM32RTC.h @@ -102,6 +102,13 @@ class STM32RTC { HSE_CLOCK = ::HSE_CLOCK }; + enum Alarm : uint32_t { + ALARM_A = ::ALARM_A, +#ifdef RTC_ALARM_B + ALARM_B = ::ALARM_B +#endif + }; + static STM32RTC &getInstance() { static STM32RTC instance; // Guaranteed to be destroyed. @@ -120,11 +127,12 @@ class STM32RTC { Source_Clock getClockSource(void); void setClockSource(Source_Clock source); - void enableAlarm(Alarm_Match match); - void disableAlarm(void); + void enableAlarm(Alarm_Match match, Alarm name = ALARM_A); + void disableAlarm(Alarm name = ALARM_A); - void attachInterrupt(voidFuncPtr callback, void *data = nullptr); - void detachInterrupt(void); + void attachInterrupt(voidFuncPtr callback, Alarm name); + void attachInterrupt(voidFuncPtr callback, void *data = nullptr, Alarm name = ALARM_A); + void detachInterrupt(Alarm name = ALARM_A); #ifdef ONESECOND_IRQn // Other mcu than stm32F1 will use the WakeUp feature to interrupt each second. @@ -149,16 +157,17 @@ class STM32RTC { uint8_t getYear(void); void getDate(uint8_t *weekDay, uint8_t *day, uint8_t *month, uint8_t *year); - uint32_t getAlarmSubSeconds(void); - uint8_t getAlarmSeconds(void); - uint8_t getAlarmMinutes(void); - uint8_t getAlarmHours(AM_PM *period = nullptr); + uint32_t getAlarmSubSeconds(Alarm name = ALARM_A); + uint8_t getAlarmSeconds(Alarm name = ALARM_A); + uint8_t getAlarmMinutes(Alarm name = ALARM_A); + uint8_t getAlarmHours(Alarm name); + uint8_t getAlarmHours(AM_PM *period = nullptr, Alarm name = ALARM_A); - uint8_t getAlarmDay(void); + uint8_t getAlarmDay(Alarm name = ALARM_A); // Kept for compatibility with Arduino RTCZero library. - uint8_t getAlarmMonth(); - uint8_t getAlarmYear(); + uint8_t getAlarmMonth(void); + uint8_t getAlarmYear(void); /* Set Functions */ @@ -175,18 +184,21 @@ class STM32RTC { void setDate(uint8_t day, uint8_t month, uint8_t year); void setDate(uint8_t weekDay, uint8_t day, uint8_t month, uint8_t year); - void setAlarmSubSeconds(uint32_t subSeconds); - void setAlarmSeconds(uint8_t seconds); - void setAlarmMinutes(uint8_t minutes); - void setAlarmHours(uint8_t hours, AM_PM period = AM); - void setAlarmTime(uint8_t hours, uint8_t minutes, uint8_t seconds, uint32_t subSeconds = 0, AM_PM period = AM); + void setAlarmSubSeconds(uint32_t subSeconds, Alarm name = ALARM_A); + void setAlarmSeconds(uint8_t seconds, Alarm name = ALARM_A); + void setAlarmMinutes(uint8_t minutes, Alarm name = ALARM_A); + void setAlarmHours(uint8_t hours, Alarm name); + void setAlarmHours(uint8_t hours, AM_PM period = AM, Alarm name = ALARM_A); + void setAlarmTime(uint8_t hours, uint8_t minutes, uint8_t seconds, Alarm name); + void setAlarmTime(uint8_t hours, uint8_t minutes, uint8_t seconds, uint32_t subSeconds, Alarm name); + void setAlarmTime(uint8_t hours, uint8_t minutes, uint8_t seconds, uint32_t subSeconds = 0, AM_PM period = AM, Alarm name = ALARM_A); - void setAlarmDay(uint8_t day); + void setAlarmDay(uint8_t day, Alarm name = ALARM_A); // Kept for compatibility with Arduino RTCZero library. void setAlarmMonth(uint8_t month); void setAlarmYear(uint8_t year); - void setAlarmDate(uint8_t day, uint8_t month, uint8_t year); + void setAlarmDate(uint8_t day, uint8_t month, uint8_t year, Alarm name = ALARM_A); /* Epoch Functions */ @@ -194,7 +206,8 @@ class STM32RTC { time_t getY2kEpoch(void); void setEpoch(time_t ts, uint32_t subSeconds = 0); void setY2kEpoch(time_t ts); - void setAlarmEpoch(time_t ts, Alarm_Match match = MATCH_DHHMMSS, uint32_t subSeconds = 0); + void setAlarmEpoch(time_t ts, Alarm_Match match, Alarm name); + void setAlarmEpoch(time_t ts, Alarm_Match match = MATCH_DHHMMSS, uint32_t subSeconds = 0, Alarm name = ALARM_A); #if defined(STM32F1xx) void getPrediv(uint32_t *predivA, int16_t *dummy = nullptr); @@ -207,10 +220,7 @@ class STM32RTC { { return RTC_IsConfigured(); } - bool isAlarmEnabled(void) - { - return RTC_IsAlarmSet(); - } + bool isAlarmEnabled(Alarm name = ALARM_A); bool isTimeSet(void) { #if defined(STM32_CORE_VERSION) && (STM32_CORE_VERSION > 0x01050000) @@ -238,6 +248,7 @@ class STM32RTC { uint8_t _day; uint8_t _wday; + /* ALARM A */ uint8_t _alarmDay; uint8_t _alarmHours; uint8_t _alarmMinutes; @@ -246,13 +257,24 @@ class STM32RTC { AM_PM _alarmPeriod; Alarm_Match _alarmMatch; +#ifdef RTC_ALARM_B + /* ALARM B */ + uint8_t _alarmBDay; + uint8_t _alarmBHours; + uint8_t _alarmBMinutes; + uint8_t _alarmBSeconds; + uint32_t _alarmBSubSeconds; + AM_PM _alarmBPeriod; + Alarm_Match _alarmBMatch; +#endif + Source_Clock _clockSource; void configForLowPower(Source_Clock source); void syncTime(void); void syncDate(void); - void syncAlarmTime(void); + void syncAlarmTime(Alarm name = ALARM_A); }; diff --git a/src/rtc.c b/src/rtc.c index 21c3ab2..94b5694 100644 --- a/src/rtc.c +++ b/src/rtc.c @@ -60,6 +60,10 @@ extern "C" { static RTC_HandleTypeDef RtcHandle = {0}; static voidCallbackPtr RTCUserCallback = NULL; static void *callbackUserData = NULL; +#ifdef RTC_ALARM_B +static voidCallbackPtr RTCUserCallbackB = NULL; +static void *callbackUserDataB = NULL; +#endif static voidCallbackPtr RTCSecondsIrqCallback = NULL; static sourceClock_t clkSrc = LSI_CLOCK; @@ -342,7 +346,13 @@ bool RTC_init(hourFormat_t format, sourceClock_t source, bool reset) uint32_t subSeconds = 0, alarmSubseconds = 0; uint8_t seconds = 0, minutes = 0, hours = 0, weekDay = 0, days = 0, month = 0, years = 0; uint8_t alarmMask = 0, alarmDay = 0, alarmHours = 0, alarmMinutes = 0, alarmSeconds = 0; - bool isAlarmSet = false; + bool isAlarmASet = false; +#ifdef RTC_ALARM_B + hourAM_PM_t alarmBPeriod = HOUR_AM; + uint8_t alarmBMask = 0, alarmBDay = 0, alarmBHours = 0, alarmBMinutes = 0, alarmBSeconds = 0; + uint32_t alarmBSubseconds = 0; + bool isAlarmBSet = false; +#endif #if defined(STM32F1xx) uint32_t asynch; #else @@ -365,8 +375,10 @@ bool RTC_init(hourFormat_t format, sourceClock_t source, bool reset) #endif __HAL_RCC_RTC_ENABLE(); - isAlarmSet = RTC_IsAlarmSet(); - + isAlarmASet = RTC_IsAlarmSet(ALARM_A); +#ifdef RTC_ALARM_B + isAlarmBSet = RTC_IsAlarmSet(ALARM_B); +#endif #if defined(STM32F1xx) uint32_t BackupDate; BackupDate = getBackupRegister(RTC_BKP_DATE) << 16; @@ -440,10 +452,14 @@ bool RTC_init(hourFormat_t format, sourceClock_t source, bool reset) #else RTC_getPrediv(&asynch, &sync); #endif // STM32F1xx - if (isAlarmSet) { - RTC_GetAlarm(&alarmDay, &alarmHours, &alarmMinutes, &alarmSeconds, &alarmSubseconds, &alarmPeriod, &alarmMask); + if (isAlarmASet) { + RTC_GetAlarm(ALARM_A, &alarmDay, &alarmHours, &alarmMinutes, &alarmSeconds, &alarmSubseconds, &alarmPeriod, &alarmMask); } - +#ifdef RTC_ALARM_B + if (isAlarmBSet) { + RTC_GetAlarm(ALARM_B, &alarmBDay, &alarmBHours, &alarmBMinutes, &alarmBSeconds, &alarmBSubseconds, &alarmBPeriod, &alarmBMask); + } +#endif // Init RTC clock RTC_initClock(source); @@ -455,9 +471,14 @@ bool RTC_init(hourFormat_t format, sourceClock_t source, bool reset) #else RTC_setPrediv(asynch, sync); #endif // STM32F1xx - if (isAlarmSet) { - RTC_StartAlarm(alarmDay, alarmHours, alarmMinutes, alarmSeconds, alarmSubseconds, alarmPeriod, alarmMask); + if (isAlarmASet) { + RTC_StartAlarm(ALARM_A, alarmDay, alarmHours, alarmMinutes, alarmSeconds, alarmSubseconds, alarmPeriod, alarmMask); + } +#ifdef RTC_ALARM_B + if (isAlarmBSet) { + RTC_StartAlarm(ALARM_B, alarmBDay, alarmBHours, alarmBMinutes, alarmBSeconds, alarmBSubseconds, alarmBPeriod, alarmBMask); } +#endif } else { // RTC is already initialized, and RTC stays on the same clock source @@ -492,6 +513,10 @@ void RTC_DeInit(void) HAL_RTC_DeInit(&RtcHandle); RTCUserCallback = NULL; callbackUserData = NULL; +#ifdef RTC_ALARM_B + RTCUserCallbackB = NULL; + callbackUserDataB = NULL; +#endif RTCSecondsIrqCallback = NULL; } @@ -652,6 +677,7 @@ void RTC_GetDate(uint8_t *year, uint8_t *month, uint8_t *day, uint8_t *wday) /** * @brief Set RTC alarm and activate it with IT mode + * @param name: ALARM_A or ALARM_B if exists * @param day: 1-31 (day of the month) * @param hours: 0-12 or 0-23 depends on the hours mode. * @param minutes: 0-59 @@ -662,7 +688,7 @@ void RTC_GetDate(uint8_t *year, uint8_t *month, uint8_t *day, uint8_t *wday) * See AN4579 Table 5 for possible values. * @retval None */ -void RTC_StartAlarm(uint8_t day, uint8_t hours, uint8_t minutes, uint8_t seconds, uint32_t subSeconds, hourAM_PM_t period, uint8_t mask) +void RTC_StartAlarm(alarm_t name, uint8_t day, uint8_t hours, uint8_t minutes, uint8_t seconds, uint32_t subSeconds, hourAM_PM_t period, uint8_t mask) { RTC_AlarmTypeDef RTC_AlarmStructure; @@ -675,14 +701,21 @@ void RTC_StartAlarm(uint8_t day, uint8_t hours, uint8_t minutes, uint8_t seconds && IS_RTC_DATE(day) && IS_RTC_MINUTES(minutes) && IS_RTC_SECONDS(seconds)) { /* Set RTC_AlarmStructure with calculated values*/ /* Use alarm A by default because it is common to all STM32 HAL */ - RTC_AlarmStructure.Alarm = RTC_ALARM_A; + RTC_AlarmStructure.Alarm = name; RTC_AlarmStructure.AlarmTime.Seconds = seconds; RTC_AlarmStructure.AlarmTime.Minutes = minutes; RTC_AlarmStructure.AlarmTime.Hours = hours; #if !defined(STM32F1xx) #if defined(RTC_SSR_SS) if (subSeconds < 1000) { - RTC_AlarmStructure.AlarmSubSecondMask = predivSync_bits << RTC_ALRMASSR_MASKSS_Pos; +#ifdef RTC_ALARM_B + if (name == ALARM_B) { + RTC_AlarmStructure.AlarmSubSecondMask = predivSync_bits << RTC_ALRMBSSR_MASKSS_Pos; + } else +#endif + { + RTC_AlarmStructure.AlarmSubSecondMask = predivSync_bits << RTC_ALRMASSR_MASKSS_Pos; + } RTC_AlarmStructure.AlarmTime.SubSeconds = predivSync - (subSeconds * (predivSync + 1)) / 1000; } else { RTC_AlarmStructure.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL; @@ -733,34 +766,53 @@ void RTC_StartAlarm(uint8_t day, uint8_t hours, uint8_t minutes, uint8_t seconds /** * @brief Disable RTC alarm - * @param None + * @param name: ALARM_A or ALARM_B if exists * @retval None */ -void RTC_StopAlarm(void) +void RTC_StopAlarm(alarm_t name) { /* Clear RTC Alarm Flag */ - __HAL_RTC_ALARM_CLEAR_FLAG(&RtcHandle, RTC_FLAG_ALRAF); - +#ifdef RTC_ALARM_B + if (name == ALARM_B) { + __HAL_RTC_ALARM_CLEAR_FLAG(&RtcHandle, RTC_FLAG_ALRBF); + } else +#endif + { + __HAL_RTC_ALARM_CLEAR_FLAG(&RtcHandle, RTC_FLAG_ALRAF); + } /* Disable the Alarm A interrupt */ - HAL_RTC_DeactivateAlarm(&RtcHandle, RTC_ALARM_A); + HAL_RTC_DeactivateAlarm(&RtcHandle, name); } /** * @brief Check whether RTC alarm is set - * @param None + * @param ALARM_A or ALARM_B if exists * @retval True if Alarm is set */ -bool RTC_IsAlarmSet(void) +bool RTC_IsAlarmSet(alarm_t name) { + bool status = false; #if defined(STM32F1xx) - return LL_RTC_IsEnabledIT_ALR(RtcHandle.Instance); + UNUSED(name); + status = LL_RTC_IsEnabledIT_ALR(RtcHandle.Instance); +#else +#ifdef RTC_ALARM_B + if (name == ALARM_B) { + status = LL_RTC_IsEnabledIT_ALRB(RtcHandle.Instance); + } else #else - return LL_RTC_IsEnabledIT_ALRA(RtcHandle.Instance); + UNUSED(name); +#endif + { + status = LL_RTC_IsEnabledIT_ALRA(RtcHandle.Instance); + } #endif + return status; } /** * @brief Get RTC alarm + * @param name: ALARM_A or ALARM_B if exists * @param day: 1-31 day of the month (optional could be NULL) * @param hours: 0-12 or 0-23 depends on the hours mode * @param minutes: 0-59 @@ -771,12 +823,12 @@ bool RTC_IsAlarmSet(void) * See AN4579 Table 5 for possible values * @retval None */ -void RTC_GetAlarm(uint8_t *day, uint8_t *hours, uint8_t *minutes, uint8_t *seconds, uint32_t *subSeconds, hourAM_PM_t *period, uint8_t *mask) +void RTC_GetAlarm(alarm_t name, uint8_t *day, uint8_t *hours, uint8_t *minutes, uint8_t *seconds, uint32_t *subSeconds, hourAM_PM_t *period, uint8_t *mask) { RTC_AlarmTypeDef RTC_AlarmStructure; if ((hours != NULL) && (minutes != NULL) && (seconds != NULL)) { - HAL_RTC_GetAlarm(&RtcHandle, &RTC_AlarmStructure, RTC_ALARM_A, RTC_FORMAT_BIN); + HAL_RTC_GetAlarm(&RtcHandle, &RTC_AlarmStructure, name, RTC_FORMAT_BIN); *seconds = RTC_AlarmStructure.AlarmTime.Seconds; *minutes = RTC_AlarmStructure.AlarmTime.Minutes; @@ -827,23 +879,45 @@ void RTC_GetAlarm(uint8_t *day, uint8_t *hours, uint8_t *minutes, uint8_t *secon /** * @brief Attach alarm callback. * @param func: pointer to the callback + * @param func: pointer to callback argument + * @param name: ALARM_A or ALARM_B if exists * @retval None */ -void attachAlarmCallback(voidCallbackPtr func, void *data) +void attachAlarmCallback(voidCallbackPtr func, void *data, alarm_t name) { - RTCUserCallback = func; - callbackUserData = data; +#ifdef RTC_ALARM_B + if (name == ALARM_B) { + RTCUserCallbackB = func; + callbackUserDataB = data; + } else +#else + UNUSED(name); +#endif + { + RTCUserCallback = func; + callbackUserData = data; + } } /** * @brief Detach alarm callback. - * @param None + * @param name: ALARM_A or ALARM_B if exists * @retval None */ -void detachAlarmCallback(void) +void detachAlarmCallback(alarm_t name) { - RTCUserCallback = NULL; - callbackUserData = NULL; +#ifdef RTC_ALARM_B + if (name == ALARM_B) { + RTCUserCallbackB = NULL; + callbackUserDataB = NULL; + } else +#else + UNUSED(name); +#endif + { + RTCUserCallback = NULL; + callbackUserData = NULL; + } } /** @@ -860,6 +934,22 @@ void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc) } } +#ifdef RTC_ALARM_B +/** + * @brief Alarm B callback. + * @param hrtc RTC handle + * @retval None + */ +void HAL_RTCEx_AlarmBEventCallback(RTC_HandleTypeDef *hrtc) +{ + UNUSED(hrtc); + + if (RTCUserCallbackB != NULL) { + RTCUserCallbackB(callbackUserDataB); + } +} +#endif + /** * @brief RTC Alarm IRQHandler * @param None diff --git a/src/rtc.h b/src/rtc.h index 534cd3c..7ead699 100644 --- a/src/rtc.h +++ b/src/rtc.h @@ -74,6 +74,13 @@ typedef enum { Y_MSK = 32 } alarmMask_t; +typedef enum { + ALARM_A = RTC_ALARM_A, +#ifdef RTC_ALARM_B + ALARM_B = RTC_ALARM_B +#endif +} alarm_t; + typedef void(*voidCallbackPtr)(void *); /* Exported constants --------------------------------------------------------*/ @@ -176,12 +183,12 @@ void RTC_GetTime(uint8_t *hours, uint8_t *minutes, uint8_t *seconds, uint32_t *s void RTC_SetDate(uint8_t year, uint8_t month, uint8_t day, uint8_t wday); void RTC_GetDate(uint8_t *year, uint8_t *month, uint8_t *day, uint8_t *wday); -void RTC_StartAlarm(uint8_t day, uint8_t hours, uint8_t minutes, uint8_t seconds, uint32_t subSeconds, hourAM_PM_t period, uint8_t mask); -void RTC_StopAlarm(void); -bool RTC_IsAlarmSet(void); -void RTC_GetAlarm(uint8_t *day, uint8_t *hours, uint8_t *minutes, uint8_t *seconds, uint32_t *subSeconds, hourAM_PM_t *period, uint8_t *mask); -void attachAlarmCallback(voidCallbackPtr func, void *data); -void detachAlarmCallback(void); +void RTC_StartAlarm(alarm_t name, uint8_t day, uint8_t hours, uint8_t minutes, uint8_t seconds, uint32_t subSeconds, hourAM_PM_t period, uint8_t mask); +void RTC_StopAlarm(alarm_t name); +bool RTC_IsAlarmSet(alarm_t name); +void RTC_GetAlarm(alarm_t name, uint8_t *day, uint8_t *hours, uint8_t *minutes, uint8_t *seconds, uint32_t *subSeconds, hourAM_PM_t *period, uint8_t *mask); +void attachAlarmCallback(voidCallbackPtr func, void *data, alarm_t name); +void detachAlarmCallback(alarm_t name); #ifdef ONESECOND_IRQn void attachSecondsIrqCallback(voidCallbackPtr func); void detachSecondsIrqCallback(void); From 74e2b1712928ccbd7f283ad4e4fb7d8bc9dd060a Mon Sep 17 00:00:00 2001 From: Frederic Pillon Date: Tue, 17 Jan 2023 11:19:39 +0100 Subject: [PATCH 3/7] feat: add getAlarmEpoch method Signed-off-by: Frederic Pillon --- keywords.txt | 1 + src/STM32RTC.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ src/STM32RTC.h | 2 ++ 3 files changed, 57 insertions(+) diff --git a/keywords.txt b/keywords.txt index d20756d..5663a9e 100644 --- a/keywords.txt +++ b/keywords.txt @@ -38,6 +38,7 @@ setTime KEYWORD2 getEpoch KEYWORD2 getY2kEpoch KEYWORD2 +getAlarmEpoch KEYWORD2 setEpoch KEYWORD2 setY2kEpoch KEYWORD2 setAlarmEpoch KEYWORD2 diff --git a/src/STM32RTC.cpp b/src/STM32RTC.cpp index b24a3d0..1538223 100644 --- a/src/STM32RTC.cpp +++ b/src/STM32RTC.cpp @@ -1077,6 +1077,60 @@ 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 diff --git a/src/STM32RTC.h b/src/STM32RTC.h index 06e639d..4e19632 100644 --- a/src/STM32RTC.h +++ b/src/STM32RTC.h @@ -206,6 +206,8 @@ class STM32RTC { time_t getY2kEpoch(void); void setEpoch(time_t ts, uint32_t subSeconds = 0); void setY2kEpoch(time_t ts); + time_t getAlarmEpoch(Alarm name); + time_t getAlarmEpoch(uint32_t *subSeconds = nullptr, Alarm name = ALARM_A); void setAlarmEpoch(time_t ts, Alarm_Match match, Alarm name); void setAlarmEpoch(time_t ts, Alarm_Match match = MATCH_DHHMMSS, uint32_t subSeconds = 0, Alarm name = ALARM_A); From ead6df50ee4169c0c3de145cc714a966faad74ec Mon Sep 17 00:00:00 2001 From: Frederic Pillon Date: Tue, 17 Jan 2023 11:23:23 +0100 Subject: [PATCH 4/7] fix: restore default alarm values if valid Signed-off-by: Frederic Pillon --- src/STM32RTC.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/STM32RTC.cpp b/src/STM32RTC.cpp index 1538223..0b1071f 100644 --- a/src/STM32RTC.cpp +++ b/src/STM32RTC.cpp @@ -73,7 +73,7 @@ void STM32RTC::begin(bool resetTime, Hour_Format format) 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 @@ -85,6 +85,7 @@ void STM32RTC::begin(bool resetTime, Hour_Format format) _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 From d7ea564ea5a7eae2fe76e6efcce964702c85eb89 Mon Sep 17 00:00:00 2001 From: Frederic Pillon Date: Wed, 18 Jan 2023 17:13:14 +0100 Subject: [PATCH 5/7] fix(examples): use RTC_SSR_SS for subsecond management STM32F2xx also have no subsecond Signed-off-by: Frederic Pillon --- examples/advancedRTCAlarm/advancedRTCAlarm.ino | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/examples/advancedRTCAlarm/advancedRTCAlarm.ino b/examples/advancedRTCAlarm/advancedRTCAlarm.ino index ff52c9e..e59d440 100644 --- a/examples/advancedRTCAlarm/advancedRTCAlarm.ino +++ b/examples/advancedRTCAlarm/advancedRTCAlarm.ino @@ -30,9 +30,12 @@ volatile int alarmBMatch_counter = 0; #endif /* Change this value to set alarm match offset in millisecond */ -/* Note that STM32F1xx does not manage subsecond only second */ +/* Note that only mcu with RTC_SSR_SS defined managed subsecond else only second */ +#if defined(RTC_SSR_SS) static uint32_t atime = 678; - +#else +static uint32_t atime = 1000; +#endif /* Change these values to set the current initial time */ const byte seconds = 0; const byte minutes = 0; @@ -83,14 +86,10 @@ void alarmMatch(void *data) if (data != NULL) { _millis = *(uint32_t*)data; - // Minimum is 1 second - if (sec == 0) { - sec = 1; - } } sec = _millis / 1000; -#ifdef STM32F1xx +#if !defined(RTC_SSR_SS) // Minimum is 1 second if (sec == 0) { sec = 1; From 47631efc524ad91be467d5c1c41893ba8edc89ad Mon Sep 17 00:00:00 2001 From: Frederic Pillon Date: Wed, 18 Jan 2023 14:09:32 +0100 Subject: [PATCH 6/7] chore: include time.h within STM32RTC.h Signed-off-by: Frederic Pillon --- examples/Epoch/Epoch.ino | 3 +-- src/STM32RTC.cpp | 3 --- src/STM32RTC.h | 1 + 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/examples/Epoch/Epoch.ino b/examples/Epoch/Epoch.ino index 60838fa..6caff4a 100644 --- a/examples/Epoch/Epoch.ino +++ b/examples/Epoch/Epoch.ino @@ -14,7 +14,6 @@ */ #include -#include /* Get the rtc object */ STM32RTC& rtc = STM32RTC::getInstance(); @@ -63,4 +62,4 @@ void print2digits(uint32_t number) { Serial.print("0"); } Serial.print(number); -} \ No newline at end of file +} diff --git a/src/STM32RTC.cpp b/src/STM32RTC.cpp index 0b1071f..10d8379 100644 --- a/src/STM32RTC.cpp +++ b/src/STM32RTC.cpp @@ -33,9 +33,6 @@ * ****************************************************************************** */ - -#include - #include "STM32RTC.h" #define EPOCH_TIME_OFF 946684800 // This is 1st January 2000, 00:00:00 in epoch time diff --git a/src/STM32RTC.h b/src/STM32RTC.h index 4e19632..2705279 100644 --- a/src/STM32RTC.h +++ b/src/STM32RTC.h @@ -46,6 +46,7 @@ #ifndef HAL_RTC_MODULE_ENABLED #error "RTC configuration is missing. Check flag HAL_RTC_MODULE_ENABLED in variants/board_name/stm32yzxx_hal_conf.h" #endif +#include /** * @brief STM32 RTC library version number From 5fcb571c076a9e3b7e7a246dc024544f96e29936 Mon Sep 17 00:00:00 2001 From: Frederic Pillon Date: Tue, 17 Jan 2023 14:09:08 +0100 Subject: [PATCH 7/7] chore(examples): add RTCReset Signed-off-by: Frederic Pillon --- examples/RTCReset/RTCReset.ino | 221 +++++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 examples/RTCReset/RTCReset.ino diff --git a/examples/RTCReset/RTCReset.ino b/examples/RTCReset/RTCReset.ino new file mode 100644 index 0000000..ef55302 --- /dev/null +++ b/examples/RTCReset/RTCReset.ino @@ -0,0 +1,221 @@ +/* + RTCReset + + This sketch allows to test STM32RTC after a software reset or power off + with VBat. Including Alarm A and B management. + + Creation 17 jan 2023 + by Frederic Pillon for STMicroelectronics + + This example code is in the public domain. + + https://github.com/stm32duino/STM32RTC + +*/ +#include + +/* Get the rtc object */ +STM32RTC& rtc = STM32RTC::getInstance(); + +/* Change these values to set the current initial time + + format: date: "Dec 31 2022" and time: "23:59:56" + by default use built date and time +*/ +static const char* mydate = __DATE__; +static const char* mytime = __TIME__; +//static const char* mydate = "Dec 31 2022"; +//static const char* mytime = "23:59:56"; + +/* Declare it volatile since it's incremented inside an interrupt */ +volatile int alarmMatch_counter = 0; +volatile int alarmMatchB_counter = 0; + +typedef struct { + uint32_t next; + bool alarm_a; +} cb_data_t; + +static cb_data_t atime = { 2222, true}; +#ifdef RTC_ALARM_B +static cb_data_t btime = { 3333, false}; +#endif +static byte seconds = 0; +static byte minutes = 0; +static byte hours = 0; +static uint32_t subSeconds = 0; + +static byte weekDay = 1; +static byte day = 0; +static byte month = 0; +static byte year = 0; +static STM32RTC::Hour_Format hourFormat = STM32RTC::HOUR_24; +static STM32RTC::AM_PM period = STM32RTC::AM; + +#ifndef USER_BTN +#define USER_BTN PA0 +#endif + +static uint8_t conv2d(const char* p) { + uint8_t v = 0; + if ('0' <= *p && *p <= '9') + v = *p - '0'; + return 10 * v + *++p - '0'; +} + +// sample input: date = "Dec 26 2009", time = "12:34:56" +void initDateTime (void) { + Serial.printf("Build date & time %s, %s\n", mydate, mytime); + + year = conv2d(mydate + 9); + // Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec + switch (mydate[0]) { + case 'J': month = (mydate[1] == 'a') ? 1 : ((mydate[2] == 'n') ? 6 : 7); break; + case 'F': month = 2; break; + case 'A': month = mydate[2] == 'r' ? 4 : 8; break; + case 'M': month = mydate[2] == 'r' ? 3 : 5; break; + case 'S': month = 9; break; + case 'O': month = 10; break; + case 'N': month = 11; break; + case 'D': month = 12; break; + } + day = conv2d(mydate + 4); + hours = conv2d(mytime); + if (hourFormat == rtc.HOUR_12) { + period = hours >= 12 ? rtc.PM : rtc.AM; + hours = hours >= 13 ? hours - 12 : (hours < 1 ? hours + 12 : hours); + } + minutes = conv2d(mytime + 3); + seconds = conv2d(mytime + 6); +} + +void setup() +{ + pinMode(USER_BTN, INPUT_PULLUP); + int32_t default_state = digitalRead(USER_BTN); + + Serial.begin(9600); + while (!Serial); + // Wait user input to start + while (digitalRead(USER_BTN) == default_state); + // Convenient function to init date and time variables + initDateTime(); + + // Select RTC clock source: LSI_CLOCK, LSE_CLOCK or HSE_CLOCK. + // By default the LSI is selected as source. + // rtc.setClockSource(STM32RTC::LSE_CLOCK); + + // In any case attach a callback to the RTC alarm interrupt. + rtc.attachInterrupt(alarmMatch, &atime); +#ifdef RTC_ALARM_B + rtc.attachInterrupt(alarmMatch, &btime, STM32RTC::ALARM_B); +#endif + rtc.begin(); // Initialize RTC 24H format + if (!rtc.isTimeSet()) { + Serial.printf("RTC time not set\n Set it.\n"); + // Set the time + rtc.setTime(hours, minutes, seconds); + rtc.setDate(weekDay, day, month, year); + // ALARM_A (default argument) + rtc.setAlarmDay(day); + rtc.setAlarmTime(hours, minutes, seconds + 5, 567); + rtc.enableAlarm(rtc.MATCH_DHHMMSS); +#ifdef RTC_ALARM_B + // ALARM_B + rtc.setAlarmDay(day, STM32RTC::ALARM_B); + rtc.setAlarmTime(hours, minutes, seconds + 5, 567, STM32RTC::ALARM_B); + rtc.enableAlarm(rtc.MATCH_DHHMMSS, STM32RTC::ALARM_B); +#endif + } else { + // RTC already initialized + time_t epoc, alarm_epoc; + if (rtc.isAlarmEnabled()) { + rtc.enableAlarm(rtc.MATCH_DHHMMSS); + alarm_epoc = rtc.getAlarmEpoch(); + epoc = rtc.getEpoch(); + if (difftime(alarm_epoc, epoc) <= 0) { + Serial.printf("Alarm A was enabled and expired, force callback call\n"); + alarmMatch(&atime); + } else { + Serial.printf("Alarm A was enabled and restored\n"); + } + } +#ifdef RTC_ALARM_B + // ALARM_B + if (rtc.isAlarmEnabled(STM32RTC::ALARM_B)) { + rtc.enableAlarm(rtc.MATCH_DHHMMSS, STM32RTC::ALARM_B); + alarm_epoc = rtc.getAlarmEpoch(STM32RTC::ALARM_B); + epoc = rtc.getEpoch(); + if (difftime(alarm_epoc, epoc) <= 0) { + Serial.printf("Alarm B was enabled and expired, force callback call\n"); + alarmMatch(&btime); + } else { + Serial.printf("Alarm B was enabled and restored\n"); + } + } +#endif + Serial.printf("RTC time already set\n"); + } + Serial.printf("Alarm A enable status: %s\n", (rtc.isAlarmEnabled(STM32RTC::ALARM_A)) ? "True" : "False"); +#ifdef RTC_ALARM_B + Serial.printf("Alarm B enable status: %s\n", (rtc.isAlarmEnabled(STM32RTC::ALARM_B)) ? "True" : "False"); +#else + Serial.println("Alarm B not available."); +#endif +} + +void loop() +{ + rtc.getTime(&hours, &minutes, &seconds, &subSeconds, &period); + // Print current date & time + Serial.printf("\n%02d/%02d/%02d %02d:%02d:%02d.%03d\n", rtc.getDay(), rtc.getMonth(), rtc.getYear(), hours, minutes, seconds, subSeconds); + // Print current alarm configuration + Serial.printf("Alarm A: %02d %02d:%02d:%02d.%03d\n", rtc.getAlarmDay(), rtc.getAlarmHours(), rtc.getAlarmMinutes(), rtc.getAlarmSeconds(), rtc.getAlarmSubSeconds()); +#ifdef RTC_ALARM_B + Serial.printf("Alarm B: %02d %02d:%02d:%02d.%03d\n", rtc.getAlarmDay(STM32RTC::ALARM_B), rtc.getAlarmHours(STM32RTC::ALARM_B), rtc.getAlarmMinutes(STM32RTC::ALARM_B), rtc.getAlarmSeconds(STM32RTC::ALARM_B), rtc.getAlarmSubSeconds(STM32RTC::ALARM_B)); +#endif + delay(1000); +} + +void alarmMatch(void *data) +{ + time_t epoc; + uint32_t epoc_ms; + uint32_t sec = 0; + uint32_t _millis = 1000; + cb_data_t cbdata = {.next = 1000, .alarm_a = true}; + if (data != NULL) { + cbdata.next = ((cb_data_t*)data)->next; + cbdata.alarm_a = ((cb_data_t*)data)->alarm_a; + _millis = cbdata.next; + } + + sec = _millis / 1000; +#if !defined(RTC_SSR_SS) + // Minimum is 1 second + if (sec == 0) { + sec = 1; + } + epoc = rtc.getEpoch(&epoc_ms); +#else + _millis = _millis % 1000; + epoc = rtc.getEpoch(&epoc_ms); + + // Update epoch_ms - might need to add a second to epoch + epoc_ms += _millis; + if (epoc_ms >= 1000) { + sec ++; + epoc_ms -= 1000; + } +#endif + if (cbdata.alarm_a) { + Serial.printf("\t\t\tAlarm A Match %i\n", ++alarmMatch_counter); + rtc.setAlarmEpoch(epoc + sec, STM32RTC::MATCH_SS, epoc_ms); + } +#ifdef RTC_ALARM_B + else { + Serial.printf("\t\t\tAlarm B Match %i\n", ++alarmMatchB_counter); + rtc.setAlarmEpoch(epoc + sec, STM32RTC::MATCH_SS, epoc_ms, STM32RTC::ALARM_B); + } +#endif +}