From c8069d63feb77b5fa55bb991c7bd9b94114aef17 Mon Sep 17 00:00:00 2001 From: Francois Ramu Date: Tue, 9 May 2023 14:41:30 +0200 Subject: [PATCH 1/7] feat: add a parameter to set binary mode This is valid when the RTC_BINARY_MIX mode exists in the RTC (bitfield in the RTC ICSR register) Set the RTC mode through a setBinaryMode function to be called before begin. Signed-off-by: Francois Ramu --- src/STM32RTC.cpp | 21 ++++++++++ src/STM32RTC.h | 10 +++++ src/rtc.c | 103 +++++++++++++++++++++++++++++++++-------------- src/rtc.h | 8 +++- 4 files changed, 110 insertions(+), 32 deletions(-) diff --git a/src/STM32RTC.cpp b/src/STM32RTC.cpp index 0c8bf9d..3ee776c 100644 --- a/src/STM32RTC.cpp +++ b/src/STM32RTC.cpp @@ -63,6 +63,7 @@ void STM32RTC::begin(bool resetTime, Hour_Format format) _format = format; reinit = RTC_init((format == HOUR_12) ? HOUR_FORMAT_12 : HOUR_FORMAT_24, + (_mode == MODE_MIX) ? ::MODE_BINARY_MIX : ((_mode == MODE_BIN) ? ::MODE_BINARY_ONLY : ::MODE_BINARY_NONE), (_clockSource == LSE_CLOCK) ? ::LSE_CLOCK : (_clockSource == HSE_CLOCK) ? ::HSE_CLOCK : ::LSI_CLOCK , resetTime); @@ -131,6 +132,26 @@ void STM32RTC::setClockSource(Source_Clock source) } } +/** + * @brief get the Binary Mode. + * @retval mode: MODE_BCD, MODE_BIN or MODE_MIX + */ +STM32RTC::Binary_Mode STM32RTC::getBinaryMode(void) +{ + return _mode; +} + +/** + * @brief set the Binary Mode. By default MODE_BCD is selected. This + * method must be called before begin(). + * @param mode: the RTC mode: MODE_BCD, MODE_BIN or MODE_MIX + * @retval None + */ +void STM32RTC::setBinaryMode(Binary_Mode mode) +{ + _mode = mode; +} + #if defined(STM32F1xx) /** * @brief get user asynchronous prescaler value for the current clock source. diff --git a/src/STM32RTC.h b/src/STM32RTC.h index b8be12b..eba3f5c 100644 --- a/src/STM32RTC.h +++ b/src/STM32RTC.h @@ -85,6 +85,12 @@ class STM32RTC { PM = HOUR_PM }; + enum Binary_Mode : uint8_t { + MODE_BCD = MODE_BINARY_NONE, /* not used */ + MODE_BIN = MODE_BINARY_ONLY, + MODE_MIX = MODE_BINARY_MIX + }; + enum Alarm_Match : uint8_t { MATCH_OFF = OFF_MSK, // Never MATCH_SS = SS_MSK, // Every Minute @@ -128,6 +134,9 @@ class STM32RTC { Source_Clock getClockSource(void); void setClockSource(Source_Clock source); + Binary_Mode getBinaryMode(void); + void setBinaryMode(Binary_Mode mode); + void enableAlarm(Alarm_Match match, Alarm name = ALARM_A); void disableAlarm(Alarm name = ALARM_A); @@ -237,6 +246,7 @@ class STM32RTC { static bool _timeSet; Hour_Format _format; + Binary_Mode _mode; AM_PM _hoursPeriod; uint8_t _hours; uint8_t _minutes; diff --git a/src/rtc.c b/src/rtc.c index 903ca7c..03aafd2 100644 --- a/src/rtc.c +++ b/src/rtc.c @@ -66,9 +66,10 @@ static void *callbackUserDataB = NULL; static voidCallbackPtr RTCSecondsIrqCallback = NULL; static sourceClock_t clkSrc = LSI_CLOCK; +static uint32_t clkVal = 0; static uint8_t HSEDiv = 0; #if !defined(STM32F1xx) -/* predividers values */ +/* predividers values (-1 means not initialised values) */ static uint8_t predivSync_bits = 0xFF; static int8_t predivAsync = -1; static int16_t predivSync = -1; @@ -77,12 +78,16 @@ static uint32_t prediv = RTC_AUTO_1_SECOND; #endif /* !STM32F1xx */ static hourFormat_t initFormat = HOUR_FORMAT_12; +static binaryMode_t initMode = MODE_BINARY_NONE; /* Private function prototypes -----------------------------------------------*/ static void RTC_initClock(sourceClock_t source); #if !defined(STM32F1xx) static void RTC_computePrediv(int8_t *asynch, int16_t *synch); #endif /* !STM32F1xx */ +#if defined(RTC_BINARY_NONE) +static void RTC_BinaryConf(binaryMode_t mode); +#endif static inline int _log2(int x) { @@ -134,6 +139,7 @@ static void RTC_initClock(sourceClock_t source) Error_Handler(); } clkSrc = LSE_CLOCK; + clkVal = LSE_VALUE; } else if (source == HSE_CLOCK) { /* Enable the clock if not already set by user */ enableClock(HSE_CLOCK); @@ -192,6 +198,7 @@ static void RTC_initClock(sourceClock_t source) Error_Handler(); } clkSrc = HSE_CLOCK; + clkVal = HSE_VALUE / HSEDiv; } else if (source == LSI_CLOCK) { /* Enable the clock if not already set by user */ enableClock(LSI_CLOCK); @@ -202,6 +209,7 @@ static void RTC_initClock(sourceClock_t source) Error_Handler(); } clkSrc = LSI_CLOCK; + clkVal = LSI_VALUE; } else { Error_Handler(); } @@ -237,8 +245,11 @@ void RTC_setPrediv(int8_t asynch, int16_t synch) } else { RTC_computePrediv(&predivAsync, &predivSync); } + predivSync_bits = (uint8_t)_log2(predivSync) + 1; } + + #endif /* STM32F1xx */ #if defined(STM32F1xx) @@ -285,29 +296,17 @@ void RTC_getPrediv(int8_t *asynch, int16_t *synch) static void RTC_computePrediv(int8_t *asynch, int16_t *synch) { uint32_t predivS = PREDIVS_MAX + 1; - uint32_t clk = 0; /* Get user predividers if manually configured */ if ((asynch == NULL) || (synch == NULL)) { return; } - /* Get clock frequency */ - if (clkSrc == LSE_CLOCK) { - clk = LSE_VALUE; - } else if (clkSrc == LSI_CLOCK) { - clk = LSI_VALUE; - } else if (clkSrc == HSE_CLOCK) { - clk = HSE_VALUE / HSEDiv; - } else { - Error_Handler(); - } - /* Find (a)synchronous prescalers to obtain the 1Hz calendar clock */ for (*asynch = PREDIVA_MAX; *asynch >= 0; (*asynch)--) { - predivS = (clk / (*asynch + 1)) - 1; + predivS = (clkVal / (*asynch + 1)) - 1; - if (((predivS + 1) * (*asynch + 1)) == clk) { + if (((predivS + 1) * (*asynch + 1)) == clkVal) { break; } } @@ -318,7 +317,7 @@ static void RTC_computePrediv(int8_t *asynch, int16_t *synch) */ if ((predivS > PREDIVS_MAX) || (*asynch < 0)) { *asynch = PREDIVA_MAX; - predivS = (clk / (*asynch + 1)) - 1; + predivS = (clkVal / (*asynch + 1)) - 1; } if (predivS > PREDIVS_MAX) { @@ -328,17 +327,49 @@ static void RTC_computePrediv(int8_t *asynch, int16_t *synch) } #endif /* !STM32F1xx */ +#if defined(RTC_BINARY_NONE) +static void RTC_BinaryConf(binaryMode_t mode) +{ + RtcHandle.Init.BinMode = (mode == MODE_BINARY_MIX) ? RTC_BINARY_MIX : ((mode == MODE_BINARY_ONLY) ? RTC_BINARY_ONLY : RTC_BINARY_NONE); + if (RtcHandle.Init.BinMode == RTC_BINARY_MIX) { + /* Configure the 1s BCD calendar increment */ + + uint32_t inc = 1 / (1.0 / ((float)clkVal / (float)(predivAsync + 1.0))); + if (inc <= 256) { + RtcHandle.Init.BinMixBcdU = RTC_BINARY_MIX_BCDU_0; + } else if (inc < (256 << 1)) { + RtcHandle.Init.BinMixBcdU = RTC_BINARY_MIX_BCDU_1; + } else if (inc < (256 << 2)) { + RtcHandle.Init.BinMixBcdU = RTC_BINARY_MIX_BCDU_2; + } else if (inc < (256 << 3)) { + RtcHandle.Init.BinMixBcdU = RTC_BINARY_MIX_BCDU_3; + } else if (inc < (256 << 4)) { + RtcHandle.Init.BinMixBcdU = RTC_BINARY_MIX_BCDU_4; + } else if (inc < (256 << 5)) { + RtcHandle.Init.BinMixBcdU = RTC_BINARY_MIX_BCDU_5; + } else if (inc < (256 << 6)) { + RtcHandle.Init.BinMixBcdU = RTC_BINARY_MIX_BCDU_6; + } else if (inc < (256 << 7)) { + RtcHandle.Init.BinMixBcdU = RTC_BINARY_MIX_BCDU_7; + } else { + Error_Handler(); + } + } +} +#endif /* RTC_BINARY_NONE */ + /** * @brief RTC Initialization * This function configures the RTC time and calendar. By default, the * RTC is set to the 1st January 2001 * Note: year 2000 is invalid as it is the hardware reset value and doesn't raise INITS flag * @param format: enable the RTC in 12 or 24 hours mode + * @param mode: enable the RTC in BCD or Mix or Binary mode * @param source: RTC clock source: LSE, LSI or HSE * @param reset: force RTC reset, even if previously configured * @retval True if RTC is reinitialized, else false */ -bool RTC_init(hourFormat_t format, sourceClock_t source, bool reset) +bool RTC_init(hourFormat_t format, binaryMode_t mode, sourceClock_t source, bool reset) { bool reinit = false; hourAM_PM_t period = HOUR_AM, alarmPeriod = HOUR_AM; @@ -360,6 +391,7 @@ bool RTC_init(hourFormat_t format, sourceClock_t source, bool reset) #endif initFormat = format; + initMode = mode; RtcHandle.Instance = RTC; /* Ensure backup domain is enabled before we init the RTC so we can use the backup registers for date retention on stm32f1xx boards */ @@ -387,10 +419,12 @@ bool RTC_init(hourFormat_t format, sourceClock_t source, bool reset) /* Let HAL calculate the prescaler */ RtcHandle.Init.AsynchPrediv = prediv; RtcHandle.Init.OutPut = RTC_OUTPUTSOURCE_NONE; + // Init RTC clock + RTC_initClock(source); #else if (!LL_RTC_IsActiveFlag_INITS(RtcHandle.Instance) || reset) { // RTC needs initialization - RtcHandle.Init.HourFormat = format == HOUR_FORMAT_12 ? RTC_HOURFORMAT_12 : RTC_HOURFORMAT_24; + RtcHandle.Init.HourFormat = (format == HOUR_FORMAT_12) ? RTC_HOURFORMAT_12 : RTC_HOURFORMAT_24; RtcHandle.Init.OutPut = RTC_OUTPUT_DISABLE; RtcHandle.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH; RtcHandle.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN; @@ -400,14 +434,15 @@ bool RTC_init(hourFormat_t format, sourceClock_t source, bool reset) #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 RTC_initClock(source); + RTC_getPrediv((int8_t *) & (RtcHandle.Init.AsynchPrediv), (int16_t *) & (RtcHandle.Init.SynchPrediv)); +#if defined(RTC_BINARY_NONE) + RTC_BinaryConf(mode); +#endif /* RTC_BINARY_NONE */ +#endif // STM32F1xx + HAL_RTC_Init(&RtcHandle); // Default: saturday 1st of January 2001 // Note: year 2000 is invalid as it is the hardware reset value and doesn't raise INITS flag @@ -445,23 +480,24 @@ bool RTC_init(hourFormat_t format, sourceClock_t source, bool reset) // In case of RTC source clock change, Backup Domain is reset by RTC_initClock() // Save current config before call to RTC_initClock() RTC_GetDate(&years, &month, &days, &weekDay); - RTC_GetTime(&hours, &minutes, &seconds, &subSeconds, &period); -#if defined(STM32F1xx) - RTC_getPrediv(&asynch); -#else - RTC_getPrediv(&asynch, &sync); -#endif // STM32F1xx + RTC_GetTime(mode, &hours, &minutes, &seconds, &subSeconds, &period); if (isAlarmASet) { - RTC_GetAlarm(ALARM_A, &alarmDay, &alarmHours, &alarmMinutes, &alarmSeconds, &alarmSubseconds, &alarmPeriod, &alarmMask); + RTC_GetAlarm(mode, 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); + RTC_GetAlarm(mode, ALARM_B, &alarmBDay, &alarmBHours, &alarmBMinutes, &alarmBSeconds, &alarmBSubseconds, &alarmBPeriod, &alarmBMask); } #endif // Init RTC clock RTC_initClock(source); +#if defined(STM32F1xx) + RTC_getPrediv(&asynch); +#else + RTC_getPrediv(&asynch, &sync); +#endif // STM32F1xx + // Restore config RTC_SetTime(hours, minutes, seconds, subSeconds, period); RTC_SetDate(years, month, days, weekDay); @@ -483,6 +519,7 @@ bool RTC_init(hourFormat_t format, sourceClock_t source, bool reset) // Init RTC clock RTC_initClock(source); + #if defined(STM32F1xx) memcpy(&RtcHandle.DateToUpdate, &BackupDate, 4); /* Update date automatically by calling HAL_RTC_GetDate */ @@ -494,7 +531,11 @@ bool RTC_init(hourFormat_t format, sourceClock_t source, bool reset) // This initialize variables: predivAsync, predivSync and predivSync_bits RTC_getPrediv(NULL, NULL); #endif // STM32F1xx +#if defined(RTC_BINARY_NONE) + RTC_BinaryConf(mode); +#endif /* RTC_BINARY_NONE */ } + } #if defined(RTC_CR_BYPSHAD) diff --git a/src/rtc.h b/src/rtc.h index bad868b..818885d 100644 --- a/src/rtc.h +++ b/src/rtc.h @@ -55,6 +55,12 @@ typedef enum { HOUR_FORMAT_24 } hourFormat_t; +typedef enum { + MODE_BINARY_NONE, /* BCD only */ + MODE_BINARY_ONLY, + MODE_BINARY_MIX +} binaryMode_t; + typedef enum { HOUR_AM, HOUR_PM @@ -173,7 +179,7 @@ void RTC_getPrediv(int8_t *asynch, int16_t *synch); void RTC_setPrediv(int8_t asynch, int16_t synch); #endif /* STM32F1xx */ -bool RTC_init(hourFormat_t format, sourceClock_t source, bool reset); +bool RTC_init(hourFormat_t format, binaryMode_t mode, sourceClock_t source, bool reset); void RTC_DeInit(void); bool RTC_IsConfigured(void); From 5885b253212bf4e7e216397cb07d79b5ef4dfda1 Mon Sep 17 00:00:00 2001 From: Francois Ramu Date: Wed, 10 May 2023 17:09:25 +0200 Subject: [PATCH 2/7] stm32duino RTC add a subsecond mask to configure the alarm configure the Alarm with Second mask --> trigs IRQHandler The Autoclear bit field depends on the RTC ALARM SSR register. Signed-off-by: Francois Ramu --- src/STM32RTC.cpp | 1 + src/STM32RTC.h | 1 + src/rtc.c | 42 ++++++++++++++++++++++++++++++++++++++++-- src/rtc.h | 3 ++- 4 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/STM32RTC.cpp b/src/STM32RTC.cpp index 3ee776c..546bf23 100644 --- a/src/STM32RTC.cpp +++ b/src/STM32RTC.cpp @@ -243,6 +243,7 @@ void STM32RTC::enableAlarm(Alarm_Match match, Alarm name) case MATCH_HHMMSS: case MATCH_MMSS: case MATCH_SS: + case MATCH_SUBSEC: #ifdef RTC_ALARM_B if (name == ALARM_B) { RTC_StartAlarm(::ALARM_B, _alarmBDay, _alarmBHours, _alarmBMinutes, _alarmBSeconds, diff --git a/src/STM32RTC.h b/src/STM32RTC.h index eba3f5c..4789d62 100644 --- a/src/STM32RTC.h +++ b/src/STM32RTC.h @@ -93,6 +93,7 @@ class STM32RTC { enum Alarm_Match : uint8_t { MATCH_OFF = OFF_MSK, // Never + MATCH_SUBSEC = SUBSEC_MSK, // Every Subsecond MATCH_SS = SS_MSK, // Every Minute MATCH_MMSS = SS_MSK | MM_MSK, // Every Hour MATCH_HHMMSS = SS_MSK | MM_MSK | HH_MSK, // Every Day diff --git a/src/rtc.c b/src/rtc.c index 03aafd2..cb72938 100644 --- a/src/rtc.c +++ b/src/rtc.c @@ -739,11 +739,12 @@ void RTC_StartAlarm(alarm_t name, uint8_t day, uint8_t hours, uint8_t minutes, u period = HOUR_AM; } + /* Use alarm A by default because it is common to all STM32 HAL */ + RTC_AlarmStructure.Alarm = name; + if ((((initFormat == HOUR_FORMAT_24) && IS_RTC_HOUR24(hours)) || IS_RTC_HOUR12(hours)) && 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 = name; RTC_AlarmStructure.AlarmTime.Seconds = seconds; RTC_AlarmStructure.AlarmTime.Minutes = minutes; RTC_AlarmStructure.AlarmTime.Hours = hours; @@ -799,6 +800,43 @@ void RTC_StartAlarm(alarm_t name, uint8_t day, uint8_t hours, uint8_t minutes, u UNUSED(mask); #endif /* !STM32F1xx */ + /* Set RTC_Alarm */ + HAL_RTC_SetAlarm_IT(&RtcHandle, &RTC_AlarmStructure, RTC_FORMAT_BIN); + HAL_NVIC_SetPriority(RTC_Alarm_IRQn, RTC_IRQ_PRIO, RTC_IRQ_SUBPRIO); + HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn); +#if defined(RTC_ICSR_BIN) + } else if (RtcHandle.Init.BinMode != RTC_BINARY_NONE) { + /* We have an SubSecond alarm to set in RTC_BINARY_MIX or RTC_BINARY_ONLY mode */ +#else + } else { +#endif /* RTC_ICSR_BIN */ +#if defined(RTC_SSR_SS) + { +#if defined(RTC_ALRMASSR_SSCLR) + RTC_AlarmStructure.BinaryAutoClr = RTC_ALARMSUBSECONDBIN_AUTOCLR_NO; +#endif /* RTC_ALRMASSR_SSCLR */ + RTC_AlarmStructure.AlarmMask = RTC_ALARMMASK_NONE; + +#ifdef RTC_ALARM_B + if (name == ALARM_B) + { + /* Expecting RTC_ALARMSUBSECONDBINMASK_NONE for the subsecond mask on ALARM B */ + RTC_AlarmStructure.AlarmMask = RTC_ALARMMASK_ALL; + RTC_AlarmStructure.AlarmSubSecondMask = mask << RTC_ALRMBSSR_MASKSS_Pos; + } else +#endif + { + /* Expecting RTC_ALARMSUBSECONDBINMASK_NONE for the subsecond mask on ALARM A */ + RTC_AlarmStructure.AlarmMask = RTC_ALARMMASK_ALL; + RTC_AlarmStructure.AlarmSubSecondMask = mask << RTC_ALRMASSR_MASKSS_Pos; + } + /* Special case for ALARM B configuration for stm32WL Lorawan */ + RTC_AlarmStructure.AlarmTime.SubSeconds = subSeconds; + } + +#else + UNUSED(subSeconds); +#endif /* RTC_SSR_SS */ /* Set RTC_Alarm */ HAL_RTC_SetAlarm_IT(&RtcHandle, &RTC_AlarmStructure, RTC_FORMAT_BIN); HAL_NVIC_SetPriority(RTC_Alarm_IRQn, RTC_IRQ_PRIO, RTC_IRQ_SUBPRIO); diff --git a/src/rtc.h b/src/rtc.h index 818885d..eabad9c 100644 --- a/src/rtc.h +++ b/src/rtc.h @@ -76,7 +76,8 @@ typedef enum { /* NOTE: STM32 RTC can't assign a month or a year to an alarm. Those enum are kept for compatibility but are ignored inside enableAlarm(). */ M_MSK = 16, - Y_MSK = 32 + Y_MSK = 32, + SUBSEC_MSK = 64 } alarmMask_t; typedef enum { From cbb926a94016ade8edf32553a8e063431b3146dd Mon Sep 17 00:00:00 2001 From: Francois Ramu Date: Fri, 30 Jun 2023 12:32:29 +0200 Subject: [PATCH 3/7] rtc with getTime and getAlarm depends on MIX mode When the RTC is running in MIX mode (bianry and calendar), the ssub-second register is a 32-bit value which must not be converted in ms Signed-off-by: Francois Ramu --- src/rtc.c | 29 ++++++++++++++++++----------- src/rtc.h | 3 ++- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/rtc.c b/src/rtc.c index cb72938..5d191d5 100644 --- a/src/rtc.c +++ b/src/rtc.c @@ -480,13 +480,14 @@ bool RTC_init(hourFormat_t format, binaryMode_t mode, sourceClock_t source, bool // In case of RTC source clock change, Backup Domain is reset by RTC_initClock() // Save current config before call to RTC_initClock() RTC_GetDate(&years, &month, &days, &weekDay); - RTC_GetTime(mode, &hours, &minutes, &seconds, &subSeconds, &period); + RTC_GetTime(&hours, &minutes, &seconds, &subSeconds, &period); + if (isAlarmASet) { - RTC_GetAlarm(mode, ALARM_A, &alarmDay, &alarmHours, &alarmMinutes, &alarmSeconds, &alarmSubseconds, &alarmPeriod, &alarmMask); + RTC_GetAlarm(ALARM_A, &alarmDay, &alarmHours, &alarmMinutes, &alarmSeconds, &alarmSubseconds, &alarmPeriod, &alarmMask); } #ifdef RTC_ALARM_B if (isAlarmBSet) { - RTC_GetAlarm(mode, ALARM_B, &alarmBDay, &alarmBHours, &alarmBMinutes, &alarmBSeconds, &alarmBSubseconds, &alarmBPeriod, &alarmBMask); + RTC_GetAlarm(ALARM_B, &alarmBDay, &alarmBHours, &alarmBMinutes, &alarmBSeconds, &alarmBSubseconds, &alarmBPeriod, &alarmBMask); } #endif // Init RTC clock @@ -583,14 +584,14 @@ bool RTC_IsConfigured(void) * @param hours: 0-12 or 0-23. Depends on the format used. * @param minutes: 0-59 * @param seconds: 0-59 - * @param subSeconds: 0-999 + * @param subSeconds: 0-999 (not used) * @param period: select HOUR_AM or HOUR_PM period in case RTC is set in 12 hours mode. Else ignored. * @retval None */ void RTC_SetTime(uint8_t hours, uint8_t minutes, uint8_t seconds, uint32_t subSeconds, hourAM_PM_t period) { RTC_TimeTypeDef RTC_TimeStruct; - UNUSED(subSeconds); + UNUSED(subSeconds); /* not used (read-only register) */ /* Ignore time AM PM configuration if in 24 hours format */ if (initFormat == HOUR_FORMAT_24) { period = HOUR_AM; @@ -656,7 +657,11 @@ void RTC_GetTime(uint8_t *hours, uint8_t *minutes, uint8_t *seconds, uint32_t *s } #if defined(RTC_SSR_SS) if (subSeconds != NULL) { - *subSeconds = ((predivSync - RTC_TimeStruct.SubSeconds) * 1000) / (predivSync + 1); + if (initMode == MODE_BINARY_MIX) { + *subSeconds = UINT32_MAX - RTC_TimeStruct.SubSeconds; + } else { + *subSeconds = ((predivSync - RTC_TimeStruct.SubSeconds) * 1000) / (predivSync + 1); + } } #else UNUSED(subSeconds); @@ -815,22 +820,20 @@ void RTC_StartAlarm(alarm_t name, uint8_t day, uint8_t hours, uint8_t minutes, u #if defined(RTC_ALRMASSR_SSCLR) RTC_AlarmStructure.BinaryAutoClr = RTC_ALARMSUBSECONDBIN_AUTOCLR_NO; #endif /* RTC_ALRMASSR_SSCLR */ - RTC_AlarmStructure.AlarmMask = RTC_ALARMMASK_NONE; + RTC_AlarmStructure.AlarmMask = RTC_ALARMMASK_ALL; #ifdef RTC_ALARM_B if (name == ALARM_B) { /* Expecting RTC_ALARMSUBSECONDBINMASK_NONE for the subsecond mask on ALARM B */ - RTC_AlarmStructure.AlarmMask = RTC_ALARMMASK_ALL; RTC_AlarmStructure.AlarmSubSecondMask = mask << RTC_ALRMBSSR_MASKSS_Pos; } else #endif { /* Expecting RTC_ALARMSUBSECONDBINMASK_NONE for the subsecond mask on ALARM A */ - RTC_AlarmStructure.AlarmMask = RTC_ALARMMASK_ALL; RTC_AlarmStructure.AlarmSubSecondMask = mask << RTC_ALRMASSR_MASKSS_Pos; } - /* Special case for ALARM B configuration for stm32WL Lorawan */ + /* Special case for ALARM B configuration when using subsecond reg. in RTC Mix mode */ RTC_AlarmStructure.AlarmTime.SubSeconds = subSeconds; } @@ -927,7 +930,11 @@ void RTC_GetAlarm(alarm_t name, uint8_t *day, uint8_t *hours, uint8_t *minutes, } #if defined(RTC_SSR_SS) if (subSeconds != NULL) { - *subSeconds = ((predivSync - RTC_AlarmStructure.AlarmTime.SubSeconds) * 1000) / (predivSync + 1); + if (initMode == MODE_BINARY_MIX) { + *subSeconds = UINT32_MAX - RTC_AlarmStructure.AlarmTime.SubSeconds; + } else { + *subSeconds = ((predivSync - RTC_AlarmStructure.AlarmTime.SubSeconds) * 1000) / (predivSync + 1); + } } #else UNUSED(subSeconds); diff --git a/src/rtc.h b/src/rtc.h index eabad9c..75e1c8c 100644 --- a/src/rtc.h +++ b/src/rtc.h @@ -77,7 +77,8 @@ typedef enum { are kept for compatibility but are ignored inside enableAlarm(). */ M_MSK = 16, Y_MSK = 32, - SUBSEC_MSK = 64 + SUBSEC_MSK = 48, + ALL_MSK = 0xFF } alarmMask_t; typedef enum { From 2ea1ffc191c624657fd51819f47da40913b5b150 Mon Sep 17 00:00:00 2001 From: Francois Ramu Date: Fri, 30 Jun 2023 12:32:14 +0200 Subject: [PATCH 4/7] STM32RTC library configure the alarm depending on the MIX mode In case the RTC is running in MIX mode (BINary and calendar), the subsecond register is a 32-bit value (and not msec) Signed-off-by: Francois Ramu --- src/STM32RTC.cpp | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/STM32RTC.cpp b/src/STM32RTC.cpp index 546bf23..1298a9a 100644 --- a/src/STM32RTC.cpp +++ b/src/STM32RTC.cpp @@ -237,13 +237,27 @@ void STM32RTC::enableAlarm(Alarm_Match match, Alarm name) RTC_StopAlarm(::ALARM_A); } break; + case MATCH_SUBSEC: + /* force _alarmday to 0 to go to the right alarm config in MIX mode */ +#ifdef RTC_ALARM_B + if (name == ALARM_B) { + RTC_StartAlarm(::ALARM_B, 0, 0, 0, 0, + (UINT32_MAX - _alarmBSubSeconds), (_alarmBPeriod == AM) ? HOUR_AM : HOUR_PM, + static_cast(31UL)); + } else +#endif + { + RTC_StartAlarm(::ALARM_A, 0, 0, 0, 0, + (UINT32_MAX - _alarmSubSeconds), (_alarmPeriod == AM) ? HOUR_AM : HOUR_PM, + static_cast(31UL)); + } + break; case MATCH_YYMMDDHHMMSS://kept for compatibility case MATCH_MMDDHHMMSS: //kept for compatibility case MATCH_DHHMMSS: case MATCH_HHMMSS: case MATCH_MMSS: case MATCH_SS: - case MATCH_SUBSEC: #ifdef RTC_ALARM_B if (name == ALARM_B) { RTC_StartAlarm(::ALARM_B, _alarmBDay, _alarmBHours, _alarmBMinutes, _alarmBSeconds, @@ -845,7 +859,8 @@ void STM32RTC::setDate(uint8_t weekDay, uint8_t day, uint8_t month, uint8_t year */ void STM32RTC::setAlarmSubSeconds(uint32_t subSeconds, Alarm name) { - if (subSeconds < 1000) { + + if (getBinaryMode() == MODE_MIX) { #ifdef RTC_ALARM_B if (name == ALARM_B) { _alarmBSubSeconds = subSeconds; @@ -856,6 +871,19 @@ void STM32RTC::setAlarmSubSeconds(uint32_t subSeconds, Alarm name) { _alarmSubSeconds = subSeconds; } + } else { + if (subSeconds < 1000) { +#ifdef RTC_ALARM_B + if (name == ALARM_B) { + _alarmBSubSeconds = subSeconds; + } +#else + UNUSED(name); +#endif + { + _alarmSubSeconds = subSeconds; + } + } } } @@ -1268,6 +1296,7 @@ bool STM32RTC::isAlarmEnabled(Alarm name) void STM32RTC::syncTime(void) { hourAM_PM_t p = HOUR_AM; + RTC_GetTime(&_hours, &_minutes, &_seconds, &_subSeconds, &p); _hoursPeriod = (p == HOUR_AM) ? AM : PM; } From 35da74919e5a0ac9f76ee416f111947a53494377 Mon Sep 17 00:00:00 2001 From: Francois Ramu Date: Tue, 13 Jun 2023 14:51:34 +0200 Subject: [PATCH 5/7] export the RTC Handle as a global Set the subsecond for the alarm value. Add the HAL_MSP_Init/DeInit function from the rtc library. Signed-off-by: Francois Ramu --- src/rtc.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/src/rtc.c b/src/rtc.c index 5d191d5..a561cb4 100644 --- a/src/rtc.c +++ b/src/rtc.c @@ -56,7 +56,6 @@ extern "C" { /* Private define ------------------------------------------------------------*/ /* Private macro -------------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ -static RTC_HandleTypeDef RtcHandle = {0}; static voidCallbackPtr RTCUserCallback = NULL; static void *callbackUserData = NULL; #ifdef RTC_ALARM_B @@ -94,8 +93,56 @@ static inline int _log2(int x) return (x > 0) ? (sizeof(int) * 8 - __builtin_clz(x) - 1) : 0; } +/* Exported variable --------------------------------------------------------*/ +RTC_HandleTypeDef RtcHandle = {0}; + /* Exported functions --------------------------------------------------------*/ +/* HAL MSP function used for RTC_Init */ +void HAL_RTC_MspInit(RTC_HandleTypeDef *rtcHandle) +{ +#if defined(STM32WLxx) + /* Only the STM32WLxx series has a TAMP_STAMP_LSECSS_SSRU_IRQn */ + if (rtcHandle->Instance == RTC) + { + if (HAL_RTCEx_SetSSRU_IT(rtcHandle) != HAL_OK) { + Error_Handler(); + } + /* Give init value for the RtcFeatures enable */ + rtcHandle->IsEnabled.RtcFeatures = 0; + + /* RTC interrupt Init */ + HAL_NVIC_SetPriority(TAMP_STAMP_LSECSS_SSRU_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(TAMP_STAMP_LSECSS_SSRU_IRQn); + + } +#else + UNUSED(rtcHandle); +#endif /* STM32WLxx */ + /* RTC_Alarm_IRQn is enabled when enabling Alarm */ +} + +void HAL_RTC_MspDeInit(RTC_HandleTypeDef *rtcHandle) +{ + + if (rtcHandle->Instance == RTC) + { + /* USER CODE BEGIN RTC_MspDeInit 0 */ + + /* USER CODE END RTC_MspDeInit 0 */ + /* Peripheral clock disable */ + __HAL_RCC_RTC_DISABLE(); + __HAL_RCC_RTCAPB_CLK_DISABLE(); + + /* RTC interrupt Deinit */ + HAL_NVIC_DisableIRQ(TAMP_STAMP_LSECSS_SSRU_IRQn); + HAL_NVIC_DisableIRQ(RTC_Alarm_IRQn); + /* USER CODE BEGIN RTC_MspDeInit 1 */ + + /* USER CODE END RTC_MspDeInit 1 */ + } +} + /** * @brief Set RTC clock source * @param source: RTC clock source: LSE, LSI or HSE @@ -737,7 +784,7 @@ void RTC_GetDate(uint8_t *year, uint8_t *month, uint8_t *day, uint8_t *wday) */ 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; + RTC_AlarmTypeDef RTC_AlarmStructure = {0}; /* Ignore time AM PM configuration if in 24 hours format */ if (initFormat == HOUR_FORMAT_24) { From afa27aef6010a37a8311c11517ced9f30662fcc3 Mon Sep 17 00:00:00 2001 From: Francois Ramu Date: Thu, 7 Sep 2023 10:16:06 +0200 Subject: [PATCH 6/7] stm32RTC with subsecond to milliseconds conversion The binaryMode_t is retrieved directly from the RTC mode, not as a parameter. The Subsecond parameter is expressed in millisecond in RTC_SetTime/GetTime RTC_StartAlarm/GetAlarm Signed-off-by: Francois Ramu --- src/STM32RTC.cpp | 22 ++++------------------ src/rtc.c | 30 ++++++++++++++++++++++++------ 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/STM32RTC.cpp b/src/STM32RTC.cpp index 1298a9a..3799109 100644 --- a/src/STM32RTC.cpp +++ b/src/STM32RTC.cpp @@ -242,13 +242,13 @@ void STM32RTC::enableAlarm(Alarm_Match match, Alarm name) #ifdef RTC_ALARM_B if (name == ALARM_B) { RTC_StartAlarm(::ALARM_B, 0, 0, 0, 0, - (UINT32_MAX - _alarmBSubSeconds), (_alarmBPeriod == AM) ? HOUR_AM : HOUR_PM, + _alarmBSubSeconds, (_alarmBPeriod == AM) ? HOUR_AM : HOUR_PM, static_cast(31UL)); } else #endif { RTC_StartAlarm(::ALARM_A, 0, 0, 0, 0, - (UINT32_MAX - _alarmSubSeconds), (_alarmPeriod == AM) ? HOUR_AM : HOUR_PM, + _alarmSubSeconds, (_alarmPeriod == AM) ? HOUR_AM : HOUR_PM, static_cast(31UL)); } break; @@ -647,7 +647,7 @@ uint8_t STM32RTC::getAlarmYear(void) /** * @brief set RTC subseconds. - * @param subseconds: 0-999 + * @param subseconds: 0-999 milliseconds * @retval none */ void STM32RTC::setSubSeconds(uint32_t subSeconds) @@ -859,8 +859,7 @@ void STM32RTC::setDate(uint8_t weekDay, uint8_t day, uint8_t month, uint8_t year */ void STM32RTC::setAlarmSubSeconds(uint32_t subSeconds, Alarm name) { - - if (getBinaryMode() == MODE_MIX) { + if (subSeconds < 1000) { #ifdef RTC_ALARM_B if (name == ALARM_B) { _alarmBSubSeconds = subSeconds; @@ -871,19 +870,6 @@ void STM32RTC::setAlarmSubSeconds(uint32_t subSeconds, Alarm name) { _alarmSubSeconds = subSeconds; } - } else { - if (subSeconds < 1000) { -#ifdef RTC_ALARM_B - if (name == ALARM_B) { - _alarmBSubSeconds = subSeconds; - } -#else - UNUSED(name); -#endif - { - _alarmSubSeconds = subSeconds; - } - } } } diff --git a/src/rtc.c b/src/rtc.c index a561cb4..ef41ec4 100644 --- a/src/rtc.c +++ b/src/rtc.c @@ -76,6 +76,8 @@ static int16_t predivSync = -1; static uint32_t prediv = RTC_AUTO_1_SECOND; #endif /* !STM32F1xx */ +static uint32_t fqce_apre; + static hourFormat_t initFormat = HOUR_FORMAT_12; static binaryMode_t initMode = MODE_BINARY_NONE; @@ -371,6 +373,8 @@ static void RTC_computePrediv(int8_t *asynch, int16_t *synch) Error_Handler(); } *synch = (int16_t)predivS; + + fqce_apre = clkVal / (*asynch + 1); } #endif /* !STM32F1xx */ @@ -705,8 +709,13 @@ void RTC_GetTime(uint8_t *hours, uint8_t *minutes, uint8_t *seconds, uint32_t *s #if defined(RTC_SSR_SS) if (subSeconds != NULL) { if (initMode == MODE_BINARY_MIX) { - *subSeconds = UINT32_MAX - RTC_TimeStruct.SubSeconds; + /* The subsecond is the free-running downcounter, to be converted in milliseconds */ + *subSeconds = (((UINT32_MAX - RTC_TimeStruct.SubSeconds + 1) & UINT32_MAX) + * 1000) / fqce_apre; /* give one more to compensate the 3.9ms uncertainty */ + *subSeconds = *subSeconds % 1000; /* nb of milliseconds */ + /* predivAsync is 0x7F with LSE clock source */ } else { + /* the subsecond register value is converted in millisec */ *subSeconds = ((predivSync - RTC_TimeStruct.SubSeconds) * 1000) / (predivSync + 1); } } @@ -776,7 +785,7 @@ void RTC_GetDate(uint8_t *year, uint8_t *month, uint8_t *day, uint8_t *wday) * @param hours: 0-12 or 0-23 depends on the hours mode. * @param minutes: 0-59 * @param seconds: 0-59 - * @param subSeconds: 0-999 + * @param subSeconds: 0-999 milliseconds * @param period: HOUR_AM or HOUR_PM if in 12 hours mode else ignored. * @param mask: configure alarm behavior using alarmMask_t combination. * See AN4579 Table 5 for possible values. @@ -811,7 +820,12 @@ void RTC_StartAlarm(alarm_t name, uint8_t day, uint8_t hours, uint8_t minutes, u { RTC_AlarmStructure.AlarmSubSecondMask = predivSync_bits << RTC_ALRMASSR_MASKSS_Pos; } - RTC_AlarmStructure.AlarmTime.SubSeconds = predivSync - (subSeconds * (predivSync + 1)) / 1000; + if (initMode == MODE_BINARY_MIX) { + /* the subsecond is the millisecond to be converted in a subsecond downcounter value */ + RTC_AlarmStructure.AlarmTime.SubSeconds = UINT32_MAX - ((subSeconds * fqce_apre) / 1000 + 1); + } else { + RTC_AlarmStructure.AlarmTime.SubSeconds = predivSync - (subSeconds * (predivSync + 1)) / 1000; + } } else { RTC_AlarmStructure.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL; } @@ -880,8 +894,8 @@ void RTC_StartAlarm(alarm_t name, uint8_t day, uint8_t hours, uint8_t minutes, u /* Expecting RTC_ALARMSUBSECONDBINMASK_NONE for the subsecond mask on ALARM A */ RTC_AlarmStructure.AlarmSubSecondMask = mask << RTC_ALRMASSR_MASKSS_Pos; } - /* Special case for ALARM B configuration when using subsecond reg. in RTC Mix mode */ - RTC_AlarmStructure.AlarmTime.SubSeconds = subSeconds; + /* The subsecond in ms is converted in ticks unit 1 tick is 1000 / fqce_apre */ + RTC_AlarmStructure.AlarmTime.SubSeconds = UINT32_MAX - (subSeconds * fqce_apre) / 1000; } #else @@ -978,7 +992,11 @@ void RTC_GetAlarm(alarm_t name, uint8_t *day, uint8_t *hours, uint8_t *minutes, #if defined(RTC_SSR_SS) if (subSeconds != NULL) { if (initMode == MODE_BINARY_MIX) { - *subSeconds = UINT32_MAX - RTC_AlarmStructure.AlarmTime.SubSeconds; + /* + * The subsecond is the bit SS[14:0] of the ALARM SSR register (not ALARMxINR) + * to be converted in milliseconds + */ + *subSeconds = (((0x7fff - RTC_AlarmStructure.AlarmTime.SubSeconds + 1) & 0x7fff) * 1000) / fqce_apre; } else { *subSeconds = ((predivSync - RTC_AlarmStructure.AlarmTime.SubSeconds) * 1000) / (predivSync + 1); } From 8832c54433dea5d5a20da764b6de449dddda6676 Mon Sep 17 00:00:00 2001 From: Francois Ramu Date: Mon, 3 Jul 2023 09:35:00 +0200 Subject: [PATCH 7/7] add new sketch for RTC running in MIX mode with Alarm This examples is configuring the RTC in MIX mode (binary + calendar BCD) and set Alarm B (if exists) few ms after the current time, whenAlarm A is set in calendar mode. Mainly used on stm32wl55 devices. --- examples/mixRTCAlarm/mixRTCAlarm.ino | 97 ++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 examples/mixRTCAlarm/mixRTCAlarm.ino diff --git a/examples/mixRTCAlarm/mixRTCAlarm.ino b/examples/mixRTCAlarm/mixRTCAlarm.ino new file mode 100644 index 0000000..02ea167 --- /dev/null +++ b/examples/mixRTCAlarm/mixRTCAlarm.ino @@ -0,0 +1,97 @@ +/* + mode Mix RTC alarm + + This sketch shows how to configure the alarm A & B of the RTC in MIX mode + + Creation 12 Dec 2017 + by Wi6Labs + Modified 03 Jul 2020 + by Frederic Pillon for STMicroelectronics + Modified 03 Jul 2023 + by Francois Ramu 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 */ +const byte seconds = 06; +const byte minutes = 22; +const byte hours = 16; + +/* Change these values to set the current initial date */ +const byte day = 25; +const byte month = 6; +const byte year = 23; + +uint32_t timeout; + +void setup() +{ + Serial.begin(115200); + + // Select RTC clock source: LSI_CLOCK, LSE_CLOCK or HSE_CLOCK. + rtc.setClockSource(STM32RTC::LSE_CLOCK); + + /* Configure the MIX mode */ + rtc.setBinaryMode(STM32RTC::MODE_MIX); + + rtc.begin(true, STM32RTC::HOUR_24); + + rtc.setTime(hours, minutes, seconds); + rtc.setDate(day, month, year); + + /* wait for a while */ + delay(200); + + Serial.printf("Start at %02d:%02d:%02d.%03d\r\n", + rtc.getHours(), rtc.getMinutes(), rtc.getSeconds(), rtc.getSubSeconds()); + + /* Attach the callback function before enabling Interrupt */ + rtc.attachInterrupt(alarmAMatch); + + /* Program the AlarmA in a 12 seconds */ + rtc.setAlarmDay(day); + rtc.setAlarmTime(hours, minutes, seconds + 12); + rtc.enableAlarm(rtc.MATCH_DHHMMSS); + Serial.printf("Set Alarm A in 12s (at %02d:%02d:%02d)\r\n", + rtc.getAlarmHours(), rtc.getAlarmMinutes(), rtc.getAlarmSeconds()); + +#ifdef RTC_ALARM_B + /* Program ALARM B in 400ms ms from now (keep timeout < 1000ms) */ + timeout = rtc.getSubSeconds() + 400; + + rtc.attachInterrupt(alarmBMatch, STM32RTC::ALARM_B); + rtc.setAlarmSubSeconds(timeout, STM32RTC::ALARM_B); + rtc.enableAlarm(rtc.MATCH_SUBSEC, STM32RTC::ALARM_B); + Serial.printf("Set Alarm B (in %d ms) at %d ms\r\n", 400, + rtc.getAlarmSubSeconds(STM32RTC::ALARM_B)); +#endif + +} + +void loop() +{ + +} + +void alarmAMatch(void *data) +{ + UNUSED(data); + Serial.printf("Alarm A Match at %02d:%02d:%02d\r\n", + rtc.getHours(), rtc.getMinutes(), rtc.getSeconds()); +} + +void alarmBMatch(void *data) +{ + UNUSED(data); + Serial.printf("Alarm B Match at %d ms\r\n", rtc.getSubSeconds()); +} + +