/* * This file is part of the libopencm3 project. * * Copyright (C) 2010 Uwe Hermann * Copyright (C) 2010 Lord James * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . */ #include #include #include void rtc_awake_from_off(osc_t clock_source) { u32 reg32; /* Enable power and backup interface clocks. */ RCC_APB1ENR |= (RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN); /* Enable access to the backup registers and the RTC. */ PWR_CR |= PWR_CR_DBP; /* * Reset the backup domain, clears everything RTC related. * If not wanted use the rtc_awake_from_standby() function. */ rcc_backupdomain_reset(); switch (clock_source) { case LSE: /* Turn the LSE on and wait while it stabilises. */ RCC_BDCR |= RCC_BDCR_LSEON; while ((reg32 = (RCC_BDCR & RCC_BDCR_LSERDY)) == 0); /* Choose LSE as the RTC clock source. */ RCC_BDCR &= ~((1 << 8) | (1 << 9)); RCC_BDCR |= (1 << 8); break; case LSI: /* Turn the LSI on and wait while it stabilises. */ RCC_CSR |= RCC_CSR_LSION; while ((reg32 = (RCC_CSR & RCC_CSR_LSIRDY)) == 0); /* Choose LSI as the RTC clock source. */ RCC_BDCR &= ~((1 << 8) | (1 << 9)); RCC_BDCR |= (1 << 9); break; case HSE: /* Turn the HSE on and wait while it stabilises. */ RCC_CR |= RCC_CR_HSEON; while ((reg32 = (RCC_CR & RCC_CR_HSERDY)) == 0); /* Choose HSE as the RTC clock source. */ RCC_BDCR &= ~((1 << 8) | (1 << 9)); RCC_BDCR |= (1 << 9) | (1 << 8); break; case PLL: case HSI: /* Unusable clock source, here to prevent warnings. */ /* Turn off clock sources to RTC. */ RCC_BDCR &= ~((1 << 8) | (1 << 9)); break; } /* Enable the RTC. */ RCC_BDCR |= RCC_BDCR_RTCEN; /* Wait for the RSF bit in RTC_CRL to be set by hardware. */ RTC_CRL &= ~RTC_CRL_RSF; while ((reg32 = (RTC_CRL & RTC_CRL_RSF)) == 0); /* Wait for the last write operation to finish. */ /* TODO: Necessary? */ while ((reg32 = (RTC_CRL & RTC_CRL_RTOFF)) == 0); } void rtc_enter_config_mode(void) { u32 reg32; /* Wait until the RTOFF bit is 1 (no RTC register writes ongoing). */ while ((reg32 = (RTC_CRL & RTC_CRL_RTOFF)) == 0); /* Enter configuration mode. */ RTC_CRL |= RTC_CRL_CNF; } void rtc_exit_config_mode(void) { u32 reg32; /* Exit configuration mode. */ RTC_CRL &= ~RTC_CRL_CNF; /* Wait until the RTOFF bit is 1 (our RTC register write finished). */ while ((reg32 = (RTC_CRL & RTC_CRL_RTOFF)) == 0); } void rtc_set_alarm_time(u32 alarm_time) { rtc_enter_config_mode(); RTC_ALRL = (alarm_time & 0x0000ffff); RTC_ALRH = (alarm_time & 0xffff0000) >> 16; rtc_exit_config_mode(); } void rtc_enable_alarm(void) { rtc_enter_config_mode(); RTC_CRH |= RTC_CRH_ALRIE; rtc_exit_config_mode(); } void rtc_disable_alarm(void) { rtc_enter_config_mode(); RTC_CRH &= ~RTC_CRH_ALRIE; rtc_exit_config_mode(); } void rtc_set_prescale_val(u32 prescale_val) { rtc_enter_config_mode(); RTC_PRLL = prescale_val & 0x0000ffff; /* PRL[15:0] */ RTC_PRLH = (prescale_val & 0x000f0000) >> 16; /* PRL[19:16] */ rtc_exit_config_mode(); } u32 rtc_get_counter_val(void) { return (RTC_CNTH << 16) | RTC_CNTL; } u32 rtc_get_prescale_div_val(void) { return (RTC_DIVH << 16) | RTC_DIVL; } u32 rtc_get_alarm_val(void) { return (RTC_ALRH << 16) | RTC_ALRL; } void rtc_set_counter_val(u32 counter_val) { rtc_enter_config_mode(); RTC_CNTH = (counter_val & 0xffff0000) >> 16; /* CNT[31:16] */ RTC_CNTL = counter_val & 0x0000ffff; /* CNT[15:0] */ rtc_exit_config_mode(); } void rtc_interrupt_enable(rtcflag_t flag_val) { rtc_enter_config_mode(); /* Set the correct interrupt enable. */ switch (flag_val) { case RTC_SEC: RTC_CRH |= RTC_CRH_SECIE; break; case RTC_ALR: RTC_CRH |= RTC_CRH_ALRIE; break; case RTC_OW: RTC_CRH |= RTC_CRH_OWIE; break; } rtc_exit_config_mode(); } void rtc_interrupt_disable(rtcflag_t flag_val) { rtc_enter_config_mode(); /* Disable the correct interrupt enable. */ switch (flag_val) { case RTC_SEC: RTC_CRH &= ~RTC_CRH_SECIE; break; case RTC_ALR: RTC_CRH &= ~RTC_CRH_ALRIE; break; case RTC_OW: RTC_CRH &= ~RTC_CRH_OWIE; break; } rtc_exit_config_mode(); } void rtc_clear_flag(rtcflag_t flag_val) { /* Configuration mode not needed. */ /* Clear the correct flag. */ switch (flag_val) { case RTC_SEC: RTC_CRL &= ~RTC_CRL_SECF; break; case RTC_ALR: RTC_CRL &= ~RTC_CRL_ALRF; break; case RTC_OW: RTC_CRL &= ~RTC_CRL_OWF; break; } } u32 rtc_check_flag(rtcflag_t flag_val) { u32 reg32; /* Read correct flag. */ switch (flag_val) { case RTC_SEC: reg32 = RTC_CRL & RTC_CRL_SECF; break; case RTC_ALR: reg32 = RTC_CRL & RTC_CRL_ALRF; break; case RTC_OW: reg32 = RTC_CRL & RTC_CRL_OWF; break; default: reg32 = 0; break; } return reg32; } void rtc_awake_from_standby(void) { u32 reg32; /* Enable power and backup interface clocks. */ RCC_APB1ENR |= (RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN); /* Enable access to the backup registers and the RTC. */ PWR_CR |= PWR_CR_DBP; /* Wait for the RSF bit in RTC_CRL to be set by hardware. */ RTC_CRL &= ~RTC_CRL_RSF; while ((reg32 = (RTC_CRL & RTC_CRL_RSF)) == 0); /* Wait for the last write operation to finish. */ /* TODO: Necessary? */ while ((reg32 = (RTC_CRL & RTC_CRL_RTOFF)) == 0); } void rtc_auto_awake(osc_t clock_source, u32 prescale_val) { u32 reg32; /* Enable power and backup interface clocks. */ RCC_APB1ENR |= (RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN); /* Enable access to the backup registers and the RTC. */ /* TODO: Not sure if this is necessary to just read the flag. */ PWR_CR |= PWR_CR_DBP; if ((reg32 = RCC_BDCR & RCC_BDCR_RTCEN) != 0) { rtc_awake_from_standby(); } else { rtc_awake_from_off(clock_source); rtc_set_prescale_val(prescale_val); } }