#include #include #include #include #include #include "clock.h" #include "deepsleep.h" #include "libopencm3/stm32/f0/rcc.h" void init_rtc(void) { // RTC clock setup // see libopencm3-examples::examples/stm32/l1/stm32l-discovery/button-irq-printf-lowpower/main.c /* turn on power block to enable unlocking */ rcc_periph_clock_enable(RCC_PWR); pwr_disable_backup_domain_write_protect(); /* reset rtc */ RCC_BDCR |= RCC_BDCR_BDRST; RCC_BDCR &= ~RCC_BDCR_BDRST; // use LSI for RTC rcc_osc_on(RCC_LSI); rcc_wait_for_osc_ready(RCC_LSI); /* Select the LSI as rtc clock */ RCC_BDCR |= RCC_BDCR_RTCSEL_LSI; /* ?! Stdperiph examples don't turn this on until _afterwards_ which * simply doesn't work. It must be on at least to be able to * configure it */ rcc_enable_rtc_clock(); pwr_enable_backup_domain_write_protect(); nvic_enable_irq(NVIC_RTC_IRQ); exti_set_trigger(EXTI17, EXTI_TRIGGER_RISING); exti_enable_request(EXTI17); } static void unlock_rtc_access(void) { pwr_disable_backup_domain_write_protect(); rtc_unlock(); } static void lock_rtc_access(void) { rtc_lock(); pwr_enable_backup_domain_write_protect(); } void deepsleep(uint32_t duration_secs) { uint32_t tmp = 0; // unlock RTC registers unlock_rtc_access(); // enter initialization mode rtc_set_init_flag(); rtc_wait_for_init_ready(); RTC_TR = 0; // 00:00:00 RTC_DR = // friday, 01.01.16 (1 << RTC_DR_YT_SHIFT) | (6 << RTC_DR_YU_SHIFT) | (5 << RTC_DR_WDU_SHIFT) | (0 << RTC_DR_MT_SHIFT) | (1 << RTC_DR_MU_SHIFT) | (0 << RTC_DR_DT_SHIFT) | (1 << RTC_DR_DU_SHIFT); // disable Alarm A RTC_CR &= ~RTC_CR_ALRAE; // wait until register is writeable while((RTC_ISR & RTC_ISR_ALRAWF) != RTC_ISR_ALRAWF) { // do nothing } tmp |= (duration_secs % 10) << RTC_ALRMXR_SU_SHIFT; duration_secs /= 10; tmp |= (duration_secs % 6) << RTC_ALRMXR_ST_SHIFT; duration_secs /= 6; tmp |= (duration_secs % 10) << RTC_ALRMXR_MNU_SHIFT; duration_secs /= 10; tmp |= (duration_secs % 6) << RTC_ALRMXR_MNT_SHIFT; duration_secs /= 6; tmp |= (duration_secs % 10) << RTC_ALRMXR_HU_SHIFT; duration_secs /= 10; tmp |= (duration_secs % 2) << RTC_ALRMXR_HT_SHIFT; // FIXME: >1d is not supported tmp |= RTC_ALRMXR_MSK4; // ignore day/date // set alarm register RTC_ALRMAR = tmp; // clear Alarm A flag RTC_ISR &= ~RTC_ISR_ALRAF; // enable RTC alarm interrupt for wakeup RTC_CR |= RTC_CR_ALRAE | RTC_CR_ALRAIE; // leave initialization mode rtc_clear_init_flag(); // lock registers again (using invalid key) lock_rtc_access(); // enter deep sleep mode pwr_set_stop_mode(); pwr_voltage_regulator_low_power_in_stop(); SCB_SCR |= SCB_SCR_SLEEPDEEP; __WFI(); SCB_SCR &= ~SCB_SCR_SLEEPDEEP; // no deepsleep except in this function //rcc_periph_clock_disable(RCC_PWR); // no longer needed //rcc_osc_off(RCC_LSI); init_clock(); // ensure that all clocks are running again } void rtc_isr(void) { exti_reset_request(EXTI17); }