diff --git a/README.md b/README.md index bc11b65..bbcc584 100644 --- a/README.md +++ b/README.md @@ -190,6 +190,12 @@ underflow. Used by STM32LoRaWAN. Refer to the Arduino RTC documentation for the other functions http://arduino.cc/en/Reference/RTC +### Since STM32RTC version higher than 1.4.0 +_IsFormat_24Hour_ + +Returns True if the current Hour Format is HOUR_24 else false if Hour format is HOUR_12 + + ## Source Source files available at: diff --git a/examples/changeMode/changeMode.ino b/examples/changeMode/changeMode.ino new file mode 100644 index 0000000..2d85bd2 --- /dev/null +++ b/examples/changeMode/changeMode.ino @@ -0,0 +1,82 @@ +/* + change RTC mode from BCD --> MIX --> BIN + + This sketch shows that changing the RTC mode does not affect the calendar + Configure the RTC, set an alarm, on alarm match change the RTC mode. + + Creation 01 march 2024 + 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(); +bool rtc_mode_changed; + +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_BCD); + rtc_mode_changed = true; + + /* RTC mode is in BCD mode by default */ + rtc.begin(true, STM32RTC::HOUR_24); + + /* wait for a while */ + delay(500); + + // Set the calendar + rtc.setDate(01, 03, 24); + rtc.setTime(14, 02, 58); + + // Program ALARM A to change the RTC mode (set then enable) + rtc.attachInterrupt(alarmMatch); + rtc.setAlarmSeconds(5, STM32RTC::ALARM_A); + rtc.enableAlarm(rtc.MATCH_SS, STM32RTC::ALARM_A); +} + +void loop() +{ + /* Reports the RTC mode when it has been changed */ + if (rtc_mode_changed) { + if (rtc.getBinaryMode() == STM32RTC::MODE_BCD) { + Serial.printf("RTC mode is MODE_BCD at "); + } else if (rtc.getBinaryMode() == STM32RTC::MODE_MIX) { + Serial.printf("RTC mode is MODE_MIX at "); + } else { + Serial.printf("RTC mode is MODE_BIN at "); + } + + Serial.printf("%02d/%02d/%02d - ", rtc.getDay(), rtc.getMonth(), rtc.getYear()); + Serial.printf("%02d:%02d:%02d \n", rtc.getHours(), rtc.getMinutes(), rtc.getSeconds()); + + rtc_mode_changed = false; + } +} + +void alarmMatch(void *data) +{ + UNUSED(data); + Serial.printf("Alarm A Match! : change RTC mode at "); + Serial.printf("%02d:%02d:%02d \n", rtc.getHours(), rtc.getMinutes(), rtc.getSeconds()); + + /* Configure the new RTC mode */ + if (rtc.getBinaryMode() == STM32RTC::MODE_BCD) { + rtc.setBinaryMode(STM32RTC::MODE_MIX); + } else if (rtc.getBinaryMode() == STM32RTC::MODE_MIX) { + rtc.setBinaryMode(STM32RTC::MODE_BIN); + } else { + rtc.setBinaryMode(STM32RTC::MODE_BCD); + } + rtc_mode_changed = true; +} diff --git a/library.json b/library.json index 5c570df..c485639 100644 --- a/library.json +++ b/library.json @@ -7,7 +7,7 @@ "type": "git", "url": "https://github.com/stm32duino/STM32RTC" }, - "version": "1.4.0", + "version": "1.5.0", "frameworks": "arduino", "platforms": "ststm32", "build": { diff --git a/library.properties b/library.properties index 4666e3a..814896b 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=STM32duino RTC -version=1.4.0 +version=1.5.0 author=STMicroelectronics maintainer=stm32duino sentence=Allows to use the RTC functionalities of STM32 based boards. diff --git a/src/STM32RTC.h b/src/STM32RTC.h index fe0374c..e7c9611 100644 --- a/src/STM32RTC.h +++ b/src/STM32RTC.h @@ -240,6 +240,10 @@ class STM32RTC { { return RTC_IsConfigured(); } + bool isFormat_24hour(void) + { + return (_format == HOUR_24); + } bool isAlarmEnabled(Alarm name = ALARM_A); bool isTimeSet(void) { diff --git a/src/rtc.c b/src/rtc.c index 73c911f..f3e0bca 100644 --- a/src/rtc.c +++ b/src/rtc.c @@ -93,6 +93,7 @@ static void RTC_computePrediv(uint32_t *asynch, uint32_t *synch); #endif /* !STM32F1xx */ #if defined(RTC_BINARY_NONE) static void RTC_BinaryConf(binaryMode_t mode); +static void RTC_SetBinaryConf(void); #endif static inline int _log2(int x) @@ -383,6 +384,34 @@ static void RTC_BinaryConf(binaryMode_t mode) } } } + +/* +* Function to check if the RTC ICSR register must be updated with new bit +* field BIN and BCDU. To be called just after the RTC_BinaryConf +* Map the LL RTC bin mode to the corresponding RtcHandle.Init.BinMode values +* assuming the LL_RTC_BINARY_xxx is identical to RTC_BINARY_xxx (RTC_ICSR_BIN_xxx) +* Idem for the LL_RTC_BINARY_MIX_BCDU_n and RTC_BINARY_MIX_BCDU_n +*/ +#if (RTC_BINARY_MIX != LL_RTC_BINARY_MIX) +#error "RTC_BINARY_MIX and LL_RTC_BINARY_MIX do not match" +#endif +#if (RTC_BINARY_MIX_BCDU_7 != LL_RTC_BINARY_MIX_BCDU_7) +#error "RTC_BINARY_MIX_BCDU_n and LL_RTC_BINARY_MIX_BCDU_n do not match" +#endif +static void RTC_SetBinaryConf(void) +{ + if (LL_RTC_GetBinaryMode(RtcHandle.Instance) != RtcHandle.Init.BinMode) { + LL_RTC_DisableWriteProtection(RtcHandle.Instance); + LL_RTC_EnableInitMode(RtcHandle.Instance); + + LL_RTC_SetBinaryMode(RtcHandle.Instance, RtcHandle.Init.BinMode); + if (RtcHandle.Init.BinMode == RTC_BINARY_MIX) { + LL_RTC_SetBinMixBCDU(RtcHandle.Instance, RtcHandle.Init.BinMixBcdU); + } + LL_RTC_ExitInitMode(RtcHandle.Instance); + LL_RTC_EnableWriteProtection(RtcHandle.Instance); + } +} #endif /* RTC_BINARY_NONE */ /** @@ -441,8 +470,9 @@ bool RTC_init(hourFormat_t format, binaryMode_t mode, sourceClock_t source, bool #ifdef __HAL_RCC_RTCAPB_CLK_ENABLE __HAL_RCC_RTCAPB_CLK_ENABLE(); #endif +#ifdef __HAL_RCC_RTC_ENABLE __HAL_RCC_RTC_ENABLE(); - +#endif isAlarmASet = RTC_IsAlarmSet(ALARM_A); #ifdef RTC_ALARM_B isAlarmBSet = RTC_IsAlarmSet(ALARM_B); @@ -462,6 +492,10 @@ bool RTC_init(hourFormat_t format, binaryMode_t mode, sourceClock_t source, bool RTC_initClock(source); RTC_getPrediv(&(RtcHandle.Init.AsynchPrediv), &(RtcHandle.Init.SynchPrediv)); #if defined(RTC_BINARY_NONE) + /* + * If RTC BIN mode changed, calling the HAL_RTC_Init will + * force the update of the BIN register in the RTC_ICSR + */ RTC_BinaryConf(mode); #endif /* RTC_BINARY_NONE */ #endif // STM32F1xx @@ -526,11 +560,11 @@ bool RTC_init(hourFormat_t format, binaryMode_t mode, sourceClock_t source, bool #else RTC_getPrediv(&(RtcHandle.Init.AsynchPrediv), &(RtcHandle.Init.SynchPrediv)); #endif +#if defined(RTC_BINARY_NONE) /* - * TODO: RTC is already initialized, but RTC BIN mode is changed + * If RTC BIN mode changed, calling the HAL_RTC_Init will * force the update of the BIN register in the RTC_ICSR */ -#if defined(RTC_BINARY_NONE) RTC_BinaryConf(mode); #endif /* RTC_BINARY_NONE */ HAL_RTC_Init(&RtcHandle); @@ -557,6 +591,13 @@ bool RTC_init(hourFormat_t format, binaryMode_t mode, sourceClock_t source, bool #endif #if defined(RTC_BINARY_NONE) RTC_BinaryConf(mode); + /* + * RTC is already initialized, but RTC BIN mode is changed : + * force the update of the BIN register and BCDu in the RTC_ICSR + * by the RTC_SetBinaryConf function + */ + RTC_SetBinaryConf(); + #endif /* RTC_BINARY_NONE */ #if defined(STM32F1xx) memcpy(&RtcHandle.DateToUpdate, &BackupDate, 4); @@ -591,7 +632,9 @@ void RTC_DeInit(bool reset_cb) { HAL_RTC_DeInit(&RtcHandle); /* Peripheral clock disable */ +#ifdef __HAL_RCC_RTC_DISABLE __HAL_RCC_RTC_DISABLE(); +#endif #ifdef __HAL_RCC_RTCAPB_CLK_DISABLE __HAL_RCC_RTCAPB_CLK_DISABLE(); #endif @@ -786,19 +829,20 @@ 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 + * @brief Set RTC alarm and activate it with IT mode with 64bit accuracy on subsecond param + * Mainly used by Lorawan in RTC BIN or MIX 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 * @param seconds: 0-59 - * @param subSeconds: 0-999 milliseconds + * @param subSeconds: 0-999 milliseconds or 64bit nb of milliseconds in no BCD mode * @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. * @retval None */ -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_StartAlarm64(alarm_t name, uint8_t day, uint8_t hours, uint8_t minutes, uint8_t seconds, uint64_t subSeconds, hourAM_PM_t period, uint8_t mask) { #if !defined(RTC_SSR_SS) UNUSED(subSeconds); @@ -836,9 +880,9 @@ void RTC_StartAlarm(alarm_t name, uint8_t day, uint8_t hours, uint8_t minutes, u */ if ((initMode == MODE_BINARY_ONLY) || (initMode == MODE_BINARY_MIX)) { /* the subsecond is the millisecond to be converted in a subsecond downcounter value */ - RTC_AlarmStructure.AlarmTime.SubSeconds = UINT32_MAX - (subSeconds * (predivSync + 1)) / 1000; + RTC_AlarmStructure.AlarmTime.SubSeconds = UINT32_MAX - ((uint32_t)subSeconds * (predivSync + 1)) / 1000; } else { - RTC_AlarmStructure.AlarmTime.SubSeconds = predivSync - (subSeconds * (predivSync + 1)) / 1000; + RTC_AlarmStructure.AlarmTime.SubSeconds = predivSync - ((uint32_t)subSeconds * (predivSync + 1)) / 1000; } } else { RTC_AlarmStructure.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL; @@ -902,8 +946,15 @@ void RTC_StartAlarm(alarm_t name, uint8_t day, uint8_t hours, uint8_t minutes, u #if defined(RTC_ICSR_BIN) if ((initMode == MODE_BINARY_ONLY) || (initMode == MODE_BINARY_MIX)) { /* 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 * (predivSync + 1)) / 1000; + /* The subsecond in ms is converted in ticks unit 1 tick is 1000 / fqce_apre + * It keeps the subsecond accuracy on 64 bits if needed + */ + if (subSeconds > (uint64_t)UINT32_MAX) { + uint64_t tmp = (subSeconds * (uint64_t)(predivSync + 1)) / (uint64_t)1000; + RTC_AlarmStructure.AlarmTime.SubSeconds = (uint32_t)UINT32_MAX - (uint32_t)tmp; + } else { + RTC_AlarmStructure.AlarmTime.SubSeconds = (uint32_t)((uint32_t)UINT32_MAX - (uint32_t)(subSeconds * (predivSync + 1)) / 1000); + } } else #endif /* RTC_ICSR_BIN */ { @@ -917,6 +968,25 @@ void RTC_StartAlarm(alarm_t name, uint8_t day, uint8_t hours, uint8_t minutes, u #endif /* RTC_SSR_SS */ } +/** + * @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 + * @param seconds: 0-59 + * @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. + * @retval None + */ +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) +{ + /* Same RTC_StartAlarm where the nb of SubSeconds is lower than UINT32_MAX */ + RTC_StartAlarm64(name, day, hours, minutes, seconds, (uint64_t)subSeconds, period, mask); +} + /** * @brief Disable RTC alarm * @param name: ALARM_A or ALARM_B if exists @@ -1124,7 +1194,8 @@ void RTC_Alarm_IRQHandler(void) #if defined(STM32F071xB) || defined(STM32F072xB) || defined(STM32F078xx) || \ defined(STM32F091xC) || defined(STM32F098xx) || defined(STM32F070xB) || \ defined(STM32F030xC) || defined(STM32G0xx) || defined(STM32H5xx) || \ - defined(STM32L0xx) || defined(STM32L5xx) || defined(STM32U5xx) + defined(STM32L0xx) || defined(STM32L5xx) || defined(STM32U5xx) || \ + defined(STM32WBAxx) // In some cases, the same vector is used to manage WakeupTimer, // but with a dedicated HAL IRQHandler HAL_RTCEx_WakeUpTimerIRQHandler(&RtcHandle); diff --git a/src/rtc.h b/src/rtc.h index 8d0bcd8..80319e4 100644 --- a/src/rtc.h +++ b/src/rtc.h @@ -129,7 +129,8 @@ typedef void(*voidCallbackPtr)(void *); #endif /* !STM32F1xx */ #if defined(STM32C0xx) || defined(STM32F0xx) || defined(STM32H5xx) || \ - defined(STM32L0xx) || defined(STM32L5xx) || defined(STM32U5xx) + defined(STM32L0xx) || defined(STM32L5xx) || defined(STM32U5xx) || \ + defined(STM32WBAxx) #define RTC_Alarm_IRQn RTC_IRQn #define RTC_Alarm_IRQHandler RTC_IRQHandler #endif @@ -141,7 +142,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(STM32H5xx) || defined(STM32L0xx) || defined(STM32L5xx) || \ - defined(STM32U5xx) + defined(STM32U5xx) || defined(STM32WBAxx) // specific WakeUp interrupt #define ONESECOND_IRQn RTC_IRQn #elif defined(STM32MP1xx) @@ -193,6 +194,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); 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_StartAlarm64(alarm_t name, uint8_t day, uint8_t hours, uint8_t minutes, uint8_t seconds, uint64_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);