#include #include #include #include #include static void rtc_set_clock_source(enum rcc_osc clock_source) { uint32_t reg; switch (clock_source) { case RCC_LSE: /* Turn on the LSE and wait until it stabilised. */ RCC_BDCR |= RCC_BDCR_LSEON; while ((reg = (RCC_BDCR & RCC_BDCR_LSERDY)) == 0); /* Choose LSE as the RTC clock source. */ RCC_BDCR &= ~BITS(8, 9); RCC_BDCR |= BIT(8); break; case RCC_LSI: /* Turn on the LSI and wait until it stabilised. */ RCC_CSR |= RCC_CSR_LSION; while ((reg = (RCC_CSR & RCC_CSR_LSIRDY)) == 0); /* Choose LSI as the RTC clock source. */ RCC_BDCR &= ~BITS(8, 9); RCC_BDCR |= BIT(9); break; case RCC_HSE: /* Turn on the HSE and wait until it stabilised. */ RCC_CR |= RCC_CR_HSEON; while ((reg = (RCC_CR & RCC_CR_HSERDY)) == 0); /* Choose HSE as the RTC clock source. */ RCC_BDCR &= ~BITS(8, 9); RCC_BDCR |= BITS(8, 9); break; default: /* Unusable clock sources. Turn off the clock source for RTC. */ RCC_BDCR &= ~BITS(8, 9); break; } } int rtc_get_time(struct tm *time) { uint32_t rtc_tr, rtc_dr; rtc_tr = RTC_TR; rtc_dr = RTC_DR; time->tm_sec = (unsigned char)(rtc_tr & 0xF); time->tm_sec += (unsigned char)(((rtc_tr >> 4) & 0xF) * 10); time->tm_min = (unsigned char)((rtc_tr >> 8) & 0xF); time->tm_min += (unsigned char)(((rtc_tr >> 12) & 0xF) * 10); time->tm_hour = (unsigned char)((rtc_tr >> 16) & 0xF); time->tm_hour += (unsigned char)(((rtc_tr >> 20) & 0xF) * 10); time->tm_mday = (unsigned char)(rtc_dr & 0xF); time->tm_mday += (unsigned char)(((rtc_dr >> 4) & 0x3) * 10); time->tm_mon = (unsigned char)((rtc_dr >> 8) & 0xF); time->tm_mon += (unsigned char)(((rtc_dr >> 12) & 0x1) * 10); time->tm_mon -= 1; time->tm_year = (unsigned char)((rtc_dr >> 16) & 0xF); time->tm_year += (unsigned char)(((rtc_dr >> 20) & 0xF) * 10); time->tm_year += 100; return 0; } static int rtc_set_time(struct tm *time) { uint32_t rtc_tr, rtc_dr; uint32_t mon, year; if (!time) return -1; mon = time->tm_mon + 1; year = time->tm_year - 100; rtc_tr = (time->tm_sec % 10); rtc_tr |= (time->tm_sec / 10) << 4; rtc_tr |= (time->tm_min % 10) << 8; rtc_tr |= (time->tm_min / 10) << 12; rtc_tr |= (time->tm_hour % 10) << 16; rtc_tr |= (time->tm_hour / 10) << 20; rtc_dr = (time->tm_mday % 10); rtc_dr |= (time->tm_mday / 10) << 4; rtc_dr |= (mon % 10) << 8; rtc_dr |= (mon / 10) << 12; rtc_dr |= (year % 10) << 16; rtc_dr |= (year / 10) << 20; RTC_TR = rtc_tr; RTC_DR = rtc_dr; return 0; } int rtc_init(struct tm *time) { /* Turn on power block to enable unlocking. */ rcc_periph_clock_enable(RCC_PWR); pwr_disable_backup_domain_write_protect(); /* Reset the RTC. */ RCC_BDCR |= RCC_BDCR_BDRST; RCC_BDCR &= ~RCC_BDCR_BDRST; /* Enable the LSI and set it as the clock source for RTC. */ rcc_osc_on(RCC_LSI); rcc_wait_for_osc_ready(RCC_LSI); rtc_set_clock_source(RCC_LSI); /* Enable the RTC clock */ rcc_periph_clock_enable(RCC_RTC); /* Unlock the registers in the RTC domain. */ rtc_unlock(); /* Enter init mode. */ RTC_ISR |= RTC_ISR_INIT; while ((RTC_ISR & RTC_ISR_INITF) == 0); /* Configure the RTC. */ rtc_set_prescaler(0x1ff, 0x7f); rtc_set_time(time); /* Exit init mode. */ RTC_ISR &= ~(RTC_ISR_INIT); /* Lock the registers. */ rtc_lock(); pwr_enable_backup_domain_write_protect(); /* Wait for the synchronisation. */ rtc_wait_for_synchro(); return 0; }