diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..94f04f6 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,20 @@ +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +[keywords.txt] +indent_style = tab + +[*.sh] +# like -i=2 +indent_style = space +indent_size = 2 + +#shell_variant = posix # like -ln=posix +#binary_next_line = true # like -bn +switch_case_indent = true # like -ci +space_redirects = true # like -sr +#keep_padding = true # like -kp diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..97c0d7d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,44 @@ +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +.astyleignore text eol=lf +.astylerc text eol=lf +.editorconfig text eol=lf +.flake8 text eol=lf +.gitattributes text eol=lf +.gitignore text eol=lf + +*.adoc text eol=lf +*.c text eol=lf +*.cmake text eol=lf +*.cpp text eol=lf +*.css text eol=lf +*.dtsi text eol=lf +*.gv text eol=lf +*.h text eol=lf +*.html text eol=lf +*.in text eol=lf +*.ino text eol=lf +*.json text eol=lf +*.ld text eol=lf +*.md text eol=lf +*.MD text eol=lf +*.old text eol=lf +*.patch text eol=lf +*.pde text eol=lf +*.properties text eol=lf +*.py text eol=lf +*.s text eol=lf +*.S text eol=lf +*.sh text eol=lf +*.spec text eol=lf +*.txt text eol=lf +*.yml text eol=lf + +# Denote all files that are truly binary and should not be modified. +*.jpg binary +*.pdf binary +*.png binary + 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/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/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 +} diff --git a/examples/advancedRTCAlarm/advancedRTCAlarm.ino b/examples/advancedRTCAlarm/advancedRTCAlarm.ino index 4871d14..e59d440 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,11 +25,17 @@ 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 */ +/* 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; @@ -54,6 +63,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() @@ -70,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; @@ -97,3 +109,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 0429b4b..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 @@ -70,7 +71,9 @@ detachSecondsInterrupt KEYWORD2 getClockSource KEYWORD2 setClockSource KEYWORD2 +isAlarmEnabled KEYWORD2 isConfigured KEYWORD2 +isTimeSet KEYWORD2 getPrediv KEYWORD2 setPrediv KEYWORD2 @@ -95,3 +98,5 @@ PM LITERAL1 LSE_CLOCK LITERAL1 LSI_CLOCK LITERAL1 HSE_CLOCK LITERAL1 +ALARM_A LITERAL1 +ALARM_B LITERAL1 diff --git a/library.json b/library.json index cd37329..c50457d 100644 --- a/library.json +++ b/library.json @@ -7,7 +7,7 @@ "type": "git", "url": "https://github.com/stm32duino/STM32RTC" }, - "version": "1.3.4", + "version": "1.3.5", "frameworks": "arduino", "platforms": "ststm32", "build": { diff --git a/library.properties b/library.properties index bfd70ef..9682a54 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=STM32duino RTC -version=1.3.4 +version=1.3.5 author=STMicroelectronics, Wi6Labs maintainer=stm32duino sentence=Allows to use the RTC functionalities of STM32 based boards. diff --git a/src/STM32RTC.cpp b/src/STM32RTC.cpp index 8f1f859..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 @@ -64,7 +61,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 : @@ -74,7 +70,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 +81,19 @@ void STM32RTC::begin(bool resetTime, Hour_Format format) _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 } /** @@ -180,14 +189,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 +222,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 +242,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(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::disableAlarm(void) +void STM32RTC::attachInterrupt(voidFuncPtr callback, Alarm name) { - RTC_StopAlarm(); + 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) +void STM32RTC::attachInterrupt(voidFuncPtr callback, void *data, Alarm name) { - attachAlarmCallback(callback, data); + 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 +461,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 +817,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 +959,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 +1023,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); } /** @@ -813,7 +1039,7 @@ void STM32RTC::setAlarmDate(uint8_t day, uint8_t month, uint8_t year) * @param subSeconds: optional pointer to where to store subseconds of the epoch in ms * @retval epoch time in seconds */ -uint32_t STM32RTC::getEpoch(uint32_t *subSeconds) +time_t STM32RTC::getEpoch(uint32_t *subSeconds) { struct tm tm; @@ -844,18 +1070,86 @@ uint32_t STM32RTC::getEpoch(uint32_t *subSeconds) * @brief get epoch time since 1st January 2000, 00:00:00 * @retval epoch time in seconds */ -uint32_t STM32RTC::getY2kEpoch(void) +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 subSeconds subSeconds in ms + * @param name: optional (default: ALARM_A) + * ALARM_A or ALARM_B if exists */ -void STM32RTC::setAlarmEpoch(uint32_t ts, Alarm_Match match, uint32_t subSeconds) +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; @@ -864,12 +1158,12 @@ void STM32RTC::setAlarmEpoch(uint32_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); } /** @@ -877,7 +1171,7 @@ void STM32RTC::setAlarmEpoch(uint32_t ts, Alarm_Match match, uint32_t subSeconds * @param epoch time in seconds * @param subSeconds subSeconds in ms */ -void STM32RTC::setEpoch(uint32_t ts, uint32_t subSeconds) +void STM32RTC::setEpoch(time_t ts, uint32_t subSeconds) { if (ts < EPOCH_TIME_OFF) { ts = EPOCH_TIME_OFF; @@ -908,7 +1202,7 @@ void STM32RTC::setEpoch(uint32_t ts, uint32_t subSeconds) * @brief set RTC time from epoch time since 1st January 2000, 00:00:00 * @param epoch time in seconds */ -void STM32RTC::setY2kEpoch(uint32_t ts) +void STM32RTC::setY2kEpoch(time_t ts) { setEpoch(ts + EPOCH_TIME_OFF); } @@ -934,6 +1228,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 +1263,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 +1293,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 f5785a0..f92aae9 100644 --- a/src/STM32RTC.h +++ b/src/STM32RTC.h @@ -38,20 +38,22 @@ #define __STM32_RTC_H #include "Arduino.h" -#if defined(STM32_CORE_VERSION) && (STM32_CORE_VERSION > 0x01090000) - #include "rtc.h" +#if defined(STM32_CORE_VERSION) && (STM32_CORE_VERSION < 0x02000000) + #error "This library is not compatible with core version used. Please update the core." #endif +#include "rtc.h" // Check if RTC HAL enable in variants/board_name/stm32yzxx_hal_conf.h #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 */ #define STM32_RTC_VERSION_MAJOR (0x01U) /*!< [31:24] major version */ #define STM32_RTC_VERSION_MINOR (0x03U) /*!< [23:16] minor version */ -#define STM32_RTC_VERSION_PATCH (0x01U) /*!< [15:8] patch version */ +#define STM32_RTC_VERSION_PATCH (0x05U) /*!< [15:8] patch version */ /* * Extra label for development: * 0: official release @@ -101,6 +103,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. @@ -119,11 +128,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. @@ -148,16 +158,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 */ @@ -174,26 +185,32 @@ 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 */ - uint32_t getEpoch(uint32_t *subSeconds = nullptr); - uint32_t getY2kEpoch(void); - void setEpoch(uint32_t ts, uint32_t subSeconds = 0); - void setY2kEpoch(uint32_t ts); - void setAlarmEpoch(uint32_t ts, Alarm_Match match = MATCH_DHHMMSS, uint32_t subSeconds = 0); + time_t getEpoch(uint32_t *subSeconds = nullptr); + 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); #if defined(STM32F1xx) void getPrediv(uint32_t *predivA, int16_t *dummy = nullptr); @@ -206,17 +223,10 @@ 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) return _timeSet; -#else - return false; -#endif } friend class STM32LowPower; @@ -237,6 +247,7 @@ class STM32RTC { uint8_t _day; uint8_t _wday; + /* ALARM A */ uint8_t _alarmDay; uint8_t _alarmHours; uint8_t _alarmMinutes; @@ -245,13 +256,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 85ce964..67475c4 100644 --- a/src/rtc.c +++ b/src/rtc.c @@ -38,8 +38,7 @@ #include "stm32yyxx_ll_rtc.h" #include -#if defined(STM32_CORE_VERSION) && (STM32_CORE_VERSION > 0x01090000) &&\ - defined(HAL_RTC_MODULE_ENABLED) && !defined(HAL_RTC_MODULE_ONLY) +#if defined(HAL_RTC_MODULE_ENABLED) && !defined(HAL_RTC_MODULE_ONLY) #if defined(STM32MP1xx) /** * Currently there is no RTC driver for STM32MP1xx. If RTC is used in the future @@ -60,6 +59,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 +345,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 +374,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; @@ -383,10 +394,15 @@ bool RTC_init(hourFormat_t format, sourceClock_t source, bool reset) RtcHandle.Init.OutPut = RTC_OUTPUT_DISABLE; RtcHandle.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH; RtcHandle.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN; +#if defined(RTC_OUTPUT_PULLUP_NONE) + RtcHandle.Init.OutPutPullUp = RTC_OUTPUT_PULLUP_NONE; +#endif #if defined(RTC_OUTPUT_REMAP_NONE) RtcHandle.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE; #endif /* RTC_OUTPUT_REMAP_NONE */ - +#if defined(RTC_BINARY_NONE) + RtcHandle.Init.BinMode = RTC_BINARY_NONE; +#endif RTC_getPrediv((int8_t *) & (RtcHandle.Init.AsynchPrediv), (int16_t *) & (RtcHandle.Init.SynchPrediv)); #endif // STM32F1xx // Init RTC clock @@ -435,10 +451,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); @@ -450,9 +470,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 @@ -487,6 +512,10 @@ void RTC_DeInit(void) HAL_RTC_DeInit(&RtcHandle); RTCUserCallback = NULL; callbackUserData = NULL; +#ifdef RTC_ALARM_B + RTCUserCallbackB = NULL; + callbackUserDataB = NULL; +#endif RTCSecondsIrqCallback = NULL; } @@ -647,6 +676,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 @@ -657,7 +687,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; @@ -670,14 +700,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; @@ -728,34 +765,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 - return LL_RTC_IsEnabledIT_ALRA(RtcHandle.Instance); +#ifdef RTC_ALARM_B + if (name == ALARM_B) { + status = LL_RTC_IsEnabledIT_ALRB(RtcHandle.Instance); + } else +#else + 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 @@ -766,12 +822,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; @@ -822,23 +878,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; + } } /** @@ -855,6 +933,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 a1789e6..d647b94 100644 --- a/src/rtc.h +++ b/src/rtc.h @@ -43,8 +43,7 @@ #include "backup.h" #include "clock.h" -#if defined(STM32_CORE_VERSION) && (STM32_CORE_VERSION > 0x01090000) &&\ - defined(HAL_RTC_MODULE_ENABLED) && !defined(HAL_RTC_MODULE_ONLY) +#if defined(HAL_RTC_MODULE_ENABLED) && !defined(HAL_RTC_MODULE_ONLY) #ifdef __cplusplus extern "C" { @@ -74,6 +73,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 --------------------------------------------------------*/ @@ -121,8 +127,7 @@ typedef void(*voidCallbackPtr)(void *); /* mapping the IRQn for the one-second interrupt depending on the soc */ #if defined(STM32F1xx) || (defined(STM32F0xx) && defined(RTC_CR_WUTE)) || \ - defined(STM32L0xx) || defined(STM32L5xx) || defined(STM32U5xx) || \ - defined(STM32WLE4xx) + defined(STM32L0xx) || defined(STM32L5xx) || defined(STM32U5xx) // specific WakeUp interrupt #define ONESECOND_IRQn RTC_IRQn #elif defined(STM32MP1xx) @@ -131,7 +136,8 @@ typedef void(*voidCallbackPtr)(void *); #elif defined(STM32G0xx) // global RTC/TAMP interrupt #define ONESECOND_IRQn RTC_TAMP_IRQn -#elif defined(STM32WL54xx)|| defined(STM32WL55xx) +#elif defined(CORE_CM0PLUS) && \ + (defined(STM32WL54xx) || defined(STM32WL55xx) || defined(STM32WL5Mxx)) // global RTC/LSS interrupt #define ONESECOND_IRQn RTC_LSECSS_IRQn #elif defined(RTC_CR_WUTE) @@ -155,18 +161,6 @@ typedef void(*voidCallbackPtr)(void *); #define IS_RTC_HOUR12(HOUR) IS_RTC_HOUR24(HOUR) #endif /* !STM32F1xx && !IS_RTC_WEEKDAY */ -/* __HAL_RCC_GET_RTC_SOURCE is not defined for F2*/ -/* -#ifndef __HAL_RCC_GET_RTC_SOURCE -static uint32_t RTC_getSource(void) { - RCC_PeriphCLKInitTypeDef *PeriphClkInit; - HAL_RCCEx_GetPeriphCLKConfig(PeriphClkInit); - return PeriphClkInit->RTCClockSelection; -} -#define __HAL_RCC_GET_RTC_SOURCE() RTC_getSource() -#endif -*/ - /* Exported macro ------------------------------------------------------------*/ /* Exported functions ------------------------------------------------------- */ void RTC_SetClockSource(sourceClock_t source); @@ -188,12 +182,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);