LNSC-2420-Firmware/src/deepsleep.c

137 lines
3.0 KiB
C

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