From 7714616af9369be81c5b8cb950087d4578a33a51 Mon Sep 17 00:00:00 2001 From: Francois Ramu Date: Wed, 10 May 2023 17:09:25 +0200 Subject: [PATCH 1/8] feat: support binary mode 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) The Subsecond parameter is expressed in millisecond in RTC_SetTime/GetTime RTC_StartAlarm/GetAlarm Signed-off-by: Francois Ramu --- src/STM32RTC.cpp | 18 ++++++++++- src/STM32RTC.h | 1 + src/rtc.c | 77 +++++++++++++++++++++++++++++++++++++++++++----- src/rtc.h | 4 ++- 4 files changed, 90 insertions(+), 10 deletions(-) diff --git a/src/STM32RTC.cpp b/src/STM32RTC.cpp index 3397043..a9b0ef3 100644 --- a/src/STM32RTC.cpp +++ b/src/STM32RTC.cpp @@ -220,6 +220,21 @@ 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, + _alarmBSubSeconds, (_alarmBPeriod == AM) ? HOUR_AM : HOUR_PM, + static_cast(31UL)); + } else +#endif + { + RTC_StartAlarm(::ALARM_A, 0, 0, 0, 0, + _alarmSubSeconds, (_alarmPeriod == AM) ? HOUR_AM : HOUR_PM, + static_cast(31UL)); + } + break; case MATCH_YYMMDDHHMMSS://kept for compatibility case MATCH_MMDDHHMMSS: //kept for compatibility case MATCH_DHHMMSS: @@ -615,7 +630,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) @@ -1250,6 +1265,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; } diff --git a/src/STM32RTC.h b/src/STM32RTC.h index f0e065b..489ba38 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 8e86ae6..36e4c5e 100644 --- a/src/rtc.c +++ b/src/rtc.c @@ -73,6 +73,7 @@ static uint8_t HSEDiv = 0; static uint8_t predivSync_bits = 0xFF; static uint32_t predivAsync = (PREDIVA_MAX + 1); static uint32_t predivSync = (PREDIVS_MAX + 1); +static uint32_t fqce_apre; #else /* Default, let HAL calculate the prescaler*/ static uint32_t predivAsync = RTC_AUTO_1_SECOND; @@ -335,6 +336,8 @@ static void RTC_computePrediv(uint32_t *asynch, uint32_t *synch) Error_Handler(); } *synch = predivS; + + fqce_apre = clkVal / (*asynch + 1); } #endif /* !STM32F1xx */ @@ -597,14 +600,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; @@ -670,7 +673,16 @@ 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) { + /* 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); + } } #else UNUSED(subSeconds); @@ -738,7 +750,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. @@ -753,11 +765,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; @@ -772,7 +785,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; } @@ -813,6 +831,41 @@ 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_ALL; + +#ifdef RTC_ALARM_B + if (name == ALARM_B) + { + /* Expecting RTC_ALARMSUBSECONDBINMASK_NONE for the subsecond mask on ALARM B */ + RTC_AlarmStructure.AlarmSubSecondMask = mask << RTC_ALRMBSSR_MASKSS_Pos; + } else +#endif + { + /* Expecting RTC_ALARMSUBSECONDBINMASK_NONE for the subsecond mask on ALARM A */ + RTC_AlarmStructure.AlarmSubSecondMask = mask << RTC_ALRMASSR_MASKSS_Pos; + } + /* 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 + 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); @@ -903,7 +956,15 @@ 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) { + /* + * 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); + } } #else UNUSED(subSeconds); diff --git a/src/rtc.h b/src/rtc.h index 72070d2..629dc9e 100644 --- a/src/rtc.h +++ b/src/rtc.h @@ -76,7 +76,9 @@ 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 = 48, + ALL_MSK = 0xFF } alarmMask_t; typedef enum { From 699658dbbb24a10d754f887379a05f11e800f7c1 Mon Sep 17 00:00:00 2001 From: Francois Ramu Date: Tue, 13 Jun 2023 14:51:34 +0200 Subject: [PATCH 2/8] chore: provide a way to get the RTC handle Signed-off-by: Frederic Pillon --- README.md | 6 ++++++ src/STM32RTC.h | 6 ++++++ src/rtc.c | 9 +++++++++ src/rtc.h | 1 + 4 files changed, 22 insertions(+) diff --git a/README.md b/README.md index 6340ce2..c913529 100644 --- a/README.md +++ b/README.md @@ -153,6 +153,12 @@ It is possible to use it thanks all alarm API with an extra argument: rtc.enableAlarm(rtc.MATCH_DHHMMSS, STM32RTC::ALARM_B); ``` +### Since STM32RTC version higher than 1.3.7 +_Get the RTC handle_ + +* **`RTC_HandleTypeDef *RTC_GetHandle(void)`** + + Refer to the Arduino RTC documentation for the other functions http://arduino.cc/en/Reference/RTC diff --git a/src/STM32RTC.h b/src/STM32RTC.h index 489ba38..e4a8198 100644 --- a/src/STM32RTC.h +++ b/src/STM32RTC.h @@ -132,6 +132,12 @@ class STM32RTC { void end(void); + // Could be used to mix Arduino API and STM32Cube HAL API (ex: DMA). Use at your own risk. + RTC_HandleTypeDef *getHandle(void) + { + return RTC_GetHandle(); + } + Source_Clock getClockSource(void); void setClockSource(Source_Clock source, uint32_t predivA = (PREDIVA_MAX + 1), uint32_t predivS = (PREDIVS_MAX + 1)); void getPrediv(uint32_t *predivA, uint32_t *predivS); diff --git a/src/rtc.c b/src/rtc.c index 36e4c5e..7fd07a2 100644 --- a/src/rtc.c +++ b/src/rtc.c @@ -98,6 +98,15 @@ static inline int _log2(int x) /* Exported functions --------------------------------------------------------*/ +/** + * @brief Get pointer to RTC_HandleTypeDef + * @param None + * @retval pointer to RTC_HandleTypeDef + */ +RTC_HandleTypeDef *RTC_GetHandle(void) { + return &RtcHandle; +} + /** * @brief Set RTC clock source * @param source: RTC clock source: LSE, LSI or HSE diff --git a/src/rtc.h b/src/rtc.h index 629dc9e..4983148 100644 --- a/src/rtc.h +++ b/src/rtc.h @@ -173,6 +173,7 @@ typedef void(*voidCallbackPtr)(void *); /* Exported macro ------------------------------------------------------------*/ /* Exported functions ------------------------------------------------------- */ +RTC_HandleTypeDef *RTC_GetHandle(void); void RTC_SetClockSource(sourceClock_t source); void RTC_getPrediv(uint32_t *asynch, uint32_t *synch); void RTC_setPrediv(uint32_t asynch, uint32_t synch); From 88f773384a7d5a60fbdf9353b04ae8732ce7d1a0 Mon Sep 17 00:00:00 2001 From: Frederic Pillon Date: Fri, 15 Sep 2023 17:27:02 +0200 Subject: [PATCH 3/8] feat: add the HAL_MSP_Init/DeInit functions Signed-off-by: Frederic Pillon --- src/rtc.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/rtc.c b/src/rtc.c index 7fd07a2..9ee676b 100644 --- a/src/rtc.c +++ b/src/rtc.c @@ -98,6 +98,54 @@ static inline int _log2(int x) /* Exported functions --------------------------------------------------------*/ +/* HAL MSP function used for RTC_Init */ +void HAL_RTC_MspInit(RTC_HandleTypeDef *rtcHandle) +{ +#if defined(RTC_SCR_CSSRUF) + if (rtcHandle->Instance == RTC) { + /* In BINARY mode (MIX or ONLY), set the SSR Underflow interrupt */ + if (rtcHandle->Init.BinMode != RTC_BINARY_NONE) { +#if defined(STM32WLxx) + /* Only the STM32WLxx series has a TAMP_STAMP_LSECSS_SSRU_IRQn */ + 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 + /* The STM32U5, STM32H5, STM32L4plus have common RTC interrupt and a SSRU flag */ + __HAL_RTC_SSRU_ENABLE_IT(rtcHandle, RTC_IT_SSRU); +#endif /* STM32WLxx */ + } + } +#else /* RTC_SCR_CSSRUF */ + UNUSED(rtcHandle); +#endif /* RTC_SCR_CSSRUF */ + /* RTC_Alarm_IRQn is enabled when enabling Alarm */ +} + +void HAL_RTC_MspDeInit(RTC_HandleTypeDef *rtcHandle) +{ + + if (rtcHandle->Instance == RTC) { + /* Peripheral clock disable */ + __HAL_RCC_RTC_DISABLE(); +#ifdef __HAL_RCC_RTCAPB_CLK_DISABLE + __HAL_RCC_RTCAPB_CLK_DISABLE(); +#endif + /* RTC interrupt Deinit */ +#if defined(STM32WLxx) + /* Only the STM32WLxx series has a TAMP_STAMP_LSECSS_SSRU_IRQn */ + HAL_NVIC_DisableIRQ(TAMP_STAMP_LSECSS_SSRU_IRQn); +#endif /* STM32WLxx */ + HAL_NVIC_DisableIRQ(RTC_Alarm_IRQn); + } +} + /** * @brief Get pointer to RTC_HandleTypeDef * @param None From 9e2f974503a0c7d015790bae9254267ee0c39662 Mon Sep 17 00:00:00 2001 From: Francois Ramu Date: Tue, 19 Sep 2023 16:06:32 +0200 Subject: [PATCH 4/8] feat: RTC Time and Alarm configuration in BCD and BIN modes define the Getime and startAlarm function when the RTC is configured in BCD (MODE_BINARY_NONE) or BIN (MODE_BINARY_ONLY). In MODE_BINARY_ONLY, Time and Date are not used, only subsecond Signed-off-by: Francois Ramu --- src/rtc.c | 100 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 55 insertions(+), 45 deletions(-) diff --git a/src/rtc.c b/src/rtc.c index 9ee676b..2033171 100644 --- a/src/rtc.c +++ b/src/rtc.c @@ -151,7 +151,8 @@ void HAL_RTC_MspDeInit(RTC_HandleTypeDef *rtcHandle) * @param None * @retval pointer to RTC_HandleTypeDef */ -RTC_HandleTypeDef *RTC_GetHandle(void) { +RTC_HandleTypeDef *RTC_GetHandle(void) +{ return &RtcHandle; } @@ -707,7 +708,7 @@ void RTC_SetTime(uint8_t hours, uint8_t minutes, uint8_t seconds, uint32_t subSe */ void RTC_GetTime(uint8_t *hours, uint8_t *minutes, uint8_t *seconds, uint32_t *subSeconds, hourAM_PM_t *period) { - RTC_TimeTypeDef RTC_TimeStruct; + RTC_TimeTypeDef RTC_TimeStruct = {0}; /* in BIN mode, only the subsecond is used */ if ((hours != NULL) && (minutes != NULL) && (seconds != NULL)) { #if defined(STM32F1xx) @@ -730,15 +731,21 @@ void RTC_GetTime(uint8_t *hours, uint8_t *minutes, uint8_t *seconds, uint32_t *s } #if defined(RTC_SSR_SS) if (subSeconds != NULL) { + /* + * The subsecond is the free-running downcounter, to be converted in milliseconds. + * Give one more to compensate the fqce_apre uncertainty + */ if (initMode == MODE_BINARY_MIX) { - /* 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 */ + * 1000) / fqce_apre; + *subSeconds = *subSeconds % 1000; /* nb of milliseconds [0..999] */ + } else if (initMode == MODE_BINARY_ONLY) { + *subSeconds = (((UINT32_MAX - RTC_TimeStruct.SubSeconds + 1) & UINT32_MAX) + * 1000) / fqce_apre; } else { - /* the subsecond register value is converted in millisec */ - *subSeconds = ((predivSync - RTC_TimeStruct.SubSeconds) * 1000) / (predivSync + 1); + /* the subsecond register value is converted in millisec on 32bit */ + *subSeconds = (((predivSync - RTC_TimeStruct.SubSeconds + 1) & predivSync) + * 1000) / fqce_apre; } } #else @@ -789,7 +796,7 @@ 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) { - RTC_DateTypeDef RTC_DateStruct; + RTC_DateTypeDef RTC_DateStruct = {0}; /* in BIN mode, the date is not used */ if ((year != NULL) && (month != NULL) && (day != NULL) && (wday != NULL)) { HAL_RTC_GetDate(&RtcHandle, &RTC_DateStruct, RTC_FORMAT_BIN); @@ -815,6 +822,9 @@ 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) { +#if !defined(RTC_SSR_SS) + UNUSED(subSeconds); +#endif RTC_AlarmTypeDef RTC_AlarmStructure; /* Ignore time AM PM configuration if in 24 hours format */ @@ -842,17 +852,19 @@ 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; } - if (initMode == MODE_BINARY_MIX) { + /* + * The subsecond param is a nb of milliseconds to be converted in a subsecond + * downcounter value and to be comapred to the SubSecond register + */ + if ((initMode == MODE_BINARY_MIX) || (initMode == MODE_BINARY_NONE)) { /* the subsecond is the millisecond to be converted in a subsecond downcounter value */ - RTC_AlarmStructure.AlarmTime.SubSeconds = UINT32_MAX - ((subSeconds * fqce_apre) / 1000 + 1); + RTC_AlarmStructure.AlarmTime.SubSeconds = UINT32_MAX - (subSeconds * (predivSync + 1)) / 1000 + 1; } else { - RTC_AlarmStructure.AlarmTime.SubSeconds = predivSync - (subSeconds * (predivSync + 1)) / 1000; + RTC_AlarmStructure.AlarmTime.SubSeconds = predivSync - (subSeconds * (predivSync + 1)) / 1000 + 1; } } else { RTC_AlarmStructure.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL; } -#else - UNUSED(subSeconds); #endif /* RTC_SSR_SS */ if (period == HOUR_PM) { RTC_AlarmStructure.AlarmTime.TimeFormat = RTC_HOURFORMAT12_PM; @@ -882,7 +894,6 @@ void RTC_StartAlarm(alarm_t name, uint8_t day, uint8_t hours, uint8_t minutes, u } } #else - UNUSED(subSeconds); UNUSED(period); UNUSED(day); UNUSED(mask); @@ -892,42 +903,40 @@ void RTC_StartAlarm(alarm_t name, uint8_t day, uint8_t hours, uint8_t minutes, u 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) - { + else { + /* SS have to be managed*/ #if defined(RTC_ALRMASSR_SSCLR) - RTC_AlarmStructure.BinaryAutoClr = RTC_ALARMSUBSECONDBIN_AUTOCLR_NO; + RTC_AlarmStructure.BinaryAutoClr = RTC_ALARMSUBSECONDBIN_AUTOCLR_NO; #endif /* RTC_ALRMASSR_SSCLR */ - RTC_AlarmStructure.AlarmMask = RTC_ALARMMASK_ALL; - + 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.AlarmSubSecondMask = mask << RTC_ALRMBSSR_MASKSS_Pos; - } else + if (name == ALARM_B) { + /* Expecting RTC_ALARMSUBSECONDBINMASK_NONE for the subsecond mask on ALARM B */ + RTC_AlarmStructure.AlarmSubSecondMask = mask << RTC_ALRMBSSR_MASKSS_Pos; + } else #endif - { - /* Expecting RTC_ALARMSUBSECONDBINMASK_NONE for the subsecond mask on ALARM A */ - RTC_AlarmStructure.AlarmSubSecondMask = mask << RTC_ALRMASSR_MASKSS_Pos; - } + { + /* Expecting RTC_ALARMSUBSECONDBINMASK_NONE for the subsecond mask on ALARM A */ + RTC_AlarmStructure.AlarmSubSecondMask = mask << RTC_ALRMASSR_MASKSS_Pos; + } +#if defined(RTC_ICSR_BIN) + if ((initMode == MODE_BINARY_MIX) || (initMode == MODE_BINARY_ONLY)) { + /* We have an SubSecond alarm to set in RTC_BINARY_MIX or RTC_BINARY_ONLY mode */ /* 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; + RTC_AlarmStructure.AlarmTime.SubSeconds = UINT32_MAX - (subSeconds * (predivSync + 1)) / 1000; + } else +#endif /* RTC_ICSR_BIN */ + { + RTC_AlarmStructure.AlarmTime.SubSeconds = predivSync - subSeconds * (predivSync + 1) / 1000; } - -#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); HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn); } +#endif /* RTC_SSR_SS */ } /** @@ -1013,14 +1022,15 @@ 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) { - /* - * The subsecond is the bit SS[14:0] of the ALARM SSR register (not ALARMxINR) - * to be converted in milliseconds - */ + /* + * The subsecond is the bit SS[14:0] of the ALARM SSR register (not ALARMxINR) + * to be converted in milliseconds + */ + if ((initMode == MODE_BINARY_MIX) || (initMode == MODE_BINARY_ONLY)) { + /* read the ALARM SSR register on SS[14:0] bits --> 0x7FFF */ *subSeconds = (((0x7fff - RTC_AlarmStructure.AlarmTime.SubSeconds + 1) & 0x7fff) * 1000) / fqce_apre; } else { - *subSeconds = ((predivSync - RTC_AlarmStructure.AlarmTime.SubSeconds) * 1000) / (predivSync + 1); + *subSeconds = (((predivSync - RTC_AlarmStructure.AlarmTime.SubSeconds + 1) & predivSync) * 1000) / (predivSync + 1); } } #else From a1543ea1ae0d624b18921d27aaa5c264937a7876 Mon Sep 17 00:00:00 2001 From: Francois Ramu Date: Thu, 21 Sep 2023 16:22:51 +0200 Subject: [PATCH 5/8] feat: add new API to setAlarmTime with subsecond Add new API to set alarm Time with subseconds which is a nb of milliseconds. Similar to setAlarmSubSeconds. Signed-off-by: Francois Ramu --- README.md | 18 +++++++++++++++++ src/STM32RTC.cpp | 52 +++++++++++++++++++++++++++++++++++++----------- src/STM32RTC.h | 3 ++- 3 files changed, 60 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index c913529..be001ec 100644 --- a/README.md +++ b/README.md @@ -158,6 +158,24 @@ _Get the RTC handle_ * **`RTC_HandleTypeDef *RTC_GetHandle(void)`** +_binary and mix modes_ + +Some STM32 RTC have a binary mode with 32-bit free-running counter +in addition to their BCD mode for calendar (for example stm32wl55). +Combined with BCD this is the MIX mode. Only using the binary counter is the BIN mode. +Three RTC functional modes are available: + - `STM32RTC::MODE_BCD` + - `STM32RTC::MODE_MIX` + - `STM32RTC::MODE_BIN` + +* **`Binary_Mode getBinaryMode(void);`** +* **`void setBinaryMode(Binary_Mode mode);`** + + +Any API using the Subsecond parameter is expressed in milliseconds +whatever the RTC input clock. This parameter is [0..999] in MIX or BCD mode +and [0..0xFFFFFFFF] in BIN mode. In this configuration, time and date registers +are not used by the RTC. Refer to the Arduino RTC documentation for the other functions http://arduino.cc/en/Reference/RTC diff --git a/src/STM32RTC.cpp b/src/STM32RTC.cpp index a9b0ef3..20ea918 100644 --- a/src/STM32RTC.cpp +++ b/src/STM32RTC.cpp @@ -154,7 +154,13 @@ STM32RTC::Binary_Mode STM32RTC::getBinaryMode(void) */ void STM32RTC::setBinaryMode(Binary_Mode mode) { +#if defined(RTC_BINARY_NONE) _mode = mode; +#else +#warning "only BCD mode is supported" + UNUSED(mode); + _mode = MODE_BCD; +#endif /* RTC_BINARY_NONE */ } /** @@ -340,7 +346,7 @@ void STM32RTC::standbyMode(void) /** * @brief get RTC subseconds. - * @retval return the current subseconds from the RTC. + * @retval return the current milliseconds from the RTC. */ uint32_t STM32RTC::getSubSeconds(void) { @@ -388,7 +394,7 @@ uint8_t STM32RTC::getHours(AM_PM *period) * @param hours: pointer to the current hours * @param minutes: pointer to the current minutes * @param seconds: pointer to the current seconds - * @param subSeconds: pointer to the current subSeconds + * @param subSeconds: pointer to the current subSeconds (in milliseconds) * @param period: optional (default: nullptr) * pointer to the current hour period set in the RTC: AM or PM * @retval none @@ -835,20 +841,30 @@ 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 subseconds: 0-999 (in ms) or 32bit nb of milliseconds in BIN mode * @param name: optional (default: ALARM_A) * ALARM_A or ALARM_B if exists * @retval none */ void STM32RTC::setAlarmSubSeconds(uint32_t subSeconds, Alarm name) { - if (subSeconds < 1000) { +#ifndef RTC_ALARM_B + UNUSED(name); +#endif + if (_mode == MODE_BIN) { +#ifdef RTC_ALARM_B + if (name == ALARM_B) { + _alarmBSubSeconds = subSeconds; + } else +#endif + { + _alarmSubSeconds = subSeconds; + } + } else if (subSeconds < 1000) { #ifdef RTC_ALARM_B if (name == ALARM_B) { _alarmBSubSeconds = subSeconds; } else -#else - UNUSED(name); #endif { _alarmSubSeconds = subSeconds; @@ -931,7 +947,7 @@ void STM32RTC::setAlarmHours(uint8_t hours, AM_PM period, Alarm name) if (_format == HOUR_12) { _alarmBPeriod = period; } - } + } else #else UNUSED(name); #endif @@ -944,6 +960,18 @@ void STM32RTC::setAlarmHours(uint8_t hours, AM_PM period, Alarm name) } } + +/** + * @brief set RTC alarm time. + * @param subSeconds: 0-999 ms or 32bit nb of milliseconds in BIN mode + * @param name: ALARM_A or ALARM_B if exists + * @retval none + */ +void STM32RTC::setAlarmTime(uint32_t subSeconds, Alarm name) +{ + setAlarmTime(0, 0, 0, subSeconds, AM, name); +} + /** * @brief set RTC alarm time. * @param hours: 0-23 @@ -962,7 +990,7 @@ void STM32RTC::setAlarmTime(uint8_t hours, uint8_t minutes, uint8_t seconds, Ala * @param hours: 0-23 * @param minutes: 0-59 * @param seconds: 0-59 - * @param subSeconds: 0-999 + * @param subSeconds: 0-999 ms or 32bit nb of milliseconds in BIN mode * @param name: ALARM_A or ALARM_B if exists * @retval none */ @@ -973,10 +1001,10 @@ void STM32RTC::setAlarmTime(uint8_t hours, uint8_t minutes, uint8_t seconds, uin /** * @brief set RTC alarm time. - * @param hours: 0-23 - * @param minutes: 0-59 - * @param seconds: 0-59 - * @param subSeconds: 0-999 (optional) + * @param hours: 0-23 (not used in BIN mode) + * @param minutes: 0-59 (not used in BIN mode) + * @param seconds: 0-59 (not used in BIN mode) + * @param subSeconds: 0-999 ms (optional) or 32bit nb of milliseconds in BIN mode * @param period: hour format AM or PM (optional) * @param name: optional (default: ALARM_A) * ALARM_A or ALARM_B if exists diff --git a/src/STM32RTC.h b/src/STM32RTC.h index e4a8198..0854727 100644 --- a/src/STM32RTC.h +++ b/src/STM32RTC.h @@ -86,7 +86,7 @@ class STM32RTC { }; enum Binary_Mode : uint8_t { - MODE_BCD = MODE_BINARY_NONE, /* not used */ + MODE_BCD = MODE_BINARY_NONE, MODE_BIN = MODE_BINARY_ONLY, MODE_MIX = MODE_BINARY_MIX }; @@ -208,6 +208,7 @@ class STM32RTC { 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(uint32_t subSeconds, 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); From 2ae934d619261e829efee44376ad5616b8c0a6be Mon Sep 17 00:00:00 2001 From: Francois Ramu Date: Mon, 3 Jul 2023 09:35:00 +0200 Subject: [PATCH 6/8] chore(examples): RTC running in MIX or BCD mode with Alarm This examples is configuring the RTC in MIX (binary + calendar) or BCD and sets Alarm A & B (if exists) few ms after the current time. Signed-off-by: Francois Ramu --- examples/mixRTCAlarm/mixRTCAlarm.ino | 99 ++++++++++++++++++++++++++++ 1 file changed, 99 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..429181d --- /dev/null +++ b/examples/mixRTCAlarm/mixRTCAlarm.ino @@ -0,0 +1,99 @@ +/* + mode Mix RTC alarm + + This sketch shows how to configure the alarm A & B (if exists) + of the RTC in MIX or BCD (BINARY none) 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 RTC mode : STM32RTC::MODE_MIX or STM32RTC::MODE_BCD */ + rtc.setBinaryMode(STM32RTC::MODE_BCD); + + rtc.begin(true, STM32RTC::HOUR_24); + + rtc.setTime(hours, minutes, seconds); + rtc.setDate(day, month, year); + + /* wait for a while */ + delay(300); + + 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 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 600ms ms from now (keep timeout < 1000ms) */ + timeout = rtc.getSubSeconds() + 600; + + 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", 600, + rtc.getAlarmSubSeconds(STM32RTC::ALARM_B)); +#endif /* RTC_ALARM_B */ +} + +void loop() +{ + /* Just wait for Alarm */ +} + +void alarmAMatch(void *data) +{ + UNUSED(data); + rtc.disableAlarm(STM32RTC::ALARM_A); + Serial.printf("Alarm A Match at %02d:%02d:%02d\r\n", + rtc.getHours(), rtc.getMinutes(), rtc.getSeconds()); +} + +#ifdef RTC_ALARM_B +void alarmBMatch(void *data) +{ + UNUSED(data); + rtc.disableAlarm(STM32RTC::ALARM_B); + Serial.printf("Alarm B Match at %d ms\r\n", rtc.getSubSeconds()); +} +#endif /* RTC_ALARM_B */ From c9d149687bad8db4ec02bed456c8f78fc2bab035 Mon Sep 17 00:00:00 2001 From: Francois Ramu Date: Thu, 21 Sep 2023 09:59:49 +0200 Subject: [PATCH 7/8] chore(examples): RTC running in BINary mode with Alarm This examples is configuring the RTC in BIN mode (binary only) and sets Alarm A & B (if exists) a few ms after the current time. Signed-off-by: Francois Ramu --- .../bin_onlyRTCAlarm/bin_onlyRTCAlarm.ino | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 examples/bin_onlyRTCAlarm/bin_onlyRTCAlarm.ino diff --git a/examples/bin_onlyRTCAlarm/bin_onlyRTCAlarm.ino b/examples/bin_onlyRTCAlarm/bin_onlyRTCAlarm.ino new file mode 100644 index 0000000..e10d869 --- /dev/null +++ b/examples/bin_onlyRTCAlarm/bin_onlyRTCAlarm.ino @@ -0,0 +1,85 @@ +/* + mode BINary only RTC alarm + + This sketch shows how to configure the alarm A & B of the RTC in BIN mode + + Creation 12 Dec 2017 + by Wi6Labs + Modified 03 Jul 2020 + by Frederic Pillon for STMicroelectronics + Modified 03 sept 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(); + +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 RTC mode */ + rtc.setBinaryMode(STM32RTC::MODE_BIN); + + /* in BIN mode time and Date register are not used, only the subsecond register for milisseconds */ + rtc.begin(true, STM32RTC::HOUR_24); + + /* wait for a while */ + delay(300); + + /* subsecond expressed in milliseconds */ + Serial.printf("Start at %d ms \r\n", rtc.getSubSeconds()); + + /* Attach the callback function before enabling Interrupt */ + rtc.attachInterrupt(alarmAMatch); + + /* Program the AlarmA in 12 seconds */ + rtc.setAlarmTime(0, 0, 0, 12000); + rtc.enableAlarm(rtc.MATCH_SUBSEC); + Serial.printf("Set Alarm A in 12s (at %d ms)\r\n", rtc.getAlarmSubSeconds()); + +#ifdef RTC_ALARM_B + /* Program ALARM B in 600ms ms from now (keep timeout < 1000ms) */ + timeout = rtc.getSubSeconds() + 600; + + 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", 600, + rtc.getAlarmSubSeconds(STM32RTC::ALARM_B)); +#endif /* RTC_ALARM_B */ + +} + +void loop() +{ + +} + +void alarmAMatch(void *data) +{ + UNUSED(data); + rtc.disableAlarm(STM32RTC::ALARM_A); + Serial.printf("Alarm A Match at %d ms \r\n", rtc.getSubSeconds()); +} + +#ifdef RTC_ALARM_B +void alarmBMatch(void *data) +{ + UNUSED(data); + rtc.disableAlarm(STM32RTC::ALARM_B); /* Else it will trig again */ + Serial.printf("Alarm B Match at %d ms\r\n", rtc.getSubSeconds()); +} +#endif /* RTC_ALARM_B */ + From e466ec94fc89ec1455253f295616eb27d123e1fa Mon Sep 17 00:00:00 2001 From: Frederic Pillon Date: Mon, 25 Sep 2023 13:59:23 +0200 Subject: [PATCH 8/8] chore(keywords): add new api and types Signed-off-by: Frederic Pillon --- keywords.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/keywords.txt b/keywords.txt index 5663a9e..0c1af19 100644 --- a/keywords.txt +++ b/keywords.txt @@ -13,6 +13,10 @@ STM32RTC KEYWORD1 ####################################### getInstance KEYWORD2 +getHandle KEYWORD2 + +getBinaryMode KEYWORD2 +setBinaryMode KEYWORD2 getWeekDay KEYWORD2 getDay KEYWORD2 @@ -85,6 +89,7 @@ IS_HOUR_FORMAT KEYWORD2 # Constants (LITERAL1) ####################################### MATCH_OFF LITERAL1 +MATCH_SUBSEC LITERAL1 MATCH_SS LITERAL1 MATCH_MMSS LITERAL1 MATCH_HHMMSS LITERAL1 @@ -100,3 +105,6 @@ LSI_CLOCK LITERAL1 HSE_CLOCK LITERAL1 ALARM_A LITERAL1 ALARM_B LITERAL1 +MODE_BCD LITERAL1 +MODE_BIN LITERAL1 +MODE_MIX LITERAL1