|
|
|
#include <libopencm3/stm32/pwr.h>
|
|
|
|
#include <libopencm3/stm32/rcc.h>
|
|
|
|
#include <libopencm3/stm32/rtc.h>
|
|
|
|
|
|
|
|
#include <macros.h>
|
|
|
|
#include <rtc.h>
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|