diff --git a/boards/lora-e5-dev/Makefile.features b/boards/lora-e5-dev/Makefile.features index 424bffac743f..905c9845cf3c 100644 --- a/boards/lora-e5-dev/Makefile.features +++ b/boards/lora-e5-dev/Makefile.features @@ -5,6 +5,7 @@ CPU_MODEL = stm32wle5jc FEATURES_PROVIDED += periph_adc FEATURES_PROVIDED += periph_i2c FEATURES_PROVIDED += periph_lpuart +FEATURES_PROVIDED += periph_rtc FEATURES_PROVIDED += periph_rtt FEATURES_PROVIDED += periph_spi FEATURES_PROVIDED += periph_timer diff --git a/boards/nucleo-wl55jc/Makefile.features b/boards/nucleo-wl55jc/Makefile.features index df235b4039c8..69aaeb97afad 100644 --- a/boards/nucleo-wl55jc/Makefile.features +++ b/boards/nucleo-wl55jc/Makefile.features @@ -5,6 +5,7 @@ CPU_MODEL = stm32wl55jc FEATURES_PROVIDED += periph_adc FEATURES_PROVIDED += periph_i2c FEATURES_PROVIDED += periph_lpuart +FEATURES_PROVIDED += periph_rtc FEATURES_PROVIDED += periph_rtt FEATURES_PROVIDED += periph_spi FEATURES_PROVIDED += periph_timer diff --git a/cpu/stm32/Makefile.features b/cpu/stm32/Makefile.features index f4db2fe0cb65..5996871677e6 100644 --- a/cpu/stm32/Makefile.features +++ b/cpu/stm32/Makefile.features @@ -35,7 +35,8 @@ ifneq (,$(filter $(CPU_FAM),c0 f0 f1 f3 g0 g4 l0 l1 l4 l5 u5 wb wl)) endif ifneq (,$(filter $(CPU_FAM),f0 f2 f3 f4 f7 l0 l1 l4 l5 u5 wb wl)) - CPU_MODELS_WITHOUT_RTC_BKPR += stm32f030% stm32f070% + CPU_MODELS_WITHOUT_RTC_BKPR += stm32f030% stm32f070% \ + stm32f302% ifeq (,$(filter $(CPU_MODELS_WITHOUT_RTC_BKPR),$(CPU_MODEL))) FEATURES_PROVIDED += periph_rtc_mem endif diff --git a/cpu/stm32/periph/rtc_all.c b/cpu/stm32/periph/rtc_all.c index c77d20f64ede..e7d8fa59d70e 100644 --- a/cpu/stm32/periph/rtc_all.c +++ b/cpu/stm32/periph/rtc_all.c @@ -26,109 +26,112 @@ /* map some CPU specific register names */ #if defined (CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L1) -#define EN_REG (RCC->CSR) -#define EN_BIT (RCC_CSR_RTCEN) -#define CLKSEL_MASK (RCC_CSR_RTCSEL) -#define CLKSEL_LSE (RCC_CSR_RTCSEL_LSE) -#define CLKSEL_LSI (RCC_CSR_RTCSEL_LSI) +# define EN_REG (RCC->CSR) +# define EN_BIT (RCC_CSR_RTCEN) +# define CLKSEL_MASK (RCC_CSR_RTCSEL) +# define CLKSEL_LSE (RCC_CSR_RTCSEL_LSE) +# define CLKSEL_LSI (RCC_CSR_RTCSEL_LSI) #else -#define EN_REG (RCC->BDCR) -#define EN_BIT (RCC_BDCR_RTCEN) -#define CLKSEL_MASK (RCC_BDCR_RTCSEL_0 | RCC_BDCR_RTCSEL_1) -#define CLKSEL_LSE (RCC_BDCR_RTCSEL_0) -#define CLKSEL_LSI (RCC_BDCR_RTCSEL_1) +# define EN_REG (RCC->BDCR) +# define EN_BIT (RCC_BDCR_RTCEN) +# define CLKSEL_MASK (RCC_BDCR_RTCSEL_0 | RCC_BDCR_RTCSEL_1) +# define CLKSEL_LSE (RCC_BDCR_RTCSEL_0) +# define CLKSEL_LSI (RCC_BDCR_RTCSEL_1) #endif /* map some EXTI register names */ #if defined(CPU_FAM_STM32L4) || defined(CPU_FAM_STM32WB) || \ defined(CPU_FAM_STM32G4) -#define EXTI_REG_RTSR (EXTI->RTSR1) -#define EXTI_REG_FTSR (EXTI->FTSR1) -#define EXTI_REG_PR (EXTI->PR1) -#define EXTI_REG_IMR (EXTI->IMR1) -#elif defined(CPU_FAM_STM32G0) || defined(CPU_FAM_STM32U5) -#define EXTI_REG_RTSR (EXTI->RTSR1) -#define EXTI_REG_FTSR (EXTI->FTSR1) -#define EXTI_REG_PR (EXTI->RPR1) -#define EXTI_REG_IMR (EXTI->IMR1) +# define EXTI_REG_RTSR (EXTI->RTSR1) +# define EXTI_REG_FTSR (EXTI->FTSR1) +# define EXTI_REG_PR (EXTI->PR1) +# define EXTI_REG_IMR (EXTI->IMR1) +#elif defined(CPU_FAM_STM32G0) || defined(CPU_FAM_STM32U5) || defined(CPU_FAM_STM32WL) +# define EXTI_REG_RTSR (EXTI->RTSR1) +# define EXTI_REG_FTSR (EXTI->FTSR1) +# define EXTI_REG_PR (EXTI->RPR1) +# define EXTI_REG_IMR (EXTI->IMR1) #elif defined(CPU_FAM_STM32L5) -#define EXTI_REG_IMR (EXTI->IMR1) +# define EXTI_REG_IMR (EXTI->IMR1) #else -#define EXTI_REG_RTSR (EXTI->RTSR) -#define EXTI_REG_FTSR (EXTI->FTSR) -#define EXTI_REG_PR (EXTI->PR) -#define EXTI_REG_IMR (EXTI->IMR) +# define EXTI_REG_RTSR (EXTI->RTSR) +# define EXTI_REG_FTSR (EXTI->FTSR) +# define EXTI_REG_PR (EXTI->PR) +# define EXTI_REG_IMR (EXTI->IMR) #endif /* map some RTC register names and bitfield */ #if defined(CPU_FAM_STM32G4) || defined(CPU_FAM_STM32G0) -#define RTC_REG_ISR RTC->ICSR -#define RTC_REG_SR RTC->SR -#define RTC_REG_SCR RTC->SCR -#define RTC_ISR_RSF RTC_ICSR_RSF -#define RTC_ISR_INIT RTC_ICSR_INIT -#define RTC_ISR_INITF RTC_ICSR_INITF -#define RTC_ISR_ALRAWF RTC_ICSR_ALRAWF -#define RTC_ISR_ALRAF RTC_SR_ALRAF -#elif defined(CPU_FAM_STM32L5) -#define RTC_REG_ISR RTC->ICSR -#define RTC_REG_SR RTC->SR -#define RTC_REG_SCR RTC->SCR -#define RTC_ISR_RSF RTC_ICSR_RSF -#define RTC_ISR_INIT RTC_ICSR_INIT -#define RTC_ISR_INITF RTC_ICSR_INITF +# define RTC_REG_ISR RTC->ICSR +# define RTC_REG_SR RTC->SR +# define RTC_REG_SCR RTC->SCR +# define RTC_ISR_RSF RTC_ICSR_RSF +# define RTC_ISR_INIT RTC_ICSR_INIT +# define RTC_ISR_INITF RTC_ICSR_INITF +# define RTC_ISR_INITS RTC_ICSR_INITS +# define RTC_ISR_ALRAWF RTC_ICSR_ALRAWF +# define RTC_ISR_ALRAF RTC_SR_ALRAF +#elif defined(CPU_FAM_STM32L5) || defined(CPU_FAM_STM32WL) +# define RTC_REG_ISR RTC->ICSR +# define RTC_REG_SR RTC->SR +# define RTC_REG_SCR RTC->SCR +# define RTC_ISR_RSF RTC_ICSR_RSF +# define RTC_ISR_INIT RTC_ICSR_INIT +# define RTC_ISR_INITF RTC_ICSR_INITF +# define RTC_ISR_INITS RTC_ICSR_INITS #elif defined(CPU_FAM_STM32U5) -#define RTC_REG_ISR RTC->ICSR -#define RTC_REG_SR RTC->SR -#define RTC_REG_SCR RTC->SCR -#define RTC_ISR_RSF RTC_ICSR_RSF -#define RTC_ISR_INIT RTC_ICSR_INIT -#define RTC_ISR_INITF RTC_ICSR_INITF -#define RTC_ISR_ALRAF RTC_SR_ALRAF +# define RTC_REG_ISR RTC->ICSR +# define RTC_REG_SR RTC->SR +# define RTC_REG_SCR RTC->SCR +# define RTC_ISR_RSF RTC_ICSR_RSF +# define RTC_ISR_INIT RTC_ICSR_INIT +# define RTC_ISR_INITF RTC_ICSR_INITF +# define RTC_ISR_INITS RTC_ICSR_INITS +# define RTC_ISR_ALRAF RTC_SR_ALRAF #else -#define RTC_REG_ISR RTC->ISR +# define RTC_REG_ISR RTC->ISR #endif /* interrupt line name mapping */ #if defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32L0) || \ defined(CPU_FAM_STM32L5) || defined(CPU_FAM_STM32U5) -#define IRQN (RTC_IRQn) -#define ISR_NAME isr_rtc +# define IRQN (RTC_IRQn) +# define ISR_NAME isr_rtc #elif defined(CPU_FAM_STM32G0) -#define IRQN (RTC_TAMP_IRQn) -#define ISR_NAME isr_rtc_tamp +# define IRQN (RTC_TAMP_IRQn) +# define ISR_NAME isr_rtc_tamp #else -#define IRQN (RTC_Alarm_IRQn) -#define ISR_NAME isr_rtc_alarm +# define IRQN (RTC_Alarm_IRQn) +# define ISR_NAME isr_rtc_alarm #endif /* EXTI bitfield mapping */ #if defined(CPU_FAM_STM32L4) -#define EXTI_IMR_BIT (EXTI_IMR1_IM18) -#define EXTI_FTSR_BIT (EXTI_FTSR1_FT18) -#define EXTI_RTSR_BIT (EXTI_RTSR1_RT18) -#define EXTI_PR_BIT (EXTI_PR1_PIF18) -#elif defined(CPU_FAM_STM32L5) -#define EXTI_IMR_BIT (EXTI_IMR1_IM17) +# define EXTI_IMR_BIT (EXTI_IMR1_IM18) +# define EXTI_FTSR_BIT (EXTI_FTSR1_FT18) +# define EXTI_RTSR_BIT (EXTI_RTSR1_RT18) +# define EXTI_PR_BIT (EXTI_PR1_PIF18) +#elif defined(CPU_FAM_STM32L5) || defined(CPU_FAM_STM32WL) +# define EXTI_IMR_BIT (EXTI_IMR1_IM17) #elif defined(CPU_FAM_STM32WB) || defined(CPU_FAM_STM32G4) -#define EXTI_IMR_BIT (EXTI_IMR1_IM17) -#define EXTI_FTSR_BIT (EXTI_FTSR1_FT17) -#define EXTI_RTSR_BIT (EXTI_RTSR1_RT17) -#define EXTI_PR_BIT (EXTI_PR1_PIF17) +# define EXTI_IMR_BIT (EXTI_IMR1_IM17) +# define EXTI_FTSR_BIT (EXTI_FTSR1_FT17) +# define EXTI_RTSR_BIT (EXTI_RTSR1_RT17) +# define EXTI_PR_BIT (EXTI_PR1_PIF17) #elif defined(CPU_FAM_STM32G0) || defined(CPU_FAM_STM32U5) -#define EXTI_IMR_BIT (EXTI_IMR1_IM11) -#define EXTI_FTSR_BIT (EXTI_FTSR1_FT11) -#define EXTI_RTSR_BIT (EXTI_RTSR1_RT11) -#define EXTI_PR_BIT (EXTI_RPR1_RPIF11) +# define EXTI_IMR_BIT (EXTI_IMR1_IM11) +# define EXTI_FTSR_BIT (EXTI_FTSR1_FT11) +# define EXTI_RTSR_BIT (EXTI_RTSR1_RT11) +# define EXTI_PR_BIT (EXTI_RPR1_RPIF11) #else -#if defined(CPU_FAM_STM32L0) -#define EXTI_IMR_BIT (EXTI_IMR_IM17) -#else -#define EXTI_IMR_BIT (EXTI_IMR_MR17) -#endif -#define EXTI_FTSR_BIT (EXTI_FTSR_TR17) -#define EXTI_RTSR_BIT (EXTI_RTSR_TR17) -#define EXTI_PR_BIT (EXTI_PR_PR17) +# if defined(CPU_FAM_STM32L0) +# define EXTI_IMR_BIT (EXTI_IMR_IM17) +# else +# define EXTI_IMR_BIT (EXTI_IMR_MR17) +# endif +# define EXTI_FTSR_BIT (EXTI_FTSR_TR17) +# define EXTI_RTSR_BIT (EXTI_RTSR_TR17) +# define EXTI_PR_BIT (EXTI_PR_PR17) #endif /* write protection values */ @@ -147,62 +150,62 @@ #define ALRM_M_MASK (RTC_ALRMAR_MNU | RTC_ALRMAR_MNT) #define ALRM_S_MASK (RTC_ALRMAR_SU | RTC_ALRMAR_ST) #ifndef RTC_DR_YU_Pos -#define RTC_DR_YU_Pos (16U) +# define RTC_DR_YU_Pos (16U) #endif #ifndef RTC_DR_MU_Pos -#define RTC_DR_MU_Pos (8U) +# define RTC_DR_MU_Pos (8U) #endif #ifndef RTC_DR_DU_Pos -#define RTC_DR_DU_Pos (0U) +# define RTC_DR_DU_Pos (0U) #endif #ifndef RTC_TR_HU_Pos -#define RTC_TR_HU_Pos (16U) +# define RTC_TR_HU_Pos (16U) #endif #ifndef RTC_TR_MNU_Pos -#define RTC_TR_MNU_Pos (8U) +# define RTC_TR_MNU_Pos (8U) #endif #ifndef RTC_TR_SU_Pos -#define RTC_TR_SU_Pos (0U) +# define RTC_TR_SU_Pos (0U) #endif #ifndef RTC_ALRMAR_DU_Pos -#define RTC_ALRMAR_DU_Pos (24U) +# define RTC_ALRMAR_DU_Pos (24U) #endif #ifndef RTC_ALRMAR_HU_Pos -#define RTC_ALRMAR_HU_Pos (16U) +# define RTC_ALRMAR_HU_Pos (16U) #endif #ifndef RTC_ALRMAR_MNU_Pos -#define RTC_ALRMAR_MNU_Pos (8U) +# define RTC_ALRMAR_MNU_Pos (8U) #endif #ifndef RTC_ALRMAR_SU_Pos -#define RTC_ALRMAR_SU_Pos (0U) +# define RTC_ALRMAR_SU_Pos (0U) #endif /* figure out sync and async prescaler */ #if IS_ACTIVE(CONFIG_BOARD_HAS_LSE) -#define PRE_SYNC (255) -#define PRE_ASYNC (127) +# define PRE_SYNC (255) +# define PRE_ASYNC (127) #elif (CLOCK_LSI == 40000) -#define PRE_SYNC (319) -#define PRE_ASYNC (124) +# define PRE_SYNC (319) +# define PRE_ASYNC (124) #elif (CLOCK_LSI == 37000) -#define PRE_SYNC (295) -#define PRE_ASYNC (124) +# define PRE_SYNC (295) +# define PRE_ASYNC (124) #elif (CLOCK_LSI == 32000) -#define PRE_SYNC (249) -#define PRE_ASYNC (127) +# define PRE_SYNC (249) +# define PRE_ASYNC (127) #else -#error "rtc: unable to determine RTC SYNC and ASYNC prescalers from LSI value" +# error "RTC: unable to determine RTC SYNC and ASYNC prescalers from LSI value" #endif /* struct tm counts years since 1900 but RTC has only two-digit year, hence the offset */ #define YEAR_OFFSET (RIOT_EPOCH - 1900) /* Use a magic number to determine the initial RTC source. This will be used - to know if a reset of the RTC is required at initialization. */ + * to know if a reset of the RTC is required at initialization. */ #if IS_ACTIVE(CONFIG_BOARD_HAS_LSE) -#define MAGIC_CLCK_NUMBER (0x1970) +# define MAGIC_CLCK_NUMBER (0x1970) #else -#define MAGIC_CLCK_NUMBER (0x1971) +# define MAGIC_CLCK_NUMBER (0x1971) #endif static struct { @@ -228,6 +231,14 @@ static int bcd2val(uint32_t val, int shift, uint32_t mask) return (((tmp >> 4) * 10) + (tmp & 0x0f)); } +void rtc_lock(void) +{ + /* lock RTC device */ + RTC->WPR = 0xff; + /* disable backup clock domain */ + stmclk_dbp_lock(); +} + void rtc_unlock(void) { /* enable backup clock domain */ @@ -235,20 +246,22 @@ void rtc_unlock(void) /* unlock RTC */ RTC->WPR = WPK1; RTC->WPR = WPK2; +} + +static inline void rtc_enter_init_mode(void) +{ + rtc_unlock(); /* enter RTC init mode */ RTC_REG_ISR |= RTC_ISR_INIT; while (!(RTC_REG_ISR & RTC_ISR_INITF)) {} } -void rtc_lock(void) +static inline void rtc_exit_init_mode(void) { /* exit RTC init mode */ RTC_REG_ISR &= ~RTC_ISR_INIT; while (RTC_REG_ISR & RTC_ISR_INITF) {} - /* lock RTC device */ - RTC->WPR = 0xff; - /* disable backup clock domain */ - stmclk_dbp_lock(); + rtc_lock(); } void rtc_init(void) @@ -256,47 +269,53 @@ void rtc_init(void) stmclk_dbp_unlock(); #if defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L1) /* Compare the stored magic number with the current one. If it's different - it means the clock source has changed and thus a RTC reset is - required. */ + * it means the clock source has changed and thus a RTC reset is + * required. */ if (RTC->BKP0R != MAGIC_CLCK_NUMBER) { RCC->CSR |= RCC_CSR_RTCRST; RCC->CSR &= ~RCC_CSR_RTCRST; RTC->BKP0R = MAGIC_CLCK_NUMBER; /* Store the new magic number */ } #endif - stmclk_dbp_lock(); - /* enable low frequency clock */ stmclk_enable_lfclk(); /* select input clock and enable the RTC */ stmclk_dbp_unlock(); -#if defined(CPU_FAM_STM32L5) +#if defined(CPU_FAM_STM32L5) || defined(CPU_FAM_STM32WL) periph_clk_en(APB1, RCC_APB1ENR1_RTCAPBEN); #elif defined(CPU_FAM_STM32G0) periph_clk_en(APB1, RCC_APBENR1_RTCAPBEN); #elif defined(CPU_FAM_STM32U5) periph_clk_en(APB3, RCC_APB3ENR_RTCAPBEN); #endif - EN_REG &= ~(CLKSEL_MASK); + #if IS_ACTIVE(CONFIG_BOARD_HAS_LSE) - EN_REG |= (CLKSEL_LSE | EN_BIT); + if ((EN_REG & (CLKSEL_MASK | EN_BIT)) != (CLKSEL_LSE | EN_BIT)) { + EN_REG &= ~(CLKSEL_MASK); + EN_REG |= (CLKSEL_LSE | EN_BIT); + } #else - EN_REG |= (CLKSEL_LSI | EN_BIT); + if ((EN_REG & (CLKSEL_MASK | EN_BIT)) != (CLKSEL_LSI | EN_BIT)) { + EN_REG &= ~(CLKSEL_MASK); + EN_REG |= (CLKSEL_LSI | EN_BIT); + } #endif - rtc_unlock(); - /* reset configuration */ - RTC->CR = 0; - RTC_REG_ISR = RTC_ISR_INIT; - /* configure prescaler (RTC PRER) */ - RTC->PRER = (PRE_SYNC | (PRE_ASYNC << 16)); - rtc_lock(); + if (!(RTC_REG_ISR & RTC_ISR_INITS)) + { + rtc_enter_init_mode(); + /* reset configuration */ + RTC->CR = 0; + /* configure prescaler (RTC PRER) */ + RTC->PRER = (PRE_SYNC | (PRE_ASYNC << 16)); + rtc_exit_init_mode(); + } /* configure the EXTI channel, as RTC interrupts are routed through it. * Needs to be configured to trigger on rising edges. */ EXTI_REG_IMR |= EXTI_IMR_BIT; -#if !defined(CPU_FAM_STM32L5) +#if !(defined(CPU_FAM_STM32L5) || defined(CPU_FAM_STM32WL)) EXTI_REG_FTSR &= ~(EXTI_FTSR_BIT); EXTI_REG_RTSR |= EXTI_RTSR_BIT; EXTI_REG_PR = EXTI_PR_BIT; @@ -310,15 +329,14 @@ int rtc_set_time(struct tm *time) /* normalize input */ rtc_tm_normalize(time); - rtc_unlock(); - + rtc_enter_init_mode(); RTC->DR = (val2bcd((time->tm_year - YEAR_OFFSET), RTC_DR_YU_Pos, DR_Y_MASK) | val2bcd(time->tm_mon + 1, RTC_DR_MU_Pos, DR_M_MASK) | val2bcd(time->tm_mday, RTC_DR_DU_Pos, DR_D_MASK)); RTC->TR = (val2bcd(time->tm_hour, RTC_TR_HU_Pos, TR_H_MASK) | val2bcd(time->tm_min, RTC_TR_MNU_Pos, TR_M_MASK) | val2bcd(time->tm_sec, RTC_TR_SU_Pos, TR_S_MASK)); - rtc_lock(); + rtc_exit_init_mode(); while (!(RTC_REG_ISR & RTC_ISR_RSF)) {} return 0; @@ -326,6 +344,16 @@ int rtc_set_time(struct tm *time) int rtc_get_time(struct tm *time) { + /* After waking up from standby, the RSF flag has to be manually cleared. + * To be safe, we do it every time even though we might not have been in + * standby before. */ + rtc_unlock(); + RTC_REG_ISR &= ~RTC_ISR_RSF; + rtc_lock(); + + /* waiting for the RSF bit to be set again before accessing the time */ + while (!(RTC_REG_ISR & RTC_ISR_RSF)) {}; + /* save current time */ uint32_t tr = RTC->TR; uint32_t dr = RTC->DR; @@ -360,7 +388,7 @@ int rtc_set_alarm(struct tm *time, rtc_alarm_cb_t cb, void *arg) val2bcd(time->tm_sec, RTC_ALRMAR_SU_Pos, ALRM_S_MASK)); /* Enable Alarm A */ -#if !defined(CPU_FAM_STM32L5) +#if !(defined(CPU_FAM_STM32L5) || defined(CPU_FAM_STM32WL)) RTC_REG_ISR &= ~(RTC_ISR_ALRAF); #else RTC_REG_SCR = RTC_SCR_CALRAF; @@ -393,7 +421,7 @@ void rtc_clear_alarm(void) RTC->CR &= ~(RTC_CR_ALRAE | RTC_CR_ALRAIE); -#if !defined(CPU_FAM_STM32L5) && !defined(CPU_FAM_STM32U5) +#if !(defined(CPU_FAM_STM32L5) || defined(CPU_FAM_STM32U5) || defined(CPU_FAM_STM32WL)) while (!(RTC_REG_ISR & RTC_ISR_ALRAWF)) {} #else RTC_REG_SCR = RTC_SCR_CALRAF; @@ -421,7 +449,8 @@ void rtc_poweroff(void) void ISR_NAME(void) { -#if !defined(CPU_FAM_STM32L5) && !defined(CPU_FAM_STM32G0) && !defined(CPU_FAM_STM32U5) +#if !(defined(CPU_FAM_STM32L5) || defined(CPU_FAM_STM32WL) || defined(CPU_FAM_STM32G0) || \ + defined(CPU_FAM_STM32U5)) if (RTC_REG_ISR & RTC_ISR_ALRAF) { if (isr_ctx.cb != NULL) { isr_ctx.cb(isr_ctx.arg); diff --git a/tests/periph/rtc/main.c b/tests/periph/rtc/main.c index d5433fee1c63..7d1fffa367ef 100644 --- a/tests/periph/rtc/main.c +++ b/tests/periph/rtc/main.c @@ -138,7 +138,7 @@ int main(void) } time = (struct tm){ - .tm_year = 2020 - 1900, /* years are counted from 1900 */ + .tm_year = 2025 - 1900, /* years are counted from 1900 */ .tm_mon = 1, /* 0 = January, 11 = December */ .tm_mday = 28, .tm_hour = 23,